From e77f90cef078f66ecc0569401a3a43026768be3a Mon Sep 17 00:00:00 2001 From: Jefferson Michael Date: Tue, 4 Apr 2023 04:19:16 -0300 Subject: PT BR translation improvements (#1717) --- archinstall/locales/pt_BR/LC_MESSAGES/base.mo | Bin 26469 -> 27361 bytes archinstall/locales/pt_BR/LC_MESSAGES/base.po | 55 ++++++++++++++++++-------- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo index bcffafbf..8cba125a 100644 Binary files a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo and b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.po b/archinstall/locales/pt_BR/LC_MESSAGES/base.po index 33e78a20..88506ce0 100644 --- a/archinstall/locales/pt_BR/LC_MESSAGES/base.po +++ b/archinstall/locales/pt_BR/LC_MESSAGES/base.po @@ -1,13 +1,19 @@ # Translators: # @Cain-dev (cain-dev.github.io) # Rafael Fontenelle +# Jefferson Michael msgid "" msgstr "" -"Last-Translator: Rafael Fontenelle \n" +"Project-Id-Version: \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: Jefferson Michael \n" +"Language-Team: \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.2.2\n" msgid "[!] A log file has been created here: {} {}" msgstr "[!] Um arquivo de log foi criado aqui: {} {}" @@ -817,9 +823,8 @@ msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {max_ msgid "Parallel Downloads" msgstr "Downloads Paralelos" -#, fuzzy msgid "ESC to skip" -msgstr "Use ESC para pular" +msgstr "ESC para sair" msgid "CTRL+C to reset" msgstr "CTRL+C para reiniciar" @@ -836,43 +841,59 @@ msgstr "Para poder usar esta tradução, instale manualmente uma fonte que supor msgid "The font should be stored as {}" msgstr "A fonte deve ser armazenada como {}" -#, fuzzy msgid "Encryption type" -msgstr "Senha de encriptação" +msgstr "Tipo de encriptação" msgid "Partitions" -msgstr "" +msgstr "Partições" msgid "No HSM devices available" -msgstr "" +msgstr "Nenhum dispositivo HSM disponivel" -#, fuzzy msgid "Partitions to be encrypted" -msgstr "Selecione quais partições encriptar" +msgstr "Partições a serem encriptadas" msgid "Select disk encryption option" -msgstr "" +msgstr "Selecione a opção de encriptação de disco" msgid "Select a FIDO2 device to use for HSM" -msgstr "" +msgstr "Selecione um dispositivo FIDO2 para usar como HSM" -#, fuzzy msgid "All settings will be reset, are you sure?" -msgstr "{} contém partições em fila, isto irá removê-las, tem certeza?" +msgstr "Todas as configurações serão redefinidas,você tem certeza ?" msgid "Back" -msgstr "" +msgstr "Voltar" msgid "Disk encryption" -msgstr "" +msgstr "Encriptação de disco" -#, fuzzy msgid "Password" -msgstr "Senha de root" +msgstr "Senha" msgid "Partition encryption" +msgstr "Encriptação de partição" + +msgid "When picking a directory to save configuration files to, by default we will ignore the following folders: " +msgstr "" + +msgid "Finding possible directories to save configuration files ..." msgstr "" +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "Selecione um ou mais discos rígidos para usar e configurar" + +msgid "" +"Do you want to save {} configuration file(s) in the following locations?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "Salvar configuração" + #~ msgid "Enter the start sector (percentage or block number, default: {}): " #~ msgstr "Digite o setor de início (porcentagem ou número do bloco, padrão: {}): " -- cgit v1.2.3-70-g09d2 From 5689d4f59f4f08465d2ef72412f5819e9df87430 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Tue, 4 Apr 2023 03:46:49 -0400 Subject: Fix sd-boot code duplication (#1721) --- archinstall/lib/installer.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index f1c7b3db..b33a1a34 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -919,7 +919,7 @@ class Installer: kernel_options = f"options" - if self._disk_encryption.hsm_device: + if self._disk_encryption and self._disk_encryption.hsm_device: # Note: lsblk UUID must be used, not PARTUUID for sd-encrypt to work kernel_options += f" rd.luks.name={real_device.uuid}=luksdev" # Note: tpm2-device and fido2-device don't play along very well: @@ -929,13 +929,6 @@ class Installer: kernel_options += f" cryptdevice=PARTUUID={real_device.part_uuid}:luksdev" entry.write(f'{kernel_options} root=/dev/mapper/luksdev {options_entry}') - - if self._disk_encryption and self._disk_encryption.hsm_device: - # Note: lsblk UUID must be used, not PARTUUID for sd-encrypt to work - kernel_options += f" rd.luks.name={real_device.uuid}=luksdev" - # Note: tpm2-device and fido2-device don't play along very well: - # https://github.com/archlinux/archinstall/pull/1196#issuecomment-1129715645 - kernel_options += f" rd.luks.options=fido2-device=auto,password-echo=no" else: log(f"Identifying root partition by PARTUUID on {root_partition}, looking for '{root_partition.part_uuid}'.", level=logging.DEBUG) entry.write(f'options root=PARTUUID={root_partition.part_uuid} {options_entry}') -- cgit v1.2.3-70-g09d2 From c241203ded21676354f8d3f589d2f8b5fa5e0c36 Mon Sep 17 00:00:00 2001 From: 牧瀬紅莉栖 Date: Sun, 16 Apr 2023 15:36:01 +0800 Subject: Add runtime dependency for dmenu (#1514) --- profiles/sway.py | 1 + 1 file changed, 1 insertion(+) diff --git a/profiles/sway.py b/profiles/sway.py index 5fbd3365..4716fe78 100644 --- a/profiles/sway.py +++ b/profiles/sway.py @@ -21,6 +21,7 @@ __packages__ = [ "slurp", "pavucontrol", "foot", + "xorg-xwayland", ] -- cgit v1.2.3-70-g09d2 From 98f13ece6e99e32604e915c1e5e43527a7578346 Mon Sep 17 00:00:00 2001 From: Callum Andrew Date: Sun, 16 Apr 2023 17:36:56 +1000 Subject: profiles/sway: update packages (#1745) --- profiles/sway.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/profiles/sway.py b/profiles/sway.py index 4716fe78..f69f73ce 100644 --- a/profiles/sway.py +++ b/profiles/sway.py @@ -12,11 +12,12 @@ is_top_level_profile = False __packages__ = [ "sway", + "swaybg", "swaylock", "swayidle", "waybar", "dmenu", - "light", + "brightnessctl", "grim", "slurp", "pavucontrol", -- cgit v1.2.3-70-g09d2 From 5253e57e9f26cf3e59cb2460544af13f56e485bb Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Wed, 19 Apr 2023 06:52:51 -0400 Subject: Remove superfluous time sync steps (#1752) --- archinstall/lib/installer.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index b33a1a34..b4d253b3 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -531,15 +531,6 @@ class Installer: self.log('Activating systemd-timesyncd for time synchronization using Arch Linux and ntp.org NTP servers.', level=logging.INFO) self.enable_service('systemd-timesyncd') - with open(f"{self.target}/etc/systemd/timesyncd.conf", "w") as fh: - fh.write("[Time]\n") - fh.write("NTP=0.arch.pool.ntp.org 1.arch.pool.ntp.org 2.arch.pool.ntp.org 3.arch.pool.ntp.org\n") - fh.write("FallbackNTP=0.pool.ntp.org 1.pool.ntp.org 0.fr.pool.ntp.org\n") - - from .systemd import Boot - with Boot(self) as session: - session.SysCommand(["timedatectl", "set-ntp", 'true']) - def enable_espeakup(self) -> None: self.log('Enabling espeakup.service for speech synthesis (accessibility).', level=logging.INFO) self.enable_service('espeakup') -- cgit v1.2.3-70-g09d2 From 00b0ae7ba439a5a420095175b3bedd52c569db51 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Wed, 19 Apr 2023 20:55:42 +1000 Subject: PyParted and a large rewrite of the underlying partitioning (#1604) * Invert mypy files * Add optional pre-commit hooks * New profile structure * Serialize profiles * Use profile instead of classmethod * Custom profile setup * Separator between back * Support profile import via url * Move profiles module * Refactor files * Remove symlink * Add user to docker group * Update schema description * Handle list services * mypy fixes * mypy fixes * Rename profilesv2 to profiles * flake8 * mypy again * Support selecting DM * Fix mypy * Cleanup * Update greeter setting * Update schema * Revert toml changes * Poc external dependencies * Dependency support * New encryption menu * flake8 * Mypy and flake8 * Unify lsblk command * Update bootloader configuration * Git hooks * Fix import * Pyparted * Remove custom font setting * flake8 * Remove default preview * Manual partitioning menu * Update structure * Disk configuration * Update filesystem * luks2 encryption * Everything works until installation * Btrfsutil * Btrfs handling * Update btrfs * Save encryption config * Fix pipewire issue * Update mypy version * Update all pre-commit * Update package versions * Revert audio/pipewire * Merge master PRs * Add master changes * Merge master changes * Small renaming * Pull master changes * Reset disk enc after disk config change * Generate locals * Update naming * Fix imports * Fix broken sync * Fix pre selection on table menu * Profile menu * Update profile * Fix post_install * Added python-pyparted to PKGBUILD, this requires [testing] to be enabled in order to run makepkg. Package still works via python -m build etc. * Swaped around some setuptools logic in pyproject Since we define `package-data` and `packages` there should be no need for: ``` [tool.setuptools.packages.find] where = ["archinstall", "archinstall.*"] ``` * Removed pyproject collisions. Duplicate definitions. * Made sure pyproject.toml includes languages * Add example and update README * Fix pyproject issues * Generate locale * Refactor imports * Simplify imports * Add profile description and package examples * Align code * Fix mypy * Simplify imports * Fix saving config * Fix wrong luks merge * Refactor installation * Fix cdrom device loading * Fix wrongly merged code * Fix imports and greeter * Don't terminate on partprobe error * Use specific path on partprobe from luks * Update archinstall/lib/disk/device_model.py Co-authored-by: codefiles <11915375+codefiles@users.noreply.github.com> * Update archinstall/lib/disk/device_model.py Co-authored-by: codefiles <11915375+codefiles@users.noreply.github.com> * Update github workflow to test archinstall installation * Update sway merge * Generate locales * Update workflow --------- Co-authored-by: Daniel Girtler Co-authored-by: Anton Hvornum Co-authored-by: Anton Hvornum Co-authored-by: codefiles <11915375+codefiles@users.noreply.github.com> --- .github/workflows/iso-build.yaml | 12 +- .github/workflows/mypy.yaml | 6 +- .github/workflows/python-build.yml | 27 +- .gitignore | 3 +- .pre-commit-config.yaml | 42 + PKGBUILD | 2 +- README.md | 201 +- archinstall/__init__.py | 143 +- archinstall/__main__.py | 6 +- archinstall/default_profiles/__init__.py | 0 .../default_profiles/applications/__init__.py | 0 .../default_profiles/applications/pipewire.py | 40 + archinstall/default_profiles/custom.py | 218 +++ archinstall/default_profiles/desktop.py | 87 + archinstall/default_profiles/desktops/__init__.py | 0 archinstall/default_profiles/desktops/awesome.py | 36 + archinstall/default_profiles/desktops/bspwm.py | 30 + archinstall/default_profiles/desktops/budgie.py | 30 + archinstall/default_profiles/desktops/cinnamon.py | 31 + archinstall/default_profiles/desktops/cutefish.py | 31 + archinstall/default_profiles/desktops/deepin.py | 28 + .../default_profiles/desktops/enlightenment.py | 27 + archinstall/default_profiles/desktops/gnome.py | 27 + archinstall/default_profiles/desktops/i3.py | 33 + archinstall/default_profiles/desktops/kde.py | 32 + archinstall/default_profiles/desktops/lxqt.py | 35 + archinstall/default_profiles/desktops/mate.py | 27 + archinstall/default_profiles/desktops/qtile.py | 27 + archinstall/default_profiles/desktops/sway.py | 66 + archinstall/default_profiles/desktops/xfce4.py | 30 + archinstall/default_profiles/minimal.py | 15 + archinstall/default_profiles/profile.py | 206 ++ archinstall/default_profiles/server.py | 57 + archinstall/default_profiles/servers/__init__.py | 0 archinstall/default_profiles/servers/cockpit.py | 19 + archinstall/default_profiles/servers/docker.py | 33 + archinstall/default_profiles/servers/httpd.py | 19 + archinstall/default_profiles/servers/lighttpd.py | 19 + archinstall/default_profiles/servers/mariadb.py | 25 + archinstall/default_profiles/servers/nginx.py | 19 + archinstall/default_profiles/servers/postgresql.py | 26 + archinstall/default_profiles/servers/sshd.py | 19 + archinstall/default_profiles/servers/tomcat.py | 19 + archinstall/default_profiles/tailored.py | 21 + archinstall/default_profiles/xorg.py | 21 + archinstall/lib/configuration.py | 69 +- archinstall/lib/disk/__init__.py | 47 +- archinstall/lib/disk/blockdevice.py | 301 --- archinstall/lib/disk/btrfs/__init__.py | 56 - archinstall/lib/disk/btrfs/btrfs_helpers.py | 136 -- archinstall/lib/disk/btrfs/btrfspartition.py | 109 -- archinstall/lib/disk/btrfs/btrfssubvolumeinfo.py | 192 -- archinstall/lib/disk/device_handler.py | 599 ++++++ archinstall/lib/disk/device_model.py | 1033 ++++++++++ archinstall/lib/disk/diskinfo.py | 40 - archinstall/lib/disk/dmcryptdev.py | 48 - archinstall/lib/disk/encryption.py | 174 -- archinstall/lib/disk/encryption_menu.py | 179 ++ archinstall/lib/disk/fido.py | 94 + archinstall/lib/disk/filesystem.py | 343 +--- archinstall/lib/disk/helpers.py | 556 ------ archinstall/lib/disk/mapperdev.py | 92 - archinstall/lib/disk/partition.py | 661 ------- archinstall/lib/disk/partitioning_menu.py | 335 ++++ archinstall/lib/disk/subvolume_menu.py | 101 + archinstall/lib/disk/user_guides.py | 240 --- archinstall/lib/disk/validators.py | 48 - archinstall/lib/general.py | 31 +- archinstall/lib/global_menu.py | 364 ++++ archinstall/lib/hsm/__init__.py | 1 - archinstall/lib/hsm/fido.py | 109 -- archinstall/lib/installer.py | 670 +++---- archinstall/lib/locale_helpers.py | 20 +- archinstall/lib/luks.py | 331 ++-- archinstall/lib/menu/__init__.py | 11 +- archinstall/lib/menu/abstract_menu.py | 200 +- archinstall/lib/menu/global_menu.py | 429 ----- archinstall/lib/menu/list_manager.py | 46 +- archinstall/lib/menu/menu.py | 114 +- archinstall/lib/menu/simple_menu.py | 2002 -------------------- archinstall/lib/menu/table_selection_menu.py | 68 +- archinstall/lib/mirrors.py | 4 +- archinstall/lib/models/__init__.py | 5 +- archinstall/lib/models/bootloader.py | 40 + archinstall/lib/models/dataclasses.py | 136 -- archinstall/lib/models/disk_encryption.py | 90 - archinstall/lib/models/gen.py | 146 ++ archinstall/lib/models/network_configuration.py | 2 +- archinstall/lib/models/password_strength.py | 85 - archinstall/lib/models/pydantic.py | 134 -- archinstall/lib/models/subvolume.py | 68 - archinstall/lib/models/users.py | 94 +- archinstall/lib/networking.py | 33 +- archinstall/lib/output.py | 47 +- archinstall/lib/packages/__init__.py | 4 + archinstall/lib/packages/packages.py | 4 +- archinstall/lib/pacman.py | 4 + archinstall/lib/profile/__init__.py | 0 archinstall/lib/profile/profile_menu.py | 203 ++ archinstall/lib/profile/profile_model.py | 35 + archinstall/lib/profile/profiles_handler.py | 391 ++++ archinstall/lib/profiles.py | 340 ---- archinstall/lib/storage.py | 19 +- archinstall/lib/udev/__init__.py | 1 - archinstall/lib/udev/udevadm.py | 17 - archinstall/lib/user_interaction/__init__.py | 16 +- .../user_interaction/backwards_compatible_conf.py | 95 - archinstall/lib/user_interaction/disk_conf.py | 403 +++- archinstall/lib/user_interaction/general_conf.py | 59 +- archinstall/lib/user_interaction/locale_conf.py | 3 +- .../lib/user_interaction/manage_users_conf.py | 21 +- archinstall/lib/user_interaction/network_conf.py | 6 +- .../lib/user_interaction/partitioning_conf.py | 362 ---- archinstall/lib/user_interaction/save_conf.py | 74 +- .../lib/user_interaction/subvolume_config.py | 98 - archinstall/lib/user_interaction/system_conf.py | 133 +- archinstall/lib/user_interaction/utils.py | 47 +- archinstall/lib/utils/__init__.py | 0 archinstall/lib/utils/singleton.py | 15 + archinstall/lib/utils/util.py | 30 + archinstall/locales/ar/LC_MESSAGES/base.po | 209 +- archinstall/locales/base.pot | 225 ++- archinstall/locales/cs/LC_MESSAGES/base.po | 246 ++- archinstall/locales/de/LC_MESSAGES/base.po | 243 ++- archinstall/locales/el/LC_MESSAGES/base.po | 246 ++- archinstall/locales/en/LC_MESSAGES/base.po | 203 +- archinstall/locales/es/LC_MESSAGES/base.po | 246 ++- archinstall/locales/fr/LC_MESSAGES/base.mo | Bin 27591 -> 27562 bytes archinstall/locales/fr/LC_MESSAGES/base.po | 300 ++- archinstall/locales/id/LC_MESSAGES/base.mo | Bin 27433 -> 26392 bytes archinstall/locales/id/LC_MESSAGES/base.po | 274 ++- archinstall/locales/it/LC_MESSAGES/base.po | 246 ++- archinstall/locales/ka/LC_MESSAGES/base.po | 250 ++- archinstall/locales/ko/LC_MESSAGES/base.po | 246 ++- archinstall/locales/languages.json | 2 +- archinstall/locales/nl/LC_MESSAGES/base.po | 242 ++- archinstall/locales/pl/LC_MESSAGES/base.po | 246 ++- archinstall/locales/pt/LC_MESSAGES/base.po | 242 ++- archinstall/locales/pt_BR/LC_MESSAGES/base.mo | Bin 27361 -> 27157 bytes archinstall/locales/pt_BR/LC_MESSAGES/base.po | 248 ++- archinstall/locales/ru/LC_MESSAGES/base.mo | Bin 35760 -> 36023 bytes archinstall/locales/ru/LC_MESSAGES/base.po | 249 ++- archinstall/locales/sv/LC_MESSAGES/base.po | 245 ++- archinstall/locales/ta/LC_MESSAGES/base.po | 246 ++- archinstall/locales/tr/LC_MESSAGES/base.po | 245 ++- archinstall/locales/uk/LC_MESSAGES/base.po | 250 ++- archinstall/locales/ur/LC_MESSAGES/base.po | 243 ++- archinstall/locales/zh-CN/LC_MESSAGES/base.po | 246 ++- archinstall/profiles | 1 - archinstall/scripts/__init__.py | 0 archinstall/scripts/guided.py | 276 +++ archinstall/scripts/minimal.py | 104 + archinstall/scripts/only_hd.py | 104 + archinstall/scripts/swiss.py | 353 ++++ archinstall/scripts/unattended.py | 18 + build_iso.sh | 31 + docs/examples/python.rst | 4 +- examples/__init__.py | 0 examples/auto_discovery_mounted.py | 13 + examples/config-sample.json | 126 +- examples/creds-sample.json | 19 +- examples/full_automated_installation.py | 95 + examples/guided.py | 306 --- examples/interactive_installation.py | 220 +++ examples/mac_address_installation.py | 18 + examples/minimal.py | 75 - examples/minimal_installation.py | 85 + examples/only_hd.py | 151 -- examples/only_hd_installation.py | 63 + examples/swiss.py | 526 ----- examples/unattended.py | 21 - mypy-strict.ini | 102 + mypy.ini | 15 + profiles/52-54-00-12-34-56.py | 62 - profiles/__init__.py | 0 profiles/applications/__init__.py | 0 profiles/applications/awesome.py | 34 - profiles/applications/cockpit.py | 13 - profiles/applications/docker.py | 9 - profiles/applications/httpd.py | 9 - profiles/applications/lighttpd.py | 9 - profiles/applications/mariadb.py | 11 - profiles/applications/nginx.py | 9 - profiles/applications/pipewire.py | 14 - profiles/applications/postgresql.py | 11 - profiles/applications/sshd.py | 9 - profiles/applications/tomcat.py | 12 - profiles/awesome.py | 51 - profiles/bspwm.py | 43 - profiles/budgie.py | 45 - profiles/cinnamon.py | 46 - profiles/cutefish.py | 41 - profiles/deepin.py | 44 - profiles/desktop.py | 97 - profiles/enlightenment.py | 43 - profiles/gnome.py | 45 - profiles/i3.py | 59 - profiles/kde.py | 58 - profiles/lxqt.py | 50 - profiles/mate.py | 42 - profiles/minimal.py | 24 - profiles/qtile.py | 42 - profiles/server.py | 63 - profiles/sway.py | 100 - profiles/xfce4.py | 45 - profiles/xorg.py | 68 - pyproject.toml | 32 +- schema.json | 111 +- 208 files changed, 13870 insertions(+), 10753 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 archinstall/default_profiles/__init__.py create mode 100644 archinstall/default_profiles/applications/__init__.py create mode 100644 archinstall/default_profiles/applications/pipewire.py create mode 100644 archinstall/default_profiles/custom.py create mode 100644 archinstall/default_profiles/desktop.py create mode 100644 archinstall/default_profiles/desktops/__init__.py create mode 100644 archinstall/default_profiles/desktops/awesome.py create mode 100644 archinstall/default_profiles/desktops/bspwm.py create mode 100644 archinstall/default_profiles/desktops/budgie.py create mode 100644 archinstall/default_profiles/desktops/cinnamon.py create mode 100644 archinstall/default_profiles/desktops/cutefish.py create mode 100644 archinstall/default_profiles/desktops/deepin.py create mode 100644 archinstall/default_profiles/desktops/enlightenment.py create mode 100644 archinstall/default_profiles/desktops/gnome.py create mode 100644 archinstall/default_profiles/desktops/i3.py create mode 100644 archinstall/default_profiles/desktops/kde.py create mode 100644 archinstall/default_profiles/desktops/lxqt.py create mode 100644 archinstall/default_profiles/desktops/mate.py create mode 100644 archinstall/default_profiles/desktops/qtile.py create mode 100644 archinstall/default_profiles/desktops/sway.py create mode 100644 archinstall/default_profiles/desktops/xfce4.py create mode 100644 archinstall/default_profiles/minimal.py create mode 100644 archinstall/default_profiles/profile.py create mode 100644 archinstall/default_profiles/server.py create mode 100644 archinstall/default_profiles/servers/__init__.py create mode 100644 archinstall/default_profiles/servers/cockpit.py create mode 100644 archinstall/default_profiles/servers/docker.py create mode 100644 archinstall/default_profiles/servers/httpd.py create mode 100644 archinstall/default_profiles/servers/lighttpd.py create mode 100644 archinstall/default_profiles/servers/mariadb.py create mode 100644 archinstall/default_profiles/servers/nginx.py create mode 100644 archinstall/default_profiles/servers/postgresql.py create mode 100644 archinstall/default_profiles/servers/sshd.py create mode 100644 archinstall/default_profiles/servers/tomcat.py create mode 100644 archinstall/default_profiles/tailored.py create mode 100644 archinstall/default_profiles/xorg.py delete mode 100644 archinstall/lib/disk/blockdevice.py delete mode 100644 archinstall/lib/disk/btrfs/__init__.py delete mode 100644 archinstall/lib/disk/btrfs/btrfs_helpers.py delete mode 100644 archinstall/lib/disk/btrfs/btrfspartition.py delete mode 100644 archinstall/lib/disk/btrfs/btrfssubvolumeinfo.py create mode 100644 archinstall/lib/disk/device_handler.py create mode 100644 archinstall/lib/disk/device_model.py delete mode 100644 archinstall/lib/disk/diskinfo.py delete mode 100644 archinstall/lib/disk/dmcryptdev.py delete mode 100644 archinstall/lib/disk/encryption.py create mode 100644 archinstall/lib/disk/encryption_menu.py create mode 100644 archinstall/lib/disk/fido.py delete mode 100644 archinstall/lib/disk/helpers.py delete mode 100644 archinstall/lib/disk/mapperdev.py delete mode 100644 archinstall/lib/disk/partition.py create mode 100644 archinstall/lib/disk/partitioning_menu.py create mode 100644 archinstall/lib/disk/subvolume_menu.py delete mode 100644 archinstall/lib/disk/user_guides.py delete mode 100644 archinstall/lib/disk/validators.py create mode 100644 archinstall/lib/global_menu.py delete mode 100644 archinstall/lib/hsm/__init__.py delete mode 100644 archinstall/lib/hsm/fido.py delete mode 100644 archinstall/lib/menu/global_menu.py delete mode 100644 archinstall/lib/menu/simple_menu.py create mode 100644 archinstall/lib/models/bootloader.py delete mode 100644 archinstall/lib/models/dataclasses.py delete mode 100644 archinstall/lib/models/disk_encryption.py create mode 100644 archinstall/lib/models/gen.py delete mode 100644 archinstall/lib/models/password_strength.py delete mode 100644 archinstall/lib/models/pydantic.py delete mode 100644 archinstall/lib/models/subvolume.py create mode 100644 archinstall/lib/profile/__init__.py create mode 100644 archinstall/lib/profile/profile_menu.py create mode 100644 archinstall/lib/profile/profile_model.py create mode 100644 archinstall/lib/profile/profiles_handler.py delete mode 100644 archinstall/lib/profiles.py delete mode 100644 archinstall/lib/udev/__init__.py delete mode 100644 archinstall/lib/udev/udevadm.py delete mode 100644 archinstall/lib/user_interaction/backwards_compatible_conf.py delete mode 100644 archinstall/lib/user_interaction/partitioning_conf.py delete mode 100644 archinstall/lib/user_interaction/subvolume_config.py create mode 100644 archinstall/lib/utils/__init__.py create mode 100644 archinstall/lib/utils/singleton.py create mode 100644 archinstall/lib/utils/util.py delete mode 120000 archinstall/profiles create mode 100644 archinstall/scripts/__init__.py create mode 100644 archinstall/scripts/guided.py create mode 100644 archinstall/scripts/minimal.py create mode 100644 archinstall/scripts/only_hd.py create mode 100644 archinstall/scripts/swiss.py create mode 100644 archinstall/scripts/unattended.py create mode 100755 build_iso.sh delete mode 100644 examples/__init__.py create mode 100644 examples/auto_discovery_mounted.py create mode 100644 examples/full_automated_installation.py delete mode 100644 examples/guided.py create mode 100644 examples/interactive_installation.py create mode 100644 examples/mac_address_installation.py delete mode 100644 examples/minimal.py create mode 100644 examples/minimal_installation.py delete mode 100644 examples/only_hd.py create mode 100644 examples/only_hd_installation.py delete mode 100644 examples/swiss.py delete mode 100644 examples/unattended.py create mode 100644 mypy-strict.ini create mode 100644 mypy.ini delete mode 100644 profiles/52-54-00-12-34-56.py delete mode 100644 profiles/__init__.py delete mode 100644 profiles/applications/__init__.py delete mode 100644 profiles/applications/awesome.py delete mode 100644 profiles/applications/cockpit.py delete mode 100644 profiles/applications/docker.py delete mode 100644 profiles/applications/httpd.py delete mode 100644 profiles/applications/lighttpd.py delete mode 100644 profiles/applications/mariadb.py delete mode 100644 profiles/applications/nginx.py delete mode 100644 profiles/applications/pipewire.py delete mode 100644 profiles/applications/postgresql.py delete mode 100644 profiles/applications/sshd.py delete mode 100644 profiles/applications/tomcat.py delete mode 100644 profiles/awesome.py delete mode 100644 profiles/bspwm.py delete mode 100644 profiles/budgie.py delete mode 100644 profiles/cinnamon.py delete mode 100644 profiles/cutefish.py delete mode 100644 profiles/deepin.py delete mode 100644 profiles/desktop.py delete mode 100644 profiles/enlightenment.py delete mode 100644 profiles/gnome.py delete mode 100644 profiles/i3.py delete mode 100644 profiles/kde.py delete mode 100644 profiles/lxqt.py delete mode 100644 profiles/mate.py delete mode 100644 profiles/minimal.py delete mode 100644 profiles/qtile.py delete mode 100644 profiles/server.py delete mode 100644 profiles/sway.py delete mode 100644 profiles/xfce4.py delete mode 100644 profiles/xorg.py diff --git a/.github/workflows/iso-build.yaml b/.github/workflows/iso-build.yaml index ab4e6f5f..00e2c13f 100644 --- a/.github/workflows/iso-build.yaml +++ b/.github/workflows/iso-build.yaml @@ -32,17 +32,7 @@ jobs: - run: cat /etc/os-release - run: pacman-key --init - run: pacman --noconfirm -Sy archlinux-keyring - - run: mkdir -p /tmp/archlive/airootfs/root/archinstall-git; cp -r . /tmp/archlive/airootfs/root/archinstall-git - - run: echo "pip uninstall archinstall -y; cd archinstall-git; rm -rf dist; python -m build -n; pip install dist/archinstall*.whl" > /tmp/archlive/airootfs/root/.zprofile - - run: echo "echo \"This is an unofficial ISO for development and testing of archinstall. No support will be provided.\"" >> /tmp/archlive/airootfs/root/.zprofile - - run: echo "echo \"This ISO was built from Git SHA $GITHUB_SHA\"" >> /tmp/archlive/airootfs/root/.zprofile - - run: echo "echo \"Type archinstall to launch the installer.\"" >> /tmp/archlive/airootfs/root/.zprofile - - run: cat /tmp/archlive/airootfs/root/.zprofile - - run: pacman -Sy; pacman --noconfirm -S git archiso - - run: cp -r /usr/share/archiso/configs/releng/* /tmp/archlive - - run: echo -e "git\npython\npython-pip\npython-build\npython-flit\npython-setuptools\npython-wheel" >> /tmp/archlive/packages.x86_64 - - run: find /tmp/archlive - - run: cd /tmp/archlive; mkarchiso -v -w work/ -o out/ ./ + - run: ./build_iso.sh - uses: actions/upload-artifact@v3 with: name: Arch Live ISO diff --git a/.github/workflows/mypy.yaml b/.github/workflows/mypy.yaml index 20c98f3b..8689570f 100644 --- a/.github/workflows/mypy.yaml +++ b/.github/workflows/mypy.yaml @@ -15,8 +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/abstract_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 - archinstall/lib/disk/blockdevice.py archinstall/lib/user_interaction/subvolume_config.py archinstall/lib/disk/btrfs/btrfs_helpers.py - archinstall/lib/translationhandler.py archinstall/lib/disk/diskinfo.py archinstall/lib/menu/table_selection_menu.py archinstall/lib/hsm - archinstall/lib/disk/encryption.py archinstall/lib/models/disk_encryption.py + run: mypy --config-file mypy.ini diff --git a/.github/workflows/python-build.yml b/.github/workflows/python-build.yml index 647ad70e..f98ce160 100644 --- a/.github/workflows/python-build.yml +++ b/.github/workflows/python-build.yml @@ -7,20 +7,37 @@ on: [ push, pull_request ] jobs: deploy: runs-on: ubuntu-latest + container: + image: archlinux:latest + options: --privileged steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: - python-version: '3.x' - - name: Install dependencies + python-version: '3.10' + - name: Prepare arch + run: | + pacman-key --init + pacman --noconfirm -Sy archlinux-keyring + pacman --noconfirm -Sy python-pyparted pkgconfig gcc + - name: Install build dependencies run: | python -m pip install --upgrade pip - pip install build twine + pip install --upgrade build twine wheel setuptools installer + pip uninstall archinstall -y + - name: Install package dependencies + run: | + pip install --upgrade simple-term-menu pyparted - name: Build archinstall + run: python -m build --wheel --no-isolation + - name: Install archinstall + run: python -m installer dist/*.whl + - name: Run archinstall run: | - python -m build . --wheel + python -V + archinstall -v - uses: actions/upload-artifact@v3 with: name: archinstall - path: dist/* \ No newline at end of file + path: dist/* diff --git a/.gitignore b/.gitignore index 40e00e87..18173914 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,5 @@ venv **/cmd_history.txt **/*.*~ /*.sig -/*.json \ No newline at end of file +/*.json +requirements.txt diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..87128289 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,42 @@ +default_stages: ['commit'] +repos: + - repo: https://github.com/pycqa/autoflake + rev: v2.0.2 + hooks: + - id: autoflake + args: [ + '--in-place', + '--remove-all-unused-imports', + '--ignore-init-module-imports' + ] + files: \.py$ + require_serial: true + fail_fast: true + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + # general hooks: + - id: check-added-large-files # Prevent giant files from being committed + args: ['--maxkb=5000'] + - id: check-merge-conflict # Check for files that contain merge conflict strings + - id: check-symlinks # Checks for symlinks which do not point to anything + - id: check-yaml # Attempts to load all yaml files to verify syntax + - id: destroyed-symlinks # Detects symlinks which are changed to regular files + - id: detect-private-key # Checks for the existence of private keys + - id: end-of-file-fixer # Makes sure files end in a newline and only a newline + - id: trailing-whitespace # Trims trailing whitespace + # Python specific hooks: + - id: check-ast # Simply check whether files parse as valid python + - id: check-docstring-first # Checks for a common error of placing code before the docstring + - repo: https://github.com/pycqa/flake8 + rev: 6.0.0 + hooks: + - id: flake8 + args: [--config=.flake8] + fail_fast: true + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.1.1 + hooks: + - id: mypy + args: [--config=mypy.ini] + fail_fast: true diff --git a/PKGBUILD b/PKGBUILD index 08c2001b..69f81f49 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -13,6 +13,7 @@ license=(GPL3) depends=( 'python' 'systemd' + 'python-pyparted' ) makedepends=( 'python-setuptools' @@ -48,7 +49,6 @@ prepare() { # use real directories for examples and profiles, as symlinks do not work rm -fv $pkgname/{examples,profiles} - mv -v examples profiles $pkgname/ } build() { diff --git a/README.md b/README.md index d83a8901..517621bc 100644 --- a/README.md +++ b/README.md @@ -26,44 +26,60 @@ Assuming you are on an Arch Linux live-ISO: # archinstall +#### Advanced Some additional options that are not needed by most users are hidden behind the `--advanced` flag. ## Running from a declarative configuration file or URL -Prerequisites: - 1. Edit the [configuration file](https://github.com/archlinux/archinstall/blob/master/examples/config-sample.json) according to your requirements. +`archinstall` can be run with a JSON configuration file. There are 2 different configuration files to consider, +the `user_configuration.json` contains all general installation configuration, whereas the `user_credentials.json` +contains the sensitive user configuration such as user password, root password and encryption password. -Assuming you are on a Arch Linux live-ISO and booted into EFI mode. +An example of the user configuration file can be found here +[configuration file](https://github.com/archlinux/archinstall/blob/master/examples/config-sample.json) +and example of the credentials configuration here +[credentials file](https://github.com/archlinux/archinstall/blob/master/examples/creds-sample.json). - # archinstall --config --disk-layout --creds +**HINT:** The configuration files can be auto-generated by starting `archisntall`, configuring all desired menu +points and then going to `Save configuration`. + +To load the configuration file into `archinstall` run the following command +``` +archinstall --config --creds +``` # Available Languages -Archinstall is available in different languages which have been contributed and are maintained by the community. +Archinstall is available in different languages which have been contributed and are maintained by the community. Current translations are listed below and vary in the amount of translations per language ``` English -Deutsch -Español -Français +Arabic +Brazilian Portuguese +Czech +Dutch +French +Georgian +German Indonesian -Italiano -Nederlands -Polskie -Português do Brasil -Português -Svenska -Türkçe -čeština -Русский -اردو -Ελληνικά -தமிழ் +Italian +Korean +Modern Greek +Polish +Portuguese +Russian +Spanish +Swedish +Tamil +Turkish +Ukrainian +Urdu ``` -Any contributions to the translations are more than welcome, and to get started please follow [the guide](https://github.com/archlinux/archinstall/blob/master/archinstall/locales/README.md) +Any contributions to the translations are more than welcome, +to get started please follow [the guide](https://github.com/archlinux/archinstall/blob/master/archinstall/locales/README.md) -# Help? +# Help or Issues Submit an issue here on GitHub, or submit a post in the discord help channel.
When doing so, attach the `/var/log/archinstall/install.log` to the issue ticket. This helps us help you! @@ -86,73 +102,111 @@ Therefore, Archinstall will try its best to not introduce any breaking changes e # Scripting your own installation -You could just copy [guided.py](https://github.com/archlinux/archinstall/blob/master/examples/guided.py) as a starting point. - -However, assuming you're building your own ISO and want to create an automated installation process, or you want to install virtual machines onto local disk images, here is a [minimal example](https://github.com/archlinux/archinstall/blob/master/examples/minimal.py) of how to install using archinstall as a Python library:
- -```python -import archinstall, getpass - -# Select a harddrive and a disk password -harddrive = archinstall.select_disk(archinstall.all_blockdevices(partitions=False)) -disk_password = getpass.getpass(prompt='Disk password (won\'t echo): ') - -# We disable safety precautions in the library that protects the partitions -harddrive.keep_partitions = False - -# First, we configure the basic filesystem layout -with archinstall.Filesystem(harddrive, archinstall.GPT) as fs: - # We create a filesystem layout that will use the entire drive - # (this is a helper function, you can partition manually as well) - fs.use_entire_disk(root_filesystem_type='btrfs') - - boot = fs.find_partition('/boot') - root = fs.find_partition('/') - - boot.format('vfat') - - # Set the flag for encrypted to allow for encryption and then encrypt - root.encrypted = True - root.encrypt(password=disk_password) +## Scripting interactive installation -with archinstall.luks2(root, 'luksloop', disk_password) as unlocked_root: - unlocked_root.format(root.filesystem) - unlocked_root.mount('/mnt') +There are some examples in the `examples/` directory that should serve as a starting point. - boot.mount('/mnt/boot') +The following is a small example of how to script your own *interative* installation: -with archinstall.Installer('/mnt') as installation: - if installation.minimal_installation(hostname='minimal-arch'): - installation.add_bootloader() - - installation.add_additional_packages(['nano', 'wget', 'git']) - - # Optionally, install a profile of choice. - # In this case, we install a minimal profile that is empty - installation.install_profile('minimal') - - user = User('devel', 'devel', False) - installation.create_users(user) - installation.user_set_pw('root', 'airoot') +```python +from pathlib import Path + +from archinstall import Installer, ProfileConfiguration, profile_handler, User +from archinstall.default_profiles.minimal import MinimalProfile +from archinstall.lib.disk.device_model import FilesystemType +from archinstall.lib.disk.encryption_menu import DiskEncryptionMenu +from archinstall.lib.disk.filesystem import FilesystemHandler +from archinstall.lib.user_interaction.disk_conf import select_disk_config + +fs_type = FilesystemType('ext4') + +# Select a device to use for the installation +disk_config = select_disk_config() + +# Optional: ask for disk encryption configuration +data_store = {} +disk_encryption = DiskEncryptionMenu(disk_config.device_modifications, data_store).run() + +# initiate file handler with the disk config and the optional disk encryption config +fs_handler = FilesystemHandler(disk_config, disk_encryption) + +# perform all file operations +# WARNING: this will potentially format the filesystem and delete all data +fs_handler.perform_filesystem_operations() + +mountpoint = Path('/tmp') + +with Installer( + mountpoint, + disk_config, + disk_encryption=disk_encryption, + kernels=['linux'] +) as installation: + installation.mount_ordered_layout() + installation.minimal_installation(hostname='minimal-arch') + installation.add_additional_packages(['nano', 'wget', 'git']) + + # Optionally, install a profile of choice. + # In this case, we install a minimal profile that is empty + profile_config = ProfileConfiguration(MinimalProfile()) + profile_handler.install_profile_config(installation, profile_config) + + user = User('archinstall', 'password', True) + installation.create_users(user) ``` This installer will perform the following: -* Prompt the user to select a disk and disk-password -* Proceed to wipe the selected disk with a `GPT` partition table on a UEFI system and MBR on a BIOS system. -* Sets up a default 100% used disk with encryption. +* Prompt the user to configurate the disk partitioning +* Prompt the user to setup disk encryption +* Create a file handler instance for the configured disk and the optional disk encryption +* Perform the disk operations (WARNING: this will potentially format the disks and erase all data) * Installs a basic instance of Arch Linux *(base base-devel linux linux-firmware btrfs-progs efibootmgr)* * Installs and configures a bootloader to partition 0 on uefi. On BIOS, it sets the root to partition 0. * Install additional packages *(nano, wget, git)* +* Create a new user > **Creating your own ISO with this script on it:** Follow [ArchISO](https://wiki.archlinux.org/index.php/archiso)'s guide on how to create your own ISO. +## Script non-interactive automated installation + +For an example of a fully scripted, automated installation please see the example +[full_automated_installation.py](https://github.com/archlinux/archinstall/blob/master/examples/full_automated_installation.py) + ## Unattended installation based on MAC address -Archinstall comes with an [unattended](https://github.com/archlinux/archinstall/blob/master/examples/unattended.py) example which will look for a matching profile for the machine it is being run on, based on any local MAC address. -For instance, if the machine that [unattended](https://github.com/archlinux/archinstall/blob/master/examples/unattended.py) is run on has the MAC address `52:54:00:12:34:56` it will look for a profile called [profiles/52-54-00-12-34-56.py](https://github.com/archlinux/archinstall/blob/master/profiles/52-54-00-12-34-56.py). +Archinstall comes with an [unattended](https://github.com/archlinux/archinstall/blob/master/examples/mac_address_installation.py) +example which will look for a matching profile for the machine it is being run on, based on any local MAC address. +For instance, if the machine the code is executed on has the MAC address `52:54:00:12:34:56` it will look for a profile called +[52-54-00-12-34-56.py](https://github.com/archlinux/archinstall/default_profiles/tailored.py). If it's found, the unattended installation will commence and source that profile as its installation procedure. +# Profiles + +`archinstall` ships with a set of pre-defined profiles that can be chosen during the installation process. + +| *Desktop* | *Server* | +|---------------|------------| +| Awesome | Cockpit | +| Bspwm | Docker | +| Budgie | Lighttpd | +| Cinnamon | Mariadb | +| Cutefish | Nginx | +| Deepin | Postgresql | +| Enlightenment | Tomcat | +| Gnome | httpd | +| Kde | sshd | +| Lxqt | | +| Mate | | +| Qtile | | +| Sway | | +| Xfce4 | | +| i3-wm | | + +The definitions of the profiles and what packages they will install can be seen directly in the menu or +[default profiles](https://github.com/archlinux/archinstall/default_profiles) + + # Testing ## Using a Live ISO Image @@ -167,8 +221,7 @@ you can replace the version of archinstall with a new version and run that with 4. Now clone the latest repository with `git clone https://github.com/archlinux/archinstall` 5. Enter the repository with `cd archinstall` *At this stage, you can choose to check out a feature branch for instance with `git checkout v2.3.1-rc1`* -6. Build the project and install it using `python setup.py install` - *If you get a 'No Module named setuptools' error, run `pacman -S python-setuptools`* +6. Build the project and install it using `pip install` After this, running archinstall with `python -m archinstall` will run against whatever branch you chose in step 5. diff --git a/archinstall/__init__.py b/archinstall/__init__.py index 9de4a3ec..8cb6ced9 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -1,8 +1,12 @@ """Arch Linux installer - guided, templates etc.""" -import typing +import importlib from argparse import ArgumentParser, Namespace -from .lib.disk import * +from .lib import disk +from .lib import menu +from .lib import models as models +from .lib import packages + from .lib.exceptions import * from .lib.general import * from .lib.hardware import * @@ -10,41 +14,20 @@ from .lib.installer import __packages__, Installer, accessibility_tools_in_use 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 ( - VersionDef, - PackageSearchResult, - PackageSearch, - LocalPackage -) -from .lib.packages.packages import ( - group_search, - package_search, - find_package, - find_packages, - installed_package, - validate_package_list, -) -from .lib.profiles import * +from archinstall.lib.profile.profiles_handler import ProfileHandler, profile_handler +from .lib.profile.profile_menu import ProfileConfiguration from .lib.services import * from .lib.storage import * from .lib.systemd import * from .lib.user_interaction import * -from .lib.menu import Menu -from .lib.menu.list_manager import ListManager -from .lib.menu.text_input import TextInput -from .lib.menu.global_menu import GlobalMenu -from .lib.menu.abstract_menu import ( - Selector, - AbstractMenu -) +from .lib.global_menu import GlobalMenu from .lib.translationhandler import TranslationHandler, DeferredTranslation -from .lib.plugins import plugins, load_plugin # This initiates the plugin loading ceremony +from .lib.plugins import plugins, load_plugin # This initiates the plugin loading ceremony from .lib.configuration import * -from .lib.udev import udevadm_info + + parser = ArgumentParser() __version__ = "2.5.4" @@ -66,8 +49,6 @@ def define_arguments(): parser.add_argument("-v", "--version", action="version", version="%(prog)s " + __version__) parser.add_argument("--config", nargs="?", help="JSON configuration file or URL") parser.add_argument("--creds", nargs="?", help="JSON credentials configuration file") - parser.add_argument("--disk_layouts","--disk_layout","--disk-layouts","--disk-layout",nargs="?", - help="JSON disk layout file") parser.add_argument("--silent", action="store_true", help="WARNING: Disables all prompts for input and confirmation. If no configuration is provided, this is ignored") parser.add_argument("--dry-run", "--dry_run", action="store_true", @@ -79,6 +60,7 @@ def define_arguments(): parser.add_argument("--no-pkg-lookups", action="store_true", default=False, help="Disabled package validation specifically prior to starting installation.") parser.add_argument("--plugin", nargs="?", type=str) + def parse_unspecified_argument_list(unknowns :list, multiple :bool = False, error :bool = False) -> dict: """We accept arguments not defined to the parser. (arguments "ad hoc"). Internally argparse return to us a list of words so we have to parse its contents, manually. @@ -129,7 +111,8 @@ def parse_unspecified_argument_list(unknowns :list, multiple :bool = False, erro print(f" We ignore the entry {element} as it isn't related to any argument") return config -def cleanup_empty_args(args :typing.Union[Namespace, dict]) -> dict: + +def cleanup_empty_args(args: Union[Namespace, dict]) -> dict: """ Takes arguments (dictionary or argparse Namespace) and removes any None values. This ensures clean mergers during dict.update(args) @@ -161,7 +144,7 @@ def get_arguments() -> Dict[str, Any]: 3) Amend Change whatever is needed on the configuration dictionary (it could be done in post_process_arguments but this ougth to be left to changes anywhere else in the code, not in the arguments dictionary """ - config = {} + config: Dict[str, Any] = {} args, unknowns = parser.parse_known_args() # preprocess the JSON files. # TODO Expand the url access to the other JSON file arguments ? @@ -174,15 +157,15 @@ def get_arguments() -> Dict[str, Any]: exit(1) # load the parameters. first the known, then the unknowns - args = cleanup_empty_args(args) - config.update(args) + clean_args = cleanup_empty_args(args) + config.update(clean_args) config.update(parse_unspecified_argument_list(unknowns)) # amend the parameters (check internal consistency) # Installation can't be silent if config is not passed - if args.get('config') is None: + if clean_args.get('config') is None: config["silent"] = False else: - config["silent"] = args.get('silent') + config["silent"] = clean_args.get('silent') # avoiding a compatibility issue if 'dry-run' in config: @@ -190,29 +173,24 @@ def get_arguments() -> Dict[str, Any]: return config + def load_config(): """ refine and set some arguments. Formerly at the scripts """ from .lib.models import NetworkConfiguration + arguments.setdefault('sys-language', 'en_US') + arguments.setdefault('sys-encoding', 'utf-8') + if (archinstall_lang := arguments.get('archinstall-language', None)) is not None: arguments['archinstall-language'] = TranslationHandler().get_language_by_name(archinstall_lang) - if arguments.get('harddrives', None) is not None: - if type(arguments['harddrives']) is str: - arguments['harddrives'] = arguments['harddrives'].split(',') - arguments['harddrives'] = [BlockDevice(BlockDev) for BlockDev in arguments['harddrives']] - # Temporarily disabling keep_partitions if config file is loaded - # Temporary workaround to make Desktop Environments work - - if arguments.get('profile', None) is not None: - if type(arguments.get('profile', None)) is dict: - arguments['profile'] = Profile(None, arguments.get('profile', None)['path']) - else: - arguments['profile'] = Profile(None, arguments.get('profile', None)) + if disk_config := arguments.get('disk_config', {}): + arguments['disk_config'] = disk.DiskLayoutConfiguration.parse_arg(disk_config) - storage['_desktop_profile'] = arguments.get('desktop-environment', None) + if profile_config := arguments.get('profile_config', None): + arguments['profile_config'] = ProfileConfiguration.parse_arg(profile_config) if arguments.get('mirror-region', None) is not None: if type(arguments.get('mirror-region', None)) is dict: @@ -221,12 +199,6 @@ def load_config(): selected_region = arguments.get('mirror-region', None) arguments['mirror-region'] = {selected_region: list_mirrors()[selected_region]} - arguments.setdefault('sys-language', 'en_US') - arguments.setdefault('sys-encoding', 'utf-8') - - if arguments.get('gfx_driver', None) is not None: - storage['gfx_driver_packages'] = AVAILABLE_GFX_DRIVERS.get(arguments.get('gfx_driver', None), None) - if arguments.get('servers', None) is not None: storage['_selected_servers'] = arguments.get('servers', None) @@ -240,10 +212,13 @@ def load_config(): superusers = arguments.get('!superusers', None) arguments['!users'] = User.parse_arguments(users, superusers) - if arguments.get('disk_encryption', None) is not None and arguments.get('disk_layouts', None) is not None: + if arguments.get('bootloader', None) is not None: + arguments['bootloader'] = Bootloader.from_arg(arguments['bootloader']) + + if arguments.get('disk_encryption', None) is not None and disk_config is not None: password = arguments.get('encryption_password', '') - arguments['disk_encryption'] = DiskEncryption.parse_arg( - arguments['disk_layouts'], + arguments['disk_encryption'] = disk.DiskEncryption.parse_arg( + arguments['disk_config'], arguments['disk_encryption'], password ) @@ -251,8 +226,8 @@ def load_config(): def post_process_arguments(arguments): storage['arguments'] = arguments - if arguments.get('mount_point'): - storage['MOUNT_POINT'] = arguments['mount_point'] + if mountpoint := arguments.get('mount_point', None): + storage['MOUNT_POINT'] = Path(mountpoint) if arguments.get('debug', False): log(f"Warning: --debug mode will write certain credentials to {storage['LOG_PATH']}/{storage['LOG_FILE']}!", fg="red", level=logging.WARNING) @@ -260,53 +235,31 @@ def post_process_arguments(arguments): if arguments.get('plugin', None): load_plugin(arguments['plugin']) - if arguments.get('disk_layouts', None) is not None: - layout_storage = {} - if not json_stream_to_structure('--disk_layouts',arguments['disk_layouts'],layout_storage): - exit(1) - else: - if arguments.get('harddrives') is None: - arguments['harddrives'] = [disk for disk in layout_storage] - # backward compatibility. Change partition.format for partition.wipe - for disk in layout_storage: - for i, partition in enumerate(layout_storage[disk].get('partitions',[])): - if 'format' in partition: - partition['wipe'] = partition['format'] - del partition['format'] - elif 'btrfs' in partition: - partition['btrfs']['subvolumes'] = Subvolume.parse_arguments(partition['btrfs']['subvolumes']) - arguments['disk_layouts'] = layout_storage - load_config() define_arguments() -arguments = get_arguments() +arguments: Dict[str, Any] = get_arguments() post_process_arguments(arguments) + # @archinstall.plugin decorator hook to programmatically add -# plugins in runtime. Useful in profiles and other things. +# plugins in runtime. Useful in profiles_bck and other things. def plugin(f, *args, **kwargs): plugins[f.__name__] = f def run_as_a_module(): """ - Since we're running this as a 'python -m archinstall' module OR - a nuitka3 compiled version of the project. - This function and the file __main__ acts as a entry point. + This can either be run as the compiled and installed application: python setup.py install + OR straight as a module: python -m archinstall + In any case we will be attempting to load the provided script to be run from the scripts/ folder """ + script = arguments.get('script', None) - # Add another path for finding profiles, so that list_profiles() in Script() can find guided.py, unattended.py etc. - storage['PROFILE_PATH'].append(os.path.abspath(f'{os.path.dirname(__file__)}/examples')) - try: - script = Script(arguments.get('script', None)) - except ProfileNotFound as err: - print(f"Couldn't find file: {err}") - sys.exit(1) - - os.chdir(os.path.abspath(os.path.dirname(__file__))) + if script is None: + print('No script to run provided') - # Remove the example directory from the PROFILE_PATH, to avoid guided.py etc shows up in user input questions. - storage['PROFILE_PATH'].pop() - script.execute() + mod_name = f'archinstall.scripts.{script}' + # by loading the module we'll automatically run the script + importlib.import_module(mod_name) diff --git a/archinstall/__main__.py b/archinstall/__main__.py index e125930f..69a79855 100644 --- a/archinstall/__main__.py +++ b/archinstall/__main__.py @@ -5,9 +5,13 @@ import pathlib # Load .git version before the builtin version if pathlib.Path('./archinstall/__init__.py').absolute().exists(): spec = importlib.util.spec_from_file_location("archinstall", "./archinstall/__init__.py") + + if spec is None or spec.loader is None: + raise ValueError('Could not retrieve spec from file: archinstall/__init__.py') + archinstall = importlib.util.module_from_spec(spec) sys.modules["archinstall"] = archinstall - spec.loader.exec_module(sys.modules["archinstall"]) + spec.loader.exec_module(archinstall) else: import archinstall diff --git a/archinstall/default_profiles/__init__.py b/archinstall/default_profiles/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/archinstall/default_profiles/applications/__init__.py b/archinstall/default_profiles/applications/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/archinstall/default_profiles/applications/pipewire.py b/archinstall/default_profiles/applications/pipewire.py new file mode 100644 index 00000000..5d2e5ea3 --- /dev/null +++ b/archinstall/default_profiles/applications/pipewire.py @@ -0,0 +1,40 @@ +from typing import List, Union, Any, TYPE_CHECKING + +import archinstall + +from archinstall.default_profiles.profile import Profile, ProfileType +from archinstall.lib.models import User + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +class PipewireProfile(Profile): + def __init__(self): + super().__init__('Pipewire', ProfileType.Application) + + @property + def packages(self) -> List[str]: + return [ + 'pipewire', + 'pipewire-alsa', + 'pipewire-jack', + 'pipewire-pulse', + 'gst-plugin-pipewire', + 'libpulse', + 'wireplumber' + ] + + def _enable_pipewire_for_all(self, install_session: 'Installer'): + users: Union[User, List[User]] = archinstall.arguments.get('!users', None) + if not isinstance(users, list): + users = [users] + + for user in users: + install_session.arch_chroot('systemctl enable --user pipewire-pulse.service', run_as=user.username) + + def install(self, install_session: 'Installer'): + super().install(install_session) + install_session.add_additional_packages(self.packages) + self._enable_pipewire_for_all(install_session) diff --git a/archinstall/default_profiles/custom.py b/archinstall/default_profiles/custom.py new file mode 100644 index 00000000..f7e100ed --- /dev/null +++ b/archinstall/default_profiles/custom.py @@ -0,0 +1,218 @@ +from typing import List, Dict, Optional, TYPE_CHECKING, Any + +from ..lib import menu +from archinstall.lib.output import log, FormattedOutput +from archinstall.lib.profile.profiles_handler import profile_handler +from archinstall.default_profiles.profile import Profile, ProfileType, SelectResult, ProfileInfo, TProfile + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +class CustomProfileList(menu.ListManager): + def __init__(self, prompt: str, profiles: List[TProfile]): + self._actions = [ + str(_('Add profile')), + str(_('Edit profile')), + str(_('Delete profile')) + ] + super().__init__(prompt, profiles, [self._actions[0]], self._actions[1:]) + + def reformat(self, data: List[TProfile]) -> Dict[str, Optional[TProfile]]: + table = FormattedOutput.as_table(data) + rows = table.split('\n') + + # these are the header rows of the table and do not map to any profile obviously + # we're adding 2 spaces as prefix because the menu selector '> ' will be put before + # the selectable rows so the header has to be aligned + display_data: Dict[str, Optional[TProfile]] = {f' {rows[0]}': None, f' {rows[1]}': None} + + for row, profile in zip(rows[2:], data): + row = row.replace('|', '\\|') + display_data[row] = profile + + return display_data + + def selected_action_display(self, profile: TProfile) -> str: + return profile.name + + def handle_action( + self, + action: str, + entry: Optional['CustomTypeProfile'], + data: List['CustomTypeProfile'] + ) -> List['CustomTypeProfile']: + if action == self._actions[0]: # add + new_profile = self._add_profile() + if new_profile is not None: + # in case a profile with the same name as an existing profile + # was created we'll replace the existing one + data = [d for d in data if d.name != new_profile.name] + data += [new_profile] + elif entry is not None: + if action == self._actions[1]: # edit + new_profile = self._add_profile(entry) + if new_profile is not None: + # we'll remove the original profile and add the modified version + data = [d for d in data if d.name != entry.name and d.name != new_profile.name] + data += [new_profile] + elif action == self._actions[2]: # delete + data = [d for d in data if d != entry] + + return data + + def _is_new_profile_name(self, name: str) -> bool: + existing_profile = profile_handler.get_profile_by_name(name) + if existing_profile is not None and existing_profile.profile_type != ProfileType.CustomType: + return False + return True + + def _add_profile(self, editing: Optional['CustomTypeProfile'] = None) -> Optional['CustomTypeProfile']: + name_prompt = '\n\n' + str(_('Profile name: ')) + + while True: + profile_name = menu.TextInput(name_prompt, editing.name if editing else '').run().strip() + + if not profile_name: + return None + + if not self._is_new_profile_name(profile_name): + error_prompt = str(_("The profile name you entered is already in use. Try again")) + print(error_prompt) + else: + break + + packages_prompt = str(_('Packages to be install with this profile (space separated, leave blank to skip): ')) + edit_packages = ' '.join(editing.packages) if editing else '' + packages = menu.TextInput(packages_prompt, edit_packages).run().strip() + + services_prompt = str(_('Services to be enabled with this profile (space separated, leave blank to skip): ')) + edit_services = ' '.join(editing.services) if editing else '' + services = menu.TextInput(services_prompt, edit_services).run().strip() + + choice = menu.Menu( + str(_('Should this profile be enabled for installation?')), + menu.Menu.yes_no(), + skip=False, + default_option=menu.Menu.no(), + clear_screen=False, + show_search_hint=False + ).run() + + enable_profile = True if choice.value == menu.Menu.yes() else False + + profile = CustomTypeProfile( + profile_name, + enabled=enable_profile, + packages=packages.split(' '), + services=services.split(' ') + ) + + return profile + + +# TODO +# Still needs some ironing out +class CustomProfile(): + def __init__(self): + super().__init__( + 'Custom', + ProfileType.Custom, + description=str(_('Create your own')) + ) + + def json(self) -> Dict[str, Any]: + data: Dict[str, Any] = {'main': self.name, 'gfx_driver': self.gfx_driver, 'custom': []} + + for profile in self._current_selection: + data['custom'].append({ + 'name': profile.name, + 'packages': profile.packages, + 'services': profile.services, + 'enabled': profile.custom_enabled + }) + + return data + + def do_on_select(self) -> SelectResult: + custom_profile_list = CustomProfileList('', profile_handler.get_custom_profiles()) + custom_profiles = custom_profile_list.run() + + # we'll first remove existing custom default_profiles with + # the same name and then add the new ones this + # will avoid errors of default_profiles with duplicate naming + profile_handler.remove_custom_profiles(custom_profiles) + profile_handler.add_custom_profiles(custom_profiles) + + self.set_current_selection(custom_profiles) + + if custom_profile_list.is_last_choice_cancel(): + return SelectResult.SameSelection + + enabled_profiles = [p for p in self._current_selection if p.custom_enabled] + # in case we only created inactive default_profiles we wanna store them but + # we want to reset the original setting + if not enabled_profiles: + return SelectResult.ResetCurrent + + return SelectResult.NewSelection + + def post_install(self, install_session: 'Installer'): + for profile in self._current_selection: + profile.post_install(install_session) + + def install(self, install_session: 'Installer'): + driver_packages = self.gfx_driver_packages() + install_session.add_additional_packages(driver_packages) + + for profile in self._current_selection: + if profile.custom_enabled: + log(f'Installing custom profile {profile.name}...') + + install_session.add_additional_packages(profile.packages) + install_session.enable_service(profile.services) + + profile.install(install_session) + + def info(self) -> Optional[ProfileInfo]: + enabled_profiles = [p for p in self._current_selection if p.custom_enabled] + if enabled_profiles: + details = ', '.join([p.name for p in enabled_profiles]) + gfx_driver = self.gfx_driver + return ProfileInfo(self.name, details, gfx_driver) + + return None + + def reset(self): + for profile in self._current_selection: + profile.set_enabled(False) + + self.gfx_driver = None + + +class CustomTypeProfile(Profile): + def __init__( + self, + name: str, + enabled: bool = False, + packages: List[str] = [], + services: List[str] = [] + ): + super().__init__( + name, + ProfileType.CustomType, + packages=packages, + services=services, + support_gfx_driver=True + ) + + self.custom_enabled = enabled + + def json(self) -> Dict[str, Any]: + return { + 'name': self.name, + 'packages': self.packages, + 'services': self.services, + 'enabled': self.custom_enabled + } diff --git a/archinstall/default_profiles/desktop.py b/archinstall/default_profiles/desktop.py new file mode 100644 index 00000000..2351bd08 --- /dev/null +++ b/archinstall/default_profiles/desktop.py @@ -0,0 +1,87 @@ +from typing import Any, TYPE_CHECKING, List, Optional, Dict + +from archinstall.lib import menu +from archinstall.lib.output import log +from archinstall.lib.profile.profiles_handler import profile_handler +from archinstall.default_profiles.profile import Profile, ProfileType, SelectResult, GreeterType + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +class DesktopProfile(Profile): + def __init__(self, current_selection: List[Profile] = []): + super().__init__( + 'Desktop', + ProfileType.Desktop, + description=str(_('Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway')), + current_selection=current_selection, + support_greeter=True + ) + + @property + def packages(self) -> List[str]: + return [ + 'nano', + 'vim', + 'openssh', + 'htop', + 'wget', + 'iwd', + 'wireless_tools', + 'wpa_supplicant', + 'smartmontools', + 'xdg-utils' + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + combined_greeters: Dict[GreeterType, int] = {} + for profile in self.current_selection: + if profile.default_greeter_type: + combined_greeters.setdefault(profile.default_greeter_type, 0) + combined_greeters[profile.default_greeter_type] += 1 + + if len(combined_greeters) >= 1: + return list(combined_greeters)[0] + + return None + + def _do_on_select_profiles(self): + for profile in self.current_selection: + profile.do_on_select() + + def do_on_select(self) -> SelectResult: + choice = profile_handler.select_profile( + profile_handler.get_desktop_profiles(), + self._current_selection, + title=str(_('Select your desired desktop environment')), + multi=True + ) + + match choice.type_: + case menu.MenuSelectionType.Selection: + self.set_current_selection(choice.value) # type: ignore + self._do_on_select_profiles() + return SelectResult.NewSelection + case menu.MenuSelectionType.Skip: + return SelectResult.SameSelection + case menu.MenuSelectionType.Reset: + return SelectResult.ResetCurrent + + def post_install(self, install_session: 'Installer'): + for profile in self._current_selection: + profile.post_install(install_session) + + def install(self, install_session: 'Installer'): + # Install common packages for all desktop environments + install_session.add_additional_packages(self.packages) + + for profile in self._current_selection: + log(f'Installing profile {profile.name}...') + + install_session.add_additional_packages(profile.packages) + install_session.enable_service(profile.services) + + profile.install(install_session) diff --git a/archinstall/default_profiles/desktops/__init__.py b/archinstall/default_profiles/desktops/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/archinstall/default_profiles/desktops/awesome.py b/archinstall/default_profiles/desktops/awesome.py new file mode 100644 index 00000000..bb481914 --- /dev/null +++ b/archinstall/default_profiles/desktops/awesome.py @@ -0,0 +1,36 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +class AwesomeProfile(XorgProfile): + def __init__(self): + super().__init__('Awesome', ProfileType.WindowMgr, description='') + + @property + def packages(self) -> List[str]: + return ['alacritty'] + + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + return text + '\n' + self.packages_text() + + def install(self, install_session: 'Installer'): + super().install(install_session) + + # TODO: Copy a full configuration to ~/.config/awesome/rc.lua instead. + with open(f"{install_session.target}/etc/xdg/awesome/rc.lua", 'r') as fh: + awesome_lua = fh.read() + + # Replace xterm with alacritty for a smoother experience. + awesome_lua = awesome_lua.replace('"xterm"', '"alacritty"') + + with open(f"{install_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) diff --git a/archinstall/default_profiles/desktops/bspwm.py b/archinstall/default_profiles/desktops/bspwm.py new file mode 100644 index 00000000..f3bc982d --- /dev/null +++ b/archinstall/default_profiles/desktops/bspwm.py @@ -0,0 +1,30 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class BspwmProfile(XorgProfile): + def __init__(self): + super().__init__('Bspwm', ProfileType.WindowMgr, description='') + + @property + def packages(self) -> List[str]: + return [ + 'bspwm', + 'sxhkd', + 'dmenu', + 'xdo', + 'rxvt-unicode' + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm + + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/budgie.py b/archinstall/default_profiles/desktops/budgie.py new file mode 100644 index 00000000..32bd718d --- /dev/null +++ b/archinstall/default_profiles/desktops/budgie.py @@ -0,0 +1,30 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class BudgieProfile(XorgProfile): + def __init__(self): + super().__init__('Budgie', ProfileType.DesktopEnv, description='') + + @property + def packages(self) -> List[str]: + return [ + "arc-gtk-theme", + "budgie", + "mate-terminal", + "nemo", + "papirus-icon-theme", + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm + + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/cinnamon.py b/archinstall/default_profiles/desktops/cinnamon.py new file mode 100644 index 00000000..22fd0d9d --- /dev/null +++ b/archinstall/default_profiles/desktops/cinnamon.py @@ -0,0 +1,31 @@ +from typing import Optional, List, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class CinnamonProfile(XorgProfile): + def __init__(self): + super().__init__('Cinnamon', ProfileType.DesktopEnv, description='') + + @property + def packages(self) -> List[str]: + return [ + "cinnamon", + "system-config-printer", + "gnome-keyring", + "gnome-terminal", + "blueberry", + "metacity" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm + + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/cutefish.py b/archinstall/default_profiles/desktops/cutefish.py new file mode 100644 index 00000000..6f88c47a --- /dev/null +++ b/archinstall/default_profiles/desktops/cutefish.py @@ -0,0 +1,31 @@ +from typing import Optional, List, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +class CutefishProfile(XorgProfile): + def __init__(self): + super().__init__('Cutefish', ProfileType.DesktopEnv, description='') + + @property + def packages(self) -> List[str]: + return [ + "cutefish", + "noto-fonts" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Sddm + + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + return text + '\n' + self.packages_text() + + def install(self, install_session: 'Installer'): + super().install(install_session) diff --git a/archinstall/default_profiles/desktops/deepin.py b/archinstall/default_profiles/desktops/deepin.py new file mode 100644 index 00000000..054c8fdf --- /dev/null +++ b/archinstall/default_profiles/desktops/deepin.py @@ -0,0 +1,28 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class DeepinProfile(XorgProfile): + def __init__(self): + super().__init__('Deepin', ProfileType.DesktopEnv, description='') + + @property + def packages(self) -> List[str]: + return [ + "deepin", + "deepin-terminal", + "deepin-editor" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm + + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/enlightenment.py b/archinstall/default_profiles/desktops/enlightenment.py new file mode 100644 index 00000000..164f64fe --- /dev/null +++ b/archinstall/default_profiles/desktops/enlightenment.py @@ -0,0 +1,27 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class EnlighenmentProfile(XorgProfile): + def __init__(self): + super().__init__('Enlightenment', ProfileType.WindowMgr, description='') + + @property + def packages(self) -> List[str]: + return [ + "enlightenment", + "terminology" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm + + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/gnome.py b/archinstall/default_profiles/desktops/gnome.py new file mode 100644 index 00000000..3cbd49bd --- /dev/null +++ b/archinstall/default_profiles/desktops/gnome.py @@ -0,0 +1,27 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class GnomeProfile(XorgProfile): + def __init__(self): + super().__init__('Gnome', ProfileType.DesktopEnv, description='') + + @property + def packages(self) -> List[str]: + return [ + 'gnome', + 'gnome-tweaks' + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Gdm + + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/i3.py b/archinstall/default_profiles/desktops/i3.py new file mode 100644 index 00000000..7c6f24ca --- /dev/null +++ b/archinstall/default_profiles/desktops/i3.py @@ -0,0 +1,33 @@ +from typing import Optional, List, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class I3wmProfile(XorgProfile): + def __init__(self): + super().__init__('i3-wm', ProfileType.WindowMgr, description='') + + @property + def packages(self) -> List[str]: + return [ + 'i3-wm', + 'i3lock', + 'i3status', + 'i3blocks', + 'xterm', + 'lightdm-gtk-greeter', + 'lightdm', + 'dmenu', + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm + + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/kde.py b/archinstall/default_profiles/desktops/kde.py new file mode 100644 index 00000000..cd02e069 --- /dev/null +++ b/archinstall/default_profiles/desktops/kde.py @@ -0,0 +1,32 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class KdeProfile(XorgProfile): + def __init__(self): + super().__init__('Kde', ProfileType.DesktopEnv, description='') + + @property + def packages(self) -> List[str]: + return [ + "plasma-meta", + "konsole", + "kwrite", + "dolphin", + "ark", + "plasma-wayland-session", + "egl-wayland" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Sddm + + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/lxqt.py b/archinstall/default_profiles/desktops/lxqt.py new file mode 100644 index 00000000..146e168e --- /dev/null +++ b/archinstall/default_profiles/desktops/lxqt.py @@ -0,0 +1,35 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class LxqtProfile(XorgProfile): + def __init__(self): + super().__init__('Lxqt', ProfileType.DesktopEnv, description='') + + # NOTE: SDDM is the only officially supported greeter for LXQt, so unlike other DEs, lightdm is not used here. + # LXQt works with lightdm, but since this is not supported, we will not default to this. + # https://github.com/lxqt/lxqt/issues/795 + @property + def packages(self) -> List[str]: + return [ + "lxqt", + "breeze-icons", + "oxygen-icons", + "xdg-utils", + "ttf-freefont", + "leafpad", + "slock", + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Sddm + + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/mate.py b/archinstall/default_profiles/desktops/mate.py new file mode 100644 index 00000000..0ddaaaab --- /dev/null +++ b/archinstall/default_profiles/desktops/mate.py @@ -0,0 +1,27 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class MateProfile(XorgProfile): + def __init__(self): + super().__init__('Mate', ProfileType.DesktopEnv, description='') + + @property + def packages(self) -> List[str]: + return [ + "mate", + "mate-extra" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm + + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/qtile.py b/archinstall/default_profiles/desktops/qtile.py new file mode 100644 index 00000000..66c6fa1b --- /dev/null +++ b/archinstall/default_profiles/desktops/qtile.py @@ -0,0 +1,27 @@ +from typing import Optional, List, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class QtileProfile(XorgProfile): + def __init__(self): + super().__init__('Qtile', ProfileType.WindowMgr, description='') + + @property + def packages(self) -> List[str]: + return [ + 'qtile', + 'alacritty' + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm + + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/sway.py b/archinstall/default_profiles/desktops/sway.py new file mode 100644 index 00000000..519f5bbb --- /dev/null +++ b/archinstall/default_profiles/desktops/sway.py @@ -0,0 +1,66 @@ +from typing import List, Optional, TYPE_CHECKING, Any + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile +from archinstall.lib.menu import Menu + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +class SwayProfile(XorgProfile): + def __init__(self): + super().__init__( + 'Sway', + ProfileType.WindowMgr, + description='' + ) + self._control_preference = [] + + @property + def packages(self) -> List[str]: + return [ + "sway", + "swaybg", + "swaylock", + "swayidle", + "waybar", + "dmenu", + "brightnessctl", + "grim", + "slurp", + "pavucontrol", + "foot", + "xorg-xwayland" + ] + self._control_preference + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm + + @property + def services(self) -> List[str]: + if "seatd" in self._control_preference: + return ['seatd'] + elif "polkit" in self._control_preference: + return ['polkit'] + + return [] + + def _get_system_privelege_control_preference(self): + # need to activate seat service and add to seat group + title = str(_('Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)')) + title += str(_('\n\nChoose an option to give Sway access to your hardware')) + choice = Menu(title, ["polkit", "seatd"], skip=False).run() + self._control_preference = [choice.value] + + def do_on_select(self): + self._get_system_privelege_control_preference() + + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + return text + '\n' + self.packages_text() + + def install(self, install_session: 'Installer'): + super().install(install_session) diff --git a/archinstall/default_profiles/desktops/xfce4.py b/archinstall/default_profiles/desktops/xfce4.py new file mode 100644 index 00000000..bd6c3038 --- /dev/null +++ b/archinstall/default_profiles/desktops/xfce4.py @@ -0,0 +1,30 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class Xfce4Profile(XorgProfile): + def __init__(self): + super().__init__('Xfce4', ProfileType.DesktopEnv, description='') + + @property + def packages(self) -> List[str]: + return [ + "xfce4", + "xfce4-goodies", + "pavucontrol", + "gvfs", + "xarchiver" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm + + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/minimal.py b/archinstall/default_profiles/minimal.py new file mode 100644 index 00000000..f78708e9 --- /dev/null +++ b/archinstall/default_profiles/minimal.py @@ -0,0 +1,15 @@ +from typing import Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import Profile, ProfileType + +if TYPE_CHECKING: + _: Any + + +class MinimalProfile(Profile): + def __init__(self): + super().__init__( + 'Minimal', + ProfileType.Minimal, + description=str(_('A very basic installation that allows you to customize Arch Linux as you see fit.')) + ) diff --git a/archinstall/default_profiles/profile.py b/archinstall/default_profiles/profile.py new file mode 100644 index 00000000..c7d6b3dc --- /dev/null +++ b/archinstall/default_profiles/profile.py @@ -0,0 +1,206 @@ +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum, auto +from typing import List, Optional, Any, Dict, TYPE_CHECKING, TypeVar + +from archinstall.lib.output import FormattedOutput + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +TProfile = TypeVar('TProfile', bound='Profile') + + +class ProfileType(Enum): + # top level default_profiles + Server = 'Server' + Desktop = 'Desktop' + Xorg = 'Xorg' + Minimal = 'Minimal' + Custom = 'Custom' + # detailed selection default_profiles + ServerType = 'ServerType' + WindowMgr = 'Window Manager' + DesktopEnv = 'Desktop Environment' + CustomType = 'CustomType' + # special things + Tailored = 'Tailored' + Application = 'Application' + + +class GreeterType(Enum): + Lightdm = 'lightdm' + Sddm = 'sddm' + Gdm = 'gdm' + + +class SelectResult(Enum): + NewSelection = auto() + SameSelection = auto() + ResetCurrent = auto() + + +@dataclass +class ProfileInfo: + name: str + details: Optional[str] + gfx_driver: Optional[str] = None + greeter: Optional[str] = None + + @property + def absolute_name(self) -> str: + if self.details is not None: + return self.details + return self.name + + +class Profile: + def __init__( + self, + name: str, + profile_type: ProfileType, + description: str = '', + current_selection: List[TProfile] = [], + packages: List[str] = [], + services: List[str] = [], + support_gfx_driver: bool = False, + support_greeter: bool = False + ): + self.name = name + self.description = description + self.profile_type = profile_type + self._support_gfx_driver = support_gfx_driver + self._support_greeter = support_greeter + + # self.gfx_driver: Optional[str] = None + + self._current_selection = current_selection + self._packages = packages + self._services = services + + # Only used for custom default_profiles + self.custom_enabled = False + + @property + def current_selection(self) -> List[TProfile]: + return self._current_selection + + @property + def packages(self) -> List[str]: + """ + Returns a list of packages that should be installed when + this profile is among the choosen ones + """ + return self._packages + + @property + def services(self) -> List[str]: + """ + Returns a list of services that should be enabled when + this profile is among the chosen ones + """ + return self._services + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + """ + Setting a default greeter type for a desktop profile + """ + return None + + def install(self, install_session: 'Installer'): + """ + Performs installation steps when this profile was selected + """ + + def post_install(self, install_session: 'Installer'): + """ + Hook that will be called when the installation process is + finished and custom installation steps for specific default_profiles + are needed + """ + + def json(self) -> Dict: + """ + Returns a json representation of the profile + """ + return {} + + def do_on_select(self) -> SelectResult: + """ + Hook that will be called when a profile is selected + """ + return SelectResult.NewSelection + + def current_selection_names(self) -> List[str]: + if self._current_selection: + return [s.name for s in self._current_selection] + return [] + + def reset(self): + self.set_current_selection([]) + + def set_current_selection(self, current_selection: List[TProfile]): + self._current_selection = current_selection + + def is_top_level_profile(self) -> bool: + top_levels = [ProfileType.Desktop, ProfileType.Server, ProfileType.Xorg, ProfileType.Minimal, ProfileType.Custom] + return self.profile_type in top_levels + + def is_desktop_profile(self) -> bool: + return self.profile_type == ProfileType.Desktop + + def is_server_type_profile(self) -> bool: + return self.profile_type == ProfileType.ServerType + + def is_desktop_type_profile(self) -> bool: + return self.profile_type == ProfileType.DesktopEnv or self.profile_type == ProfileType.WindowMgr + + def is_xorg_type_profile(self) -> bool: + return self.profile_type == ProfileType.Xorg + + def is_tailored(self) -> bool: + return self.profile_type == ProfileType.Tailored + + def is_custom_type_profile(self) -> bool: + return self.profile_type == ProfileType.CustomType + + def is_graphic_driver_supported(self) -> bool: + if not self._current_selection: + return self._support_gfx_driver + else: + if any([p._support_gfx_driver for p in self._current_selection]): + return True + return False + + def is_greeter_supported(self) -> bool: + return self._support_greeter + + def preview_text(self) -> Optional[str]: + """ + Used for preview text in profiles_bck. If a description is set for a + profile it will automatically display that one in the preivew. + If no preview or a different text should be displayed just + """ + if self.description: + return self.description + return None + + def packages_text(self) -> str: + text = str(_('Installed packages')) + ':\n' + + nr_packages = len(self.packages) + if nr_packages <= 5: + col = 1 + elif nr_packages <= 10: + col = 2 + elif nr_packages <= 15: + col = 3 + else: + col = 4 + + text += FormattedOutput.as_columns(self.packages, col) + return text diff --git a/archinstall/default_profiles/server.py b/archinstall/default_profiles/server.py new file mode 100644 index 00000000..e240b3ef --- /dev/null +++ b/archinstall/default_profiles/server.py @@ -0,0 +1,57 @@ +import logging +from typing import Any, TYPE_CHECKING, List + +from archinstall.lib.output import log +from archinstall.lib.menu import MenuSelectionType +from archinstall.lib.profile.profiles_handler import profile_handler +from archinstall.default_profiles.profile import ProfileType, Profile, SelectResult, TProfile + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +class ServerProfile(Profile): + def __init__(self, current_value: List[TProfile] = []): + super().__init__( + 'Server', + ProfileType.Server, + description=str(_('Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb')), + current_selection=current_value + ) + + def do_on_select(self) -> SelectResult: + available_servers = profile_handler.get_server_profiles() + + choice = profile_handler.select_profile( + available_servers, + self._current_selection, + title=str(_('Choose which servers to install, if none then a minimal installation will be done')), + multi=True + ) + + match choice.type_: + case MenuSelectionType.Selection: + self.set_current_selection(choice.value) # type: ignore + return SelectResult.NewSelection + case MenuSelectionType.Skip: + return SelectResult.SameSelection + case MenuSelectionType.Reset: + return SelectResult.ResetCurrent + + def post_install(self, install_session: 'Installer'): + for profile in self._current_selection: + profile.post_install(install_session) + + def install(self, install_session: 'Installer'): + server_info = self.current_selection_names() + details = ', '.join(server_info) + log(f'Now installing the selected servers: {details}', level=logging.INFO) + + for server in self._current_selection: + log(f'Installing {server.name}...', level=logging.INFO) + install_session.add_additional_packages(server.packages) + install_session.enable_service(server.services) + server.install(install_session) + + log('If your selections included multiple servers with the same port, you may have to reconfigure them.', fg="yellow", level=logging.INFO) diff --git a/archinstall/default_profiles/servers/__init__.py b/archinstall/default_profiles/servers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/archinstall/default_profiles/servers/cockpit.py b/archinstall/default_profiles/servers/cockpit.py new file mode 100644 index 00000000..8cac0976 --- /dev/null +++ b/archinstall/default_profiles/servers/cockpit.py @@ -0,0 +1,19 @@ +from typing import List + +from archinstall.default_profiles.profile import Profile, ProfileType + + +class CockpitProfile(Profile): + def __init__(self): + super().__init__( + 'Cockpit', + ProfileType.ServerType + ) + + @property + def packages(self) -> List[str]: + return ['cockpit', 'udisks2', 'packagekit'] + + @property + def services(self) -> List[str]: + return ['cockpit.socket'] diff --git a/archinstall/default_profiles/servers/docker.py b/archinstall/default_profiles/servers/docker.py new file mode 100644 index 00000000..e6e17831 --- /dev/null +++ b/archinstall/default_profiles/servers/docker.py @@ -0,0 +1,33 @@ +from typing import List, Union, TYPE_CHECKING + +import archinstall + +from archinstall.default_profiles.profile import Profile, ProfileType +from archinstall.lib.models import User + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + + +class DockerProfile(Profile): + def __init__(self): + super().__init__( + 'Docker', + ProfileType.ServerType + ) + + @property + def packages(self) -> List[str]: + return ['docker'] + + @property + def services(self) -> List[str]: + return ['docker'] + + def post_install(self, install_session: 'Installer'): + users: Union[User, List[User]] = archinstall.arguments.get('!users', None) + if not isinstance(users, list): + users = [users] + + for user in users: + install_session.arch_chroot(f'usermod -a -G docker {user.username}') diff --git a/archinstall/default_profiles/servers/httpd.py b/archinstall/default_profiles/servers/httpd.py new file mode 100644 index 00000000..595ce84f --- /dev/null +++ b/archinstall/default_profiles/servers/httpd.py @@ -0,0 +1,19 @@ +from typing import List + +from archinstall.default_profiles.profile import Profile, ProfileType + + +class HttpdProfile(Profile): + def __init__(self): + super().__init__( + 'httpd', + ProfileType.ServerType + ) + + @property + def packages(self) -> List[str]: + return ['apache'] + + @property + def services(self) -> List[str]: + return ['httpd'] diff --git a/archinstall/default_profiles/servers/lighttpd.py b/archinstall/default_profiles/servers/lighttpd.py new file mode 100644 index 00000000..00aa5564 --- /dev/null +++ b/archinstall/default_profiles/servers/lighttpd.py @@ -0,0 +1,19 @@ +from typing import List + +from archinstall.default_profiles.profile import Profile, ProfileType + + +class LighttpdProfile(Profile): + def __init__(self): + super().__init__( + 'Lighttpd', + ProfileType.ServerType + ) + + @property + def packages(self) -> List[str]: + return ['lighttpd'] + + @property + def services(self) -> List[str]: + return ['lighttpd'] diff --git a/archinstall/default_profiles/servers/mariadb.py b/archinstall/default_profiles/servers/mariadb.py new file mode 100644 index 00000000..4506f1bc --- /dev/null +++ b/archinstall/default_profiles/servers/mariadb.py @@ -0,0 +1,25 @@ +from typing import List, TYPE_CHECKING + +from archinstall.default_profiles.profile import Profile, ProfileType + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + + +class MariadbProfile(Profile): + def __init__(self): + super().__init__( + 'Mariadb', + ProfileType.ServerType + ) + + @property + def packages(self) -> List[str]: + return ['mariadb'] + + @property + def services(self) -> List[str]: + return ['mariadb'] + + def post_install(self, install_session: 'Installer'): + install_session.arch_chroot('mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql') diff --git a/archinstall/default_profiles/servers/nginx.py b/archinstall/default_profiles/servers/nginx.py new file mode 100644 index 00000000..6038616c --- /dev/null +++ b/archinstall/default_profiles/servers/nginx.py @@ -0,0 +1,19 @@ +from typing import List + +from archinstall.default_profiles.profile import Profile, ProfileType + + +class NginxProfile(Profile): + def __init__(self): + super().__init__( + 'Nginx', + ProfileType.ServerType + ) + + @property + def packages(self) -> List[str]: + return ['nginx'] + + @property + def services(self) -> List[str]: + return ['nginx'] diff --git a/archinstall/default_profiles/servers/postgresql.py b/archinstall/default_profiles/servers/postgresql.py new file mode 100644 index 00000000..dba722ce --- /dev/null +++ b/archinstall/default_profiles/servers/postgresql.py @@ -0,0 +1,26 @@ +from typing import List, TYPE_CHECKING + +from archinstall.default_profiles.profile import Profile, ProfileType + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + + +class PostgresqlProfile(Profile): + def __init__(self): + super().__init__( + 'Postgresql', + ProfileType.ServerType, + '' + ) + + @property + def packages(self) -> List[str]: + return ['postgresql'] + + @property + def services(self) -> List[str]: + return ['postgresql'] + + def post_install(self, install_session: 'Installer'): + install_session.arch_chroot("initdb -D /var/lib/postgres/data", run_as='postgres') diff --git a/archinstall/default_profiles/servers/sshd.py b/archinstall/default_profiles/servers/sshd.py new file mode 100644 index 00000000..7f855b1a --- /dev/null +++ b/archinstall/default_profiles/servers/sshd.py @@ -0,0 +1,19 @@ +from typing import List + +from archinstall.default_profiles.profile import Profile, ProfileType + + +class SshdProfile(Profile): + def __init__(self): + super().__init__( + 'sshd', + ProfileType.ServerType + ) + + @property + def packages(self) -> List[str]: + return ['openssh'] + + @property + def services(self) -> List[str]: + return ['sshd'] diff --git a/archinstall/default_profiles/servers/tomcat.py b/archinstall/default_profiles/servers/tomcat.py new file mode 100644 index 00000000..9bd8837b --- /dev/null +++ b/archinstall/default_profiles/servers/tomcat.py @@ -0,0 +1,19 @@ +from typing import List + +from archinstall.default_profiles.profile import Profile, ProfileType + + +class TomcatProfile(Profile): + def __init__(self): + super().__init__( + 'Tomcat', + ProfileType.ServerType + ) + + @property + def packages(self) -> List[str]: + return ['tomcat10'] + + @property + def services(self) -> List[str]: + return ['tomcat10'] diff --git a/archinstall/default_profiles/tailored.py b/archinstall/default_profiles/tailored.py new file mode 100644 index 00000000..62666249 --- /dev/null +++ b/archinstall/default_profiles/tailored.py @@ -0,0 +1,21 @@ +from typing import List, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +class TailoredProfile(XorgProfile): + def __init__(self): + super().__init__('52-54-00-12-34-56', ProfileType.Tailored, description='') + + @property + def packages(self) -> List[str]: + return ['nano', 'wget', 'git'] + + def install(self, install_session: 'Installer'): + super().install(install_session) + # do whatever you like here :) diff --git a/archinstall/default_profiles/xorg.py b/archinstall/default_profiles/xorg.py new file mode 100644 index 00000000..553421a4 --- /dev/null +++ b/archinstall/default_profiles/xorg.py @@ -0,0 +1,21 @@ +from typing import Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import Profile, ProfileType + +if TYPE_CHECKING: + _: Any + + +class XorgProfile(Profile): + def __init__( + self, + name: str = 'Xorg', + profile_type: ProfileType = ProfileType.Xorg, + description: str = str(_('Installs a minimal system as well as xorg and graphics drivers.')), + ): + super().__init__( + name, + profile_type, + description=description, + support_gfx_driver=True + ) diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py index c036783f..77fed755 100644 --- a/archinstall/lib/configuration.py +++ b/archinstall/lib/configuration.py @@ -2,27 +2,15 @@ import os import json import stat import logging -import pathlib -from typing import Optional, Dict +from pathlib import Path +from typing import Optional, Dict, Any, TYPE_CHECKING -from .hsm.fido import Fido2 -from .models.disk_encryption import DiskEncryption from .storage import storage from .general import JSON, UNSAFE_JSON from .output import log -from .exceptions import RequirementError - - -def configuration_sanity_check(): - disk_encryption: DiskEncryption = storage['arguments'].get('disk_encryption') - if disk_encryption is not None and disk_encryption.hsm_device: - if not Fido2.get_fido2_devices(): - raise RequirementError( - f"In order to use HSM to pair with the disk encryption," - + f" one needs to be accessible through /dev/hidraw* and support" - + f" the FIDO2 protocol. You can check this by running" - + f" 'systemd-cryptenroll --fido2-device=list'." - ) + +if TYPE_CHECKING: + _: Any class ConfigurationOutput: @@ -35,13 +23,11 @@ class ConfigurationOutput: :type config: Dict """ self._config = config - self._user_credentials = {} - self._disk_layout = None - self._user_config = {} - self._default_save_path = pathlib.Path(storage.get('LOG_PATH', '.')) + self._user_credentials: Dict[str, Any] = {} + self._user_config: Dict[str, Any] = {} + self._default_save_path = Path(storage.get('LOG_PATH', '.')) self._user_config_file = 'user_configuration.json' self._user_creds_file = "user_credentials.json" - self._disk_layout_file = "user_disk_layout.json" self._sensitive = ['!users'] self._ignore = ['abort', 'install', 'config', 'creds', 'dry_run'] @@ -56,23 +42,18 @@ class ConfigurationOutput: def user_configuration_file(self): return self._user_config_file - @property - def disk_layout_file(self): - return self._disk_layout_file - def _process_config(self): for key in self._config: if key in self._sensitive: self._user_credentials[key] = self._config[key] - elif key == 'disk_layouts': - self._disk_layout = self._config[key] elif key in self._ignore: pass else: self._user_config[key] = self._config[key] - if key == 'disk_encryption' and self._config[key]: # special handling for encryption password - self._user_credentials['encryption_password'] = self._config[key].encryption_password + # special handling for encryption password + if key == 'disk_encryption' and self._config[key] is not None: + self._user_credentials['encryption_password'] = self._config[key].encryption_password def user_config_to_json(self) -> str: return json.dumps({ @@ -81,11 +62,6 @@ class ConfigurationOutput: 'version': storage['__version__'] }, indent=4, sort_keys=True, cls=JSON) - def disk_layout_to_json(self) -> Optional[str]: - if self._disk_layout: - return json.dumps(self._disk_layout, indent=4, sort_keys=True, cls=JSON) - return None - def user_credentials_to_json(self) -> Optional[str]: if self._user_credentials: return json.dumps(self._user_credentials, indent=4, sort_keys=True, cls=UNSAFE_JSON) @@ -96,15 +72,11 @@ class ConfigurationOutput: log(" -- Chosen configuration --", level=logging.DEBUG) user_conig = self.user_config_to_json() - disk_layout = self.disk_layout_to_json() log(user_conig, level=logging.INFO) - if disk_layout: - log(disk_layout, level=logging.INFO) - print() - def _is_valid_path(self, dest_path :pathlib.Path) -> bool: + def _is_valid_path(self, dest_path: Path) -> bool: if (not dest_path.exists()) or not (dest_path.is_dir()): log( 'Destination directory {} does not exist or is not a directory,\n Configuration files can not be saved'.format(dest_path.resolve()), @@ -113,7 +85,7 @@ class ConfigurationOutput: return False return True - def save_user_config(self, dest_path :pathlib.Path = None): + def save_user_config(self, dest_path: Path): if self._is_valid_path(dest_path): target = dest_path / self._user_config_file @@ -122,7 +94,7 @@ class ConfigurationOutput: os.chmod(str(dest_path / self._user_config_file), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) - def save_user_creds(self, dest_path :pathlib.Path = None): + def save_user_creds(self, dest_path: Path): if self._is_valid_path(dest_path): if user_creds := self.user_credentials_to_json(): target = dest_path / self._user_creds_file @@ -132,21 +104,10 @@ class ConfigurationOutput: os.chmod(str(target), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) - def save_disk_layout(self, dest_path :pathlib.Path = None): - if self._is_valid_path(dest_path): - if disk_layout := self.disk_layout_to_json(): - target = dest_path / self._disk_layout_file - - with target.open('w') as config_file: - config_file.write(disk_layout) - - os.chmod(str(target), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) - - def save(self, dest_path :pathlib.Path = None): + def save(self, dest_path: Optional[Path] = None): if not dest_path: dest_path = self._default_save_path if self._is_valid_path(dest_path): self.save_user_config(dest_path) self.save_user_creds(dest_path) - self.save_disk_layout(dest_path) diff --git a/archinstall/lib/disk/__init__.py b/archinstall/lib/disk/__init__.py index 352d04b9..cdc96373 100644 --- a/archinstall/lib/disk/__init__.py +++ b/archinstall/lib/disk/__init__.py @@ -1,7 +1,40 @@ -from .btrfs import * -from .helpers import * -from .blockdevice import BlockDevice -from .filesystem import Filesystem, MBR, GPT -from .partition import * -from .user_guides import * -from .validators import * \ No newline at end of file +from .device_handler import device_handler, disk_layouts +from .fido import Fido2 +from .filesystem import FilesystemHandler +from .subvolume_menu import SubvolumeMenu +from .partitioning_menu import ( + manual_partitioning, + PartitioningList +) +from .device_model import ( + _DeviceInfo, + BDevice, + DiskLayoutType, + DiskLayoutConfiguration, + PartitionTable, + Unit, + Size, + SubvolumeModification, + DeviceGeometry, + PartitionType, + PartitionFlag, + FilesystemType, + ModificationStatus, + PartitionModification, + DeviceModification, + EncryptionType, + DiskEncryption, + Fido2Device, + LsblkInfo, + CleanType, + get_lsblk_info, + get_all_lsblk_info, + get_lsblk_by_mountpoint +) +from .encryption_menu import ( + select_encryption_type, + select_encrypted_password, + select_hsm, + select_partitions_to_encrypt, + DiskEncryptionMenu, +) diff --git a/archinstall/lib/disk/blockdevice.py b/archinstall/lib/disk/blockdevice.py deleted file mode 100644 index 178b786a..00000000 --- a/archinstall/lib/disk/blockdevice.py +++ /dev/null @@ -1,301 +0,0 @@ -from __future__ import annotations -import json -import logging -import time - -from collections import OrderedDict -from dataclasses import dataclass -from typing import Optional, Dict, Any, Iterator, List, TYPE_CHECKING - -from ..exceptions import DiskError, SysCallError -from ..output import log -from ..general import SysCommand -from ..storage import storage - - -if TYPE_CHECKING: - from .partition import Partition - _: Any - - -@dataclass -class BlockSizeInfo: - start: str - end: str - size: str - - -@dataclass -class BlockInfo: - pttype: str - ptuuid: str - size: int - tran: Optional[str] - rota: bool - free_space: Optional[List[BlockSizeInfo]] - - -class BlockDevice: - def __init__(self, path :str, info :Optional[Dict[str, Any]] = None): - if not info: - from .helpers import all_blockdevices - # If we don't give any information, we need to auto-fill it. - # Otherwise any subsequent usage will break. - self.info = all_blockdevices(partitions=False)[path].info - else: - self.info = info - - self._path = path - self.keep_partitions = True - self._block_info = self._fetch_information() - self._partitions: Dict[str, 'Partition'] = {} - - self._load_partitions() - - # TODO: Currently disk encryption is a BIT misleading. - # It's actually partition-encryption, but for future-proofing this - # I'm placing the encryption password on a BlockDevice level. - - def __repr__(self, *args :str, **kwargs :str) -> str: - return self._str_repr - - @property - def path(self) -> str: - return self._path - - @property - def _str_repr(self) -> str: - return f"BlockDevice({self._device_or_backfile}, size={self.size}GB, free_space={self._safe_free_space()}, bus_type={self.bus_type})" - - def as_json(self) -> Dict[str, Any]: - return { - str(_('Device')): self._device_or_backfile, - str(_('Size')): f'{self.size}GB', - str(_('Free space')): f'{self._safe_free_space()}', - str(_('Bus-type')): f'{self.bus_type}' - } - - def __iter__(self) -> Iterator['Partition']: - for partition in self.partitions: - yield self.partitions[partition] - - def __getitem__(self, key :str, *args :str, **kwargs :str) -> Any: - if hasattr(self, key): - return getattr(self, key) - - if self.info and key in self.info: - return self.info[key] - - raise KeyError(f'{self.info} does not contain information: "{key}"') - - def __lt__(self, left_comparitor :'BlockDevice') -> bool: - return self._path < left_comparitor.path - - def json(self) -> str: - """ - json() has precedence over __dump__, so this is a way - to give less/partial information for user readability. - """ - return self._path - - def __dump__(self) -> Dict[str, Dict[str, Any]]: - return { - self._path: { - 'partuuid': self.uuid, - 'wipe': self.info.get('wipe', None), - 'partitions': [part.__dump__() for part in self.partitions.values()] - } - } - - def _call_lsblk(self, path: str) -> Dict[str, Any]: - output = SysCommand(f'lsblk --json -b -o+SIZE,PTTYPE,ROTA,TRAN,PTUUID {self._path}').decode('UTF-8') - if output: - lsblk_info = json.loads(output) - return lsblk_info - - raise DiskError(f'Failed to read disk "{self.path}" with lsblk') - - def _load_partitions(self): - from .partition import Partition - - self._partitions.clear() - - lsblk_info = self._call_lsblk(self._path) - device = lsblk_info['blockdevices'][0] - self._partitions.clear() - - if children := device.get('children', None): - root = f'/dev/{device["name"]}' - for child in children: - part_id = child['name'].removeprefix(device['name']) - self._partitions[part_id] = Partition(root + part_id, block_device=self, part_id=part_id) - - def _get_free_space(self) -> Optional[List[BlockSizeInfo]]: - # NOTE: parted -s will default to `cancel` on prompt, skipping any partition - # that is "outside" the disk. in /dev/sr0 this is usually the case with Archiso, - # so the free will ignore the ESP partition and just give the "free" space. - # Doesn't harm us, but worth noting in case something weird happens. - try: - output = SysCommand(f"parted -s --machine {self._path} print free").decode('utf-8') - if output: - free_lines = [line for line in output.split('\n') if 'free' in line] - sizes = [] - for free_space in free_lines: - _, start, end, size, *_ = free_space.strip('\r\n;').split(':') - sizes.append(BlockSizeInfo(start, end, size)) - - return sizes - except SysCallError as error: - log(f"Could not get free space on {self._path}: {error}", level=logging.DEBUG) - - return None - - def _fetch_information(self) -> BlockInfo: - lsblk_info = self._call_lsblk(self._path) - device = lsblk_info['blockdevices'][0] - free_space = self._get_free_space() - - return BlockInfo( - pttype=device['pttype'], - ptuuid=device['ptuuid'], - size=device['size'], - tran=device['tran'], - rota=device['rota'], - free_space=free_space - ) - - @property - def _device_or_backfile(self) -> Optional[str]: - """ - Returns the actual device-endpoint of the BlockDevice. - If it's a loop-back-device it returns the back-file, - For other types it return self.device - """ - if self.info.get('type') == 'loop': - return self.info['back-file'] - else: - return self.device - - @property - def mountpoint(self) -> None: - """ - A dummy function to enable transparent comparisons of mountpoints. - As blockdevices can't be mounted directly, this will always be None - """ - return None - - @property - def device(self) -> Optional[str]: - """ - Returns the device file of the BlockDevice. - If it's a loop-back-device it returns the /dev/X device, - If it's a ATA-drive it returns the /dev/X device - And if it's a crypto-device it returns the parent device - """ - if "DEVTYPE" not in self.info: - raise DiskError(f'Could not locate backplane info for "{self._path}"') - - if self.info['DEVTYPE'] in ['disk','loop']: - return self._path - elif self.info['DEVTYPE'][:4] == 'raid': - # This should catch /dev/md## raid devices - return self._path - elif self.info['DEVTYPE'] == 'crypt': - if 'pkname' not in self.info: - raise DiskError(f'A crypt device ({self._path}) without a parent kernel device name.') - return f"/dev/{self.info['pkname']}" - else: - log(f"Unknown blockdevice type for {self._path}: {self.info['DEVTYPE']}", level=logging.DEBUG) - - return None - - @property - def partition_type(self) -> str: - return self._block_info.pttype - - @property - def uuid(self) -> str: - return self._block_info.ptuuid - - @property - def size(self) -> float: - from .helpers import convert_size_to_gb - return convert_size_to_gb(self._block_info.size) - - @property - def bus_type(self) -> Optional[str]: - return self._block_info.tran - - @property - def spinning(self) -> bool: - return self._block_info.rota - - @property - def partitions(self) -> Dict[str, 'Partition']: - return OrderedDict(sorted(self._partitions.items())) - - @property - def partition(self) -> List['Partition']: - return list(self.partitions.values()) - - @property - def first_free_sector(self) -> str: - if block_size := self._largest_free_space(): - return block_size.start - else: - return '512MB' - - @property - def first_end_sector(self) -> str: - if block_size := self._largest_free_space(): - return block_size.end - else: - return f"{self.size}GB" - - def _safe_free_space(self) -> str: - if self._block_info.free_space: - sizes = [free_space.size for free_space in self._block_info.free_space] - return '+'.join(sizes) - return '?' - - def _largest_free_space(self) -> Optional[BlockSizeInfo]: - if self._block_info.free_space: - sorted_sizes = sorted(self._block_info.free_space, key=lambda x: x.size, reverse=True) - return sorted_sizes[0] - return None - - def _partprobe(self) -> bool: - return SysCommand(['partprobe', self._path]).exit_code == 0 - - def flush_cache(self) -> None: - self._load_partitions() - - def get_partition(self, uuid :Optional[str] = None, partuuid :Optional[str] = None) -> Partition: - if not uuid and not partuuid: - raise ValueError(f"BlockDevice.get_partition() requires either a UUID or a PARTUUID for lookups.") - - log(f"Retrieving partition PARTUUID={partuuid} or UUID={uuid}", level=logging.DEBUG, fg="gray") - - for count in range(storage.get('DISK_RETRY_ATTEMPTS', 5)): - for partition_index, partition in self.partitions.items(): - try: - if uuid and partition.uuid and partition.uuid.lower() == uuid.lower(): - log(f"Matched UUID={uuid} against {partition.uuid}", level=logging.DEBUG, fg="gray") - return partition - elif partuuid and partition.part_uuid and partition.part_uuid.lower() == partuuid.lower(): - log(f"Matched PARTUUID={partuuid} against {partition.part_uuid}", level=logging.DEBUG, fg="gray") - return partition - except DiskError as error: - # Most likely a blockdevice that doesn't support or use UUID's - # (like Microsoft recovery partition) - log(f"Could not get UUID/PARTUUID of {partition}: {error}", level=logging.DEBUG, fg="gray") - pass - - log(f"uuid {uuid} or {partuuid} not found. Waiting {storage.get('DISK_TIMEOUTS', 1) * count}s for next attempt",level=logging.DEBUG) - self.flush_cache() - time.sleep(storage.get('DISK_TIMEOUTS', 1) * count) - - log(f"Could not find {uuid}/{partuuid} in disk after 5 retries", level=logging.INFO) - log(f"Cache: {self._partitions}") - log(f"Partitions: {self.partitions.items()}") - raise DiskError(f"Partition {uuid}/{partuuid} was never found on {self} despite several attempts.") diff --git a/archinstall/lib/disk/btrfs/__init__.py b/archinstall/lib/disk/btrfs/__init__.py deleted file mode 100644 index a26e0160..00000000 --- a/archinstall/lib/disk/btrfs/__init__.py +++ /dev/null @@ -1,56 +0,0 @@ -from __future__ import annotations -import pathlib -import glob -import logging -from typing import Union, Dict, TYPE_CHECKING - -# https://stackoverflow.com/a/39757388/929999 -if TYPE_CHECKING: - from ...installer import Installer - -from .btrfs_helpers import ( - subvolume_info_from_path as subvolume_info_from_path, - find_parent_subvolume as find_parent_subvolume, - setup_subvolumes as setup_subvolumes, - mount_subvolume as mount_subvolume -) -from .btrfssubvolumeinfo import BtrfsSubvolumeInfo as BtrfsSubvolume -from .btrfspartition import BTRFSPartition as BTRFSPartition - -from ...exceptions import DiskError, Deprecated -from ...general import SysCommand -from ...output import log - - -def create_subvolume(installation: Installer, subvolume_location :Union[pathlib.Path, str]) -> bool: - """ - This function uses btrfs to create a subvolume. - - @installation: archinstall.Installer instance - @subvolume_location: a localized string or path inside the installation / or /boot for instance without specifying /mnt/boot - """ - - installation_mountpoint = installation.target - if type(installation_mountpoint) == str: - installation_mountpoint = pathlib.Path(installation_mountpoint) - # Set up the required physical structure - if type(subvolume_location) == str: - subvolume_location = pathlib.Path(subvolume_location) - - target = installation_mountpoint / subvolume_location.relative_to(subvolume_location.anchor) - - # Difference from mount_subvolume: - # We only check if the parent exists, since we'll run in to "target path already exists" otherwise - if not target.parent.exists(): - target.parent.mkdir(parents=True) - - if glob.glob(str(target / '*')): - raise DiskError(f"Cannot create subvolume at {target} because it contains data (non-empty folder target)") - - # Remove the target if it exists - if target.exists(): - target.rmdir() - - log(f"Creating a subvolume on {target}", level=logging.INFO) - if (cmd := SysCommand(f"btrfs subvolume create {target}")).exit_code != 0: - raise DiskError(f"Could not create a subvolume at {target}: {cmd}") diff --git a/archinstall/lib/disk/btrfs/btrfs_helpers.py b/archinstall/lib/disk/btrfs/btrfs_helpers.py deleted file mode 100644 index f6d2734a..00000000 --- a/archinstall/lib/disk/btrfs/btrfs_helpers.py +++ /dev/null @@ -1,136 +0,0 @@ -import logging -import re -from pathlib import Path -from typing import Optional, Dict, Any, TYPE_CHECKING - -from ...models.subvolume import Subvolume -from ...exceptions import SysCallError, DiskError -from ...general import SysCommand -from ...output import log -from ...plugins import plugins -from ..helpers import get_mount_info -from .btrfssubvolumeinfo import BtrfsSubvolumeInfo - -if TYPE_CHECKING: - from .btrfspartition import BTRFSPartition - from ...installer import Installer - - -class fstab_btrfs_compression_plugin(): - def __init__(self, partition_dict): - self.partition_dict = partition_dict - - def on_genfstab(self, installation): - with open(f"{installation.target}/etc/fstab", 'r') as fh: - fstab = fh.read() - - # Replace the {installation}/etc/fstab with entries - # using the compress=zstd where the mountpoint has compression set. - with open(f"{installation.target}/etc/fstab", 'w') as fh: - for line in fstab.split('\n'): - # So first we grab the mount options by using subvol=.*? as a locator. - # And we also grab the mountpoint for the entry, for instance /var/log - if (subvoldef := re.findall(',.*?subvol=.*?[\t ]', line)) and (mountpoint := re.findall('[\t ]/.*?[\t ]', line)): - for subvolume in self.partition_dict.get('btrfs', {}).get('subvolumes', []): - # We then locate the correct subvolume and check if it's compressed - if subvolume.compress and subvolume.mountpoint == mountpoint[0].strip(): - # We then sneak in the compress=zstd option if it doesn't already exist: - # We skip entries where compression is already defined - if ',compress=zstd,' not in line: - line = line.replace(subvoldef[0], f",compress=zstd{subvoldef[0]}") - break - - fh.write(f"{line}\n") - - return True - - -def mount_subvolume(installation: 'Installer', device: 'BTRFSPartition', subvolume: Subvolume): - # we normalize the subvolume name (getting rid of slash at the start if exists. - # In our implementation has no semantic load. - # Every subvolume is created from the top of the hierarchy- and simplifies its further use - name = subvolume.name.lstrip('/') - mountpoint = Path(subvolume.mountpoint) - installation_target = Path(installation.target) - - mountpoint = installation_target / mountpoint.relative_to(mountpoint.anchor) - mountpoint.mkdir(parents=True, exist_ok=True) - mount_options = subvolume.options + [f'subvol={name}'] - - log(f"Mounting subvolume {name} on {device} to {mountpoint}", level=logging.INFO, fg="gray") - SysCommand(f"mount {device.path} {mountpoint} -o {','.join(mount_options)}") - - -def setup_subvolumes(installation: 'Installer', partition_dict: Dict[str, Any]): - log(f"Setting up subvolumes: {partition_dict['btrfs']['subvolumes']}", level=logging.INFO, fg="gray") - - for subvolume in partition_dict['btrfs']['subvolumes']: - # we normalize the subvolume name (getting rid of slash at the start if exists. In our implementation has no semantic load. - # Every subvolume is created from the top of the hierarchy- and simplifies its further use - name = subvolume.name.lstrip('/') - - # We create the subvolume using the BTRFSPartition instance. - # That way we ensure not only easy access, but also accurate mount locations etc. - partition_dict['device_instance'].create_subvolume(name, installation=installation) - - # Make the nodatacow processing now - # It will be the main cause of creation of subvolumes which are not to be mounted - # it is not an options which can be established by subvolume (but for whole file systems), and can be - # set up via a simple attribute change in a directory (if empty). And here the directories are brand new - if subvolume.nodatacow: - if (cmd := SysCommand(f"chattr +C {installation.target}/{name}")).exit_code != 0: - raise DiskError(f"Could not set nodatacow attribute at {installation.target}/{name}: {cmd}") - - # Make the compress processing now - # it is not an options which can be established by subvolume (but for whole file systems), and can be - # set up via a simple attribute change in a directory (if empty). And here the directories are brand new - # in this way only zstd compression is activaded - # TODO WARNING it is not clear if it should be a standard feature, so it might need to be deactivated - - if subvolume.compress: - if not any(['compress' in filesystem_option for filesystem_option in partition_dict.get('filesystem', {}).get('mount_options', [])]): - if (cmd := SysCommand(f"chattr +c {installation.target}/{name}")).exit_code != 0: - raise DiskError(f"Could not set compress attribute at {installation.target}/{name}: {cmd}") - - if 'fstab_btrfs_compression_plugin' not in plugins: - plugins['fstab_btrfs_compression_plugin'] = fstab_btrfs_compression_plugin(partition_dict) - - -def subvolume_info_from_path(path: Path) -> Optional[BtrfsSubvolumeInfo]: - try: - subvolume_name = '' - result = {} - for index, line in enumerate(SysCommand(f"btrfs subvolume show {path}")): - if index == 0: - subvolume_name = line.strip().decode('UTF-8') - continue - - if b':' in line: - key, value = line.strip().decode('UTF-8').split(':', 1) - - # A bit of a hack, until I figure out how @dataclass - # allows for hooking in a pre-processor to do this we have to do it here: - result[key.lower().replace(' ', '_').replace('(s)', 's')] = value.strip() - - return BtrfsSubvolumeInfo(**{'full_path' : path, 'name' : subvolume_name, **result}) # type: ignore - except SysCallError as error: - log(f"Could not retrieve subvolume information from {path}: {error}", level=logging.WARNING, fg="orange") - - return None - - -def find_parent_subvolume(path: Path, filters=[]) -> Optional[BtrfsSubvolumeInfo]: - # A root path cannot have a parent - if str(path) == '/': - return None - - if found_mount := get_mount_info(str(path.parent), traverse=True, ignore=filters): - if not (subvolume := subvolume_info_from_path(found_mount['target'])): - if found_mount['target'] == '/': - return None - - return find_parent_subvolume(path.parent, filters=[*filters, found_mount['target']]) - - return subvolume - - return None diff --git a/archinstall/lib/disk/btrfs/btrfspartition.py b/archinstall/lib/disk/btrfs/btrfspartition.py deleted file mode 100644 index d04c9b98..00000000 --- a/archinstall/lib/disk/btrfs/btrfspartition.py +++ /dev/null @@ -1,109 +0,0 @@ -import glob -import pathlib -import logging -from typing import Optional, TYPE_CHECKING - -from ...exceptions import DiskError -from ...storage import storage -from ...output import log -from ...general import SysCommand -from ..partition import Partition -from ..helpers import findmnt -from .btrfs_helpers import ( - subvolume_info_from_path -) - -if TYPE_CHECKING: - from ...installer import Installer - from .btrfssubvolumeinfo import BtrfsSubvolumeInfo - - -class BTRFSPartition(Partition): - def __init__(self, *args, **kwargs): - Partition.__init__(self, *args, **kwargs) - - @property - def subvolumes(self): - for filesystem in findmnt(pathlib.Path(self.path), recurse=True).get('filesystems', []): - if '[' in filesystem.get('source', ''): - yield subvolume_info_from_path(filesystem['target']) - - def iterate_children(struct): - for c in struct.get('children', []): - if '[' in child.get('source', ''): - yield subvolume_info_from_path(c['target']) - - for sub_child in iterate_children(c): - yield sub_child - - for child in iterate_children(filesystem): - yield child - - def create_subvolume(self, subvolume :pathlib.Path, installation :Optional['Installer'] = None) -> 'BtrfsSubvolumeInfo': - """ - Subvolumes have to be created within a mountpoint. - This means we need to get the current installation target. - After we get it, we need to verify it is a btrfs subvolume filesystem. - Finally, the destination must be empty. - """ - - # Allow users to override the installation session - if not installation: - installation = storage.get('installation_session') - - # Determain if the path given, is an absolute path or a relative path. - # We do this by checking if the path contains a known mountpoint. - if str(subvolume)[0] == '/': - if filesystems := findmnt(subvolume, traverse=True).get('filesystems'): - if (target := filesystems[0].get('target')) and target != '/' and str(subvolume).startswith(target): - # Path starts with a known mountpoint which isn't / - # Which means it's an absolute path to a mounted location. - pass - else: - # Since it's not an absolute position with a known start. - # We omit the anchor ('/' basically) and make sure it's appendable - # to the installation.target later - subvolume = subvolume.relative_to(subvolume.anchor) - # else: We don't need to do anything about relative paths, they should be appendable to installation.target as-is. - - # If the subvolume is not absolute, then we do two checks: - # 1. Check if the partition itself is mounted somewhere, and use that as a root - # 2. Use an active Installer().target as the root, assuming it's filesystem is btrfs - # If both above fail, we need to warn the user that such setup is not supported. - if str(subvolume)[0] != '/': - if self.mountpoint is None and installation is None: - raise DiskError("When creating a subvolume on BTRFSPartition()'s, you need to either initiate a archinstall.Installer() or give absolute paths when creating the subvoulme.") - elif self.mountpoint: - subvolume = self.mountpoint / subvolume - elif installation: - ongoing_installation_destination = installation.target - if type(ongoing_installation_destination) == str: - ongoing_installation_destination = pathlib.Path(ongoing_installation_destination) - - subvolume = ongoing_installation_destination / subvolume - - subvolume.parent.mkdir(parents=True, exist_ok=True) - - # - - log(f'Attempting to create subvolume at {subvolume}', level=logging.DEBUG, fg="grey") - - if glob.glob(str(subvolume / '*')): - raise DiskError(f"Cannot create subvolume at {subvolume} because it contains data (non-empty folder target is not supported by BTRFS)") - # Ideally we would like to check if the destination is already a subvolume. - # But then we would need the mount-point at this stage as well. - # So we'll comment out this check: - # elif subvolinfo := subvolume_info_from_path(subvolume): - # raise DiskError(f"Destination {subvolume} is already a subvolume: {subvolinfo}") - - # And deal with it here: - SysCommand(f"btrfs subvolume create {subvolume}") - - return subvolume_info_from_path(subvolume) diff --git a/archinstall/lib/disk/btrfs/btrfssubvolumeinfo.py b/archinstall/lib/disk/btrfs/btrfssubvolumeinfo.py deleted file mode 100644 index 5f5bdea6..00000000 --- a/archinstall/lib/disk/btrfs/btrfssubvolumeinfo.py +++ /dev/null @@ -1,192 +0,0 @@ -import pathlib -import datetime -import logging -import string -import random -import shutil -from dataclasses import dataclass -from typing import Optional, List# , TYPE_CHECKING -from functools import cached_property - -# if TYPE_CHECKING: -# from ..blockdevice import BlockDevice - -from ...exceptions import DiskError -from ...general import SysCommand -from ...output import log -from ...storage import storage - - -@dataclass -class BtrfsSubvolumeInfo: - full_path :pathlib.Path - name :str - uuid :str - parent_uuid :str - creation_time :datetime.datetime - subvolume_id :int - generation :int - gen_at_creation :int - parent_id :int - top_level_id :int - send_transid :int - send_time :datetime.datetime - receive_transid :int - received_uuid :Optional[str] = None - flags :Optional[str] = None - receive_time :Optional[datetime.datetime] = None - snapshots :Optional[List] = None - - def __post_init__(self): - self.full_path = pathlib.Path(self.full_path) - - # Convert "-" entries to `None` - if self.parent_uuid == "-": - self.parent_uuid = None - if self.received_uuid == "-": - self.received_uuid = None - if self.flags == "-": - self.flags = None - if self.receive_time == "-": - self.receive_time = None - if self.snapshots == "": - self.snapshots = [] - - # Convert timestamps into datetime workable objects (and preserve timezone by using ISO formats) - self.creation_time = datetime.datetime.fromisoformat(self.convert_to_ISO_format(self.creation_time)) - self.send_time = datetime.datetime.fromisoformat(self.convert_to_ISO_format(self.send_time)) - if self.receive_time: - self.receive_time = datetime.datetime.fromisoformat(self.convert_to_ISO_format(self.receive_time)) - - @property - def parent_subvolume(self): - from .btrfs_helpers import find_parent_subvolume - - return find_parent_subvolume(self.full_path) - - @property - def root(self) -> bool: - from .btrfs_helpers import subvolume_info_from_path - - # TODO: Make this function traverse storage['MOUNT_POINT'] and find the first - # occurrence of a mountpoint that is a btrfs volume instead of lazy assume / is a subvolume. - # It would also be nice if it could use findmnt(self.full_path) and traverse backwards - # finding the last occurrence of a subvolume which 'self' belongs to. - if volume := subvolume_info_from_path(storage['MOUNT_POINT']): - return self.full_path == volume.full_path - - return False - - @cached_property - def partition(self): - from ..helpers import findmnt, get_parent_of_partition, all_blockdevices - from ..partition import Partition - from ..blockdevice import BlockDevice - from ..mapperdev import MapperDev - from .btrfspartition import BTRFSPartition - from .btrfs_helpers import subvolume_info_from_path - - try: - # If the subvolume is mounted, it's pretty trivial to lookup the partition (parent) device. - if filesystem := findmnt(self.full_path).get('filesystems', []): - if source := filesystem[0].get('source', None): - # Strip away subvolume definitions from findmnt - if '[' in source: - source = source[:source.find('[')] - - if filesystem[0].get('fstype', '') == 'btrfs': - return BTRFSPartition(source, BlockDevice(get_parent_of_partition(pathlib.Path(source)))) - elif filesystem[0].get('source', '').startswith('/dev/mapper'): - return MapperDev(source) - else: - return Partition(source, BlockDevice(get_parent_of_partition(pathlib.Path(source)))) - except DiskError: - # Subvolume has never been mounted, we have no reliable way of finding where it is. - # But we have the UUID of the partition, and can begin looking for it by mounting - # all blockdevices that we can reliably support.. This is taxing tho and won't cover all devices. - - log(f"Looking up {self}, this might take time.", fg="orange", level=logging.WARNING) - for blockdevice, instance in all_blockdevices(mappers=True, partitions=True, error=True).items(): - if type(instance) in (Partition, MapperDev): - we_mounted_it = False - detection_mountpoint = instance.mountpoint - if not detection_mountpoint: - if type(instance) == Partition and instance.encrypted: - # TODO: Perhaps support unlocking encrypted volumes? - # This will cause a lot of potential user interactions tho. - log(f"Ignoring {blockdevice} because it's encrypted.", fg="gray", level=logging.DEBUG) - continue - - detection_mountpoint = pathlib.Path(f"/tmp/{''.join([random.choice(string.ascii_letters) for x in range(20)])}") - detection_mountpoint.mkdir(parents=True, exist_ok=True) - - instance.mount(str(detection_mountpoint)) - we_mounted_it = True - - if (filesystem := findmnt(detection_mountpoint)) and (filesystem := filesystem.get('filesystems', [])): - if subvolume := subvolume_info_from_path(filesystem[0]['target']): - if subvolume.uuid == self.uuid: - # The top level subvolume matched of ourselves, - # which means the instance we're iterating has the subvol we're looking for. - log(f"Found the subvolume on device {instance}", level=logging.DEBUG, fg="gray") - return instance - - def iterate_children(struct): - for child in struct.get('children', []): - if '[' in child.get('source', ''): - yield subvolume_info_from_path(child['target']) - - for sub_child in iterate_children(child): - yield sub_child - - for child in iterate_children(filesystem[0]): - if child.uuid == self.uuid: - # We found a child within the instance that has the subvol we're looking for. - log(f"Found the subvolume on device {instance}", level=logging.DEBUG, fg="gray") - return instance - - if we_mounted_it: - instance.unmount() - shutil.rmtree(detection_mountpoint) - - @cached_property - def mount_options(self) -> Optional[List[str]]: - from ..helpers import findmnt - - if filesystem := findmnt(self.full_path).get('filesystems', []): - return filesystem[0].get('options').split(',') - - def convert_to_ISO_format(self, time_string): - time_string_almost_done = time_string.replace(' ', 'T', 1).replace(' ', '') - iso_string = f"{time_string_almost_done[:-2]}:{time_string_almost_done[-2:]}" - return iso_string - - def mount(self, mountpoint :pathlib.Path, options=None, include_previously_known_options=True): - from ..helpers import findmnt - - try: - if mnt_info := findmnt(pathlib.Path(mountpoint), traverse=False): - log(f"Unmounting {mountpoint} as it was already mounted using {mnt_info}") - SysCommand(f"umount {mountpoint}") - except DiskError: - # No previously mounted device at the mountpoint - pass - - if not options: - options = [] - - try: - if include_previously_known_options and (cached_options := self.mount_options): - options += cached_options - except DiskError: - pass - - if not any('subvol=' in x for x in options): - options += f'subvol={self.name}' - - SysCommand(f"mount {self.partition.path} {mountpoint} -o {','.join(options)}") - log(f"{self} has successfully been mounted to {mountpoint}", level=logging.INFO, fg="gray") - - def unmount(self, recurse :bool = True): - SysCommand(f"umount {'-R' if recurse else ''} {self.full_path}") - log(f"Successfully unmounted {self}", level=logging.INFO, fg="gray") diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py new file mode 100644 index 00000000..12cf18ea --- /dev/null +++ b/archinstall/lib/disk/device_handler.py @@ -0,0 +1,599 @@ +from __future__ import annotations + +import json +import logging +import os +import time +from pathlib import Path +from typing import List, Dict, Any, Optional, TYPE_CHECKING + +from parted import ( # type: ignore + Disk, Geometry, FileSystem, + PartitionException, DiskLabelException, + getAllDevices, freshDisk, Partition, +) + +from .device_model import ( + DeviceModification, PartitionModification, + BDevice, _DeviceInfo, _PartitionInfo, + FilesystemType, Unit, PartitionTable, + ModificationStatus, get_lsblk_info, LsblkInfo, + _BtrfsSubvolumeInfo, get_all_lsblk_info, DiskEncryption +) + +from ..exceptions import DiskError, UnknownFilesystemFormat +from ..general import SysCommand, SysCallError, JSON +from ..luks import Luks2 +from ..output import log +from ..utils.util import is_subpath + +if TYPE_CHECKING: + _: Any + + +class DeviceHandler(object): + _TMP_BTRFS_MOUNT = Path('/mnt/arch_btrfs') + + def __init__(self): + self._devices: Dict[Path, BDevice] = {} + self.load_devices() + + @property + def devices(self) -> List[BDevice]: + return list(self._devices.values()) + + def load_devices(self): + block_devices = {} + + for device in getAllDevices(): + try: + disk = Disk(device) + except DiskLabelException as error: + if 'unrecognised disk label' in getattr(error, 'message', str(error)): + disk = freshDisk(device, PartitionTable.GPT.value) + else: + log(f'Unable to get disk from device: {device}', level=logging.DEBUG) + continue + + device_info = _DeviceInfo.from_disk(disk) + partition_infos = [] + + for partition in disk.partitions: + lsblk_info = get_lsblk_info(partition.path) + fs_type = self._determine_fs_type(partition, lsblk_info) + subvol_infos = [] + + if fs_type == FilesystemType.Btrfs: + subvol_infos = self.get_btrfs_info(partition.path) + + partition_infos.append( + _PartitionInfo.from_partition( + partition, + fs_type, + lsblk_info.partuuid, + lsblk_info.mountpoints, + subvol_infos + ) + ) + + block_device = BDevice(disk, device_info, partition_infos) + block_devices[block_device.device_info.path] = block_device + + self._devices = block_devices + + def _determine_fs_type( + self, + partition: Partition, + lsblk_info: Optional[LsblkInfo] = None + ) -> Optional[FilesystemType]: + try: + if partition.fileSystem: + return FilesystemType(partition.fileSystem.type) + elif lsblk_info is not None: + return FilesystemType(lsblk_info.fstype) if lsblk_info.fstype else None + return None + except ValueError: + log(f'Could not determine the filesystem: {partition.fileSystem}', level=logging.DEBUG) + + return None + + def get_device(self, path: Path) -> Optional[BDevice]: + return self._devices.get(path, None) + + def get_device_by_partition_path(self, partition_path: Path) -> Optional[BDevice]: + partition = self.find_partition(partition_path) + if partition: + return partition.disk.device + return None + + def find_partition(self, path: Path) -> Optional[_PartitionInfo]: + for device in self._devices.values(): + part = next(filter(lambda x: str(x.path) == str(path), device.partition_infos), None) + if part is not None: + return part + return None + + def get_uuid_for_path(self, path: Path) -> Optional[str]: + partition = self.find_partition(path) + return partition.partuuid if partition else None + + def get_btrfs_info(self, dev_path: Path) -> List[_BtrfsSubvolumeInfo]: + lsblk_info = get_lsblk_info(dev_path) + subvol_infos: List[_BtrfsSubvolumeInfo] = [] + + if not lsblk_info.mountpoint: + self.mount(dev_path, self._TMP_BTRFS_MOUNT, create_target_mountpoint=True) + mountpoint = self._TMP_BTRFS_MOUNT + else: + # when multiple subvolumes are mounted then the lsblk output may look like + # "mountpoint": "/mnt/archinstall/.snapshots" + # "mountpoints": ["/mnt/archinstall/.snapshots", "/mnt/archinstall/home", ..] + # so we'll determine the minimum common path and assume that's the root + path_strings = [str(m) for m in lsblk_info.mountpoints] + common_prefix = os.path.commonprefix(path_strings) + mountpoint = Path(common_prefix) + + try: + result = SysCommand(f'btrfs subvolume list {mountpoint}') + except SysCallError as err: + log(f'Failed to read btrfs subvolume information: {err}', level=logging.DEBUG) + return subvol_infos + + if result.exit_code == 0: + try: + if decoded := result.decode('utf-8'): + # ID 256 gen 16 top level 5 path @ + for line in decoded.splitlines(): + # expected output format: + # ID 257 gen 8 top level 5 path @home + name = Path(line.split(' ')[-1]) + sub_vol_mountpoint = lsblk_info.btrfs_subvol_info.get(name, None) + subvol_infos.append(_BtrfsSubvolumeInfo(name, sub_vol_mountpoint)) + except json.decoder.JSONDecodeError as err: + log(f"Could not decode lsblk JSON: {result}", fg="red", level=logging.ERROR) + raise err + + if not lsblk_info.mountpoint: + self.umount(dev_path) + + return subvol_infos + + def _perform_formatting( + self, + fs_type: FilesystemType, + path: Path, + additional_parted_options: List[str] = [] + ): + options = [] + command = '' + + match fs_type: + case FilesystemType.Btrfs: + options += ['-f'] + command += 'mkfs.btrfs' + case FilesystemType.Fat16: + options += ['-F16'] + command += 'mkfs.fat' + case FilesystemType.Fat32: + options += ['-F32'] + command += 'mkfs.fat' + case FilesystemType.Ext2: + options += ['-F'] + command += 'mkfs.ext2' + case FilesystemType.Ext3: + options += ['-F'] + command += 'mkfs.ext3' + case FilesystemType.Ext4: + options += ['-F'] + command += 'mkfs.ext4' + case FilesystemType.Xfs: + options += ['-f'] + command += 'mkfs.xfs' + case FilesystemType.F2fs: + options += ['-f'] + command += 'mkfs.f2fs' + case FilesystemType.Ntfs: + options += ['-f', '-Q'] + command += 'mkfs.ntfs' + case FilesystemType.Reiserfs: + command += 'mkfs.reiserfs' + case _: + raise UnknownFilesystemFormat(f'Filetype "{fs_type.value}" is not supported') + + options += additional_parted_options + options_str = ' '.join(options) + + log(f'Formatting filesystem: /usr/bin/{command} {options_str} {path}') + + try: + if (handle := SysCommand(f"/usr/bin/{command} {options_str} {path}")).exit_code != 0: + mkfs_error = handle.decode() + raise DiskError(f'Could not format {path} with {fs_type.value}: {mkfs_error}') + except SysCallError as error: + msg = f'Could not format {path} with {fs_type.value}: {error.message}' + log(msg, fg='red') + raise DiskError(msg) from error + + def _perform_enc_formatting( + self, + dev_path: Path, + mapper_name: Optional[str], + fs_type: FilesystemType, + enc_conf: DiskEncryption + ): + luks_handler = Luks2( + dev_path, + mapper_name=mapper_name, + password=enc_conf.encryption_password + ) + + key_file = luks_handler.encrypt() + + log(f'Unlocking luks2 device: {dev_path}', level=logging.DEBUG) + luks_handler.unlock(key_file=key_file) + + if not luks_handler.mapper_dev: + raise DiskError('Failed to unlock luks device') + + log(f'luks2 formatting mapper dev: {luks_handler.mapper_dev}', level=logging.INFO) + self._perform_formatting(fs_type, luks_handler.mapper_dev) + + log(f'luks2 locking device: {dev_path}', level=logging.INFO) + luks_handler.lock() + + def format( + self, + modification: DeviceModification, + enc_conf: Optional['DiskEncryption'] = None + ): + """ + Format can be given an overriding path, for instance /dev/null to test + the formatting functionality and in essence the support for the given filesystem. + """ + + # verify that all partitions have a path set (which implies that they have been created) + missing_path = next(filter(lambda x: x.dev_path is None, modification.partitions), None) + if missing_path is not None: + raise ValueError('When formatting, all partitions must have a path set') + + # crypto luks is not known to parted and can therefore not + # be used as a filesystem type in that sense; + invalid_fs_type = next(filter(lambda x: x.fs_type is FilesystemType.Crypto_luks, modification.partitions), None) + if invalid_fs_type is not None: + raise ValueError('Crypto luks cannot be set as a filesystem type') + + # make sure all devices are unmounted + self._umount_all_existing(modification) + + for part_mod in modification.partitions: + # partition will be encrypted + if enc_conf is not None and part_mod in enc_conf.partitions: + self._perform_enc_formatting( + part_mod.real_dev_path, + part_mod.mapper_name, + part_mod.fs_type, + enc_conf + ) + else: + self._perform_formatting(part_mod.fs_type, part_mod.real_dev_path) + + def _perform_partitioning( + self, + part_mod: PartitionModification, + block_device: BDevice, + disk: Disk, + requires_delete: bool + ): + # when we require a delete and the partition to be (re)created + # already exists then we have to delete it first + if requires_delete and part_mod.status in [ModificationStatus.Modify, ModificationStatus.Delete]: + log(f'Delete existing partition: {part_mod.real_dev_path}', level=logging.INFO) + part_info = self.find_partition(part_mod.real_dev_path) + + if not part_info: + raise DiskError(f'No partition for dev path found: {part_mod.real_dev_path}') + + disk.deletePartition(part_info.partition) + disk.commit() + + if part_mod.status == ModificationStatus.Delete: + return + + start_sector = part_mod.start.convert( + Unit.sectors, + block_device.device_info.sector_size + ) + + length_sector = part_mod.length.convert( + Unit.sectors, + block_device.device_info.sector_size + ) + + geometry = Geometry( + device=block_device.disk.device, + start=start_sector.value, + length=length_sector.value + ) + + filesystem = FileSystem(type=part_mod.fs_type.value, geometry=geometry) + + partition = Partition( + disk=disk, + type=part_mod.type.get_partition_code(), + fs=filesystem, + geometry=geometry + ) + + for flag in part_mod.flags: + partition.setFlag(flag.value) + + log(f'\tType: {part_mod.type.value}', level=logging.DEBUG) + log(f'\tFilesystem: {part_mod.fs_type.value}', level=logging.DEBUG) + log(f'\tGeometry: {start_sector.value} start sector, {length_sector.value} length', level=logging.DEBUG) + + try: + disk.addPartition(partition=partition, constraint=disk.device.optimalAlignedConstraint) + disk.commit() + + # the creation will take a bit of time + time.sleep(3) + + # the partition has a real path now as it was created + part_mod.dev_path = Path(partition.path) + + info = get_lsblk_info(part_mod.dev_path) + + if not info.partuuid: + raise DiskError(f'Unable to determine new partition uuid: {part_mod.dev_path}') + + part_mod.partuuid = info.partuuid + part_mod.uuid = info.uuid + except PartitionException as ex: + raise DiskError(f'Unable to add partition, most likely due to overlapping sectors: {ex}') from ex + + def create_btrfs_volumes( + self, + part_mod: PartitionModification, + enc_conf: Optional['DiskEncryption'] = None + ): + log(f'Creating subvolumes: {part_mod.real_dev_path}', level=logging.INFO) + + luks_handler = None + + # unlock the partition first if it's encrypted + if enc_conf is not None and part_mod in enc_conf.partitions: + if not part_mod.mapper_name: + raise ValueError('No device path specified for modification') + + luks_handler = self.unlock_luks2_dev( + part_mod.real_dev_path, + part_mod.mapper_name, + enc_conf.encryption_password + ) + + if not luks_handler.mapper_dev: + raise DiskError('Failed to unlock luks device') + + self.mount(luks_handler.mapper_dev, self._TMP_BTRFS_MOUNT, create_target_mountpoint=True) + else: + self.mount(part_mod.real_dev_path, self._TMP_BTRFS_MOUNT, create_target_mountpoint=True) + + for sub_vol in part_mod.btrfs_subvols: + log(f'Creating subvolume: {sub_vol.name}', level=logging.DEBUG) + + if luks_handler is not None: + subvol_path = self._TMP_BTRFS_MOUNT / sub_vol.name + else: + subvol_path = self._TMP_BTRFS_MOUNT / sub_vol.name + + SysCommand(f"btrfs subvolume create {subvol_path}") + + if sub_vol.nodatacow: + if (result := SysCommand(f'chattr +C {subvol_path}')).exit_code != 0: + raise DiskError(f'Could not set nodatacow attribute at {subvol_path}: {result.decode()}') + + if sub_vol.compress: + if (result := SysCommand(f'chattr +c {subvol_path}')).exit_code != 0: + raise DiskError(f'Could not set compress attribute at {subvol_path}: {result}') + + if luks_handler is not None and luks_handler.mapper_dev is not None: + self.umount(luks_handler.mapper_dev) + luks_handler.lock() + else: + self.umount(part_mod.real_dev_path) + + def unlock_luks2_dev(self, dev_path: Path, mapper_name: str, enc_password: str) -> Luks2: + luks_handler = Luks2(dev_path, mapper_name=mapper_name, password=enc_password) + + if not luks_handler.is_unlocked(): + luks_handler.unlock() + + if not luks_handler.is_unlocked(): + raise DiskError(f'Failed to unlock luks2 device: {dev_path}') + + return luks_handler + + def _umount_all_existing(self, modification: DeviceModification): + log(f'Unmounting all partitions: {modification.device_path}', level=logging.INFO) + + existing_partitions = self._devices[modification.device_path].partition_infos + + for partition in existing_partitions: + log(f'Unmounting: {partition.path}', level=logging.DEBUG) + + # un-mount for existing encrypted partitions + if partition.fs_type == FilesystemType.Crypto_luks: + Luks2(partition.path).lock() + else: + self.umount(partition.path, recursive=True) + + def partition( + self, + modification: DeviceModification, + partition_table: Optional[PartitionTable] = None + ): + """ + Create a partition table on the block device and create all partitions. + """ + if modification.wipe: + if partition_table is None: + raise ValueError('Modification is marked as wipe but no partitioning table was provided') + + if partition_table.MBR and len(modification.partitions) > 3: + raise DiskError('Too many partitions on disk, MBR disks can only have 3 primary partitions') + + # make sure all devices are unmounted + self._umount_all_existing(modification) + + # WARNING: the entire device will be wiped and all data lost + if modification.wipe: + self.wipe_dev(modification.device) + part_table = partition_table.value if partition_table else None + disk = freshDisk(modification.device.disk.device, part_table) + else: + log(f'Use existing device: {modification.device_path}') + disk = modification.device.disk + + log(f'Creating partitions: {modification.device_path}') + + # TODO sort by delete first + + for part_mod in modification.partitions: + # don't touch existing partitions + if part_mod.exists(): + continue + + # if the entire disk got nuked then we don't have to delete + # any existing partitions anymore because they're all gone already + requires_delete = modification.wipe is False + self._perform_partitioning(part_mod, modification.device, disk, requires_delete=requires_delete) + + self.partprobe(modification.device.device_info.path) + + def mount( + self, + dev_path: Path, + target_mountpoint: Path, + mount_fs: Optional[str] = None, + create_target_mountpoint: bool = True, + options: List[str] = [] + ): + if create_target_mountpoint and not target_mountpoint.exists(): + target_mountpoint.mkdir(parents=True, exist_ok=True) + + if not target_mountpoint.exists(): + raise ValueError('Target mountpoint does not exist') + + lsblk_info = get_lsblk_info(dev_path) + if target_mountpoint in lsblk_info.mountpoints: + log(f'Device already mounted at {target_mountpoint}') + return + + str_options = ','.join(options) + str_options = f'-o {str_options}' if str_options else '' + + mount_fs = f'-t {mount_fs}' if mount_fs else '' + + command = f'mount {mount_fs} {str_options} {dev_path} {target_mountpoint}' + + log(f'Mounting {dev_path}: command', level=logging.DEBUG) + + try: + result = SysCommand(command) + if result.exit_code != 0: + raise DiskError(f'Could not mount {dev_path}: {command}\n{result.decode()}') + except SysCallError as err: + raise DiskError(f'Could not mount {dev_path}: {command}\n{err.message}') + + def umount(self, mountpoint: Path, recursive: bool = False): + try: + lsblk_info = get_lsblk_info(mountpoint) + except SysCallError as ex: + # this could happen if before partitioning the device contained 3 partitions + # and after partitioning only 2 partitions were created, then the modifications object + # will have a reference to /dev/sX3 which is being tried to umount here now + if 'not a block device' in ex.message: + return + raise ex + + if len(lsblk_info.mountpoints) > 0: + log(f'Partition {mountpoint} is currently mounted at: {[str(m) for m in lsblk_info.mountpoints]}', level=logging.DEBUG) + + for mountpoint in lsblk_info.mountpoints: + log(f'Unmounting mountpoint: {mountpoint}', level=logging.DEBUG) + + command = 'umount' + + if recursive: + command += ' -R' + + SysCommand(f'{command} {mountpoint}') + + def detect_pre_mounted_mods(self, base_mountpoint: Path) -> List[DeviceModification]: + part_mods: Dict[Path, List[PartitionModification]] = {} + + for device in self.devices: + for part_info in device.partition_infos: + for mountpoint in part_info.mountpoints: + if is_subpath(mountpoint, base_mountpoint): + path = Path(part_info.disk.device.path) + part_mods.setdefault(path, []) + part_mods[path].append(PartitionModification.from_existing_partition(part_info)) + break + + device_mods: List[DeviceModification] = [] + for device_path, mods in part_mods.items(): + device_mod = DeviceModification(self._devices[device_path], False, mods) + device_mods.append(device_mod) + + return device_mods + + def partprobe(self, path: Optional[Path] = None): + if path is not None: + command = f'partprobe {path}' + else: + command = 'partprobe' + + try: + result = SysCommand(command) + if result.exit_code != 0: + log(f'Error calling partprobe: {result.decode()}', level=logging.DEBUG) + raise DiskError(f'Could not perform partprobe on {path}: {result.decode()}') + except SysCallError as error: + log(f"partprobe experienced an error with {path}: {error}", level=logging.DEBUG) + + def _wipe(self, dev_path: Path): + """ + Wipe a device (partition or otherwise) of meta-data, be it file system, LVM, etc. + @param dev_path: Device path of the partition to be wiped. + @type dev_path: str + """ + with open(dev_path, 'wb') as p: + p.write(bytearray(1024)) + + def wipe_dev(self, block_device: BDevice): + """ + Wipe the block device of meta-data, be it file system, LVM, etc. + This is not intended to be secure, but rather to ensure that + auto-discovery tools don't recognize anything here. + """ + log(f'Wiping partitions and metadata: {block_device.device_info.path}') + for partition in block_device.partition_infos: + self._wipe(partition.path) + + self._wipe(block_device.device_info.path) + + +device_handler = DeviceHandler() + + +def disk_layouts() -> str: + try: + lsblk_info = get_all_lsblk_info() + return json.dumps(lsblk_info, indent=4, sort_keys=True, cls=JSON) + except SysCallError as err: + log(f"Could not return disk layouts: {err}", level=logging.WARNING, fg="yellow") + return '' + except json.decoder.JSONDecodeError as err: + log(f"Could not return disk layouts: {err}", level=logging.WARNING, fg="yellow") + return '' diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py new file mode 100644 index 00000000..0270a4dd --- /dev/null +++ b/archinstall/lib/disk/device_model.py @@ -0,0 +1,1033 @@ +from __future__ import annotations + +import dataclasses +import json +import logging +import math +import time +import uuid +from dataclasses import dataclass, field +from enum import Enum +from enum import auto +from pathlib import Path +from typing import Optional, List, Dict, TYPE_CHECKING, Any +from typing import Union + +import parted # type: ignore +from parted import Disk, Geometry, Partition + +from ..exceptions import DiskError, SysCallError +from ..general import SysCommand +from ..output import log +from ..storage import storage + +if TYPE_CHECKING: + _: Any + + +class DiskLayoutType(Enum): + Default = 'default_layout' + Manual = 'manual_partitioning' + Pre_mount = 'pre_mounted_config' + + def display_msg(self) -> str: + match self: + case DiskLayoutType.Default: return str(_('Use a best-effort default partition layout')) + case DiskLayoutType.Manual: return str(_('Manual Partitioning')) + case DiskLayoutType.Pre_mount: return str(_('Pre-mounted configuration')) + + +@dataclass +class DiskLayoutConfiguration: + config_type: DiskLayoutType + device_modifications: List[DeviceModification] = field(default_factory=list) + # used for pre-mounted config + relative_mountpoint: Optional[Path] = None + + def __post_init__(self): + if self.config_type == DiskLayoutType.Pre_mount and self.relative_mountpoint is None: + raise ValueError('Must set a relative mountpoint when layout type is pre-mount"') + + def __dump__(self) -> Dict[str, Any]: + return { + 'config_type': self.config_type.value, + 'device_modifications': [mod.__dump__() for mod in self.device_modifications] + } + + @classmethod + def parse_arg(cls, disk_config: Dict[str, List[Dict[str, Any]]]) -> Optional[DiskLayoutConfiguration]: + from .device_handler import device_handler + + device_modifications: List[DeviceModification] = [] + config_type = disk_config.get('config_type', None) + + if not config_type: + raise ValueError('Missing disk layout configuration: config_type') + + config = DiskLayoutConfiguration( + config_type=DiskLayoutType(config_type), + device_modifications=device_modifications + ) + + for entry in disk_config.get('device_modifications', []): + device_path = Path(entry.get('device', None)) if entry.get('device', None) else None + + if not device_path: + continue + + device = device_handler.get_device(device_path) + + if not device: + continue + + device_modification = DeviceModification( + wipe=entry.get('wipe', False), + device=device + ) + + device_partitions: List[PartitionModification] = [] + + for partition in entry.get('partitions', []): + device_partition = PartitionModification( + status=ModificationStatus(partition['status']), + fs_type=FilesystemType(partition['fs_type']), + start=Size.parse_args(partition['start']), + length=Size.parse_args(partition['length']), + mount_options=partition['mount_options'], + mountpoint=Path(partition['mountpoint']) if partition['mountpoint'] else None, + type=PartitionType(partition['type']), + flags=[PartitionFlag[f] for f in partition.get('flags', [])], + btrfs_subvols=SubvolumeModification.parse_args(partition.get('btrfs', [])), + ) + # special 'invisible attr to internally identify the part mod + setattr(device_partition, '_obj_id', partition['obj_id']) + device_partitions.append(device_partition) + + device_modification.partitions = device_partitions + device_modifications.append(device_modification) + + return config + + +class PartitionTable(Enum): + GPT = 'gpt' + MBR = 'msdos' + + +class Unit(Enum): + B = 1 # byte + kB = 1000**1 # kilobyte + MB = 1000**2 # megabyte + GB = 1000**3 # gigabyte + TB = 1000**4 # terabyte + PB = 1000**5 # petabyte + EB = 1000**6 # exabyte + ZB = 1000**7 # zettabyte + YB = 1000**8 # yottabyte + + KiB = 1024**1 # kibibyte + MiB = 1024**2 # mebibyte + GiB = 1024**3 # gibibyte + TiB = 1024**4 # tebibyte + PiB = 1024**5 # pebibyte + EiB = 1024**6 # exbibyte + ZiB = 1024**7 # zebibyte + YiB = 1024**8 # yobibyte + + sectors = 'sectors' # size in sector + + Percent = '%' # size in percentile + + +@dataclass +class Size: + value: int + unit: Unit + sector_size: Optional[Size] = None # only required when unit is sector + total_size: Optional[Size] = None # required when operating on percentages + + def __post_init__(self): + if self.unit == Unit.sectors and self.sector_size is None: + raise ValueError('Sector size is required when unit is sectors') + elif self.unit == Unit.Percent: + if self.value < 0 or self.value > 100: + raise ValueError('Percentage must be between 0 and 100') + elif self.total_size is None: + raise ValueError('Total size is required when unit is percentage') + + @property + def _total_size(self) -> Size: + """ + Save method to get the total size, mainly to satisfy mypy + This shouldn't happen as the Size object fails instantiation on missing total size + """ + if self.unit == Unit.Percent and self.total_size is None: + raise ValueError('Percent unit size must specify a total size') + return self.total_size # type: ignore + + def __dump__(self) -> Dict[str, Any]: + return { + 'value': self.value, + 'unit': self.unit.name, + 'sector_size': self.sector_size.__dump__() if self.sector_size else None, + 'total_size': self._total_size.__dump__() if self._total_size else None + } + + @classmethod + def parse_args(cls, size_arg: Dict[str, Any]) -> Size: + sector_size = size_arg['sector_size'] + total_size = size_arg['total_size'] + + return Size( + size_arg['value'], + Unit[size_arg['unit']], + Size.parse_args(sector_size) if sector_size else None, + Size.parse_args(total_size) if total_size else None + ) + + def convert( + self, + target_unit: Unit, + sector_size: Optional[Size] = None, + total_size: Optional[Size] = None + ) -> Size: + if target_unit == Unit.sectors and sector_size is None: + raise ValueError('If target has unit sector, a sector size must be provided') + + # not sure why we would ever wanna convert to percentages + if target_unit == Unit.Percent and total_size is None: + raise ValueError('Missing paramter total size to be able to convert to percentage') + + if self.unit == target_unit: + return self + elif self.unit == Unit.Percent: + amount = int(self._total_size._normalize() * (self.value / 100)) + return Size(amount, Unit.B) + elif self.unit == Unit.sectors: + norm = self._normalize() + return Size(norm, Unit.B).convert(target_unit, sector_size) + else: + if target_unit == Unit.sectors and sector_size is not None: + norm = self._normalize() + sectors = math.ceil(norm / sector_size.value) + return Size(sectors, Unit.sectors, sector_size) + else: + value = int(self._normalize() / target_unit.value) # type: ignore + return Size(value, target_unit) + + def format_size( + self, + target_unit: Unit, + sector_size: Optional[Size] = None + ) -> str: + if self.unit == Unit.Percent: + return f'{self.value}%' + else: + target_size = self.convert(target_unit, sector_size) + return f'{target_size.value} {target_unit.name}' + + def _normalize(self) -> int: + """ + will normalize the value of the unit to Byte + """ + if self.unit == Unit.Percent: + return self.convert(Unit.B).value + elif self.unit == Unit.sectors and self.sector_size is not None: + return self.value * self.sector_size._normalize() + return int(self.value * self.unit.value) # type: ignore + + def __sub__(self, other: Size) -> Size: + src_norm = self._normalize() + dest_norm = other._normalize() + return Size(abs(src_norm - dest_norm), Unit.B) + + def __lt__(self, other): + return self._normalize() < other._normalize() + + def __le__(self, other): + return self._normalize() <= other._normalize() + + def __eq__(self, other): + return self._normalize() == other._normalize() + + def __ne__(self, other): + return self._normalize() != other._normalize() + + def __gt__(self, other): + return self._normalize() > other._normalize() + + def __ge__(self, other): + return self._normalize() >= other._normalize() + + +@dataclass +class _BtrfsSubvolumeInfo: + name: Path + mountpoint: Optional[Path] + + +@dataclass +class _PartitionInfo: + partition: Partition + name: str + type: PartitionType + fs_type: FilesystemType + path: Path + start: Size + length: Size + flags: List[PartitionFlag] + partuuid: str + disk: Disk + mountpoints: List[Path] + btrfs_subvol_infos: List[_BtrfsSubvolumeInfo] = field(default_factory=list) + + def as_json(self) -> Dict[str, Any]: + info = { + 'Name': self.name, + 'Type': self.type.value, + 'Filesystem': self.fs_type.value if self.fs_type else str(_('Unknown')), + 'Path': str(self.path), + 'Start': self.start.format_size(Unit.MiB), + 'Length': self.length.format_size(Unit.MiB), + 'Flags': ', '.join([f.name for f in self.flags]) + } + + if self.btrfs_subvol_infos: + info['Btrfs vol.'] = f'{len(self.btrfs_subvol_infos)} subvolumes' + + return info + + @classmethod + def from_partition( + cls, + partition: Partition, + fs_type: FilesystemType, + partuuid: str, + mountpoints: List[Path], + btrfs_subvol_infos: List[_BtrfsSubvolumeInfo] = [] + ) -> _PartitionInfo: + partition_type = PartitionType.get_type_from_code(partition.type) + flags = [f for f in PartitionFlag if partition.getFlag(f.value)] + + start = Size( + partition.geometry.start, + Unit.sectors, + Size(partition.disk.device.sectorSize, Unit.B) + ) + + length = Size(int(partition.getLength(unit='B')), Unit.B) + + return _PartitionInfo( + partition=partition, + name=partition.get_name(), + type=partition_type, + fs_type=fs_type, + path=partition.path, + start=start, + length=length, + flags=flags, + partuuid=partuuid, + disk=partition.disk, + mountpoints=mountpoints, + btrfs_subvol_infos=btrfs_subvol_infos + ) + + +@dataclass +class _DeviceInfo: + model: str + path: Path + type: str + total_size: Size + free_space_regions: List[DeviceGeometry] + sector_size: Size + read_only: bool + dirty: bool + + def as_json(self) -> Dict[str, Any]: + total_free_space = sum([region.get_length(unit=Unit.MiB) for region in self.free_space_regions]) + return { + 'Model': self.model, + 'Path': str(self.path), + 'Type': self.type, + 'Size': self.total_size.format_size(Unit.MiB), + 'Free space': int(total_free_space), + 'Sector size': self.sector_size.value, + 'Read only': self.read_only + } + + @classmethod + def from_disk(cls, disk: Disk) -> _DeviceInfo: + device = disk.device + device_type = parted.devices[device.type] + + sector_size = Size(device.sectorSize, Unit.B) + free_space = [DeviceGeometry(g, sector_size) for g in disk.getFreeSpaceRegions()] + + return _DeviceInfo( + model=device.model.strip(), + path=Path(device.path), + type=device_type, + sector_size=sector_size, + total_size=Size(int(device.getLength(unit='B')), Unit.B), + free_space_regions=free_space, + read_only=device.readOnly, + dirty=device.dirty + ) + + +@dataclass +class SubvolumeModification: + name: Path + mountpoint: Optional[Path] = None + compress: bool = False + nodatacow: bool = False + + @classmethod + def from_existing_subvol_info(cls, info: _BtrfsSubvolumeInfo) -> SubvolumeModification: + return SubvolumeModification(info.name, mountpoint=info.mountpoint) + + @classmethod + def parse_args(cls, subvol_args: List[Dict[str, Any]]) -> List[SubvolumeModification]: + mods = [] + for entry in subvol_args: + if not entry.get('name', None) or not entry.get('mountpoint', None): + log(f'Subvolume arg is missing name: {entry}', level=logging.DEBUG) + continue + + mountpoint = Path(entry['mountpoint']) if entry['mountpoint'] else None + + mods.append( + SubvolumeModification( + entry['name'], + mountpoint, + entry.get('compress', False), + entry.get('nodatacow', False) + ) + ) + + return mods + + @property + def mount_options(self) -> List[str]: + options = [] + options += ['compress'] if self.compress else [] + options += ['nodatacow'] if self.nodatacow else [] + return options + + @property + def relative_mountpoint(self) -> Path: + """ + Will return the relative path based on the anchor + e.g. Path('/mnt/test') -> Path('mnt/test') + """ + if self.mountpoint is not None: + return self.mountpoint.relative_to(self.mountpoint.anchor) + + raise ValueError('Mountpoint is not specified') + + def is_root(self, relative_mountpoint: Optional[Path] = None) -> bool: + if self.mountpoint: + if relative_mountpoint is not None: + return self.mountpoint.relative_to(relative_mountpoint) == Path('.') + return self.mountpoint == Path('/') + return False + + def __dump__(self) -> Dict[str, Any]: + return { + 'name': str(self.name), + 'mountpoint': str(self.mountpoint), + 'compress': self.compress, + 'nodatacow': self.nodatacow + } + + def as_json(self) -> Dict[str, Any]: + return { + 'name': str(self.name), + 'mountpoint': str(self.mountpoint), + 'compress': self.compress, + 'nodatacow': self.nodatacow + } + + +class DeviceGeometry: + def __init__(self, geometry: Geometry, sector_size: Size): + self._geometry = geometry + self._sector_size = sector_size + + @property + def start(self) -> int: + return self._geometry.start + + @property + def end(self) -> int: + return self._geometry.end + + def get_length(self, unit: Unit = Unit.sectors) -> int: + return self._geometry.getLength(unit.name) + + def as_json(self) -> Dict[str, Any]: + return { + 'Sector size': self._sector_size.value, + 'Start sector': self._geometry.start, + 'End sector': self._geometry.end, + 'Length': self._geometry.getLength() + } + + +@dataclass +class BDevice: + disk: Disk + device_info: _DeviceInfo + partition_infos: List[_PartitionInfo] + + def __hash__(self): + return hash(self.disk.device.path) + + +class PartitionType(Enum): + Boot = 'boot' + Primary = 'primary' + + @classmethod + def get_type_from_code(cls, code: int) -> PartitionType: + if code == parted.PARTITION_NORMAL: + return PartitionType.Primary + + raise DiskError(f'Partition code not supported: {code}') + + def get_partition_code(self) -> Optional[int]: + if self == PartitionType.Primary: + return parted.PARTITION_NORMAL + elif self == PartitionType.Boot: + return parted.PARTITION_BOOT + return None + + +class PartitionFlag(Enum): + Boot = 1 + + +class FilesystemType(Enum): + Btrfs = 'btrfs' + Ext2 = 'ext2' + Ext3 = 'ext3' + Ext4 = 'ext4' + F2fs = 'f2fs' + Fat16 = 'fat16' + Fat32 = 'fat32' + Ntfs = 'ntfs' + Reiserfs = 'reiserfs' + Xfs = 'xfs' + + # this is not a FS known to parted, so be careful + # with the usage from this enum + Crypto_luks = 'crypto_LUKS' + + def is_crypto(self) -> bool: + return self == FilesystemType.Crypto_luks + + @property + def fs_type_mount(self) -> str: + match self: + case FilesystemType.Ntfs: return 'ntfs3' + case FilesystemType.Fat32: return 'vfat' + case _: return self.value # type: ignore + + @property + def installation_pkg(self) -> Optional[str]: + match self: + case FilesystemType.Btrfs: return 'btrfs-progs' + case FilesystemType.Xfs: return 'xfsprogs' + case FilesystemType.F2fs: return 'f2fs-tools' + case _: return None + + @property + def installation_module(self) -> Optional[str]: + match self: + case FilesystemType.Btrfs: return 'btrfs' + case _: return None + + @property + def installation_binary(self) -> Optional[str]: + match self: + case FilesystemType.Btrfs: return '/usr/bin/btrfs' + case _: return None + + @property + def installation_hooks(self) -> Optional[str]: + match self: + case FilesystemType.Btrfs: return 'btrfs' + case _: return None + + +class ModificationStatus(Enum): + Exist = 'existing' + Modify = 'modify' + Delete = 'delete' + Create = 'create' + + +@dataclass +class PartitionModification: + status: ModificationStatus + type: PartitionType + start: Size + length: Size + fs_type: FilesystemType + mountpoint: Optional[Path] = None + mount_options: List[str] = field(default_factory=list) + flags: List[PartitionFlag] = field(default_factory=list) + btrfs_subvols: List[SubvolumeModification] = field(default_factory=list) + + # only set if the device was created or exists + dev_path: Optional[Path] = None + partuuid: Optional[str] = None + uuid: Optional[str] = None + + def __post_init__(self): + # needed to use the object as a dictionary key due to hash func + if not hasattr(self, '_obj_id'): + self._obj_id = uuid.uuid4() + + if self.is_exists_or_modify() and not self.dev_path: + raise ValueError('If partition marked as existing a path must be set') + + def __hash__(self): + return hash(self._obj_id) + + @property + def obj_id(self) -> str: + if hasattr(self, '_obj_id'): + return str(self._obj_id) + return '' + + @property + def real_dev_path(self) -> Path: + if self.dev_path is None: + raise ValueError('Device path was not set') + return self.dev_path + + @classmethod + def from_existing_partition(cls, partition_info: _PartitionInfo) -> PartitionModification: + if partition_info.btrfs_subvol_infos: + mountpoint = None + subvol_mods = [] + for info in partition_info.btrfs_subvol_infos: + subvol_mods.append( + SubvolumeModification.from_existing_subvol_info(info) + ) + else: + mountpoint = partition_info.mountpoints[0] if partition_info.mountpoints else None + subvol_mods = [] + + return PartitionModification( + status=ModificationStatus.Exist, + type=partition_info.type, + start=partition_info.start, + length=partition_info.length, + fs_type=partition_info.fs_type, + dev_path=partition_info.path, + flags=partition_info.flags, + mountpoint=mountpoint, + btrfs_subvols=subvol_mods + ) + + @property + def relative_mountpoint(self) -> Path: + """ + Will return the relative path based on the anchor + e.g. Path('/mnt/test') -> Path('mnt/test') + """ + if self.mountpoint: + return self.mountpoint.relative_to(self.mountpoint.anchor) + + raise ValueError('Mountpoint is not specified') + + def is_boot(self) -> bool: + return PartitionFlag.Boot in self.flags + + def is_root(self, relative_mountpoint: Optional[Path] = None) -> bool: + if relative_mountpoint is not None and self.mountpoint is not None: + return self.mountpoint.relative_to(relative_mountpoint) == Path('.') + elif self.mountpoint is not None: + return Path('/') == self.mountpoint + else: + for subvol in self.btrfs_subvols: + if subvol.is_root(relative_mountpoint): + return True + + return False + + def is_modify(self) -> bool: + return self.status == ModificationStatus.Modify + + def exists(self) -> bool: + return self.status == ModificationStatus.Exist + + def is_exists_or_modify(self) -> bool: + return self.status in [ModificationStatus.Exist, ModificationStatus.Modify] + + @property + def mapper_name(self) -> Optional[str]: + if self.dev_path: + return f'{storage.get("ENC_IDENTIFIER", "ai")}{self.dev_path.name}' + return None + + def set_flag(self, flag: PartitionFlag): + if flag not in self.flags: + self.flags.append(flag) + + def invert_flag(self, flag: PartitionFlag): + if flag in self.flags: + self.flags = [f for f in self.flags if f != flag] + else: + self.set_flag(flag) + + def json(self) -> Dict[str, Any]: + """ + Called for configuration settings + """ + return { + 'obj_id': self.obj_id, + 'status': self.status.value, + 'type': self.type.value, + 'start': self.start.__dump__(), + 'length': self.length.__dump__(), + 'fs_type': self.fs_type.value, + 'mountpoint': str(self.mountpoint) if self.mountpoint else None, + 'mount_options': self.mount_options, + 'flags': [f.name for f in self.flags], + 'btrfs': [vol.__dump__() for vol in self.btrfs_subvols] + } + + def as_json(self) -> Dict[str, Any]: + """ + Called for displaying data in table format + """ + info = { + 'Status': self.status.value, + 'Device': str(self.dev_path) if self.dev_path else '', + 'Type': self.type.value, + 'Start': self.start.format_size(Unit.MiB), + 'Length': self.length.format_size(Unit.MiB), + 'FS type': self.fs_type.value, + 'Mountpoint': self.mountpoint if self.mountpoint else '', + 'Mount options': ', '.join(self.mount_options), + 'Flags': ', '.join([f.name for f in self.flags]), + } + + if self.btrfs_subvols: + info['Btrfs vol.'] = f'{len(self.btrfs_subvols)} subvolumes' + + return info + + +@dataclass +class DeviceModification: + device: BDevice + wipe: bool + partitions: List[PartitionModification] = field(default_factory=list) + + @property + def device_path(self) -> Path: + return self.device.device_info.path + + def add_partition(self, partition: PartitionModification): + self.partitions.append(partition) + + def get_boot_partition(self) -> Optional[PartitionModification]: + liltered = filter(lambda x: x.is_boot(), self.partitions) + return next(liltered, None) + + def get_root_partition(self, relative_path: Optional[Path]) -> Optional[PartitionModification]: + filtered = filter(lambda x: x.is_root(relative_path), self.partitions) + return next(filtered, None) + + def __dump__(self) -> Dict[str, Any]: + """ + Called when generating configuration files + """ + return { + 'device': str(self.device.device_info.path), + 'wipe': self.wipe, + 'partitions': [p.json() for p in self.partitions] + } + + +class EncryptionType(Enum): + NoEncryption = "no_encryption" + Partition = "partition" + + @classmethod + def _encryption_type_mapper(cls) -> Dict[str, 'EncryptionType']: + return { + # str(_('Full disk encryption')): EncryptionType.FullDiskEncryption, + str(_('Partition encryption')): EncryptionType.Partition + } + + @classmethod + def text_to_type(cls, text: str) -> 'EncryptionType': + mapping = cls._encryption_type_mapper() + return mapping[text] + + @classmethod + def type_to_text(cls, type_: 'EncryptionType') -> str: + mapping = cls._encryption_type_mapper() + type_to_text = {type_: text for text, type_ in mapping.items()} + return type_to_text[type_] + + +@dataclass +class DiskEncryption: + encryption_type: EncryptionType = EncryptionType.Partition + encryption_password: str = '' + partitions: List[PartitionModification] = field(default_factory=list) + hsm_device: Optional[Fido2Device] = None + + def should_generate_encryption_file(self, part_mod: PartitionModification) -> bool: + return part_mod in self.partitions and part_mod.mountpoint != Path('/') + + def json(self) -> Dict[str, Any]: + obj: Dict[str, Any] = { + 'encryption_type': self.encryption_type.value, + 'partitions': [p.obj_id for p in self.partitions] + } + + if self.hsm_device: + obj['hsm_device'] = self.hsm_device.json() + + return obj + + @classmethod + def parse_arg( + cls, + disk_config: DiskLayoutConfiguration, + arg: Dict[str, Any], + password: str = '' + ) -> 'DiskEncryption': + enc_partitions = [] + for mod in disk_config.device_modifications: + for part in mod.partitions: + if part.obj_id in arg.get('partitions', []): + enc_partitions.append(part) + + enc = DiskEncryption( + EncryptionType(arg['encryption_type']), + password, + enc_partitions + ) + + if hsm := arg.get('hsm_device', None): + enc.hsm_device = Fido2Device.parse_arg(hsm) + + return enc + + +@dataclass +class Fido2Device: + path: Path + manufacturer: str + product: str + + def json(self) -> Dict[str, str]: + return { + 'path': str(self.path), + 'manufacturer': self.manufacturer, + 'product': self.product + } + + @classmethod + def parse_arg(cls, arg: Dict[str, str]) -> 'Fido2Device': + return Fido2Device( + Path(arg['path']), + arg['manufacturer'], + arg['product'] + ) + + +@dataclass +class LsblkInfo: + name: str = '' + path: Path = Path() + pkname: str = '' + size: Size = Size(0, Unit.B) + log_sec: int = 0 + pttype: str = '' + ptuuid: str = '' + rota: bool = False + tran: Optional[str] = None + partuuid: Optional[str] = None + uuid: Optional[str] = None + fstype: Optional[str] = None + fsver: Optional[str] = None + fsavail: Optional[str] = None + fsuse_percentage: Optional[str] = None + type: Optional[str] = None + mountpoint: Optional[Path] = None + mountpoints: List[Path] = field(default_factory=list) + fsroots: List[Path] = field(default_factory=list) + children: List[LsblkInfo] = field(default_factory=list) + + def json(self) -> Dict[str, Any]: + return { + 'name': self.name, + 'path': str(self.path), + 'pkname': self.pkname, + 'size': self.size.format_size(Unit.MiB), + 'log_sec': self.log_sec, + 'pttype': self.pttype, + 'ptuuid': self.ptuuid, + 'rota': self.rota, + 'tran': self.tran, + 'partuuid': self.partuuid, + 'uuid': self.uuid, + 'fstype': self.fstype, + 'fsver': self.fsver, + 'fsavail': self.fsavail, + 'fsuse_percentage': self.fsuse_percentage, + 'type': self.type, + 'mountpoint': self.mountpoint, + 'mountpoints': [str(m) for m in self.mountpoints], + 'fsroots': [str(r) for r in self.fsroots], + 'children': [c.json() for c in self.children] + } + + @property + def btrfs_subvol_info(self) -> Dict[Path, Path]: + """ + It is assumed that lsblk will contain the fields as + + "mountpoints": ["/mnt/archinstall/log", "/mnt/archinstall/home", "/mnt/archinstall", ...] + "fsroots": ["/@log", "/@home", "/@"...] + + we'll thereby map the fsroot, which are the mounted filesystem roots + to the corresponding mountpoints + """ + return dict(zip(self.fsroots, self.mountpoints)) + + @classmethod + def exclude(cls) -> List[str]: + return ['children'] + + @classmethod + def fields(cls) -> List[str]: + return [f.name for f in dataclasses.fields(LsblkInfo) if f.name not in cls.exclude()] + + @classmethod + def from_json(cls, blockdevice: Dict[str, Any]) -> LsblkInfo: + info = cls() + + for f in cls.fields(): + lsblk_field = _clean_field(f, CleanType.Blockdevice) + data_field = _clean_field(f, CleanType.Dataclass) + + val: Any = None + if isinstance(getattr(info, data_field), Path): + val = Path(blockdevice[lsblk_field]) + elif isinstance(getattr(info, data_field), Size): + val = Size(blockdevice[lsblk_field], Unit.B) + else: + val = blockdevice[lsblk_field] + + setattr(info, data_field, val) + + info.children = [LsblkInfo.from_json(child) for child in blockdevice.get('children', [])] + + # sometimes lsblk returns 'mountpoints': [null] + info.mountpoints = [Path(mnt) for mnt in info.mountpoints if mnt] + + fs_roots = [] + for r in info.fsroots: + if r: + path = Path(r) + # store the fsroot entries without the leading / + fs_roots.append(path.relative_to(path.anchor)) + info.fsroots = fs_roots + + return info + + +class CleanType(Enum): + Blockdevice = auto() + Dataclass = auto() + Lsblk = auto() + + +def _clean_field(name: str, clean_type: CleanType) -> str: + match clean_type: + case CleanType.Blockdevice: + return name.replace('_percentage', '%').replace('_', '-') + case CleanType.Dataclass: + return name.lower().replace('-', '_').replace('%', '_percentage') + case CleanType.Lsblk: + return name.replace('_percentage', '%').replace('_', '-') + + +def _fetch_lsblk_info(dev_path: Optional[Union[Path, str]] = None, retry: int = 3) -> List[LsblkInfo]: + fields = [_clean_field(f, CleanType.Lsblk) for f in LsblkInfo.fields()] + lsblk_fields = ','.join(fields) + + if not dev_path: + dev_path = '' + + if retry == 0: + retry = 1 + + result = None + + for i in range(retry): + try: + result = SysCommand(f'lsblk --json -b -o+{lsblk_fields} {dev_path}') + except SysCallError as error: + # Get the output minus the message/info from lsblk if it returns a non-zero exit code. + if error.worker: + err = error.worker.decode('UTF-8') + log(f'Error calling lsblk: {err}', level=logging.DEBUG) + time.sleep(1) + else: + raise error + + if result and result.exit_code == 0: + try: + if decoded := result.decode('utf-8'): + block_devices = json.loads(decoded) + blockdevices = block_devices['blockdevices'] + return [LsblkInfo.from_json(device) for device in blockdevices] + except json.decoder.JSONDecodeError as err: + log(f"Could not decode lsblk JSON: {result}", fg="red", level=logging.ERROR) + raise err + + raise DiskError(f'Failed to read disk "{dev_path}" with lsblk') + + +def get_lsblk_info(dev_path: Union[Path, str]) -> LsblkInfo: + if infos := _fetch_lsblk_info(dev_path): + return infos[0] + + raise DiskError(f'lsblk failed to retrieve information for "{dev_path}"') + + +def get_all_lsblk_info() -> List[LsblkInfo]: + return _fetch_lsblk_info() + + +def get_lsblk_by_mountpoint(mountpoint: Path, as_prefix: bool = False) -> List[LsblkInfo]: + def _check(infos: List[LsblkInfo]) -> List[LsblkInfo]: + devices = [] + for entry in infos: + if as_prefix: + matches = [m for m in entry.mountpoints if str(m).startswith(str(mountpoint))] + if matches: + devices += [entry] + elif mountpoint in entry.mountpoints: + devices += [entry] + + if len(entry.children) > 0: + if len(match := _check(entry.children)) > 0: + devices += match + + return devices + + all_info = get_all_lsblk_info() + return _check(all_info) diff --git a/archinstall/lib/disk/diskinfo.py b/archinstall/lib/disk/diskinfo.py deleted file mode 100644 index b56ba282..00000000 --- a/archinstall/lib/disk/diskinfo.py +++ /dev/null @@ -1,40 +0,0 @@ -import dataclasses -import json -from dataclasses import dataclass, field -from typing import Optional, List - -from ..general import SysCommand -from ..exceptions import DiskError - -@dataclass -class LsblkInfo: - size: int = 0 - log_sec: int = 0 - pttype: Optional[str] = None - rota: bool = False - tran: Optional[str] = None - ptuuid: Optional[str] = None - partuuid: Optional[str] = None - uuid: Optional[str] = None - fstype: Optional[str] = None - type: Optional[str] = None - mountpoints: List[str] = field(default_factory=list) - - -def get_lsblk_info(dev_path: str) -> LsblkInfo: - fields = [f.name for f in dataclasses.fields(LsblkInfo)] - lsblk_fields = ','.join([f.upper().replace('_', '-') for f in fields]) - - output = SysCommand(f'lsblk --json -b -o+{lsblk_fields} {dev_path}').decode('UTF-8') - - if output: - block_devices = json.loads(output) - info = block_devices['blockdevices'][0] - lsblk_info = LsblkInfo() - - for f in fields: - setattr(lsblk_info, f, info[f.replace('_', '-')]) - - return lsblk_info - - raise DiskError(f'Failed to read disk "{dev_path}" with lsblk') diff --git a/archinstall/lib/disk/dmcryptdev.py b/archinstall/lib/disk/dmcryptdev.py deleted file mode 100644 index 63392ffb..00000000 --- a/archinstall/lib/disk/dmcryptdev.py +++ /dev/null @@ -1,48 +0,0 @@ -import pathlib -import logging -import json -from dataclasses import dataclass -from typing import Optional -from ..exceptions import SysCallError -from ..general import SysCommand -from ..output import log -from .mapperdev import MapperDev - -@dataclass -class DMCryptDev: - dev_path :pathlib.Path - - @property - def name(self): - with open(f"/sys/devices/virtual/block/{pathlib.Path(self.path).name}/dm/name", "r") as fh: - return fh.read().strip() - - @property - def path(self): - return f"/dev/mapper/{self.dev_path}" - - @property - def blockdev(self): - pass - - @property - def MapperDev(self): - return MapperDev(mappername=self.name) - - @property - def mountpoint(self) -> Optional[str]: - try: - data = json.loads(SysCommand(f"findmnt --json -R {self.dev_path}").decode()) - for filesystem in data['filesystems']: - return filesystem.get('target') - - except SysCallError as error: - # Not mounted anywhere most likely - log(f"Could not locate mount information for {self.dev_path}: {error}", level=logging.WARNING, fg="yellow") - pass - - return None - - @property - def filesystem(self) -> Optional[str]: - return self.MapperDev.filesystem \ No newline at end of file diff --git a/archinstall/lib/disk/encryption.py b/archinstall/lib/disk/encryption.py deleted file mode 100644 index c7496bfa..00000000 --- a/archinstall/lib/disk/encryption.py +++ /dev/null @@ -1,174 +0,0 @@ -from typing import Dict, Optional, Any, TYPE_CHECKING, List - -from ..menu.abstract_menu import Selector, AbstractSubMenu -from ..menu.menu import MenuSelectionType -from ..menu.table_selection_menu import TableMenu -from ..models.disk_encryption import EncryptionType, DiskEncryption -from ..user_interaction.partitioning_conf import current_partition_layout -from ..user_interaction.utils import get_password -from ..menu import Menu -from ..general import secret -from ..hsm.fido import Fido2Device, Fido2 - -if TYPE_CHECKING: - _: Any - - -class DiskEncryptionMenu(AbstractSubMenu): - def __init__(self, data_store: Dict[str, Any], preset: Optional[DiskEncryption], disk_layouts: Dict[str, Any]): - if preset: - self._preset = preset - else: - self._preset = DiskEncryption() - - self._disk_layouts = disk_layouts - super().__init__(data_store=data_store) - - def _setup_selection_menu_options(self): - self._menu_options['encryption_password'] = \ - Selector( - _('Encryption password'), - lambda x: select_encrypted_password(), - display_func=lambda x: secret(x) if x else '', - default=self._preset.encryption_password, - enabled=True - ) - self._menu_options['encryption_type'] = \ - Selector( - _('Encryption type'), - func=lambda preset: select_encryption_type(preset), - display_func=lambda x: EncryptionType.type_to_text(x) if x else None, - dependencies=['encryption_password'], - default=self._preset.encryption_type, - enabled=True - ) - self._menu_options['partitions'] = \ - Selector( - _('Partitions'), - func=lambda preset: select_partitions_to_encrypt(self._disk_layouts, preset), - display_func=lambda x: f'{sum([len(y) for y in x.values()])} {_("Partitions")}' if x else None, - dependencies=['encryption_password'], - default=self._preset.partitions, - preview_func=self._prev_disk_layouts, - enabled=True - ) - self._menu_options['HSM'] = \ - Selector( - description=_('Use HSM to unlock encrypted drive'), - func=lambda preset: select_hsm(preset), - display_func=lambda x: self._display_hsm(x), - dependencies=['encryption_password'], - default=self._preset.hsm_device, - enabled=True - ) - - def run(self, allow_reset: bool = True) -> Optional[DiskEncryption]: - super().run(allow_reset=allow_reset) - - if self._data_store.get('encryption_password', None): - return DiskEncryption( - encryption_password=self._data_store.get('encryption_password', None), - encryption_type=self._data_store['encryption_type'], - partitions=self._data_store.get('partitions', None), - hsm_device=self._data_store.get('HSM', None) - ) - - return None - - def _display_hsm(self, device: Optional[Fido2Device]) -> Optional[str]: - if device: - return device.manufacturer - - if not Fido2.get_fido2_devices(): - return str(_('No HSM devices available')) - return None - - def _prev_disk_layouts(self) -> Optional[str]: - selector = self._menu_options['partitions'] - if selector.has_selection(): - partitions: Dict[str, Any] = selector.current_selection - - all_partitions = [] - for parts in partitions.values(): - all_partitions += parts - - output = str(_('Partitions to be encrypted')) + '\n' - output += current_partition_layout(all_partitions, with_title=False) - return output.rstrip() - return None - - -def select_encryption_type(preset: EncryptionType) -> Optional[EncryptionType]: - title = str(_('Select disk encryption option')) - options = [ - # _type_to_text(EncryptionType.FullDiskEncryption), - EncryptionType.type_to_text(EncryptionType.Partition) - ] - - preset_value = EncryptionType.type_to_text(preset) - choice = Menu(title, options, preset_values=preset_value).run() - - match choice.type_: - case MenuSelectionType.Reset: return None - case MenuSelectionType.Skip: return preset - case MenuSelectionType.Selection: return EncryptionType.text_to_type(choice.value) # type: ignore - - -def select_encrypted_password() -> Optional[str]: - if passwd := get_password(prompt=str(_('Enter disk encryption password (leave blank for no encryption): '))): - return passwd - return None - - -def select_hsm(preset: Optional[Fido2Device] = None) -> Optional[Fido2Device]: - title = _('Select a FIDO2 device to use for HSM') - fido_devices = Fido2.get_fido2_devices() - - if fido_devices: - choice = TableMenu(title, data=fido_devices).run() - match choice.type_: - case MenuSelectionType.Reset: - return None - case MenuSelectionType.Skip: - return preset - case MenuSelectionType.Selection: - return choice.value # type: ignore - - return None - - -def select_partitions_to_encrypt(disk_layouts: Dict[str, Any], preset: Dict[str, Any]) -> Dict[str, Any]: - # If no partitions was marked as encrypted, but a password was supplied and we have some disks to format.. - # Then we need to identify which partitions to encrypt. This will default to / (root). - all_partitions = [] - for blockdevice in disk_layouts.values(): - if partitions := blockdevice.get('partitions'): - partitions = [p for p in partitions if p['mountpoint'] != '/boot'] - all_partitions += partitions - - if all_partitions: - title = str(_('Select which partitions to encrypt')) - partition_table = current_partition_layout(all_partitions, with_title=False).strip() - - choice = TableMenu( - title, - table_data=(all_partitions, partition_table), - multi=True - ).run() - - match choice.type_: - case MenuSelectionType.Reset: - return {} - case MenuSelectionType.Skip: - return preset - case MenuSelectionType.Selection: - selections: List[Any] = choice.value # type: ignore - partitions = {} - - for path, device in disk_layouts.items(): - for part in selections: - if part in device.get('partitions', []): - partitions.setdefault(path, []).append(part) - - return partitions - return {} diff --git a/archinstall/lib/disk/encryption_menu.py b/archinstall/lib/disk/encryption_menu.py new file mode 100644 index 00000000..285270fb --- /dev/null +++ b/archinstall/lib/disk/encryption_menu.py @@ -0,0 +1,179 @@ +from pathlib import Path +from typing import Dict, Optional, Any, TYPE_CHECKING, List + +from ..disk import ( + DeviceModification, + PartitionModification, + DiskEncryption, + EncryptionType +) +from ..menu import ( + Selector, + AbstractSubMenu, + MenuSelectionType, + TableMenu +) +from ..user_interaction.utils import get_password +from ..menu import Menu +from ..general import secret +from .fido import Fido2Device, Fido2 +from ..output import FormattedOutput + +if TYPE_CHECKING: + _: Any + + +class DiskEncryptionMenu(AbstractSubMenu): + def __init__( + self, + mods: List[DeviceModification], + data_store: Dict[str, Any], + preset: Optional[DiskEncryption] = None + ): + if preset: + self._preset = preset + else: + self._preset = DiskEncryption() + + self._modifications = mods + super().__init__(data_store=data_store) + + def setup_selection_menu_options(self): + self._menu_options['encryption_password'] = \ + Selector( + _('Encryption password'), + lambda x: select_encrypted_password(), + display_func=lambda x: secret(x) if x else '', + default=self._preset.encryption_password, + enabled=True + ) + self._menu_options['encryption_type'] = \ + Selector( + _('Encryption type'), + func=lambda preset: select_encryption_type(preset), + display_func=lambda x: EncryptionType.type_to_text(x) if x else None, + dependencies=['encryption_password'], + default=self._preset.encryption_type, + enabled=True + ) + self._menu_options['partitions'] = \ + Selector( + _('Partitions'), + func=lambda preset: select_partitions_to_encrypt(self._modifications.device_modifications, preset), + display_func=lambda x: f'{len(x)} {_("Partitions")}' if x else None, + dependencies=['encryption_password'], + default=self._preset.partitions, + preview_func=self._prev_disk_layouts, + enabled=True + ) + self._menu_options['HSM'] = \ + Selector( + description=_('Use HSM to unlock encrypted drive'), + func=lambda preset: select_hsm(preset), + display_func=lambda x: self._display_hsm(x), + dependencies=['encryption_password'], + default=self._preset.hsm_device, + enabled=True + ) + + def run(self, allow_reset: bool = True) -> Optional[DiskEncryption]: + super().run(allow_reset=allow_reset) + + if self._data_store.get('encryption_password', None): + return DiskEncryption( + encryption_password=self._data_store.get('encryption_password', None), + encryption_type=self._data_store['encryption_type'], + partitions=self._data_store.get('partitions', None), + hsm_device=self._data_store.get('HSM', None) + ) + + return None + + def _display_hsm(self, device: Optional[Fido2Device]) -> Optional[str]: + if device: + return device.manufacturer + + if not Fido2.get_fido2_devices(): + return str(_('No HSM devices available')) + return None + + def _prev_disk_layouts(self) -> Optional[str]: + partitions: Optional[List[PartitionModification]] = self._menu_options['partitions'].current_selection + if partitions: + output = str(_('Partitions to be encrypted')) + '\n' + output += FormattedOutput.as_table(partitions) + return output.rstrip() + + return None + + +def select_encryption_type(preset: EncryptionType) -> Optional[EncryptionType]: + title = str(_('Select disk encryption option')) + options = [ + EncryptionType.type_to_text(EncryptionType.Partition) + ] + + preset_value = EncryptionType.type_to_text(preset) + choice = Menu(title, options, preset_values=preset_value).run() + + match choice.type_: + case MenuSelectionType.Reset: return None + case MenuSelectionType.Skip: return preset + case MenuSelectionType.Selection: return EncryptionType.text_to_type(choice.value) # type: ignore + + +def select_encrypted_password() -> Optional[str]: + if passwd := get_password(prompt=str(_('Enter disk encryption password (leave blank for no encryption): '))): + return passwd + return None + + +def select_hsm(preset: Optional[Fido2Device] = None) -> Optional[Fido2Device]: + title = _('Select a FIDO2 device to use for HSM') + fido_devices = Fido2.get_fido2_devices() + + if fido_devices: + choice = TableMenu(title, data=fido_devices).run() + match choice.type_: + case MenuSelectionType.Reset: + return None + case MenuSelectionType.Skip: + return preset + case MenuSelectionType.Selection: + return choice.value # type: ignore + + return None + + +def select_partitions_to_encrypt( + modification: List[DeviceModification], + preset: List[PartitionModification] +) -> List[PartitionModification]: + partitions: List[PartitionModification] = [] + + # do not allow encrypting the boot partition + for mod in modification: + partitions += list(filter(lambda x: x.mountpoint != Path('/boot'), mod.partitions)) + + # do not allow encrypting existing partitions that are not marked as wipe + avail_partitions = list(filter(lambda x: not x.exists(), partitions)) + + if avail_partitions: + title = str(_('Select which partitions to encrypt')) + partition_table = FormattedOutput.as_table(avail_partitions) + + choice = TableMenu( + title, + table_data=(avail_partitions, partition_table), + preset=preset, + multi=True + ).run() + + match choice.type_: + case MenuSelectionType.Reset: + return [] + case MenuSelectionType.Skip: + return preset + case MenuSelectionType.Selection: + return choice.multi_value + return [] diff --git a/archinstall/lib/disk/fido.py b/archinstall/lib/disk/fido.py new file mode 100644 index 00000000..436be4d4 --- /dev/null +++ b/archinstall/lib/disk/fido.py @@ -0,0 +1,94 @@ +from __future__ import annotations + +import getpass +import logging +from typing import List + +from .device_model import PartitionModification, Fido2Device +from ..general import SysCommand, SysCommandWorker, clear_vt100_escape_codes +from ..output import log + + +class Fido2: + _loaded: bool = False + _fido2_devices: List[Fido2Device] = [] + + @classmethod + def get_fido2_devices(cls, reload: bool = False) -> List[Fido2Device]: + """ + Uses systemd-cryptenroll to list the FIDO2 devices + connected that supports FIDO2. + Some devices might show up in udevadm as FIDO2 compliant + when they are in fact not. + + The drawback of systemd-cryptenroll is that it uses human readable format. + That means we get this weird table like structure that is of no use. + + So we'll look for `MANUFACTURER` and `PRODUCT`, we take their index + and we split each line based on those positions. + + Output example: + + PATH MANUFACTURER PRODUCT + /dev/hidraw1 Yubico YubiKey OTP+FIDO+CCID + """ + + # to prevent continous reloading which will slow + # down moving the cursor in the menu + if not cls._loaded or reload: + ret = SysCommand(f"systemd-cryptenroll --fido2-device=list").decode('UTF-8') + if not ret: + log('Unable to retrieve fido2 devices', level=logging.ERROR) + return [] + + fido_devices = clear_vt100_escape_codes(ret) + + manufacturer_pos = 0 + product_pos = 0 + devices = [] + + for line in fido_devices.split('\r\n'): + if '/dev' not in line: + manufacturer_pos = line.find('MANUFACTURER') + product_pos = line.find('PRODUCT') + continue + + path = line[:manufacturer_pos].rstrip() + manufacturer = line[manufacturer_pos:product_pos].rstrip() + product = line[product_pos:] + + devices.append( + Fido2Device(path, manufacturer, product) + ) + + cls._loaded = True + cls._fido2_devices = devices + + return cls._fido2_devices + + @classmethod + def fido2_enroll( + cls, + hsm_device: Fido2Device, + part_mod: PartitionModification, + password: str + ): + worker = SysCommandWorker(f"systemd-cryptenroll --fido2-device={hsm_device.path} {part_mod.dev_path}", peek_output=True) + pw_inputted = False + pin_inputted = False + + while worker.is_alive(): + if pw_inputted is False: + if bytes(f"please enter current passphrase for disk {part_mod.dev_path}", 'UTF-8') in worker._trace_log.lower(): + worker.write(bytes(password, 'UTF-8')) + pw_inputted = True + elif pin_inputted is False: + if bytes(f"please enter security token pin", 'UTF-8') in worker._trace_log.lower(): + worker.write(bytes(getpass.getpass(" "), 'UTF-8')) + pin_inputted = True + + log( + f"You might need to touch the FIDO2 device to unlock it if no prompt comes up after 3 seconds.", + level=logging.INFO, + fg="yellow" + ) diff --git a/archinstall/lib/disk/filesystem.py b/archinstall/lib/disk/filesystem.py index 1083df53..6ea99340 100644 --- a/archinstall/lib/disk/filesystem.py +++ b/archinstall/lib/disk/filesystem.py @@ -1,301 +1,98 @@ from __future__ import annotations -import time -import logging -import json -import pathlib -from typing import Optional, Dict, Any, TYPE_CHECKING -# https://stackoverflow.com/a/39757388/929999 -from ..models.disk_encryption import DiskEncryption -if TYPE_CHECKING: - from .blockdevice import BlockDevice - _: Any +import logging +import signal +import sys +import time +from typing import Any, Optional, TYPE_CHECKING -from .partition import Partition -from .validators import valid_fs_type -from ..exceptions import DiskError, SysCallError -from ..general import SysCommand +from .device_model import DiskLayoutConfiguration, DiskLayoutType, PartitionTable, FilesystemType, DiskEncryption +from .device_handler import device_handler +from ..hardware import has_uefi from ..output import log -from ..storage import storage - -GPT = 0b00000001 -MBR = 0b00000010 - -# A sane default is 5MiB, that allows for plenty of buffer for GRUB on MBR -# but also 4MiB for memory cards for instance. And another 1MiB to avoid issues. -# (we've been pestered by disk issues since the start, so please let this be here for a few versions) -DEFAULT_PARTITION_START = '5MiB' - -class Filesystem: - # TODO: - # When instance of a HDD is selected, check all usages and gracefully unmount them - # as well as close any crypto handles. - def __init__(self, blockdevice :BlockDevice, mode :int): - self.blockdevice = blockdevice - self.mode = mode - - def __enter__(self, *args :str, **kwargs :str) -> 'Filesystem': - return self - - def __repr__(self) -> str: - return f"Filesystem(blockdevice={self.blockdevice}, mode={self.mode})" +from ..menu import Menu - def __exit__(self, *args :str, **kwargs :str) -> bool: - # TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager - if len(args) >= 2 and args[1]: - raise args[1] - - SysCommand('sync') - return True - - def partuuid_to_index(self, uuid :str) -> Optional[int]: - for i in range(storage['DISK_RETRY_ATTEMPTS']): - self.partprobe() - time.sleep(max(0.1, storage['DISK_TIMEOUTS'] * i)) - - # We'll use unreliable lbslk to grab children under the /dev/ - output = json.loads(SysCommand(f"lsblk --json {self.blockdevice.device}").decode('UTF-8')) +if TYPE_CHECKING: + _: Any - for device in output['blockdevices']: - for index, partition in enumerate(device.get('children', [])): - # But we'll use blkid to reliably grab the PARTUUID for that child device (partition) - partition_uuid = SysCommand(f"blkid -s PARTUUID -o value /dev/{partition.get('name')}").decode().strip() - if partition_uuid.lower() == uuid.lower(): - return index - raise DiskError(f"Failed to convert PARTUUID {uuid} to a partition index number on blockdevice {self.blockdevice.device}") +class FilesystemHandler: + def __init__( + self, + disk_config: DiskLayoutConfiguration, + enc_conf: Optional[DiskEncryption] = None + ): + self._disk_config = disk_config + self._enc_config = enc_conf - def load_layout(self, layout :Dict[str, Any]) -> None: - from ..luks import luks2 - from .btrfs import BTRFSPartition + def perform_filesystem_operations(self, show_countdown: bool = True): + if self._disk_config.config_type == DiskLayoutType.Pre_mount: + log('Disk layout configuration is set to pre-mount, not performing any operations', level=logging.DEBUG) + return - # If the layout tells us to wipe the drive, we do so - if layout.get('wipe', False): - if self.mode == GPT: - if not self.parted_mklabel(self.blockdevice.device, "gpt"): - raise KeyError(f"Could not create a GPT label on {self}") - elif self.mode == MBR: - if not self.parted_mklabel(self.blockdevice.device, "msdos"): - raise KeyError(f"Could not create a MS-DOS label on {self}") + device_mods = list(filter(lambda x: len(x.partitions) > 0, self._disk_config.device_modifications)) - self.blockdevice.flush_cache() - time.sleep(3) + if not device_mods: + log('No modifications required', level=logging.DEBUG) + return - prev_partition = None - # We then iterate the partitions in order - for partition in layout.get('partitions', []): - # We don't want to re-add an existing partition (those containing a UUID already) - if partition.get('wipe', False) and not partition.get('PARTUUID', None): - start = partition.get('start') or ( - prev_partition and f'{prev_partition["device_instance"].end_sectors}s' or DEFAULT_PARTITION_START) - partition['device_instance'] = self.add_partition(partition.get('type', 'primary'), - start=start, - end=partition.get('size', '100%'), - partition_format=partition.get('filesystem', {}).get('format', 'btrfs'), - skip_mklabel=layout.get('wipe', False) is not False) + device_paths = ', '.join([str(mod.device.device_info.path) for mod in device_mods]) - elif (partition_uuid := partition.get('PARTUUID')): - # We try to deal with both UUID and PARTUUID of a partition when it's being re-used. - # We should re-name or separate this logi based on partition.get('PARTUUID') and partition.get('UUID') - # but for now, lets just attempt to deal with both. - try: - partition['device_instance'] = self.blockdevice.get_partition(uuid=partition_uuid) - except DiskError: - partition['device_instance'] = self.blockdevice.get_partition(partuuid=partition_uuid) + # Issue a final warning before we continue with something un-revertable. + # We mention the drive one last time, and count from 5 to 0. + print(str(_(' ! Formatting {} in ')).format(device_paths)) - log(_("Re-using partition instance: {}").format(partition['device_instance']), level=logging.DEBUG, fg="gray") - else: - log(f"{self}.load_layout() doesn't know how to work without 'wipe' being set or UUID ({partition.get('PARTUUID')}) was given and found.", fg="yellow", level=logging.WARNING) - continue + if show_countdown: + self._do_countdown() - if partition.get('filesystem', {}).get('format', False): - # needed for backward compatibility with the introduction of the new "format_options" - format_options = partition.get('options',[]) + partition.get('filesystem',{}).get('format_options',[]) - disk_encryption: DiskEncryption = storage['arguments'].get('disk_encryption') + # Setup the blockdevice, filesystem (and optionally encryption). + # Once that's done, we'll hand over to perform_installation() + partition_table = PartitionTable.GPT + if has_uefi() is False: + partition_table = PartitionTable.MBR - if disk_encryption and partition in disk_encryption.all_partitions: - if not partition['device_instance']: - raise DiskError(f"Internal error caused us to loose the partition. Please report this issue upstream!") + for mod in device_mods: + device_handler.partition(mod, partition_table=partition_table) + device_handler.format(mod, enc_conf=self._enc_config) - if partition.get('mountpoint',None): - 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}" + for part_mod in mod.partitions: + if part_mod.fs_type == FilesystemType.Btrfs: + device_handler.create_btrfs_volumes(part_mod, enc_conf=self._enc_config) - partition['device_instance'].encrypt(password=disk_encryption.encryption_password) - # Immediately unlock the encrypted device to format the inner volume - with luks2(partition['device_instance'], loopdev, disk_encryption.encryption_password, auto_unmount=True) as unlocked_device: - if not partition.get('wipe'): - if storage['arguments'] == 'silent': - raise ValueError(f"Missing fs-type to format on newly created encrypted partition {partition['device_instance']}") - else: - if not partition.get('filesystem'): - partition['filesystem'] = {} + def _do_countdown(self) -> bool: + SIG_TRIGGER = False - if not partition['filesystem'].get('format', False): - while True: - partition['filesystem']['format'] = input(f"Enter a valid fs-type for newly encrypted partition {partition['filesystem']['format']}: ").strip() - if not partition['filesystem']['format'] or valid_fs_type(partition['filesystem']['format']) is False: - log(_("You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's.")) - continue - break + def kill_handler(sig: int, frame: Any) -> None: + print() + exit(0) - unlocked_device.format(partition['filesystem']['format'], options=format_options) + def sig_handler(sig: int, frame: Any) -> None: + signal.signal(signal.SIGINT, kill_handler) - elif partition.get('wipe', False): - if not partition['device_instance']: - raise DiskError(f"Internal error caused us to loose the partition. Please report this issue upstream!") + original_sigint_handler = signal.getsignal(signal.SIGINT) + signal.signal(signal.SIGINT, sig_handler) - partition['device_instance'].format(partition['filesystem']['format'], options=format_options) + for i in range(5, 0, -1): + print(f"{i}", end='') - if partition['filesystem']['format'] == 'btrfs': - # We upgrade the device instance to a BTRFSPartition if we format it as such. - # This is so that we can gain access to more features than otherwise available in Partition() - partition['device_instance'] = BTRFSPartition( - partition['device_instance'].path, - block_device=partition['device_instance'].block_device, - encrypted=False, - filesystem='btrfs', - autodetect_filesystem=False - ) + for x in range(4): + sys.stdout.flush() + time.sleep(0.25) + print(".", end='') - if partition.get('boot', False): - log(f"Marking partition {partition['device_instance']} as bootable.") - self.set(self.partuuid_to_index(partition['device_instance'].part_uuid), 'boot on') + if SIG_TRIGGER: + prompt = _('Do you really want to abort?') + choice = Menu(prompt, Menu.yes_no(), skip=False).run() + if choice.value == Menu.yes(): + exit(0) - prev_partition = partition + if SIG_TRIGGER is False: + sys.stdin.read() - def find_partition(self, mountpoint :str) -> Partition: - for partition in self.blockdevice: - if partition.target_mountpoint == mountpoint or partition.mountpoint == mountpoint: - return partition + SIG_TRIGGER = False + signal.signal(signal.SIGINT, sig_handler) - def partprobe(self) -> bool: - try: - SysCommand(f'partprobe {self.blockdevice.device}') - except SysCallError as error: - log(f"Could not execute partprobe: {error!r}", level=logging.ERROR, fg="red") - raise DiskError(f"Could not run partprobe on {self.blockdevice.device}: {error!r}") + print() + signal.signal(signal.SIGINT, original_sigint_handler) return True - - def raw_parted(self, string: str) -> SysCommand: - try: - cmd_handle = SysCommand(f'/usr/bin/parted -s {string}') - time.sleep(0.5) - return cmd_handle - except SysCallError as error: - log(f"Parted ended with a bad exit code: {error.exit_code} ({error})", level=logging.ERROR, fg="red") - return error - - def parted(self, string: str) -> bool: - """ - Performs a parted execution of the given string - - :param string: A raw string passed to /usr/bin/parted -s - :type string: str - """ - if (parted_handle := self.raw_parted(string)).exit_code == 0: - return self.partprobe() - else: - raise DiskError(f"Parted failed to add a partition: {parted_handle}") - - def use_entire_disk(self, root_filesystem_type :str = 'ext4') -> Partition: - # TODO: Implement this with declarative profiles instead. - raise ValueError("Installation().use_entire_disk() has to be re-worked.") - - def add_partition( - self, - partition_type :str, - start :str, - end :str, - partition_format :Optional[str] = None, - skip_mklabel :bool = False - ) -> Partition: - log(f'Adding partition to {self.blockdevice}, {start}->{end}', level=logging.INFO) - - if len(self.blockdevice.partitions) == 0 and skip_mklabel is False: - # If it's a completely empty drive, and we're about to add partitions to it - # we need to make sure there's a filesystem label. - if self.mode == GPT: - if not self.parted_mklabel(self.blockdevice.device, "gpt"): - raise KeyError(f"Could not create a GPT label on {self}") - elif self.mode == MBR: - if not self.parted_mklabel(self.blockdevice.device, "msdos"): - raise KeyError(f"Could not create a MS-DOS label on {self}") - - self.blockdevice.flush_cache() - - previous_partuuids = [] - for partition in self.blockdevice.partitions.values(): - try: - previous_partuuids.append(partition.part_uuid) - except DiskError: - pass - - # TODO this check should probably run in the setup process rather than during the installation - if self.mode == MBR: - if len(self.blockdevice.partitions) > 3: - DiskError("Too many partitions on disk, MBR disks can only have 3 primary partitions") - - if partition_format: - parted_string = f'{self.blockdevice.device} mkpart {partition_type} {partition_format} {start} {end}' - else: - parted_string = f'{self.blockdevice.device} mkpart {partition_type} {start} {end}' - - log(f"Adding partition using the following parted command: {parted_string}", level=logging.DEBUG) - - if self.parted(parted_string): - for count in range(storage.get('DISK_RETRY_ATTEMPTS', 3)): - self.blockdevice.flush_cache() - - new_partition_uuids = [partition.part_uuid for partition in self.blockdevice.partitions.values()] - new_partuuid_set = (set(previous_partuuids) ^ set(new_partition_uuids)) - - if len(new_partuuid_set) and (new_partuuid := new_partuuid_set.pop()): - try: - return self.blockdevice.get_partition(partuuid=new_partuuid) - except Exception as err: - log(f'Blockdevice: {self.blockdevice}', level=logging.ERROR, fg="red") - log(f'Partitions: {self.blockdevice.partitions}', level=logging.ERROR, fg="red") - log(f'Partition set: {new_partuuid_set}', level=logging.ERROR, fg="red") - log(f'New PARTUUID: {[new_partuuid]}', level=logging.ERROR, fg="red") - log(f'get_partition(): {self.blockdevice.get_partition}', level=logging.ERROR, fg="red") - raise err - else: - log(f"Could not get UUID for partition. Waiting {storage.get('DISK_TIMEOUTS', 1) * count}s before retrying.",level=logging.DEBUG) - self.partprobe() - time.sleep(max(0.1, storage.get('DISK_TIMEOUTS', 1))) - else: - print("Parted did not return True during partition creation") - - total_partitions = set([partition.part_uuid for partition in self.blockdevice.partitions.values()]) - total_partitions.update(previous_partuuids) - - # TODO: This should never be able to happen - log(f"Could not find the new PARTUUID after adding the partition.", level=logging.ERROR, fg="red") - log(f"Previous partitions: {previous_partuuids}", level=logging.ERROR, fg="red") - log(f"New partitions: {total_partitions}", level=logging.ERROR, fg="red") - - raise DiskError(f"Could not add partition using: {parted_string}") - - def set_name(self, partition: int, name: str) -> bool: - return self.parted(f'{self.blockdevice.device} name {partition + 1} "{name}"') == 0 - - def set(self, partition: int, string: str) -> bool: - log(f"Setting {string} on (parted) partition index {partition+1}", level=logging.INFO) - return self.parted(f'{self.blockdevice.device} set {partition + 1} {string}') == 0 - - def parted_mklabel(self, device: str, disk_label: str) -> bool: - log(f"Creating a new partition label on {device}", level=logging.INFO, fg="yellow") - # Try to unmount devices before attempting to run mklabel - try: - SysCommand(f'bash -c "umount {device}?"') - except: - pass - - self.partprobe() - worked = self.raw_parted(f'{device} mklabel {disk_label}').exit_code == 0 - self.partprobe() - - return worked diff --git a/archinstall/lib/disk/helpers.py b/archinstall/lib/disk/helpers.py deleted file mode 100644 index 80d0cb53..00000000 --- a/archinstall/lib/disk/helpers.py +++ /dev/null @@ -1,556 +0,0 @@ -from __future__ import annotations -import json -import logging -import os # type: ignore -import pathlib -import re -import time -import glob - -from typing import Union, List, Iterator, Dict, Optional, Any, TYPE_CHECKING -# https://stackoverflow.com/a/39757388/929999 -from .diskinfo import get_lsblk_info -from ..models.subvolume import Subvolume - -from .blockdevice import BlockDevice -from .dmcryptdev import DMCryptDev -from .mapperdev import MapperDev -from ..exceptions import SysCallError, DiskError -from ..general import SysCommand -from ..output import log -from ..storage import storage - -if TYPE_CHECKING: - from .partition import Partition - - -ROOT_DIR_PATTERN = re.compile('^.*?/devices') -GIGA = 2 ** 30 - -def convert_size_to_gb(size :Union[int, float]) -> float: - return round(size / GIGA,1) - -def sort_block_devices_based_on_performance(block_devices :List[BlockDevice]) -> Dict[BlockDevice, int]: - result = {device: 0 for device in block_devices} - - for device, weight in result.items(): - if device.spinning: - weight -= 10 - else: - weight += 5 - - if device.bus_type == 'nvme': - weight += 20 - elif device.bus_type == 'sata': - weight += 10 - - result[device] = weight - - return result - -def filter_disks_below_size_in_gb(devices :List[BlockDevice], gigabytes :int) -> Iterator[BlockDevice]: - for disk in devices: - if disk.size >= gigabytes: - yield disk - -def select_largest_device(devices :List[BlockDevice], gigabytes :int, filter_out :Optional[List[BlockDevice]] = None) -> BlockDevice: - if not filter_out: - filter_out = [] - - copy_devices = [*devices] - for filter_device in filter_out: - if filter_device in copy_devices: - copy_devices.pop(copy_devices.index(filter_device)) - - copy_devices = list(filter_disks_below_size_in_gb(copy_devices, gigabytes)) - - if not len(copy_devices): - return None - - return max(copy_devices, key=(lambda device : device.size)) - -def select_disk_larger_than_or_close_to(devices :List[BlockDevice], gigabytes :int, filter_out :Optional[List[BlockDevice]] = None) -> BlockDevice: - if not filter_out: - filter_out = [] - - copy_devices = [*devices] - for filter_device in filter_out: - if filter_device in copy_devices: - copy_devices.pop(copy_devices.index(filter_device)) - - if not len(copy_devices): - return None - - return min(copy_devices, key=(lambda device : abs(device.size - gigabytes))) - -def convert_to_gigabytes(string :str) -> float: - unit = string.strip()[-1] - size = float(string.strip()[:-1]) - - if unit == 'M': - size = size / 1024 - elif unit == 'T': - size = size * 1024 - - return size - -def device_state(name :str, *args :str, **kwargs :str) -> Optional[bool]: - # Based out of: https://askubuntu.com/questions/528690/how-to-get-list-of-all-non-removable-disk-device-names-ssd-hdd-and-sata-ide-onl/528709#528709 - if os.path.isfile('/sys/block/{}/device/block/{}/removable'.format(name, name)): - with open('/sys/block/{}/device/block/{}/removable'.format(name, name)) as f: - if f.read(1) == '1': - return - - path = ROOT_DIR_PATTERN.sub('', os.readlink('/sys/block/{}'.format(name))) - hotplug_buses = ("usb", "ieee1394", "mmc", "pcmcia", "firewire") - for bus in hotplug_buses: - if os.path.exists('/sys/bus/{}'.format(bus)): - for device_bus in os.listdir('/sys/bus/{}/devices'.format(bus)): - device_link = ROOT_DIR_PATTERN.sub('', os.readlink('/sys/bus/{}/devices/{}'.format(bus, device_bus))) - if re.search(device_link, path): - return - return True - - -def cleanup_bash_escapes(data :str) -> str: - return data.replace(r'\ ', ' ') - -def blkid(cmd :str) -> Dict[str, Any]: - if '-o' in cmd and '-o export' not in cmd: - raise ValueError(f"blkid() requires '-o export' to be used and can therefore not continue reliably.") - elif '-o' not in cmd: - cmd += ' -o export' - - try: - raw_data = SysCommand(cmd).decode() - except SysCallError as error: - log(f"Could not get block device information using blkid() using command {cmd}", level=logging.DEBUG) - raise error - - result = {} - # Process the raw result - devname = None - for line in raw_data.split('\r\n'): - if not len(line): - devname = None - continue - - key, val = line.split('=', 1) - if key.lower() == 'devname': - devname = val - # Lowercase for backwards compatibility with all_disks() previous use cases - result[devname] = { - "path": devname, - "PATH": devname - } - continue - - result[devname][key] = cleanup_bash_escapes(val) - - return result - -def get_loop_info(path :str) -> Dict[str, Any]: - for drive in json.loads(SysCommand(['losetup', '--json']).decode('UTF_8'))['loopdevices']: - if not drive['name'] == path: - continue - - return { - path: { - **drive, - 'type' : 'loop', - 'TYPE' : 'loop', - 'DEVTYPE' : 'loop', - 'PATH' : drive['name'], - 'path' : drive['name'] - } - } - - return {} - -def enrich_blockdevice_information(information :Dict[str, Any]) -> Dict[str, Any]: - result = {} - for device_path, device_information in information.items(): - dev_name = pathlib.Path(device_information['PATH']).name - if not device_information.get('TYPE') or not device_information.get('DEVTYPE'): - with open(f"/sys/class/block/{dev_name}/uevent") as fh: - device_information.update(uevent(fh.read())) - - if (dmcrypt_name := pathlib.Path(f"/sys/class/block/{dev_name}/dm/name")).exists(): - with dmcrypt_name.open('r') as fh: - device_information['DMCRYPT_NAME'] = fh.read().strip() - - result[device_path] = device_information - - return result - -def uevent(data :str) -> Dict[str, Any]: - information = {} - - for line in data.replace('\r\n', '\n').split('\n'): - if len((line := line.strip())): - key, val = line.split('=', 1) - information[key] = val - - return information - -def get_blockdevice_uevent(dev_name :str) -> Dict[str, Any]: - device_information = {} - with open(f"/sys/class/block/{dev_name}/uevent") as fh: - device_information.update(uevent(fh.read())) - - return { - f"/dev/{dev_name}" : { - **device_information, - 'path' : f'/dev/{dev_name}', - 'PATH' : f'/dev/{dev_name}', - 'PTTYPE' : None - } - } - - -def all_disks() -> List[BlockDevice]: - log(f"[Deprecated] archinstall.all_disks() is deprecated. Use archinstall.all_blockdevices() with the appropriate filters instead.", level=logging.WARNING, fg="yellow") - return all_blockdevices(partitions=False, mappers=False) - -def get_blockdevice_info(device_path, exclude_iso_dev :bool = True) -> Dict[str, Any]: - for retry_attempt in range(storage['DISK_RETRY_ATTEMPTS']): - partprobe(device_path) - time.sleep(max(0.1, storage['DISK_TIMEOUTS'] * retry_attempt)) - - try: - if exclude_iso_dev: - # exclude all devices associated with the iso boot locations - iso_devs = ['/run/archiso/airootfs', '/run/archiso/bootmnt'] - - try: - lsblk_info = get_lsblk_info(device_path) - except DiskError: - continue - - if any([dev in lsblk_info.mountpoints for dev in iso_devs]): - continue - - information = blkid(f'blkid -p -o export {device_path}') - return enrich_blockdevice_information(information) - except SysCallError as ex: - if ex.exit_code == 2: - # Assume that it's a loop device, and try to get info on it - try: - resolved_device_name = device_path.readlink().name - except OSError: - resolved_device_name = device_path.name - - try: - information = get_loop_info(device_path) - if not information: - raise SysCallError(f"Could not get loop information for {resolved_device_name}", exit_code=1) - return enrich_blockdevice_information(information) - - except SysCallError: - information = get_blockdevice_uevent(resolved_device_name) - return enrich_blockdevice_information(information) - else: - # We could not reliably get any information, perhaps the disk is clean of information? - if retry_attempt == storage['DISK_RETRY_ATTEMPTS'] - 1: - raise ex - -def all_blockdevices( - mappers: bool = False, - partitions: bool = False, - error: bool = False, - exclude_iso_dev: bool = True -) -> Dict[str, Any]: - """ - Returns BlockDevice() and Partition() objects for all available devices. - """ - from .partition import Partition - - instances = {} - - # Due to lsblk being highly unreliable for this use case, - # we'll iterate the /sys/class definitions and find the information - # from there. - for block_device in glob.glob("/sys/class/block/*"): - try: - device_path = pathlib.Path(f"/dev/{pathlib.Path(block_device).readlink().name}") - except FileNotFoundError: - log(f"Unknown device found by '/sys/class/block/*', ignoring: {device_path}", level=logging.WARNING, fg="yellow") - - if device_path.exists() is False: - log(f"Unknown device found by '/sys/class/block/*', ignoring: {device_path}", level=logging.WARNING, fg="yellow") - continue - - information = get_blockdevice_info(device_path) - if not information: - continue - - for path, path_info in information.items(): - if path_info.get('DMCRYPT_NAME'): - instances[path] = DMCryptDev(dev_path=path) - elif path_info.get('PARTUUID') or path_info.get('PART_ENTRY_NUMBER'): - if partitions: - instances[path] = Partition(path, block_device=BlockDevice(get_parent_of_partition(pathlib.Path(path)))) - elif path_info.get('PTTYPE', False) is not False or path_info.get('TYPE') == 'loop': - instances[path] = BlockDevice(path, path_info) - elif path_info.get('TYPE') in ('squashfs', 'erofs'): - # We can ignore squashfs devices (usually /dev/loop0 on Arch ISO) - continue - else: - log(f"Unknown device found by all_blockdevices(), ignoring: {information}", level=logging.WARNING, fg="yellow") - - if mappers: - for block_device in glob.glob("/dev/mapper/*"): - if (pathobj := pathlib.Path(block_device)).is_symlink(): - instances[f"/dev/mapper/{pathobj.name}"] = MapperDev(mappername=pathobj.name) - - return instances - - -def get_parent_of_partition(path :pathlib.Path) -> pathlib.Path: - partition_name = path.name - pci_device = (pathlib.Path("/sys/class/block") / partition_name).resolve() - return f"/dev/{pci_device.parent.name}" - -def harddrive(size :Optional[float] = None, model :Optional[str] = None, fuzzy :bool = False) -> Optional[BlockDevice]: - collection = all_blockdevices(partitions=False) - for drive in collection: - if size and convert_to_gigabytes(collection[drive]['size']) != size: - continue - if model and (collection[drive]['model'] is None or collection[drive]['model'].lower() != model.lower()): - continue - - return collection[drive] - -def split_bind_name(path :Union[pathlib.Path, str]) -> list: - # log(f"[Deprecated] Partition().subvolumes now contain the split bind name via it's subvolume.name instead.", level=logging.WARNING, fg="yellow") - # we check for the bind notation. if exist we'll only use the "true" device path - if '[' in str(path) : # is a bind path (btrfs subvolume path) - device_path, bind_path = str(path).split('[') - bind_path = bind_path[:-1].strip() # remove the ] - else: - device_path = path - bind_path = None - return device_path,bind_path - -def find_mountpoint(device_path :str) -> Dict[str, Any]: - try: - for filesystem in json.loads(SysCommand(f'/usr/bin/findmnt -R --json {device_path}').decode())['filesystems']: - yield filesystem - except SysCallError: - return {} - -def findmnt(path :pathlib.Path, traverse :bool = False, ignore :List = [], recurse :bool = True) -> Dict[str, Any]: - for traversal in list(map(str, [str(path)] + list(path.parents))): - if traversal in ignore: - continue - - try: - log(f"Getting mount information for device path {traversal}", level=logging.DEBUG) - if (output := SysCommand(f"/usr/bin/findmnt --json {'--submounts' if recurse else ''} {traversal}").decode('UTF-8')): - return json.loads(output) - - except SysCallError as error: - log(f"Could not get mount information on {path} but continuing and ignoring: {error}", level=logging.INFO, fg="gray") - pass - - if not traverse: - break - - raise DiskError(f"Could not get mount information for path {path}") - - -def get_mount_info(path :Union[pathlib.Path, str], traverse :bool = False, return_real_path :bool = False, ignore :List = []) -> Dict[str, Any]: - import traceback - - log(f"Deprecated: archinstall.get_mount_info(). Use archinstall.findmnt() instead, which does not do any automatic parsing. Please change at:\n{''.join(traceback.format_stack())}") - device_path, bind_path = split_bind_name(path) - output = {} - - for traversal in list(map(str, [str(device_path)] + list(pathlib.Path(str(device_path)).parents))): - if traversal in ignore: - continue - - try: - log(f"Getting mount information for device path {traversal}", level=logging.DEBUG) - if (output := SysCommand(f'/usr/bin/findmnt --json {traversal}').decode('UTF-8')): - break - - except SysCallError as error: - print('ERROR:', error) - pass - - if not traverse: - break - - if not output: - raise DiskError(f"Could not get mount information for device path {device_path}") - - output = json.loads(output) - - # for btrfs partitions we redice the filesystem list to the one with the source equals to the parameter - # i.e. the subvolume filesystem we're searching for - if 'filesystems' in output and len(output['filesystems']) > 1 and bind_path is not None: - output['filesystems'] = [entry for entry in output['filesystems'] if entry['source'] == str(path)] - - if 'filesystems' in output: - if len(output['filesystems']) > 1: - raise DiskError(f"Path '{device_path}' contains multiple mountpoints: {output['filesystems']}") - - if return_real_path: - return output['filesystems'][0], traversal - else: - return output['filesystems'][0] - - if return_real_path: - return {}, traversal - else: - return {} - - -def get_all_targets(data :Dict[str, Any], filters :Dict[str, None] = {}) -> Dict[str, None]: - for info in data: - if info.get('target') not in filters: - filters[info.get('target')] = None - - filters.update(get_all_targets(info.get('children', []))) - - return filters - -def get_partitions_in_use(mountpoint :str) -> Dict[str, Any]: - from .partition import Partition - - try: - output = SysCommand(f"/usr/bin/findmnt --json -R {mountpoint}").decode('UTF-8') - except SysCallError: - return {} - - if not output: - return {} - - output = json.loads(output) - - mounts = {} - - block_devices_available = all_blockdevices(mappers=True, partitions=True, error=True) - - block_devices_mountpoints = {} - for blockdev in block_devices_available.values(): - if not type(blockdev) in (Partition, MapperDev): - continue - - if isinstance(blockdev, Partition): - if blockdev.mountpoints: - for blockdev_mountpoint in blockdev.mountpoints: - block_devices_mountpoints[blockdev_mountpoint] = blockdev - else: - if blockdev.mount_information: - for blockdev_mountpoint in blockdev.mount_information: - block_devices_mountpoints[blockdev_mountpoint['target']] = blockdev - - log(f'Filtering available mounts {block_devices_mountpoints} to those under {mountpoint}', level=logging.DEBUG) - - for mountpoint in list(get_all_targets(output['filesystems']).keys()): - # Since all_blockdevices() returns PosixPath objects, we need to convert - # findmnt paths to pathlib.Path() first: - mountpoint = pathlib.Path(mountpoint) - - if mountpoint in block_devices_mountpoints: - if mountpoint not in mounts: - mounts[mountpoint] = block_devices_mountpoints[mountpoint] - # If the already defined mountpoint is a DMCryptDev, and the newly found - # mountpoint is a MapperDev, it has precedence and replaces the old mountpoint definition. - elif type(mounts[mountpoint]) == DMCryptDev and type(block_devices_mountpoints[mountpoint]) == MapperDev: - mounts[mountpoint] = block_devices_mountpoints[mountpoint] - - log(f"Available partitions: {mounts}", level=logging.DEBUG) - - return mounts - - -def get_filesystem_type(path :str) -> Optional[str]: - try: - return SysCommand(f"blkid -o value -s TYPE {path}").decode('UTF-8').strip() - except SysCallError: - return None - - -def disk_layouts() -> Optional[Dict[str, Any]]: - try: - if (handle := SysCommand("lsblk -f -o+TYPE,SIZE -J")).exit_code == 0: - return {str(key): val for key, val in json.loads(handle.decode('UTF-8')).items()} - else: - log(f"Could not return disk layouts: {handle}", level=logging.WARNING, fg="yellow") - return None - except SysCallError as err: - log(f"Could not return disk layouts: {err}", level=logging.WARNING, fg="yellow") - return None - except json.decoder.JSONDecodeError as err: - log(f"Could not return disk layouts: {err}", level=logging.WARNING, fg="yellow") - return None - - -def find_partition_by_mountpoint(block_devices :List[BlockDevice], relative_mountpoint :str) -> Partition: - for device in block_devices: - for partition in block_devices[device]['partitions']: - if partition.get('mountpoint', None) == relative_mountpoint: - return partition - -def partprobe(path :str = '') -> bool: - try: - if SysCommand(f'bash -c "partprobe {path}"').exit_code == 0: - return True - except SysCallError: - pass - return False - -def convert_device_to_uuid(path :str) -> str: - device_name, bind_name = split_bind_name(path) - - for i in range(storage['DISK_RETRY_ATTEMPTS']): - partprobe(device_name) - time.sleep(max(0.1, storage['DISK_TIMEOUTS'] * i)) # TODO: Remove, we should be relying on blkid instead of lsblk - - # TODO: Convert lsblk to blkid - # (lsblk supports BlockDev and Partition UUID grabbing, blkid requires you to pick PTUUID and PARTUUID) - output = json.loads(SysCommand(f"lsblk --json -o+UUID {device_name}").decode('UTF-8')) - - for device in output['blockdevices']: - if (dev_uuid := device.get('uuid', None)): - return dev_uuid - - raise DiskError(f"Could not retrieve the UUID of {path} within a timely manner.") - - -def has_mountpoint(partition: Union[dict,Partition,MapperDev], target: str, strict: bool = True) -> bool: - """ Determine if a certain partition is mounted (or has a mountpoint) as specific target (path) - Coded for clarity rather than performance - - Input parms: - :parm partition the partition we check - :type Either a Partition object or a dict with the contents of a partition definition in the disk_layouts schema - - :parm target (a string representing a mount path we want to check for. - :type str - - :parm strict if the check will be strict, target is exactly the mountpoint, or no, where the target is a leaf (f.i. to check if it is in /mnt/archinstall/). Not available for root check ('/') for obvious reasons - - """ - # we create the mountpoint list - if isinstance(partition,dict): - subvolumes: List[Subvolume] = partition.get('btrfs',{}).get('subvolumes', []) - mountpoints = [partition.get('mountpoint')] - mountpoints += [volume.mountpoint for volume in subvolumes] - else: - mountpoints = [partition.mountpoint,] + [subvol.target for subvol in partition.subvolumes] - - # we check - if strict or target == '/': - if target in mountpoints: - return True - else: - return False - else: - for mp in mountpoints: - if mp and mp.endswith(target): - return True - return False diff --git a/archinstall/lib/disk/mapperdev.py b/archinstall/lib/disk/mapperdev.py deleted file mode 100644 index bf1b3583..00000000 --- a/archinstall/lib/disk/mapperdev.py +++ /dev/null @@ -1,92 +0,0 @@ -import glob -import pathlib -import logging -import json -from dataclasses import dataclass -from typing import Optional, List, Dict, Any, Iterator, TYPE_CHECKING - -from ..exceptions import SysCallError -from ..general import SysCommand -from ..output import log - -if TYPE_CHECKING: - from .btrfs import BtrfsSubvolumeInfo - -@dataclass -class MapperDev: - mappername :str - - @property - def name(self): - return self.mappername - - @property - def path(self): - return f"/dev/mapper/{self.mappername}" - - @property - def part_uuid(self): - return self.partition.part_uuid - - @property - def partition(self): - from .helpers import uevent, get_parent_of_partition - from .partition import Partition - from .blockdevice import BlockDevice - - for mapper in glob.glob('/dev/mapper/*'): - path_obj = pathlib.Path(mapper) - if path_obj.name == self.mappername and pathlib.Path(mapper).is_symlink(): - dm_device = (pathlib.Path("/dev/mapper/") / path_obj.readlink()).resolve() - - for slave in glob.glob(f"/sys/class/block/{dm_device.name}/slaves/*"): - partition_belonging_to_dmcrypt_device = pathlib.Path(slave).name - - try: - uevent_data = SysCommand(f"blkid -o export /dev/{partition_belonging_to_dmcrypt_device}").decode() - except SysCallError as error: - log(f"Could not get information on device /dev/{partition_belonging_to_dmcrypt_device}: {error}", level=logging.ERROR, fg="red") - - information = uevent(uevent_data) - block_device = BlockDevice(get_parent_of_partition('/dev/' / pathlib.Path(information['DEVNAME']))) - - return Partition(information['DEVNAME'], block_device=block_device) - - raise ValueError(f"Could not convert {self.mappername} to a real dm-crypt device") - - @property - def mountpoint(self) -> Optional[pathlib.Path]: - try: - data = json.loads(SysCommand(f"findmnt --json -R {self.path}").decode()) - for filesystem in data['filesystems']: - return pathlib.Path(filesystem.get('target')) - - except SysCallError as error: - # Not mounted anywhere most likely - log(f"Could not locate mount information for {self.path}: {error}", level=logging.WARNING, fg="yellow") - pass - - return None - - @property - def mountpoints(self) -> List[Dict[str, Any]]: - return [obj['target'] for obj in self.mount_information] - - @property - def mount_information(self) -> List[Dict[str, Any]]: - from .helpers import find_mountpoint - return [{**obj, 'target' : pathlib.Path(obj.get('target', '/dev/null'))} for obj in find_mountpoint(self.path)] - - @property - def filesystem(self) -> Optional[str]: - from .helpers import get_filesystem_type - return get_filesystem_type(self.path) - - @property - def subvolumes(self) -> Iterator['BtrfsSubvolumeInfo']: - from .btrfs import subvolume_info_from_path - - for mountpoint in self.mount_information: - if target := mountpoint.get('target'): - if subvolume := subvolume_info_from_path(pathlib.Path(target)): - yield subvolume diff --git a/archinstall/lib/disk/partition.py b/archinstall/lib/disk/partition.py deleted file mode 100644 index 87eaa6a7..00000000 --- a/archinstall/lib/disk/partition.py +++ /dev/null @@ -1,661 +0,0 @@ -import glob -import time -import logging -import json -import os -import hashlib -import typing -from dataclasses import dataclass, field -from pathlib import Path -from typing import Optional, Dict, Any, List, Union, Iterator - -from .blockdevice import BlockDevice -from .helpers import get_filesystem_type, convert_size_to_gb, split_bind_name -from ..storage import storage -from ..exceptions import DiskError, SysCallError, UnknownFilesystemFormat -from ..output import log -from ..general import SysCommand -from .btrfs.btrfs_helpers import subvolume_info_from_path -from .btrfs.btrfssubvolumeinfo import BtrfsSubvolumeInfo - -@dataclass -class PartitionInfo: - partition_object: 'Partition' - device_path: str # This would be /dev/sda1 for instance - bootable: bool - size: float - sector_size: int - start: Optional[int] - end: Optional[int] - pttype: Optional[str] - filesystem_type: Optional[str] - partuuid: Optional[str] - uuid: Optional[str] - mountpoints: List[Path] = field(default_factory=list) - - def __post_init__(self): - if not all([self.partuuid, self.uuid]): - for i in range(storage['DISK_RETRY_ATTEMPTS']): - lsblk_info = SysCommand(f"lsblk --json -b -o+LOG-SEC,SIZE,PTTYPE,PARTUUID,UUID,FSTYPE {self.device_path}").decode('UTF-8') - try: - lsblk_info = json.loads(lsblk_info) - except json.decoder.JSONDecodeError: - log(f"Could not decode JSON: {lsblk_info}", fg="red", level=logging.ERROR) - raise DiskError(f'Failed to retrieve information for "{self.device_path}" with lsblk') - - if not (device := lsblk_info.get('blockdevices', [None])[0]): - raise DiskError(f'Failed to retrieve information for "{self.device_path}" with lsblk') - - self.partuuid = device.get('partuuid') - self.uuid = device.get('uuid') - - # Lets build a list of requirements that we would like - # to retry and build (stuff that can take time between partprobes) - requirements = [] - requirements.append(self.partuuid) - - # Unformatted partitions won't have a UUID - if lsblk_info.get('fstype') is not None: - requirements.append(self.uuid) - - if all(requirements): - break - - self.partition_object.partprobe() - time.sleep(max(0.1, storage['DISK_TIMEOUTS'] * i)) - - def get_first_mountpoint(self) -> Optional[Path]: - if len(self.mountpoints) > 0: - return self.mountpoints[0] - return None - - -class Partition: - def __init__( - self, - path: str, - block_device: BlockDevice, - part_id :Optional[str] = None, - filesystem :Optional[str] = None, - mountpoint :Optional[str] = None, - encrypted :bool = False, - autodetect_filesystem :bool = True, - ): - if not part_id: - part_id = os.path.basename(path) - - if type(block_device) is str: - raise ValueError(f"Partition()'s 'block_device' parameter has to be a archinstall.BlockDevice() instance!") - - self.block_device = block_device - self._path = path - self._part_id = part_id - self._target_mountpoint = mountpoint - self._encrypted = encrypted - self._wipe = False - self._type = 'primary' - - if mountpoint: - self.mount(mountpoint) - - try: - self._partition_info = self._fetch_information() - - if not autodetect_filesystem and filesystem: - self._partition_info.filesystem_type = filesystem - - if self._partition_info.filesystem_type == 'crypto_LUKS': - self._encrypted = True - except DiskError: - self._partition_info = None - - @typing.no_type_check # I hate doint this but I'm currently unsure where this is used. - def __lt__(self, left_comparitor :BlockDevice) -> bool: - if type(left_comparitor) == Partition: - left_comparitor = left_comparitor.path - else: - left_comparitor = str(left_comparitor) - - # The goal is to check if /dev/nvme0n1p1 comes before /dev/nvme0n1p5 - return self._path < left_comparitor - - def __repr__(self, *args :str, **kwargs :str) -> str: - mount_repr = '' - if self._partition_info: - if mountpoint := self._partition_info.get_first_mountpoint(): - mount_repr = f", mounted={mountpoint}" - elif self._target_mountpoint: - mount_repr = f", rel_mountpoint={self._target_mountpoint}" - - classname = self.__class__.__name__ - - if not self._partition_info: - return f'{classname}(path={self._path})' - elif self._encrypted: - return f'{classname}(path={self._path}, size={self.size}, PARTUUID={self.part_uuid}, parent={self.real_device}, fs={self._partition_info.filesystem_type}{mount_repr})' - else: - return f'{classname}(path={self._path}, size={self.size}, PARTUUID={self.part_uuid}, fs={self._partition_info.filesystem_type}{mount_repr})' - - def as_json(self) -> Dict[str, Any]: - """ - this is used for the table representation of the partition (see FormattedOutput) - """ - partition_info = { - 'type': self._type, - 'PARTUUID': self.part_uuid, - 'wipe': self._wipe, - 'boot': self.boot, - 'ESP': self.boot, - 'mountpoint': self._target_mountpoint, - 'encrypted': self._encrypted, - 'start': self.start, - 'size': self.end, - 'filesystem': self._partition_info.filesystem_type if self._partition_info else 'Unknown' - } - - return partition_info - - def __dump__(self) -> Dict[str, Any]: - # TODO remove this in favour of as_json - return { - 'type': self._type, - 'PARTUUID': self.part_uuid, - 'wipe': self._wipe, - 'boot': self.boot, - 'ESP': self.boot, - 'mountpoint': self._target_mountpoint, - 'encrypted': self._encrypted, - 'start': self.start, - 'size': self.end, - 'filesystem': { - 'format': self._partition_info.filesystem_type if self._partition_info else 'None' - } - } - - def _call_lsblk(self) -> Dict[str, Any]: - for retry_attempt in range(storage['DISK_RETRY_ATTEMPTS']): - self.partprobe() - time.sleep(max(0.1, storage['DISK_TIMEOUTS'] * retry_attempt)) # TODO: Remove, we should be relying on blkid instead of lsblk - # This sleep might be overkill, but lsblk is known to - # work against a chaotic cache that can change during call - # causing no information to be returned (blkid is better) - # time.sleep(1) - - # TODO: Maybe incorporate a re-try system here based on time.sleep(max(0.1, storage.get('DISK_TIMEOUTS', 1))) - - try: - output = SysCommand(f"lsblk --json -b -o+LOG-SEC,SIZE,PTTYPE,PARTUUID,UUID,FSTYPE {self.device_path}").decode('UTF-8') - except SysCallError as error: - # Get the output minus the message/info from lsblk if it returns a non-zero exit code. - output = error.worker.decode('UTF-8') - if '{' in output: - output = output[output.find('{'):] - - if output: - try: - lsblk_info = json.loads(output) - return lsblk_info - except json.decoder.JSONDecodeError: - log(f"Could not decode JSON: {output}", fg="red", level=logging.ERROR) - - raise DiskError(f'Failed to get partition information "{self.device_path}" with lsblk') - - def _call_sfdisk(self) -> Dict[str, Any]: - output = SysCommand(f"sfdisk --json {self.block_device.path}").decode('UTF-8') - - if output: - sfdisk_info = json.loads(output) - partitions = sfdisk_info.get('partitiontable', {}).get('partitions', []) - node = list(filter(lambda x: x['node'] == self._path, partitions)) - - if len(node) > 0: - return node[0] - - return {} - - raise DiskError(f'Failed to read disk "{self.block_device.path}" with sfdisk') - - def _fetch_information(self) -> PartitionInfo: - lsblk_info = self._call_lsblk() - sfdisk_info = self._call_sfdisk() - - if not (device := lsblk_info.get('blockdevices', [])): - raise DiskError(f'Failed to retrieve information for "{self.device_path}" with lsblk') - - # Grab the first (and only) block device in the list as we're targeting a specific partition - device = device[0] - - mountpoints = [Path(mountpoint) for mountpoint in device['mountpoints'] if mountpoint] - bootable = sfdisk_info.get('bootable', False) or sfdisk_info.get('type', '') == 'C12A7328-F81F-11D2-BA4B-00A0C93EC93B' - - return PartitionInfo( - partition_object=self, - device_path=self._path, - pttype=device['pttype'], - partuuid=device['partuuid'], - uuid=device['uuid'], - sector_size=device['log-sec'], - size=convert_size_to_gb(device['size']), - start=sfdisk_info.get('start', None), - end=sfdisk_info.get('size', None), - bootable=bootable, - filesystem_type=device['fstype'], - mountpoints=mountpoints - ) - - @property - def target_mountpoint(self) -> Optional[str]: - return self._target_mountpoint - - @property - def path(self) -> str: - return self._path - - @property - def filesystem(self) -> str: - if self._partition_info: - return self._partition_info.filesystem_type - - @property - def mountpoint(self) -> Optional[Path]: - if len(self.mountpoints) > 0: - return self.mountpoints[0] - return None - - @property - def mountpoints(self) -> List[Path]: - if self._partition_info: - return self._partition_info.mountpoints - - @property - def sector_size(self) -> int: - if self._partition_info: - return self._partition_info.sector_size - - @property - def start(self) -> Optional[int]: - if self._partition_info: - return self._partition_info.start - - @property - def end(self) -> Optional[int]: - if self._partition_info: - return self._partition_info.end - - @property - def end_sectors(self) -> Optional[int]: - if self._partition_info: - start = self._partition_info.start - end = self._partition_info.end - if start and end: - return start + end - - @property - def size(self) -> Optional[float]: - if self._partition_info: - return self._partition_info.size - - @property - def boot(self) -> bool: - if self._partition_info: - return self._partition_info.bootable - - @property - def partition_type(self) -> Optional[str]: - if self._partition_info: - return self._partition_info.pttype - - @property - def part_uuid(self) -> str: - if self._partition_info: - return self._partition_info.partuuid - - @property - def uuid(self) -> Optional[str]: - """ - Returns the UUID as returned by lsblk for the **partition**. - This is more reliable than relying on /dev/disk/by-uuid as - it doesn't seam to be able to detect md raid partitions. - For bind mounts all the subvolumes share the same uuid - """ - for i in range(storage['DISK_RETRY_ATTEMPTS']): - if not self.partprobe(): - raise DiskError(f"Could not perform partprobe on {self.device_path}") - - time.sleep(storage.get('DISK_TIMEOUTS', 1) * i) - - partuuid = self._safe_uuid - if partuuid: - return partuuid - - raise DiskError(f"Could not get PARTUUID for {self.path} using 'blkid -s PARTUUID -o value {self.path}'") - - @property - def _safe_uuid(self) -> Optional[str]: - """ - A near copy of self.uuid but without any delays. - This function should only be used where uuid is not crucial. - For instance when you want to get a __repr__ of the class. - """ - if not self.partprobe(): - if self.block_device.partition_type == 'iso9660': - return None - - log(f"Could not reliably refresh PARTUUID of partition {self.device_path} due to partprobe error.", level=logging.DEBUG) - - try: - return SysCommand(f'blkid -s UUID -o value {self.device_path}').decode('UTF-8').strip() - except SysCallError as error: - if self.block_device.partition_type == 'iso9660': - # Parent device is a Optical Disk (.iso dd'ed onto a device for instance) - return None - - log(f"Could not get PARTUUID of partition using 'blkid -s UUID -o value {self.device_path}': {error}") - - @property - def _safe_part_uuid(self) -> Optional[str]: - """ - A near copy of self.uuid but without any delays. - This function should only be used where uuid is not crucial. - For instance when you want to get a __repr__ of the class. - """ - if not self.partprobe(): - if self.block_device.partition_type == 'iso9660': - return None - - log(f"Could not reliably refresh PARTUUID of partition {self.device_path} due to partprobe error.", level=logging.DEBUG) - - try: - return SysCommand(f'blkid -s PARTUUID -o value {self.device_path}').decode('UTF-8').strip() - except SysCallError as error: - if self.block_device.partition_type == 'iso9660': - # Parent device is a Optical Disk (.iso dd'ed onto a device for instance) - return None - - log(f"Could not get PARTUUID of partition using 'blkid -s PARTUUID -o value {self.device_path}': {error}") - - if self._partition_info: - return self._partition_info.uuid - - @property - def encrypted(self) -> Union[bool, None]: - return self._encrypted - - @property - def parent(self) -> str: - return self.real_device - - @property - def real_device(self) -> str: - output = SysCommand('lsblk -J').decode('UTF-8') - - if output: - for blockdevice in json.loads(output)['blockdevices']: - if parent := self.find_parent_of(blockdevice, os.path.basename(self.device_path)): - return f"/dev/{parent}" - return self._path - - raise DiskError('Unable to get disk information for command "lsblk -J"') - - @property - def device_path(self) -> str: - """ for bind mounts returns the physical path of the partition - """ - device_path, bind_name = split_bind_name(self._path) - return device_path - - @property - def bind_name(self) -> str: - """ for bind mounts returns the bind name (subvolume path). - Returns none if this property does not exist - """ - device_path, bind_name = split_bind_name(self._path) - return bind_name - - @property - def subvolumes(self) -> Iterator[BtrfsSubvolumeInfo]: - from .helpers import findmnt - - def iterate_children_recursively(information): - for child in information.get('children', []): - if target := child.get('target'): - if child.get('fstype') == 'btrfs': - if subvolume := subvolume_info_from_path(Path(target)): - yield subvolume - - if child.get('children'): - for subchild in iterate_children_recursively(child): - yield subchild - - if self._partition_info.filesystem_type == 'btrfs': - for mountpoint in self._partition_info.mountpoints: - if result := findmnt(mountpoint): - for filesystem in result.get('filesystems', []): - if subvolume := subvolume_info_from_path(mountpoint): - yield subvolume - - for child in iterate_children_recursively(filesystem): - yield child - - def partprobe(self) -> bool: - try: - if self.block_device: - return 0 == SysCommand(f'partprobe {self.block_device.device}').exit_code - except SysCallError as error: - log(f"Unreliable results might be given for {self._path} due to partprobe error: {error}", level=logging.DEBUG) - - return False - - def detect_inner_filesystem(self, password :str) -> Optional[str]: - log(f'Trying to detect inner filesystem format on {self} (This might take a while)', level=logging.INFO) - from ..luks import luks2 - - try: - with luks2(self, storage.get('ENC_IDENTIFIER', 'ai') + 'loop', password, auto_unmount=True) as unlocked_device: - return unlocked_device.filesystem - except SysCallError: - pass - return None - - def has_content(self) -> bool: - fs_type = self._partition_info.filesystem_type - if not fs_type or "swap" in fs_type: - return False - - temporary_mountpoint = '/tmp/' + hashlib.md5(bytes(f"{time.time()}", 'UTF-8') + os.urandom(12)).hexdigest() - temporary_path = Path(temporary_mountpoint) - - temporary_path.mkdir(parents=True, exist_ok=True) - if (handle := SysCommand(f'/usr/bin/mount {self._path} {temporary_mountpoint}')).exit_code != 0: - raise DiskError(f'Could not mount and check for content on {self._path} because: {handle}') - - files = len(glob.glob(f"{temporary_mountpoint}/*")) - iterations = 0 - while SysCommand(f"/usr/bin/umount -R {temporary_mountpoint}").exit_code != 0 and (iterations := iterations + 1) < 10: - time.sleep(1) - - temporary_path.rmdir() - - return True if files > 0 else False - - def encrypt(self, password: Optional[str] = None) -> str: - """ - A wrapper function for luks2() instances and the .encrypt() method of that instance. - """ - from ..luks import luks2 - - handle = luks2(self, None, None) - return handle.encrypt(self, password=password) - - def format(self, filesystem :Optional[str] = None, path :Optional[str] = None, log_formatting :bool = True, options :List[str] = [], retry :bool = True) -> bool: - """ - Format can be given an overriding path, for instance /dev/null to test - the formatting functionality and in essence the support for the given filesystem. - """ - if filesystem is None: - filesystem = self._partition_info.filesystem_type - - if path is None: - path = self._path - - # This converts from fat32 -> vfat to unify filesystem names - filesystem = get_mount_fs_type(filesystem) - - # To avoid "unable to open /dev/x: No such file or directory" - start_wait = time.time() - while Path(path).exists() is False and time.time() - start_wait < 10: - time.sleep(0.025) - - if log_formatting: - log(f'Formatting {path} -> {filesystem}', level=logging.INFO) - - try: - if filesystem == 'btrfs': - options = ['-f'] + options - - mkfs = SysCommand(f"/usr/bin/mkfs.btrfs {' '.join(options)} {path}").decode('UTF-8') - if mkfs and 'UUID:' not in mkfs: - raise DiskError(f'Could not format {path} with {filesystem} because: {mkfs}') - self._partition_info.filesystem_type = filesystem - - elif filesystem == 'vfat': - options = ['-F32'] + options - log(f"/usr/bin/mkfs.vfat {' '.join(options)} {path}") - if (handle := SysCommand(f"/usr/bin/mkfs.vfat {' '.join(options)} {path}")).exit_code != 0: - raise DiskError(f"Could not format {path} with {filesystem} because: {handle.decode('UTF-8')}") - self._partition_info.filesystem_type = filesystem - - elif filesystem == 'ext4': - options = ['-F'] + options - - if (handle := SysCommand(f"/usr/bin/mkfs.ext4 {' '.join(options)} {path}")).exit_code != 0: - raise DiskError(f"Could not format {path} with {filesystem} because: {handle.decode('UTF-8')}") - self._partition_info.filesystem_type = filesystem - - elif filesystem == 'ext2': - options = ['-F'] + options - - if (handle := SysCommand(f"/usr/bin/mkfs.ext2 {' '.join(options)} {path}")).exit_code != 0: - raise DiskError(f"Could not format {path} with {filesystem} because: {handle.decode('UTF-8')}") - self._partition_info.filesystem_type = 'ext2' - elif filesystem == 'xfs': - options = ['-f'] + options - - if (handle := SysCommand(f"/usr/bin/mkfs.xfs {' '.join(options)} {path}")).exit_code != 0: - raise DiskError(f"Could not format {path} with {filesystem} because: {handle.decode('UTF-8')}") - self._partition_info.filesystem_type = filesystem - - elif filesystem == 'f2fs': - options = ['-f'] + options - - if (handle := SysCommand(f"/usr/bin/mkfs.f2fs {' '.join(options)} {path}")).exit_code != 0: - raise DiskError(f"Could not format {path} with {filesystem} because: {handle.decode('UTF-8')}") - self._partition_info.filesystem_type = filesystem - - elif filesystem == 'ntfs3': - options = ['-f'] + options - - if (handle := SysCommand(f"/usr/bin/mkfs.ntfs -Q {' '.join(options)} {path}")).exit_code != 0: - raise DiskError(f"Could not format {path} with {filesystem} because: {handle.decode('UTF-8')}") - self._partition_info.filesystem_type = filesystem - - elif filesystem == 'crypto_LUKS': - # from ..luks import luks2 - # encrypted_partition = luks2(self, None, None) - # encrypted_partition.format(path) - self._partition_info.filesystem_type = filesystem - - else: - raise UnknownFilesystemFormat(f"Fileformat '{filesystem}' is not yet implemented.") - except SysCallError as error: - log(f"Formatting ran in to an error: {error}", level=logging.WARNING, fg="orange") - if retry is True: - log(f"Retrying in {storage.get('DISK_TIMEOUTS', 1)} seconds.", level=logging.WARNING, fg="orange") - time.sleep(storage.get('DISK_TIMEOUTS', 1)) - - return self.format(filesystem, path, log_formatting, options, retry=False) - - if get_filesystem_type(path) == 'crypto_LUKS' or get_filesystem_type(self.real_device) == 'crypto_LUKS': - self._encrypted = True - else: - self._encrypted = False - - return True - - def find_parent_of(self, data :Dict[str, Any], name :str, parent :Optional[str] = None) -> Optional[str]: - if data['name'] == name: - return parent - elif 'children' in data: - for child in data['children']: - if parent := self.find_parent_of(child, name, parent=data['name']): - return parent - - return None - - def mount(self, target :str, fs :Optional[str] = None, options :str = '') -> bool: - if not self._partition_info.get_first_mountpoint(): - log(f'Mounting {self} to {target}', level=logging.INFO) - - if not fs: - fs = self._partition_info.filesystem_type - - fs_type = get_mount_fs_type(fs) - - Path(target).mkdir(parents=True, exist_ok=True) - - if self.bind_name: - device_path = self.device_path - # TODO options should be better be a list than a string - if options: - options = f"{options},subvol={self.bind_name}" - else: - options = f"subvol={self.bind_name}" - else: - device_path = self._path - try: - if options: - mnt_handle = SysCommand(f"/usr/bin/mount -t {fs_type} -o {options} {device_path} {target}") - else: - mnt_handle = SysCommand(f"/usr/bin/mount -t {fs_type} {device_path} {target}") - - # TODO: Should be redundant to check for exit_code - if mnt_handle.exit_code != 0: - raise DiskError(f"Could not mount {self._path} to {target} using options {options}") - except SysCallError as err: - raise err - - # Update the partition info since the mount info has changed after this call. - self._partition_info = self._fetch_information() - return True - - return False - - def unmount(self) -> bool: - SysCommand(f"/usr/bin/umount {self._path}") - - # Update the partition info since the mount info has changed after this call. - self._partition_info = self._fetch_information() - return True - - def filesystem_supported(self) -> bool: - """ - The support for a filesystem (this partition) is tested by calling - partition.format() with a path set to '/dev/null' which returns two exceptions: - 1. SysCallError saying that /dev/null is not formattable - but the filesystem is supported - 2. UnknownFilesystemFormat that indicates that we don't support the given filesystem type - """ - try: - self.format(self._partition_info.filesystem_type, '/dev/null', log_formatting=False) - except (SysCallError, DiskError): - pass # We supported it, but /dev/null is not formattable as expected so the mkfs call exited with an error code - except UnknownFilesystemFormat as err: - raise err - return True - - -def get_mount_fs_type(fs :str) -> str: - if fs == 'ntfs': - return 'ntfs3' # Needed to use the Paragon R/W NTFS driver - elif fs == 'fat32': - return 'vfat' # This is the actual type used for fat32 mounting - return fs diff --git a/archinstall/lib/disk/partitioning_menu.py b/archinstall/lib/disk/partitioning_menu.py new file mode 100644 index 00000000..686e8c29 --- /dev/null +++ b/archinstall/lib/disk/partitioning_menu.py @@ -0,0 +1,335 @@ +from __future__ import annotations + +import logging +from pathlib import Path +from typing import Any, Dict, TYPE_CHECKING, List, Optional, Tuple + +from .device_model import PartitionModification, FilesystemType, BDevice, Size, Unit, PartitionType, PartitionFlag, \ + ModificationStatus +from ..menu import Menu, ListManager, MenuSelection, TextInput +from ..output import FormattedOutput, log +from .subvolume_menu import SubvolumeMenu + +if TYPE_CHECKING: + _: Any + + +class PartitioningList(ListManager): + """ + subclass of ListManager for the managing of user accounts + """ + def __init__(self, prompt: str, device: BDevice, device_partitions: List[PartitionModification]): + self._device = device + self._actions = { + 'create_new_partition': str(_('Create a new partition')), + 'suggest_partition_layout': str(_('Suggest partition layout')), + 'remove_added_partitions': str(_('Remove all newly added partitions')), + 'assign_mountpoint': str(_('Assign mountpoint')), + 'mark_formatting': str(_('Mark/Unmark to be formatted (wipes data)')), + 'mark_bootable': str(_('Mark/Unmark as bootable')), + 'set_filesystem': str(_('Change filesystem')), + 'btrfs_mark_compressed': str(_('Mark/Unmark as compressed')), # btrfs only + 'btrfs_set_subvolumes': str(_('Set subvolumes')), # btrfs only + 'delete_partition': str(_('Delete partition')) + } + + display_actions = list(self._actions.values()) + super().__init__(prompt, device_partitions, display_actions[:2], display_actions[3:]) + + def reformat(self, data: List[PartitionModification]) -> Dict[str, Optional[PartitionModification]]: + table = FormattedOutput.as_table(data) + rows = table.split('\n') + + # these are the header rows of the table and do not map to any User obviously + # we're adding 2 spaces as prefix because the menu selector '> ' will be put before + # the selectable rows so the header has to be aligned + display_data: Dict[str, Optional[PartitionModification]] = {f' {rows[0]}': None, f' {rows[1]}': None} + + for row, user in zip(rows[2:], data): + row = row.replace('|', '\\|') + display_data[row] = user + + return display_data + + def selected_action_display(self, partition: PartitionModification) -> str: + return str(_('Partition')) + + def filter_options(self, selection: PartitionModification, options: List[str]) -> List[str]: + not_filter = [] + + # only display formatting if the partition exists already + if not selection.exists(): + not_filter += [self._actions['mark_formatting']] + else: + # only allow these options if the existing partition + # was marked as formatting, otherwise we run into issues where + # 1. select a new fs -> potentially mark as wipe now + # 2. Switch back to old filesystem -> should unmark wipe now, but + # how do we know it was the original one? + not_filter += [ + self._actions['set_filesystem'], + self._actions['assign_mountpoint'], + self._actions['mark_bootable'], + self._actions['btrfs_mark_compressed'], + self._actions['btrfs_set_subvolumes'] + ] + + # non btrfs partitions shouldn't get btrfs options + if selection.fs_type != FilesystemType.Btrfs: + not_filter += [self._actions['btrfs_mark_compressed'], self._actions['btrfs_set_subvolumes']] + else: + not_filter += [self._actions['assign_mountpoint']] + + return [o for o in options if o not in not_filter] + + def handle_action( + self, + action: str, + entry: Optional[PartitionModification], + data: List[PartitionModification] + ) -> List[PartitionModification]: + action_key = [k for k, v in self._actions.items() if v == action][0] + + match action_key: + case 'create_new_partition': + new_partition = self._create_new_partition() + data += [new_partition] + case 'suggest_partition_layout': + new_partitions = self._suggest_partition_layout(data) + if len(new_partitions) > 0: + data = new_partitions + case 'remove_added_partitions': + choice = self._reset_confirmation() + if choice.value == Menu.yes(): + data = [part for part in data if part.is_exists_or_modify()] + case 'assign_mountpoint' if entry: + entry.mountpoint = self._prompt_mountpoint() + if entry.mountpoint == Path('/boot'): + entry.set_flag(PartitionFlag.Boot) + case 'mark_formatting' if entry: + self._prompt_formatting(entry) + case 'mark_bootable' if entry: + entry.invert_flag(PartitionFlag.Boot) + case 'set_filesystem' if entry: + fs_type = self._prompt_partition_fs_type() + if fs_type: + entry.fs_type = fs_type + # btrfs subvolumes will define mountpoints + if fs_type == FilesystemType.Btrfs: + entry.mountpoint = None + case 'btrfs_mark_compressed' if entry: + self._set_compressed(entry) + case 'btrfs_set_subvolumes' if entry: + self._set_btrfs_subvolumes(entry) + case 'delete_partition' if entry: + data = self._delete_partition(entry, data) + + return data + + def _delete_partition( + self, + entry: PartitionModification, + data: List[PartitionModification] + ) -> List[PartitionModification]: + if entry.is_exists_or_modify(): + entry.status = ModificationStatus.Delete + return data + else: + return [d for d in data if d != entry] + + def _set_compressed(self, partition: PartitionModification): + compression = 'compress=zstd' + + if compression in partition.mount_options: + partition.mount_options = [o for o in partition.mount_options if o != compression] + else: + partition.mount_options.append(compression) + + def _set_btrfs_subvolumes(self, partition: PartitionModification): + partition.btrfs_subvols = SubvolumeMenu( + _("Manage btrfs subvolumes for current partition"), + partition.btrfs_subvols + ).run() + + def _prompt_formatting(self, partition: PartitionModification): + # an existing partition can toggle between Exist or Modify + if partition.is_modify(): + partition.status = ModificationStatus.Exist + return + elif partition.exists(): + partition.status = ModificationStatus.Modify + + # If we mark a partition for formatting, but the format is CRYPTO LUKS, there's no point in formatting it really + # without asking the user which inner-filesystem they want to use. Since the flag 'encrypted' = True is already set, + # it's safe to change the filesystem for this partition. + if partition.fs_type == FilesystemType.Crypto_luks: + prompt = str(_('This partition is currently encrypted, to format it a filesystem has to be specified')) + fs_type = self._prompt_partition_fs_type(prompt) + partition.fs_type = fs_type + + if fs_type == FilesystemType.Btrfs: + partition.mountpoint = None + + def _prompt_mountpoint(self) -> Path: + header = str(_('Partition mount-points are relative to inside the installation, the boot would be /boot as an example.')) + '\n' + header += str(_('If mountpoint /boot is set, then the partition will also be marked as bootable.')) + '\n' + prompt = str(_('Mountpoint: ')) + + print(header) + + while True: + value = TextInput(prompt).run().strip() + + if value: + mountpoint = Path(value) + break + + return mountpoint + + def _prompt_partition_fs_type(self, prompt: str = '') -> FilesystemType: + options = {fs.value: fs for fs in FilesystemType if fs != FilesystemType.Crypto_luks} + + prompt = prompt + '\n' + str(_('Enter a desired filesystem type for the partition')) + choice = Menu(prompt, options, sort=False, skip=False).run() + return options[choice.single_value] + + def _validate_sector(self, start_sector: str, end_sector: Optional[str] = None) -> bool: + if not start_sector.isdigit(): + return False + + if end_sector: + if end_sector.endswith('%'): + if not end_sector[:-1].isdigit(): + return False + elif not end_sector.isdigit(): + return False + elif int(start_sector) > int(end_sector): + return False + + return True + + def _prompt_sectors(self) -> Tuple[Size, Size]: + device_info = self._device.device_info + + text = str(_('Current free sectors on device {}:')).format(device_info.path) + '\n\n' + free_space_table = FormattedOutput.as_table(device_info.free_space_regions) + prompt = text + free_space_table + '\n' + + total_sectors = device_info.total_size.format_size(Unit.sectors, device_info.sector_size) + prompt += str(_('Total sectors: {}')).format(total_sectors) + '\n' + print(prompt) + + largest_free_area = max(device_info.free_space_regions, key=lambda r: r.get_length()) + + # prompt until a valid start sector was entered + while True: + start_prompt = str(_('Enter the start sector (default: {}): ')).format(largest_free_area.start) + start_sector = TextInput(start_prompt).run().strip() + + if not start_sector or self._validate_sector(start_sector): + break + + log(f'Invalid start sector entered: {start_sector}', fg='red', level=logging.INFO) + + if not start_sector: + start_sector = str(largest_free_area.start) + end_sector = str(largest_free_area.end) + else: + end_sector = '100%' + + # prompt until valid end sector was entered + while True: + end_prompt = str(_('Enter the end sector of the partition (percentage or block number, default: {}): ')).format(end_sector) + end_value = TextInput(end_prompt).run().strip() + + if not end_value or self._validate_sector(start_sector, end_value): + break + + log(f'Invalid end sector entered: {start_sector}', fg='red', level=logging.INFO) + + # override the default value with the user value + if end_value: + end_sector = end_value + + start_size = Size(int(start_sector), Unit.sectors, device_info.sector_size) + + if end_sector.endswith('%'): + end_size = Size(int(end_sector[:-1]), Unit.Percent, device_info.sector_size, device_info.total_size) + else: + end_size = Size(int(end_sector), Unit.sectors, device_info.sector_size) + + return start_size, end_size + + def _create_new_partition(self) -> PartitionModification: + fs_type = self._prompt_partition_fs_type() + + start_size, end_size = self._prompt_sectors() + length = end_size - start_size + + # new line for the next prompt + print() + + mountpoint = None + if fs_type != FilesystemType.Btrfs: + mountpoint = self._prompt_mountpoint() + + partition = PartitionModification( + status=ModificationStatus.Create, + type=PartitionType.Primary, + start=start_size, + length=length, + fs_type=fs_type, + mountpoint=mountpoint + ) + + if partition.mountpoint == Path('/boot'): + partition.set_flag(PartitionFlag.Boot) + + return partition + + def _reset_confirmation(self) -> MenuSelection: + prompt = str(_('This will remove all newly added partitions, continue?')) + choice = Menu(prompt, Menu.yes_no(), default_option=Menu.no(), skip=False).run() + return choice + + def _suggest_partition_layout(self, data: List[PartitionModification]) -> List[PartitionModification]: + # if modifications have been done already, inform the user + # that this operation will erase those modifications + if any([not entry.exists() for entry in data]): + choice = self._reset_confirmation() + if choice.value == Menu.no(): + return [] + + from ..user_interaction.disk_conf import suggest_single_disk_layout + + device_modification = suggest_single_disk_layout(self._device) + return device_modification.partitions + + +def manual_partitioning( + device: BDevice, + prompt: str = '', + preset: List[PartitionModification] = [] +) -> List[PartitionModification]: + if not prompt: + prompt = str(_('Partition management: {}')).format(device.device_info.path) + '\n' + prompt += str(_('Total length: {}')).format(device.device_info.total_size.format_size(Unit.MiB)) + + manual_preset = [] + + if not preset: + # we'll display the existing partitions of the device + for partition in device.partition_infos: + manual_preset.append( + PartitionModification.from_existing_partition(partition) + ) + else: + manual_preset = preset + + menu_list = PartitioningList(prompt, device, manual_preset) + partitions = menu_list.run() + + if menu_list.is_last_choice_cancel(): + return preset + + return partitions diff --git a/archinstall/lib/disk/subvolume_menu.py b/archinstall/lib/disk/subvolume_menu.py new file mode 100644 index 00000000..32a0e616 --- /dev/null +++ b/archinstall/lib/disk/subvolume_menu.py @@ -0,0 +1,101 @@ +from pathlib import Path +from typing import Dict, List, Optional, Any, TYPE_CHECKING + +from .device_model import SubvolumeModification +from ..menu import Menu, TextInput, MenuSelectionType, ListManager +from ..output import FormattedOutput + +if TYPE_CHECKING: + _: Any + + +class SubvolumeMenu(ListManager): + def __init__(self, prompt: str, btrfs_subvols: List[SubvolumeModification]): + self._actions = [ + str(_('Add subvolume')), + str(_('Edit subvolume')), + str(_('Delete subvolume')) + ] + super().__init__(prompt, btrfs_subvols, [self._actions[0]], self._actions[1:]) + + def reformat(self, data: List[SubvolumeModification]) -> Dict[str, Optional[SubvolumeModification]]: + table = FormattedOutput.as_table(data) + rows = table.split('\n') + + # these are the header rows of the table and do not map to any User obviously + # we're adding 2 spaces as prefix because the menu selector '> ' will be put before + # the selectable rows so the header has to be aligned + display_data: Dict[str, Optional[SubvolumeModification]] = {f' {rows[0]}': None, f' {rows[1]}': None} + + for row, subvol in zip(rows[2:], data): + row = row.replace('|', '\\|') + display_data[row] = subvol + + return display_data + + def selected_action_display(self, subvolume: SubvolumeModification) -> str: + return str(subvolume.name) + + def _prompt_options(self, editing: Optional[SubvolumeModification] = None) -> List[str]: + preset_options = [] + if editing: + preset_options = editing.mount_options + + choice = Menu( + str(_("Select the desired subvolume options ")), + ['nodatacow', 'compress'], + skip=True, + preset_values=preset_options, + multi=True + ).run() + + if choice.type_ == MenuSelectionType.Selection: + return choice.value # type: ignore + + return [] + + def _add_subvolume(self, editing: Optional[SubvolumeModification] = None) -> Optional[SubvolumeModification]: + name = TextInput(f'\n\n{_("Subvolume name")}: ', editing.name if editing else '').run() + + if not name: + return None + + mountpoint = TextInput(f'{_("Subvolume mountpoint")}: ', str(editing.mountpoint) if editing else '').run() + + if not mountpoint: + return None + + options = self._prompt_options(editing) + + subvolume = SubvolumeModification(Path(name), Path(mountpoint)) + subvolume.compress = 'compress' in options + subvolume.nodatacow = 'nodatacow' in options + + return subvolume + + def handle_action( + self, + action: str, + entry: Optional[SubvolumeModification], + data: List[SubvolumeModification] + ) -> List[SubvolumeModification]: + if action == self._actions[0]: # add + new_subvolume = self._add_subvolume() + + if new_subvolume 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.name != new_subvolume.name] + data += [new_subvolume] + elif entry is not None: + if action == self._actions[1]: # edit subvolume + new_subvolume = self._add_subvolume(entry) + + if new_subvolume is not None: + # we'll remove the original subvolume and add the modified version + data = [d for d in data if d.name != entry.name and d.name != new_subvolume.name] + data += [new_subvolume] + elif action == self._actions[2]: # delete + data = [d for d in data if d != entry] + + return data diff --git a/archinstall/lib/disk/user_guides.py b/archinstall/lib/disk/user_guides.py deleted file mode 100644 index 5809c073..00000000 --- a/archinstall/lib/disk/user_guides.py +++ /dev/null @@ -1,240 +0,0 @@ -from __future__ import annotations -import logging -from typing import Optional, Dict, Any, List, TYPE_CHECKING - -# https://stackoverflow.com/a/39757388/929999 -from ..models.subvolume import Subvolume - -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 -from ..output import log -from ..menu import Menu - - -def suggest_single_disk_layout(block_device :BlockDevice, - default_filesystem :Optional[str] = None, - advanced_options :bool = False) -> Dict[str, Any]: - - if not default_filesystem: - from ..user_interaction import ask_for_main_filesystem_format - default_filesystem = ask_for_main_filesystem_format(advanced_options) - - MIN_SIZE_TO_ALLOW_HOME_PART = 40 # GiB - using_subvolumes = False - using_home_partition = False - compression = False - - 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.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.value == Menu.yes() - - layout = { - block_device.path : { - "wipe" : True, - "partitions" : [] - } - } - - # Used for reference: https://wiki.archlinux.org/title/partitioning - - # 2 MiB is unallocated for GRUB on BIOS. Potentially unneeded for - # other bootloaders? - - # TODO: On BIOS, /boot partition is only needed if the drive will - # be encrypted, otherwise it is not recommended. We should probably - # add a check for whether the drive will be encrypted or not. - layout[block_device.path]['partitions'].append({ - # Boot - "type" : "primary", - "start" : "3MiB", - "size" : "203MiB", - "boot" : True, - "encrypted" : False, - "wipe" : True, - "mountpoint" : "/boot", - "filesystem" : { - "format" : "fat32" - } - }) - - # Increase the UEFI partition if UEFI is detected. - # Also re-align the start to 1MiB since we don't need the first sectors - # like we do in MBR layouts where the boot loader is installed traditionally. - if has_uefi(): - layout[block_device.path]['partitions'][-1]['start'] = '1MiB' - layout[block_device.path]['partitions'][-1]['size'] = '512MiB' - - layout[block_device.path]['partitions'].append({ - # Root - "type" : "primary", - "start" : "206MiB", - "encrypted" : False, - "wipe" : True, - "mountpoint" : "/" if not using_subvolumes else None, - "filesystem" : { - "format" : default_filesystem, - "mount_options" : ["compress=zstd"] if compression else [] - } - }) - - if has_uefi(): - layout[block_device.path]['partitions'][-1]['start'] = '513MiB' - - 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.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: - # We'll use subvolumes - # Or the disk size is too small to allow for a separate /home - # Or the user doesn't want to create a separate partition for /home - layout[block_device.path]['partitions'][-1]['size'] = '100%' - else: - layout[block_device.path]['partitions'][-1]['size'] = f"{min(block_device.size, 20)}GiB" - - if default_filesystem == 'btrfs' and using_subvolumes: - # if input('Do you want to use a recommended structure? (Y/n): ').strip().lower() in ('', 'y', 'yes'): - # https://btrfs.wiki.kernel.org/index.php/FAQ - # https://unix.stackexchange.com/questions/246976/btrfs-subvolume-uuid-clash - # https://github.com/classy-giraffe/easy-arch/blob/main/easy-arch.sh - layout[block_device.path]['partitions'][1]['btrfs'] = { - 'subvolumes': [ - Subvolume('@', '/'), - Subvolume('@home', '/home'), - Subvolume('@log', '/var/log'), - Subvolume('@pkg', '/var/cache/pacman/pkg'), - Subvolume('@.snapshots', '/.snapshots') - ] - } - elif using_home_partition: - # If we don't want to use subvolumes, - # But we want to be able to re-use data between re-installs.. - # A second partition for /home would be nice if we have the space for it - layout[block_device.path]['partitions'].append({ - # Home - "type" : "primary", - "start" : f"{min(block_device.size, 20)}GiB", - "size" : "100%", - "encrypted" : False, - "wipe" : True, - "mountpoint" : "/home", - "filesystem" : { - "format" : default_filesystem, - "mount_options" : ["compress=zstd"] if compression else [] - } - }) - - return layout - - -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 - default_filesystem = ask_for_main_filesystem_format(advanced_options) - - # Not really a rock solid foundation of information to stand on, but it's a start: - # https://www.reddit.com/r/btrfs/comments/m287gp/partition_strategy_for_two_physical_disks/ - # https://www.reddit.com/r/btrfs/comments/9us4hr/what_is_your_btrfs_partitionsubvolumes_scheme/ - - MIN_SIZE_TO_ALLOW_HOME_PART = 40 # GiB - ARCH_LINUX_INSTALLED_SIZE = 20 # GiB, rough estimate taking in to account user desktops etc. TODO: Catch user packages to detect size? - - block_devices = sort_block_devices_based_on_performance(block_devices).keys() - - 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': - # 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 compression?')) - choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run() - 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) - - layout = { - root_device.path : { - "wipe" : True, - "partitions" : [] - }, - home_device.path : { - "wipe" : True, - "partitions" : [] - }, - } - - # TODO: Same deal as with the single disk layout, we should - # probably check if the drive will be encrypted. - layout[root_device.path]['partitions'].append({ - # Boot - "type" : "primary", - "start" : "3MiB", - "size" : "203MiB", - "boot" : True, - "encrypted" : False, - "wipe" : True, - "mountpoint" : "/boot", - "filesystem" : { - "format" : "fat32" - } - }) - - if has_uefi(): - layout[root_device.path]['partitions'][-1]['start'] = '1MiB' - layout[root_device.path]['partitions'][-1]['size'] = '512MiB' - - layout[root_device.path]['partitions'].append({ - # Root - "type" : "primary", - "start" : "206MiB", - "size" : "100%", - "encrypted" : False, - "wipe" : True, - "mountpoint" : "/", - "filesystem" : { - "format" : default_filesystem, - "mount_options" : ["compress=zstd"] if compression else [] - } - }) - if has_uefi(): - layout[root_device.path]['partitions'][-1]['start'] = '513MiB' - - layout[home_device.path]['partitions'].append({ - # Home - "type" : "primary", - "start" : "1MiB", - "size" : "100%", - "encrypted" : False, - "wipe" : True, - "mountpoint" : "/home", - "filesystem" : { - "format" : default_filesystem, - "mount_options" : ["compress=zstd"] if compression else [] - } - }) - - return layout diff --git a/archinstall/lib/disk/validators.py b/archinstall/lib/disk/validators.py deleted file mode 100644 index 076a8ba2..00000000 --- a/archinstall/lib/disk/validators.py +++ /dev/null @@ -1,48 +0,0 @@ -from typing import List - -def valid_parted_position(pos :str) -> bool: - if not len(pos): - return False - - if pos.isdigit(): - return True - - pos_lower = pos.lower() - - if (pos_lower.endswith('b') or pos_lower.endswith('s')) and pos[:-1].isdigit(): - return True - - if any(pos_lower.endswith(size) and pos[:-len(size)].replace(".", "", 1).isdigit() - for size in ['%', 'kb', 'mb', 'gb', 'tb', 'kib', 'mib', 'gib', 'tib']): - return True - - return False - - -def fs_types() -> List[str]: - # https://www.gnu.org/software/parted/manual/html_node/mkpart.html - # Above link doesn't agree with `man parted` /mkpart documentation: - """ - fs-type can - be one of "btrfs", "ext2", - "ext3", "ext4", "fat16", - "fat32", "hfs", "hfs+", - "linux-swap", "ntfs", "reis‐ - erfs", "udf", or "xfs". - """ - return [ - "btrfs", - "ext2", - "ext3", "ext4", # `man parted` allows these - "fat16", "fat32", - "hfs", "hfs+", # "hfsx", not included in `man parted` - "linux-swap", - "ntfs", - "reiserfs", - "udf", # "ufs", not included in `man parted` - "xfs", # `man parted` allows this - ] - - -def valid_fs_type(fstype :str) -> bool: - return fstype.lower() in fs_types() diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py index 79ab024b..57f13288 100644 --- a/archinstall/lib/general.py +++ b/archinstall/lib/general.py @@ -1,4 +1,5 @@ from __future__ import annotations + import hashlib import json import logging @@ -17,7 +18,7 @@ import urllib.error import pathlib from datetime import datetime, date from typing import Callable, Optional, Dict, Any, List, Union, Iterator, TYPE_CHECKING -# https://stackoverflow.com/a/39757388/929999 + if TYPE_CHECKING: from .installer import Installer @@ -140,7 +141,7 @@ class JsonEncoder: return obj.isoformat() elif isinstance(obj, (list, set, tuple)): return [json.loads(json.dumps(item, cls=JSON)) for item in obj] - elif isinstance(obj, (pathlib.Path)): + elif isinstance(obj, pathlib.Path): return str(obj) else: return obj @@ -184,22 +185,21 @@ class UNSAFE_JSON(json.JSONEncoder, json.JSONDecoder): def encode(self, obj :Any) -> Any: return super(UNSAFE_JSON, self).encode(self._encode(obj)) + class SysCommandWorker: - def __init__(self, + def __init__( + self, cmd :Union[str, List[str]], callbacks :Optional[Dict[str, Any]] = None, peek_output :Optional[bool] = False, - peak_output :Optional[bool] = False, environment_vars :Optional[Dict[str, Any]] = None, logfile :Optional[None] = None, working_directory :Optional[str] = './', - remove_vt100_escape_codes_from_lines :bool = True): - - if peak_output: - log("SysCommandWorker()'s peak_output is deprecated, use peek_output instead.", level=logging.WARNING, fg='red') - + remove_vt100_escape_codes_from_lines :bool = True + ): if not callbacks: callbacks = {} + if not environment_vars: environment_vars = {} @@ -216,8 +216,6 @@ class SysCommandWorker: self.cmd = cmd self.callbacks = callbacks self.peek_output = peek_output - if not self.peek_output and peak_output: - self.peek_output = peak_output # define the standard locale for command outputs. For now the C ascii one. Can be overridden self.environment_vars = {**storage.get('CMD_LOCALE',{}),**environment_vars} self.logfile = logfile @@ -396,7 +394,7 @@ class SysCommandWorker: os.chmod(str(history_logfile), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) except PermissionError: pass - # If history_logfile does not exist, ignore the error + # If history_logfile does not exist, ignore the error except FileNotFoundError: pass except Exception as e: @@ -431,14 +429,10 @@ class SysCommand: callbacks :Optional[Dict[str, Callable[[Any], Any]]] = None, start_callback :Optional[Callable[[Any], Any]] = None, peek_output :Optional[bool] = False, - peak_output :Optional[bool] = False, environment_vars :Optional[Dict[str, Any]] = None, working_directory :Optional[str] = './', remove_vt100_escape_codes_from_lines :bool = True): - if peak_output: - log("SysCommandWorker()'s peak_output is deprecated, use peek_output instead.", level=logging.WARNING, fg='red') - _callbacks = {} if callbacks: for hook, func in callbacks.items(): @@ -449,8 +443,6 @@ class SysCommand: self.cmd = cmd self._callbacks = _callbacks self.peek_output = peek_output - if not self.peek_output and peak_output: - self.peek_output = peak_output self.environment_vars = environment_vars self.working_directory = working_directory self.remove_vt100_escape_codes_from_lines = remove_vt100_escape_codes_from_lines @@ -575,9 +567,8 @@ def run_custom_user_commands(commands :List[str], installation :Installer) -> No with open(f"{installation.target}/var/tmp/user-command.{index}.sh", "w") as temp_script: temp_script.write(command) - execution_output = SysCommand(f"arch-chroot {installation.target} bash /var/tmp/user-command.{index}.sh") + SysCommand(f"arch-chroot {installation.target} bash /var/tmp/user-command.{index}.sh") - log(execution_output) os.unlink(f"{installation.target}/var/tmp/user-command.{index}.sh") def json_stream_to_structure(configuration_identifier : str, stream :str, target :dict) -> bool : diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py new file mode 100644 index 00000000..bc9164ee --- /dev/null +++ b/archinstall/lib/global_menu.py @@ -0,0 +1,364 @@ +from __future__ import annotations + +from typing import Any, List, Optional, Union, Dict, TYPE_CHECKING + +from . import disk +from .general import SysCommand, secret +from .menu import Selector, AbstractMenu +from .models import NetworkConfiguration +from .models.bootloader import Bootloader +from .models.users import User +from .output import FormattedOutput +from .profile.profile_menu import ProfileConfiguration +from .storage import storage +from .user_interaction import add_number_of_parrallel_downloads +from .user_interaction import ask_additional_packages_to_install +from .user_interaction import ask_for_additional_users +from .user_interaction import ask_for_audio_selection +from .user_interaction import ask_for_bootloader +from .user_interaction import ask_for_swap +from .user_interaction import ask_hostname +from .user_interaction import ask_ntp +from .user_interaction import ask_to_configure_network +from .user_interaction import get_password, ask_for_a_timezone +from .user_interaction import select_additional_repositories +from .user_interaction import select_kernel +from .user_interaction import select_language +from .user_interaction import select_locale_enc +from .user_interaction import select_locale_lang +from .user_interaction import select_mirror_regions +from .user_interaction.disk_conf import select_disk_config +from .user_interaction.save_conf import save_config + +if TYPE_CHECKING: + _: Any + + +class GlobalMenu(AbstractMenu): + def __init__(self, data_store: Dict[str, Any]): + super().__init__(data_store=data_store, auto_cursor=True, preview_size=0.3) + + def setup_selection_menu_options(self): + # archinstall.Language will not use preset values + self._menu_options['archinstall-language'] = \ + Selector( + _('Archinstall language'), + lambda x: self._select_archinstall_language(x), + display_func=lambda x: x.display_name, + default=self.translation_handler.get_language_by_abbr('en')) + self._menu_options['keyboard-layout'] = \ + Selector( + _('Keyboard layout'), + lambda preset: select_language(preset), + default='us') + self._menu_options['mirror-region'] = \ + Selector( + _('Mirror region'), + lambda preset: select_mirror_regions(preset), + display_func=lambda x: list(x.keys()) if x else '[]', + default={}) + self._menu_options['sys-language'] = \ + Selector( + _('Locale language'), + lambda preset: select_locale_lang(preset), + default='en_US') + self._menu_options['sys-encoding'] = \ + Selector( + _('Locale encoding'), + lambda preset: select_locale_enc(preset), + default='UTF-8') + self._menu_options['disk_config'] = \ + Selector( + _('Disk configuration'), + lambda preset: self._select_disk_config(preset), + preview_func=self._prev_disk_layouts, + display_func=lambda x: self._display_disk_layout(x), + ) + self._menu_options['disk_encryption'] = \ + Selector( + _('Disk encryption'), + lambda preset: self._disk_encryption(preset), + preview_func=self._prev_disk_encryption, + display_func=lambda x: self._display_disk_encryption(x), + dependencies=['disk_config']) + self._menu_options['swap'] = \ + Selector( + _('Swap'), + lambda preset: ask_for_swap(preset), + default=True) + self._menu_options['bootloader'] = \ + Selector( + _('Bootloader'), + lambda preset: ask_for_bootloader(preset), + display_func=lambda x: x.value, + default=Bootloader.get_default()) + self._menu_options['hostname'] = \ + Selector( + _('Hostname'), + lambda preset: ask_hostname(preset), + default='archlinux') + # root password won't have preset value + self._menu_options['!root-password'] = \ + Selector( + _('Root password'), + lambda preset:self._set_root_password(), + display_func=lambda x: secret(x) if x else 'None') + self._menu_options['!users'] = \ + Selector( + _('User account'), + lambda x: self._create_user_account(x), + default={}, + display_func=lambda x: f'{len(x)} {_("User(s)")}' if len(x) > 0 else None, + preview_func=self._prev_users) + self._menu_options['profile_config'] = \ + Selector( + _('Profile'), + lambda preset: self._select_profile(preset), + display_func=lambda x: x.profile.name if x else 'None', + preview_func=self._prev_profile + ) + self._menu_options['audio'] = \ + Selector( + _('Audio'), + lambda preset: self._select_audio(preset), + display_func=lambda x: x if x else 'None', + default=None + ) + self._menu_options['parallel downloads'] = \ + Selector( + _('Parallel Downloads'), + add_number_of_parrallel_downloads, + display_func=lambda x: x if x else '0', + default=0 + ) + self._menu_options['kernels'] = \ + Selector( + _('Kernels'), + lambda preset: select_kernel(preset), + display_func=lambda x: ', '.join(x) if x else None, + default=['linux']) + self._menu_options['packages'] = \ + Selector( + _('Additional packages'), + # lambda x: ask_additional_packages_to_install(storage['arguments'].get('packages', None)), + ask_additional_packages_to_install, + default=[]) + self._menu_options['additional-repositories'] = \ + Selector( + _('Optional repositories'), + select_additional_repositories, + display_func=lambda x: ', '.join(x) if x else None, + default=[]) + self._menu_options['nic'] = \ + Selector( + _('Network configuration'), + ask_to_configure_network, + display_func=lambda x: self._display_network_conf(x), + preview_func=self._prev_network_config, + default={}) + self._menu_options['timezone'] = \ + Selector( + _('Timezone'), + lambda preset: ask_for_a_timezone(preset), + default='UTC') + self._menu_options['ntp'] = \ + Selector( + _('Automatic time sync (NTP)'), + lambda preset: self._select_ntp(preset), + default=True) + self._menu_options['__separator__'] = \ + Selector('') + self._menu_options['save_config'] = \ + Selector( + _('Save configuration'), + lambda preset: save_config(self._data_store), + no_store=True) + self._menu_options['install'] = \ + Selector( + self._install_text(), + exec_func=lambda n,v: True if len(self._missing_configs()) == 0 else False, + preview_func=self._prev_install_missing_config, + no_store=True) + + self._menu_options['abort'] = Selector(_('Abort'), exec_func=lambda n,v:exit(1)) + + def _update_install_text(self, name: str, value: str): + text = self._install_text() + self._menu_options['install'].update_description(text) + + def post_callback(self, name: str, value: str): + self._update_install_text(name, value) + + def _install_text(self): + missing = len(self._missing_configs()) + if missing > 0: + return _('Install ({} config(s) missing)').format(missing) + return _('Install') + + def _display_network_conf(self, cur_value: Union[NetworkConfiguration, List[NetworkConfiguration]]) -> str: + if not cur_value: + return _('Not configured, unavailable unless setup manually') + else: + if isinstance(cur_value, list): + return str(_('Configured {} interfaces')).format(len(cur_value)) + else: + return str(cur_value) + + def _disk_encryption(self, preset: Optional[disk.DiskEncryption]) -> Optional[disk.DiskEncryption]: + mods: Optional[List[disk.DeviceModification]] = self._menu_options['disk_config'].current_selection + + if not mods: + # this should not happen as the encryption menu has the disk_config as dependency + raise ValueError('No disk layout specified') + + data_store: Dict[str, Any] = {} + disk_encryption = disk.DiskEncryptionMenu(mods, data_store, preset=preset).run() + return disk_encryption + + def _prev_network_config(self) -> Optional[str]: + selector = self._menu_options['nic'] + if selector.has_selection(): + ifaces = selector.current_selection + if isinstance(ifaces, list): + return FormattedOutput.as_table(ifaces) + return None + + def _prev_disk_layouts(self) -> Optional[str]: + selector = self._menu_options['disk_config'] + disk_layout_conf: Optional[disk.DiskLayoutConfiguration] = selector.current_selection + + if disk_layout_conf: + device_mods: List[disk.DeviceModification] = \ + list(filter(lambda x: len(x.partitions) > 0, disk_layout_conf.device_modifications)) + + if device_mods: + output_partition = '{}: {}\n'.format(str(_('Configuration')), disk_layout_conf.config_type.display_msg()) + output_btrfs = '' + + for mod in device_mods: + # create partition table + partition_table = FormattedOutput.as_table(mod.partitions) + + output_partition += f'{mod.device_path}: {mod.device.device_info.model}\n' + output_partition += partition_table + '\n' + + # create btrfs table + btrfs_partitions = list( + filter(lambda p: len(p.btrfs_subvols) > 0, mod.partitions) + ) + for partition in btrfs_partitions: + output_btrfs += FormattedOutput.as_table(partition.btrfs_subvols) + '\n' + + output = output_partition + output_btrfs + return output.rstrip() + + return None + + def _display_disk_layout(self, current_value: Optional[disk.DiskLayoutConfiguration] = None) -> str: + if current_value: + return current_value.config_type.display_msg() + return '' + + def _prev_disk_encryption(self) -> Optional[str]: + encryption: Optional[disk.DiskEncryption] = self._menu_options['disk_encryption'].current_selection + if encryption: + enc_type = disk.EncryptionType.type_to_text(encryption.encryption_type) + output = str(_('Encryption type')) + f': {enc_type}\n' + output += str(_('Password')) + f': {secret(encryption.encryption_password)}\n' + + if encryption.partitions: + output += 'Partitions: {} selected'.format(len(encryption.partitions)) + '\n' + + if encryption.hsm_device: + output += f'HSM: {encryption.hsm_device.manufacturer}' + + return output + + return None + + def _display_disk_encryption(self, current_value: Optional[disk.DiskEncryption]) -> str: + if current_value: + return disk.EncryptionType.type_to_text(current_value.encryption_type) + return '' + + def _prev_install_missing_config(self) -> Optional[str]: + if missing := self._missing_configs(): + text = str(_('Missing configurations:\n')) + for m in missing: + text += f'- {m}\n' + return text[:-1] # remove last new line + return None + + def _prev_users(self) -> Optional[str]: + selector = self._menu_options['!users'] + users: Optional[List[User]] = selector.current_selection + + if users: + return FormattedOutput.as_table(users) + return None + + def _prev_profile(self) -> Optional[str]: + selector = self._menu_options['profile_config'] + profile_config: Optional[ProfileConfiguration] = selector.current_selection + + if profile_config and profile_config.profile: + output = str(_('Profiles')) + ': ' + if profile_names := profile_config.profile.current_selection_names(): + output += ', '.join(profile_names) + '\n' + else: + output += profile_config.profile.name + '\n' + + if profile_config.gfx_driver: + output += str(_('Graphics driver')) + ': ' + profile_config.gfx_driver + '\n' + + if profile_config.greeter: + output += str(_('Greeter')) + ': ' + profile_config.greeter.value + '\n' + + return output + + return None + + def _set_root_password(self) -> Optional[str]: + prompt = str(_('Enter root password (leave blank to disable root): ')) + password = get_password(prompt=prompt) + return password + + def _select_ntp(self, preset :bool = True) -> bool: + ntp = ask_ntp(preset) + + value = str(ntp).lower() + SysCommand(f'timedatectl set-ntp {value}') + + return ntp + + def _select_disk_config( + self, + preset: Optional[disk.DiskLayoutConfiguration] = None + ) -> Optional[disk.DiskLayoutConfiguration]: + disk_config = select_disk_config( + preset, + storage['arguments'].get('advanced', False) + ) + + if disk_config != preset: + self._menu_options['disk_encryption'].set_current_selection(None) + + return disk_config + + def _select_profile(self, current_profile: Optional[ProfileConfiguration]): + from .profile.profile_menu import ProfileMenu + store: Dict[str, Any] = {} + profile_config = ProfileMenu(store, preset=current_profile).run() + return profile_config + + def _select_audio(self, current: Union[str, None]) -> Optional[str]: + profile_config: Optional[ProfileConfiguration] = self._menu_options['profile_config'].current_selection + if profile_config and profile_config.profile: + is_desktop = profile_config.profile.is_desktop_profile() if profile_config else False + selection = ask_for_audio_selection(is_desktop, current) + return selection + return None + + def _create_user_account(self, defined_users: List[User]) -> List[User]: + users = ask_for_additional_users(defined_users=defined_users) + return users diff --git a/archinstall/lib/hsm/__init__.py b/archinstall/lib/hsm/__init__.py deleted file mode 100644 index a3f64019..00000000 --- a/archinstall/lib/hsm/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .fido import Fido2 diff --git a/archinstall/lib/hsm/fido.py b/archinstall/lib/hsm/fido.py deleted file mode 100644 index 1c226322..00000000 --- a/archinstall/lib/hsm/fido.py +++ /dev/null @@ -1,109 +0,0 @@ -from __future__ import annotations - -import getpass -import logging - -from dataclasses import dataclass -from pathlib import Path -from typing import List, Dict - -from ..general import SysCommand, SysCommandWorker, clear_vt100_escape_codes -from ..disk.partition import Partition -from ..general import log - - -@dataclass -class Fido2Device: - path: Path - manufacturer: str - product: str - - def json(self) -> Dict[str, str]: - return { - 'path': str(self.path), - 'manufacturer': self.manufacturer, - 'product': self.product - } - - @classmethod - def parse_arg(cls, arg: Dict[str, str]) -> 'Fido2Device': - return Fido2Device( - Path(arg['path']), - arg['manufacturer'], - arg['product'] - ) - - -class Fido2: - _loaded: bool = False - _fido2_devices: List[Fido2Device] = [] - - @classmethod - def get_fido2_devices(cls, reload: bool = False) -> List[Fido2Device]: - """ - Uses systemd-cryptenroll to list the FIDO2 devices - connected that supports FIDO2. - Some devices might show up in udevadm as FIDO2 compliant - when they are in fact not. - - The drawback of systemd-cryptenroll is that it uses human readable format. - That means we get this weird table like structure that is of no use. - - So we'll look for `MANUFACTURER` and `PRODUCT`, we take their index - and we split each line based on those positions. - - Output example: - - PATH MANUFACTURER PRODUCT - /dev/hidraw1 Yubico YubiKey OTP+FIDO+CCID - """ - - # to prevent continous reloading which will slow - # down moving the cursor in the menu - if not cls._loaded or reload: - ret = SysCommand(f"systemd-cryptenroll --fido2-device=list").decode('UTF-8') - if not ret: - log('Unable to retrieve fido2 devices', level=logging.ERROR) - return [] - - fido_devices = clear_vt100_escape_codes(ret) - - manufacturer_pos = 0 - product_pos = 0 - devices = [] - - for line in fido_devices.split('\r\n'): - if '/dev' not in line: - manufacturer_pos = line.find('MANUFACTURER') - product_pos = line.find('PRODUCT') - continue - - path = line[:manufacturer_pos].rstrip() - manufacturer = line[manufacturer_pos:product_pos].rstrip() - product = line[product_pos:] - - devices.append( - Fido2Device(path, manufacturer, product) - ) - - cls._loaded = True - cls._fido2_devices = devices - - return cls._fido2_devices - - @classmethod - def fido2_enroll(cls, hsm_device: Fido2Device, partition :Partition, password :str): - worker = SysCommandWorker(f"systemd-cryptenroll --fido2-device={hsm_device.path} {partition.real_device}", peek_output=True) - pw_inputted = False - pin_inputted = False - - while worker.is_alive(): - if pw_inputted is False and bytes(f"please enter current passphrase for disk {partition.real_device}", 'UTF-8') in worker._trace_log.lower(): - worker.write(bytes(password, 'UTF-8')) - pw_inputted = True - - elif pin_inputted is False and bytes(f"please enter security token pin", 'UTF-8') in worker._trace_log.lower(): - worker.write(bytes(getpass.getpass(" "), 'UTF-8')) - pin_inputted = True - - log(f"You might need to touch the FIDO2 device to unlock it if no prompt comes up after 3 seconds.", level=logging.INFO, fg="yellow") diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index b4d253b3..ddbcc2f2 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -1,30 +1,29 @@ -import time +import glob import logging import os import re -import shutil import shlex -import pathlib +import shutil import subprocess -import glob -from types import ModuleType -from typing import Union, Dict, Any, List, Optional, Iterator, Mapping, TYPE_CHECKING -from .disk import get_partitions_in_use, Partition -from .general import SysCommand, generate_password +import time +from pathlib import Path +from typing import Any, Iterator, List, Mapping, Optional, TYPE_CHECKING, Union, Dict + +from . import disk +from .exceptions import DiskError, ServiceException, RequirementError, HardwareIncompatibilityError, SysCallError +from .general import SysCommand from .hardware import has_uefi, is_vm, cpu_vendor from .locale_helpers import verify_keyboard_layout, verify_x11_keyboard_layout -from .disk.helpers import findmnt +from .luks import Luks2 from .mirrors import use_mirrors -from .models.disk_encryption import DiskEncryption +from .models.bootloader import Bootloader +from .models.network_configuration import NetworkConfiguration +from .models.users import User +from .output import log +from .pacman import run_pacman from .plugins import plugins +from .services import service_state from .storage import storage -from .output import log -from .profiles import Profile -from .disk.partition import get_mount_fs_type -from .exceptions import DiskError, ServiceException, RequirementError, HardwareIncompatibilityError, SysCallError -from .models.users import User -from .models.subvolume import Subvolume -from .hsm import Fido2 if TYPE_CHECKING: _: Any @@ -36,9 +35,6 @@ __packages__ = ["base", "base-devel", "linux-firmware", "linux", "linux-lts", "l # Additional packages that are installed if the user is running the Live ISO with accessibility tools enabled __accessibility_packages__ = ["brltty", "espeakup", "alsa-utils"] -from .pacman import run_pacman -from .models.network_configuration import NetworkConfiguration - class InstallationFile: def __init__(self, installation :'Installer', filename :str, owner :str, mode :str = "w"): @@ -92,26 +88,35 @@ class Installer: :param hostname: The given /etc/hostname for the machine. :type hostname: str, optional - """ - - def __init__(self, target :str, *, base_packages :Optional[List[str]] = None, kernels :Optional[List[str]] = None): - if base_packages is None: + def __init__( + self, + target: Path, + disk_config: disk.DiskLayoutConfiguration, + disk_encryption: Optional[disk.DiskEncryption] = None, + base_packages: List[str] = [], + kernels: Optional[List[str]] = None + ): + if not base_packages: base_packages = __packages__[:3] + if kernels is None: self.kernels = ['linux'] else: self.kernels = kernels + + self._disk_config = disk_config + self._disk_encryption = disk_encryption + + if self._disk_encryption is None: + self._disk_encryption = disk.DiskEncryption(disk.EncryptionType.NoEncryption) + self.target = target self.init_time = time.strftime('%Y-%m-%d_%H-%M-%S') self.milliseconds = int(str(time.time()).split('.')[1]) + self.helper_flags = {'base': False, 'bootloader': False} + self.base_packages = base_packages - self.helper_flags = { - 'base': False, - 'bootloader': False - } - - self.base_packages = base_packages.split(' ') if type(base_packages) is str else base_packages for kernel in self.kernels: self.base_packages.append(kernel) @@ -136,19 +141,10 @@ class Installer: self._zram_enabled = False - self._disk_encryption: DiskEncryption = storage['arguments'].get('disk_encryption') - - def log(self, *args :str, level :int = logging.DEBUG, **kwargs :str): - """ - installer.log() wraps output.log() mainly to set a default log-level for this install session. - Any manual override can be done per log() call. - """ - log(*args, level=level, **kwargs) - - def __enter__(self, *args :str, **kwargs :str) -> 'Installer': + def __enter__(self, *args: str, **kwargs: str) -> 'Installer': return self - def __exit__(self, *args :str, **kwargs :str) -> None: + def __exit__(self, *args :str, **kwargs :str) -> bool: # TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager if len(args) >= 2 and args[1]: @@ -165,7 +161,6 @@ class Installer: if not (missing_steps := self.post_install_check()): self.log('Installation completed without any errors. You may now reboot.', fg='green', level=logging.INFO) self.sync_log_to_install_medium() - return True else: self.log('Some required steps were not successfully installed/configured before leaving the installer:', fg='red', level=logging.WARNING) @@ -178,146 +173,168 @@ class Installer: self.sync_log_to_install_medium() return False - @property - def partitions(self) -> List[Partition]: - return get_partitions_in_use(self.target).values() + def log(self, *args :str, level :int = logging.DEBUG, **kwargs :str): + """ + installer.log() wraps output.log() mainly to set a default log-level for this install session. + Any manual override can be done per log() call. + """ + log(*args, level=level, **kwargs) - def sync_log_to_install_medium(self) -> bool: - # Copy over the install log (if there is one) to the install medium if - # at least the base has been strapped in, otherwise we won't have a filesystem/structure to copy to. - if self.helper_flags.get('base-strapped', False) is True: - if filename := storage.get('LOG_FILE', None): - absolute_logfile = os.path.join(storage.get('LOG_PATH', './'), filename) + def _verify_service_stop(self): + """ + Certain services might be running that affects the system during installation. + Currently, only one such service is "reflector.service" which updates /etc/pacman.d/mirrorlist + We need to wait for it before we continue since we opted in to use a custom mirror/region. + """ + log('Waiting for automatic mirror selection (reflector) to complete...', level=logging.INFO) + while service_state('reflector') not in ('dead', 'failed', 'exited'): + time.sleep(1) - if not os.path.isdir(f"{self.target}/{os.path.dirname(absolute_logfile)}"): - os.makedirs(f"{self.target}/{os.path.dirname(absolute_logfile)}") + log('Waiting pacman-init.service to complete.', level=logging.INFO) + while service_state('pacman-init') not in ('dead', 'failed', 'exited'): + time.sleep(1) - shutil.copy2(absolute_logfile, f"{self.target}/{absolute_logfile}") + log('Waiting Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete.', level=logging.INFO) + while service_state('archlinux-keyring-wkd-sync') not in ('dead', 'failed', 'exited'): + time.sleep(1) - return True + def _verify_boot_part(self): + """ + Check that mounted /boot device has at minimum size for installation + The reason this check is here is to catch pre-mounted device configuration and potentially + configured one that has not gone through any previous checks (e.g. --silence mode) - def _create_keyfile(self,luks_handle , partition :dict, password :str): - """ roiutine to create keyfiles, so it can be moved elsewhere + NOTE: this function should be run AFTER running the mount_ordered_layout function """ - if self._disk_encryption and self._disk_encryption.generate_encryption_file(partition): - if not (cryptkey_dir := pathlib.Path(f"{self.target}/etc/cryptsetup-keys.d")).exists(): - cryptkey_dir.mkdir(parents=True) - # Once we store the key as ../xyzloop.key systemd-cryptsetup can automatically load this key - # if we name the device to "xyzloop". - if partition.get('mountpoint',None): - encryption_key_path = f"/etc/cryptsetup-keys.d/{pathlib.Path(partition['mountpoint']).name}loop.key" - else: - encryption_key_path = f"/etc/cryptsetup-keys.d/{pathlib.Path(partition['device_instance'].path).name}.key" - with open(f"{self.target}{encryption_key_path}", "w") as keyfile: - keyfile.write(generate_password(length=512)) + boot_mount = self.target / 'boot' + lsblk_info = disk.get_lsblk_by_mountpoint(boot_mount) + + if len(lsblk_info) > 0: + if lsblk_info[0].size < disk.Size(200, disk.Unit.MiB): + raise DiskError( + f'The boot partition mounted at {boot_mount} is not large enough to install a boot loader. ' + f'Please resize it to at least 200MiB and re-run the installation.' + ) - os.chmod(f"{self.target}{encryption_key_path}", 0o400) + def sanity_check(self): + self._verify_boot_part() + self._verify_service_stop() - luks_handle.add_key(pathlib.Path(f"{self.target}{encryption_key_path}"), password=password) - luks_handle.crypttab(self, encryption_key_path, options=["luks", "key-slot=1"]) + def mount_ordered_layout(self): + log('Mounting partitions in order', level=logging.INFO) - def _has_root(self, partition :dict) -> bool: - """ - Determine if an encrypted partition contains root in it - """ - if partition.get("mountpoint") is None: - if (sub_list := partition.get("btrfs",{}).get('subvolumes',{})): - for mountpoint in [sub_list[subvolume].get("mountpoint") if isinstance(subvolume, dict) else subvolume.mountpoint for subvolume in sub_list]: - if mountpoint == '/': - return True - return False + for mod in self._disk_config.device_modifications: + # partitions have to mounted in the right order on btrfs the mountpoint will + # be empty as the actual subvolumes are getting mounted instead so we'll use + # '/' just for sorting + sorted_part_mods = sorted(mod.partitions, key=lambda x: x.mountpoint if x.mountpoint else Path('/')) + + if self._disk_encryption.encryption_type is not disk.EncryptionType.NoEncryption: + enc_partitions = list(filter(lambda x: x in self._disk_encryption.partitions, sorted_part_mods)) else: - return False - elif partition.get("mountpoint") == '/': - return True - else: - return False + enc_partitions = [] - def mount_ordered_layout(self, layouts: Dict[str, Any]) -> None: - from .luks import luks2 - from .disk.btrfs import setup_subvolumes, mount_subvolume - - # set the partitions as a list not part of a tree (which we don't need anymore (i think) - list_part = [] - list_luks_handles = [] - for blockdevice in layouts: - list_part.extend(layouts[blockdevice]['partitions']) - - # TODO: Implement a proper mount-queue system that does not depend on return values. - mount_queue = {} - - # we manage the encrypted partititons - if self._disk_encryption: - for partition in self._disk_encryption.all_partitions: - # open the luks device and all associate stuff - 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, self._disk_encryption.encryption_password, auto_unmount=False)) as unlocked_device: - if self._disk_encryption.generate_encryption_file(partition) and not self._has_root(partition): - list_luks_handles.append([luks_handle, partition, self._disk_encryption.encryption_password]) - # this way all the requesrs will be to the dm_crypt device and not to the physical partition - partition['device_instance'] = unlocked_device - - if self._has_root(partition) and self._disk_encryption.generate_encryption_file(partition) is False: - if self._disk_encryption.hsm_device: - Fido2.fido2_enroll(self._disk_encryption.hsm_device, partition['device_instance'], self._disk_encryption.encryption_password) - - btrfs_subvolumes = [entry for entry in list_part if entry.get('btrfs', {}).get('subvolumes', [])] - - for partition in btrfs_subvolumes: - device_instance = partition['device_instance'] - mount_options = partition.get('filesystem', {}).get('mount_options', []) - self.mount(device_instance, "/", options=','.join(mount_options)) - setup_subvolumes(installation=self, partition_dict=partition) - device_instance.unmount() - - # We then handle any special cases, such as btrfs - for partition in btrfs_subvolumes: - subvolumes: List[Subvolume] = partition['btrfs']['subvolumes'] - for subvolume in sorted(subvolumes, key=lambda item: item.mountpoint): - # We cache the mount call for later - mount_queue[subvolume.mountpoint] = lambda sub_vol=subvolume, device=partition['device_instance']: mount_subvolume( - installation=self, - device=device, - subvolume=sub_vol - ) + # attempt to decrypt all luks partitions + luks_handlers = self._prepare_luks_partitions(enc_partitions) - # We mount ordinary partitions, and we sort them by the mountpoint - for partition in sorted([entry for entry in list_part if entry.get('mountpoint', False)], key=lambda part: part['mountpoint']): - mountpoint = partition['mountpoint'] - log(f"Mounting {mountpoint} to {self.target}{mountpoint} using {partition['device_instance']}", level=logging.INFO) + for part_mod in sorted_part_mods: + if part_mod not in luks_handlers: # partition is not encrypted + self._mount_partition(part_mod) + else: # mount encrypted partition + self._mount_luks_partiton(part_mod, luks_handlers[part_mod]) - if partition.get('filesystem',{}).get('mount_options',[]): - mount_options = ','.join(partition['filesystem']['mount_options']) - mount_queue[mountpoint] = lambda instance=partition['device_instance'], target=f"{self.target}{mountpoint}", options=mount_options: instance.mount(target, options=options) - else: - mount_queue[mountpoint] = lambda instance=partition['device_instance'], target=f"{self.target}{mountpoint}": instance.mount(target) + def _prepare_luks_partitions(self, partitions: List[disk.PartitionModification]) -> Dict[disk.PartitionModification, Luks2]: + luks_handlers = {} - log(f"Using mount order: {list(sorted(mount_queue.items(), key=lambda item: item[0]))}", level=logging.DEBUG, fg="white") + for part_mod in partitions: + luks_handler = disk.device_handler.unlock_luks2_dev( + part_mod.dev_path, + part_mod.mapper_name, + self._disk_encryption.encryption_password + ) + luks_handlers[part_mod] = luks_handler + + return luks_handlers + + def _mount_partition(self, part_mod: disk.PartitionModification): + # it would be none if it's btrfs as the subvolumes will have the mountpoints defined + if part_mod.mountpoint is not None: + target = self.target / part_mod.relative_mountpoint + disk.device_handler.mount(part_mod.dev_path, target, options=part_mod.mount_options) + + if part_mod.fs_type == disk.FilesystemType.Btrfs: + self._mount_btrfs_subvol(part_mod.dev_path, part_mod.btrfs_subvols) + + def _mount_luks_partiton(self, part_mod: disk.PartitionModification, luks_handler: Luks2): + # it would be none if it's btrfs as the subvolumes will have the mountpoints defined + if part_mod.mountpoint is not None: + target = self.target / part_mod.relative_mountpoint + disk.device_handler.mount(luks_handler.mapper_dev, target, options=part_mod.mount_options) + + if part_mod.fs_type == disk.FilesystemType.Btrfs: + self._mount_btrfs_subvol(luks_handler.mapper_dev, part_mod.btrfs_subvols) + + def _mount_btrfs_subvol(self, dev_path: Path, subvolumes: List[disk.SubvolumeModification]): + for subvol in subvolumes: + mountpoint = self.target / subvol.relative_mountpoint + mount_options = subvol.mount_options + [f'subvol={subvol.name}'] + disk.device_handler.mount(dev_path, mountpoint, options=mount_options) + + def generate_key_files(self): + for part_mod in self._disk_encryption.partitions: + gen_enc_file = self._disk_encryption.should_generate_encryption_file(part_mod) + + luks_handler = Luks2( + part_mod.dev_path, + mapper_name=part_mod.mapper_name, + password=self._disk_encryption.encryption_password + ) - # We mount everything by sorting on the mountpoint itself. - for mountpoint, frozen_func in sorted(mount_queue.items(), key=lambda item: item[0]): - frozen_func() + if gen_enc_file and not part_mod.is_root(): + log(f'Creating key-file: {part_mod.dev_path}', level=logging.INFO) + luks_handler.create_keyfile(self.target) + if part_mod.is_root() and not gen_enc_file: + if self._disk_encryption.hsm_device: + disk.Fido2.fido2_enroll( + self._disk_encryption.hsm_device, + part_mod, + self._disk_encryption.encryption_password + ) + + def activate_ntp(self): + """ + If NTP is activated, confirm activiation in the ISO and at least one time-sync finishes + """ + SysCommand('timedatectl set-ntp true') + + logged = False + while service_state('dbus-org.freedesktop.timesync1.service') not in ['running']: + if not logged: + log(f"Waiting for dbus-org.freedesktop.timesync1.service to enter running state", level=logging.INFO) + logged = True time.sleep(1) - try: - findmnt(pathlib.Path(f"{self.target}{mountpoint}"), traverse=False) - except DiskError: - raise DiskError(f"Target {self.target}{mountpoint} never got mounted properly (unable to get mount information using findmnt).") + logged = False + while 'Server: n/a' in SysCommand('timedatectl timesync-status --no-pager --property=Server --value'): + if not logged: + log(f"Waiting for timedatectl timesync-status to report a timesync against a server", level=logging.INFO) + logged = True + time.sleep(1) - # once everything is mounted, we generate the key files in the correct place - for handle in list_luks_handles: - ppath = handle[1]['device_instance'].path - log(f"creating key-file for {ppath}",level=logging.INFO) - self._create_keyfile(handle[0],handle[1],handle[2]) + def sync_log_to_install_medium(self) -> bool: + # Copy over the install log (if there is one) to the install medium if + # at least the base has been strapped in, otherwise we won't have a filesystem/structure to copy to. + if self.helper_flags.get('base-strapped', False) is True: + if filename := storage.get('LOG_FILE', None): + absolute_logfile = os.path.join(storage.get('LOG_PATH', './'), filename) + + if not os.path.isdir(f"{self.target}/{os.path.dirname(absolute_logfile)}"): + os.makedirs(f"{self.target}/{os.path.dirname(absolute_logfile)}") - def mount(self, partition :Partition, mountpoint :str, create_mountpoint :bool = True, options='') -> None: - if create_mountpoint and not os.path.isdir(f'{self.target}{mountpoint}'): - os.makedirs(f'{self.target}{mountpoint}') + shutil.copy2(absolute_logfile, f"{self.target}/{absolute_logfile}") - partition.mount(f'{self.target}{mountpoint}', options=options) + return True def add_swapfile(self, size='4G', enable_resume=True, file='/swapfile'): if file[:1] != '/': @@ -394,7 +411,7 @@ class Installer: else: pacman_conf.write(line) - def pacstrap(self, *packages :str, **kwargs :str) -> bool: + def pacstrap(self, *packages: Union[str, List[str]], **kwargs :str) -> bool: if type(packages[0]) in (list, tuple): packages = packages[0] @@ -437,7 +454,7 @@ class Installer: return use_mirrors(mirrors, destination=f'{self.target}/etc/pacman.d/mirrorlist') - def genfstab(self, flags :str = '-pU') -> bool: + def genfstab(self, flags :str = '-pU'): self.log(f"Updating {self.target}/etc/fstab", level=logging.INFO) try: @@ -460,7 +477,37 @@ class Installer: for entry in self.FSTAB_ENTRIES: fstab_fh.write(f'{entry}\n') - return True + for mod in self._disk_config.device_modifications: + for part_mod in mod.partitions: + if part_mod.fs_type != disk.FilesystemType.Btrfs: + continue + + fstab_file = Path(f'{self.target}/etc/fstab') + + with fstab_file.open('r') as fp: + fstab = fp.readlines() + + # Replace the {installation}/etc/fstab with entries + # using the compress=zstd where the mountpoint has compression set. + for index, line in enumerate(fstab): + # So first we grab the mount options by using subvol=.*? as a locator. + # And we also grab the mountpoint for the entry, for instance /var/log + subvoldef = re.findall(',.*?subvol=.*?[\t ]', line) + mountpoint = re.findall('[\t ]/.*?[\t ]', line) + + if not subvoldef or not mountpoint: + continue + + for sub_vol in part_mod.btrfs_subvols: + # We then locate the correct subvolume and check if it's compressed, + # and skip entries where compression is already defined + # We then sneak in the compress=zstd option if it doesn't already exist: + if sub_vol.compress and str(sub_vol.mountpoint) == Path(mountpoint[0].strip()) and ',compress=zstd,' not in line: + fstab[index] = line.replace(subvoldef[0], f',compress=zstd{subvoldef[0]}') + break + + with fstab_file.open('w') as fp: + fp.writelines(fstab) def set_hostname(self, hostname: str, *args :str, **kwargs :str) -> None: with open(f'{self.target}/etc/hostname', 'w') as fh: @@ -509,8 +556,8 @@ class Installer: if result := plugin.on_timezone(zone): zone = result - if (pathlib.Path("/usr") / "share" / "zoneinfo" / zone).exists(): - (pathlib.Path(self.target) / "etc" / "localtime").unlink(missing_ok=True) + if (Path("/usr") / "share" / "zoneinfo" / zone).exists(): + (Path(self.target) / "etc" / "localtime").unlink(missing_ok=True) SysCommand(f'/usr/bin/arch-chroot {self.target} ln -s /usr/share/zoneinfo/{zone} /etc/localtime') return True @@ -523,10 +570,6 @@ class Installer: return False - def activate_ntp(self) -> None: - log(f"activate_ntp() is deprecated, use activate_time_syncronization()", fg="yellow", level=logging.INFO) - self.activate_time_syncronization() - def activate_time_syncronization(self) -> None: self.log('Activating systemd-timesyncd for time synchronization using Arch Linux and ntp.org NTP servers.', level=logging.INFO) self.enable_service('systemd-timesyncd') @@ -540,7 +583,10 @@ class Installer: # fstrim is owned by util-linux, a dependency of both base and systemd. self.enable_service("fstrim.timer") - def enable_service(self, *services :str) -> None: + def enable_service(self, *services: Union[str, List[str]]) -> None: + if type(services[0]) in (list, tuple): + services = services[0] + for service in services: self.log(f'Enabling service {service}', level=logging.INFO) try: @@ -552,10 +598,10 @@ class Installer: if hasattr(plugin, 'on_service'): plugin.on_service(service) - def run_command(self, cmd :str, *args :str, **kwargs :str) -> None: + def run_command(self, cmd :str, *args :str, **kwargs :str) -> SysCommand: return SysCommand(f'/usr/bin/arch-chroot {self.target} {cmd}') - def arch_chroot(self, cmd :str, run_as :Optional[str] = None): + def arch_chroot(self, cmd :str, run_as :Optional[str] = None) -> SysCommand: if run_as: cmd = f"su - {run_as} -c {shlex.quote(cmd)}" @@ -645,21 +691,6 @@ class Installer: return True - def detect_encryption(self, partition :Partition) -> bool: - from .disk.mapperdev import MapperDev - from .disk.dmcryptdev import DMCryptDev - from .disk.helpers import get_filesystem_type - - if type(partition) is MapperDev: - # Returns MapperDev.partition - return partition.partition - elif type(partition) is DMCryptDev: - return partition.MapperDev.partition - elif get_filesystem_type(partition.path) == 'crypto_LUKS': - return partition - - return False - def mkinitcpio(self, *flags :str) -> bool: for plugin in plugins.values(): if hasattr(plugin, 'on_mkinitcpio'): @@ -668,7 +699,7 @@ class Installer: return True # mkinitcpio will error out if there's no vconsole. - if (vconsole := pathlib.Path(f"{self.target}/etc/vconsole.conf")).exists() is False: + if (vconsole := Path(f"{self.target}/etc/vconsole.conf")).exists() is False: with vconsole.open('w') as fh: fh.write(f"KEYMAP={storage['arguments']['keyboard-layout']}\n") @@ -677,7 +708,7 @@ class Installer: mkinit.write(f"BINARIES=({' '.join(self.BINARIES)})\n") mkinit.write(f"FILES=({' '.join(self.FILES)})\n") - if self._disk_encryption and not self._disk_encryption.hsm_device: + if not self._disk_encryption.hsm_device: # For now, if we don't use HSM we revert to the old # way of setting up encryption hooks for mkinitcpio. # This is purely for stability reasons, we're going away from this. @@ -694,46 +725,36 @@ class Installer: return False def minimal_installation( - self, testing: bool = False, multilib: bool = False, - hostname: str = 'archinstall', locales: List[str] = ['en_US.UTF-8 UTF-8']) -> bool: - # Add necessary packages if encrypting the drive - # (encrypted partitions default to btrfs for now, so we need btrfs-progs) - # TODO: Perhaps this should be living in the function which dictates - # the partitioning. Leaving here for now. - - for partition in self.partitions: - if partition.filesystem == 'btrfs': - # if partition.encrypted: - if 'btrfs-progs' not in self.base_packages: - self.base_packages.append('btrfs-progs') - if partition.filesystem == 'xfs': - if 'xfs' not in self.base_packages: - self.base_packages.append('xfsprogs') - if partition.filesystem == 'f2fs': - if 'f2fs' not in self.base_packages: - self.base_packages.append('f2fs-tools') - - # Configure mkinitcpio to handle some specific use cases. - if partition.filesystem == 'btrfs': - if 'btrfs' not in self.MODULES: - self.MODULES.append('btrfs') - if '/usr/bin/btrfs' not in self.BINARIES: - self.BINARIES.append('/usr/bin/btrfs') - # There is not yet an fsck tool for NTFS. If it's being used for the root filesystem, the hook should be removed. - if partition.filesystem == 'ntfs3' and partition.mountpoint == self.target: - if 'fsck' in self.HOOKS: - self.HOOKS.remove('fsck') - - if self.detect_encryption(partition): - if self._disk_encryption and self._disk_encryption.hsm_device: - # Required bby mkinitcpio to add support for fido2-device options - self.pacstrap('libfido2') - - if 'sd-encrypt' not in self.HOOKS: - self.HOOKS.insert(self.HOOKS.index('filesystems'), 'sd-encrypt') - else: - if 'encrypt' not in self.HOOKS: - self.HOOKS.insert(self.HOOKS.index('filesystems'), 'encrypt') + self, + testing: bool = False, + multilib: bool = False, + hostname: str = 'archinstall', + locales: List[str] = ['en_US.UTF-8 UTF-8'] + ): + for mod in self._disk_config.device_modifications: + for part in mod.partitions: + if (pkg := part.fs_type.installation_pkg) is not None: + self.base_packages.append(pkg) + if (module := part.fs_type.installation_module) is not None: + self.MODULES.append(module) + if (binary := part.fs_type.installation_binary) is not None: + self.BINARIES.append(binary) + + # There is not yet an fsck tool for NTFS. If it's being used for the root filesystem, the hook should be removed. + if part.fs_type.fs_type_mount == 'ntfs3' and part.mountpoint == self.target: + if 'fsck' in self.HOOKS: + self.HOOKS.remove('fsck') + + if part in self._disk_encryption.partitions: + if self._disk_encryption.hsm_device: + # Required bby mkinitcpio to add support for fido2-device options + self.pacstrap('libfido2') + + if 'sd-encrypt' not in self.HOOKS: + self.HOOKS.insert(self.HOOKS.index('filesystems'), 'sd-encrypt') + else: + if 'encrypt' not in self.HOOKS: + self.HOOKS.insert(self.HOOKS.index('filesystems'), 'encrypt') if not has_uefi(): self.base_packages.append('grub') @@ -742,11 +763,11 @@ class Installer: vendor = cpu_vendor() if vendor == "AuthenticAMD": self.base_packages.append("amd-ucode") - if (ucode := pathlib.Path(f"{self.target}/boot/amd-ucode.img")).exists(): + if (ucode := Path(f"{self.target}/boot/amd-ucode.img")).exists(): ucode.unlink() elif vendor == "GenuineIntel": self.base_packages.append("intel-ucode") - if (ucode := pathlib.Path(f"{self.target}/boot/intel-ucode.img")).exists(): + if (ucode := Path(f"{self.target}/boot/intel-ucode.img")).exists(): ucode.unlink() else: self.log(f"Unknown CPU vendor '{vendor}' detected. Archinstall won't install any ucode.", level=logging.DEBUG) @@ -802,9 +823,7 @@ class Installer: if hasattr(plugin, 'on_install'): plugin.on_install(self) - return True - - def setup_swap(self, kind :str = 'zram') -> bool: + def setup_swap(self, kind :str = 'zram'): if kind == 'zram': self.log(f"Setting up swap on zram") self.pacstrap('zram-generator') @@ -818,16 +837,27 @@ class Installer: self.enable_service('systemd-zram-setup@zram0.service') self._zram_enabled = True - - return True else: raise ValueError(f"Archinstall currently only supports setting up swap on zram") - def add_systemd_bootloader(self, boot_partition :Partition, root_partition :Partition) -> bool: + def _get_boot_partition(self) -> Optional[disk.PartitionModification]: + for layout in self._disk_config.device_modifications: + if boot := layout.get_boot_partition(): + return boot + return None + + def _get_root_partition(self) -> Optional[disk.PartitionModification]: + for mod in self._disk_config.device_modifications: + if root := mod.get_root_partition(self._disk_config.relative_mountpoint): + return root + return None + + def _add_systemd_bootloader(self, root_partition: disk.PartitionModification): self.pacstrap('efibootmgr') if not has_uefi(): raise HardwareIncompatibilityError + # TODO: Ideally we would want to check if another config # points towards the same disk and/or partition. # And in which case we should do some clean up. @@ -882,74 +912,73 @@ class Installer: elif vendor == "GenuineIntel": entry.write("initrd /intel-ucode.img\n") else: - self.log(f"Unknown CPU vendor '{vendor}' detected. Archinstall won't add any ucode to systemd-boot config.", level=logging.DEBUG) + self.log( + f"Unknown CPU vendor '{vendor}' detected. Archinstall won't add any ucode to systemd-boot config.", + level=logging.DEBUG) entry.write(f"initrd /initramfs-{kernel}{variant}.img\n") # blkid doesn't trigger on loopback devices really well, # so we'll use the old manual method until we get that sorted out. - root_fs_type = get_mount_fs_type(root_partition.filesystem) - if root_fs_type is not None: - options_entry = f'rw rootfstype={root_fs_type} {" ".join(self.KERNEL_PARAMS)}\n' - else: - options_entry = f'rw {" ".join(self.KERNEL_PARAMS)}\n' + options_entry = f'rw rootfstype={root_partition.fs_type.fs_type_mount} {" ".join(self.KERNEL_PARAMS)}\n' - for subvolume in root_partition.subvolumes: - if subvolume.root is True and subvolume.name != '': - options_entry = f"rootflags=subvol={subvolume.name} " + options_entry + for sub_vol in root_partition.btrfs_subvols: + if sub_vol.is_root(): + options_entry = f"rootflags=subvol={sub_vol.name} " + options_entry # Zswap should be disabled when using zram. - # # https://github.com/archlinux/archinstall/issues/881 if self._zram_enabled: options_entry = "zswap.enabled=0 " + options_entry - if real_device := self.detect_encryption(root_partition): + if root_partition.fs_type.is_crypto(): # TODO: We need to detect if the encrypted device is a whole disk encryption, # or simply a partition encryption. Right now we assume it's a partition (and we always have) - log(f"Identifying root partition by PART-UUID on {real_device}: '{real_device.uuid}/{real_device.part_uuid}'.", level=logging.DEBUG) + log('Root partition is an encrypted device, identifying by PARTUUID: {root_partition.partuuid}', level=logging.DEBUG) kernel_options = f"options" if self._disk_encryption and self._disk_encryption.hsm_device: # Note: lsblk UUID must be used, not PARTUUID for sd-encrypt to work - kernel_options += f" rd.luks.name={real_device.uuid}=luksdev" + kernel_options += f' rd.luks.name={root_partition.uuid}=luksdev' # Note: tpm2-device and fido2-device don't play along very well: # https://github.com/archlinux/archinstall/pull/1196#issuecomment-1129715645 - kernel_options += f" rd.luks.options=fido2-device=auto,password-echo=no" + kernel_options += f' rd.luks.options=fido2-device=auto,password-echo=no' else: - kernel_options += f" cryptdevice=PARTUUID={real_device.part_uuid}:luksdev" + kernel_options += f' cryptdevice=PARTUUID={root_partition.partuuid}:luksdev' entry.write(f'{kernel_options} root=/dev/mapper/luksdev {options_entry}') else: - log(f"Identifying root partition by PARTUUID on {root_partition}, looking for '{root_partition.part_uuid}'.", level=logging.DEBUG) - entry.write(f'options root=PARTUUID={root_partition.part_uuid} {options_entry}') + log(f'Identifying root partition by PARTUUID: {root_partition.partuuid}', level=logging.DEBUG) + entry.write(f'options root=PARTUUID={root_partition.partuuid} {options_entry}') - self.helper_flags['bootloader'] = "systemd" - - return True + self.helper_flags['bootloader'] = 'systemd' - def add_grub_bootloader(self, boot_partition :Partition, root_partition :Partition) -> bool: + def _add_grub_bootloader( + self, + boot_partition: disk.PartitionModification, + root_partition: disk.PartitionModification + ): self.pacstrap('grub') # no need? - root_fs_type = get_mount_fs_type(root_partition.filesystem) + _file = "/etc/default/grub" - if real_device := self.detect_encryption(root_partition): - root_uuid = SysCommand(f"blkid -s UUID -o value {real_device.path}").decode().rstrip() - _file = "/etc/default/grub" - add_to_CMDLINE_LINUX = f"sed -i 's/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"cryptdevice=UUID={root_uuid}:cryptlvm rootfstype={root_fs_type}\"/'" - enable_CRYPTODISK = "sed -i 's/#GRUB_ENABLE_CRYPTODISK=y/GRUB_ENABLE_CRYPTODISK=y/'" + if root_partition.fs_type.is_crypto(): + log(f"Using UUID {root_partition.uuid} as encrypted root identifier", level=logging.DEBUG) - log(f"Using UUID {root_uuid} of {real_device} as encrypted root identifier.", level=logging.INFO) - SysCommand(f"/usr/bin/arch-chroot {self.target} {add_to_CMDLINE_LINUX} {_file}") - SysCommand(f"/usr/bin/arch-chroot {self.target} {enable_CRYPTODISK} {_file}") + cmd_line_linux = f"sed -i 's/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"cryptdevice=UUID={root_partition.uuid}:cryptlvm rootfstype={root_partition.fs_type.value}\"/'" + enable_cryptdisk = "sed -i 's/#GRUB_ENABLE_CRYPTODISK=y/GRUB_ENABLE_CRYPTODISK=y/'" + + SysCommand(f"/usr/bin/arch-chroot {self.target} {enable_cryptdisk} {_file}") else: - _file = "/etc/default/grub" - add_to_CMDLINE_LINUX = f"sed -i 's/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"rootfstype={root_fs_type}\"/'" - SysCommand(f"/usr/bin/arch-chroot {self.target} {add_to_CMDLINE_LINUX} {_file}") + cmd_line_linux = f"sed -i 's/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"rootfstype={root_partition.fs_type.value}\"/'" + + SysCommand(f"/usr/bin/arch-chroot {self.target} {cmd_line_linux} {_file}") + + log(f"GRUB boot partition: {boot_partition.dev_path}", level=logging.INFO) - log(f"GRUB uses {boot_partition.path} as the boot partition.", level=logging.INFO) if has_uefi(): self.pacstrap('efibootmgr') # TODO: Do we need? Yes, but remove from minimal_installation() instead? + try: SysCommand(f'/usr/bin/arch-chroot {self.target} grub-install --debug --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB --removable', peek_output=True) except SysCallError: @@ -961,7 +990,7 @@ class Installer: try: SysCommand(f'/usr/bin/arch-chroot {self.target} grub-install --debug --target=i386-pc --recheck {boot_partition.parent}', peek_output=True) except SysCallError as error: - raise DiskError(f"Could not install GRUB to {boot_partition.path}: {error}") + raise DiskError(f"Failed to install GRUB boot on {boot_partition.dev_path}: {error}") try: SysCommand(f'/usr/bin/arch-chroot {self.target} grub-mkconfig -o /boot/grub/grub.cfg') @@ -970,22 +999,22 @@ class Installer: self.helper_flags['bootloader'] = "grub" - return True - - def add_efistub_bootloader(self, boot_partition :Partition, root_partition :Partition) -> bool: + def _add_efistub_bootloader( + self, + boot_partition: disk.PartitionModification, + root_partition: disk.PartitionModification + ): self.pacstrap('efibootmgr') if not has_uefi(): raise HardwareIncompatibilityError + # TODO: Ideally we would want to check if another config # points towards the same disk and/or partition. # And in which case we should do some clean up. - root_fs_type = get_mount_fs_type(root_partition.filesystem) - for kernel in self.kernels: # Setup the firmware entry - label = f'Arch Linux ({kernel})' loader = f"/vmlinuz-{kernel}" @@ -1004,22 +1033,22 @@ class Installer: # blkid doesn't trigger on loopback devices really well, # so we'll use the old manual method until we get that sorted out. - if real_device := self.detect_encryption(root_partition): + + if root_partition.fs_type.is_crypto(): # TODO: We need to detect if the encrypted device is a whole disk encryption, # or simply a partition encryption. Right now we assume it's a partition (and we always have) - log(f"Identifying root partition by PART-UUID on {real_device}: '{real_device.part_uuid}'.", level=logging.DEBUG) - kernel_parameters.append(f'cryptdevice=PARTUUID={real_device.part_uuid}:luksdev root=/dev/mapper/luksdev rw rootfstype={root_fs_type} {" ".join(self.KERNEL_PARAMS)}') + log(f'Identifying root partition by PARTUUID: {root_partition.partuuid}', level=logging.DEBUG) + kernel_parameters.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:luksdev root=/dev/mapper/luksdev rw rootfstype={root_partition.fs_type.value} {" ".join(self.KERNEL_PARAMS)}') else: - log(f"Identifying root partition by PART-UUID on {root_partition}, looking for '{root_partition.part_uuid}'.", level=logging.DEBUG) - kernel_parameters.append(f'root=PARTUUID={root_partition.part_uuid} rw rootfstype={root_fs_type} {" ".join(self.KERNEL_PARAMS)}') + log(f'Root partition is an encrypted device identifying by PARTUUID: {root_partition.partuuid}', level=logging.DEBUG) + kernel_parameters.append(f'root=PARTUUID={root_partition.partuuid} rw rootfstype={root_partition.fs_type.value} {" ".join(self.KERNEL_PARAMS)}') - SysCommand(f'efibootmgr --disk {boot_partition.path[:-1]} --part {boot_partition.path[-1]} --create --label "{label}" --loader {loader} --unicode \'{" ".join(kernel_parameters)}\' --verbose') + device = disk.device_handler.get_device_by_partition_path(boot_partition.dev_path) + SysCommand(f'efibootmgr --disk {device.path} --part {device.path} --create --label "{label}" --loader {loader} --unicode \'{" ".join(kernel_parameters)}\' --verbose') self.helper_flags['bootloader'] = "efistub" - return True - - def add_bootloader(self, bootloader :str = 'systemd-bootctl') -> bool: + def add_bootloader(self, bootloader: Bootloader) -> bool: """ Adds a bootloader to the installation instance. Archinstall supports one of three types: @@ -1039,52 +1068,33 @@ class Installer: return True if type(self.target) == str: - self.target = pathlib.Path(self.target) - - boot_partition = None - root_partition = None - for partition in self.partitions: - if self.target / 'boot' in partition.mountpoints: - boot_partition = partition - elif self.target in partition.mountpoints: - root_partition = partition - - if boot_partition is None or root_partition is None: - raise ValueError(f"Could not detect root ({root_partition}) or boot ({boot_partition}) in {self.target} based on: {self.partitions}") - - self.log(f'Adding bootloader {bootloader} to {boot_partition if boot_partition else root_partition}', level=logging.INFO) - - if bootloader == 'systemd-bootctl': - self.add_systemd_bootloader(boot_partition, root_partition) - elif bootloader == "grub-install": - self.add_grub_bootloader(boot_partition, root_partition) - elif bootloader == 'efistub': - self.add_efistub_bootloader(boot_partition, root_partition) - else: - raise RequirementError(f"Unknown (or not yet implemented) bootloader requested: {bootloader}") + self.target = Path(self.target) - return True + boot_partition = self._get_boot_partition() + root_partition = self._get_root_partition() - def add_additional_packages(self, *packages :str) -> bool: - return self.pacstrap(*packages) + if boot_partition is None: + raise ValueError(f'Could not detect boot at mountpoint {self.target}') - def install_profile(self, profile :str) -> ModuleType: - """ - Installs a archinstall profile script (.py file). - This profile can be either local, remote or part of the library. + if root_partition is None: + raise ValueError(f'Could not detect root at mountpoint {self.target}') - :param profile: Can be a local path or a remote path (URL) - :return: Returns the imported script as a module, this way - you can access any remaining functions exposed by the profile. - :rtype: module - """ - storage['installation_session'] = self + self.log(f'Adding bootloader {bootloader.value} to {boot_partition.dev_path}', level=logging.INFO) + + match bootloader: + case Bootloader.Systemd: + self._add_systemd_bootloader(root_partition) + case Bootloader.Grub: + self._add_grub_bootloader(boot_partition, root_partition) + case Bootloader.Efistub: + self._add_efistub_bootloader(boot_partition, root_partition) - if type(profile) == str: - profile = Profile(self, profile) + def add_additional_packages(self, *packages: Union[str, List[str]]) -> bool: + return self.pacstrap(*packages) - self.log(f'Installing archinstall profile {profile}', level=logging.INFO) - return profile.install() + def _enable_users(self, service: str, users: List[User]): + for user in users: + self.arch_chroot(f'systemctl enable --user {service}', run_as=user.username) def enable_sudo(self, entity: str, group :bool = False): self.log(f'Enabling sudo permissions for {entity}.', level=logging.INFO) @@ -1092,7 +1102,7 @@ class Installer: sudoers_dir = f"{self.target}/etc/sudoers.d" # Creates directory if not exists - if not (sudoers_path := pathlib.Path(sudoers_dir)).exists(): + if not (sudoers_path := Path(sudoers_dir)).exists(): sudoers_path.mkdir(parents=True) # Guarantees sudoer confs directory recommended perms os.chmod(sudoers_dir, 0o440) @@ -1114,7 +1124,7 @@ class Installer: sudoers.write(f'{"%" if group else ""}{entity} ALL=(ALL) ALL\n') # Guarantees sudoer conf file recommended perms - os.chmod(pathlib.Path(rule_file_name), 0o440) + os.chmod(Path(rule_file_name), 0o440) def create_users(self, users: Union[User, List[User]]): if not isinstance(users, list): diff --git a/archinstall/lib/locale_helpers.py b/archinstall/lib/locale_helpers.py index 5580fa91..d1fb4562 100644 --- a/archinstall/lib/locale_helpers.py +++ b/archinstall/lib/locale_helpers.py @@ -1,11 +1,12 @@ import logging -from typing import Iterator, List, Callable +from typing import Iterator, List, Callable, Optional from .exceptions import ServiceException from .general import SysCommand from .output import log from .storage import storage + def list_keyboard_languages() -> Iterator[str]: for line in SysCommand("localectl --no-pager list-keymaps", environment_vars={'SYSTEMD_COLORS': '0'}): yield line.decode('UTF-8').strip() @@ -45,20 +46,25 @@ def get_locale_mode_text(mode): mode_text = "Unassigned" return mode_text + def reset_cmd_locale(): """ sets the cmd_locale to its saved default """ storage['CMD_LOCALE'] = storage.get('CMD_LOCALE_DEFAULT',{}) + def unset_cmd_locale(): """ archinstall will use the execution environment default """ storage['CMD_LOCALE'] = {} -def set_cmd_locale(general :str = None, - charset :str = 'C', - numbers :str = 'C', - time :str = 'C', - collate :str = 'C', - messages :str = 'C'): + +def set_cmd_locale( + general: Optional[str] = None, + charset :str = 'C', + numbers :str = 'C', + time :str = 'C', + collate :str = 'C', + messages :str = 'C' +): """ Set the cmd locale. If the parameter general is specified, it takes precedence over the rest (might as well not exist) diff --git a/archinstall/lib/luks.py b/archinstall/lib/luks.py index ad6bf093..fc531a06 100644 --- a/archinstall/lib/luks.py +++ b/archinstall/lib/luks.py @@ -1,92 +1,78 @@ from __future__ import annotations -import json + import logging -import os -import pathlib import shlex import time -from typing import Optional, List,TYPE_CHECKING -# https://stackoverflow.com/a/39757388/929999 -if TYPE_CHECKING: - from .installer import Installer +from dataclasses import dataclass +from pathlib import Path +from typing import Optional, List -from .disk import Partition, convert_device_to_uuid -from .general import SysCommand, SysCommandWorker +from . import disk +from .general import SysCommand, generate_password, SysCommandWorker from .output import log from .exceptions import SysCallError, DiskError from .storage import storage -from .disk.helpers import get_filesystem_type -from .disk.mapperdev import MapperDev -from .disk.btrfs import BTRFSPartition - - -class luks2: - def __init__(self, - partition: Partition, - mountpoint: Optional[str], - password: Optional[str], - key_file :Optional[str] = None, - auto_unmount :bool = False, - *args :str, - **kwargs :str): - - self.password = password - self.partition = partition - self.mountpoint = mountpoint - self.args = args - self.kwargs = kwargs - self.key_file = key_file - self.auto_unmount = auto_unmount - self.filesystem = 'crypto_LUKS' - self.mapdev = None - - def __enter__(self) -> Partition: - if not self.key_file: - self.key_file = f"/tmp/{os.path.basename(self.partition.path)}.disk_pw" # TODO: Make disk-pw-file randomly unique? - - if type(self.password) != bytes: - self.password = bytes(self.password, 'UTF-8') - - with open(self.key_file, 'wb') as fh: - fh.write(self.password) - - return self.unlock(self.partition, self.mountpoint, self.key_file) - - def __exit__(self, *args :str, **kwargs :str) -> bool: - # TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager + + +@dataclass +class Luks2: + luks_dev_path: Path + mapper_name: Optional[str] = None + password: Optional[str] = None + key_file: Optional[Path] = None + auto_unmount: bool = False + + # will be set internally after unlocking the device + _mapper_dev: Optional[Path] = None + + @property + def mapper_dev(self) -> Optional[Path]: + if self.mapper_name: + return Path(f'/dev/mapper/{self.mapper_name}') + return None + + def __post_init__(self): + if self.luks_dev_path is None: + raise ValueError('Partition must have a path set') + + def __enter__(self): + self.unlock(self.key_file) + + def __exit__(self, *args: str, **kwargs: str): if self.auto_unmount: - self.close() + self.lock() + + def _default_key_file(self) -> Path: + return Path(f'/tmp/{self.luks_dev_path.name}.disk_pw') - if len(args) >= 2 and args[1]: - raise args[1] + def _password_bytes(self) -> bytes: + if not self.password: + raise ValueError('Password for luks2 device was not specified') - return True + if isinstance(self.password, bytes): + return self.password + else: + return bytes(self.password, 'UTF-8') - def encrypt(self, partition :Partition, - password :Optional[str] = None, - key_size :int = 512, - hash_type :str = 'sha512', - iter_time :int = 10000, - key_file :Optional[str] = None) -> str: + def encrypt( + self, + key_size: int = 512, + hash_type: str = 'sha512', + iter_time: int = 10000, + key_file: Optional[Path] = None + ) -> Path: + log(f'Luks2 encrypting: {self.luks_dev_path}', level=logging.INFO) - log(f'Encrypting {partition} (This might take a while)', level=logging.INFO) + byte_password = self._password_bytes() if not key_file: if self.key_file: key_file = self.key_file else: - key_file = f"/tmp/{os.path.basename(self.partition.path)}.disk_pw" # TODO: Make disk-pw-file randomly unique? - - if not password: - password = self.password - - if type(password) != bytes: - password = bytes(password, 'UTF-8') + key_file = self._default_key_file() - with open(key_file, 'wb') as fh: - fh.write(password) - - partition.partprobe() + with open(key_file, 'wb') as fh: + fh.write(byte_password) cryptsetup_args = shlex.join([ '/usr/bin/cryptsetup', @@ -97,120 +83,163 @@ class luks2: '--hash', hash_type, '--key-size', str(key_size), '--iter-time', str(iter_time), - '--key-file', os.path.abspath(key_file), + '--key-file', str(key_file), '--use-urandom', - 'luksFormat', partition.path, + 'luksFormat', str(self.luks_dev_path), ]) try: # Retry formatting the volume because archinstall can some times be too quick # which generates a "Device /dev/sdX does not exist or access denied." between # setting up partitions and us trying to encrypt it. + cmd_handle = None for i in range(storage['DISK_RETRY_ATTEMPTS']): if (cmd_handle := SysCommand(cryptsetup_args)).exit_code != 0: time.sleep(storage['DISK_TIMEOUTS']) else: break - if cmd_handle.exit_code != 0: - raise DiskError(f'Could not encrypt volume "{partition.path}": {b"".join(cmd_handle)}') + if cmd_handle is not None and cmd_handle.exit_code != 0: + output = str(b''.join(cmd_handle)) + raise DiskError(f'Could not encrypt volume "{self.luks_dev_path}": {output}') except SysCallError as err: if err.exit_code == 1: - log(f'{partition} is being used, trying to unmount and crypt-close the device and running one more attempt at encrypting the device.', level=logging.DEBUG) - # Partition was in use, unmount it and try again - partition.unmount() - - # Get crypt-information about the device by doing a reverse lookup starting with the partition path - # For instance: /dev/sda - SysCommand(f'bash -c "partprobe"') - devinfo = json.loads(b''.join(SysCommand(f"lsblk --fs -J {partition.path}")).decode('UTF-8'))['blockdevices'][0] - - # For each child (sub-partition/sub-device) - if len(children := devinfo.get('children', [])): - for child in children: - # Unmount the child location - if child_mountpoint := child.get('mountpoint', None): - log(f'Unmounting {child_mountpoint}', level=logging.DEBUG) - SysCommand(f"umount -R {child_mountpoint}") - - # And close it if possible. - log(f"Closing crypt device {child['name']}", level=logging.DEBUG) - SysCommand(f"cryptsetup close {child['name']}") + log(f'luks2 partition currently in use: {self.luks_dev_path}') + log('Attempting to unmount, crypt-close and trying encryption again') + self.lock() # Then try again to set up the crypt-device - cmd_handle = SysCommand(cryptsetup_args) + SysCommand(cryptsetup_args) else: raise err return key_file - def unlock(self, partition :Partition, mountpoint :str, key_file :str) -> Partition: + def _get_luks_uuid(self) -> str: + command = f'/usr/bin/cryptsetup luksUUID {self.luks_dev_path}' + + try: + result = SysCommand(command) + if result.exit_code != 0: + raise DiskError(f'Unable to get UUID for Luks device: {result.decode()}') + + return result.decode() # type: ignore + except SysCallError as err: + log(f'Unable to get UUID for Luks device: {self.luks_dev_path}', level=logging.INFO) + raise err + + def is_unlocked(self) -> bool: + return self.mapper_name is not None and Path(f'/dev/mapper/{self.mapper_name}').exists() + + def unlock(self, key_file: Optional[Path] = None): """ - Mounts a luks2 compatible partition to a certain mountpoint. - Keyfile must be specified as there's no way to interact with the pw-prompt atm. + Unlocks the luks device, an optional key file location for unlocking can be specified, + otherwise a default location for the key file will be used. - :param mountpoint: The name without absolute path, for instance "luksdev" will point to /dev/mapper/luksdev - :type mountpoint: str + :param key_file: An alternative key file + :type key_file: Path """ + log(f'Unlocking luks2 device: {self.luks_dev_path}', level=logging.DEBUG) + + if not self.mapper_name: + raise ValueError('mapper name missing') + + byte_password = self._password_bytes() + + if not key_file: + if self.key_file: + key_file = self.key_file + else: + key_file = self._default_key_file() - if '/' in mountpoint: - os.path.basename(mountpoint) # TODO: Raise exception instead? + with open(key_file, 'wb') as fh: + fh.write(byte_password) wait_timer = time.time() - while pathlib.Path(partition.path).exists() is False and time.time() - wait_timer < 10: + while Path(self.luks_dev_path).exists() is False and time.time() - wait_timer < 10: time.sleep(0.025) - SysCommand(f'/usr/bin/cryptsetup open {partition.path} {mountpoint} --key-file {os.path.abspath(key_file)} --type luks2') - if os.path.islink(f'/dev/mapper/{mountpoint}'): - self.mapdev = f'/dev/mapper/{mountpoint}' - - if (filesystem_type := get_filesystem_type(pathlib.Path(self.mapdev))) == 'btrfs': - return BTRFSPartition( - self.mapdev, - block_device=MapperDev(mountpoint).partition.block_device, - encrypted=True, - filesystem=filesystem_type, - autodetect_filesystem=False - ) - - return Partition( - self.mapdev, - block_device=MapperDev(mountpoint).partition.block_device, - encrypted=True, - filesystem=get_filesystem_type(self.mapdev), - autodetect_filesystem=False - ) - - def close(self, mountpoint :Optional[str] = None) -> bool: - if not mountpoint: - mountpoint = self.mapdev - - SysCommand(f'/usr/bin/cryptsetup close {self.mapdev}') - return os.path.islink(self.mapdev) is False - - def format(self, path :str) -> None: - if (handle := SysCommand(f"/usr/bin/cryptsetup -q -v luksErase {path}")).exit_code != 0: - raise DiskError(f'Could not format {path} with {self.filesystem} because: {b"".join(handle)}') - - def add_key(self, path :pathlib.Path, password :str) -> bool: - if not path.exists(): - raise OSError(2, f"Could not import {path} as a disk encryption key, file is missing.", str(path)) - - log(f'Adding additional key-file {path} for {self.partition}', level=logging.INFO) - worker = SysCommandWorker(f"/usr/bin/cryptsetup -q -v luksAddKey {self.partition.path} {path}", - environment_vars={'LC_ALL':'C'}) + SysCommand(f'/usr/bin/cryptsetup open {self.luks_dev_path} {self.mapper_name} --key-file {key_file} --type luks2') + + if not self.mapper_dev or not self.mapper_dev.is_symlink(): + raise DiskError(f'Failed to open luks2 device: {self.luks_dev_path}') + + def lock(self): + disk.device_handler.umount(self.luks_dev_path) + + # Get crypt-information about the device by doing a reverse lookup starting with the partition path + # For instance: /dev/sda + disk.device_handler.partprobe(self.luks_dev_path) + lsblk_info = disk.get_lsblk_info(self.luks_dev_path) + + # For each child (sub-partition/sub-device) + for child in lsblk_info.children: + # Unmount the child location + for mountpoint in child.mountpoints: + log(f'Unmounting {mountpoint}', level=logging.DEBUG) + disk.device_handler.umount(mountpoint, recursive=True) + + # And close it if possible. + log(f"Closing crypt device {child.name}", level=logging.DEBUG) + SysCommand(f"cryptsetup close {child.name}") + + self._mapper_dev = None + + def create_keyfile(self, target_path: Path, override: bool = False): + """ + Routine to create keyfiles, so it can be moved elsewhere + """ + if self.mapper_name is None: + raise ValueError('Mapper name must be provided') + + # Once we store the key as ../xyzloop.key systemd-cryptsetup can + # automatically load this key if we name the device to "xyzloop" + key_file_path = target_path / 'etc/cryptsetup-keys.d/' / self.mapper_name + key_file = key_file_path / '.key' + crypttab_path = target_path / 'etc/crypttab' + + if key_file.exists(): + if not override: + log(f'Key file {key_file} already exists, keeping existing') + return + else: + log(f'Key file {key_file} already exists, overriding') + + key_file_path.mkdir(parents=True, exist_ok=True) + + with open(key_file, "w") as keyfile: + keyfile.write(generate_password(length=512)) + + key_file_path.chmod(0o400) + + self._add_key(key_file) + self._crypttab(crypttab_path, key_file, options=["luks", "key-slot=1"]) + + def _add_key(self, key_file: Path): + log(f'Adding additional key-file {key_file}', level=logging.INFO) + + command = f'/usr/bin/cryptsetup -q -v luksAddKey {self.luks_dev_path} {key_file}' + worker = SysCommandWorker(command, environment_vars={'LC_ALL': 'C'}) pw_injected = False + while worker.is_alive(): if b'Enter any existing passphrase' in worker and pw_injected is False: - worker.write(bytes(password, 'UTF-8')) + worker.write(self._password_bytes()) pw_injected = True if worker.exit_code != 0: - raise DiskError(f'Could not add encryption key {path} to {self.partition} because: {worker}') - - return True - - def crypttab(self, installation :Installer, key_path :str, options :List[str] = ["luks", "key-slot=1"]) -> None: - log(f'Adding a crypttab entry for key {key_path} in {installation}', level=logging.INFO) - with open(f"{installation.target}/etc/crypttab", "a") as crypttab: - crypttab.write(f"{self.mountpoint} UUID={convert_device_to_uuid(self.partition.path)} {key_path} {','.join(options)}\n") + raise DiskError(f'Could not add encryption key {key_file} to {self.luks_dev_path}: {worker.decode()}') + + def _crypttab( + self, + crypttab_path: Path, + key_file: Path, + options: List[str] + ) -> None: + log(f'Adding crypttab entry for key {key_file}', level=logging.INFO) + + with open(crypttab_path, 'a') as crypttab: + opt = ','.join(options) + uuid = self._get_luks_uuid() + row = f"{self.mapper_name} UUID={uuid} {key_file} {opt}\n" + crypttab.write(row) diff --git a/archinstall/lib/menu/__init__.py b/archinstall/lib/menu/__init__.py index 9b0adb8b..9c86faf5 100644 --- a/archinstall/lib/menu/__init__.py +++ b/archinstall/lib/menu/__init__.py @@ -1,2 +1,9 @@ -from .menu import Menu as Menu -from .global_menu import GlobalMenu as GlobalMenu \ No newline at end of file +from .abstract_menu import Selector, AbstractMenu, AbstractSubMenu +from .list_manager import ListManager +from .menu import ( + MenuSelectionType, + MenuSelection, + Menu, +) +from .table_selection_menu import TableMenu +from .text_input import TextInput diff --git a/archinstall/lib/menu/abstract_menu.py b/archinstall/lib/menu/abstract_menu.py index d659d709..53816655 100644 --- a/archinstall/lib/menu/abstract_menu.py +++ b/archinstall/lib/menu/abstract_menu.py @@ -7,7 +7,6 @@ from .menu import Menu, MenuSelectionType from ..locale_helpers import set_keyboard_language from ..output import log from ..translationhandler import TranslationHandler, Language -from ..user_interaction.general_conf import select_archinstall_language if TYPE_CHECKING: _: Any @@ -16,17 +15,17 @@ if TYPE_CHECKING: class Selector: def __init__( self, - description :str, - func :Optional[Callable] = None, - display_func :Optional[Callable] = None, - default :Any = None, - enabled :bool = False, - dependencies :List = [], - dependencies_not :List = [], - exec_func :Optional[Callable] = None, - preview_func :Optional[Callable] = None, - mandatory :bool = False, - no_store :bool = False + description: str, + func: Optional[Callable[[str], Any]] = None, + display_func: Optional[Callable] = None, + default: Optional[Any] = None, + enabled: bool = False, + dependencies: List = [], + dependencies_not: List = [], + exec_func: Optional[Callable] = None, + preview_func: Optional[Callable] = None, + mandatory: bool = False, + no_store: bool = False ): """ Create a new menu selection entry @@ -82,6 +81,11 @@ class Selector: self._preview_func = preview_func self.mandatory = mandatory self._no_store = no_store + self._default = default + + @property + def default(self) -> Any: + return self._default @property def description(self) -> str: @@ -96,7 +100,7 @@ class Selector: return self._dependencies_not @property - def current_selection(self): + def current_selection(self) -> Optional[Any]: return self._current_selection @property @@ -106,14 +110,14 @@ class Selector: def do_store(self) -> bool: return self._no_store is False - def set_enabled(self, status :bool = True): + def set_enabled(self, status: bool = True): self.enabled = status - def update_description(self, description :str): + def update_description(self, description: str): self._description = description def menu_text(self, padding: int = 0) -> str: - if self._description == '': # special menu option for __separator__ + if self._description == '': # special menu option for __separator__ return '' current = '' @@ -134,7 +138,7 @@ class Selector: return f'{description} {current}' - def set_current_selection(self, current :Optional[str]): + def set_current_selection(self, current: Optional[Any]): self._current_selection = current def has_selection(self) -> bool: @@ -158,14 +162,17 @@ class Selector: def is_mandatory(self) -> bool: return self.mandatory - def set_mandatory(self, status :bool = True): - self.mandatory = status - if status and not self.is_enabled(): - self.set_enabled(True) + def set_mandatory(self, value: bool): + self.mandatory = value class AbstractMenu: - def __init__(self, data_store: Optional[Dict[str, Any]] = None, auto_cursor=False, preview_size :float = 0.2): + def __init__( + self, + data_store: Dict[str, Any] = {}, + auto_cursor: bool = False, + preview_size: float = 0.2 + ): """ Create a new selection menu. @@ -179,25 +186,28 @@ class AbstractMenu: ;type preview_size: float (range 0..1) """ - self._enabled_order :List[str] = [] + self._enabled_order: List[str] = [] self._translation_handler = TranslationHandler() self.is_context_mgr = False - self._data_store = data_store if data_store is not None else {} + self._data_store = data_store self.auto_cursor = auto_cursor self._menu_options: Dict[str, Selector] = {} - self._setup_selection_menu_options() self.preview_size = preview_size self._last_choice = None + self.setup_selection_menu_options() + self._sync_all() + self._populate_default_values() + @property def last_choice(self): return self._last_choice - def __enter__(self, *args :Any, **kwargs :Any) -> AbstractMenu: + def __enter__(self, *args: Any, **kwargs: Any) -> AbstractMenu: self.is_context_mgr = True return self - def __exit__(self, *args :Any, **kwargs :Any) -> None: + def __exit__(self, *args: Any, **kwargs: Any) -> None: # TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager # TODO: skip processing when it comes from a planified exit if len(args) >= 2 and args[1]: @@ -216,7 +226,50 @@ class AbstractMenu: def translation_handler(self) -> TranslationHandler: return self._translation_handler - def _setup_selection_menu_options(self): + def _populate_default_values(self): + for config_key, selector in self._menu_options.items(): + if selector.default is not None and config_key not in self._data_store: + self._data_store[config_key] = selector.default + + def _sync_all(self): + for key in self._menu_options.keys(): + self._sync(key) + + def _sync(self, selector_name: str): + value = self._data_store.get(selector_name, None) + selector = self._menu_options.get(selector_name, None) + + if value is not None: + self._menu_options[selector_name].set_current_selection(value) + elif selector is not None and selector.has_selection(): + self._data_store[selector_name] = selector.current_selection + + def _missing_configs(self) -> List[str]: + def check(s): + return self._menu_options.get(s).has_selection() + + def has_superuser() -> bool: + sel = self._menu_options['!users'] + if sel.current_selection: + return any([u.sudo for u in sel.current_selection]) + return False + + mandatory_fields = dict(filter(lambda x: x[1].is_mandatory(), self._menu_options.items())) + missing = set() + + for key, selector in mandatory_fields.items(): + if key in ['!root-password', '!users']: + if not check('!root-password') and not has_superuser(): + missing.add( + str(_('Either root-password or at least 1 user with sudo privileges must be specified')) + ) + elif key == 'disk_config': + if not check('disk_config'): + missing.add(self._menu_options['disk_config'].description) + + return list(missing) + + def setup_selection_menu_options(self): """ Define the menu options. Menu options can be defined here in a subclass or done per program calling self.set_option() """ @@ -226,7 +279,7 @@ class AbstractMenu: """ will be called before each action in the menu """ return - def post_callback(self, selection_name: Optional[str] = None, value: Any = None): + def post_callback(self, selection_name: str, value: Any): """ will be called after each action in the menu """ return True @@ -234,31 +287,16 @@ class AbstractMenu: """ will be called at the end of the processing of the menu """ return - def synch(self, selector_name :str, omit_if_set :bool = False,omit_if_disabled :bool = False): - """ loads menu options with data_store value """ - arg = self._data_store.get(selector_name, None) - # don't display the menu option if it was defined already - if arg is not None and omit_if_set: - return - - if not self.option(selector_name).is_enabled() and omit_if_disabled: - return - - if arg is not None: - self._menu_options[selector_name].set_current_selection(arg) - def _update_enabled_order(self, selector_name: str): self._enabled_order.append(selector_name) - def enable(self, selector_name :str, omit_if_set :bool = False , mandatory :bool = False): + def enable(self, selector_name: str, mandatory: bool = False): """ activates menu options """ if self._menu_options.get(selector_name, None): self._menu_options[selector_name].set_enabled(True) self._update_enabled_order(selector_name) - - if mandatory: - self._menu_options[selector_name].set_mandatory(True) - self.synch(selector_name,omit_if_set) + self._menu_options[selector_name].set_mandatory(mandatory) + self._sync(selector_name) else: raise ValueError(f'No selector found: {selector_name}') @@ -274,7 +312,11 @@ class AbstractMenu: def _find_selection(self, selection_name: str) -> Tuple[str, Selector]: enabled_menus = self._menus_to_enable() padding = self._get_menu_text_padding(list(enabled_menus.values())) - option = [(k, v) for k, v in self._menu_options.items() if v.menu_text(padding).strip() == selection_name.strip()] + + option = [] + for k, v in self._menu_options.items(): + if v.menu_text(padding).strip() == selection_name.strip(): + option.append((k, v)) if len(option) != 1: raise ValueError(f'Selection not found: {selection_name}') @@ -283,12 +325,7 @@ class AbstractMenu: return config_name, selector def run(self, allow_reset: bool = False): - """ Calls the Menu framework""" - # we synch all the options just in case - for item in self.list_options(): - self.synch(item) - - self.post_callback() # as all the values can vary i have to exec this callback + self._sync_all() cursor_pos = None while True: @@ -341,13 +378,13 @@ class AbstractMenu: break # we get the last action key - actions = {str(v.description):k for k,v in self._menu_options.items()} + actions = {str(v.description): k for k, v in self._menu_options.items()} self._last_choice = actions[selection.value.strip()] # type: ignore if not self.is_context_mgr: self.__exit__() - def _process_selection(self, selection_name :str) -> bool: + def _process_selection(self, selection_name: str) -> bool: """ determines and executes the selection y Can / Should be extended to handle specific selection issues Returns true if the menu shall continue, False if it has ended @@ -356,7 +393,7 @@ class AbstractMenu: config_name, selector = self._find_selection(selection_name) return self.exec_option(config_name, selector) - def exec_option(self, config_name :str, p_selector :Optional[Selector] = None) -> bool: + def exec_option(self, config_name: str, p_selector: Optional[Selector] = None) -> bool: """ processes the execution of a given menu entry - pre process callback - selection function @@ -372,17 +409,21 @@ class AbstractMenu: self.pre_callback(config_name) result = None + if selector.func is not None: presel_val = self.option(config_name).get_selection() result = selector.func(presel_val) self._menu_options[config_name].set_current_selection(result) if selector.do_store(): self._data_store[config_name] = result - exec_ret_val = selector.exec_func(config_name,result) if selector.exec_func is not None else False - self.post_callback(config_name,result) - if exec_ret_val and self._check_mandatory_status(): + exec_ret_val = selector.exec_func(config_name, result) if selector.exec_func else False + + self.post_callback(config_name, result) + + if exec_ret_val: return False + return True def _set_kb_language(self): @@ -392,7 +433,7 @@ class AbstractMenu: if self._data_store.get('keyboard-layout', None) and len(self._data_store['keyboard-layout']): set_keyboard_language(self._data_store['keyboard-layout']) - def _verify_selection_enabled(self, selection_name :str) -> bool: + def _verify_selection_enabled(self, selection_name: str) -> bool: """ general """ if selection := self._menu_options.get(selection_name, None): if not selection.enabled: @@ -429,16 +470,10 @@ class AbstractMenu: return ordered_menus - def option(self,name :str) -> Selector: + def option(self, name: str) -> Selector: # TODO check inexistent name return self._menu_options[name] - def list_options(self) -> Iterator: - """ Iterator to retrieve the enabled menu option names - """ - for item in self._menu_options: - yield item - def list_enabled_options(self) -> Iterator: """ Iterator to retrieve the enabled menu options at a given time. The results are dynamic (if between calls to the iterator some elements -still not retrieved- are (de)activated @@ -447,44 +482,21 @@ class AbstractMenu: if item in self._menus_to_enable(): yield item - def set_option(self, name :str, selector :Selector): - self._menu_options[name] = selector - self.synch(name) - - def _check_mandatory_status(self) -> bool: - for field in self._menu_options: - option = self._menu_options[field] - if option.is_mandatory() and not option.has_selection(): - return False - return True - - def set_mandatory(self, field :str, status :bool): - self.option(field).set_mandatory(status) - - def mandatory_overview(self) -> Tuple[int, int]: - mandatory_fields = 0 - mandatory_waiting = 0 - 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, preset_value: Language) -> Language: + from ..user_interaction.general_conf import select_archinstall_language language = select_archinstall_language(self.translation_handler.translated_languages, preset_value) self._translation_handler.activate(language) return language class AbstractSubMenu(AbstractMenu): - def __init__(self, data_store: Optional[Dict[str, Any]] = None): + def __init__(self, data_store: Dict[str, Any] = {}): super().__init__(data_store=data_store) self._menu_options['__separator__'] = Selector('') self._menu_options['back'] = \ Selector( - _('Back'), + Menu.back(), no_store=True, enabled=True, exec_func=lambda n, v: True, diff --git a/archinstall/lib/menu/global_menu.py b/archinstall/lib/menu/global_menu.py deleted file mode 100644 index 7c5b153e..00000000 --- a/archinstall/lib/menu/global_menu.py +++ /dev/null @@ -1,429 +0,0 @@ -from __future__ import annotations - -from typing import Any, List, Optional, Union, Dict, TYPE_CHECKING - -import archinstall -from ..disk.encryption import DiskEncryptionMenu -from ..general import SysCommand, secret -from ..hardware import has_uefi -from ..menu import Menu -from ..menu.abstract_menu import Selector, AbstractMenu -from ..models import NetworkConfiguration -from ..models.disk_encryption import DiskEncryption, EncryptionType -from ..models.users import User -from ..output import FormattedOutput -from ..profiles import is_desktop_profile, Profile -from ..storage import storage -from ..user_interaction import add_number_of_parrallel_downloads -from ..user_interaction import ask_additional_packages_to_install -from ..user_interaction import ask_for_additional_users -from ..user_interaction import ask_for_audio_selection -from ..user_interaction import ask_for_bootloader -from ..user_interaction import ask_for_swap -from ..user_interaction import ask_hostname -from ..user_interaction import ask_ntp -from ..user_interaction import ask_to_configure_network -from ..user_interaction import get_password, ask_for_a_timezone, save_config -from ..user_interaction import select_additional_repositories -from ..user_interaction import select_disk_layout -from ..user_interaction import select_harddrives -from ..user_interaction import select_kernel -from ..user_interaction import select_language -from ..user_interaction import select_locale_enc -from ..user_interaction import select_locale_lang -from ..user_interaction import select_mirror_regions -from ..user_interaction import select_profile -from ..user_interaction.partitioning_conf import current_partition_layout - -if TYPE_CHECKING: - _: Any - - -class GlobalMenu(AbstractMenu): - def __init__(self,data_store): - self._disk_check = True - super().__init__(data_store=data_store, auto_cursor=True, preview_size=0.3) - - def _setup_selection_menu_options(self): - # archinstall.Language will not use preset values - self._menu_options['archinstall-language'] = \ - Selector( - _('Archinstall language'), - lambda x: self._select_archinstall_language(x), - display_func=lambda x: x.display_name, - default=self.translation_handler.get_language_by_abbr('en')) - self._menu_options['keyboard-layout'] = \ - Selector( - _('Keyboard layout'), - lambda preset: select_language(preset), - default='us') - self._menu_options['mirror-region'] = \ - Selector( - _('Mirror region'), - lambda preset: select_mirror_regions(preset), - display_func=lambda x: list(x.keys()) if x else '[]', - default={}) - self._menu_options['sys-language'] = \ - Selector( - _('Locale language'), - lambda preset: select_locale_lang(preset), - default='en_US') - self._menu_options['sys-encoding'] = \ - Selector( - _('Locale encoding'), - lambda preset: select_locale_enc(preset), - default='UTF-8') - self._menu_options['harddrives'] = \ - Selector( - _('Drive(s)'), - lambda preset: self._select_harddrives(preset), - display_func=lambda x: f'{len(x)} ' + str(_('Drive(s)')) if x is not None and len(x) > 0 else '', - preview_func=self._prev_harddrives, - ) - self._menu_options['disk_layouts'] = \ - Selector( - _('Disk layout'), - lambda preset: select_disk_layout( - preset, - storage['arguments'].get('harddrives', []), - storage['arguments'].get('advanced', False) - ), - preview_func=self._prev_disk_layouts, - display_func=lambda x: self._display_disk_layout(x), - dependencies=['harddrives']) - self._menu_options['disk_encryption'] = \ - Selector( - _('Disk encryption'), - lambda preset: self._disk_encryption(preset), - preview_func=self._prev_disk_encryption, - display_func=lambda x: self._display_disk_encryption(x), - dependencies=['disk_layouts']) - self._menu_options['swap'] = \ - Selector( - _('Swap'), - lambda preset: ask_for_swap(preset), - default=True) - self._menu_options['bootloader'] = \ - Selector( - _('Bootloader'), - lambda preset: ask_for_bootloader(storage['arguments'].get('advanced', False),preset), - default="systemd-bootctl" if has_uefi() else "grub-install") - self._menu_options['hostname'] = \ - Selector( - _('Hostname'), - ask_hostname, - default='archlinux') - # root password won't have preset value - self._menu_options['!root-password'] = \ - Selector( - _('Root password'), - lambda preset:self._set_root_password(), - display_func=lambda x: secret(x) if x else 'None') - self._menu_options['!users'] = \ - Selector( - _('User account'), - lambda x: self._create_user_account(x), - default={}, - 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'), - lambda preset: self._select_profile(preset), - display_func=lambda x: x if x else 'None' - ) - self._menu_options['audio'] = \ - Selector( - _('Audio'), - lambda preset: ask_for_audio_selection(is_desktop_profile(storage['arguments'].get('profile', None)),preset), - display_func=lambda x: x if x else 'None', - default=None - ) - - self._menu_options['parallel downloads'] = \ - Selector( - _('Parallel Downloads'), - add_number_of_parrallel_downloads, - display_func=lambda x: x if x else '0', - default=0 - ) - - self._menu_options['kernels'] = \ - Selector( - _('Kernels'), - lambda preset: select_kernel(preset), - default=['linux']) - self._menu_options['packages'] = \ - Selector( - _('Additional packages'), - # lambda x: ask_additional_packages_to_install(storage['arguments'].get('packages', None)), - ask_additional_packages_to_install, - default=[]) - self._menu_options['additional-repositories'] = \ - Selector( - _('Optional repositories'), - select_additional_repositories, - default=[]) - self._menu_options['nic'] = \ - Selector( - _('Network configuration'), - ask_to_configure_network, - display_func=lambda x: self._display_network_conf(x), - preview_func=self._prev_network_config, - default={}) - self._menu_options['timezone'] = \ - Selector( - _('Timezone'), - lambda preset: ask_for_a_timezone(preset), - default='UTC') - self._menu_options['ntp'] = \ - Selector( - _('Automatic time sync (NTP)'), - lambda preset: self._select_ntp(preset), - default=True) - self._menu_options['__separator__'] = \ - Selector('') - self._menu_options['save_config'] = \ - Selector( - _('Save configuration'), - lambda preset: save_config(self._data_store), - no_store=True) - self._menu_options['install'] = \ - Selector( - self._install_text(), - exec_func=lambda n,v: True if len(self._missing_configs()) == 0 else False, - preview_func=self._prev_install_missing_config, - no_store=True) - - self._menu_options['abort'] = Selector(_('Abort'), exec_func=lambda n,v:exit(1)) - - def _update_install_text(self, name :Optional[str] = None, result :Any = None): - text = self._install_text() - self._menu_options['install'].update_description(text) - - def post_callback(self,name :Optional[str] = None ,result :Any = None): - self._update_install_text(name, result) - - def _install_text(self): - missing = len(self._missing_configs()) - if missing > 0: - return _('Install ({} config(s) missing)').format(missing) - return _('Install') - - def _display_network_conf(self, cur_value: Union[NetworkConfiguration, List[NetworkConfiguration]]) -> str: - if not cur_value: - return _('Not configured, unavailable unless setup manually') - else: - if isinstance(cur_value, list): - return str(_('Configured {} interfaces')).format(len(cur_value)) - else: - return str(cur_value) - - def _disk_encryption(self, preset: Optional[DiskEncryption]) -> Optional[DiskEncryption]: - data_store: Dict[str, Any] = {} - - selector = self._menu_options['disk_layouts'] - - if selector.has_selection(): - layouts: Dict[str, Dict[str, Any]] = selector.current_selection - else: - # this should not happen as the encryption menu has the disk layout as dependency - raise ValueError('No disk layout specified') - - disk_encryption = DiskEncryptionMenu(data_store, preset, layouts).run() - return disk_encryption - - def _prev_network_config(self) -> Optional[str]: - selector = self._menu_options['nic'] - if selector.has_selection(): - ifaces = selector.current_selection - if isinstance(ifaces, list): - return FormattedOutput.as_table(ifaces) - return None - - def _prev_harddrives(self) -> Optional[str]: - selector = self._menu_options['harddrives'] - if selector.has_selection(): - drives = selector.current_selection - return FormattedOutput.as_table(drives) - return None - - def _prev_disk_layouts(self) -> Optional[str]: - selector = self._menu_options['disk_layouts'] - if selector.has_selection(): - layouts: Dict[str, Dict[str, Any]] = selector.current_selection - - output = '' - for device, layout in layouts.items(): - output += f'{_("Device")}: {device}\n\n' - output += current_partition_layout(layout['partitions'], with_title=False) - output += '\n\n' - - return output.rstrip() - - return None - - def _display_disk_layout(self, current_value: Optional[Dict[str, Any]]) -> str: - if current_value: - total_partitions = [entry['partitions'] for entry in current_value.values()] - total_nr = sum([len(p) for p in total_partitions]) - return f'{total_nr} {_("Partitions")}' - return '' - - def _prev_disk_encryption(self) -> Optional[str]: - selector = self._menu_options['disk_encryption'] - if selector.has_selection(): - encryption: DiskEncryption = selector.current_selection - - enc_type = EncryptionType.type_to_text(encryption.encryption_type) - output = str(_('Encryption type')) + f': {enc_type}\n' - output += str(_('Password')) + f': {secret(encryption.encryption_password)}\n' - - if encryption.all_partitions: - output += 'Partitions: {} selected'.format(len(encryption.all_partitions)) + '\n' - - if encryption.hsm_device: - output += f'HSM: {encryption.hsm_device.manufacturer}' - - return output - - return None - - def _display_disk_encryption(self, current_value: Optional[DiskEncryption]) -> str: - if current_value: - return EncryptionType.type_to_text(current_value.encryption_type) - return '' - - def _prev_install_missing_config(self) -> Optional[str]: - if missing := self._missing_configs(): - text = str(_('Missing configurations:\n')) - for m in missing: - text += f'- {m}\n' - 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 has_superuser(): - missing += [str(_('Either root-password or at least 1 user with sudo privileges must be specified'))] - if self._disk_check: - if not check('harddrives'): - missing += [str(_('Drive(s)'))] - if check('harddrives'): - if not self._menu_options['harddrives'].is_empty() and not check('disk_layouts'): - missing += [str(_('Disk layout'))] - - return missing - - def _set_root_password(self) -> Optional[str]: - prompt = str(_('Enter root password (leave blank to disable root): ')) - password = get_password(prompt=prompt) - return password - - # def _select_encrypted_password(self) -> Optional[str]: - # if passwd := get_password(prompt=str(_('Enter disk encryption password (leave blank for no encryption): '))): - # return passwd - # return None - - def _select_ntp(self, preset :bool = True) -> bool: - ntp = ask_ntp(preset) - - value = str(ntp).lower() - SysCommand(f'timedatectl set-ntp {value}') - - return ntp - - def _select_harddrives(self, old_harddrives: List[str] = []) -> List: - harddrives = select_harddrives(old_harddrives) - - if harddrives is not None: - 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(), skip=False).run() - - if choice.value == Menu.no(): - self._disk_check = True - return self._select_harddrives(old_harddrives) - else: - self._disk_check = False - - # in case the harddrives got changed we have to reset the disk layout as well - if old_harddrives != harddrives: - self._menu_options['disk_layouts'].set_current_selection(None) - storage['arguments']['disk_layouts'] = {} - - return harddrives - - def _select_profile(self, preset) -> Optional[Profile]: - ret: Optional[Profile] = None - profile = select_profile(preset) - - 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(servers=servers, desktop=desktop, desktop_env=desktop_env, gfx_driver=gfx_driver): - ret = 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(preset) - elif profile: - ret = profile - - return ret - - def _create_user_account(self, defined_users: List[User]) -> List[User]: - users = ask_for_additional_users(defined_users=defined_users) - return users diff --git a/archinstall/lib/menu/list_manager.py b/archinstall/lib/menu/list_manager.py index 1e09d987..be31fdf0 100644 --- a/archinstall/lib/menu/list_manager.py +++ b/archinstall/lib/menu/list_manager.py @@ -34,7 +34,7 @@ class ListManager: self._data = copy.deepcopy(entries) explainer = str(_('\n Choose an object from the list, and select one of the available actions for it to execute')) - self._prompt = prompt + explainer if prompt else explainer + self._prompt = prompt if prompt else explainer self._separator = '' self._confirm_action = str(_('Confirm and exit')) @@ -44,13 +44,18 @@ class ListManager: self._base_actions = base_actions self._sub_menu_actions = sub_menu_actions - self._last_choice = None + self._last_choice: Optional[str] = None @property - def last_choice(self): + def last_choice(self) -> Optional[str]: return self._last_choice - def run(self): + def is_last_choice_cancel(self) -> bool: + if self._last_choice is not None: + return self._last_choice == self._cancel_action + return False + + def run(self) -> List[Any]: while True: # this will return a dictionary with the key as the menu entry to be displayed # and the value is the original value from the self._data container @@ -76,10 +81,11 @@ class ListManager: elif choice.value in self._terminate_actions: break else: # an entry of the existing selection was choosen - selected_entry = data_formatted[choice.value] + selected_entry = data_formatted[choice.value] # type: ignore self._run_actions_on_entry(selected_entry) - self._last_choice = choice + self._last_choice = choice.value # type: ignore + if choice.value == self._cancel_action: return self._original_data # return the original list else: @@ -122,21 +128,29 @@ class ListManager: self._data = self.handle_action(choice.value, entry, self._data) def selected_action_display(self, selection: Any) -> str: - # this will return the value to be displayed in the - # "Select an action for '{}'" string + """ + this will return the value to be displayed in the + "Select an action for '{}'" string + """ raise NotImplementedError('Please implement me in the child class') - def reformat(self, data: List[Any]) -> Dict[str, Any]: - # this should return a dictionary of display string to actual data entry - # mapping; if the value for a given display string is None it will be used - # in the header value (useful when displaying tables) + def reformat(self, data: List[Any]) -> Dict[str, Optional[Any]]: + """ + this should return a dictionary of display string to actual data entry + mapping; if the value for a given display string is None it will be used + in the header value (useful when displaying tables) + """ raise NotImplementedError('Please implement me in the child class') def handle_action(self, action: Any, entry: Optional[Any], data: List[Any]) -> List[Any]: - # this function is called when a base action or - # a specific action for an entry is triggered + """ + this function is called when a base action or + a specific action for an entry is triggered + """ raise NotImplementedError('Please implement me in the child class') - def filter_options(self, selection :Any, options :List[str]) -> List[str]: - # filter which actions to show for an specific selection + def filter_options(self, selection: Any, options: List[str]) -> List[str]: + """ + filter which actions to show for an specific selection + """ return options diff --git a/archinstall/lib/menu/menu.py b/archinstall/lib/menu/menu.py index 09685c55..44ac33a6 100644 --- a/archinstall/lib/menu/menu.py +++ b/archinstall/lib/menu/menu.py @@ -3,7 +3,7 @@ from enum import Enum, auto from os import system from typing import Dict, List, Union, Any, TYPE_CHECKING, Optional, Callable -from .simple_menu import TerminalMenu +from simple_term_menu import TerminalMenu from ..exceptions import RequirementError from ..output import log @@ -27,42 +27,56 @@ class MenuSelection: type_: MenuSelectionType value: Optional[Union[str, List[str]]] = None + @property + def single_value(self) -> Any: + return self.value + + @property + def multi_value(self) -> List[Any]: + return self.value + class Menu(TerminalMenu): @classmethod - def yes(cls): + def back(cls) -> str: + return str(_('← Back')) + + @classmethod + def yes(cls) -> str: return str(_('yes')) @classmethod - def no(cls): + def no(cls) -> str: return str(_('no')) @classmethod - def yes_no(cls): + def yes_no(cls) -> List[str]: return [cls.yes(), cls.no()] def __init__( self, - title :str, - p_options :Union[List[str], Dict[str, Any]], - skip :bool = True, - multi :bool = False, - default_option : Optional[str] = None, - sort :bool = True, - preset_values :Union[str, List[str]] = None, - cursor_index : Optional[int] = None, + title: str, + p_options: Union[List[str], Dict[str, Any]], + skip: bool = True, + multi: bool = False, + default_option: Optional[str] = None, + sort: bool = True, + preset_values: Optional[Union[str, List[str]]] = None, + cursor_index: Optional[int] = None, preview_command: Optional[Callable] = None, preview_size: float = 0.0, preview_title: str = 'Info', - header :Union[List[str],str] = None, - allow_reset :bool = False, - allow_reset_warning_msg :str = '', + header: Union[List[str],str] = None, + allow_reset: bool = False, + allow_reset_warning_msg: Optional[str] = None, clear_screen: bool = True, show_search_hint: bool = True, cycle_cursor: bool = True, clear_menu_on_exit: bool = True, - skip_empty_entries: bool = False + skip_empty_entries: bool = False, + display_back_option: bool = False, + extra_bottom_space: bool = False ): """ Creates a new menu @@ -72,7 +86,7 @@ class Menu(TerminalMenu): :param p_options: Options to be displayed in the menu to chose from; if dict is specified then the keys of such will be used as options - :type options: list, dict + :type p_options: list, dict :param skip: Indicate if the selection is not mandatory and can be skipped :type skip: bool @@ -101,16 +115,17 @@ 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 - type param: string or list + :param header: one or more header lines for the menu + :type header: string or list - param raise_error_on_interrupt: This will explicitly handle a ctrl+c instead and return that specific state - type param: bool + :param allow_reset: This will explicitly handle a ctrl+c instead and return that specific state + :type allow_reset: bool - param raise_error_warning_msg: If raise_error_on_interrupt is True and this is non-empty, there will be a warning with a user confirmation displayed - type param: str + param allow_reset_warning_msg: If raise_error_on_interrupt is True the warnign is set, a user confirmation is displayed + type allow_reset_warning_msg: str - :param kwargs : any SimpleTerminal parameter + :param extra_bottom_space: Add an extra empty line at the end of the menu + :type extra_bottom_space: bool """ # we guarantee the inmutability of the options outside the class. # an unknown number of iterables (.keys(),.values(),generator,...) can't be directly copied, in this case @@ -152,7 +167,6 @@ class Menu(TerminalMenu): self._multi = multi self._raise_error_on_interrupt = allow_reset self._raise_error_warning_msg = allow_reset_warning_msg - self._preview_command = preview_command action_info = '' if skip: @@ -182,6 +196,14 @@ class Menu(TerminalMenu): default = f'{default_option} {self._default_str}' self._menu_options = [default] + [o for o in self._menu_options if default_option != o] + if display_back_option and not multi and skip: + skip_empty_entries = True + self._menu_options += ['', self.back()] + + if extra_bottom_space: + skip_empty_entries = True + self._menu_options += [''] + self._preselection(preset_values,cursor_index) cursor = "> " @@ -194,13 +216,10 @@ class Menu(TerminalMenu): menu_cursor=cursor, menu_cursor_style=main_menu_cursor_style, menu_highlight_style=main_menu_style, - # cycle_cursor=True, - # clear_screen=True, multi_select=multi, - # show_search_hint=True, preselected_entries=self.preset_values, cursor_index=self.cursor_index, - preview_command=lambda x: self._preview_wrapper(preview_command, x), + preview_command=lambda x: self._show_preview(preview_command, x), preview_size=preview_size, preview_title=preview_title, raise_error_on_interrupt=self._raise_error_on_interrupt, @@ -212,6 +231,17 @@ class Menu(TerminalMenu): skip_empty_entries=skip_empty_entries ) + def _show_preview(self, preview_command: Optional[Callable], selection: str) -> Optional[str]: + if selection == self.back(): + return None + + if preview_command: + if self._default_option is not None and f'{self._default_option} {self._default_str}' == selection: + selection = self._default_option + return preview_command(selection) + + return None + def _show(self) -> MenuSelection: try: idx = self.show() @@ -225,39 +255,37 @@ class Menu(TerminalMenu): return elem if idx is not None: - if isinstance(idx, (list, tuple)): + if isinstance(idx, (list, tuple)): # on multi selection results = [] for i in idx: option = check_default(self._menu_options[i]) results.append(option) return MenuSelection(type_=MenuSelectionType.Selection, value=results) - else: + else: # on single selection result = check_default(self._menu_options[idx]) return MenuSelection(type_=MenuSelectionType.Selection, value=result) else: return MenuSelection(type_=MenuSelectionType.Skip) - def _preview_wrapper(self, preview_command: Optional[Callable], current_selection: str) -> Optional[str]: - if preview_command: - if self._default_option is not None and f'{self._default_option} {self._default_str}' == current_selection: - current_selection = self._default_option - return preview_command(current_selection) - return None - def run(self) -> MenuSelection: - ret = self._show() + selection = self._show() - if ret.type_ == MenuSelectionType.Reset: - if self._raise_error_on_interrupt and len(self._raise_error_warning_msg) > 0: + if selection.type_ == MenuSelectionType.Reset: + if self._raise_error_on_interrupt and self._raise_error_warning_msg is not None: response = Menu(self._raise_error_warning_msg, Menu.yes_no(), skip=False).run() if response.value == Menu.no(): return self.run() - elif ret.type_ is MenuSelectionType.Skip: + elif selection.type_ is MenuSelectionType.Skip: if not self._skip: system('clear') return self.run() - return ret + if selection.type_ == MenuSelectionType.Selection: + if selection.value == self.back(): + selection.type_ = MenuSelectionType.Skip + selection.value = None + + return selection def set_cursor_pos(self,pos :int): if pos and 0 < pos < len(self._menu_entries): diff --git a/archinstall/lib/menu/simple_menu.py b/archinstall/lib/menu/simple_menu.py deleted file mode 100644 index 1980e2ce..00000000 --- a/archinstall/lib/menu/simple_menu.py +++ /dev/null @@ -1,2002 +0,0 @@ -""" -This file is copied over from the simple-term-menu project -(https://github.com/IngoMeyer441/simple-term-menu) -In order to comply with installation methods of Arch Linux. -We here by copy the MIT license attached to the project at the time of copy: - -Copyright 2021 Forschungszentrum Jülich GmbH - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -""" -import argparse -import copy -import ctypes -import io -import locale -import os -import platform -import re -import shlex -import signal -import string -import subprocess -import sys -from locale import getlocale -from types import FrameType -from typing import ( - Any, - Callable, - Dict, - Iterable, - Iterator, - List, - Match, - Optional, - Pattern, - Sequence, - Set, - TextIO, - Tuple, - Union, - cast, -) - -try: - import termios -except ImportError as e: - raise NotImplementedError('"{}" is currently not supported.'.format(platform.system())) from e - -__author__ = "Ingo Meyer" -__email__ = "i.meyer@fz-juelich.de" -__copyright__ = "Copyright © 2021 Forschungszentrum Jülich GmbH. All rights reserved." -__license__ = "MIT" -__version_info__ = (1, 5, 0) -__version__ = ".".join(map(str, __version_info__)) - - -DEFAULT_ACCEPT_KEYS = ("enter",) -DEFAULT_CLEAR_MENU_ON_EXIT = True -DEFAULT_CLEAR_SCREEN = False -DEFAULT_CYCLE_CURSOR = True -DEFAULT_EXIT_ON_SHORTCUT = True -DEFAULT_MENU_CURSOR = "> " -DEFAULT_MENU_CURSOR_STYLE = ("fg_red", "bold") -DEFAULT_MENU_HIGHLIGHT_STYLE = ("standout",) -DEFAULT_MULTI_SELECT = False -DEFAULT_MULTI_SELECT_CURSOR = "[*] " -DEFAULT_MULTI_SELECT_CURSOR_BRACKETS_STYLE = ("fg_gray",) -DEFAULT_MULTI_SELECT_CURSOR_STYLE = ("fg_yellow", "bold") -DEFAULT_MULTI_SELECT_KEYS = (" ", "tab") -DEFAULT_MULTI_SELECT_SELECT_ON_ACCEPT = True -DEFAULT_PREVIEW_BORDER = True -DEFAULT_PREVIEW_SIZE = 0.25 -DEFAULT_PREVIEW_TITLE = "preview" -DEFAULT_QUIT_KEYS = ("escape", "q") -DEFAULT_SEARCH_CASE_SENSITIVE = False -DEFAULT_SEARCH_HIGHLIGHT_STYLE = ("fg_black", "bg_yellow", "bold") -DEFAULT_SEARCH_KEY = "/" -DEFAULT_SHORTCUT_BRACKETS_HIGHLIGHT_STYLE = ("fg_gray",) -DEFAULT_SHORTCUT_KEY_HIGHLIGHT_STYLE = ("fg_blue",) -DEFAULT_SHOW_MULTI_SELECT_HINT = False -DEFAULT_SHOW_SEARCH_HINT = False -DEFAULT_SHOW_SHORTCUT_HINTS = False -DEFAULT_SHOW_SHORTCUT_HINTS_IN_STATUS_BAR = True -DEFAULT_STATUS_BAR_BELOW_PREVIEW = False -DEFAULT_STATUS_BAR_STYLE = ("fg_yellow", "bg_black") -MIN_VISIBLE_MENU_ENTRIES_COUNT = 3 - - -class InvalidParameterCombinationError(Exception): - pass - - -class InvalidStyleError(Exception): - pass - - -class NoMenuEntriesError(Exception): - pass - - -class PreviewCommandFailedError(Exception): - pass - - -class UnknownMenuEntryError(Exception): - pass - - -def get_locale() -> str: - user_locale = locale.getlocale()[1] - if user_locale is None: - return "ascii" - else: - return user_locale.lower() - - -def wcswidth(text: str) -> int: - if not hasattr(wcswidth, "libc"): - if platform.system() == "Darwin": - wcswidth.libc = ctypes.cdll.LoadLibrary("libSystem.dylib") # type: ignore - else: - wcswidth.libc = ctypes.cdll.LoadLibrary("libc.so.6") # type: ignore - user_locale = get_locale() - # First replace any null characters with the unicode replacement character (U+FFFD) since they cannot be passed - # in a `c_wchar_p` - encoded_text = text.replace("\0", "\uFFFD").encode(encoding=user_locale, errors="replace") - return wcswidth.libc.wcswidth( # type: ignore - ctypes.c_wchar_p(encoded_text.decode(encoding=user_locale)), len(encoded_text) - ) - - -def static_variables(**variables: Any) -> Callable[[Callable[..., Any]], Callable[..., Any]]: - def decorator(f: Callable[..., Any]) -> Callable[..., Any]: - for key, value in variables.items(): - setattr(f, key, value) - return f - - return decorator - - -class BoxDrawingCharacters: - if getlocale()[1] == "UTF-8": - # Unicode box characters - horizontal = "─" - vertical = "│" - upper_left = "┌" - upper_right = "┐" - lower_left = "└" - lower_right = "┘" - else: - # ASCII box characters - horizontal = "-" - vertical = "|" - upper_left = "+" - upper_right = "+" - lower_left = "+" - lower_right = "+" - - -class TerminalMenu: - class Search: - def __init__( - self, - menu_entries: Iterable[str], - search_text: Optional[str] = None, - case_senitive: bool = False, - show_search_hint: bool = False, - ): - self._menu_entries = menu_entries - self._case_sensitive = case_senitive - self._show_search_hint = show_search_hint - self._matches = [] # type: List[Tuple[int, Match[str]]] - self._search_regex = None # type: Optional[Pattern[str]] - self._change_callback = None # type: Optional[Callable[[], None]] - # Use the property setter since it has some more logic - self.search_text = search_text - - def _update_matches(self) -> None: - if self._search_regex is None: - self._matches = [] - else: - matches = [] - for i, menu_entry in enumerate(self._menu_entries): - match_obj = self._search_regex.search(menu_entry) - if match_obj: - matches.append((i, match_obj)) - self._matches = matches - - @property - def matches(self) -> List[Tuple[int, Match[str]]]: - return list(self._matches) - - @property - def search_regex(self) -> Optional[Pattern[str]]: - return self._search_regex - - @property - def search_text(self) -> Optional[str]: - return self._search_text - - @search_text.setter - def search_text(self, text: Optional[str]) -> None: - self._search_text = text - search_text = self._search_text - self._search_regex = None - while search_text and self._search_regex is None: - try: - self._search_regex = re.compile(search_text, flags=re.IGNORECASE if not self._case_sensitive else 0) - except re.error: - search_text = search_text[:-1] - self._update_matches() - if self._change_callback: - self._change_callback() - - @property - def change_callback(self) -> Optional[Callable[[], None]]: - return self._change_callback - - @change_callback.setter - def change_callback(self, callback: Optional[Callable[[], None]]) -> None: - self._change_callback = callback - - @property - def occupied_lines_count(self) -> int: - if not self and not self._show_search_hint: - return 0 - else: - return 1 - - def __bool__(self) -> bool: - return self._search_text is not None - - def __contains__(self, menu_index: int) -> bool: - return any(i == menu_index for i, _ in self._matches) - - def __len__(self) -> int: - return wcswidth(self._search_text) if self._search_text is not None else 0 - - class Selection: - def __init__(self, num_menu_entries: int, preselected_indices: Optional[Iterable[int]] = None): - self._num_menu_entries = num_menu_entries - self._selected_menu_indices = set(preselected_indices) if preselected_indices is not None else set() - - def clear(self) -> None: - self._selected_menu_indices.clear() - - def add(self, menu_index: int) -> None: - self[menu_index] = True - - def remove(self, menu_index: int) -> None: - self[menu_index] = False - - def toggle(self, menu_index: int) -> bool: - self[menu_index] = menu_index not in self._selected_menu_indices - return self[menu_index] - - def __bool__(self) -> bool: - return bool(self._selected_menu_indices) - - def __contains__(self, menu_index: int) -> bool: - return menu_index in self._selected_menu_indices - - def __getitem__(self, menu_index: int) -> bool: - return menu_index in self._selected_menu_indices - - def __setitem__(self, menu_index: int, is_selected: bool) -> None: - if is_selected: - self._selected_menu_indices.add(menu_index) - else: - self._selected_menu_indices.remove(menu_index) - - def __iter__(self) -> Iterator[int]: - return iter(self._selected_menu_indices) - - @property - def selected_menu_indices(self) -> Tuple[int, ...]: - return tuple(sorted(self._selected_menu_indices)) - - class View: - def __init__( - self, - menu_entries: Iterable[str], - search: "TerminalMenu.Search", - selection: "TerminalMenu.Selection", - viewport: "TerminalMenu.Viewport", - cycle_cursor: bool = True, - skip_indices: List[int] = [], - ): - self._menu_entries = list(menu_entries) - self._search = search - self._selection = selection - self._viewport = viewport - self._cycle_cursor = cycle_cursor - self._active_displayed_index = None # type: Optional[int] - self._skip_indices = skip_indices - self.update_view() - - def update_view(self) -> None: - if self._search and self._search.search_text != "": - self._displayed_index_to_menu_index = tuple(i for i, match_obj in self._search.matches) - else: - self._displayed_index_to_menu_index = tuple(range(len(self._menu_entries))) - self._menu_index_to_displayed_index = { - menu_index: displayed_index - for displayed_index, menu_index in enumerate(self._displayed_index_to_menu_index) - } - self._active_displayed_index = 0 if self._displayed_index_to_menu_index else None - self._viewport.search_lines_count = self._search.occupied_lines_count - self._viewport.keep_visible(self._active_displayed_index) - - def increment_active_index(self) -> None: - if self._active_displayed_index is not None: - if self._active_displayed_index + 1 < len(self._displayed_index_to_menu_index): - self._active_displayed_index += 1 - elif self._cycle_cursor: - self._active_displayed_index = 0 - self._viewport.keep_visible(self._active_displayed_index) - - if self._active_displayed_index in self._skip_indices: - self.increment_active_index() - - def decrement_active_index(self) -> None: - if self._active_displayed_index is not None: - if self._active_displayed_index > 0: - self._active_displayed_index -= 1 - elif self._cycle_cursor: - self._active_displayed_index = len(self._displayed_index_to_menu_index) - 1 - self._viewport.keep_visible(self._active_displayed_index) - - if self._active_displayed_index in self._skip_indices: - self.decrement_active_index() - - def is_visible(self, menu_index: int) -> bool: - return menu_index in self._menu_index_to_displayed_index and ( - self._viewport.lower_index - <= self._menu_index_to_displayed_index[menu_index] - <= self._viewport.upper_index - ) - - def convert_menu_index_to_displayed_index(self, menu_index: int) -> Optional[int]: - if menu_index in self._menu_index_to_displayed_index: - return self._menu_index_to_displayed_index[menu_index] - else: - return None - - def convert_displayed_index_to_menu_index(self, displayed_index: int) -> int: - return self._displayed_index_to_menu_index[displayed_index] - - @property - def active_menu_index(self) -> Optional[int]: - if self._active_displayed_index is not None: - return self._displayed_index_to_menu_index[self._active_displayed_index] - else: - return None - - @active_menu_index.setter - def active_menu_index(self, value: int) -> None: - self._selected_index = value - self._active_displayed_index = [ - displayed_index - for displayed_index, menu_index in enumerate(self._displayed_index_to_menu_index) - if menu_index == value - ][0] - self._viewport.keep_visible(self._active_displayed_index) - - @property - def active_displayed_index(self) -> Optional[int]: - return self._active_displayed_index - - @property - def displayed_selected_indices(self) -> List[int]: - return [ - self._menu_index_to_displayed_index[selected_index] - for selected_index in self._selection - if selected_index in self._menu_index_to_displayed_index - ] - - def __bool__(self) -> bool: - return self._active_displayed_index is not None - - def __iter__(self) -> Iterator[Tuple[int, int, str]]: - for displayed_index, menu_index in enumerate(self._displayed_index_to_menu_index): - if self._viewport.lower_index <= displayed_index <= self._viewport.upper_index: - yield (displayed_index, menu_index, self._menu_entries[menu_index]) - - class Viewport: - def __init__( - self, - num_menu_entries: int, - title_lines_count: int, - status_bar_lines_count: int, - preview_lines_count: int, - search_lines_count: int, - ): - self._num_menu_entries = num_menu_entries - self._title_lines_count = title_lines_count - self._status_bar_lines_count = status_bar_lines_count - # Use the property setter since it has some more logic - self.preview_lines_count = preview_lines_count - self.search_lines_count = search_lines_count - self._num_lines = self._calculate_num_lines() - self._viewport = (0, min(self._num_menu_entries, self._num_lines) - 1) - self.keep_visible(cursor_position=None, refresh_terminal_size=False) - - def _calculate_num_lines(self) -> int: - return ( - TerminalMenu._num_lines() - - self._title_lines_count - - self._status_bar_lines_count - - self._preview_lines_count - - self._search_lines_count - ) - - def keep_visible(self, cursor_position: Optional[int], refresh_terminal_size: bool = True) -> None: - # Treat `cursor_position=None` like `cursor_position=0` - if cursor_position is None: - cursor_position = 0 - if refresh_terminal_size: - self.update_terminal_size() - if self._viewport[0] <= cursor_position <= self._viewport[1]: - # Cursor is already visible - return - if cursor_position < self._viewport[0]: - scroll_num = cursor_position - self._viewport[0] - else: - scroll_num = cursor_position - self._viewport[1] - self._viewport = (self._viewport[0] + scroll_num, self._viewport[1] + scroll_num) - - def update_terminal_size(self) -> None: - num_lines = self._calculate_num_lines() - if num_lines != self._num_lines: - # First let the upper index grow or shrink - upper_index = min(num_lines, self._num_menu_entries) - 1 - # Then, use as much space as possible for the `lower_index` - lower_index = max(0, upper_index - num_lines) - self._viewport = (lower_index, upper_index) - self._num_lines = num_lines - - @property - def lower_index(self) -> int: - return self._viewport[0] - - @property - def upper_index(self) -> int: - return self._viewport[1] - - @property - def viewport(self) -> Tuple[int, int]: - return self._viewport - - @property - def size(self) -> int: - return self._viewport[1] - self._viewport[0] + 1 - - @property - def num_menu_entries(self) -> int: - return self._num_menu_entries - - @property - def title_lines_count(self) -> int: - return self._title_lines_count - - @property - def status_bar_lines_count(self) -> int: - return self._status_bar_lines_count - - @status_bar_lines_count.setter - def status_bar_lines_count(self, value: int) -> None: - self._status_bar_lines_count = value - - @property - def preview_lines_count(self) -> int: - return self._preview_lines_count - - @preview_lines_count.setter - def preview_lines_count(self, value: int) -> None: - self._preview_lines_count = min( - value if value >= 3 else 0, - TerminalMenu._num_lines() - - self._title_lines_count - - self._status_bar_lines_count - - MIN_VISIBLE_MENU_ENTRIES_COUNT, - ) - - @property - def search_lines_count(self) -> int: - return self._search_lines_count - - @search_lines_count.setter - def search_lines_count(self, value: int) -> None: - self._search_lines_count = value - - @property - def must_scroll(self) -> bool: - return self._num_menu_entries > self._num_lines - - _codename_to_capname = { - "bg_black": "setab 0", - "bg_blue": "setab 4", - "bg_cyan": "setab 6", - "bg_gray": "setab 7", - "bg_green": "setab 2", - "bg_purple": "setab 5", - "bg_red": "setab 1", - "bg_yellow": "setab 3", - "bold": "bold", - "clear": "clear", - "colors": "colors", - "cursor_down": "cud1", - "cursor_invisible": "civis", - "cursor_left": "cub1", - "cursor_right": "cuf1", - "cursor_up": "cuu1", - "cursor_visible": "cnorm", - "delete_line": "dl1", - "down": "kcud1", - "enter_application_mode": "smkx", - "exit_application_mode": "rmkx", - "fg_black": "setaf 0", - "fg_blue": "setaf 4", - "fg_cyan": "setaf 6", - "fg_gray": "setaf 7", - "fg_green": "setaf 2", - "fg_purple": "setaf 5", - "fg_red": "setaf 1", - "fg_yellow": "setaf 3", - "italics": "sitm", - "reset_attributes": "sgr0", - "standout": "smso", - "underline": "smul", - "up": "kcuu1", - } - _name_to_control_character = { - "backspace": "", # Is assigned later in `self._init_backspace_control_character` - "ctrl-j": "\012", - "ctrl-k": "\013", - "enter": "\015", - "escape": "\033", - "tab": "\t", - } - _codenames = tuple(_codename_to_capname.keys()) - _codename_to_terminal_code = None # type: Optional[Dict[str, str]] - _terminal_code_to_codename = None # type: Optional[Dict[str, str]] - - def __init__( - self, - menu_entries: Iterable[str], - *, - accept_keys: Iterable[str] = DEFAULT_ACCEPT_KEYS, - clear_menu_on_exit: bool = DEFAULT_CLEAR_MENU_ON_EXIT, - clear_screen: bool = DEFAULT_CLEAR_SCREEN, - cursor_index: Optional[int] = None, - cycle_cursor: bool = DEFAULT_CYCLE_CURSOR, - exit_on_shortcut: bool = DEFAULT_EXIT_ON_SHORTCUT, - menu_cursor: Optional[str] = DEFAULT_MENU_CURSOR, - menu_cursor_style: Optional[Iterable[str]] = DEFAULT_MENU_CURSOR_STYLE, - menu_highlight_style: Optional[Iterable[str]] = DEFAULT_MENU_HIGHLIGHT_STYLE, - multi_select: bool = DEFAULT_MULTI_SELECT, - multi_select_cursor: str = DEFAULT_MULTI_SELECT_CURSOR, - multi_select_cursor_brackets_style: Optional[Iterable[str]] = DEFAULT_MULTI_SELECT_CURSOR_BRACKETS_STYLE, - multi_select_cursor_style: Optional[Iterable[str]] = DEFAULT_MULTI_SELECT_CURSOR_STYLE, - multi_select_empty_ok: bool = False, - multi_select_keys: Optional[Iterable[str]] = DEFAULT_MULTI_SELECT_KEYS, - multi_select_select_on_accept: bool = DEFAULT_MULTI_SELECT_SELECT_ON_ACCEPT, - preselected_entries: Optional[Iterable[Union[str, int]]] = None, - preview_border: bool = DEFAULT_PREVIEW_BORDER, - preview_command: Optional[Union[str, Callable[[str], str]]] = None, - preview_size: float = DEFAULT_PREVIEW_SIZE, - preview_title: str = DEFAULT_PREVIEW_TITLE, - quit_keys: Iterable[str] = DEFAULT_QUIT_KEYS, - raise_error_on_interrupt: bool = False, - search_case_sensitive: bool = DEFAULT_SEARCH_CASE_SENSITIVE, - search_highlight_style: Optional[Iterable[str]] = DEFAULT_SEARCH_HIGHLIGHT_STYLE, - search_key: Optional[str] = DEFAULT_SEARCH_KEY, - shortcut_brackets_highlight_style: Optional[Iterable[str]] = DEFAULT_SHORTCUT_BRACKETS_HIGHLIGHT_STYLE, - shortcut_key_highlight_style: Optional[Iterable[str]] = DEFAULT_SHORTCUT_KEY_HIGHLIGHT_STYLE, - show_multi_select_hint: bool = DEFAULT_SHOW_MULTI_SELECT_HINT, - show_multi_select_hint_text: Optional[str] = None, - show_search_hint: bool = DEFAULT_SHOW_SEARCH_HINT, - show_search_hint_text: Optional[str] = None, - show_shortcut_hints: bool = DEFAULT_SHOW_SHORTCUT_HINTS, - show_shortcut_hints_in_status_bar: bool = DEFAULT_SHOW_SHORTCUT_HINTS_IN_STATUS_BAR, - skip_empty_entries: bool = False, - 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 - ): - def extract_shortcuts_menu_entries_and_preview_arguments( - entries: Iterable[str], - ) -> Tuple[List[str], List[Optional[str]], List[Optional[str]], List[int]]: - separator_pattern = re.compile(r"([^\\])\|") - escaped_separator_pattern = re.compile(r"\\\|") - menu_entry_pattern = re.compile(r"^(?:\[(\S)\]\s*)?([^\x1F]+)(?:\x1F([^\x1F]*))?") - shortcut_keys = [] # type: List[Optional[str]] - menu_entries = [] # type: List[str] - preview_arguments = [] # type: List[Optional[str]] - skip_indices = [] # type: List[int] - - for idx, entry in enumerate(entries): - if entry is None or (entry == "" and skip_empty_entries): - shortcut_keys.append(None) - menu_entries.append("") - preview_arguments.append(None) - skip_indices.append(idx) - else: - unit_separated_entry = escaped_separator_pattern.sub("|", separator_pattern.sub("\\1\x1F", entry)) - match_obj = menu_entry_pattern.match(unit_separated_entry) - # this is none in case the entry was an emtpy string which - # will be interpreted as a separator - assert match_obj is not None - shortcut_key = match_obj.group(1) - display_text = match_obj.group(2) - preview_argument = match_obj.group(3) - shortcut_keys.append(shortcut_key) - menu_entries.append(display_text) - preview_arguments.append(preview_argument) - - return menu_entries, shortcut_keys, preview_arguments, skip_indices - - def convert_preselected_entries_to_indices( - preselected_indices_or_entries: Iterable[Union[str, int]] - ) -> Set[int]: - menu_entry_to_indices = {} # type: Dict[str, Set[int]] - for menu_index, menu_entry in enumerate(self._menu_entries): - menu_entry_to_indices.setdefault(menu_entry, set()) - menu_entry_to_indices[menu_entry].add(menu_index) - preselected_indices = set() - for item in preselected_indices_or_entries: - if isinstance(item, int): - if 0 <= item < len(self._menu_entries): - preselected_indices.add(item) - else: - raise IndexError( - "Error: {} is outside the allowable range of 0..{}.".format( - item, len(self._menu_entries) - 1 - ) - ) - elif isinstance(item, str): - try: - preselected_indices.update(menu_entry_to_indices[item]) - except KeyError as e: - raise UnknownMenuEntryError('Pre-selection "{}" is not a valid menu entry.'.format(item)) from e - else: - raise ValueError('"preselected_entries" must either contain integers or strings.') - return preselected_indices - - def setup_title_or_status_bar_lines( - title_or_status_bar: Optional[Union[str, Iterable[str]]], - show_shortcut_hints: bool, - menu_entries: Iterable[str], - shortcut_keys: Iterable[Optional[str]], - shortcut_hints_in_parentheses: bool, - ) -> Tuple[str, ...]: - if title_or_status_bar is None: - lines = [] # type: List[str] - elif isinstance(title_or_status_bar, str): - lines = title_or_status_bar.split("\n") - else: - lines = list(title_or_status_bar) - if show_shortcut_hints: - shortcut_hints_line = self._get_shortcut_hints_line( - menu_entries, shortcut_keys, shortcut_hints_in_parentheses - ) - if shortcut_hints_line is not None: - lines.append(shortcut_hints_line) - return tuple(lines) - - ( - self._menu_entries, - self._shortcut_keys, - self._preview_arguments, - self._skip_indices, - ) = extract_shortcuts_menu_entries_and_preview_arguments(menu_entries) - self._shortcuts_defined = any(key is not None for key in self._shortcut_keys) - self._accept_keys = tuple(accept_keys) - self._clear_menu_on_exit = clear_menu_on_exit - self._clear_screen = clear_screen - self._cycle_cursor = cycle_cursor - self._multi_select_empty_ok = multi_select_empty_ok - self._exit_on_shortcut = exit_on_shortcut - self._menu_cursor = menu_cursor if menu_cursor is not None else "" - self._menu_cursor_style = tuple(menu_cursor_style) if menu_cursor_style is not None else () - self._menu_highlight_style = tuple(menu_highlight_style) if menu_highlight_style is not None else () - self._multi_select = multi_select - self._multi_select_cursor = multi_select_cursor - self._multi_select_cursor_brackets_style = ( - tuple(multi_select_cursor_brackets_style) if multi_select_cursor_brackets_style is not None else () - ) - self._multi_select_cursor_style = ( - tuple(multi_select_cursor_style) if multi_select_cursor_style is not None else () - ) - self._multi_select_keys = tuple(multi_select_keys) if multi_select_keys is not None else () - self._multi_select_select_on_accept = multi_select_select_on_accept - if preselected_entries and not self._multi_select: - raise InvalidParameterCombinationError( - "Multi-select mode must be enabled when preselected entries are given." - ) - self._preselected_indices = ( - convert_preselected_entries_to_indices(preselected_entries) if preselected_entries is not None else None - ) - self._preview_border = preview_border - self._preview_command = preview_command - self._preview_size = preview_size - self._preview_title = preview_title - self._quit_keys = tuple(quit_keys) - self._raise_error_on_interrupt = raise_error_on_interrupt - 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._shortcut_brackets_highlight_style = ( - tuple(shortcut_brackets_highlight_style) if shortcut_brackets_highlight_style is not None else () - ) - self._shortcut_key_highlight_style = ( - tuple(shortcut_key_highlight_style) if shortcut_key_highlight_style is not None else () - ) - self._show_search_hint = show_search_hint - self._show_search_hint_text = show_search_hint_text - self._show_shortcut_hints = show_shortcut_hints - self._show_shortcut_hints_in_status_bar = show_shortcut_hints_in_status_bar - self._status_bar_func = None # type: Optional[Callable[[str], str]] - self._status_bar_lines = None # type: Optional[Tuple[str, ...]] - if callable(status_bar): - self._status_bar_func = status_bar - else: - self._status_bar_lines = setup_title_or_status_bar_lines( - status_bar, - show_shortcut_hints and show_shortcut_hints_in_status_bar, - self._menu_entries, - self._shortcut_keys, - False, - ) - self._status_bar_below_preview = status_bar_below_preview - self._status_bar_style = tuple(status_bar_style) if status_bar_style is not None else () - self._title_lines = setup_title_or_status_bar_lines( - title, - show_shortcut_hints and not show_shortcut_hints_in_status_bar, - self._menu_entries, - self._shortcut_keys, - True, - ) - self._show_multi_select_hint = show_multi_select_hint - self._show_multi_select_hint_text = show_multi_select_hint_text - self._chosen_accept_key = None # type: Optional[str] - self._chosen_menu_index = None # type: Optional[int] - self._chosen_menu_indices = None # type: Optional[Tuple[int, ...]] - self._paint_before_next_read = False - self._previous_displayed_menu_height = None # type: Optional[int] - self._reading_next_key = False - self._search = self.Search( - self._menu_entries, - case_senitive=self._search_case_sensitive, - show_search_hint=self._show_search_hint, - ) - self._selection = self.Selection(len(self._menu_entries), self._preselected_indices) - self._viewport = self.Viewport( - len(self._menu_entries), - len(self._title_lines), - len(self._status_bar_lines) if self._status_bar_lines is not None else 0, - 0, - 0, - ) - self._view = self.View( - self._menu_entries, self._search, self._selection, self._viewport, self._cycle_cursor, self._skip_indices - ) - if cursor_index and 0 < cursor_index < len(self._menu_entries): - self._view.active_menu_index = cursor_index - self._search.change_callback = self._view.update_view - self._old_term = None # type: Optional[List[Union[int, List[bytes]]]] - self._new_term = None # type: Optional[List[Union[int, List[bytes]]]] - self._tty_in = None # type: Optional[TextIO] - self._tty_out = None # type: Optional[TextIO] - self._user_locale = get_locale() - self._check_for_valid_styles() - # backspace can be queried from the terminal database but is unreliable, query the terminal directly instead - self._init_backspace_control_character() - self._add_missing_control_characters_for_keys(self._accept_keys) - self._add_missing_control_characters_for_keys(self._quit_keys) - self._init_terminal_codes() - - @staticmethod - def _get_shortcut_hints_line( - menu_entries: Iterable[str], - shortcut_keys: Iterable[Optional[str]], - shortcut_hints_in_parentheses: bool, - ) -> Optional[str]: - shortcut_hints_line = ", ".join( - "[{}]: {}".format(shortcut_key, menu_entry) - for shortcut_key, menu_entry in zip(shortcut_keys, menu_entries) - if shortcut_key is not None - ) - if shortcut_hints_line != "": - if shortcut_hints_in_parentheses: - return "(" + shortcut_hints_line + ")" - else: - return shortcut_hints_line - return None - - @staticmethod - def _get_keycode_for_key(key: str) -> str: - if len(key) == 1: - # One letter keys represent themselves - return key - alt_modified_regex = re.compile(r"[Aa]lt-(\S)") - ctrl_modified_regex = re.compile(r"[Cc]trl-(\S)") - match_obj = alt_modified_regex.match(key) - if match_obj: - return "\033" + match_obj.group(1) - match_obj = ctrl_modified_regex.match(key) - if match_obj: - # Ctrl + key is interpreted by terminals as the ascii code of that key minus 64 - ctrl_code_ascii = ord(match_obj.group(1).upper()) - 64 - if ctrl_code_ascii < 0: - # Interpret negative ascii codes as unsigned 7-Bit integers - ctrl_code_ascii = ctrl_code_ascii & 0x80 - 1 - return chr(ctrl_code_ascii) - raise ValueError('Cannot interpret the given key "{}".'.format(key)) - - @classmethod - def _init_backspace_control_character(self) -> None: - try: - with open("/dev/tty", "r") as tty: - stty_output = subprocess.check_output(["stty", "-a"], universal_newlines=True, stdin=tty) - name_to_keycode_regex = re.compile(r"^\s*(\S+)\s*=\s*\^(\S+)\s*$") - for field in stty_output.split(";"): - match_obj = name_to_keycode_regex.match(field) - if not match_obj: - continue - name, ctrl_code = match_obj.group(1), match_obj.group(2) - if name != "erase": - continue - self._name_to_control_character["backspace"] = self._get_keycode_for_key("ctrl-" + ctrl_code) - return - except subprocess.CalledProcessError: - pass - # Backspace control character could not be queried, assume `` (is most often used) - self._name_to_control_character["backspace"] = "\177" - - @classmethod - def _add_missing_control_characters_for_keys(cls, keys: Iterable[str]) -> None: - for key in keys: - if key not in cls._name_to_control_character and key not in string.ascii_letters: - cls._name_to_control_character[key] = cls._get_keycode_for_key(key) - - @classmethod - def _init_terminal_codes(cls) -> None: - if cls._codename_to_terminal_code is not None: - return - supported_colors = int(cls._query_terminfo_database("colors")) - cls._codename_to_terminal_code = { - codename: cls._query_terminfo_database(codename) - if not (codename.startswith("bg_") or codename.startswith("fg_")) or supported_colors >= 8 - else "" - for codename in cls._codenames - } - cls._codename_to_terminal_code.update(cls._name_to_control_character) - cls._terminal_code_to_codename = { - terminal_code: codename for codename, terminal_code in cls._codename_to_terminal_code.items() - } - - @classmethod - def _query_terminfo_database(cls, codename: str) -> str: - if codename in cls._codename_to_capname: - capname = cls._codename_to_capname[codename] - else: - capname = codename - try: - return subprocess.check_output(["tput"] + capname.split(), universal_newlines=True) - except subprocess.CalledProcessError as e: - # The return code 1 indicates a missing terminal capability - if e.returncode == 1: - return "" - raise e - - @classmethod - def _num_lines(self) -> int: - return int(self._query_terminfo_database("lines")) - - @classmethod - def _num_cols(self) -> int: - return int(self._query_terminfo_database("cols")) - - def _check_for_valid_styles(self) -> None: - invalid_styles = [] - for style_tuple in ( - self._menu_cursor_style, - self._menu_highlight_style, - self._search_highlight_style, - self._shortcut_key_highlight_style, - self._shortcut_brackets_highlight_style, - self._status_bar_style, - self._multi_select_cursor_brackets_style, - self._multi_select_cursor_style, - ): - for style in style_tuple: - if style not in self._codename_to_capname: - invalid_styles.append(style) - if invalid_styles: - if len(invalid_styles) == 1: - raise InvalidStyleError('The style "{}" does not exist.'.format(invalid_styles[0])) - else: - raise InvalidStyleError('The styles ("{}") do not exist.'.format('", "'.join(invalid_styles))) - - def _init_term(self) -> None: - # pylint: disable=unsubscriptable-object - assert self._codename_to_terminal_code is not None - self._tty_in = open("/dev/tty", "r", encoding=self._user_locale) - self._tty_out = open("/dev/tty", "w", encoding=self._user_locale, errors="replace") - self._old_term = termios.tcgetattr(self._tty_in.fileno()) - self._new_term = termios.tcgetattr(self._tty_in.fileno()) - # set the terminal to: unbuffered, no echo and no to translation (so sends instead of - # and since generates ) - self._new_term[3] = cast(int, self._new_term[3]) & ~termios.ICANON & ~termios.ECHO & ~termios.ICRNL - self._new_term[0] = cast(int, self._new_term[0]) & ~termios.ICRNL - termios.tcsetattr( - self._tty_in.fileno(), termios.TCSAFLUSH, cast(List[Union[int, List[Union[bytes, int]]]], self._new_term) - ) - # Enter terminal application mode to get expected escape codes for arrow keys - self._tty_out.write(self._codename_to_terminal_code["enter_application_mode"]) - self._tty_out.write(self._codename_to_terminal_code["cursor_invisible"]) - if self._clear_screen: - self._tty_out.write(self._codename_to_terminal_code["clear"]) - - def _reset_term(self) -> None: - # pylint: disable=unsubscriptable-object - assert self._codename_to_terminal_code is not None - assert self._tty_in is not None - assert self._tty_out is not None - assert self._old_term is not None - termios.tcsetattr( - self._tty_out.fileno(), termios.TCSAFLUSH, cast(List[Union[int, List[Union[bytes, int]]]], self._old_term) - ) - self._tty_out.write(self._codename_to_terminal_code["cursor_visible"]) - self._tty_out.write(self._codename_to_terminal_code["exit_application_mode"]) - if self._clear_screen: - self._tty_out.write(self._codename_to_terminal_code["clear"]) - self._tty_in.close() - self._tty_out.close() - - def _paint_menu(self) -> None: - def get_status_bar_lines() -> Tuple[str, ...]: - def get_multi_select_hint() -> str: - def get_string_from_keys(keys: Sequence[str]) -> str: - string_to_key = { - " ": "space", - } - keys_string = ", ".join( - "<" + string_to_key.get(accept_key, accept_key) + ">" for accept_key in keys - ) - return keys_string - - accept_keys_string = get_string_from_keys(self._accept_keys) - multi_select_keys_string = get_string_from_keys(self._multi_select_keys) - if self._show_multi_select_hint_text is not None: - return self._show_multi_select_hint_text.format( - multi_select_keys=multi_select_keys_string, accept_keys=accept_keys_string - ) - else: - return "Press {} for multi-selection and {} to {}accept".format( - multi_select_keys_string, - accept_keys_string, - "select and " if self._multi_select_select_on_accept else "", - ) - - if self._status_bar_func is not None and self._view.active_menu_index is not None: - status_bar_lines = tuple( - self._status_bar_func(self._menu_entries[self._view.active_menu_index]).strip().split("\n") - ) - if self._show_shortcut_hints and self._show_shortcut_hints_in_status_bar: - shortcut_hints_line = self._get_shortcut_hints_line(self._menu_entries, self._shortcut_keys, False) - if shortcut_hints_line is not None: - status_bar_lines += (shortcut_hints_line,) - elif self._status_bar_lines is not None: - status_bar_lines = self._status_bar_lines - else: - status_bar_lines = tuple() - if self._multi_select and self._show_multi_select_hint: - status_bar_lines += (get_multi_select_hint(),) - return status_bar_lines - - def apply_style( - style_iterable: Optional[Iterable[str]] = None, reset: bool = True, file: Optional[TextIO] = None - ) -> None: - # pylint: disable=unsubscriptable-object - assert self._codename_to_terminal_code is not None - assert self._tty_out is not None - if file is None: - file = self._tty_out - if reset or style_iterable is None: - file.write(self._codename_to_terminal_code["reset_attributes"]) - if style_iterable is not None: - for style in style_iterable: - file.write(self._codename_to_terminal_code[style]) - - def print_menu_entries() -> int: - # pylint: disable=unsubscriptable-object - assert self._codename_to_terminal_code is not None - assert self._tty_out is not None - all_cursors_width = wcswidth(self._menu_cursor) + ( - wcswidth(self._multi_select_cursor) if self._multi_select else 0 - ) - current_menu_block_displayed_height = 0 # sum all written lines - num_cols = self._num_cols() - if self._title_lines: - self._tty_out.write( - len(self._title_lines) * self._codename_to_terminal_code["cursor_up"] - + "\r" - + "\n".join( - (title_line[:num_cols] + (num_cols - wcswidth(title_line)) * " ") - for title_line in self._title_lines - ) - + "\n" - ) - shortcut_string_len = 4 if self._shortcuts_defined else 0 - displayed_index = -1 - for displayed_index, menu_index, menu_entry in self._view: - current_shortcut_key = self._shortcut_keys[menu_index] - self._tty_out.write(all_cursors_width * self._codename_to_terminal_code["cursor_right"]) - if self._shortcuts_defined: - if current_shortcut_key is not None: - apply_style(self._shortcut_brackets_highlight_style) - self._tty_out.write("[") - apply_style(self._shortcut_key_highlight_style) - self._tty_out.write(current_shortcut_key) - apply_style(self._shortcut_brackets_highlight_style) - self._tty_out.write("]") - apply_style() - else: - self._tty_out.write(3 * " ") - self._tty_out.write(" ") - if menu_index == self._view.active_menu_index: - apply_style(self._menu_highlight_style) - if self._search and self._search.search_text != "": - match_obj = self._search.matches[displayed_index][1] - self._tty_out.write( - menu_entry[: min(match_obj.start(), num_cols - all_cursors_width - shortcut_string_len)] - ) - apply_style(self._search_highlight_style) - self._tty_out.write( - menu_entry[ - match_obj.start() : min(match_obj.end(), num_cols - all_cursors_width - shortcut_string_len) - ] - ) - apply_style() - if menu_index == self._view.active_menu_index: - apply_style(self._menu_highlight_style) - self._tty_out.write( - menu_entry[match_obj.end() : num_cols - all_cursors_width - shortcut_string_len] - ) - else: - self._tty_out.write(menu_entry[: num_cols - all_cursors_width - shortcut_string_len]) - if menu_index == self._view.active_menu_index: - apply_style() - self._tty_out.write((num_cols - wcswidth(menu_entry) - all_cursors_width - shortcut_string_len) * " ") - if displayed_index < self._viewport.upper_index: - self._tty_out.write("\n") - empty_menu_lines = self._viewport.upper_index - displayed_index - self._tty_out.write( - max(0, empty_menu_lines - 1) * (num_cols * " " + "\n") + min(1, empty_menu_lines) * (num_cols * " ") - ) - self._tty_out.write("\r" + (self._viewport.size - 1) * self._codename_to_terminal_code["cursor_up"]) - current_menu_block_displayed_height += self._viewport.size - 1 # sum all written lines - return current_menu_block_displayed_height - - def print_search_line(current_menu_height: int) -> int: - # pylint: disable=unsubscriptable-object - assert self._codename_to_terminal_code is not None - assert self._tty_out is not None - current_menu_block_displayed_height = 0 - num_cols = self._num_cols() - if self._search or self._show_search_hint: - self._tty_out.write((current_menu_height + 1) * self._codename_to_terminal_code["cursor_down"]) - if self._search: - assert self._search.search_text is not None - self._tty_out.write( - ( - (self._search_key if self._search_key is not None else DEFAULT_SEARCH_KEY) - + self._search.search_text - )[:num_cols] - ) - self._tty_out.write((num_cols - len(self._search) - 1) * " ") - elif self._show_search_hint: - if self._show_search_hint_text is not None: - search_hint = self._show_search_hint_text.format(key=self._search_key)[:num_cols] - elif self._search_key is not None: - search_hint = '(Press "{key}" to search)'.format(key=self._search_key)[:num_cols] - else: - search_hint = "(Press any letter key to search)"[:num_cols] - self._tty_out.write(search_hint) - self._tty_out.write((num_cols - wcswidth(search_hint)) * " ") - if self._search or self._show_search_hint: - self._tty_out.write("\r" + (current_menu_height + 1) * self._codename_to_terminal_code["cursor_up"]) - current_menu_block_displayed_height = 1 - return current_menu_block_displayed_height - - def print_status_bar(current_menu_height: int, status_bar_lines: Tuple[str, ...]) -> int: - # pylint: disable=unsubscriptable-object - assert self._codename_to_terminal_code is not None - assert self._tty_out is not None - current_menu_block_displayed_height = 0 # sum all written lines - num_cols = self._num_cols() - if status_bar_lines: - self._tty_out.write((current_menu_height + 1) * self._codename_to_terminal_code["cursor_down"]) - apply_style(self._status_bar_style) - self._tty_out.write( - "\r" - + "\n".join( - (status_bar_line[:num_cols] + (num_cols - wcswidth(status_bar_line)) * " ") - for status_bar_line in status_bar_lines - ) - + "\r" - ) - apply_style() - self._tty_out.write( - (current_menu_height + len(status_bar_lines)) * self._codename_to_terminal_code["cursor_up"] - ) - current_menu_block_displayed_height += len(status_bar_lines) - return current_menu_block_displayed_height - - def print_preview(current_menu_height: int, preview_max_num_lines: int) -> int: - # pylint: disable=unsubscriptable-object - assert self._codename_to_terminal_code is not None - assert self._tty_out is not None - if self._preview_command is None or preview_max_num_lines < 3: - return 0 - - def get_preview_string() -> Optional[str]: - assert self._preview_command is not None - if self._view.active_menu_index is None: - return None - preview_argument = ( - self._preview_arguments[self._view.active_menu_index] - if self._preview_arguments[self._view.active_menu_index] is not None - else self._menu_entries[self._view.active_menu_index] - ) - if preview_argument == "": - return None - if isinstance(self._preview_command, str): - try: - preview_process = subprocess.Popen( - [cmd_part.format(preview_argument) for cmd_part in shlex.split(self._preview_command)], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - assert preview_process.stdout is not None - preview_string = ( - io.TextIOWrapper(preview_process.stdout, encoding=self._user_locale, errors="replace") - .read() - .strip() - ) - except subprocess.CalledProcessError as e: - raise PreviewCommandFailedError( - e.stderr.decode(encoding=self._user_locale, errors="replace").strip() - ) from e - else: - preview_string = self._preview_command(preview_argument) if preview_argument is not None else "" - return preview_string - - @static_variables( - # Regex taken from https://stackoverflow.com/a/14693789/5958465 - ansi_escape_regex=re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])"), - # Modified version of https://stackoverflow.com/a/2188410/5958465 - ansi_sgr_regex=re.compile(r"\x1B\[[;\d]*m"), - ) - def strip_ansi_codes_except_styling(string: str) -> str: - stripped_string = strip_ansi_codes_except_styling.ansi_escape_regex.sub( # type: ignore - lambda match_obj: match_obj.group(0) - if strip_ansi_codes_except_styling.ansi_sgr_regex.match(match_obj.group(0)) # type: ignore - else "", - string, - ) - return cast(str, stripped_string) - - @static_variables( - regular_text_regex=re.compile(r"([^\x1B]+)(.*)"), - ansi_escape_regex=re.compile(r"(\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]))(.*)"), - ) - def limit_string_with_escape_codes(string: str, max_len: int) -> Tuple[str, int]: - if max_len <= 0: - return "", 0 - string_parts = [] - string_len = 0 - while string: - regular_text_match = limit_string_with_escape_codes.regular_text_regex.match(string) # type: ignore - if regular_text_match is not None: - regular_text = regular_text_match.group(1) - regular_text_len = wcswidth(regular_text) - if string_len + regular_text_len > max_len: - string_parts.append(regular_text[: max_len - string_len]) - string_len = max_len - break - string_parts.append(regular_text) - string_len += regular_text_len - string = regular_text_match.group(2) - else: - ansi_escape_match = limit_string_with_escape_codes.ansi_escape_regex.match( # type: ignore - string - ) - if ansi_escape_match is not None: - # Adopt the ansi escape code but do not count its length - ansi_escape_code_text = ansi_escape_match.group(1) - string_parts.append(ansi_escape_code_text) - string = ansi_escape_match.group(2) - else: - # It looks like an escape code (starts with escape), but it is something else - # -> skip the escape character and continue the loop - string_parts.append("\x1B") - string = string[1:] - return "".join(string_parts), string_len - - num_cols = self._num_cols() - try: - preview_string = get_preview_string() - if preview_string is not None: - preview_string = strip_ansi_codes_except_styling(preview_string) - except PreviewCommandFailedError as e: - preview_string = "The preview command failed with error message:\n\n" + str(e) - self._tty_out.write(current_menu_height * self._codename_to_terminal_code["cursor_down"]) - if preview_string is not None: - self._tty_out.write(self._codename_to_terminal_code["cursor_down"] + "\r") - if self._preview_border: - self._tty_out.write( - ( - BoxDrawingCharacters.upper_left - + (2 * BoxDrawingCharacters.horizontal + " " + self._preview_title)[: num_cols - 3] - + " " - + (num_cols - len(self._preview_title) - 6) * BoxDrawingCharacters.horizontal - + BoxDrawingCharacters.upper_right - )[:num_cols] - + "\n" - ) - # `finditer` can be used as a generator version of `str.join` - for i, line in enumerate( - match.group(0) for match in re.finditer(r"^.*$", preview_string, re.MULTILINE) - ): - if i >= preview_max_num_lines - (2 if self._preview_border else 0): - preview_num_lines = preview_max_num_lines - break - limited_line, limited_line_len = limit_string_with_escape_codes( - line, num_cols - (3 if self._preview_border else 0) - ) - self._tty_out.write( - ( - ((BoxDrawingCharacters.vertical + " ") if self._preview_border else "") - + limited_line - + self._codename_to_terminal_code["reset_attributes"] - + max(num_cols - limited_line_len - (3 if self._preview_border else 0), 0) * " " - + (BoxDrawingCharacters.vertical if self._preview_border else "") - ) - ) - else: - preview_num_lines = i + (3 if self._preview_border else 1) - if self._preview_border: - self._tty_out.write( - "\n" - + ( - BoxDrawingCharacters.lower_left - + (num_cols - 2) * BoxDrawingCharacters.horizontal - + BoxDrawingCharacters.lower_right - )[:num_cols] - ) - self._tty_out.write("\r") - else: - preview_num_lines = 0 - self._tty_out.write( - (current_menu_height + preview_num_lines) * self._codename_to_terminal_code["cursor_up"] - ) - return preview_num_lines - - def delete_old_menu_lines(displayed_menu_height: int) -> None: - # pylint: disable=unsubscriptable-object - assert self._codename_to_terminal_code is not None - assert self._tty_out is not None - if ( - self._previous_displayed_menu_height is not None - and self._previous_displayed_menu_height > displayed_menu_height - ): - self._tty_out.write((displayed_menu_height + 1) * self._codename_to_terminal_code["cursor_down"]) - self._tty_out.write( - (self._previous_displayed_menu_height - displayed_menu_height) - * self._codename_to_terminal_code["delete_line"] - ) - self._tty_out.write((displayed_menu_height + 1) * self._codename_to_terminal_code["cursor_up"]) - - def position_cursor() -> None: - # pylint: disable=unsubscriptable-object - assert self._codename_to_terminal_code is not None - assert self._tty_out is not None - if self._view.active_displayed_index is None: - return - - cursor_width = wcswidth(self._menu_cursor) - for displayed_index in range(self._viewport.lower_index, self._viewport.upper_index + 1): - if displayed_index == self._view.active_displayed_index: - apply_style(self._menu_cursor_style) - self._tty_out.write(self._menu_cursor) - apply_style() - else: - self._tty_out.write(cursor_width * " ") - self._tty_out.write("\r") - if displayed_index < self._viewport.upper_index: - self._tty_out.write(self._codename_to_terminal_code["cursor_down"]) - self._tty_out.write((self._viewport.size - 1) * self._codename_to_terminal_code["cursor_up"]) - - def print_multi_select_column() -> None: - # pylint: disable=unsubscriptable-object - assert self._codename_to_terminal_code is not None - assert self._tty_out is not None - if not self._multi_select: - return - - def prepare_multi_select_cursors() -> Tuple[str, str]: - bracket_characters = "([{<)]}>" - bracket_style_escape_codes_io = io.StringIO() - multi_select_cursor_style_escape_codes_io = io.StringIO() - reset_codes_io = io.StringIO() - apply_style(self._multi_select_cursor_brackets_style, file=bracket_style_escape_codes_io) - apply_style(self._multi_select_cursor_style, file=multi_select_cursor_style_escape_codes_io) - apply_style(file=reset_codes_io) - bracket_style_escape_codes = bracket_style_escape_codes_io.getvalue() - multi_select_cursor_style_escape_codes = multi_select_cursor_style_escape_codes_io.getvalue() - reset_codes = reset_codes_io.getvalue() - - cursor_with_brackets_only = re.sub( - r"[^{}]".format(re.escape(bracket_characters)), " ", self._multi_select_cursor - ) - cursor_with_brackets_only_styled = re.sub( - r"[{}]+".format(re.escape(bracket_characters)), - lambda match_obj: bracket_style_escape_codes + match_obj.group(0) + reset_codes, - cursor_with_brackets_only, - ) - cursor_styled = re.sub( - r"[{brackets}]+|[^{brackets}\s]+".format(brackets=re.escape(bracket_characters)), - lambda match_obj: ( - bracket_style_escape_codes - if match_obj.group(0)[0] in bracket_characters - else multi_select_cursor_style_escape_codes - ) - + match_obj.group(0) - + reset_codes, - self._multi_select_cursor, - ) - return cursor_styled, cursor_with_brackets_only_styled - - if not self._view: - return - checked_multi_select_cursor, unchecked_multi_select_cursor = prepare_multi_select_cursors() - cursor_width = wcswidth(self._menu_cursor) - displayed_selected_indices = self._view.displayed_selected_indices - displayed_index = 0 - for displayed_index, _, _ in self._view: - self._tty_out.write("\r" + cursor_width * self._codename_to_terminal_code["cursor_right"]) - if displayed_index in self._skip_indices: - self._tty_out.write("") - elif displayed_index in displayed_selected_indices: - self._tty_out.write(checked_multi_select_cursor) - else: - self._tty_out.write(unchecked_multi_select_cursor) - if displayed_index < self._viewport.upper_index: - self._tty_out.write(self._codename_to_terminal_code["cursor_down"]) - self._tty_out.write("\r") - self._tty_out.write( - (displayed_index + (1 if displayed_index < self._viewport.upper_index else 0)) - * self._codename_to_terminal_code["cursor_up"] - ) - - # pylint: disable=unsubscriptable-object - assert self._codename_to_terminal_code is not None - assert self._tty_out is not None - displayed_menu_height = 0 # sum all written lines - status_bar_lines = get_status_bar_lines() - self._viewport.status_bar_lines_count = len(status_bar_lines) - if self._preview_command is not None: - self._viewport.preview_lines_count = int(self._preview_size * self._num_lines()) - preview_max_num_lines = self._viewport.preview_lines_count - self._viewport.keep_visible(self._view.active_displayed_index) - displayed_menu_height += print_menu_entries() - displayed_menu_height += print_search_line(displayed_menu_height) - if not self._status_bar_below_preview: - displayed_menu_height += print_status_bar(displayed_menu_height, status_bar_lines) - if self._preview_command is not None: - displayed_menu_height += print_preview(displayed_menu_height, preview_max_num_lines) - if self._status_bar_below_preview: - displayed_menu_height += print_status_bar(displayed_menu_height, status_bar_lines) - delete_old_menu_lines(displayed_menu_height) - position_cursor() - if self._multi_select: - print_multi_select_column() - self._previous_displayed_menu_height = displayed_menu_height - self._tty_out.flush() - - def _clear_menu(self) -> None: - # pylint: disable=unsubscriptable-object - assert self._codename_to_terminal_code is not None - assert self._previous_displayed_menu_height is not None - assert self._tty_out is not None - if self._clear_menu_on_exit: - if self._title_lines: - self._tty_out.write(len(self._title_lines) * self._codename_to_terminal_code["cursor_up"]) - self._tty_out.write(len(self._title_lines) * self._codename_to_terminal_code["delete_line"]) - self._tty_out.write( - (self._previous_displayed_menu_height + 1) * self._codename_to_terminal_code["delete_line"] - ) - else: - self._tty_out.write( - (self._previous_displayed_menu_height + 1) * self._codename_to_terminal_code["cursor_down"] - ) - self._tty_out.flush() - - def _read_next_key(self, ignore_case: bool = True) -> str: - # pylint: disable=unsubscriptable-object,unsupported-membership-test - assert self._terminal_code_to_codename is not None - assert self._tty_in is not None - # Needed for asynchronous handling of terminal resize events - self._reading_next_key = True - if self._paint_before_next_read: - self._paint_menu() - self._paint_before_next_read = False - # blocks until any amount of bytes is available - code = os.read(self._tty_in.fileno(), 80).decode("ascii", errors="ignore") - self._reading_next_key = False - if code in self._terminal_code_to_codename: - return self._terminal_code_to_codename[code] - elif ignore_case: - return code.lower() - else: - return code - - def show(self) -> Optional[Union[int, Tuple[int, ...]]]: - def init_signal_handling() -> None: - # `SIGWINCH` is send on terminal resizes - def handle_sigwinch(signum: signal.Signals, frame: FrameType) -> None: - # pylint: disable=unused-argument - if self._reading_next_key: - self._paint_menu() - else: - self._paint_before_next_read = True - - signal.signal(signal.SIGWINCH, handle_sigwinch) - - def reset_signal_handling() -> None: - signal.signal(signal.SIGWINCH, signal.SIG_DFL) - - def remove_letter_keys(menu_action_to_keys: Dict[str, Set[Optional[str]]]) -> None: - letter_keys = frozenset(string.ascii_lowercase) | frozenset(" ") - for keys in menu_action_to_keys.values(): - keys -= letter_keys - - # pylint: disable=unsubscriptable-object - assert self._codename_to_terminal_code is not None - self._init_term() - if self._preselected_indices is None: - self._selection.clear() - self._chosen_accept_key = None - self._chosen_menu_indices = None - self._chosen_menu_index = None - assert self._tty_out is not None - if self._title_lines: - # `print_menu` expects the cursor on the first menu item -> reserve one line for the title - self._tty_out.write(len(self._title_lines) * self._codename_to_terminal_code["cursor_down"]) - menu_was_interrupted = False - try: - init_signal_handling() - menu_action_to_keys = { - "menu_up": set(("up", "ctrl-k", "k")), - "menu_down": set(("down", "ctrl-j", "j")), - "accept": set(self._accept_keys), - "multi_select": set(self._multi_select_keys), - "quit": set(self._quit_keys), - "search_start": set((self._search_key,)), - "backspace": set(("backspace",)), - } # type: Dict[str, Set[Optional[str]]] - while True: - self._paint_menu() - current_menu_action_to_keys = copy.deepcopy(menu_action_to_keys) - next_key = self._read_next_key(ignore_case=False) - if self._search or self._search_key is None: - remove_letter_keys(current_menu_action_to_keys) - else: - next_key = next_key.lower() - if self._search_key is not None and not self._search and next_key in self._shortcut_keys: - shortcut_menu_index = self._shortcut_keys.index(next_key) - if self._exit_on_shortcut: - self._selection.add(shortcut_menu_index) - break - else: - if self._multi_select: - self._selection.toggle(shortcut_menu_index) - else: - self._view.active_menu_index = shortcut_menu_index - elif next_key in current_menu_action_to_keys["menu_up"]: - self._view.decrement_active_index() - elif next_key in current_menu_action_to_keys["menu_down"]: - self._view.increment_active_index() - elif self._multi_select and next_key in current_menu_action_to_keys["multi_select"]: - if self._view.active_menu_index is not None: - self._selection.toggle(self._view.active_menu_index) - elif next_key in current_menu_action_to_keys["accept"]: - if self._view.active_menu_index is not None: - if self._multi_select_select_on_accept or ( - not self._selection and self._multi_select_empty_ok is False - ): - self._selection.add(self._view.active_menu_index) - self._chosen_accept_key = next_key - break - elif next_key in current_menu_action_to_keys["quit"]: - if not self._search: - menu_was_interrupted = True - break - else: - self._search.search_text = None - elif not self._search: - if next_key in current_menu_action_to_keys["search_start"] or ( - self._search_key is None and next_key == DEFAULT_SEARCH_KEY - ): - self._search.search_text = "" - elif self._search_key is None: - self._search.search_text = next_key - else: - assert self._search.search_text is not None - if next_key in ("backspace",): - if self._search.search_text != "": - self._search.search_text = self._search.search_text[:-1] - else: - self._search.search_text = None - elif wcswidth(next_key) >= 0 and not ( - next_key in current_menu_action_to_keys["search_start"] and self._search.search_text == "" - ): - # 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 as e: - if self._raise_error_on_interrupt: - raise e - menu_was_interrupted = True - finally: - reset_signal_handling() - self._clear_menu() - self._reset_term() - if not menu_was_interrupted: - chosen_menu_indices = self._selection.selected_menu_indices - if chosen_menu_indices: - if self._multi_select: - self._chosen_menu_indices = chosen_menu_indices - else: - self._chosen_menu_index = chosen_menu_indices[0] - return self._chosen_menu_indices if self._multi_select else self._chosen_menu_index - - @property - def chosen_accept_key(self) -> Optional[str]: - return self._chosen_accept_key - - @property - def chosen_menu_entry(self) -> Optional[str]: - return self._menu_entries[self._chosen_menu_index] if self._chosen_menu_index is not None else None - - @property - def chosen_menu_entries(self) -> Optional[Tuple[str, ...]]: - return ( - tuple(self._menu_entries[menu_index] for menu_index in self._chosen_menu_indices) - if self._chosen_menu_indices is not None - else None - ) - - @property - def chosen_menu_index(self) -> Optional[int]: - return self._chosen_menu_index - - @property - def chosen_menu_indices(self) -> Optional[Tuple[int, ...]]: - return self._chosen_menu_indices - - -class AttributeDict(dict): # type: ignore - def __getattr__(self, attr: str) -> Any: - return self[attr] - - def __setattr__(self, attr: str, value: Any) -> None: - self[attr] = value - - -def get_argumentparser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser( - formatter_class=argparse.RawDescriptionHelpFormatter, - description=""" -%(prog)s creates simple interactive menus in the terminal and returns the selected entry as exit code. -""", - ) - parser.add_argument( - "-s", "--case-sensitive", action="store_true", dest="case_sensitive", help="searches are case sensitive" - ) - parser.add_argument( - "-X", - "--no-clear-menu-on-exit", - action="store_false", - dest="clear_menu_on_exit", - help="do not clear the menu on exit", - ) - parser.add_argument( - "-l", - "--clear-screen", - action="store_true", - dest="clear_screen", - help="clear the screen before the menu is shown", - ) - parser.add_argument( - "--cursor", - action="store", - dest="cursor", - default=DEFAULT_MENU_CURSOR, - help='menu cursor (default: "%(default)s")', - ) - parser.add_argument( - "-i", - "--cursor-index", - action="store", - dest="cursor_index", - type=int, - default=0, - help="initially selected item index", - ) - parser.add_argument( - "--cursor-style", - action="store", - dest="cursor_style", - default=",".join(DEFAULT_MENU_CURSOR_STYLE), - help='style for the menu cursor as comma separated list (default: "%(default)s")', - ) - parser.add_argument("-C", "--no-cycle", action="store_false", dest="cycle", help="do not cycle the menu selection") - parser.add_argument( - "-E", - "--no-exit-on-shortcut", - action="store_false", - dest="exit_on_shortcut", - help="do not exit on shortcut keys", - ) - parser.add_argument( - "--highlight-style", - action="store", - dest="highlight_style", - default=",".join(DEFAULT_MENU_HIGHLIGHT_STYLE), - help='style for the selected menu entry as comma separated list (default: "%(default)s")', - ) - parser.add_argument( - "-m", - "--multi-select", - action="store_true", - dest="multi_select", - help="Allow the selection of multiple entries (implies `--stdout`)", - ) - parser.add_argument( - "--multi-select-cursor", - action="store", - dest="multi_select_cursor", - default=DEFAULT_MULTI_SELECT_CURSOR, - help='multi-select menu cursor (default: "%(default)s")', - ) - parser.add_argument( - "--multi-select-cursor-brackets-style", - action="store", - dest="multi_select_cursor_brackets_style", - default=",".join(DEFAULT_MULTI_SELECT_CURSOR_BRACKETS_STYLE), - help='style for brackets of the multi-select menu cursor as comma separated list (default: "%(default)s")', - ) - parser.add_argument( - "--multi-select-cursor-style", - action="store", - dest="multi_select_cursor_style", - default=",".join(DEFAULT_MULTI_SELECT_CURSOR_STYLE), - help='style for the multi-select menu cursor as comma separated list (default: "%(default)s")', - ) - parser.add_argument( - "--multi-select-keys", - action="store", - dest="multi_select_keys", - default=",".join(DEFAULT_MULTI_SELECT_KEYS), - help=('key for toggling a selected item in a multi-selection (default: "%(default)s", '), - ) - parser.add_argument( - "--multi-select-no-select-on-accept", - action="store_false", - dest="multi_select_select_on_accept", - help=( - "do not select the currently highlighted menu item when the accept key is pressed " - "(it is still selected if no other item was selected before)" - ), - ) - parser.add_argument( - "--multi-select-empty-ok", - action="store_true", - dest="multi_select_empty_ok", - help=("when used together with --multi-select-no-select-on-accept allows returning no selection at all"), - ) - parser.add_argument( - "-p", - "--preview", - action="store", - dest="preview_command", - help=( - "Command to generate a preview for the selected menu entry. " - '"{}" can be used as placeholder for the menu text. ' - 'If the menu entry has a data component (separated by "|"), this is used instead.' - ), - ) - parser.add_argument( - "--no-preview-border", - action="store_false", - dest="preview_border", - help="do not draw a border around the preview window", - ) - parser.add_argument( - "--preview-size", - action="store", - dest="preview_size", - type=float, - default=DEFAULT_PREVIEW_SIZE, - help='maximum height of the preview window in fractions of the terminal height (default: "%(default)s")', - ) - parser.add_argument( - "--preview-title", - action="store", - dest="preview_title", - default=DEFAULT_PREVIEW_TITLE, - help='title of the preview window (default: "%(default)s")', - ) - parser.add_argument( - "--search-highlight-style", - action="store", - dest="search_highlight_style", - default=",".join(DEFAULT_SEARCH_HIGHLIGHT_STYLE), - help='style of matched search patterns (default: "%(default)s")', - ) - parser.add_argument( - "--search-key", - action="store", - dest="search_key", - default=DEFAULT_SEARCH_KEY, - help=( - 'key to start a search (default: "%(default)s", ' - '"none" is treated a special value which activates the search on any letter key)' - ), - ) - parser.add_argument( - "--shortcut-brackets-highlight-style", - action="store", - dest="shortcut_brackets_highlight_style", - default=",".join(DEFAULT_SHORTCUT_BRACKETS_HIGHLIGHT_STYLE), - help='style of brackets enclosing shortcut keys (default: "%(default)s")', - ) - parser.add_argument( - "--shortcut-key-highlight-style", - action="store", - dest="shortcut_key_highlight_style", - default=",".join(DEFAULT_SHORTCUT_KEY_HIGHLIGHT_STYLE), - help='style of shortcut keys (default: "%(default)s")', - ) - parser.add_argument( - "--show-multi-select-hint", - action="store_true", - dest="show_multi_select_hint", - help="show a multi-select hint in the status bar", - ) - parser.add_argument( - "--show-multi-select-hint-text", - action="store", - dest="show_multi_select_hint_text", - help=( - "Custom text which will be shown as multi-select hint. Use the placeholders {multi_select_keys} and " - "{accept_keys} if appropriately." - ), - ) - parser.add_argument( - "--show-search-hint", - action="store_true", - dest="show_search_hint", - help="show a search hint in the search line", - ) - parser.add_argument( - "--show-search-hint-text", - action="store", - dest="show_search_hint_text", - help=( - "Custom text which will be shown as search hint. Use the placeholders {key} for the search key " - "if appropriately." - ), - ) - parser.add_argument( - "--show-shortcut-hints", - action="store_true", - dest="show_shortcut_hints", - help="show shortcut hints in the status bar", - ) - parser.add_argument( - "--show-shortcut-hints-in-title", - action="store_false", - dest="show_shortcut_hints_in_status_bar", - default=True, - help="show shortcut hints in the menu title", - ) - parser.add_argument( - "--skip-empty-entries", - action="store_true", - dest="skip_empty_entries", - help="Interpret an empty string in menu entries as an empty menu entry", - ) - parser.add_argument( - "-b", - "--status-bar", - action="store", - dest="status_bar", - help="status bar text", - ) - parser.add_argument( - "-d", - "--status-bar-below-preview", - action="store_true", - dest="status_bar_below_preview", - help="show the status bar below the preview window if any", - ) - parser.add_argument( - "--status-bar-style", - action="store", - dest="status_bar_style", - default=",".join(DEFAULT_STATUS_BAR_STYLE), - help='style of the status bar lines (default: "%(default)s")', - ) - parser.add_argument( - "--stdout", - action="store_true", - dest="stdout", - help=( - "Print the selected menu index or indices to stdout (in addition to the exit status). " - 'Multiple indices are separated by ";".' - ), - ) - parser.add_argument("-t", "--title", action="store", dest="title", help="menu title") - parser.add_argument( - "-V", "--version", action="store_true", dest="print_version", help="print the version number and exit" - ) - parser.add_argument("entries", action="store", nargs="*", help="the menu entries to show") - group = parser.add_mutually_exclusive_group() - group.add_argument( - "-r", - "--preselected_entries", - action="store", - dest="preselected_entries", - help="Comma separated list of strings matching menu items to start pre-selected in a multi-select menu.", - ) - group.add_argument( - "-R", - "--preselected_indices", - action="store", - dest="preselected_indices", - help="Comma separated list of numeric indexes of menu items to start pre-selected in a multi-select menu.", - ) - return parser - - -def parse_arguments() -> AttributeDict: - parser = get_argumentparser() - args = AttributeDict({key: value for key, value in vars(parser.parse_args()).items()}) - if not args.print_version and not args.entries: - raise NoMenuEntriesError("No menu entries given!") - if args.skip_empty_entries: - args.entries = [entry if entry != "None" else None for entry in args.entries] - if args.cursor_style != "": - args.cursor_style = tuple(args.cursor_style.split(",")) - else: - args.cursor_style = None - if args.highlight_style != "": - args.highlight_style = tuple(args.highlight_style.split(",")) - else: - args.highlight_style = None - if args.search_highlight_style != "": - args.search_highlight_style = tuple(args.search_highlight_style.split(",")) - else: - args.search_highlight_style = None - if args.shortcut_key_highlight_style != "": - args.shortcut_key_highlight_style = tuple(args.shortcut_key_highlight_style.split(",")) - else: - args.shortcut_key_highlight_style = None - if args.shortcut_brackets_highlight_style != "": - args.shortcut_brackets_highlight_style = tuple(args.shortcut_brackets_highlight_style.split(",")) - else: - args.shortcut_brackets_highlight_style = None - if args.status_bar_style != "": - args.status_bar_style = tuple(args.status_bar_style.split(",")) - else: - args.status_bar_style = None - if args.multi_select_cursor_brackets_style != "": - args.multi_select_cursor_brackets_style = tuple(args.multi_select_cursor_brackets_style.split(",")) - else: - args.multi_select_cursor_brackets_style = None - if args.multi_select_cursor_style != "": - args.multi_select_cursor_style = tuple(args.multi_select_cursor_style.split(",")) - else: - args.multi_select_cursor_style = None - if args.multi_select_keys != "": - args.multi_select_keys = tuple(args.multi_select_keys.split(",")) - else: - args.multi_select_keys = None - if args.search_key.lower() == "none": - args.search_key = None - if args.show_shortcut_hints_in_status_bar: - args.show_shortcut_hints = True - if args.multi_select: - args.stdout = True - if args.preselected_entries is not None: - args.preselected = list(args.preselected_entries.split(",")) - elif args.preselected_indices is not None: - args.preselected = list(map(int, args.preselected_indices.split(","))) - else: - args.preselected = None - return args - - -def main() -> None: - try: - args = parse_arguments() - except SystemExit: - sys.exit(0) # Error code 0 is the error case in this program - except NoMenuEntriesError as e: - print(str(e), file=sys.stderr) - sys.exit(0) - if args.print_version: - print("{}, version {}".format(os.path.basename(sys.argv[0]), __version__)) - sys.exit(0) - try: - terminal_menu = TerminalMenu( - menu_entries=args.entries, - clear_menu_on_exit=args.clear_menu_on_exit, - clear_screen=args.clear_screen, - cursor_index=args.cursor_index, - cycle_cursor=args.cycle, - exit_on_shortcut=args.exit_on_shortcut, - menu_cursor=args.cursor, - menu_cursor_style=args.cursor_style, - menu_highlight_style=args.highlight_style, - multi_select=args.multi_select, - multi_select_cursor=args.multi_select_cursor, - multi_select_cursor_brackets_style=args.multi_select_cursor_brackets_style, - multi_select_cursor_style=args.multi_select_cursor_style, - multi_select_empty_ok=args.multi_select_empty_ok, - multi_select_keys=args.multi_select_keys, - multi_select_select_on_accept=args.multi_select_select_on_accept, - preselected_entries=args.preselected, - preview_border=args.preview_border, - preview_command=args.preview_command, - preview_size=args.preview_size, - preview_title=args.preview_title, - search_case_sensitive=args.case_sensitive, - search_highlight_style=args.search_highlight_style, - search_key=args.search_key, - shortcut_brackets_highlight_style=args.shortcut_brackets_highlight_style, - shortcut_key_highlight_style=args.shortcut_key_highlight_style, - show_multi_select_hint=args.show_multi_select_hint, - show_multi_select_hint_text=args.show_multi_select_hint_text, - show_search_hint=args.show_search_hint, - show_search_hint_text=args.show_search_hint_text, - show_shortcut_hints=args.show_shortcut_hints, - show_shortcut_hints_in_status_bar=args.show_shortcut_hints_in_status_bar, - skip_empty_entries=args.skip_empty_entries, - status_bar=args.status_bar, - status_bar_below_preview=args.status_bar_below_preview, - status_bar_style=args.status_bar_style, - title=args.title, - ) - except (InvalidParameterCombinationError, InvalidStyleError, UnknownMenuEntryError) as e: - print(str(e), file=sys.stderr) - sys.exit(0) - chosen_entries = terminal_menu.show() - if chosen_entries is None: - sys.exit(0) - else: - if isinstance(chosen_entries, Iterable): - if args.stdout: - print(",".join(str(entry + 1) for entry in chosen_entries)) - sys.exit(chosen_entries[0] + 1) - else: - chosen_entry = chosen_entries - if args.stdout: - print(chosen_entry + 1) - sys.exit(chosen_entry + 1) - - -if __name__ == "__main__": - main() diff --git a/archinstall/lib/menu/table_selection_menu.py b/archinstall/lib/menu/table_selection_menu.py index 09cd6ee2..4cff7216 100644 --- a/archinstall/lib/menu/table_selection_menu.py +++ b/archinstall/lib/menu/table_selection_menu.py @@ -1,19 +1,24 @@ -from typing import Any, Tuple, List, Dict, Optional +from typing import Any, Tuple, List, Dict, Optional, Callable -from .menu import MenuSelectionType, MenuSelection +from .menu import MenuSelectionType, MenuSelection, Menu from ..output import FormattedOutput -from ..menu import Menu class TableMenu(Menu): def __init__( self, title: str, - data: List[Any] = [], + data: Optional[List[Any]] = None, table_data: Optional[Tuple[List[Any], str]] = None, + preset: List[Any] = [], custom_menu_options: List[str] = [], default: Any = None, - multi: bool = False + multi: bool = False, + preview_command: Optional[Callable] = None, + preview_title: str = 'Info', + preview_size: float = 0.0, + allow_reset: bool = True, + allow_reset_warning_msg: Optional[str] = None, ): """ param title: Text that will be displayed above the menu @@ -29,10 +34,10 @@ class TableMenu(Menu): param custom_options: List of custom options that will be displayed under the table :type custom_menu_options: List - """ - if not data and not table_data: - raise ValueError('Either "data" or "table_data" must be provided') + :param preview_command: A function that should return a string that will be displayed in a preview window when a menu selection item is in focus + :type preview_command: Callable + """ self._custom_options = custom_menu_options self._multi = multi @@ -41,7 +46,7 @@ class TableMenu(Menu): else: header_padding = 2 - if len(data): + if data is not None: table_text = FormattedOutput.as_table(data) rows = table_text.split('\n') table = self._create_table(data, rows, header_padding=header_padding) @@ -53,20 +58,53 @@ class TableMenu(Menu): data = table_data[0] rows = table_data[1].split('\n') table = self._create_table(data, rows, header_padding=header_padding) + else: + raise ValueError('Either "data" or "table_data" must be provided') self._options, header = self._prepare_selection(table) + preset_values = self._preset_values(preset) + + extra_bottom_space = True if preview_command else False + super().__init__( title, self._options, + preset_values=preset_values, header=header, skip_empty_entries=True, show_search_hint=False, - allow_reset=True, multi=multi, - default_option=default + default_option=default, + preview_command=lambda x: self._table_show_preview(preview_command, x), + preview_size=preview_size, + preview_title=preview_title, + extra_bottom_space=extra_bottom_space, + allow_reset=allow_reset, + allow_reset_warning_msg=allow_reset_warning_msg ) + def _preset_values(self, preset: List[Any]) -> List[str]: + # when we create the table of just the preset values it will + # be formatted a bit different due to spacing, so to determine + # correct rows lets remove all the spaces and compare apples with apples + preset_table = FormattedOutput.as_table(preset).strip() + data_rows = preset_table.split('\n')[2:] # get all data rows + pure_data_rows = [self._escape_row(row.replace(' ', '')) for row in data_rows] + + # the actual preset value has to be in non-escaped form + pure_option_rows = {o.replace(' ', ''): self._unescape_row(o) for o in self._options.keys()} + preset_rows = [row for pure, row in pure_option_rows.items() if pure in pure_data_rows] + + return preset_rows + + def _table_show_preview(self, preview_command: Optional[Callable], selection: Any) -> Optional[str]: + if preview_command: + row = self._escape_row(selection) + obj = self._options[row] + return preview_command(obj) + return None + def run(self) -> MenuSelection: choice = super().run() @@ -79,6 +117,12 @@ class TableMenu(Menu): return choice + def _escape_row(self, row: str) -> str: + return row.replace('|', '\\|') + + def _unescape_row(self, row: str) -> str: + return row.replace('\\|', '|') + def _create_table(self, data: List[Any], rows: List[str], header_padding: int = 2) -> Dict[str, Any]: # these are the header rows of the table and do not map to any data obviously # we're adding 2 spaces as prefix because the menu selector '> ' will be put before @@ -87,7 +131,7 @@ class TableMenu(Menu): display_data = {f'{padding}{rows[0]}': None, f'{padding}{rows[1]}': None} for row, entry in zip(rows[2:], data): - row = row.replace('|', '\\|') + row = self._escape_row(row) display_data[row] = entry return display_data diff --git a/archinstall/lib/mirrors.py b/archinstall/lib/mirrors.py index d76e0473..4bae6d8b 100644 --- a/archinstall/lib/mirrors.py +++ b/archinstall/lib/mirrors.py @@ -2,7 +2,7 @@ import logging import pathlib import urllib.error import urllib.request -from typing import Union, Mapping, Iterable, Dict, Any, List +from typing import Union, Iterable, Dict, Any, List from .general import SysCommand from .output import log @@ -121,7 +121,7 @@ def insert_mirrors(mirrors :Dict[str, Any], *args :str, **kwargs :str) -> bool: def use_mirrors( - regions: Mapping[str, Iterable[str]], + regions: Dict[str, Iterable[str]], destination: str = '/etc/pacman.d/mirrorlist' ) -> None: log(f'A new package mirror-list has been created: {destination}', level=logging.INFO) diff --git a/archinstall/lib/models/__init__.py b/archinstall/lib/models/__init__.py index 4a018b2c..8cc49ea0 100644 --- a/archinstall/lib/models/__init__.py +++ b/archinstall/lib/models/__init__.py @@ -1 +1,4 @@ -from .network_configuration import NetworkConfiguration as NetworkConfiguration \ No newline at end of file +from .network_configuration import NetworkConfiguration, NicType, NetworkConfigurationHandler +from .bootloader import Bootloader +from .gen import VersionDef, PackageSearchResult, PackageSearch, LocalPackage +from .users import PasswordStrength, User diff --git a/archinstall/lib/models/bootloader.py b/archinstall/lib/models/bootloader.py new file mode 100644 index 00000000..38254c99 --- /dev/null +++ b/archinstall/lib/models/bootloader.py @@ -0,0 +1,40 @@ +from __future__ import annotations + +import logging +import sys +from enum import Enum +from typing import List + +from ..hardware import has_uefi +from ..output import log + + +class Bootloader(Enum): + Systemd = 'Systemd-boot' + Grub = 'Grub' + Efistub = 'Efistub' + + def json(self): + return self.value + + @classmethod + def values(cls) -> List[str]: + return [e.value for e in cls] + + @classmethod + def get_default(cls) -> Bootloader: + if has_uefi(): + return Bootloader.Systemd + else: + return Bootloader.Grub + + @classmethod + def from_arg(cls, bootloader: str) -> Bootloader: + # to support old configuration files + bootloader = bootloader.capitalize() + + if bootloader not in cls.values(): + values = ', '.join(cls.values()) + log(f'Invalid bootloader value "{bootloader}". Allowed values: {values}', level=logging.WARN) + sys.exit(1) + return Bootloader(bootloader) diff --git a/archinstall/lib/models/dataclasses.py b/archinstall/lib/models/dataclasses.py deleted file mode 100644 index 99221fe3..00000000 --- a/archinstall/lib/models/dataclasses.py +++ /dev/null @@ -1,136 +0,0 @@ -from dataclasses import dataclass -from typing import Optional, List - -@dataclass -class VersionDef: - version_string: str - - @classmethod - def parse_version(self) -> List[str]: - if '.' in self.version_string: - versions = self.version_string.split('.') - else: - versions = [self.version_string] - - return versions - - @classmethod - def major(self) -> str: - return self.parse_version()[0] - - @classmethod - def minor(self) -> str: - versions = self.parse_version() - if len(versions) >= 2: - return versions[1] - - @classmethod - def patch(self) -> str: - versions = self.parse_version() - if '-' in versions[-1]: - _, patch_version = versions[-1].split('-', 1) - return patch_version - - def __eq__(self, other :'VersionDef') -> bool: - if other.major == self.major and \ - other.minor == self.minor and \ - other.patch == self.patch: - - return True - return False - - def __lt__(self, other :'VersionDef') -> bool: - if self.major > other.major: - return False - elif self.minor and other.minor and self.minor > other.minor: - return False - elif self.patch and other.patch and self.patch > other.patch: - return False - - def __str__(self) -> str: - return self.version_string - -@dataclass -class PackageSearchResult: - pkgname: str - pkgbase: str - repo: str - arch: str - pkgver: str - pkgrel: str - epoch: int - pkgdesc: str - url: str - filename: str - compressed_size: int - installed_size: int - build_date: str - last_update: str - flag_date: Optional[str] - maintainers: List[str] - packager: str - groups: List[str] - licenses: List[str] - conflicts: List[str] - provides: List[str] - replaces: List[str] - depends: List[str] - optdepends: List[str] - makedepends: List[str] - checkdepends: List[str] - - @property - def pkg_version(self) -> str: - return self.pkgver - - def __eq__(self, other :'VersionDef') -> bool: - return self.pkg_version == other.pkg_version - - def __lt__(self, other :'VersionDef') -> bool: - return self.pkg_version < other.pkg_version - -@dataclass -class PackageSearch: - version: int - limit: int - valid: bool - num_pages: int - page: int - results: List[PackageSearchResult] - - def __post_init__(self): - self.results = [PackageSearchResult(**x) for x in self.results] - -@dataclass -class LocalPackage: - name: str - version: str - description:str - architecture: str - url: str - licenses: str - groups: str - depends_on: str - optional_deps: str - required_by: str - optional_for: str - conflicts_with: str - replaces: str - installed_size: str - packager: str - build_date: str - install_date: str - install_reason: str - install_script: str - validated_by: str - provides: str - - @property - def pkg_version(self) -> str: - return self.version - - def __eq__(self, other :'VersionDef') -> bool: - return self.pkg_version == other.pkg_version - - def __lt__(self, other :'VersionDef') -> bool: - return self.pkg_version < other.pkg_version \ No newline at end of file diff --git a/archinstall/lib/models/disk_encryption.py b/archinstall/lib/models/disk_encryption.py deleted file mode 100644 index a4a501d9..00000000 --- a/archinstall/lib/models/disk_encryption.py +++ /dev/null @@ -1,90 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass, field -from enum import Enum -from typing import Optional, List, Dict, TYPE_CHECKING, Any - -from ..hsm.fido import Fido2Device - -if TYPE_CHECKING: - _: Any - - -class EncryptionType(Enum): - Partition = 'partition' - - @classmethod - def _encryption_type_mapper(cls) -> Dict[str, 'EncryptionType']: - return { - # str(_('Full disk encryption')): EncryptionType.FullDiskEncryption, - str(_('Partition encryption')): EncryptionType.Partition - } - - @classmethod - def text_to_type(cls, text: str) -> 'EncryptionType': - mapping = cls._encryption_type_mapper() - return mapping[text] - - @classmethod - def type_to_text(cls, type_: 'EncryptionType') -> str: - mapping = cls._encryption_type_mapper() - type_to_text = {type_: text for text, type_ in mapping.items()} - return type_to_text[type_] - - -@dataclass -class DiskEncryption: - encryption_type: EncryptionType = EncryptionType.Partition - encryption_password: str = '' - partitions: Dict[str, List[Dict[str, Any]]] = field(default_factory=dict) - hsm_device: Optional[Fido2Device] = None - - @property - def all_partitions(self) -> List[Dict[str, Any]]: - _all: List[Dict[str, Any]] = [] - for parts in self.partitions.values(): - _all += parts - return _all - - def generate_encryption_file(self, partition) -> bool: - return partition in self.all_partitions and partition['mountpoint'] != '/' - - def json(self) -> Dict[str, Any]: - obj = { - 'encryption_type': self.encryption_type.value, - 'partitions': self.partitions - } - - if self.hsm_device: - obj['hsm_device'] = self.hsm_device.json() - - return obj - - @classmethod - def parse_arg( - cls, - disk_layout: Dict[str, Any], - arg: Dict[str, Any], - password: str = '' - ) -> 'DiskEncryption': - # we have to map the enc partition config to the disk layout objects - # they both need to point to the same object as it will get modified - # during the installation process - enc_partitions: Dict[str, List[Dict[str, Any]]] = {} - - for path, partitions in disk_layout.items(): - conf_partitions = arg['partitions'].get(path, []) - for part in partitions['partitions']: - if part in conf_partitions: - enc_partitions.setdefault(path, []).append(part) - - enc = DiskEncryption( - EncryptionType(arg['encryption_type']), - password, - enc_partitions - ) - - if hsm := arg.get('hsm_device', None): - enc.hsm_device = Fido2Device.parse_arg(hsm) - - return enc diff --git a/archinstall/lib/models/gen.py b/archinstall/lib/models/gen.py new file mode 100644 index 00000000..cc8d7605 --- /dev/null +++ b/archinstall/lib/models/gen.py @@ -0,0 +1,146 @@ +from dataclasses import dataclass +from typing import Optional, List + + +@dataclass +class VersionDef: + version_string: str + + @classmethod + def parse_version(cls) -> List[str]: + if '.' in cls.version_string: + versions = cls.version_string.split('.') + else: + versions = [cls.version_string] + + return versions + + @classmethod + def major(self) -> str: + return self.parse_version()[0] + + @classmethod + def minor(cls) -> Optional[str]: + versions = cls.parse_version() + if len(versions) >= 2: + return versions[1] + + return None + + @classmethod + def patch(cls) -> Optional[str]: + versions = cls.parse_version() + if '-' in versions[-1]: + _, patch_version = versions[-1].split('-', 1) + return patch_version + + return None + + def __eq__(self, other) -> bool: + if other.major == self.major and \ + other.minor == self.minor and \ + other.patch == self.patch: + + return True + return False + + def __lt__(self, other) -> bool: + if self.major() > other.major(): + return False + elif self.minor() and other.minor() and self.minor() > other.minor(): + return False + elif self.patch() and other.patch() and self.patch() > other.patch(): + return False + + return True + + def __str__(self) -> str: + return self.version_string + + +@dataclass +class PackageSearchResult: + pkgname: str + pkgbase: str + repo: str + arch: str + pkgver: str + pkgrel: str + epoch: int + pkgdesc: str + url: str + filename: str + compressed_size: int + installed_size: int + build_date: str + last_update: str + flag_date: Optional[str] + maintainers: List[str] + packager: str + groups: List[str] + licenses: List[str] + conflicts: List[str] + provides: List[str] + replaces: List[str] + depends: List[str] + optdepends: List[str] + makedepends: List[str] + checkdepends: List[str] + + @property + def pkg_version(self) -> str: + return self.pkgver + + def __eq__(self, other) -> bool: + return self.pkg_version == other.pkg_version + + def __lt__(self, other) -> bool: + return self.pkg_version < other.pkg_version + + +@dataclass +class PackageSearch: + version: int + limit: int + valid: bool + num_pages: int + page: int + results: List[PackageSearchResult] + + def __post_init__(self): + self.results = [PackageSearchResult(**x) for x in self.results] + + +@dataclass +class LocalPackage: + name: str + version: str + description:str + architecture: str + url: str + licenses: str + groups: str + depends_on: str + optional_deps: str + required_by: str + optional_for: str + conflicts_with: str + replaces: str + installed_size: str + packager: str + build_date: str + install_date: str + install_reason: str + install_script: str + validated_by: str + provides: str + + @property + def pkg_version(self) -> str: + return self.version + + def __eq__(self, other) -> bool: + return self.pkg_version == other.pkg_version + + def __lt__(self, other) -> bool: + return self.pkg_version < other.pkg_version diff --git a/archinstall/lib/models/network_configuration.py b/archinstall/lib/models/network_configuration.py index e026e97b..b7ab690d 100644 --- a/archinstall/lib/models/network_configuration.py +++ b/archinstall/lib/models/network_configuration.py @@ -93,7 +93,7 @@ class NetworkConfigurationHandler: enable_services=True) # Sources the ISO network configuration to the install medium. elif self._configuration.is_network_manager(): installation.add_additional_packages(["networkmanager"]) - if (profile := storage['arguments'].get('profile')) and profile.is_desktop_profile: + if (profile := storage['arguments'].get('profile_config')) and profile.is_desktop_type_profile: installation.add_additional_packages(["network-manager-applet"]) installation.enable_service('NetworkManager.service') diff --git a/archinstall/lib/models/password_strength.py b/archinstall/lib/models/password_strength.py deleted file mode 100644 index 61986bf0..00000000 --- a/archinstall/lib/models/password_strength.py +++ /dev/null @@ -1,85 +0,0 @@ -from enum import Enum - - -class PasswordStrength(Enum): - VERY_WEAK = 'very weak' - WEAK = 'weak' - MODERATE = 'moderate' - STRONG = 'strong' - - @property - def value(self): - match self: - case PasswordStrength.VERY_WEAK: return str(_('very weak')) - case PasswordStrength.WEAK: return str(_('weak')) - case PasswordStrength.MODERATE: return str(_('moderate')) - case PasswordStrength.STRONG: return str(_('strong')) - - def color(self): - match self: - case PasswordStrength.VERY_WEAK: return 'red' - case PasswordStrength.WEAK: return 'red' - case PasswordStrength.MODERATE: return 'yellow' - case PasswordStrength.STRONG: return 'green' - - @classmethod - def strength(cls, password: str) -> 'PasswordStrength': - digit = any(character.isdigit() for character in password) - upper = any(character.isupper() for character in password) - lower = any(character.islower() for character in password) - symbol = any(not character.isalnum() for character in password) - return cls._check_password_strength(digit, upper, lower, symbol, len(password)) - - @classmethod - def _check_password_strength( - cls, - digit: bool, - upper: bool, - lower: bool, - symbol: bool, - length: int - ) -> 'PasswordStrength': - # suggested evaluation - # https://github.com/archlinux/archinstall/issues/1304#issuecomment-1146768163 - if digit and upper and lower and symbol: - match length: - case num if 13 <= num: - return PasswordStrength.STRONG - case num if 11 <= num <= 12: - return PasswordStrength.MODERATE - case num if 7 <= num <= 10: - return PasswordStrength.WEAK - case num if num <= 6: - return PasswordStrength.VERY_WEAK - elif digit and upper and lower: - match length: - case num if 14 <= num: - return PasswordStrength.STRONG - case num if 11 <= num <= 13: - return PasswordStrength.MODERATE - case num if 7 <= num <= 10: - return PasswordStrength.WEAK - case num if num <= 6: - return PasswordStrength.VERY_WEAK - elif upper and lower: - match length: - case num if 15 <= num: - return PasswordStrength.STRONG - case num if 12 <= num <= 14: - return PasswordStrength.MODERATE - case num if 7 <= num <= 11: - return PasswordStrength.WEAK - case num if num <= 6: - return PasswordStrength.VERY_WEAK - elif lower or upper: - match length: - case num if 18 <= num: - return PasswordStrength.STRONG - case num if 14 <= num <= 17: - return PasswordStrength.MODERATE - case num if 9 <= num <= 13: - return PasswordStrength.WEAK - case num if num <= 8: - return PasswordStrength.VERY_WEAK - - return PasswordStrength.VERY_WEAK diff --git a/archinstall/lib/models/pydantic.py b/archinstall/lib/models/pydantic.py deleted file mode 100644 index 799e92af..00000000 --- a/archinstall/lib/models/pydantic.py +++ /dev/null @@ -1,134 +0,0 @@ -from typing import Optional, List -from pydantic import BaseModel - -""" -This python file is not in use. -Pydantic is not a builtin, and we use the dataclasses.py instead! -""" - -class VersionDef(BaseModel): - version_string: str - - @classmethod - def parse_version(self) -> List[str]: - if '.' in self.version_string: - versions = self.version_string.split('.') - else: - versions = [self.version_string] - - return versions - - @classmethod - def major(self) -> str: - return self.parse_version()[0] - - @classmethod - def minor(self) -> str: - versions = self.parse_version() - if len(versions) >= 2: - return versions[1] - - @classmethod - def patch(self) -> str: - versions = self.parse_version() - if '-' in versions[-1]: - _, patch_version = versions[-1].split('-', 1) - return patch_version - - def __eq__(self, other :'VersionDef') -> bool: - if other.major == self.major and \ - other.minor == self.minor and \ - other.patch == self.patch: - - return True - return False - - def __lt__(self, other :'VersionDef') -> bool: - if self.major > other.major: - return False - elif self.minor and other.minor and self.minor > other.minor: - return False - elif self.patch and other.patch and self.patch > other.patch: - return False - - def __str__(self) -> str: - return self.version_string - - -class PackageSearchResult(BaseModel): - pkgname: str - pkgbase: str - repo: str - arch: str - pkgver: str - pkgrel: str - epoch: int - pkgdesc: str - url: str - filename: str - compressed_size: int - installed_size: int - build_date: str - last_update: str - flag_date: Optional[str] - maintainers: List[str] - packager: str - groups: List[str] - licenses: List[str] - conflicts: List[str] - provides: List[str] - replaces: List[str] - depends: List[str] - optdepends: List[str] - makedepends: List[str] - checkdepends: List[str] - - @property - def pkg_version(self) -> str: - return self.pkgver - - def __eq__(self, other :'VersionDef') -> bool: - return self.pkg_version == other.pkg_version - - def __lt__(self, other :'VersionDef') -> bool: - return self.pkg_version < other.pkg_version - - -class PackageSearch(BaseModel): - version: int - limit: int - valid: bool - results: List[PackageSearchResult] - - -class LocalPackage(BaseModel): - name: str - version: str - description:str - architecture: str - url: str - licenses: str - groups: str - depends_on: str - optional_deps: str - required_by: str - optional_for: str - conflicts_with: str - replaces: str - installed_size: str - packager: str - build_date: str - install_date: str - install_reason: str - install_script: str - validated_by: str - - @property - def pkg_version(self) -> str: - return self.version - - def __eq__(self, other :'VersionDef') -> bool: - return self.pkg_version == other.pkg_version - - def __lt__(self, other :'VersionDef') -> bool: - return self.pkg_version < other.pkg_version \ No newline at end of file diff --git a/archinstall/lib/models/subvolume.py b/archinstall/lib/models/subvolume.py deleted file mode 100644 index 34a09227..00000000 --- a/archinstall/lib/models/subvolume.py +++ /dev/null @@ -1,68 +0,0 @@ -from dataclasses import dataclass -from typing import List, Any, Dict - - -@dataclass -class Subvolume: - name: str - mountpoint: str - compress: bool = False - nodatacow: bool = False - - def display(self) -> str: - options_str = ','.join(self.options) - return f'{_("Subvolume")}: {self.name:15} {_("Mountpoint")}: {self.mountpoint:20} {_("Options")}: {options_str}' - - @property - def options(self) -> List[str]: - options = [ - 'compress' if self.compress else '', - 'nodatacow' if self.nodatacow else '' - ] - return [o for o in options if len(o)] - - def json(self) -> Dict[str, Any]: - return { - 'name': self.name, - 'mountpoint': self.mountpoint, - 'compress': self.compress, - 'nodatacow': self.nodatacow - } - - @classmethod - def _parse(cls, config_subvolumes: List[Dict[str, Any]]) -> List['Subvolume']: - subvolumes = [] - for entry in config_subvolumes: - if not entry.get('name', None) or not entry.get('mountpoint', None): - continue - - subvolumes.append( - Subvolume( - entry['name'], - entry['mountpoint'], - entry.get('compress', False), - entry.get('nodatacow', False) - ) - ) - - return subvolumes - - @classmethod - def _parse_backwards_compatible(cls, config_subvolumes) -> List['Subvolume']: - subvolumes = [] - for name, mountpoint in config_subvolumes.items(): - if not name or not mountpoint: - continue - - subvolumes.append(Subvolume(name, mountpoint)) - - return subvolumes - - @classmethod - def parse_arguments(cls, config_subvolumes: Any) -> List['Subvolume']: - if isinstance(config_subvolumes, list): - return cls._parse(config_subvolumes) - elif isinstance(config_subvolumes, dict): - return cls._parse_backwards_compatible(config_subvolumes) - - raise ValueError('Unknown disk layout btrfs subvolume format') diff --git a/archinstall/lib/models/users.py b/archinstall/lib/models/users.py index a8feb9ef..9ed70eef 100644 --- a/archinstall/lib/models/users.py +++ b/archinstall/lib/models/users.py @@ -1,12 +1,95 @@ from dataclasses import dataclass from typing import Dict, List, Union, Any, TYPE_CHECKING - -from .password_strength import PasswordStrength +from enum import Enum if TYPE_CHECKING: _: Any +class PasswordStrength(Enum): + VERY_WEAK = 'very weak' + WEAK = 'weak' + MODERATE = 'moderate' + STRONG = 'strong' + + @property + def value(self): + match self: + case PasswordStrength.VERY_WEAK: return str(_('very weak')) + case PasswordStrength.WEAK: return str(_('weak')) + case PasswordStrength.MODERATE: return str(_('moderate')) + case PasswordStrength.STRONG: return str(_('strong')) + + def color(self): + match self: + case PasswordStrength.VERY_WEAK: return 'red' + case PasswordStrength.WEAK: return 'red' + case PasswordStrength.MODERATE: return 'yellow' + case PasswordStrength.STRONG: return 'green' + + @classmethod + def strength(cls, password: str) -> 'PasswordStrength': + digit = any(character.isdigit() for character in password) + upper = any(character.isupper() for character in password) + lower = any(character.islower() for character in password) + symbol = any(not character.isalnum() for character in password) + return cls._check_password_strength(digit, upper, lower, symbol, len(password)) + + @classmethod + def _check_password_strength( + cls, + digit: bool, + upper: bool, + lower: bool, + symbol: bool, + length: int + ) -> 'PasswordStrength': + # suggested evaluation + # https://github.com/archlinux/archinstall/issues/1304#issuecomment-1146768163 + if digit and upper and lower and symbol: + match length: + case num if 13 <= num: + return PasswordStrength.STRONG + case num if 11 <= num <= 12: + return PasswordStrength.MODERATE + case num if 7 <= num <= 10: + return PasswordStrength.WEAK + case num if num <= 6: + return PasswordStrength.VERY_WEAK + elif digit and upper and lower: + match length: + case num if 14 <= num: + return PasswordStrength.STRONG + case num if 11 <= num <= 13: + return PasswordStrength.MODERATE + case num if 7 <= num <= 10: + return PasswordStrength.WEAK + case num if num <= 6: + return PasswordStrength.VERY_WEAK + elif upper and lower: + match length: + case num if 15 <= num: + return PasswordStrength.STRONG + case num if 12 <= num <= 14: + return PasswordStrength.MODERATE + case num if 7 <= num <= 11: + return PasswordStrength.WEAK + case num if num <= 6: + return PasswordStrength.VERY_WEAK + elif lower or upper: + match length: + case num if 18 <= num: + return PasswordStrength.STRONG + case num if 14 <= num <= 17: + return PasswordStrength.MODERATE + case num if 9 <= num <= 13: + return PasswordStrength.WEAK + case num if num <= 8: + return PasswordStrength.VERY_WEAK + + return PasswordStrength.VERY_WEAK + + @dataclass class User: username: str @@ -26,13 +109,6 @@ class User: 'sudo': self.sudo } - def display(self) -> str: - password = '*' * (len(self.password) if self.password else 0) - if password: - strength = PasswordStrength.strength(self.password) - password += f' ({strength.value})' - return f'{_("Username")}: {self.username:16} {_("Password")}: {password:20} sudo: {str(self.sudo)}' - @classmethod def _parse(cls, config_users: List[Dict[str, Any]]) -> List['User']: users = [] diff --git a/archinstall/lib/networking.py b/archinstall/lib/networking.py index 96e8f3a1..3516aac4 100644 --- a/archinstall/lib/networking.py +++ b/archinstall/lib/networking.py @@ -1,8 +1,12 @@ import logging import os import socket +import ssl import struct -from typing import Union, Dict, Any, List +from typing import Union, Dict, Any, List, Optional +from urllib.error import URLError +from urllib.parse import urlencode +from urllib.request import urlopen from .exceptions import HardwareIncompatibilityError, SysCallError from .general import SysCommand @@ -39,7 +43,7 @@ def check_mirror_reachable() -> bool: elif os.geteuid() != 0: log("check_mirror_reachable() uses 'pacman -Sy' which requires root.", level=logging.ERROR, fg="red") except SysCallError as err: - log(err, level=logging.DEBUG) + log(f'exit_code: {err.exit_code}, Error: {err.message}', level=logging.DEBUG) return False @@ -75,12 +79,8 @@ def enrich_iface_types(interfaces: Union[Dict[str, Any], List[str]]) -> Dict[str return result -def get_interface_from_mac(mac :str) -> str: - return list_interfaces().get(mac.lower(), None) - - def wireless_scan(interface :str) -> None: - interfaces = enrich_iface_types(list_interfaces().values()) + interfaces = enrich_iface_types(list(list_interfaces().values())) if interfaces[interface] != 'WIRELESS': raise HardwareIncompatibilityError(f"Interface {interface} is not a wireless interface: {interfaces}") @@ -107,3 +107,22 @@ def get_wireless_networks(interface :str) -> None: for line in SysCommand(f"iwctl station {interface} get-networks"): print(line) + + +def fetch_data_from_url(url: str, params: Optional[Dict] = None) -> str: + ssl_context = ssl.create_default_context() + ssl_context.check_hostname = False + ssl_context.verify_mode = ssl.CERT_NONE + + if params is not None: + encoded = urlencode(params) + full_url = f'{url}?{encoded}' + else: + full_url = url + + try: + response = urlopen(full_url, context=ssl_context) + data = response.read().decode('UTF-8') + return data + except URLError: + raise ValueError(f'Unable to fetch data from url: {url}') diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py index 709a7382..d65f835f 100644 --- a/archinstall/lib/output.py +++ b/archinstall/lib/output.py @@ -2,7 +2,7 @@ import logging import os import sys from pathlib import Path -from typing import Dict, Union, List, Any, Callable +from typing import Dict, Union, List, Any, Callable, Optional from .storage import storage from dataclasses import asdict, is_dataclass @@ -11,7 +11,12 @@ from dataclasses import asdict, is_dataclass class FormattedOutput: @classmethod - def values(cls, o: Any, class_formatter: str = None, filter_list: List[str] = None) -> Dict[str, Any]: + def values( + cls, + o: Any, + class_formatter: Optional[Union[str, Callable]] = None, + filter_list: List[str] = [] + ) -> Dict[str, Any]: """ the original values returned a dataclass as dict thru the call to some specific methods this version allows thru the parameter class_formatter to call a dynamicly selected formatting method. Can transmit a filter list to the class_formatter, @@ -25,7 +30,8 @@ class FormattedOutput: elif hasattr(o, class_formatter) and callable(getattr(o, class_formatter)): func = getattr(o, class_formatter) return func(filter_list) - # kept as to make it backward compatible + + raise ValueError('Unsupported formatting call') elif hasattr(o, 'as_json'): return o.as_json() elif hasattr(o, 'json'): @@ -36,7 +42,13 @@ class FormattedOutput: return o.__dict__ @classmethod - def as_table(cls, obj: List[Any], class_formatter: Union[str, Callable] = None, filter_list: List[str] = None) -> str: + def as_table( + cls, + obj: List[Any], + class_formatter: Optional[Union[str, Callable]] = None, + filter_list: List[str] = [], + capitalize: bool = False + ) -> str: """ variant of as_table (subtly different code) which has two additional parameters filter which is a list of fields which will be shon class_formatter a special method to format the outgoing data @@ -46,6 +58,7 @@ class FormattedOutput: As_table_filter can be a drop in replacement for as_table """ raw_data = [cls.values(o, class_formatter, filter_list) for o in obj] + # determine the maximum column size column_width: Dict[str, int] = {} for o in raw_data: @@ -55,14 +68,20 @@ class FormattedOutput: column_width[k] = max([column_width[k], len(str(v)), len(k)]) if not filter_list: - filter_list = (column_width.keys()) + filter_list = list(column_width.keys()) + # create the header lines output = '' key_list = [] for key in filter_list: width = column_width[key] - key = key.replace('!', '') + key = key.replace('!', '').replace('_', ' ') + + if capitalize: + key = key.capitalize() + key_list.append(key.ljust(width)) + output += ' | '.join(key_list) + '\n' output += '-' * len(output) + '\n' @@ -82,6 +101,20 @@ class FormattedOutput: return output + @classmethod + def as_columns(cls, entries: List[str], cols: int) -> str: + chunks = [] + output = '' + + for i in range(0, len(entries), cols): + chunks.append(entries[i:i + cols]) + + for row in chunks: + out_fmt = '{: <30} ' * len(row) + output += out_fmt.format(*row) + '\n' + + return output + class Journald: @staticmethod @@ -204,6 +237,6 @@ def log(*args :str, **kwargs :Union[str, int, Dict[str, Union[str, int]]]) -> No # Finally, print the log unless we skipped it based on level. # We use sys.stdout.write()+flush() instead of print() to try and # fix issue #94 - if kwargs.get('level', logging.INFO) != logging.DEBUG or storage['arguments'].get('verbose', False): + if kwargs.get('level', logging.INFO) != logging.DEBUG or storage.get('arguments', {}).get('verbose', False): sys.stdout.write(f"{string}\n") sys.stdout.flush() diff --git a/archinstall/lib/packages/__init__.py b/archinstall/lib/packages/__init__.py index e69de29b..e2aab577 100644 --- a/archinstall/lib/packages/__init__.py +++ b/archinstall/lib/packages/__init__.py @@ -0,0 +1,4 @@ +from .packages import ( + group_search, package_search, find_package, + find_packages, validate_package_list, installed_package +) diff --git a/archinstall/lib/packages/packages.py b/archinstall/lib/packages/packages.py index 0743e83b..71818ca5 100644 --- a/archinstall/lib/packages/packages.py +++ b/archinstall/lib/packages/packages.py @@ -7,7 +7,7 @@ from urllib.parse import urlencode from urllib.request import urlopen from ..exceptions import PackageError, SysCallError -from ..models.dataclasses import PackageSearch, PackageSearchResult, LocalPackage +from ..models.gen import PackageSearch, PackageSearchResult, LocalPackage from ..pacman import run_pacman BASE_URL_PKG_SEARCH = 'https://archlinux.org/packages/search/json/' @@ -113,4 +113,4 @@ def installed_package(package :str) -> LocalPackage: except SysCallError: pass - return LocalPackage({field.name: package_info.get(field.name) for field in dataclasses.fields(LocalPackage)}) + return LocalPackage({field.name: package_info.get(field.name) for field in dataclasses.fields(LocalPackage)}) # type: ignore diff --git a/archinstall/lib/pacman.py b/archinstall/lib/pacman.py index 9c427aff..0dfd5afa 100644 --- a/archinstall/lib/pacman.py +++ b/archinstall/lib/pacman.py @@ -1,10 +1,14 @@ import logging import pathlib import time +from typing import TYPE_CHECKING, Any from .general import SysCommand from .output import log +if TYPE_CHECKING: + _: Any + def run_pacman(args :str, default_cmd :str = 'pacman') -> SysCommand: """ diff --git a/archinstall/lib/profile/__init__.py b/archinstall/lib/profile/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/archinstall/lib/profile/profile_menu.py b/archinstall/lib/profile/profile_menu.py new file mode 100644 index 00000000..6462685a --- /dev/null +++ b/archinstall/lib/profile/profile_menu.py @@ -0,0 +1,203 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Optional, Dict + +from archinstall.default_profiles.profile import Profile, GreeterType +from .profile_model import ProfileConfiguration +from ..hardware import AVAILABLE_GFX_DRIVERS +from ..menu import Menu, MenuSelectionType, AbstractSubMenu, Selector +from ..user_interaction.system_conf import select_driver + +if TYPE_CHECKING: + _: Any + + +class ProfileMenu(AbstractSubMenu): + def __init__( + self, + data_store: Dict[str, Any], + preset: Optional[ProfileConfiguration] = None + ): + if preset: + self._preset = preset + else: + self._preset = ProfileConfiguration() + + super().__init__(data_store=data_store) + + def setup_selection_menu_options(self): + self._menu_options['profile'] = Selector( + _('Profile'), + lambda x: self._select_profile(x), + display_func=lambda x: x.name if x else None, + preview_func=self._preview_profile, + default=self._preset.profile, + enabled=True + ) + + self._menu_options['gfx_driver'] = Selector( + _('Graphics driver'), + lambda preset: self._select_gfx_driver(preset), + display_func=lambda x: x if x else None, + dependencies=['profile'], + default=self._preset.gfx_driver if self._preset.profile and self._preset.profile.is_graphic_driver_supported() else None, + enabled=self._preset.profile.is_graphic_driver_supported() if self._preset.profile else False + ) + + self._menu_options['greeter'] = Selector( + _('Greeter'), + lambda preset: select_greeter(self._menu_options['profile'].current_selection, preset), + display_func=lambda x: x.value if x else None, + dependencies=['profile'], + default=self._preset.greeter if self._preset.profile and self._preset.profile.is_greeter_supported() else None, + enabled=self._preset.profile.is_greeter_supported() if self._preset.profile else False + ) + + def run(self, allow_reset: bool = True) -> Optional[ProfileConfiguration]: + super().run(allow_reset=allow_reset) + + if self._data_store.get('profile', None): + return ProfileConfiguration( + self._menu_options['profile'].current_selection, + self._menu_options['gfx_driver'].current_selection, + self._menu_options['greeter'].current_selection + ) + + return None + + def _select_profile(self, preset: Optional[Profile]) -> Optional[Profile]: + profile = select_profile(preset) + if profile is not None: + if not profile.is_graphic_driver_supported(): + self._menu_options['gfx_driver'].set_enabled(False) + self._menu_options['gfx_driver'].set_current_selection(None) + else: + self._menu_options['gfx_driver'].set_enabled(True) + self._menu_options['gfx_driver'].set_current_selection('All open-source (default)') + + if not profile.is_greeter_supported(): + self._menu_options['greeter'].set_enabled(False) + self._menu_options['greeter'].set_current_selection(None) + else: + self._menu_options['greeter'].set_enabled(True) + self._menu_options['greeter'].set_current_selection(profile.default_greeter_type) + else: + self._menu_options['gfx_driver'].set_current_selection(None) + self._menu_options['greeter'].set_current_selection(None) + + return profile + + def _select_gfx_driver(self, preset: Optional[str] = None) -> Optional[str]: + driver = preset + profile: Optional[Profile] = self._menu_options['profile'].current_selection + + if profile: + if profile.is_graphic_driver_supported(): + driver = select_driver(current_value=preset) + + if driver and 'Sway' in profile.current_selection_names(): + packages = AVAILABLE_GFX_DRIVERS[driver] + + if packages and "nvidia" in packages: + prompt = str( + _('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(), skip=False).run() + + if choice.value == Menu.no(): + return None + + return driver + + def _preview_profile(self) -> Optional[str]: + profile: Optional[Profile] = self._menu_options['profile'].current_selection + + if profile: + names = profile.current_selection_names() + return '\n'.join(names) + + return None + + +def select_greeter( + profile: Optional[Profile] = None, + preset: Optional[GreeterType] = None +) -> Optional[GreeterType]: + if not profile or profile.is_greeter_supported(): + title = str(_('Please chose which greeter to install')) + greeter_options = [greeter.value for greeter in GreeterType] + + default: Optional[GreeterType] = None + + if preset is not None: + default = preset + elif profile is not None: + default_greeter = profile.default_greeter_type + default = default_greeter if default_greeter else None + + choice = Menu( + title, + greeter_options, + skip=True, + default_option=default.value if default else None + ).run() + + match choice.type_: + case MenuSelectionType.Skip: + return default + + return GreeterType(choice.single_value) + + return None + + +def select_profile( + current_profile: Optional[Profile] = None, + title: Optional[str] = None, + allow_reset: bool = True, + multi: bool = False +) -> Optional[Profile]: + from archinstall.lib.profile.profiles_handler import profile_handler + top_level_profiles = profile_handler.get_top_level_profiles() + + display_title = title + if not display_title: + display_title = str(_('This is a list of pre-programmed default_profiles')) + + choice = profile_handler.select_profile( + top_level_profiles, + current_profile=current_profile, + title=display_title, + allow_reset=allow_reset, + multi=multi + ) + + match choice.type_: + case MenuSelectionType.Selection: + profile_selection: Profile = choice.single_value + select_result = profile_selection.do_on_select() + + if not select_result: + return select_profile( + current_profile=current_profile, + title=title, + allow_reset=allow_reset, + multi=multi + ) + + # we're going to reset the currently selected profile(s) to avoid + # any stale data laying around + match select_result: + case select_result.NewSelection: + profile_handler.reset_top_level_profiles(exclude=[profile_selection]) + current_profile = profile_selection + case select_result.ResetCurrent: + profile_handler.reset_top_level_profiles() + current_profile = None + case select_result.SameSelection: + pass + + return current_profile + case MenuSelectionType.Reset: + return None + case MenuSelectionType.Skip: + return current_profile diff --git a/archinstall/lib/profile/profile_model.py b/archinstall/lib/profile/profile_model.py new file mode 100644 index 00000000..ad3015ae --- /dev/null +++ b/archinstall/lib/profile/profile_model.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, Optional, Dict + +from archinstall.default_profiles.profile import Profile, GreeterType + +if TYPE_CHECKING: + _: Any + + +@dataclass +class ProfileConfiguration: + profile: Optional[Profile] = None + gfx_driver: Optional[str] = None + greeter: Optional[GreeterType] = None + + def json(self) -> Dict[str, Any]: + from .profiles_handler import profile_handler + return { + 'profile': profile_handler.to_json(self.profile), + 'gfx_driver': self.gfx_driver, + 'greeter': self.greeter.value if self.greeter else None + } + + @classmethod + def parse_arg(cls, arg: Dict[str, Any]) -> 'ProfileConfiguration': + from .profiles_handler import profile_handler + greeter = arg.get('greeter', None) + + return ProfileConfiguration( + profile_handler.parse_profile_config(arg['profile']), + arg.get('gfx_driver', None), + GreeterType(greeter) if greeter else None + ) diff --git a/archinstall/lib/profile/profiles_handler.py b/archinstall/lib/profile/profiles_handler.py new file mode 100644 index 00000000..063b12ea --- /dev/null +++ b/archinstall/lib/profile/profiles_handler.py @@ -0,0 +1,391 @@ +from __future__ import annotations + +import importlib.util +import logging +import sys +from collections import Counter +from functools import cached_property +from pathlib import Path +from tempfile import NamedTemporaryFile +from types import ModuleType +from typing import List, TYPE_CHECKING, Any, Optional, Dict, Union + +from archinstall.default_profiles.profile import Profile, TProfile, GreeterType +from .profile_model import ProfileConfiguration +from ..hardware import AVAILABLE_GFX_DRIVERS +from ..menu import MenuSelectionType, Menu, MenuSelection +from ..networking import list_interfaces, fetch_data_from_url +from ..output import log +from ..storage import storage + +if TYPE_CHECKING: + from ..installer import Installer + _: Any + + +class ProfileHandler: + def __init__(self): + self._profiles_path: Path = storage['PROFILE'] + self._profiles = None + + # special variable to keep track of a profile url configuration + # it is merely used to be able to export the path again when a user + # wants to save the configuration + self._url_path = None + + def to_json(self, profile: Optional[Profile]) -> Dict[str, Any]: + """ + Serialize the selected profile setting to JSON + """ + data: Dict[str, Any] = {} + + if profile is not None: + data = { + 'main': profile.name, + 'details': [profile.name for profile in profile.current_selection], + } + + if self._url_path is not None: + data['path'] = self._url_path + + return data + + def parse_profile_config(self, profile_config: Dict[str, Any]) -> Optional[Profile]: + """ + Deserialize JSON configuration + """ + profile = None + + # the order of these is important, we want to + # load all the default_profiles from url and custom + # so that we can then apply whatever was specified + # in the main/detail sections + if url_path := profile_config.get('path', None): + self._url_path = url_path + local_path = Path(url_path) + + if local_path.is_file(): + profiles = self._process_profile_file(local_path) + self.remove_custom_profiles(profiles) + self.add_custom_profiles(profiles) + else: + self._import_profile_from_url(url_path) + + if custom := profile_config.get('custom', None): + from archinstall.default_profiles.custom import CustomTypeProfile + custom_types = [] + + for entry in custom: + custom_types.append( + CustomTypeProfile( + entry['name'], + entry['enabled'], + entry.get('packages', []), + entry.get('services', []) + ) + ) + + self.remove_custom_profiles(custom_types) + self.add_custom_profiles(custom_types) + + # this doesn't mean it's actual going to be set as a selection + # but we are simply populating the custom profile with all + # possible custom definitions + if custom_profile := self.get_profile_by_name('Custom'): + custom_profile.set_current_selection(custom_types) + + if main := profile_config.get('main', None): + profile = self.get_profile_by_name(main) if main else None + + valid: List[Profile] = [] + if details := profile_config.get('details', []): + resolved = {detail: self.get_profile_by_name(detail) for detail in details if detail} + valid = [p for p in resolved.values() if p is not None] + invalid = ', '.join([k for k, v in resolved.items() if v is None]) + + if invalid: + log(f'No profile definition found: {invalid}') + + if profile is not None: + profile.set_current_selection(valid) + + return profile + + @property + def profiles(self) -> List[Profile]: + """ + List of all available default_profiles + """ + if self._profiles is None: + self._profiles = self._find_available_profiles() + return self._profiles + + @cached_property + def _local_mac_addresses(self) -> List[str]: + ifaces = list_interfaces() + return list(ifaces.keys()) + + def add_custom_profiles(self, profiles: Union[TProfile, List[TProfile]]): + if not isinstance(profiles, list): + profiles = [profiles] + + for profile in profiles: + self._profiles.append(profile) + + self._verify_unique_profile_names(self._profiles) + + def remove_custom_profiles(self, profiles: Union[TProfile, List[TProfile]]): + if not isinstance(profiles, list): + profiles = [profiles] + + remove_names = [p.name for p in profiles] + self._profiles = [p for p in self._profiles if p.name not in remove_names] + + def get_profile_by_name(self, name: str) -> Optional[Profile]: + return next(filter(lambda x: x.name == name, self.profiles), None) # type: ignore + + def get_top_level_profiles(self) -> List[Profile]: + return list(filter(lambda x: x.is_top_level_profile(), self.profiles)) + + def get_server_profiles(self) -> List[Profile]: + return list(filter(lambda x: x.is_server_type_profile(), self.profiles)) + + def get_desktop_profiles(self) -> List[Profile]: + return list(filter(lambda x: x.is_desktop_type_profile(), self.profiles)) + + def get_custom_profiles(self) -> List[Profile]: + return list(filter(lambda x: x.is_custom_type_profile(), self.profiles)) + + def get_mac_addr_profiles(self) -> List[Profile]: + tailored = list(filter(lambda x: x.is_tailored(), self.profiles)) + match_mac_addr_profiles = list(filter(lambda x: x.name in self._local_mac_addresses, tailored)) + return match_mac_addr_profiles + + def install_greeter(self, install_session: 'Installer', greeter: GreeterType): + packages = [] + service = None + + match greeter: + case GreeterType.Lightdm: + packages = ['lightdm', 'lightdm-gtk-greeter'] + service = ['lightdm'] + case GreeterType.Sddm: + packages = ['sddm'] + service = ['sddm'] + case GreeterType.Gdm: + packages = ['gdm'] + service = ['gdm'] + + if packages: + install_session.add_additional_packages(packages) + if service: + install_session.enable_service(service) + + def install_gfx_driver(self, install_session: 'Installer', driver: str): + try: + driver_pkgs = AVAILABLE_GFX_DRIVERS[driver] if driver else [] + additional_pkg = ' '.join(['xorg-server', 'xorg-xinit'] + driver_pkgs) + + if driver is not None: + if 'nvidia' in driver: + if "linux-zen" in install_session.base_packages or "linux-lts" in install_session.base_packages: + for kernel in install_session.kernels: + # Fixes https://github.com/archlinux/archinstall/issues/585 + install_session.add_additional_packages(f"{kernel}-headers") + + # I've had kernel regen fail if it wasn't installed before nvidia-dkms + install_session.add_additional_packages("dkms xorg-server xorg-xinit nvidia-dkms") + return + elif 'amdgpu' in driver_pkgs: + # The order of these two are important if amdgpu is installed #808 + if 'amdgpu' in install_session.MODULES: + install_session.MODULES.remove('amdgpu') + install_session.MODULES.append('amdgpu') + + if 'radeon' in install_session.MODULES: + install_session.MODULES.remove('radeon') + install_session.MODULES.append('radeon') + + install_session.add_additional_packages(additional_pkg) + except Exception as err: + log(f"Could not handle nvidia and linuz-zen specific situations during xorg installation: {err}", level=logging.WARNING, fg="yellow") + # Prep didn't run, so there's no driver to install + install_session.add_additional_packages("xorg-server xorg-xinit") + + def install_profile_config(self, install_session: 'Installer', profile_config: ProfileConfiguration): + profile = profile_config.profile + + if profile: + profile.install(install_session) + + if profile and profile_config.gfx_driver: + if profile.is_xorg_type_profile() or profile.is_desktop_type_profile(): + self.install_gfx_driver(install_session, profile_config.gfx_driver) + + if profile and profile_config.greeter: + self.install_greeter(install_session, profile_config.greeter) + + def _import_profile_from_url(self, url: str): + """ + Import default_profiles from a url path + """ + try: + data = fetch_data_from_url(url) + b_data = bytes(data, 'utf-8') + + with NamedTemporaryFile(delete=False, suffix='.py') as fp: + fp.write(b_data) + filepath = Path(fp.name) + + profiles = self._process_profile_file(filepath) + self.remove_custom_profiles(profiles) + self.add_custom_profiles(profiles) + except ValueError: + err = str(_('Unable to fetch profile from specified url: {}')).format(url) + log(err, level=logging.ERROR, fg="red") + + def _load_profile_class(self, module: ModuleType) -> List[Profile]: + """ + Load all default_profiles defined in a module + """ + profiles = [] + for k, v in module.__dict__.items(): + if isinstance(v, type) and v.__module__ == module.__name__: + try: + cls_ = v() + if isinstance(cls_, Profile): + profiles.append(cls_) + except Exception: + log(f'Cannot import {module}, it does not appear to be a Profile class', level=logging.DEBUG) + + return profiles + + def _verify_unique_profile_names(self, profiles: List[Profile]): + """ + All profile names have to be unique, this function will verify + that the provided list contains only default_profiles with unique names + """ + counter = Counter([p.name for p in profiles]) + duplicates = list(filter(lambda x: x[1] != 1, counter.items())) + + if len(duplicates) > 0: + err = str(_('Profiles must have unique name, but profile definitions with duplicate name found: {}')).format(duplicates[0][0]) + log(err, level=logging.ERROR, fg="red") + sys.exit(1) + + def _is_legacy(self, file: Path) -> bool: + """ + Check if the provided profile file contains a + legacy profile definition + """ + with open(file, 'r') as fp: + for line in fp.readlines(): + if '__packages__' in line: + return True + return False + + def _process_profile_file(self, file: Path) -> List[Profile]: + """ + Process a file for profile definitions + """ + if self._is_legacy(file): + log(f'Cannot import {file} because it is no longer supported, please use the new profile format') + return [] + + if not file.is_file(): + log(f'Cannot find profile file {file}') + return [] + + name = file.name.removesuffix(file.suffix) + log(f'Importing profile: {file}', level=logging.DEBUG) + + try: + spec = importlib.util.spec_from_file_location(name, file) + if spec is not None: + imported = importlib.util.module_from_spec(spec) + if spec.loader is not None: + spec.loader.exec_module(imported) + return self._load_profile_class(imported) + except Exception as e: + log(f'Unable to parse file {file}: {e}', level=logging.ERROR) + + return [] + + def _find_available_profiles(self) -> List[Profile]: + """ + Search the profile path for profile definitions + """ + profiles = [] + for file in self._profiles_path.glob('**/*.py'): + # ignore the abstract default_profiles class + if 'profile.py' in file.name: + continue + profiles += self._process_profile_file(file) + + self._verify_unique_profile_names(profiles) + return profiles + + def reset_top_level_profiles(self, exclude: List[Profile] = []): + """ + Reset all top level profile configurations, this is usually necessary + when a new top level profile is selected + """ + excluded_profiles = [p.name for p in exclude] + for profile in self.get_top_level_profiles(): + if profile.name not in excluded_profiles: + profile.reset() + + def select_profile( + self, + selectable_profiles: List[Profile], + current_profile: Optional[Union[TProfile, List[TProfile]]] = None, + title: str = '', + allow_reset: bool = True, + multi: bool = False, + ) -> MenuSelection: + """ + Helper function to perform a profile selection + """ + options = {p.name: p for p in selectable_profiles} + + warning = str(_('Are you sure you want to reset this setting?')) + + preset_value: Optional[Union[str, List[str]]] = None + if current_profile is not None: + if isinstance(current_profile, list): + preset_value = [p.name for p in current_profile] + else: + preset_value = current_profile.name + + choice = Menu( + title=title, + preset_values=preset_value, + p_options=options, + allow_reset=allow_reset, + allow_reset_warning_msg=warning, + multi=multi, + sort=True, + preview_command=self.preview_text, + preview_size=0.5 + ).run() + + if choice.type_ == MenuSelectionType.Selection: + value = choice.value + if multi: + # this is quite dirty and should eb switched to a + # dedicated return type instead + choice.value = [options[val] for val in value] # type: ignore + else: + choice.value = options[value] # type: ignore + + return choice + + def preview_text(self, selection: str) -> Optional[str]: + """ + Callback for preview display on profile selection + """ + profile = self.get_profile_by_name(selection) + return profile.preview_text() if profile is not None else None + + +profile_handler = ProfileHandler() diff --git a/archinstall/lib/profiles.py b/archinstall/lib/profiles.py deleted file mode 100644 index a4fbe490..00000000 --- a/archinstall/lib/profiles.py +++ /dev/null @@ -1,340 +0,0 @@ -from __future__ import annotations -import hashlib -import importlib.util -import json -import os -import re -import ssl -import sys -import urllib.error -import urllib.parse -import urllib.request -from typing import Optional, Dict, Union, TYPE_CHECKING, Any -from types import ModuleType -# https://stackoverflow.com/a/39757388/929999 -if TYPE_CHECKING: - from .installer import Installer - _: Any - -from .general import multisplit -from .networking import list_interfaces -from .storage import storage -from .exceptions import ProfileNotFound - - -def grab_url_data(path :str) -> str: - safe_path = path[: path.find(':') + 1] + ''.join([item if item in ('/', '?', '=', '&') else urllib.parse.quote(item) for item in multisplit(path[path.find(':') + 1:], ('/', '?', '=', '&'))]) - ssl_context = ssl.create_default_context() - ssl_context.check_hostname = False - ssl_context.verify_mode = ssl.CERT_NONE - response = urllib.request.urlopen(safe_path, context=ssl_context) - return response.read() # bytes? - - -def is_desktop_profile(profile :str) -> bool: - if str(profile) == 'Profile(desktop)': - return True - - desktop_profile = Profile(None, "desktop") - with open(desktop_profile.path, 'r') as source: - source_data = source.read() - - if '__name__' in source_data and '__supported__' in source_data: - with desktop_profile.load_instructions(namespace=f"{desktop_profile.namespace}.py") as imported: - if hasattr(imported, '__supported__'): - desktop_profiles = imported.__supported__ - return str(profile) in [f"Profile({s})" for s in desktop_profiles] - - return False - - -def list_profiles( - filter_irrelevant_macs :bool = True, - subpath :str = '', - filter_top_level_profiles :bool = False -) -> Dict[str, Dict[str, Union[str, bool]]]: - # TODO: Grab from github page as well, not just local static files - - if filter_irrelevant_macs: - local_macs = list_interfaces() - - cache = {} - # Grab all local profiles found in PROFILE_PATH - for PATH_ITEM in storage['PROFILE_PATH']: - for root, folders, files in os.walk(os.path.abspath(os.path.expanduser(PATH_ITEM + subpath))): - for file in files: - if file == '__init__.py': - continue - if os.path.splitext(file)[1] == '.py': - tailored = False - if len(mac := re.findall('(([a-zA-z0-9]{2}[-:]){5}([a-zA-z0-9]{2}))', file)): - if filter_irrelevant_macs and mac[0][0].lower() not in local_macs: - continue - tailored = True - - description = '' - with open(os.path.join(root, file), 'r') as fh: - first_line = fh.readline() - if len(first_line) and first_line[0] == '#': - description = first_line[1:].strip() - - cache[file[:-3]] = {'path': os.path.join(root, file), 'description': description, 'tailored': tailored} - break - - # Grab profiles from upstream URL - if storage['PROFILE_DB']: - profiles_url = os.path.join(storage["UPSTREAM_URL"] + subpath, storage['PROFILE_DB']) - try: - profile_list = json.loads(grab_url_data(profiles_url)) - except urllib.error.HTTPError as err: - print(_('Error: Listing profiles on URL "{}" resulted in:').format(profiles_url), err) - return cache - except json.decoder.JSONDecodeError as err: - print(_('Error: Could not decode "{}" result as JSON:').format(profiles_url), err) - return cache - - for profile in profile_list: - if os.path.splitext(profile)[1] == '.py': - tailored = False - if len(mac := re.findall('(([a-zA-z0-9]{2}[-:]){5}([a-zA-z0-9]{2}))', profile)): - if filter_irrelevant_macs and mac[0][0].lower() not in local_macs: - continue - tailored = True - - cache[profile[:-3]] = {'path': os.path.join(storage["UPSTREAM_URL"] + subpath, profile), 'description': profile_list[profile], 'tailored': tailored} - - if filter_top_level_profiles: - for profile in list(cache.keys()): - if Profile(None, profile).is_top_level_profile() is False: - del cache[profile] - - return cache - - -class Script: - def __init__(self, profile :str, installer :Optional[Installer] = None): - """ - :param profile: A string representing either a boundled profile, a local python file - or a remote path (URL) to a python script-profile. Three examples: - * profile: https://archlinux.org/some_profile.py - * profile: desktop - * profile: /path/to/profile.py - """ - self.profile = profile - self.installer = installer # TODO: Appears not to be used anymore? - self.converted_path = None - self.spec = None - self.examples = {} - self.namespace = os.path.splitext(os.path.basename(self.path))[0] - self.original_namespace = self.namespace - - def __enter__(self, *args :str, **kwargs :str) -> ModuleType: - self.execute() - return sys.modules[self.namespace] - - def __exit__(self, *args :str, **kwargs :str) -> None: - # TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager - if len(args) >= 2 and args[1]: - raise args[1] - - if self.original_namespace: - self.namespace = self.original_namespace - - def localize_path(self, profile_path :str) -> str: - if (url := urllib.parse.urlparse(profile_path)).scheme and url.scheme in ('https', 'http'): - if not self.converted_path: - self.converted_path = f"/tmp/{os.path.basename(self.profile).replace('.py', '')}_{hashlib.md5(os.urandom(12)).hexdigest()}.py" - - with open(self.converted_path, "w") as temp_file: - temp_file.write(urllib.request.urlopen(url.geturl()).read().decode('utf-8')) - - return self.converted_path - else: - return profile_path - - @property - def path(self) -> str: - parsed_url = urllib.parse.urlparse(self.profile) - - # The Profile was not a direct match on a remote URL - if not parsed_url.scheme: - # Try to locate all local or known URL's - if not self.examples: - self.examples = list_profiles() - - if f"{self.profile}" in self.examples: - return self.localize_path(self.examples[self.profile]['path']) - # TODO: Redundant, the below block shouldn't be needed as profiles are stripped of their .py, but just in case for now: - elif f"{self.profile}.py" in self.examples: - return self.localize_path(self.examples[f"{self.profile}.py"]['path']) - - # Path was not found in any known examples, check if it's an absolute path - if os.path.isfile(self.profile): - return self.profile - - raise ProfileNotFound(f"File {self.profile} does not exist in {storage['PROFILE_PATH']}") - elif parsed_url.scheme in ('https', 'http'): - return self.localize_path(self.profile) - else: - raise ProfileNotFound(f"Cannot handle scheme {parsed_url.scheme}") - - def load_instructions(self, namespace :Optional[str] = None) -> 'Script': - if namespace: - self.namespace = namespace - - self.spec = importlib.util.spec_from_file_location(self.namespace, self.path) - imported = importlib.util.module_from_spec(self.spec) - sys.modules[self.namespace] = imported - - return self - - def execute(self) -> ModuleType: - if self.namespace not in sys.modules or self.spec is None: - self.load_instructions() - - self.spec.loader.exec_module(sys.modules[self.namespace]) - - return sys.modules[self.namespace] - - -class Profile(Script): - def __init__(self, installer :Optional[Installer], path :str): - super(Profile, self).__init__(path, installer) - - def __dump__(self, *args :str, **kwargs :str) -> Dict[str, str]: - return {'path': self.path} - - 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) - - @property - def is_desktop_profile(self) -> bool: - return is_desktop_profile(repr(self)) - - 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. - # (For instance awesome instead of aweosme.py or app-awesome.py) - self.namespace = self.original_namespace - return self.execute() - - def has_prep_function(self) -> bool: - with open(self.path, 'r') as source: - source_data = source.read() - - # Some crude safety checks, make sure the imported profile has - # a __name__ check and if so, check if it's got a _prep_function() - # we can call to ask for more user input. - # - # If the requirements are met, import with .py in the namespace to not - # trigger a traditional: - # if __name__ == 'moduleName' - if '__name__' in source_data and '_prep_function' in source_data: - with self.load_instructions(namespace=f"{self.namespace}.py") as imported: - if hasattr(imported, '_prep_function'): - return True - return False - - def has_post_install(self) -> bool: - with open(self.path, 'r') as source: - source_data = source.read() - - # Some crude safety checks, make sure the imported profile has - # a __name__ check and if so, check if it's got a _prep_function() - # we can call to ask for more user input. - # - # If the requirements are met, import with .py in the namespace to not - # trigger a traditional: - # if __name__ == 'moduleName' - if '__name__' in source_data and '_post_install' in source_data: - with self.load_instructions(namespace=f"{self.namespace}.py") as imported: - if hasattr(imported, '_post_install'): - return True - - def is_top_level_profile(self) -> bool: - with open(self.path, 'r') as source: - source_data = source.read() - - if '__name__' in source_data and 'is_top_level_profile' in source_data: - with self.load_instructions(namespace=f"{self.namespace}.py") as imported: - if hasattr(imported, 'is_top_level_profile'): - return imported.is_top_level_profile - - # Default to True if nothing is specified, - # since developers like less code - omitting it should assume they want to present it. - return True - - def get_profile_description(self) -> str: - with open(self.path, 'r') as source: - source_data = source.read() - - if '__description__' in source_data: - with self.load_instructions(namespace=f"{self.namespace}.py") as imported: - if hasattr(imported, '__description__'): - return imported.__description__ - - # Default to this string if the profile does not have a description. - return "This profile does not have the __description__ attribute set." - - @property - def packages(self) -> Optional[list]: - """ - Returns a list of packages baked into the profile definition. - If no package definition has been done, .packages() will return None. - """ - with open(self.path, 'r') as source: - source_data = source.read() - - # Some crude safety checks, make sure the imported profile has - # a __name__ check before importing. - # - # If the requirements are met, import with .py in the namespace to not - # trigger a traditional: - # if __name__ == 'moduleName' - if '__name__' in source_data and '__packages__' in source_data: - with self.load_instructions(namespace=f"{self.namespace}.py") as imported: - if hasattr(imported, '__packages__'): - return imported.__packages__ - return None - - -class Application(Profile): - def __repr__(self, *args :str, **kwargs :str): - return f'Application({os.path.basename(self.profile)})' - - @property - def path(self) -> str: - parsed_url = urllib.parse.urlparse(self.profile) - - # The Profile was not a direct match on a remote URL - if not parsed_url.scheme: - # Try to locate all local or known URL's - if not self.examples: - self.examples = list_profiles(subpath='/applications') - - if f"{self.profile}" in self.examples: - return self.localize_path(self.examples[self.profile]['path']) - # TODO: Redundant, the below block shouldn't be needed as profiles are stripped of their .py, but just in case for now: - elif f"{self.profile}.py" in self.examples: - return self.localize_path(self.examples[f"{self.profile}.py"]['path']) - - # Path was not found in any known examples, check if it's an absolute path - if os.path.isfile(self.profile): - return os.path.basename(self.profile) - - raise ProfileNotFound(f"Application file {self.profile} does not exist in {storage['PROFILE_PATH']}") - elif parsed_url.scheme in ('https', 'http'): - return self.localize_path(self.profile) - else: - raise ProfileNotFound(f"Application cannot handle scheme {parsed_url.scheme}") - - 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. - # (For instance awesome instead of aweosme.py or app-awesome.py) - self.namespace = self.original_namespace - return self.execute() diff --git a/archinstall/lib/storage.py b/archinstall/lib/storage.py index 8c358161..5a54d816 100644 --- a/archinstall/lib/storage.py +++ b/archinstall/lib/storage.py @@ -1,26 +1,19 @@ -import os - # There's a few scenarios of execution: -# 1. In the git repository, where ./profiles/ exist +# 1. In the git repository, where ./profiles_bck/ exist # 2. When executing from a remote directory, but targeted a script that starts from the git repository -# 3. When executing as a python -m archinstall module where profiles exist one step back for library reasons. +# 3. When executing as a python -m archinstall module where profiles_bck exist one step back for library reasons. # (4. Added the ~/.config directory as an additional option for future reasons) # # And Keeping this in dict ensures that variables are shared across imports. from typing import Any, Dict +from pathlib import Path + storage: Dict[str, Any] = { - 'PROFILE_PATH': [ - './profiles', - '~/.config/archinstall/profiles', - os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'profiles'), - # os.path.abspath(f'{os.path.dirname(__file__)}/../examples') - ], - 'UPSTREAM_URL': 'https://raw.githubusercontent.com/archlinux/archinstall/master/profiles', - 'PROFILE_DB': None, # Used in cases when listing profiles is desired, not mandatory for direct profile grabbing. + 'PROFILE': Path(__file__).parent.parent.joinpath('default_profiles'), 'LOG_PATH': '/var/log/archinstall', 'LOG_FILE': 'install.log', - 'MOUNT_POINT': '/mnt/archinstall', + 'MOUNT_POINT': Path('/mnt/archinstall'), 'ENC_IDENTIFIER': 'ainst', 'DISK_TIMEOUTS' : 1, # seconds 'DISK_RETRY_ATTEMPTS' : 5, # RETRY_ATTEMPTS * DISK_TIMEOUTS is used in disk operations diff --git a/archinstall/lib/udev/__init__.py b/archinstall/lib/udev/__init__.py deleted file mode 100644 index 86c8cc29..00000000 --- a/archinstall/lib/udev/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .udevadm import udevadm_info \ No newline at end of file diff --git a/archinstall/lib/udev/udevadm.py b/archinstall/lib/udev/udevadm.py deleted file mode 100644 index 84ec9cfd..00000000 --- a/archinstall/lib/udev/udevadm.py +++ /dev/null @@ -1,17 +0,0 @@ -import typing -import pathlib -from ..general import SysCommand - -def udevadm_info(path :pathlib.Path) -> typing.Dict[str, str]: - if path.resolve().exists() is False: - return {} - - result = SysCommand(f"udevadm info {path.resolve()}") - data = {} - for line in result: - if b': ' in line and b'=' in line: - _, obj = line.split(b': ', 1) - key, value = obj.split(b'=', 1) - data[key.decode('UTF-8').lower()] = value.decode('UTF-8').strip() - - return data \ No newline at end of file diff --git a/archinstall/lib/user_interaction/__init__.py b/archinstall/lib/user_interaction/__init__.py index 2bc46759..5ee89de0 100644 --- a/archinstall/lib/user_interaction/__init__.py +++ b/archinstall/lib/user_interaction/__init__.py @@ -1,12 +1,10 @@ -from .save_conf import save_config 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 +from .system_conf import select_kernel, select_driver, ask_for_bootloader, ask_for_swap from .network_conf import ask_to_configure_network -from .partitioning_conf import select_partition -from .general_conf import (ask_ntp, ask_for_a_timezone, ask_for_audio_selection, select_language, select_mirror_regions, - select_profile, select_archinstall_language, ask_additional_packages_to_install, - select_additional_repositories, ask_hostname, add_number_of_parrallel_downloads) -from .disk_conf import ask_for_main_filesystem_format, select_individual_blockdevice_usage, select_disk_layout, select_disk -from .utils import get_password, do_countdown +from .general_conf import ( + ask_ntp, ask_for_a_timezone, ask_for_audio_selection, select_language, select_mirror_regions, + select_archinstall_language, ask_additional_packages_to_install, + select_additional_repositories, ask_hostname, add_number_of_parrallel_downloads +) +from .utils import get_password diff --git a/archinstall/lib/user_interaction/backwards_compatible_conf.py b/archinstall/lib/user_interaction/backwards_compatible_conf.py deleted file mode 100644 index 296572d2..00000000 --- a/archinstall/lib/user_interaction/backwards_compatible_conf.py +++ /dev/null @@ -1,95 +0,0 @@ -from __future__ import annotations - -import logging -import sys -from collections.abc import Iterable -from typing import Any, Union, TYPE_CHECKING - -from ..exceptions import RequirementError -from ..menu import Menu -from ..output import log - -if TYPE_CHECKING: - _: Any - - -def generic_select( - p_options: Union[list, dict], - input_text: str = '', - allow_empty_input: bool = True, - options_output: bool = True, # function not available - sort: bool = False, - multi: bool = False, - default: Any = None) -> Any: - """ - A generic select function that does not output anything - other than the options and their indexes. As an example: - - generic_select(["first", "second", "third option"]) - > first - second - third option - When the user has entered the option correctly, - this function returns an item from list, a string, or None - - Options can be any iterable. - Duplicate entries are not checked, but the results with them are unreliable. Which element to choose from the duplicates depends on the return of the index() - Default value if not on the list of options will be added as the first element - sort will be handled by Menu() - """ - # We check that the options are iterable. If not we abort. Else we copy them to lists - # it options is a dictionary we use the values as entries of the list - # if options is a string object, each character becomes an entry - # if options is a list, we implictily build a copy to maintain immutability - if not isinstance(p_options, Iterable): - log(f"Objects of type {type(p_options)} is not iterable, and are not supported at generic_select", fg="red") - log(f"invalid parameter at Menu() call was at <{sys._getframe(1).f_code.co_name}>", level=logging.WARNING) - raise RequirementError("generic_select() requires an iterable as option.") - - input_text = input_text if input_text else _('Select one of the values shown below: ') - - if isinstance(p_options, dict): - options = list(p_options.values()) - else: - options = list(p_options) - # check that the default value is in the list. If not it will become the first entry - if default and default not in options: - options.insert(0, default) - - # one of the drawbacks of the new interface is that in only allows string like options, so we do a conversion - # also for the default value if it exists - soptions = list(map(str, options)) - default_value = options[options.index(default)] if default else None - - selected_option = Menu(input_text, - soptions, - skip=allow_empty_input, - multi=multi, - default_option=default_value, - sort=sort).run() - # we return the original objects, not the strings. - # options is the list with the original objects and soptions the list with the string values - # thru the map, we get from the value selected in soptions it index, and thu it the original object - if not selected_option: - return selected_option - elif isinstance(selected_option, list): # for multi True - selected_option = list(map(lambda x: options[soptions.index(x)], selected_option)) - else: # for multi False - selected_option = options[soptions.index(selected_option)] - return selected_option - - -def generic_multi_select(p_options: Union[list, dict], - text: str = '', - sort: bool = False, - default: Any = None, - allow_empty: bool = False) -> Any: - - text = text if text else _("Select one or more of the options below: ") - - return generic_select(p_options, - input_text=text, - allow_empty_input=allow_empty, - sort=sort, - multi=True, - default=default) diff --git a/archinstall/lib/user_interaction/disk_conf.py b/archinstall/lib/user_interaction/disk_conf.py index 554d13ef..a77e950a 100644 --- a/archinstall/lib/user_interaction/disk_conf.py +++ b/archinstall/lib/user_interaction/disk_conf.py @@ -1,86 +1,391 @@ from __future__ import annotations -from typing import Any, Dict, TYPE_CHECKING, Optional +import logging +from pathlib import Path +from typing import Any, TYPE_CHECKING, Optional, List, Tuple -from .partitioning_conf import manage_new_and_existing_partitions, get_default_partition_layout -from ..disk import BlockDevice -from ..exceptions import DiskError -from ..menu import Menu -from ..menu.menu import MenuSelectionType +from .. import disk +from ..hardware import has_uefi +from ..menu import Menu, MenuSelectionType, TableMenu +from ..output import FormattedOutput +from ..output import log +from ..utils.util import prompt_dir if TYPE_CHECKING: _: Any -def ask_for_main_filesystem_format(advanced_options=False) -> str: - options = {'btrfs': 'btrfs', 'ext4': 'ext4', 'xfs': 'xfs', 'f2fs': 'f2fs'} +def select_devices(preset: List[disk.BDevice] = []) -> List[disk.BDevice]: + """ + Asks the user to select one or multiple devices - advanced = {'ntfs': 'ntfs'} + :return: List of selected devices + :rtype: list + """ - if advanced_options: - options.update(advanced) + def _preview_device_selection(selection: disk._DeviceInfo) -> Optional[str]: + dev = disk.device_handler.get_device(selection.path) + if dev and dev.partition_infos: + return FormattedOutput.as_table(dev.partition_infos) + return None - prompt = _('Select which filesystem your main partition should use') - choice = Menu(prompt, options, skip=False).run() - return choice.value + if preset is None: + preset = [] + + title = str(_('Select one or more devices to use and configure')) + warning = str(_('If you reset the device selection this will also reset the current disk layout. Are you sure?')) + + devices = disk.device_handler.devices + options = [d.device_info for d in devices] + preset_value = [p.device_info for p in preset] + + choice = TableMenu( + title, + data=options, + multi=True, + preset=preset_value, + preview_command=_preview_device_selection, + preview_title=str(_('Existing Partitions')), + preview_size=0.2, + allow_reset=True, + allow_reset_warning_msg=warning + ).run() + + match choice.type_: + case MenuSelectionType.Reset: return [] + case MenuSelectionType.Skip: return preset + case MenuSelectionType.Selection: + selected_device_info: List[disk._DeviceInfo] = choice.value # type: ignore + selected_devices = [] + + for device in devices: + if device.device_info in selected_device_info: + selected_devices.append(device) + + return selected_devices -def select_individual_blockdevice_usage(block_devices: list) -> Dict[str, Any]: - result = {} +def get_default_partition_layout( + devices: List[disk.BDevice], + filesystem_type: Optional[disk.FilesystemType] = None, + advanced_option: bool = False +) -> List[disk.DeviceModification]: - for device in block_devices: - layout = manage_new_and_existing_partitions(device) - result[device.path] = layout + if len(devices) == 1: + device_modification = suggest_single_disk_layout( + devices[0], + filesystem_type=filesystem_type, + advanced_options=advanced_option + ) + return [device_modification] + else: + return suggest_multi_disk_layout( + devices, + filesystem_type=filesystem_type, + advanced_options=advanced_option + ) - return result +def _manual_partitioning( + preset: List[disk.DeviceModification], + devices: List[disk.BDevice] +) -> List[disk.DeviceModification]: + modifications = [] + for device in devices: + mod = next(filter(lambda x: x.device == device, preset), None) + if not mod: + mod = disk.DeviceModification(device, wipe=False) -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] + if partitions := disk.manual_partitioning(device, preset=mod.partitions): + mod.partitions = partitions + modifications.append(mod) + return modifications + + +def select_disk_config( + preset: Optional[disk.DiskLayoutConfiguration] = None, + advanced_option: bool = False +) -> Optional[disk.DiskLayoutConfiguration]: + default_layout = disk.DiskLayoutType.Default.display_msg() + manual_mode = disk.DiskLayoutType.Manual.display_msg() + pre_mount_mode = disk.DiskLayoutType.Pre_mount.display_msg() + + options = [default_layout, manual_mode, pre_mount_mode] + preset_value = preset.config_type.display_msg() if preset else None 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, + _('Select a partitioning option'), + options, allow_reset=True, - allow_reset_warning_msg=warning + allow_reset_warning_msg=warning, + sort=False, + preview_size=0.2, + preset_values=preset_value ).run() match choice.type_: case MenuSelectionType.Skip: return preset case MenuSelectionType.Reset: return None case MenuSelectionType.Selection: - if choice.value == wipe_mode: - return get_default_partition_layout(block_devices, advanced_options) + if choice.single_value == pre_mount_mode: + output = "You will use whatever drive-setup is mounted at the specified directory\n" + output += "WARNING: Archinstall won't check the suitability of this setup\n" + + path = prompt_dir(str(_('Enter the root directory of the mounted devices: ')), output) + mods = disk.device_handler.detect_pre_mounted_mods(path) + + return disk.DiskLayoutConfiguration( + config_type=disk.DiskLayoutType.Pre_mount, + relative_mountpoint=path, + device_modifications=mods + ) + + preset_devices = [mod.device for mod in preset.device_modifications] if preset else [] + + devices = select_devices(preset_devices) + + if not devices: + return None + + if choice.value == default_layout: + modifications = get_default_partition_layout(devices, advanced_option=advanced_option) + if modifications: + return disk.DiskLayoutConfiguration( + config_type=disk.DiskLayoutType.Default, + device_modifications=modifications + ) + elif choice.value == manual_mode: + preset_mods = preset.device_modifications if preset else [] + modifications = _manual_partitioning(preset_mods, devices) + + if modifications: + return disk.DiskLayoutConfiguration( + config_type=disk.DiskLayoutType.Manual, + device_modifications=modifications + ) + + return None + + +def _boot_partition() -> disk.PartitionModification: + if has_uefi(): + start = disk.Size(1, disk.Unit.MiB) + size = disk.Size(512, disk.Unit.MiB) + else: + start = disk.Size(3, disk.Unit.MiB) + size = disk.Size(203, disk.Unit.MiB) + + # boot partition + return disk.PartitionModification( + status=disk.ModificationStatus.Create, + type=disk.PartitionType.Primary, + start=start, + length=size, + mountpoint=Path('/boot'), + fs_type=disk.FilesystemType.Fat32, + flags=[disk.PartitionFlag.Boot] + ) + + +def ask_for_main_filesystem_format(advanced_options=False) -> disk.FilesystemType: + options = { + 'btrfs': disk.FilesystemType.Btrfs, + 'ext4': disk.FilesystemType.Ext4, + 'xfs': disk.FilesystemType.Xfs, + 'f2fs': disk.FilesystemType.F2fs + } + + if advanced_options: + options.update({'ntfs': disk.FilesystemType.Ntfs}) + + prompt = _('Select which filesystem your main partition should use') + choice = Menu(prompt, options, skip=False, sort=False).run() + return options[choice.single_value] + + +def suggest_single_disk_layout( + device: disk.BDevice, + filesystem_type: Optional[disk.FilesystemType] = None, + advanced_options: bool = False, + separate_home: Optional[bool] = None +) -> disk.DeviceModification: + if not filesystem_type: + filesystem_type = ask_for_main_filesystem_format(advanced_options) + + min_size_to_allow_home_part = disk.Size(40, disk.Unit.GiB) + root_partition_size = disk.Size(20, disk.Unit.GiB) + using_subvolumes = False + using_home_partition = False + compression = False + device_size_gib = device.device_info.total_size + + if filesystem_type == disk.FilesystemType.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.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.value == Menu.yes() + + device_modification = disk.DeviceModification(device, wipe=True) + + # Used for reference: https://wiki.archlinux.org/title/partitioning + # 2 MiB is unallocated for GRUB on BIOS. Potentially unneeded for other bootloaders? + + # TODO: On BIOS, /boot partition is only needed if the drive will + # be encrypted, otherwise it is not recommended. We should probably + # add a check for whether the drive will be encrypted or not. + + # Increase the UEFI partition if UEFI is detected. + # Also re-align the start to 1MiB since we don't need the first sectors + # like we do in MBR layouts where the boot loader is installed traditionally. + + boot_partition = _boot_partition() + device_modification.add_partition(boot_partition) + + if not using_subvolumes: + if device_size_gib >= min_size_to_allow_home_part: + if separate_home is None: + 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.value == Menu.yes() + elif separate_home is True: + using_home_partition = True else: - return select_individual_blockdevice_usage(block_devices) + using_home_partition = False + # root partition + start = disk.Size(513, disk.Unit.MiB) if has_uefi() else disk.Size(206, disk.Unit.MiB) -def select_disk(dict_o_disks: Dict[str, BlockDevice]) -> Optional[BlockDevice]: - """ - Asks the user to select a harddrive from the `dict_o_disks` selection. - Usually this is combined with :ref:`archinstall.list_drives`. + # Set a size for / (/root) + if using_subvolumes or device_size_gib < min_size_to_allow_home_part or not using_home_partition: + length = disk.Size(100, disk.Unit.Percent, total_size=device.device_info.total_size) + else: + length = min(device.device_info.total_size, root_partition_size) - :param dict_o_disks: A `dict` where keys are the drive-name, value should be a dict containing drive information. - :type dict_o_disks: dict + root_partition = disk.PartitionModification( + status=disk.ModificationStatus.Create, + type=disk.PartitionType.Primary, + start=start, + length=length, + mountpoint=Path('/') if not using_subvolumes else None, + fs_type=filesystem_type, + mount_options=['compress=zstd'] if compression else [], + ) + device_modification.add_partition(root_partition) - :return: The name/path (the dictionary key) of the selected drive - :rtype: str - """ - drives = sorted(list(dict_o_disks.keys())) - if len(drives) >= 1: - title = str(_('You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)')) + '\n' - title += str(_('Select one of the disks or skip and use /mnt as default')) + if using_subvolumes: + # https://btrfs.wiki.kernel.org/index.php/FAQ + # https://unix.stackexchange.com/questions/246976/btrfs-subvolume-uuid-clash + # https://github.com/classy-giraffe/easy-arch/blob/main/easy-arch.sh + subvolumes = [ + disk.SubvolumeModification(Path('@'), Path('/')), + disk.SubvolumeModification(Path('@home'), Path('/home')), + disk.SubvolumeModification(Path('@log'), Path('/var/log')), + disk.SubvolumeModification(Path('@pkg'), Path('/var/cache/pacman/pkg')), + disk.SubvolumeModification(Path('@.snapshots'), Path('/.snapshots')) + ] + root_partition.btrfs_subvols = subvolumes + elif using_home_partition: + # If we don't want to use subvolumes, + # But we want to be able to re-use data between re-installs.. + # A second partition for /home would be nice if we have the space for it + home_partition = disk.PartitionModification( + status=disk.ModificationStatus.Create, + type=disk.PartitionType.Primary, + start=root_partition.length, + length=disk.Size(100, disk.Unit.Percent, total_size=device.device_info.total_size), + mountpoint=Path('/home'), + fs_type=filesystem_type, + mount_options=['compress=zstd'] if compression else [] + ) + device_modification.add_partition(home_partition) + + return device_modification + + +def suggest_multi_disk_layout( + devices: List[disk.BDevice], + filesystem_type: Optional[disk.FilesystemType] = None, + advanced_options: bool = False +) -> List[disk.DeviceModification]: + if not devices: + return [] + + # Not really a rock solid foundation of information to stand on, but it's a start: + # https://www.reddit.com/r/btrfs/comments/m287gp/partition_strategy_for_two_physical_disks/ + # https://www.reddit.com/r/btrfs/comments/9us4hr/what_is_your_btrfs_partitionsubvolumes_scheme/ + min_home_partition_size = disk.Size(40, disk.Unit.GiB) + # rough estimate taking in to account user desktops etc. TODO: Catch user packages to detect size? + desired_root_partition_size = disk.Size(20, disk.Unit.GiB) + compression = False + + if not filesystem_type: + filesystem_type = ask_for_main_filesystem_format(advanced_options) + + # find proper disk for /home + possible_devices = list(filter(lambda x: x.device_info.total_size >= min_home_partition_size, devices)) + home_device = max(possible_devices, key=lambda d: d.device_info.total_size) if possible_devices else None + + # find proper device for /root + devices_delta = {} + for device in devices: + if device is not home_device: + delta = device.device_info.total_size - desired_root_partition_size + devices_delta[device] = delta + + sorted_delta: List[Tuple[disk.BDevice, Any]] = sorted(devices_delta.items(), key=lambda x: x[1]) + root_device: Optional[disk.BDevice] = sorted_delta[0][0] + + 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: {}GiB\n').format(min_home_partition_size.format_size(disk.Unit.GiB)) + text += _('Minimum capacity for Arch Linux partition: {}GiB').format(desired_root_partition_size.format_size(disk.Unit.GiB)) + Menu(str(text), [str(_('Continue'))], skip=False).run() + return [] + + if filesystem_type == disk.FilesystemType.Btrfs: + prompt = str(_('Would you like to use BTRFS compression?')) + choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run() + compression = choice.value == Menu.yes() + + device_paths = ', '.join([str(d.device_info.path) for d in devices]) + log(f"Suggesting multi-disk-layout for devices: {device_paths}", level=logging.DEBUG) + log(f"/root: {root_device.device_info.path}", level=logging.DEBUG) + log(f"/home: {home_device.device_info.path}", level=logging.DEBUG) + + root_device_modification = disk.DeviceModification(root_device, wipe=True) + home_device_modification = disk.DeviceModification(home_device, wipe=True) - choice = Menu(title, drives).run() + # add boot partition to the root device + boot_partition = _boot_partition() + root_device_modification.add_partition(boot_partition) - if choice.type_ == MenuSelectionType.Skip: - return None + # add root partition to the root device + root_partition = disk.PartitionModification( + status=disk.ModificationStatus.Create, + type=disk.PartitionType.Primary, + start=disk.Size(513, disk.Unit.MiB) if has_uefi() else disk.Size(206, disk.Unit.MiB), + length=disk.Size(100, disk.Unit.Percent, total_size=root_device.device_info.total_size), + mountpoint=Path('/'), + mount_options=['compress=zstd'] if compression else [], + fs_type=filesystem_type + ) + root_device_modification.add_partition(root_partition) - drive = dict_o_disks[choice.value] - return drive + # add home partition to home device + home_partition = disk.PartitionModification( + status=disk.ModificationStatus.Create, + type=disk.PartitionType.Primary, + start=disk.Size(1, disk.Unit.MiB), + length=disk.Size(100, disk.Unit.Percent, total_size=home_device.device_info.total_size), + mountpoint=Path('/home'), + mount_options=['compress=zstd'] if compression else [], + fs_type=filesystem_type, + ) + home_device_modification.add_partition(home_partition) - raise DiskError('select_disk() requires a non-empty dictionary of disks to select from.') + return [root_device_modification, home_device_modification] diff --git a/archinstall/lib/user_interaction/general_conf.py b/archinstall/lib/user_interaction/general_conf.py index fc7ded45..7a6bb358 100644 --- a/archinstall/lib/user_interaction/general_conf.py +++ b/archinstall/lib/user_interaction/general_conf.py @@ -3,15 +3,13 @@ from __future__ import annotations import logging import pathlib from typing import List, Any, Optional, Dict, TYPE_CHECKING +from typing import Union from ..locale_helpers import list_keyboard_languages, list_timezones -from ..menu import Menu -from ..menu.menu import MenuSelectionType -from ..menu.text_input import TextInput +from ..menu import MenuSelectionType, Menu, TextInput from ..mirrors import list_mirrors from ..output import log from ..packages.packages import validate_package_list -from ..profiles import Profile, list_profiles from ..storage import storage from ..translationhandler import Language @@ -32,9 +30,10 @@ def ask_ntp(preset: bool = True) -> bool: def ask_hostname(preset: str = None) -> str: - hostname = TextInput(_('Desired hostname for the installation: '), preset).run().strip(' ') - return hostname - + while True: + hostname = TextInput(_('Desired hostname for the installation: '), preset).run().strip() + if hostname: + return hostname def ask_for_a_timezone(preset: str = None) -> str: timezones = list_timezones() @@ -52,7 +51,7 @@ def ask_for_a_timezone(preset: str = None) -> str: case MenuSelectionType.Selection: return choice.value -def ask_for_audio_selection(desktop: bool = True, preset: str = None) -> str: +def ask_for_audio_selection(desktop: bool = True, preset: Union[str, None] = None) -> Union[str, None]: no_audio = str(_('No audio server')) choices = ['pipewire', 'pulseaudio'] if desktop else ['pipewire', 'pulseaudio', no_audio] default = 'pipewire' if desktop else no_audio @@ -140,50 +139,6 @@ def select_archinstall_language(languages: List[Language], preset_value: Languag return options[choice.value] -def select_profile(preset) -> Optional[Profile]: - """ - # Asks the user to select a profile from the available profiles. - # - # :return: The name/dictionary key of the selected profile - # :rtype: str - # """ - top_level_profiles = sorted(list(list_profiles(filter_top_level_profiles=True))) - options = {} - - for profile in top_level_profiles: - profile = Profile(None, profile) - description = profile.get_profile_description() - - option = f'{profile.profile}: {description}' - options[option] = profile - - title = _('This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments') - warning = str(_('Are you sure you want to reset this setting?')) - - selection = Menu( - title=title, - p_options=list(options.keys()), - allow_reset=True, - allow_reset_warning_msg=warning - ).run() - - match selection.type_: - case MenuSelectionType.Selection: - return options[selection.value] if selection.value is not None else None - case MenuSelectionType.Reset: - storage['profile_minimal'] = False - storage['_selected_servers'] = [] - storage['_desktop_profile'] = None - storage['sway_sys_priv_ctrl'] = None - storage['arguments']['sway_sys_priv_ctrl'] = None - storage['arguments']['desktop-environment'] = None - storage['arguments']['gfx_driver'] = None - storage['arguments']['gfx_driver_packages'] = None - return None - case MenuSelectionType.Skip: - return None - - def ask_additional_packages_to_install(pre_set_packages: List[str] = []) -> List[str]: # Additional packages (with some light weight error handling for invalid package names) print(_('Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.')) diff --git a/archinstall/lib/user_interaction/locale_conf.py b/archinstall/lib/user_interaction/locale_conf.py index bbbe070b..88aec64e 100644 --- a/archinstall/lib/user_interaction/locale_conf.py +++ b/archinstall/lib/user_interaction/locale_conf.py @@ -3,8 +3,7 @@ from __future__ import annotations from typing import Any, TYPE_CHECKING from ..locale_helpers import list_locales -from ..menu import Menu -from ..menu.menu import MenuSelectionType +from ..menu import Menu, MenuSelectionType if TYPE_CHECKING: _: Any diff --git a/archinstall/lib/user_interaction/manage_users_conf.py b/archinstall/lib/user_interaction/manage_users_conf.py index 84ce3556..879578da 100644 --- a/archinstall/lib/user_interaction/manage_users_conf.py +++ b/archinstall/lib/user_interaction/manage_users_conf.py @@ -4,8 +4,7 @@ import re 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 ..menu import Menu, ListManager from ..models.users import User from ..output import FormattedOutput @@ -27,14 +26,14 @@ class UserList(ListManager): ] super().__init__(prompt, lusers, [self._actions[0]], self._actions[1:]) - def reformat(self, data: List[User]) -> Dict[str, User]: + def reformat(self, data: List[User]) -> Dict[str, Any]: table = FormattedOutput.as_table(data) rows = table.split('\n') # these are the header rows of the table and do not map to any User obviously # we're adding 2 spaces as prefix because the menu selector '> ' will be put before # the selectable rows so the header has to be aligned - display_data = {f' {rows[0]}': None, f' {rows[1]}': None} + display_data: Dict[str, Optional[User]] = {f' {rows[0]}': None, f' {rows[1]}': None} for row, user in zip(rows[2:], data): row = row.replace('|', '\\|') @@ -53,16 +52,16 @@ class UserList(ListManager): # was created we'll replace the existing one data = [d for d in data if d.username != new_user.username] data += [new_user] - elif action == self._actions[1]: # change password + elif action == self._actions[1] and entry: # change password prompt = str(_('Password for user "{}": ').format(entry.username)) new_password = get_password(prompt=prompt) if new_password: user = next(filter(lambda x: x == entry, data)) user.password = new_password - elif action == self._actions[2]: # promote/demote + elif action == self._actions[2] and entry: # promote/demote user = next(filter(lambda x: x == entry, data)) user.sudo = False if user.sudo else True - elif action == self._actions[3]: # delete + elif action == self._actions[3] and entry: # delete data = [d for d in data if d != entry] return data @@ -80,16 +79,20 @@ class UserList(ListManager): 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 + error_prompt = str(_("The username you entered is invalid. Try again")) + print(error_prompt) else: break password = get_password(prompt=str(_('Password for user "{}": ').format(username))) + if not password: + return None + choice = Menu( str(_('Should "{}" be a superuser (sudo)?')).format(username), Menu.yes_no(), skip=False, - default_option=Menu.no(), + default_option=Menu.yes(), clear_screen=False, show_search_hint=False ).run() diff --git a/archinstall/lib/user_interaction/network_conf.py b/archinstall/lib/user_interaction/network_conf.py index 5e637f23..b682c1d2 100644 --- a/archinstall/lib/user_interaction/network_conf.py +++ b/archinstall/lib/user_interaction/network_conf.py @@ -4,14 +4,12 @@ import ipaddress import logging from typing import Any, Optional, TYPE_CHECKING, List, Union, Dict -from ..menu.menu import MenuSelectionType -from ..menu.text_input import TextInput +from ..menu import MenuSelectionType, TextInput from ..models.network_configuration import NetworkConfiguration, NicType from ..networking import list_interfaces -from ..menu import Menu from ..output import log, FormattedOutput -from ..menu.list_manager import ListManager +from ..menu import ListManager, Menu if TYPE_CHECKING: _: Any diff --git a/archinstall/lib/user_interaction/partitioning_conf.py b/archinstall/lib/user_interaction/partitioning_conf.py deleted file mode 100644 index 0a5ede51..00000000 --- a/archinstall/lib/user_interaction/partitioning_conf.py +++ /dev/null @@ -1,362 +0,0 @@ -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, FormattedOutput - -from ..disk.validators import fs_types - -if TYPE_CHECKING: - from ..disk import BlockDevice - from ..disk.partition import Partition - _: Any - - -def partition_overlap(partitions: list, start: str, end: str) -> bool: - # TODO: Implement sanity check - return False - - -def current_partition_layout(partitions: List[Dict[str, Any]], with_idx: bool = False, with_title: bool = True) -> str: - - def do_padding(name: str, max_len: int): - spaces = abs(len(str(name)) - max_len) + 2 - pad_left = int(spaces / 2) - pad_right = spaces - pad_left - return f'{pad_right * " "}{name}{pad_left * " "}|' - - def flatten_data(data: Dict[str, Any]) -> Dict[str, Any]: - flattened = {} - for k, v in data.items(): - if k == 'filesystem': - flat = flatten_data(v) - flattened.update(flat) - elif k == 'btrfs': - # we're going to create a separate table for the btrfs subvolumes - pass - else: - flattened[k] = v - return flattened - - display_data: List[Dict[str, Any]] = [flatten_data(entry) for entry in partitions] - - column_names = {} - - # this will add an initial index to the table for each partition - if with_idx: - column_names['index'] = max([len(str(len(display_data))), len('index')]) - - # determine all attribute names and the max length - # of the value among all display_data to know the width - # of the table cells - for p in display_data: - for attribute, value in p.items(): - if attribute in column_names.keys(): - column_names[attribute] = max([column_names[attribute], len(str(value)), len(attribute)]) - else: - column_names[attribute] = max([len(str(value)), len(attribute)]) - - current_layout = '' - for name, max_len in column_names.items(): - current_layout += do_padding(name, max_len) - - current_layout = f'{current_layout[:-1]}\n{"-" * len(current_layout)}\n' - - for idx, p in enumerate(display_data): - row = '' - for name, max_len in column_names.items(): - if name == 'index': - row += do_padding(str(idx), max_len) - elif name in p: - row += do_padding(p[name], max_len) - else: - row += ' ' * (max_len + 2) + '|' - - current_layout += f'{row[:-1]}\n' - - # we'll create a separate table for the btrfs subvolumes - btrfs_subvolumes = [partition['btrfs']['subvolumes'] for partition in partitions if partition.get('btrfs', None)] - if len(btrfs_subvolumes) > 0: - for subvolumes in btrfs_subvolumes: - output = FormattedOutput.as_table(subvolumes) - current_layout += f'\n{output}' - - if with_title: - title = str(_('Current partition layout')) - return f'\n\n{title}:\n\n{current_layout}' - - return current_layout - - -def _get_partitions(partitions :List[Partition], filter_ :Callable = None) -> List[str]: - """ - filter allows to filter out the indexes once they are set. Should return True if element is to be included - """ - partition_indexes = [] - for i in range(len(partitions)): - if filter_: - if filter_(partitions[i]): - partition_indexes.append(str(i)) - else: - partition_indexes.append(str(i)) - - return partition_indexes - - -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 - - choice = Menu(title, partition_indexes, multi=multiple).run() - - if choice.type_ == MenuSelectionType.Skip: - 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( - 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: - return suggest_single_disk_layout(block_devices[0], advanced_options=advanced_options) - else: - return suggest_multi_disk_layout(block_devices, advanced_options=advanced_options) - - -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) - - new_partition = str(_('Create a new partition')) - suggest_partition_layout = str(_('Suggest partition layout')) - delete_partition = str(_('Delete a partition')) - delete_all_partitions = str(_('Clear/Delete all partitions')) - assign_mount_point = str(_('Assign mount-point for a partition')) - mark_formatted = str(_('Mark/Unmark a partition to be formatted (wipes data)')) - mark_compressed = str(_('Mark/Unmark a partition as compressed (btrfs only)')) - 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] - - if len(block_device_struct['partitions']) > 0: - modes += [ - delete_partition, - delete_all_partitions, - assign_mount_point, - mark_formatted, - mark_bootable, - mark_compressed, - set_filesystem_partition, - ] - - indexes = _get_partitions( - block_device_struct["partitions"], - filter_=lambda x: True if x.get('filesystem', {}).get('format') == 'btrfs' else False - ) - - if len(indexes) > 0: - modes += [set_btrfs_subvolumes] - - title = _('Select what to do with\n{}').format(block_device) - - # show current partition layout: - if len(block_device_struct["partitions"]): - title += current_partition_layout(block_device_struct['partitions']) + '\n' - - modes += [save_and_exit, cancel] - - 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 - - # if partition_type == 'gpt': - # # https://www.gnu.org/software/parted/manual/html_node/mkpart.html - # # https://www.gnu.org/software/parted/manual/html_node/mklabel.html - # name = input("Enter a desired name for the partition: ").strip() - - fs_choice = Menu(_('Enter a desired filesystem type for the partition'), fs_types()).run() - - if fs_choice.type_ == MenuSelectionType.Skip: - continue - - prompt = str(_('Enter the start location (in parted units: s, GB, %, etc. ; default: {}): ')).format( - block_device.first_free_sector - ) - start = input(prompt).strip() - - if not start.strip(): - start = block_device.first_free_sector - end_suggested = block_device.first_end_sector - else: - end_suggested = '100%' - - prompt = str(_('Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): ')).format( - end_suggested - ) - end = input(prompt).strip() - - if not end.strip(): - end = end_suggested - - if valid_parted_position(start) and valid_parted_position(end): - if partition_overlap(block_device_struct["partitions"], start, end): - log(f"This partition overlaps with other partitions on the drive! Ignoring this partition creation.", - fg="red") - continue - - block_device_struct["partitions"].append({ - "type": "primary", # Strictly only allowed under MS-DOS, but GPT accepts it so it's "safe" to inject - "start": start, - "size": end, - "mountpoint": None, - "wipe": True, - "filesystem": { - "format": fs_choice.value - } - }) - else: - log(f"Invalid start ({valid_parted_position(start)}) or end ({valid_parted_position(end)}) for this partition. Ignoring this partition creation.", - fg="red") - continue - elif task == suggest_partition_layout: - from ..disk import suggest_single_disk_layout - - if len(block_device_struct["partitions"]): - 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.value == Menu.no(): - continue - - block_device_struct.update(suggest_single_disk_layout(block_device)[block_device.path]) - else: - current_layout = current_partition_layout(block_device_struct['partitions'], with_idx=True) - - if task == delete_partition: - title = _('{}\n\nSelect by index which partitions to delete').format(current_layout) - to_delete = select_partition(title, block_device_struct["partitions"], multiple=True) - - if to_delete: - block_device_struct['partitions'] = [ - p for idx, p in enumerate(block_device_struct['partitions']) if idx not in to_delete - ] - elif task == mark_compressed: - 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: - if "filesystem" not in block_device_struct["partitions"][partition]: - block_device_struct["partitions"][partition]["filesystem"] = {} - if "mount_options" not in block_device_struct["partitions"][partition]["filesystem"]: - block_device_struct["partitions"][partition]["filesystem"]["mount_options"] = [] - - if "compress=zstd" not in block_device_struct["partitions"][partition]["filesystem"]["mount_options"]: - block_device_struct["partitions"][partition]["filesystem"]["mount_options"].append("compress=zstd") - elif task == delete_all_partitions: - block_device_struct["partitions"] = [] - block_device_struct["wipe"] = True - elif task == assign_mount_point: - title = _('{}\n\nSelect by index which partition to mount where').format(current_layout) - 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() - - if len(mountpoint): - block_device_struct["partitions"][partition]['mountpoint'] = mountpoint - if mountpoint == '/boot': - log(f"Marked partition as bootable because mountpoint was set to /boot.", fg="yellow") - block_device_struct["partitions"][partition]['boot'] = True - else: - del (block_device_struct["partitions"][partition]['mountpoint']) - - elif task == mark_formatted: - title = _('{}\n\nSelect which partition to mask for formatting').format(current_layout) - partition = select_partition(title, block_device_struct["partitions"]) - - if partition is not None: - # If we mark a partition for formatting, but the format is CRYPTO LUKS, there's no point in formatting it really - # without asking the user which inner-filesystem they want to use. Since the flag 'encrypted' = True is already set, - # it's safe to change the filesystem for this partition. - if block_device_struct["partitions"][partition].get('filesystem',{}).get('format', 'crypto_LUKS') == 'crypto_LUKS': - if not block_device_struct["partitions"][partition].get('filesystem', None): - block_device_struct["partitions"][partition]['filesystem'] = {} - - fs_choice = Menu(_('Enter a desired filesystem type for the partition'), fs_types()).run() - - 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) - - 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) - - elif task == set_filesystem_partition: - title = _('{}\n\nSelect which partition to set a filesystem on').format(current_layout) - partition = select_partition(title, block_device_struct["partitions"]) - - if partition is not None: - if not block_device_struct["partitions"][partition].get('filesystem', None): - block_device_struct["partitions"][partition]['filesystem'] = {} - - fstype_title = _('Enter a desired filesystem type for the partition: ') - fs_choice = Menu(fstype_title, fs_types()).run() - - 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 - - # TODO get preexisting partitions - title = _('{}\n\nSelect which partition to set subvolumes on').format(current_layout) - partition = select_partition(title, block_device_struct["partitions"],filter_=lambda x:True if x.get('filesystem',{}).get('format') == 'btrfs' else False) - - if partition is not None: - if not block_device_struct["partitions"][partition].get('btrfs', {}): - block_device_struct["partitions"][partition]['btrfs'] = {} - if not block_device_struct["partitions"][partition]['btrfs'].get('subvolumes', []): - block_device_struct["partitions"][partition]['btrfs']['subvolumes'] = [] - - prev = block_device_struct["partitions"][partition]['btrfs']['subvolumes'] - result = SubvolumeList(_("Manage btrfs subvolumes for current partition"), prev).run() - block_device_struct["partitions"][partition]['btrfs']['subvolumes'] = result - - return block_device_struct diff --git a/archinstall/lib/user_interaction/save_conf.py b/archinstall/lib/user_interaction/save_conf.py index 5b4ae2b3..e05b9afe 100644 --- a/archinstall/lib/user_interaction/save_conf.py +++ b/archinstall/lib/user_interaction/save_conf.py @@ -5,38 +5,30 @@ import logging from pathlib import Path from typing import Any, Dict, TYPE_CHECKING -from ..configuration import ConfigurationOutput from ..general import SysCommand from ..menu import Menu from ..menu.menu import MenuSelectionType from ..output import log +from ..configuration import ConfigurationOutput if TYPE_CHECKING: _: Any def save_config(config: Dict): - def preview(selection: str): if options['user_config'] == selection: - json_config = config_output.user_config_to_json() - return f'{config_output.user_configuration_file}\n{json_config}' + serialized = config_output.user_config_to_json() + return f'{config_output.user_configuration_file}\n{serialized}' elif options['user_creds'] == selection: - if json_config := config_output.user_credentials_to_json(): - return f'{config_output.user_credentials_file}\n{json_config}' - else: - return str(_('No configuration')) - elif options['disk_layout'] == selection: - if json_config := config_output.disk_layout_to_json(): - return f'{config_output.disk_layout_file}\n{json_config}' + if maybe_serial := config_output.user_credentials_to_json(): + return f'{config_output.user_credentials_file}\n{maybe_serial}' else: return str(_('No configuration')) elif options['all'] == selection: output = f'{config_output.user_configuration_file}\n' - if json_config := config_output.user_credentials_to_json(): + if config_output.user_credentials_to_json(): output += f'{config_output.user_credentials_file}\n' - if json_config := config_output.disk_layout_to_json(): - output += f'{config_output.disk_layout_file}\n' return output[:-1] return None @@ -61,6 +53,9 @@ def save_config(config: Dict): if choice.type_ == MenuSelectionType.Skip: return + save_config_value = choice.single_value + saving_key = [k for k, v in options.items() if v == save_config_value][0] + dirs_to_exclude = [ '/bin', '/dev', @@ -76,19 +71,19 @@ def save_config(config: Dict): '/usr', '/var', ] - log( - _('When picking a directory to save configuration files to,' - ' by default we will ignore the following folders: ') + ','.join(dirs_to_exclude), - level=logging.DEBUG - ) + log('Ignore configuration option folders: ' + ','.join(dirs_to_exclude), level=logging.DEBUG) log(_('Finding possible directories to save configuration files ...'), level=logging.INFO) - + find_exclude = '-path ' + ' -prune -o -path '.join(dirs_to_exclude) + ' -prune ' file_picker_command = f'find / {find_exclude} -o -type d -print0' - possible_save_dirs = list( - filter(None, SysCommand(file_picker_command).decode().split('\x00')) - ) + + directories = SysCommand(file_picker_command).decode() + + if directories is None: + raise ValueError('Failed to retrieve possible configuration directories') + + possible_save_dirs = list(filter(None, directories.split('\x00'))) selection = Menu( _('Select directory (or directories) for saving configuration files'), @@ -101,35 +96,18 @@ def save_config(config: Dict): match selection.type_: case MenuSelectionType.Skip: return - case _: - save_dirs = selection.value - - prompt = _('Do you want to save {} configuration file(s) in the following locations?\n\n{}').format( - list(options.keys())[list(options.values()).index(choice.value)], - save_dirs - ) - save_confirmation = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run() - if save_confirmation == Menu.no(): - return - - log( - _('Saving {} configuration files to {}').format( - list(options.keys())[list(options.values()).index(choice.value)], - save_dirs - ), - level=logging.DEBUG - ) - + + save_dirs = selection.multi_value + + log(f'Saving {saving_key} configuration files to {save_dirs}', level=logging.DEBUG) + if save_dirs is not None: for save_dir_str in save_dirs: save_dir = Path(save_dir_str) - if options['user_config'] == choice.value: + if options['user_config'] == save_config_value: config_output.save_user_config(save_dir) - elif options['user_creds'] == choice.value: + elif options['user_creds'] == save_config_value: config_output.save_user_creds(save_dir) - elif options['disk_layout'] == choice.value: - config_output.save_disk_layout(save_dir) - elif options['all'] == choice.value: + elif options['all'] == save_config_value: config_output.save_user_config(save_dir) config_output.save_user_creds(save_dir) - config_output.save_disk_layout(save_dir) diff --git a/archinstall/lib/user_interaction/subvolume_config.py b/archinstall/lib/user_interaction/subvolume_config.py deleted file mode 100644 index 94150dee..00000000 --- a/archinstall/lib/user_interaction/subvolume_config.py +++ /dev/null @@ -1,98 +0,0 @@ -from typing import Dict, List, Optional, Any, TYPE_CHECKING - -from ..menu.list_manager import ListManager -from ..menu.menu import MenuSelectionType -from ..menu.text_input import TextInput -from ..menu import Menu -from ..models.subvolume import Subvolume -from ... import FormattedOutput - -if TYPE_CHECKING: - _: Any - - -class SubvolumeList(ListManager): - def __init__(self, prompt: str, subvolumes: List[Subvolume]): - self._actions = [ - str(_('Add subvolume')), - str(_('Edit subvolume')), - str(_('Delete subvolume')) - ] - super().__init__(prompt, subvolumes, [self._actions[0]], self._actions[1:]) - - def reformat(self, data: List[Subvolume]) -> Dict[str, Optional[Subvolume]]: - table = FormattedOutput.as_table(data) - rows = table.split('\n') - - # these are the header rows of the table and do not map to any User obviously - # we're adding 2 spaces as prefix because the menu selector '> ' will be put before - # the selectable rows so the header has to be aligned - display_data: Dict[str, Optional[Subvolume]] = {f' {rows[0]}': None, f' {rows[1]}': None} - - for row, subvol in zip(rows[2:], data): - row = row.replace('|', '\\|') - display_data[row] = subvol - - return display_data - - def selected_action_display(self, subvolume: Subvolume) -> str: - return subvolume.name - - def _prompt_options(self, editing: Optional[Subvolume] = None) -> List[str]: - preset_options = [] - if editing: - preset_options = editing.options - - choice = Menu( - str(_("Select the desired subvolume options ")), - ['nodatacow','compress'], - skip=True, - preset_values=preset_options, - multi=True - ).run() - - if choice.type_ == MenuSelectionType.Selection: - return choice.value # type: ignore - - return [] - - def _add_subvolume(self, editing: Optional[Subvolume] = None) -> Optional[Subvolume]: - name = TextInput(f'\n\n{_("Subvolume name")}: ', editing.name if editing else '').run() - - if not name: - return None - - mountpoint = TextInput(f'\n{_("Subvolume mountpoint")}: ', editing.mountpoint if editing else '').run() - - if not mountpoint: - return None - - options = self._prompt_options(editing) - - subvolume = Subvolume(name, mountpoint) - subvolume.compress = 'compress' in options - subvolume.nodatacow = 'nodatacow' in options - - return subvolume - - def handle_action(self, action: str, entry: Optional[Subvolume], data: List[Subvolume]) -> List[Subvolume]: - if action == self._actions[0]: # add - new_subvolume = self._add_subvolume() - - if new_subvolume 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.name != new_subvolume.name] - data += [new_subvolume] - elif entry is not None: - if action == self._actions[1]: # edit subvolume - new_subvolume = self._add_subvolume(entry) - - if new_subvolume is not None: - # we'll remove the original subvolume and add the modified version - data = [d for d in data if d.name != entry.name and d.name != new_subvolume.name] - data += [new_subvolume] - elif action == self._actions[2]: # delete - data = [d for d in data if d != entry] - - return data diff --git a/archinstall/lib/user_interaction/system_conf.py b/archinstall/lib/user_interaction/system_conf.py index e1581677..3f57d0e7 100644 --- a/archinstall/lib/user_interaction/system_conf.py +++ b/archinstall/lib/user_interaction/system_conf.py @@ -1,19 +1,16 @@ from __future__ import annotations -from typing import List, Any, Dict, TYPE_CHECKING +from typing import List, Any, Dict, TYPE_CHECKING, Optional -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 ..menu import MenuSelectionType, Menu +from ..models.bootloader import Bootloader if TYPE_CHECKING: _: Any -def select_kernel(preset: List[str] = None) -> List[str]: +def select_kernel(preset: List[str] = []) -> List[str]: """ Asks the user to select a kernel for system. @@ -39,39 +36,36 @@ def select_kernel(preset: List[str] = None) -> List[str]: match choice.type_: case MenuSelectionType.Skip: return preset case MenuSelectionType.Reset: return [] - case MenuSelectionType.Selection: return choice.value + case MenuSelectionType.Selection: return choice.value # type: ignore -def select_harddrives(preset: List[str] = []) -> List[str]: - """ - Asks the user to select one or multiple hard drives - - :return: List of selected hard drives - :rtype: list - """ - hard_drives = all_blockdevices(partitions=False).values() - options = {f'{option}': option for option in hard_drives} - - 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!')) +def ask_for_bootloader(preset: Bootloader) -> Bootloader: + # when the system only supports grub + if not has_uefi(): + options = [Bootloader.Grub.value] + default = Bootloader.Grub.value + else: + options = Bootloader.values() + default = Bootloader.Systemd.value - warning = str(_('If you reset the harddrive selection this will also reset the current disk layout. Are you sure?')) + preset_value = preset.value if preset else None - selected_harddrive = Menu( - title, - list(options.keys()), - multi=True, - allow_reset=True, - allow_reset_warning_msg=warning + choice = Menu( + _('Choose a bootloader'), + options, + preset_values=preset_value, + sort=False, + default_option=default ).run() - match selected_harddrive.type_: - case MenuSelectionType.Reset: return [] + match choice.type_: case MenuSelectionType.Skip: return preset - case MenuSelectionType.Selection: return [options[i] for i in selected_harddrive.value] + case MenuSelectionType.Selection: return Bootloader(choice.value) + + return preset -def select_driver(options: Dict[str, Any] = AVAILABLE_GFX_DRIVERS) -> str: +def select_driver(options: Dict[str, Any] = {}, current_value: Optional[str] = None) -> Optional[str]: """ Some what convoluted function, whose job is simple. Select a graphics driver from a pre-defined set of popular options. @@ -80,78 +74,31 @@ def select_driver(options: Dict[str, Any] = AVAILABLE_GFX_DRIVERS) -> str: there for appeal to the general public first and edge cases later) """ - drivers = sorted(list(options)) + if not options: + options = AVAILABLE_GFX_DRIVERS + + drivers = sorted(list(options.keys())) if drivers: - arguments = storage.get('arguments', {}) title = '' - if has_amd_graphics(): - 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' + 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' if has_intel_graphics(): - title += str(_( - 'For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n' - )) + 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 += str(_( - 'For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n' - )) + title += str(_('For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n')) - title += str(_('\n\nSelect a graphics driver or leave blank to install all open-source drivers')) - choice = Menu(title, drivers).run() + title += str(_('\nSelect a graphics driver or leave blank to install all open-source drivers')) - if choice.type_ != MenuSelectionType.Selection: - return arguments.get('gfx_driver') + preset = current_value if current_value else None + choice = Menu(title, drivers, preset_values=preset).run() - 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.") + if choice.type_ != MenuSelectionType.Selection: + return None + return choice.value # type: ignore -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': - preset_val = 'grub' if advanced_options else Menu.yes() - else: - preset_val = preset - - bootloader = "systemd-bootctl" if has_uefi() else "grub-install" - - if has_uefi(): - if not advanced_options: - 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.Skip: 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() - - value = '' - match selection.type_: - case MenuSelectionType.Skip: value = preset_val - case MenuSelectionType.Selection: value = selection.value - - if value != "": - if value == 'systemd-boot': - bootloader = 'systemd-bootctl' - elif value == 'grub': - bootloader = 'grub-install' - else: - bootloader = value - - return bootloader + return current_value def ask_for_swap(preset: bool = True) -> bool: @@ -166,3 +113,5 @@ def ask_for_swap(preset: bool = True) -> bool: match choice.type_: case MenuSelectionType.Skip: return preset case MenuSelectionType.Selection: return False if choice.value == Menu.no() else True + + return preset diff --git a/archinstall/lib/user_interaction/utils.py b/archinstall/lib/user_interaction/utils.py index 7ee6fc07..918945c0 100644 --- a/archinstall/lib/user_interaction/utils.py +++ b/archinstall/lib/user_interaction/utils.py @@ -1,13 +1,9 @@ from __future__ import annotations import getpass -import signal -import sys -import time from typing import Any, Optional, TYPE_CHECKING -from ..menu import Menu -from ..models.password_strength import PasswordStrength +from ..models import PasswordStrength from ..output import log if TYPE_CHECKING: @@ -36,44 +32,3 @@ def get_password(prompt: str = '') -> Optional[str]: return password return None - - -def do_countdown() -> bool: - SIG_TRIGGER = False - - def kill_handler(sig: int, frame: Any) -> None: - print() - exit(0) - - def sig_handler(sig: int, frame: Any) -> None: - global SIG_TRIGGER - SIG_TRIGGER = True - signal.signal(signal.SIGINT, kill_handler) - - original_sigint_handler = signal.getsignal(signal.SIGINT) - signal.signal(signal.SIGINT, sig_handler) - - for i in range(5, 0, -1): - print(f"{i}", end='') - - for x in range(4): - sys.stdout.flush() - time.sleep(0.25) - print(".", end='') - - if SIG_TRIGGER: - prompt = _('Do you really want to abort?') - choice = Menu(prompt, Menu.yes_no(), skip=False).run() - if choice.value == Menu.yes(): - exit(0) - - if SIG_TRIGGER is False: - sys.stdin.read() - - SIG_TRIGGER = False - signal.signal(signal.SIGINT, sig_handler) - - print() - signal.signal(signal.SIGINT, original_sigint_handler) - - return True diff --git a/archinstall/lib/utils/__init__.py b/archinstall/lib/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/archinstall/lib/utils/singleton.py b/archinstall/lib/utils/singleton.py new file mode 100644 index 00000000..55be70eb --- /dev/null +++ b/archinstall/lib/utils/singleton.py @@ -0,0 +1,15 @@ +from typing import Dict, Any + + +class _Singleton(type): + """ A metaclass that creates a Singleton base class when called. """ + _instances: Dict[Any, Any] = {} + + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = super().__call__(*args, **kwargs) + return cls._instances[cls] + + +class Singleton(_Singleton('SingletonMeta', (object,), {})): # type: ignore + pass diff --git a/archinstall/lib/utils/util.py b/archinstall/lib/utils/util.py new file mode 100644 index 00000000..ded480ae --- /dev/null +++ b/archinstall/lib/utils/util.py @@ -0,0 +1,30 @@ +from pathlib import Path +from typing import Any, TYPE_CHECKING, Optional + +from ..output import log + +if TYPE_CHECKING: + _: Any + + +def prompt_dir(text: str, header: Optional[str] = None) -> Path: + if header: + print(header) + + while True: + path = input(text).strip(' ') + dest_path = Path(path) + if dest_path.exists() and dest_path.is_dir(): + return dest_path + log(_('Not a valid directory: {}').format(dest_path), fg='red') + + +def is_subpath(first: Path, second: Path): + """ + Check if _first_ a subpath of _second_ + """ + try: + first.relative_to(second) + return True + except ValueError: + return False diff --git a/archinstall/locales/ar/LC_MESSAGES/base.po b/archinstall/locales/ar/LC_MESSAGES/base.po index c9540b38..9c3a6bff 100644 --- a/archinstall/locales/ar/LC_MESSAGES/base.po +++ b/archinstall/locales/ar/LC_MESSAGES/base.po @@ -792,6 +792,108 @@ msgstr "" msgid "The font should be stored as {}" msgstr "" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "حدِّد منطقة زمنية" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "حدِّد واجهة شبكة واحدة للإعداد" + +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "" + +msgid "Existing Partitions" +msgstr "" + +#, fuzzy +msgid "Select a partitioning option" +msgstr "حدِّد منطقة زمنية" + +msgid "Enter the root directory of the mounted devices: " +msgstr "" + +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "" + +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "" + +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "" + +msgid "Current profile selection" +msgstr "" + +msgid "Remove all newly added partitions" +msgstr "" + +msgid "Assign mountpoint" +msgstr "" + +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +msgid "Mark/Unmark as compressed" +msgstr "" + +msgid "Set subvolumes" +msgstr "" + +msgid "Delete partition" +msgstr "" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr "" + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +msgid "Total sectors: {}" +msgstr "" + +msgid "Enter the start sector (default: {}): " +msgstr "" + +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "" + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + msgid "Encryption type" msgstr "" @@ -810,17 +912,118 @@ msgstr "" msgid "Select a FIDO2 device to use for HSM" msgstr "" -msgid "All settings will be reset, are you sure?" +msgid "Use a best-effort default partition layout" msgstr "" -msgid "Back" +msgid "Manual Partitioning" +msgstr "" + +msgid "Pre-mounted configuration" +msgstr "" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +msgid "Configuration" +msgstr "" + msgid "Password" msgstr "" -msgid "Partition encryption" +msgid "All settings will be reset, are you sure?" msgstr "" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +msgid "Installed packages" +msgstr "" + +msgid "Add profile" +msgstr "" + +msgid "Edit profile" +msgstr "" + +msgid "Delete profile" +msgstr "" + +msgid "Profile name: " +msgstr "" + +msgid "The profile name you entered is already in use. Try again" +msgstr "" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "اكتب حزمًا إضافية لتثبيتها (تُفصَل بالمسافات، اتركها فارغة للتخطي):" + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "اكتب حزمًا إضافية لتثبيتها (تُفصَل بالمسافات، اتركها فارغة للتخطي):" + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +msgid "Disk configuration" +msgstr "" + +msgid "Profiles" +msgstr "" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "حدِّد واجهة شبكة واحدة للإعداد" diff --git a/archinstall/locales/base.pot b/archinstall/locales/base.pot index 651fbd58..009dc382 100644 --- a/archinstall/locales/base.pot +++ b/archinstall/locales/base.pot @@ -844,6 +844,119 @@ msgstr "" msgid "The font should be stored as {}" msgstr "" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +msgid "Select an execution mode" +msgstr "" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "" +"Profiles must have unique name, but profile definitions with duplicate name " +"found: {}" +msgstr "" + +msgid "Select one or more devices to use and configure" +msgstr "" + +msgid "" +"If you reset the device selection this will also reset the current disk " +"layout. Are you sure?" +msgstr "" + +msgid "Existing Partitions" +msgstr "" + +msgid "Select a partitioning option" +msgstr "" + +msgid "Enter the root directory of the mounted devices: " +msgstr "" + +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "" + +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "" + +msgid "" +"This is a list of pre-programmed profiles_bck, they might make it easier to " +"install things like desktop environments" +msgstr "" + +msgid "Current profile selection" +msgstr "" + +msgid "Remove all newly added partitions" +msgstr "" + +msgid "Assign mountpoint" +msgstr "" + +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +msgid "Mark/Unmark as compressed" +msgstr "" + +msgid "Set subvolumes" +msgstr "" + +msgid "Delete partition" +msgstr "" + +msgid "Partition" +msgstr "" + +msgid "" +"This partition is currently encrypted, to format it a filesystem has to be " +"specified" +msgstr "" + +msgid "" +"Partition mount-points are relative to inside the installation, the boot " +"would be /boot as an example." +msgstr "" + +msgid "" +"If mountpoint /boot is set, then the partition will also be marked as " +"bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +msgid "Total sectors: {}" +msgstr "" + +msgid "Enter the start sector (default: {}): " +msgstr "" + +msgid "" +"Enter the end sector of the partition (percentage or block number, default: " +"{}): " +msgstr "" + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + msgid "Encryption type" msgstr "" @@ -862,17 +975,123 @@ msgstr "" msgid "Select a FIDO2 device to use for HSM" msgstr "" -msgid "All settings will be reset, are you sure?" +msgid "Use a best-effort default partition layout" msgstr "" -msgid "Back" +msgid "Manual Partitioning" +msgstr "" + +msgid "Pre-mounted configuration" +msgstr "" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +msgid "Configuration" +msgstr "" + msgid "Password" msgstr "" -msgid "Partition encryption" +msgid "All settings will be reset, are you sure?" +msgstr "" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "" +"The proprietary Nvidia driver is not supported by Sway. It is likely that " +"you will run into issues, are you okay with that?" +msgstr "" + +msgid "Installed packages" +msgstr "" + +msgid "Add profile" +msgstr "" + +msgid "Edit profile" +msgstr "" + +msgid "Delete profile" +msgstr "" + +msgid "Profile name: " +msgstr "" + +msgid "The profile name you entered is already in use. Try again" +msgstr "" + +msgid "" +"Packages to be install with this profile (space separated, leave blank to " +"skip): " +msgstr "" + +msgid "" +"Services to be enabled with this profile (space separated, leave blank to " +"skip): " +msgstr "" + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" + +msgid "" +"Sway needs access to your seat (collection of hardware devices i.e. " +"keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +msgid "Disk configuration" +msgstr "" + +msgid "Profiles" +msgstr "" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +msgid "Select directory (or directories) for saving configuration files" msgstr "" diff --git a/archinstall/locales/cs/LC_MESSAGES/base.po b/archinstall/locales/cs/LC_MESSAGES/base.po index a09c6001..76c885aa 100644 --- a/archinstall/locales/cs/LC_MESSAGES/base.po +++ b/archinstall/locales/cs/LC_MESSAGES/base.po @@ -831,6 +831,125 @@ msgstr "" msgid "The font should be stored as {}" msgstr "" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "Zvolte akci pro '{}'" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "Zvolte jeden nebo více pevných disků k použití a konfiguraci" + +#, fuzzy +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "Pokud resetujete výběr disku, také tím resetujete stávající rozdělení. Přejete si pokračovat?" + +#, fuzzy +msgid "Existing Partitions" +msgstr "Přidávání nového oddílu..." + +#, fuzzy +msgid "Select a partitioning option" +msgstr "Smazat oddíl" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "Zadejte adresář pro uložení konfigurace (konfigurací): " + +#, fuzzy +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "Minimální kapacita pro oddíl /home: {}GB\n" + +#, fuzzy +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Minimální kapacita pro oddíl s Arch Linux: {}GB" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "Toto je seznam před-programovaných profilů, které by mohly usnadnit instalaci věcí jako jsou desktopová prostředí" + +#, fuzzy +msgid "Current profile selection" +msgstr "Aktuální rozdělení disku" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "Vytvořit nový oddíl" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "Přiřaďte přípojný bod k oddílu" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Označení/Odznačení oddílu ke zformátování (vymaže data)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "Označit/Odznačit oddíl s kompresí (jen pro btrfs)" + +#, fuzzy +msgid "Set subvolumes" +msgstr "Smazat podsvazek" + +#, fuzzy +msgid "Delete partition" +msgstr "Smazat oddíl" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Přípojné body diskových oddílů jsou relativní uvnitř instalace, například spouštěcí bod by byl /boot." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "Adresář není validní: {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "Zadejte počáteční sektor (procenta nebo číslo bloku, výchozí: {}): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Zadejte koncový sektor oddílu (procenta nebo číslo bloku, např. {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + #, fuzzy msgid "Encryption type" msgstr "Šifrovací heslo" @@ -852,24 +971,135 @@ msgid "Select a FIDO2 device to use for HSM" msgstr "" #, fuzzy -msgid "All settings will be reset, are you sure?" -msgstr "{} obsahuje oddíly ve frontě, toto je odstraní, jste si jisti?" +msgid "Use a best-effort default partition layout" +msgstr "Vymazat všechny vybrané disky a použít chytré výchozí rozdělení oddílů" -msgid "Back" +#, fuzzy +msgid "Manual Partitioning" +msgstr "Ruční konfigurace" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "Žádná konfigurace" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +#, fuzzy +msgid "Configuration" +msgstr "Žádná konfigurace" + #, fuzzy msgid "Password" msgstr "Heslo správce (root)" -msgid "Partition encryption" +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "{} obsahuje oddíly ve frontě, toto je odstraní, jste si jisti?" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "Dodatečné balíčky" + +#, fuzzy +msgid "Add profile" +msgstr "Profil" + +#, fuzzy +msgid "Edit profile" +msgstr "Profil" + +#, fuzzy +msgid "Delete profile" +msgstr "Smazat rozhraní" + +#, fuzzy +msgid "Profile name: " +msgstr "Profil" + +#, fuzzy +msgid "The profile name you entered is already in use. Try again" +msgstr "Zadané uživatelské jméno není platné. Zkuste to znovu" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Zadejte další balíčky k instalaci (oddělené mezerou, ponechte prázdné k přeskočení): " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Zadejte další balíčky k instalaci (oddělené mezerou, ponechte prázdné k přeskočení): " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"Zvolte ovladač grafické karty nebo ponechte prázdné k instalaci všech open-source ovladačů" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" msgstr "" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "Zadejte počáteční sektor (procenta nebo číslo bloku, výchozí: {}): " +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +#, fuzzy +msgid "Disk configuration" +msgstr "Žádná konfigurace" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "Zadejte koncový sektor oddílu (procenta nebo číslo bloku, např. {}): " +#, fuzzy +msgid "Profiles" +msgstr "Profil" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "Zvolte jeden nebo více pevných disků k použití a konfiguraci" diff --git a/archinstall/locales/de/LC_MESSAGES/base.po b/archinstall/locales/de/LC_MESSAGES/base.po index ac677188..e1d01ab8 100644 --- a/archinstall/locales/de/LC_MESSAGES/base.po +++ b/archinstall/locales/de/LC_MESSAGES/base.po @@ -850,6 +850,125 @@ msgstr "" msgid "The font should be stored as {}" msgstr "" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "Wählen sie eine Aktion aus für '{}'" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "Bitte wählen sie eine oder mehrere Laufwerke aus die konfiguriert werden sollen" + +#, fuzzy +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "Wenn sie die Laufwerkkonfiguration ändern, dann wird die Laufwer-layout zurückgesetzt. Sind sie sicher?" + +#, fuzzy +msgid "Existing Partitions" +msgstr "Partitionen werden hinzugefügt..." + +#, fuzzy +msgid "Select a partitioning option" +msgstr "Partition löschen" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "Geben sie eine Ordner an wo die Konfigurationen gespeichert werden sollen: " + +#, fuzzy +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "Minimaler Speicherplatz für /home Partition: {}GB\n" + +#, fuzzy +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Minimaler Speicherplatz für Arch Linux Partition: {}GB" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "Dies ist eine Liste von bereits programmierten Profilen, diese ermöglichen es einfacher Desktop Umgebungen einzustellen" + +#, fuzzy +msgid "Current profile selection" +msgstr "Momentanes Partitionslayout" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "Neue Partition erstellen" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "Mountort für Partition angeben" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Markieren welche Partition formattiert werden soll (alle Daten werden gelöscht)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "Markieren/Unmarkieren Kompression von einer Partition (nur Btrfs) " + +#, fuzzy +msgid "Set subvolumes" +msgstr "Benutzerkonto löschen" + +#, fuzzy +msgid "Delete partition" +msgstr "Partition löschen" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Die Mountorte sind relativ zur Installation, zum Beispiel boot würde gemountet auf /boot" + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "Ordner existiert nicht: {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "Bitte geben Sie den start Sektor ein (in Prozent oder Blocknummer, default: {}): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Bitte geben Sie den end Sektor ein (in Prozent oder Blocknummer, default: {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + #, fuzzy msgid "Encryption type" msgstr "Verschlüsselungspasswort angeben" @@ -872,22 +991,138 @@ msgid "Select a FIDO2 device to use for HSM" msgstr "" #, fuzzy -msgid "All settings will be reset, are you sure?" -msgstr "{} enthält Partitionen in der Warteschlange, dies werden damit entfernt, sind sie sicher?" +msgid "Use a best-effort default partition layout" +msgstr "Alle Laufwerke löschen und ein vorgegebenes Partitionenlayout verwenden" -msgid "Back" +#, fuzzy +msgid "Manual Partitioning" +msgstr "Manuelle konfiguration" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "Keine Konfiguration" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +#, fuzzy +msgid "Configuration" +msgstr "Keine Konfiguration" + #, fuzzy msgid "Password" msgstr "Root Passwort" -msgid "Partition encryption" +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "{} enthält Partitionen in der Warteschlange, dies werden damit entfernt, sind sie sicher?" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" msgstr "" +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "Zus. Packete" + +#, fuzzy +msgid "Add profile" +msgstr "Profile" + +#, fuzzy +msgid "Edit profile" +msgstr "Profile" + +#, fuzzy +msgid "Delete profile" +msgstr "Verbindung löschen" + +#, fuzzy +msgid "Profile name: " +msgstr "Profile" + +msgid "The profile name you entered is already in use. Try again" +msgstr "" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Schreiben Sie zusätzliche Packete die installiert werden sollen mit einem Leerzeichen getrennt (zum Überspringen leer lassen): " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Schreiben Sie zusätzliche Packete die installiert werden sollen mit einem Leerzeichen getrennt (zum Überspringen leer lassen): " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"Bitte wählen sie einen Grafiktreiber aus oder leer lassen um alle open-source Treiber zu installieren" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +#, fuzzy +msgid "Disk configuration" +msgstr "Keine Konfiguration" + +#, fuzzy +msgid "Profiles" +msgstr "Profile" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "Bitte wählen sie eine oder mehrere Laufwerke aus die konfiguriert werden sollen" + #~ msgid "Add :" #~ msgstr "Hinzufügen :" diff --git a/archinstall/locales/el/LC_MESSAGES/base.po b/archinstall/locales/el/LC_MESSAGES/base.po index efcd6b49..2d2d04c7 100644 --- a/archinstall/locales/el/LC_MESSAGES/base.po +++ b/archinstall/locales/el/LC_MESSAGES/base.po @@ -838,6 +838,125 @@ msgstr "" msgid "The font should be stored as {}" msgstr "" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "Επιλέξτε μία ενέργεια για '{}'" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "Επιλέξτε έναν ή περισσότερους σκληρούς δίσκους προς χρήση και διαμόρφωση" + +#, fuzzy +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "Αν επαναφέρετε την επιλογή σκληρού δίσκου αυτό επίσης θα επαναφέρει την τρέχουσα διάταξη δίσκου. Είστε σίγουρη/ος;" + +#, fuzzy +msgid "Existing Partitions" +msgstr "Προσθέτωντας τη διαμέριση...." + +#, fuzzy +msgid "Select a partitioning option" +msgstr "Διαγραφή διαμέρισης" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "Εισάγετε έναν φάκελο για την αποθήκευση της/ων διαμόρφωση/ων: " + +#, fuzzy +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "Ελάχιστη χωρητικότητα για τη διαμέριση /home: {}GB\n" + +#, fuzzy +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Ελάχιστη χωρητικότητα για τη διαμέριση Arch Linux: {}GB" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "Αυτή είναι μία λίστα με προ-προγραμματισμένα προφίλ, που μπορεί να κάνουν την εγκατάσταση πραγμάτων όπως περιβάλλοντα επιφάνειας εργασίας πιο εύκολη" + +#, fuzzy +msgid "Current profile selection" +msgstr "Τρέχουσα διάταξη διαμέρισης" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "Δημιουργία καινούργιας διαμέρισης" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "Εκχώρηση σημείου mount για μία διαμέριση" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Σημείωση/Ξεμαρκάρισμα διαμέρισης προς μορφοποίηση (διαγράφει τα δεδομένα)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "Σημείωση/Ξεμαρκάρισμα μίας διαμέρισως ως συμπιεσμένη (μόνο για btrfs)" + +#, fuzzy +msgid "Set subvolumes" +msgstr "Διαγραφή υποόγκου" + +#, fuzzy +msgid "Delete partition" +msgstr "Διαγραφή διαμέρισης" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Τα σημεία mount της διαμέρισης είναι σχετικά ως προς το εσωτερικό της εγκατάστασης, για παράδειγμα το boot θα ήταν /boot." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "Μη έγκυρος φάκελος: {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "Εισάγετε τον start sector (ποσοστό ή αριθμό block, προκαθορισμένο {}): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Εισάγετε τον end sector της διαμέρισης (ποσοστό ή αριθμό block, πχ: {}) " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + #, fuzzy msgid "Encryption type" msgstr "Κωδικός κρυπτογράφησης" @@ -859,24 +978,135 @@ msgid "Select a FIDO2 device to use for HSM" msgstr "" #, fuzzy -msgid "All settings will be reset, are you sure?" -msgstr "{} περιέχει διαμερίσεις στην ουρά, αυτό θα τις διαγράψει, είστε σίγουρη/ος;" +msgid "Use a best-effort default partition layout" +msgstr "Διαγραφή όλων των επιλεγμένων δίσκων και χρήση μίας προκαθορισμένης διάταξης βέλτιστης προσπάθειας" -msgid "Back" +#, fuzzy +msgid "Manual Partitioning" +msgstr "Χειροκίνητη διαμόρφωση" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "Καμία διαμόρφωση" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +#, fuzzy +msgid "Configuration" +msgstr "Καμία διαμόρφωση" + #, fuzzy msgid "Password" msgstr "Κωδικός root" -msgid "Partition encryption" +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "{} περιέχει διαμερίσεις στην ουρά, αυτό θα τις διαγράψει, είστε σίγουρη/ος;" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "Περαιτέρω πακέτα" + +#, fuzzy +msgid "Add profile" +msgstr "Προφίλ" + +#, fuzzy +msgid "Edit profile" +msgstr "Προφίλ" + +#, fuzzy +msgid "Delete profile" +msgstr "Διαγραφή διεπαφής" + +#, fuzzy +msgid "Profile name: " +msgstr "Προφίλ" + +#, fuzzy +msgid "The profile name you entered is already in use. Try again" +msgstr "Το όνομα χρήστη που εισάγατε δεν είναι έγκυρο. Προσπαθήστε ξανά" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Γράψτε περαιτέρω πακέτα προς εγκατάσταση (χωρισμένα με κενό, αφήστε άδειο για να παραληφθεί): " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Γράψτε περαιτέρω πακέτα προς εγκατάσταση (χωρισμένα με κενό, αφήστε άδειο για να παραληφθεί): " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"Επιλέξτε έναν οδηγώ γραφικών ή αφήστε άδειο για να εγκατασταθούν όλοι οι οδηγοί ανοιχτής πηγής" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" msgstr "" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "Εισάγετε τον start sector (ποσοστό ή αριθμό block, προκαθορισμένο {}): " +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +#, fuzzy +msgid "Disk configuration" +msgstr "Καμία διαμόρφωση" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "Εισάγετε τον end sector της διαμέρισης (ποσοστό ή αριθμό block, πχ: {}) " +#, fuzzy +msgid "Profiles" +msgstr "Προφίλ" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "Επιλέξτε έναν ή περισσότερους σκληρούς δίσκους προς χρήση και διαμόρφωση" diff --git a/archinstall/locales/en/LC_MESSAGES/base.po b/archinstall/locales/en/LC_MESSAGES/base.po index f1722ef9..c95cc951 100644 --- a/archinstall/locales/en/LC_MESSAGES/base.po +++ b/archinstall/locales/en/LC_MESSAGES/base.po @@ -788,6 +788,105 @@ msgstr "" msgid "The font should be stored as {}" msgstr "" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +msgid "Select an execution mode" +msgstr "" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +msgid "Select one or more devices to use and configure" +msgstr "" + +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "" + +msgid "Existing Partitions" +msgstr "" + +msgid "Select a partitioning option" +msgstr "" + +msgid "Enter the root directory of the mounted devices: " +msgstr "" + +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "" + +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "" + +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "" + +msgid "Current profile selection" +msgstr "" + +msgid "Remove all newly added partitions" +msgstr "" + +msgid "Assign mountpoint" +msgstr "" + +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +msgid "Mark/Unmark as compressed" +msgstr "" + +msgid "Set subvolumes" +msgstr "" + +msgid "Delete partition" +msgstr "" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr "" + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +msgid "Total sectors: {}" +msgstr "" + +msgid "Enter the start sector (default: {}): " +msgstr "" + +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "" + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + msgid "Encryption type" msgstr "" @@ -806,17 +905,115 @@ msgstr "" msgid "Select a FIDO2 device to use for HSM" msgstr "" -msgid "All settings will be reset, are you sure?" +msgid "Use a best-effort default partition layout" msgstr "" -msgid "Back" +msgid "Manual Partitioning" +msgstr "" + +msgid "Pre-mounted configuration" +msgstr "" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +msgid "Configuration" +msgstr "" + msgid "Password" msgstr "" -msgid "Partition encryption" +msgid "All settings will be reset, are you sure?" +msgstr "" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +msgid "Installed packages" +msgstr "" + +msgid "Add profile" +msgstr "" + +msgid "Edit profile" +msgstr "" + +msgid "Delete profile" +msgstr "" + +msgid "Profile name: " +msgstr "" + +msgid "The profile name you entered is already in use. Try again" +msgstr "" + +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "" + +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "" + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +msgid "Disk configuration" +msgstr "" + +msgid "Profiles" +msgstr "" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +msgid "Select directory (or directories) for saving configuration files" msgstr "" diff --git a/archinstall/locales/es/LC_MESSAGES/base.po b/archinstall/locales/es/LC_MESSAGES/base.po index 20101de0..1599e7f4 100644 --- a/archinstall/locales/es/LC_MESSAGES/base.po +++ b/archinstall/locales/es/LC_MESSAGES/base.po @@ -836,6 +836,125 @@ msgstr "" msgid "The font should be stored as {}" msgstr "" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "Seleccione una acción para '{}'" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "Seleccione uno o más discos duros para usar y configurar" + +#, fuzzy +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "Si restablece la selección del disco duro, esto también restablecerá el diseño actual del disco. ¿Está seguro?" + +#, fuzzy +msgid "Existing Partitions" +msgstr "Añadiendo partición..." + +#, fuzzy +msgid "Select a partitioning option" +msgstr "Eliminar una partición" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "Introduzca un directorio para guardar la(s) configuración(es): " + +#, fuzzy +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "Capacidad mínima para la partición /home: {}GB\n" + +#, fuzzy +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Capacidad mínima para la partición Arch Linux: {}GB" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "Esta es una lista de perfiles pre-programados, pueden facilitar la instalación de aplicaciones como entornos de escritorio" + +#, fuzzy +msgid "Current profile selection" +msgstr "Distribución actual de las particiones" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "Crear una nueva partición" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "Asignar punto de montaje para una partición" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Marcar/Desmarcar una partición para ser formateada (borra los datos)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "Marcar/Desmarcar una partición como comprimida (solo btrfs)" + +#, fuzzy +msgid "Set subvolumes" +msgstr "Eliminar usuario" + +#, fuzzy +msgid "Delete partition" +msgstr "Eliminar una partición" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Los puntos de montaje de partición son relativos a la instalación, el arranque sería /boot como ejemplo." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "No es un directorio válido: {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "Introduzca el sector de inicio (porcentaje o número de bloque, predeterminado: {}): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Ingrese el sector final de la partición (porcentaje o número de bloque, ej: {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + #, fuzzy msgid "Encryption type" msgstr "Contraseña de cifrado" @@ -858,27 +977,138 @@ msgid "Select a FIDO2 device to use for HSM" msgstr "" #, fuzzy -msgid "All settings will be reset, are you sure?" -msgstr "{} contiene particiones en cola, esto eliminará esas particiones, ¿estás seguro?" +msgid "Use a best-effort default partition layout" +msgstr "Borrar todas las unidades seleccionadas y use un diseño de partición predeterminado de mejor esfuerzo" -msgid "Back" +#, fuzzy +msgid "Manual Partitioning" +msgstr "Configuración manual" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "Sin configuración" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +#, fuzzy +msgid "Configuration" +msgstr "Sin configuración" + #, fuzzy msgid "Password" msgstr "Contraseña de root" -msgid "Partition encryption" +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "{} contiene particiones en cola, esto eliminará esas particiones, ¿estás seguro?" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "Paquetes adicionales" + +#, fuzzy +msgid "Add profile" +msgstr "Perfil" + +#, fuzzy +msgid "Edit profile" +msgstr "Perfil" + +#, fuzzy +msgid "Delete profile" +msgstr "Eliminar intefaz" + +#, fuzzy +msgid "Profile name: " +msgstr "Perfil" + +#, fuzzy +msgid "The profile name you entered is already in use. Try again" +msgstr "El nombre de usuario que ingresó no es válido. Intente nuevamente" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Escriba paquetes adicionales para instalar (separados por espacios, deja en blanco para omitir): " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Escriba paquetes adicionales para instalar (separados por espacios, deja en blanco para omitir): " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"Seleccione un controlador de gráficos o déjelo en blanco para instalar todos los controladores de código abierto" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" msgstr "" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "Introduzca el sector de inicio (porcentaje o número de bloque, predeterminado: {}): " +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +#, fuzzy +msgid "Disk configuration" +msgstr "Sin configuración" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "Ingrese el sector final de la partición (porcentaje o número de bloque, ej: {}): " +#, fuzzy +msgid "Profiles" +msgstr "Perfil" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "Seleccione uno o más discos duros para usar y configurar" #~ msgid "Add :" #~ msgstr "Añadir :" diff --git a/archinstall/locales/fr/LC_MESSAGES/base.mo b/archinstall/locales/fr/LC_MESSAGES/base.mo index 16b8a6cf..22e004e0 100644 Binary files a/archinstall/locales/fr/LC_MESSAGES/base.mo and b/archinstall/locales/fr/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/fr/LC_MESSAGES/base.po b/archinstall/locales/fr/LC_MESSAGES/base.po index 76080e40..87b365e0 100644 --- a/archinstall/locales/fr/LC_MESSAGES/base.po +++ b/archinstall/locales/fr/LC_MESSAGES/base.po @@ -103,7 +103,7 @@ msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " msgstr "" msgid "{} contains queued partitions, this will remove those, are you sure?" -msgstr "{} contient des partitions en file d'attente, cela les supprimera, êtes-vous sûr ?" +msgstr "{} contient des partitions en file d'attente, cela les supprimera, êtes-vous sûr ?" msgid "" "{}\n" @@ -127,7 +127,7 @@ msgid " * Partition mount-points are relative to inside the installation, the bo msgstr " * Les points de montage de la partition sont relatifs à l'intérieur de l'installation, le démarrage serait /boot par exemple." msgid "Select where to mount partition (leave blank to remove mountpoint): " -msgstr "Sélectionner où monter la partition (laisser vide pour supprimer le point de montage) : " +msgstr "Sélectionner où monter la partition (laisser vide pour supprimer le point de montage) : " msgid "" "{}\n" @@ -166,7 +166,7 @@ msgstr "" "Sélectionner la partition sur laquelle définir un système de fichiers" msgid "Enter a desired filesystem type for the partition: " -msgstr "Entrer un type de système de fichiers souhaité pour la partition : " +msgstr "Entrer un type de système de fichiers souhaité pour la partition : " msgid "Archinstall language" msgstr "Langue d'Archinstall" @@ -223,10 +223,10 @@ msgid "Choose which locale encoding to use" msgstr "Choisir quel encodage de paramètres régionaux utiliser" msgid "Select one of the values shown below: " -msgstr "Sélectionner l'une des valeurs ci-dessous : " +msgstr "Sélectionner l'une des valeurs ci-dessous : " msgid "Select one or more of the options below: " -msgstr "Sélectionner une ou plusieurs des options ci-dessous : " +msgstr "Sélectionner une ou plusieurs des options ci-dessous : " msgid "Adding partition...." msgstr "Ajout de la partition...." @@ -235,10 +235,10 @@ msgid "You need to enter a valid fs-type in order to continue. See `man parted` msgstr "Vous devez entrer un type de fs valide pour continuer. Voir `man parted` pour les types de fs valides." msgid "Error: Listing profiles on URL \"{}\" resulted in:" -msgstr "Erreur : la liste des profils sur l'URL \"{}\" a entraîné :" +msgstr "Erreur : la liste des profils sur l'URL \"{}\" a entraîné :" msgid "Error: Could not decode \"{}\" result as JSON:" -msgstr "Erreur : Impossible de décoder le résultat \"{}\" en tant que JSON :" +msgstr "Erreur : Impossible de décoder le résultat \"{}\" en tant que JSON :" msgid "Keyboard layout" msgstr "Disposition du clavier" @@ -309,7 +309,7 @@ msgstr "" "Souhaitez-vous continuer ?" msgid "Re-using partition instance: {}" -msgstr "Réutilisation de l'instance de partition : {}" +msgstr "Réutilisation de l'instance de partition : {}" msgid "Create a new partition" msgstr "Créer une nouvelle partition" @@ -364,19 +364,19 @@ msgid "Suggest partition layout" msgstr "Suggérer la disposition des partitions" msgid "Enter a password: " -msgstr "Entrer un mot de passe : " +msgstr "Entrer un mot de passe : " msgid "Enter a encryption password for {}" msgstr "Entrer un mot de passe de chiffrement pour {}" msgid "Enter disk encryption password (leave blank for no encryption): " -msgstr "Entrer le mot de passe de chiffrement du disque (laisser vide pour aucun chiffrement) : " +msgstr "Entrer le mot de passe de chiffrement du disque (laisser vide pour aucun chiffrement) : " msgid "Create a required super-user with sudo privileges: " -msgstr "Créer un super-utilisateur requis avec les privilèges sudo : " +msgstr "Créer un super-utilisateur requis avec les privilèges sudo : " msgid "Enter root password (leave blank to disable root): " -msgstr "Entrer le mot de passe root (laisser vide pour désactiver root) : " +msgstr "Entrer le mot de passe root (laisser vide pour désactiver root) : " msgid "Password for user \"{}\": " msgstr "Mot de passe pour l'utilisateur \"{}\" : " @@ -385,7 +385,7 @@ msgid "Verifying that additional packages exist (this might take a few seconds)" msgstr "Vérifier que des packages supplémentaires existent (cela peut prendre quelques secondes)" msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" -msgstr "Souhaitez-vous utiliser la synchronisation automatique de l'heure (NTP) avec les serveurs de temps par défaut ?\n" +msgstr "Souhaitez-vous utiliser la synchronisation automatique de l'heure (NTP) avec les serveurs de temps par défaut ?\n" msgid "" "Hardware time and other post-configuration steps might be required in order for NTP to work.\n" @@ -395,7 +395,7 @@ msgstr "" "Pour plus d'informations, veuillez consulter le wiki Arch" msgid "Enter a username to create an additional user (leave blank to skip): " -msgstr "Entrer un nom d'utilisateur pour créer un utilisateur supplémentaire (laisser vide pour ignorer) : " +msgstr "Entrer un nom d'utilisateur pour créer un utilisateur supplémentaire (laisser vide pour ignorer) : " msgid "Use ESC to skip\n" msgstr "Utiliser ESC pour ignorer\n" @@ -588,22 +588,22 @@ msgid "[!] A log file has been created here: {}" msgstr "[!] Un fichier journal a été créé ici : {}" msgid "Would you like to use BTRFS subvolumes with a default structure?" -msgstr "Souhaitez-vous utiliser des sous-volumes BTRFS avec une structure par défaut ?" +msgstr "Souhaitez-vous utiliser des sous-volumes BTRFS avec une structure par défaut ?" msgid "Would you like to use BTRFS compression?" -msgstr "Souhaitez-vous utiliser la compression BTRFS ?" +msgstr "Souhaitez-vous utiliser la compression BTRFS ?" msgid "Would you like to create a separate partition for /home?" -msgstr "Souhaitez-vous créer une partition séparée pour /home ?" +msgstr "Souhaitez-vous créer une partition séparée pour /home ?" msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" msgstr "Les disques sélectionnés n'ont pas la capacité minimale requise pour une suggestion automatique\n" msgid "Minimum capacity for /home partition: {}GB\n" -msgstr "Capacité minimale pour la partition /home : {} Go\n" +msgstr "Capacité minimale pour la partition /home : {} Go\n" msgid "Minimum capacity for Arch Linux partition: {}GB" -msgstr "Capacité minimale pour la partition Arch Linux : {} Go" +msgstr "Capacité minimale pour la partition Arch Linux : {} Go" msgid "Continue" msgstr "Poursuivre" @@ -645,7 +645,7 @@ msgid "Mark/Unmark a partition as compressed (btrfs only)" msgstr "Marquer/Démarquer une partition comme compressée (btrfs uniquement)" msgid "The password you are using seems to be weak, are you sure you want to use it?" -msgstr "Le mot de passe que vous utilisez semble faible, êtes-vous sûr de vouloir l'utiliser ?" +msgstr "Le mot de passe que vous utilisez semble faible, êtes-vous sûr de vouloir l'utiliser ?" msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" msgstr "Fournit une sélection d'environnements de bureau et de gestionnaires de fenêtres en mosaïque, par ex. gnome, kde, sway" @@ -669,16 +669,16 @@ msgid "Press Enter to continue." msgstr "Appuyer sur Entrée pour continuer." msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?" -msgstr "Souhaitez-vous chrooter dans l'installation nouvellement créée et effectuer la configuration post-installation ?" +msgstr "Souhaitez-vous chrooter dans l'installation nouvellement créée et effectuer la configuration post-installation ?" msgid "Are you sure you want to reset this setting?" -msgstr "Voulez-vous vraiment réinitialiser ce paramètre ?" +msgstr "Voulez-vous vraiment réinitialiser ce paramètre ?" msgid "Select one or more hard drives to use and configure\n" msgstr "Sélectionner un ou plusieurs disques durs à utiliser et à configurer\n" msgid "Any modifications to the existing setting will reset the disk layout!" -msgstr "Toute modification du paramètre existant réinitialisera la disposition du disque !" +msgstr "Toute modification du paramètre existant réinitialisera la disposition du disque !" msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" msgstr "Si vous réinitialisez la sélection du disque dur, cela réinitialisera également la disposition actuelle du disque. Êtes-vous sûr ?" @@ -691,7 +691,7 @@ msgid "" "contains queued partitions, this will remove those, are you sure?" msgstr "" "{}\n" -"contient des partitions en file d'attente, cela les supprimera, êtes-vous sûr ?" +"contient des partitions en file d'attente, cela les supprimera, êtes-vous sûr ?" msgid "No audio server" msgstr "Pas de serveur audio" @@ -761,7 +761,7 @@ msgid "The username you entered is invalid. Try again" msgstr "Le nom d'utilisateur que vous avez saisi n'est pas valide. Réessayer" msgid "Should \"{}\" be a superuser (sudo)?" -msgstr "\"{}\" devrait-il être un superutilisateur (sudo) ?" +msgstr "\"{}\" devrait-il être un superutilisateur (sudo) ?" msgid "Select which partitions to encrypt" msgstr "Sélectionner les partitions à chiffrer" @@ -804,13 +804,13 @@ msgstr "" "Note :" msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - Valeur maximale : {max_downloads} (Autorise {max_downloads} téléchargements parallèles, autorise {max_downloads+1} téléchargements à la fois)" +msgstr " - Valeur maximale : {max_downloads} (Autorise {max_downloads} téléchargements parallèles, autorise {max_downloads+1} téléchargements à la fois)" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" msgstr " - Valeur minimale : 1 (Autorise 1 téléchargement parallèle, autorise 2 téléchargements à la fois)" msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" -msgstr " - Désactiver/Défaut : 0 (Désactive le téléchargement parallèle, n'autorise qu'un seul téléchargement à la fois)" +msgstr " - Désactiver/Défaut : 0 (Désactive le téléchargement parallèle, n'autorise qu'un seul téléchargement à la fois)" #, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" @@ -837,6 +837,125 @@ msgstr "Pour pouvoir utiliser cette traduction, veuillez installer manuellement msgid "The font should be stored as {}" msgstr "La police doit être stockée sous {}" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "Sélectionner une action pour '{}'" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "Sélectionner un ou plusieurs disques durs à utiliser et à configurer" + +#, fuzzy +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "Si vous réinitialisez la sélection du disque dur, cela réinitialisera également la disposition actuelle du disque. Êtes-vous sûr ?" + +#, fuzzy +msgid "Existing Partitions" +msgstr "Ajout de la partition...." + +#, fuzzy +msgid "Select a partitioning option" +msgstr "Supprimer une partition" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "Saisir un répertoire pour la ou les configuration(s) à enregistrer : " + +#, fuzzy +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "Capacité minimale pour la partition /home : {} Go\n" + +#, fuzzy +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Capacité minimale pour la partition Arch Linux : {} Go" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "Ceci est une liste de profils préprogrammés, ils pourraient faciliter l'installation d'outils comme les environnements de bureau" + +#, fuzzy +msgid "Current profile selection" +msgstr "Disposition actuelle des partitions" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "Créer une nouvelle partition" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "Attribuer un point de montage pour une partition" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Marquer/Démarquer une partition à formater (efface les données)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "Marquer/Démarquer une partition comme compressée (btrfs uniquement)" + +#, fuzzy +msgid "Set subvolumes" +msgstr "Supprimer le sous-volume" + +#, fuzzy +msgid "Delete partition" +msgstr "Supprimer une partition" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Les points de montage de la partition sont relatifs à l'intérieur de l'installation, le démarrage serait /boot par exemple." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "Répertoire non valide : {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "Entrer le secteur de début (pourcentage ou numéro de bloc, par défaut : {}) : " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Entrer le secteur de fin de la partition (pourcentage ou numéro de bloc, ex : {}) : " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + #, fuzzy msgid "Encryption type" msgstr "Mot de passe de chiffrement" @@ -859,27 +978,138 @@ msgid "Select a FIDO2 device to use for HSM" msgstr "" #, fuzzy -msgid "All settings will be reset, are you sure?" -msgstr "{} contient des partitions en file d'attente, cela les supprimera, êtes-vous sûr ?" +msgid "Use a best-effort default partition layout" +msgstr "Effacer tous les lecteurs sélectionnés et utiliser une disposition de partition par défaut optimale" -msgid "Back" +#, fuzzy +msgid "Manual Partitioning" +msgstr "Configuration manuelle" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "Aucune configuration" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +#, fuzzy +msgid "Configuration" +msgstr "Aucune configuration" + #, fuzzy msgid "Password" msgstr "Mot de passe root" -msgid "Partition encryption" +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "{} contient des partitions en file d'attente, cela les supprimera, êtes-vous sûr ?" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "Packages supplémentaires" + +#, fuzzy +msgid "Add profile" +msgstr "Profil" + +#, fuzzy +msgid "Edit profile" +msgstr "Profil" + +#, fuzzy +msgid "Delete profile" +msgstr "Supprimer l'interface" + +#, fuzzy +msgid "Profile name: " +msgstr "Profil" + +#, fuzzy +msgid "The profile name you entered is already in use. Try again" +msgstr "Le nom d'utilisateur que vous avez saisi n'est pas valide. Réessayer" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Écrire des packages supplémentaires à installer (espaces séparés, laisser vide pour ignorer) : " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Écrire des packages supplémentaires à installer (espaces séparés, laisser vide pour ignorer) : " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" msgstr "" +"\n" +"\n" +"Sélectionner un pilote graphique ou laisser vide pour installer tous les pilotes open-source" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "Entrer le secteur de début (pourcentage ou numéro de bloc, par défaut : {}) : " +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "Entrer le secteur de fin de la partition (pourcentage ou numéro de bloc, ex : {}) : " +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +#, fuzzy +msgid "Disk configuration" +msgstr "Aucune configuration" + +#, fuzzy +msgid "Profiles" +msgstr "Profil" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "Sélectionner un ou plusieurs disques durs à utiliser et à configurer" #, python-brace-format #~ msgid "Edit {origkey} :" diff --git a/archinstall/locales/id/LC_MESSAGES/base.mo b/archinstall/locales/id/LC_MESSAGES/base.mo index 6e707237..b81fe108 100644 Binary files a/archinstall/locales/id/LC_MESSAGES/base.mo and b/archinstall/locales/id/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/id/LC_MESSAGES/base.po b/archinstall/locales/id/LC_MESSAGES/base.po index 6cc19cbf..5ed2f3c8 100644 --- a/archinstall/locales/id/LC_MESSAGES/base.po +++ b/archinstall/locales/id/LC_MESSAGES/base.po @@ -832,46 +832,280 @@ msgid "[Default value: 0] > " msgstr "[Nilai default: 0] > " msgid "To be able to use this translation, please install a font manually that supports the language." -msgstr "Untuk dapat menggunakan terjemahan ini, silakan instal font yang mendukung bahasa tersebut secara manual." +msgstr "" msgid "The font should be stored as {}" -msgstr "Font harus disimpan sebagai {}" +msgstr "" + +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "Pilih tindakan untuk '{}'" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "Pilih satu atau lebih hard drive untuk digunakan dan dikonfigurasi" + +#, fuzzy +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "Jika Anda mengatur ulang pilihan harddrive, ini juga akan mengatur ulang tata letak disk saat ini. Apakah Anda yakin?" + +#, fuzzy +msgid "Existing Partitions" +msgstr "Menambahkan partisi...." + +#, fuzzy +msgid "Select a partitioning option" +msgstr "Hapus partisi" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "Masukkan direktori untuk konfigurasi yang akan disimpan: " + +#, fuzzy +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "Kapasitas minimum untuk partisi /home: {}GB\n" + +#, fuzzy +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Kapasitas minimum untuk partisi Arch Linux: {}GB" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "Ini adalah daftar profil yang telah diprogram sebelumnya, mereka mungkin memudahkan untuk menginstal hal-hal seperti desktop environment" + +#, fuzzy +msgid "Current profile selection" +msgstr "Tata letak partisi saat ini" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "Buat partisi baru" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "Tetapkan titik-mount untuk sebuah partisi" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Tandai/Hapus tanda partisi yang akan diformat (menghapus data)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "Tandai/Hapus tanda partisi sebagai terkompresi (hanya btrfs)" + +#, fuzzy +msgid "Set subvolumes" +msgstr "Hapus subvolume" + +#, fuzzy +msgid "Delete partition" +msgstr "Hapus partisi" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Mount point partisi relatif terhadap di dalam instalasi, boot akan menjadi /boot sebagai contoh." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "Bukan direktori yang valid: {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "Masukkan sektor awal (persentase atau nomor blok, default: {}): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Masukkan sektor akhir partisi (persentase atau nomor blok, mis: {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + +#, fuzzy msgid "Encryption type" -msgstr "Tipe enkripsi" +msgstr "Kata sandi enkripsi" msgid "Partitions" -msgstr "Partisi" +msgstr "" msgid "No HSM devices available" -msgstr "Tidak ada perangkat HSM yang tersedia" +msgstr "" +#, fuzzy msgid "Partitions to be encrypted" -msgstr "Partisi yang akan dienkripsi" +msgstr "Pilih partisi mana yang akan dienkripsi" msgid "Select disk encryption option" -msgstr "Pilih opsi enkripsi disk" +msgstr "" msgid "Select a FIDO2 device to use for HSM" -msgstr "Pilih perangkat FID02 yang akan digunakan untuk HSM" +msgstr "" -msgid "All settings will be reset, are you sure?" -msgstr "Semua pengaturan akan direset, apakah Anda yakin?" +#, fuzzy +msgid "Use a best-effort default partition layout" +msgstr "Hapus semua drive yang dipilih dan gunakan upaya terbaik tata letak partisi default" -msgid "Back" -msgstr "Kembali" +#, fuzzy +msgid "Manual Partitioning" +msgstr "Konfigurasi manual" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "Tidak ada konfigurasi" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" +msgstr "" msgid "Disk encryption" -msgstr "Enkripsi disk" +msgstr "" +#, fuzzy +msgid "Configuration" +msgstr "Tidak ada konfigurasi" + +#, fuzzy msgid "Password" -msgstr "Kata sandi" +msgstr "Kata sandi root" -msgid "Partition encryption" -msgstr "Enkripsi partisi" +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "{} berisi partisi yang mengantri, ini akan menghapusnya, apakah Anda yakin?" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "Masukkan sektor awal (persentase atau nomor blok, default: {}): " +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "Paket tambahan" + +#, fuzzy +msgid "Add profile" +msgstr "Profil" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "Masukkan sektor akhir partisi (persentase atau nomor blok, mis: {}): " +#, fuzzy +msgid "Edit profile" +msgstr "Profil" + +#, fuzzy +msgid "Delete profile" +msgstr "Hapus interface" + +#, fuzzy +msgid "Profile name: " +msgstr "Profil" + +#, fuzzy +msgid "The profile name you entered is already in use. Try again" +msgstr "Nama pengguna yang Anda masukkan tidak valid. Coba lagi" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Ketik paket tambahan untuk diinstal (dipisahkan dengan spasi, biarkan kosong untuk dilewati): " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Ketik paket tambahan untuk diinstal (dipisahkan dengan spasi, biarkan kosong untuk dilewati): " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"Pilih driver grafis atau biarkan kosong untuk menginstal semua driver open-source" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +#, fuzzy +msgid "Disk configuration" +msgstr "Tidak ada konfigurasi" + +#, fuzzy +msgid "Profiles" +msgstr "Profil" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "Pilih satu atau lebih hard drive untuk digunakan dan dikonfigurasi" diff --git a/archinstall/locales/it/LC_MESSAGES/base.po b/archinstall/locales/it/LC_MESSAGES/base.po index 00df1a3f..c4acc04d 100644 --- a/archinstall/locales/it/LC_MESSAGES/base.po +++ b/archinstall/locales/it/LC_MESSAGES/base.po @@ -837,6 +837,125 @@ msgstr "Per poter utilizzare questa traduzione, installa manualmente un font che msgid "The font should be stored as {}" msgstr "Il carattere dovrebbe essere memorizzato come {}" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "Seleziona un'azione per '{}'" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "Selezionare uno o più dischi rigidi da utilizzare e configurare" + +#, fuzzy +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "Se si ripristina la selezione del disco rigido, verrà ripristinato anche il layout del disco corrente. Sei sicuro?" + +#, fuzzy +msgid "Existing Partitions" +msgstr "Aggiungendo la partizione...." + +#, fuzzy +msgid "Select a partitioning option" +msgstr "Elimina una partizione" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "Immettere una directory per le configurazioni da salvare: " + +#, fuzzy +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "Capacità minima per la partizione /home: {}GB\n" + +#, fuzzy +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Capacità minima per la partizione Arch Linux: {}GB" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "Questo è un elenco di profili preprogrammati, che potrebbero semplificare l'installazione di elementi come gli ambienti desktop" + +#, fuzzy +msgid "Current profile selection" +msgstr "Layout della partizione corrente" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "Crea una nuova partizione" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "Assegna punto di montaggio per una partizione" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Seleziona/Deseleziona una partizione da formattare (cancella i dati)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "Seleziona/Deseleziona una partizione come compressa (solo btrfs)" + +#, fuzzy +msgid "Set subvolumes" +msgstr "Elimina sottovolume" + +#, fuzzy +msgid "Delete partition" +msgstr "Elimina una partizione" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * I punti di montaggio della partizione sono relativi all'interno dell'installazione, l'avvio sarebbe per esempio /boot." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "Directory non valida: {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "Inserisci il settore iniziale (percentuale o numero di blocco, predefinito: {}): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Inserisci il settore finale (percentuale o numero di blocco, predefinito: {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + #, fuzzy msgid "Encryption type" msgstr "Password di crittografia" @@ -858,24 +977,135 @@ msgid "Select a FIDO2 device to use for HSM" msgstr "" #, fuzzy -msgid "All settings will be reset, are you sure?" -msgstr "{} contiene partizioni in coda, questo le rimuoverà, sei sicuro?" +msgid "Use a best-effort default partition layout" +msgstr "Cancella tutte le unità selezionate e utilizza un layout di partizione predefinito ottimale" -msgid "Back" +#, fuzzy +msgid "Manual Partitioning" +msgstr "Configurazione manuale" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "Nessuna configurazione" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +#, fuzzy +msgid "Configuration" +msgstr "Nessuna configurazione" + #, fuzzy msgid "Password" msgstr "Password di root" -msgid "Partition encryption" +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "{} contiene partizioni in coda, questo le rimuoverà, sei sicuro?" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "Pacchetti aggiuntivi" + +#, fuzzy +msgid "Add profile" +msgstr "Profilo" + +#, fuzzy +msgid "Edit profile" +msgstr "Profilo" + +#, fuzzy +msgid "Delete profile" +msgstr "Elimina interfaccia" + +#, fuzzy +msgid "Profile name: " +msgstr "Profilo" + +#, fuzzy +msgid "The profile name you entered is already in use. Try again" +msgstr "Il nome utente inserito non è valido. Riprova" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Scrivi pacchetti aggiuntivi da installare (separati da spazi, lascia vuoto per saltare): " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Scrivi pacchetti aggiuntivi da installare (separati da spazi, lascia vuoto per saltare): " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"Seleziona un driver grafico o lascia vuoto per installare tutti i driver open source" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" msgstr "" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "Inserisci il settore iniziale (percentuale o numero di blocco, predefinito: {}): " +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +#, fuzzy +msgid "Disk configuration" +msgstr "Nessuna configurazione" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "Inserisci il settore finale (percentuale o numero di blocco, predefinito: {}): " +#, fuzzy +msgid "Profiles" +msgstr "Profilo" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "Selezionare uno o più dischi rigidi da utilizzare e configurare" diff --git a/archinstall/locales/ka/LC_MESSAGES/base.po b/archinstall/locales/ka/LC_MESSAGES/base.po index c89ec795..7a88f96c 100644 --- a/archinstall/locales/ka/LC_MESSAGES/base.po +++ b/archinstall/locales/ka/LC_MESSAGES/base.po @@ -838,6 +838,126 @@ msgstr "თარგმანის გამოსაყენებლად msgid "The font should be stored as {}" msgstr "ფონტი {}-ში უნდა იყოს შენახული" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "აირჩიეთ ქმედება '{}'-სთვის" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "აირჩიეთ ერთი ან მეტი მყარი დისკი და მოირგეთ" + +#, fuzzy +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "ეს მყარი დისკის არჩევანს და მიმდინარე დისკის განლაგებას საწყის მნიშვნელობებზე დააბრუნებს. დარწმუნებული ბრძანდებით?" + +#, fuzzy +msgid "Existing Partitions" +msgstr "დანაყოფები" + +#, fuzzy +msgid "Select a partitioning option" +msgstr "აირჩიეთ დისკის დაშიფვრის პარამეტრი" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "შეიყვანეთ საქაღალდე, სადაც კონფიგურაცი(ებ)-ი იქნება შენახული: " + +#, fuzzy +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "მინიმალური სივრცე დანაყოფისთვის /home: {}გბ\n" + +#, fuzzy +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "მინიმალური სივრცე ArchLinux-ის დანაყოფისთვის: {}გბ" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "ეს წინასწარ მითითებული პროფილების სიაა. მათი დახმარებით ისეთი რამების, როგორიცაა სამუშაო მაგიდის გარემოები, დაყენება უფრო ადვილია" + +#, fuzzy +msgid "Current profile selection" +msgstr "მიმდინარე დანაყოფების განლაგება" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "ახალი დანაყოფის შექმნა" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "დანაყოფის მიმაგრების წერტილის მინიჭება" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "დანაყოფის დასაფორმატებლობის ჭდის მოხსნა/დადება (მონაცემები წაიშლება)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "დანაყოფზე შეკუმშულობის ჭდის მოხსნა/დადება (მხოლოდ btrfs)" + +#, fuzzy +msgid "Set subvolumes" +msgstr "ქვეტომის წაშლა" + +#, fuzzy +msgid "Delete partition" +msgstr "დანაყოფის წაშლა" + +#, fuzzy +msgid "Partition" +msgstr "დანაყოფები" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * დანაყოფის მიმაგრების წერტილები შედარებითია დაყენების შიგნით. ჩატვირთვა, მაგალითად, /boot შეიძლება, იყოს." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "არასწორი საქაღალდე: {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "შეიყვანეთ საწყისი სექტორი (პროცენტებში ან ბლოკის ნომერი. ნაგულისხმები: {}): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "შეიყვანეთ დანაყოფის ბოლო სექტორი (პროცენტულად ან ბლოკის ნომერი. მაგ: {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + msgid "Encryption type" msgstr "დაშიფვრის ტიპი" @@ -856,23 +976,135 @@ msgstr "აირჩიეთ დისკის დაშიფვრის პ msgid "Select a FIDO2 device to use for HSM" msgstr "აირჩიეთ HSM-სთვის გამოსაყენებელი FIDO2 მოწყობილობა" -msgid "All settings will be reset, are you sure?" -msgstr "ყველა პარამეტრი დაბრუნდება. დარწმუნებული ბრძანდებით?" +#, fuzzy +msgid "Use a best-effort default partition layout" +msgstr "მონიშნულ დისკებზე ყველაფრის წაშლა და დანაყოფების განლაგების საუკეთესო განლაგების გამოყენება" -msgid "Back" +#, fuzzy +msgid "Manual Partitioning" +msgstr "დანაყოფები" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "მორგების გარეშე" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "დანაყოფის დაშიფვრა" + +msgid " ! Formatting {} in " +msgstr "" + +#, fuzzy +msgid "← Back" msgstr "უკან" msgid "Disk encryption" msgstr "დისკის დაშიფვრა" +#, fuzzy +msgid "Configuration" +msgstr "მორგების გარეშე" + msgid "Password" msgstr "პაროლი" -msgid "Partition encryption" -msgstr "დანაყოფის დაშიფვრა" +msgid "All settings will be reset, are you sure?" +msgstr "ყველა პარამეტრი დაბრუნდება. დარწმუნებული ბრძანდებით?" + +msgid "Back" +msgstr "უკან" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "შეიყვანეთ საწყისი სექტორი (პროცენტებში ან ბლოკის ნომერი. ნაგულისხმები: {}): " +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "დამატებითი პაკეტები" + +#, fuzzy +msgid "Add profile" +msgstr "პროფილი" + +#, fuzzy +msgid "Edit profile" +msgstr "პროფილი" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "შეიყვანეთ დანაყოფის ბოლო სექტორი (პროცენტულად ან ბლოკის ნომერი. მაგ: {}): " +#, fuzzy +msgid "Delete profile" +msgstr "ინტერფეისის წაშლა" + +#, fuzzy +msgid "Profile name: " +msgstr "პროფილი" + +#, fuzzy +msgid "The profile name you entered is already in use. Try again" +msgstr "შეყვანილი მომხმარებლის სახელი არასწორია. კიდევ სცადეთ" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "დამატებითი პაკეტები დასაყენებლად (გამოტოვებით გამოყოფილი, გამოსატოვებლად ცარიელი დატოვეთ): " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "დამატებითი პაკეტები დასაყენებლად (გამოტოვებით გამოყოფილი, გამოსატოვებლად ცარიელი დატოვეთ): " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"აირჩიეთ გრაფიკის დრაივერი ან, ღია კოდის მქონე დრაივერის დასაყენებლად, ცარიელი დატოვეთ" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +#, fuzzy +msgid "Disk configuration" +msgstr "მორგების გარეშე" + +#, fuzzy +msgid "Profiles" +msgstr "პროფილი" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "აირჩიეთ ერთი ან მეტი მყარი დისკი და მოირგეთ" diff --git a/archinstall/locales/ko/LC_MESSAGES/base.po b/archinstall/locales/ko/LC_MESSAGES/base.po index 4363031e..3503a816 100644 --- a/archinstall/locales/ko/LC_MESSAGES/base.po +++ b/archinstall/locales/ko/LC_MESSAGES/base.po @@ -838,6 +838,125 @@ msgstr "이 번역을 사용하려면 해당 언어를 지원하는 글꼴을 msgid "The font should be stored as {}" msgstr "글꼴은 {} (으)로 저장해야 합니다" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "'{}' 에 대한 작업 선택" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "사용하고 구성할 하드 드라이브를 하나 이상 선택하세요" + +#, fuzzy +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "하드 드라이브 선택을 재설정하면 현재 디스크 레이아웃도 재설정됩니다. 정말 진행하시겠습니까?" + +#, fuzzy +msgid "Existing Partitions" +msgstr "파티션 추가 중...." + +#, fuzzy +msgid "Select a partitioning option" +msgstr "파티션 제거" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "저장할 구성의 디렉토리를 입력하세요: " + +#, fuzzy +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "Arch Linux 파티션의 최대 용량: {}GB\n" + +#, fuzzy +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Arch Linux 파티션의 최소 용량: {}GB" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "이것은 사전 프로그래밍된 프로필 목록이며 데스크톱 환경과 같은 것을 더 쉽게 설치할 수 있습니다" + +#, fuzzy +msgid "Current profile selection" +msgstr "현재 파티션 레이아웃" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "새 파티션 생성" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "파티션에 대한 마운트 지점 할당" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "포맷할 파티션 표시/표시 해제 (데이터 삭제)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "파티션을 압축된 것으로 표시/표시 해제(btrfs만 해당)" + +#, fuzzy +msgid "Set subvolumes" +msgstr "하위 볼륨 제거" + +#, fuzzy +msgid "Delete partition" +msgstr "파티션 제거" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * 파티션 마운트 포인트는 설치 내부를 기준으로 하며 예를 들어 부팅은 /boot 입니다." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "올바른 디렉터리가 아닙니다: {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "시작 섹터를 입력하세요 (백분율 또는 블록 번호, 기본값: {}): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "파티션의 끝 섹터를 입력하세요 (백분율 또는 블록 번호, 예시: {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + #, fuzzy msgid "Encryption type" msgstr "비밀번호 암호화" @@ -859,24 +978,135 @@ msgid "Select a FIDO2 device to use for HSM" msgstr "" #, fuzzy -msgid "All settings will be reset, are you sure?" -msgstr "{} 에 대기 중인 파티션이 포함되어 있습니다. 그러면 이러한 파티션이 제거됩니다. 정말 진행하시겠습니까?" +msgid "Use a best-effort default partition layout" +msgstr "선택한 모든 드라이브를 지우고 최선의 기본 파티션 레이아웃 사용" -msgid "Back" +#, fuzzy +msgid "Manual Partitioning" +msgstr "수동 구성" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "구성 없음" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +#, fuzzy +msgid "Configuration" +msgstr "구성 없음" + #, fuzzy msgid "Password" msgstr "루트 비밀번호" -msgid "Partition encryption" +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "{} 에 대기 중인 파티션이 포함되어 있습니다. 그러면 이러한 파티션이 제거됩니다. 정말 진행하시겠습니까?" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "추가 패키지" + +#, fuzzy +msgid "Add profile" +msgstr "프로필" + +#, fuzzy +msgid "Edit profile" +msgstr "프로필" + +#, fuzzy +msgid "Delete profile" +msgstr "인터페이스 제거" + +#, fuzzy +msgid "Profile name: " +msgstr "프로필" + +#, fuzzy +msgid "The profile name you entered is already in use. Try again" +msgstr "입력한 사용자 이름이 잘못되었습니다. 다시 시도하세요" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "설치할 추가 패키지 작성하세요 (띄어쓰기로 구분, 건너뛰려면 공백): " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "설치할 추가 패키지 작성하세요 (띄어쓰기로 구분, 건너뛰려면 공백): " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"모든 오픈 소스 드라이버를 설치하려면 그래픽 드라이버를 선택하거나 공백으로 두세요" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" msgstr "" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "시작 섹터를 입력하세요 (백분율 또는 블록 번호, 기본값: {}): " +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +#, fuzzy +msgid "Disk configuration" +msgstr "구성 없음" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "파티션의 끝 섹터를 입력하세요 (백분율 또는 블록 번호, 예시: {}): " +#, fuzzy +msgid "Profiles" +msgstr "프로필" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "사용하고 구성할 하드 드라이브를 하나 이상 선택하세요" diff --git a/archinstall/locales/languages.json b/archinstall/locales/languages.json index 1e33dcde..58a55373 100644 --- a/archinstall/locales/languages.json +++ b/archinstall/locales/languages.json @@ -169,7 +169,7 @@ {"abbr": "tr", "lang": "Turkish", "translated_lang" : "Türkçe"}, {"abbr": "tw", "lang": "Twi"}, {"abbr": "ug", "lang": "Uighur"}, - {"abbr": "uk", "lang": "Ukrainian", "translated_lang": "Українська"}, + {"abbr": "uk", "lang": "Ukrainian"}, {"abbr": "ur", "lang": "Urdu", "translated_lang": "اردو"}, {"abbr": "uz", "lang": "Uzbek"}, {"abbr": "ve", "lang": "Venda"}, diff --git a/archinstall/locales/nl/LC_MESSAGES/base.po b/archinstall/locales/nl/LC_MESSAGES/base.po index 7f3de195..3f97513a 100644 --- a/archinstall/locales/nl/LC_MESSAGES/base.po +++ b/archinstall/locales/nl/LC_MESSAGES/base.po @@ -865,6 +865,122 @@ msgstr "" msgid "The font should be stored as {}" msgstr "" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "Kies een actie voor '{}'" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "Selecteer één of meer in te stellen harde schijven" + +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "" + +#, fuzzy +msgid "Existing Partitions" +msgstr "Bezig met toevoegen van partitie…" + +#, fuzzy +msgid "Select a partitioning option" +msgstr "Partitie verwijderen" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "Voer de naam in van de map waarin de configuratie(s) moet(en) worden vastgelegd: " + +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "" + +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "Dit is een vooraf opgestelde lijst met profielen, welke het installeren van zaken als werkomgevingen vereenvoudigt" + +#, fuzzy +msgid "Current profile selection" +msgstr "Huidige partitie-indeling" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "Partitie aanmaken" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "Aankoppelpunt toekennen aan partitie" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Partitie (de)markeren voor formatteren (alle gegevens worden gewist)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "Partitie (de)markeren voor versleuteling" + +#, fuzzy +msgid "Set subvolumes" +msgstr "Gebruiker verwijderen" + +#, fuzzy +msgid "Delete partition" +msgstr "Partitie verwijderen" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " *Partitie-aankoppelpunten zijn gekoppeld aan de fysieke installatie. Voorbeeld: ‘boot’ wordt ‘/boot’." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "Ongeldige map: {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "Voer de beginsector in (percentage of bloknummer - standaard: {}): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Voer de eindsector in (percentage of bloknummer - bijvoorbeeld: {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + #, fuzzy msgid "Encryption type" msgstr "Versleutelwachtwoord instellen" @@ -887,27 +1003,137 @@ msgid "Select a FIDO2 device to use for HSM" msgstr "" #, fuzzy -msgid "All settings will be reset, are you sure?" -msgstr "‘{}’ bevat in behandeling zijnde partities, welke hierdoor worden verwijderd. Weet u zeker dat u wilt doorgaan?" +msgid "Use a best-effort default partition layout" +msgstr "Alle geselecteerde schijven formatteren en best mogelijke partitie-indeling gebruiken" -msgid "Back" +#, fuzzy +msgid "Manual Partitioning" +msgstr "Configuratie vastleggen" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "Geen configuratie" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +#, fuzzy +msgid "Configuration" +msgstr "Geen configuratie" + #, fuzzy msgid "Password" msgstr "Rootwachtwoord" -msgid "Partition encryption" +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "‘{}’ bevat in behandeling zijnde partities, welke hierdoor worden verwijderd. Weet u zeker dat u wilt doorgaan?" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "Aanvullende pakketten" + +#, fuzzy +msgid "Add profile" +msgstr "Profiel" + +#, fuzzy +msgid "Edit profile" +msgstr "Profiel" + +#, fuzzy +msgid "Delete profile" +msgstr "Gebruiker verwijderen" + +#, fuzzy +msgid "Profile name: " +msgstr "Profiel" + +msgid "The profile name you entered is already in use. Try again" msgstr "" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "Voer de beginsector in (percentage of bloknummer - standaard: {}): " +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Typ de namen van te installeren pakketten (spatiegescheiden - laat leeg om over te slaan): " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Typ de namen van te installeren pakketten (spatiegescheiden - laat leeg om over te slaan): " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"Kies een grafisch stuurprogramma of laat leeg om alle opensource-stuurprogramma's te installeren" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "Voer de eindsector in (percentage of bloknummer - bijvoorbeeld: {}): " +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +#, fuzzy +msgid "Disk configuration" +msgstr "Geen configuratie" + +#, fuzzy +msgid "Profiles" +msgstr "Profiel" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "Selecteer één of meer in te stellen harde schijven" #~ msgid "Add :" #~ msgstr "Toevoegen:" diff --git a/archinstall/locales/pl/LC_MESSAGES/base.po b/archinstall/locales/pl/LC_MESSAGES/base.po index ec4fcbd4..3f6c1dca 100644 --- a/archinstall/locales/pl/LC_MESSAGES/base.po +++ b/archinstall/locales/pl/LC_MESSAGES/base.po @@ -839,6 +839,125 @@ msgstr "" msgid "The font should be stored as {}" msgstr "" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "Wybierz akcję dla '{}'" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "Wybierz jeden lub więcej dysków twardych do użycia i skonfiguruj je" + +#, fuzzy +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "Jeżeli zresetujesz wybór dysków, zresetujesz także obecny układ dysków. Jesteś pewny, że chcesz to zrobić?" + +#, fuzzy +msgid "Existing Partitions" +msgstr "Dodawanie partycji..." + +#, fuzzy +msgid "Select a partitioning option" +msgstr "Usuń partycję" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "Wprowadź katalog, w którym ma zostać zapisana konfiguracja: " + +#, fuzzy +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "Maksymalna pojemność dla partycji /home: {}GB\n" + +#, fuzzy +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Minimalna pojemność dla partycji Arch Linux: {}GB" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "To jest lista wstępnie zaprogramowanych profili, które mogą ułatwić instalację takich rzeczy jak środowiska graficzne" + +#, fuzzy +msgid "Current profile selection" +msgstr "Aktualny układ partycji" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "Utwórz nową partycję" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "Przydzielanie punktu montowania dla partycji" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Zaznacz/odznacz partycję, która ma zostać sformatowana (wymazuje dane)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "Oznacz/odznacz partycje jako skompresowaną (tylko w btrfs)" + +#, fuzzy +msgid "Set subvolumes" +msgstr "Usuń podwolumen" + +#, fuzzy +msgid "Delete partition" +msgstr "Usuń partycję" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Punkty montowania partycji są względne w stosunku do wnętrza instalacji, np. partycja startowa to /boot." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "Nie jest to prawidłowy katalog: {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "Wprowadź sektor początkowy (procent lub numer bloku, domyślnie: {}): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Wprowadź sektor końcowy (procent lub numer bloku, domyślnie: {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + #, fuzzy msgid "Encryption type" msgstr "Hasło szyfrujące" @@ -861,27 +980,138 @@ msgid "Select a FIDO2 device to use for HSM" msgstr "" #, fuzzy -msgid "All settings will be reset, are you sure?" -msgstr "{} zawiera partycje oczekujące w kolejce, to spowoduje ich usunięcie, czy jesteś pewien?" +msgid "Use a best-effort default partition layout" +msgstr "Wymaż wszystkie wybrane dyski i użyj najlepszego domyślnego układu partycji" -msgid "Back" +#, fuzzy +msgid "Manual Partitioning" +msgstr "Ręczna konfiguracja" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "Brak konfiguracji" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +#, fuzzy +msgid "Configuration" +msgstr "Brak konfiguracji" + #, fuzzy msgid "Password" msgstr "Hasło użytkownika root" -msgid "Partition encryption" +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "{} zawiera partycje oczekujące w kolejce, to spowoduje ich usunięcie, czy jesteś pewien?" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "Dodatkowe pakiety" + +#, fuzzy +msgid "Add profile" +msgstr "Profil" + +#, fuzzy +msgid "Edit profile" +msgstr "Profil" + +#, fuzzy +msgid "Delete profile" +msgstr "Usuń interfejs" + +#, fuzzy +msgid "Profile name: " +msgstr "Profil" + +#, fuzzy +msgid "The profile name you entered is already in use. Try again" +msgstr "Wprowadzona nazwa użytkownika jest nieprawidłowa. Spróbuj ponownie" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Wpisz dodatkowe pakiety do zainstalowania (oddzielone spacjami, pozostaw puste aby pominąć): " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Wpisz dodatkowe pakiety do zainstalowania (oddzielone spacjami, pozostaw puste aby pominąć): " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"Wybierz sterownik graficzny lub pozostaw puste pole, aby zainstalować wszystkie sterowniki typu open source" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" msgstr "" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "Wprowadź sektor początkowy (procent lub numer bloku, domyślnie: {}): " +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +#, fuzzy +msgid "Disk configuration" +msgstr "Brak konfiguracji" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "Wprowadź sektor końcowy (procent lub numer bloku, domyślnie: {}): " +#, fuzzy +msgid "Profiles" +msgstr "Profil" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "Wybierz jeden lub więcej dysków twardych do użycia i skonfiguruj je" #~ msgid "Add :" #~ msgstr "Dodaj :" diff --git a/archinstall/locales/pt/LC_MESSAGES/base.po b/archinstall/locales/pt/LC_MESSAGES/base.po index 4f772aae..9f44f02f 100644 --- a/archinstall/locales/pt/LC_MESSAGES/base.po +++ b/archinstall/locales/pt/LC_MESSAGES/base.po @@ -887,6 +887,122 @@ msgstr "" msgid "The font should be stored as {}" msgstr "" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "Selecione uma ação para '{}'" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "Seleciona um ou mais discos rígidos para usar e configurar" + +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "" + +#, fuzzy +msgid "Existing Partitions" +msgstr "Adicionando partição...." + +#, fuzzy +msgid "Select a partitioning option" +msgstr "Eliminar uma partição" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "Introduz um diretório para as configurações a serem guardadas: " + +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "" + +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "Esta é uma lista de perfis pré-programados, podem facilitar a instalação de ambientes de trabalho" + +#, fuzzy +msgid "Current profile selection" +msgstr "Esquema actual da partições" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "Criar uma nova partição" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "Atribuir um ponto de montagem para uma partição" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Marcar/Desmarcar uma partição para ser formatada (apaga os dados)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "Marcar/Desmarcar uma partição como encriptada" + +#, fuzzy +msgid "Set subvolumes" +msgstr "Eliminar Utilizador" + +#, fuzzy +msgid "Delete partition" +msgstr "Eliminar uma partição" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Os pontos de montagem das partições são relativos à dentro da instalação, o boot seria /boot por exemplo." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "Não é uma diretoria válida: {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "Escreve o sector de início (percentagem ou número de bloco, padrão: {}): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Escreve o sector final da partição (percentagem ou número de bloco, ex: {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + #, fuzzy msgid "Encryption type" msgstr "Define a palavra-passe de encriptação" @@ -909,27 +1025,137 @@ msgid "Select a FIDO2 device to use for HSM" msgstr "" #, fuzzy -msgid "All settings will be reset, are you sure?" -msgstr "{} contem partições em fila, isto irá remover essas, tens a certeza?" +msgid "Use a best-effort default partition layout" +msgstr "Limpar todos os discos selecionados e usar um esquema de partições padrão de melhor desempenho" -msgid "Back" +#, fuzzy +msgid "Manual Partitioning" +msgstr "Guardar configuração" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "Nenhuma configuração" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +#, fuzzy +msgid "Configuration" +msgstr "Nenhuma configuração" + #, fuzzy msgid "Password" msgstr "Palavra-passe de root" -msgid "Partition encryption" +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "{} contem partições em fila, isto irá remover essas, tens a certeza?" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "Pacotes adicionais" + +#, fuzzy +msgid "Add profile" +msgstr "Perfil" + +#, fuzzy +msgid "Edit profile" +msgstr "Perfil" + +#, fuzzy +msgid "Delete profile" +msgstr "Eliminar Utilizador" + +#, fuzzy +msgid "Profile name: " +msgstr "Perfil" + +msgid "The profile name you entered is already in use. Try again" msgstr "" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "Escreve o sector de início (percentagem ou número de bloco, padrão: {}): " +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Escreve pacotes adicionais para instalar (separados por espaço, deixa em branco para não instalar nada): " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Escreve pacotes adicionais para instalar (separados por espaço, deixa em branco para não instalar nada): " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"Seleciona um driver de gráficos ou deixa em branco para instalar todos os drivers open-source" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "Escreve o sector final da partição (percentagem ou número de bloco, ex: {}): " +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +#, fuzzy +msgid "Disk configuration" +msgstr "Nenhuma configuração" + +#, fuzzy +msgid "Profiles" +msgstr "Perfil" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "Seleciona um ou mais discos rígidos para usar e configurar" #~ msgid "Add :" #~ msgstr "Adicionar :" diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo index 8cba125a..4dd57dba 100644 Binary files a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo and b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.po b/archinstall/locales/pt_BR/LC_MESSAGES/base.po index 88506ce0..d0881905 100644 --- a/archinstall/locales/pt_BR/LC_MESSAGES/base.po +++ b/archinstall/locales/pt_BR/LC_MESSAGES/base.po @@ -841,6 +841,126 @@ msgstr "Para poder usar esta tradução, instale manualmente uma fonte que supor msgid "The font should be stored as {}" msgstr "A fonte deve ser armazenada como {}" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "Selecione uma ação para '{}'" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "Selecione um ou mais discos rígidos para usar e configurar" + +#, fuzzy +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "Se você redefinir a seleção de unidades isso também redefinirá o layout da unidade atual. Tem certeza?" + +#, fuzzy +msgid "Existing Partitions" +msgstr "Adicionando partição...." + +#, fuzzy +msgid "Select a partitioning option" +msgstr "Deletar uma partição" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "Digite um diretório para as configurações serem salvas: " + +#, fuzzy +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "Capacidade mínima para partição /home : {}GB\n" + +#, fuzzy +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Capacidade mínima para a partição do Arch Linux: {}GB" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "Esta é uma lista de perfis pré-programados, que podem por exemplo facilitar a instalação de ambientes gráficos" + +#, fuzzy +msgid "Current profile selection" +msgstr "Layout de partições atual" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "Criar uma nova partição" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "Atribuir um ponto de montagem para uma partição" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Marcar/Desmarcar uma partição para ser formatada (apaga os dados)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "Marcar/desmarcar a partição como comprimida (apenas btrfs)" + +#, fuzzy +msgid "Set subvolumes" +msgstr "Deletar subvolume" + +#, fuzzy +msgid "Delete partition" +msgstr "Deletar uma partição" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Os pontos de montagem das partições são relativos aos de dentro da instalação, boot por exemplo seria /boot." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "Não é um diretório válido: {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "Digite o setor de início (porcentagem ou número do bloco, padrão: {}): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Digite o setor final da partição (porcentagem ou número de bloco, ex.: {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + +#, fuzzy msgid "Encryption type" msgstr "Tipo de encriptação" @@ -859,43 +979,135 @@ msgstr "Selecione a opção de encriptação de disco" msgid "Select a FIDO2 device to use for HSM" msgstr "Selecione um dispositivo FIDO2 para usar como HSM" -msgid "All settings will be reset, are you sure?" -msgstr "Todas as configurações serão redefinidas,você tem certeza ?" +#, fuzzy +msgid "Use a best-effort default partition layout" +msgstr "Apagar todos os discos selecionados e usar um esquema de partições padrão de melhor desempenho" -msgid "Back" -msgstr "Voltar" +#, fuzzy +msgid "Manual Partitioning" +msgstr "Configuração manual" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "Nenhuma configuração" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "Encriptação de partição" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" +msgstr "" msgid "Disk encryption" msgstr "Encriptação de disco" +#, fuzzy +msgid "Configuration" +msgstr "Nenhuma configuração" + msgid "Password" msgstr "Senha" -msgid "Partition encryption" -msgstr "Encriptação de partição" +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "{} contém partições em fila, isto irá removê-las, tem certeza?" -msgid "When picking a directory to save configuration files to, by default we will ignore the following folders: " +msgid "Back" msgstr "" -msgid "Finding possible directories to save configuration files ..." +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" msgstr "" #, fuzzy -msgid "Select directory (or directories) for saving configuration files" -msgstr "Selecione um ou mais discos rígidos para usar e configurar" +msgid "Installed packages" +msgstr "Pacotes adicionais" + +#, fuzzy +msgid "Add profile" +msgstr "Perfil" + +#, fuzzy +msgid "Edit profile" +msgstr "Perfil" + +#, fuzzy +msgid "Delete profile" +msgstr "Deletar interface" + +#, fuzzy +msgid "Profile name: " +msgstr "Perfil" +#, fuzzy +msgid "The profile name you entered is already in use. Try again" +msgstr "O nome de usuário que você digitou é inválido. Tente novamente" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Digite pacotes adicionais para instalar (separados por espaço, deixe em branco para pular): " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Digite pacotes adicionais para instalar (separados por espaço, deixe em branco para pular): " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy msgid "" -"Do you want to save {} configuration file(s) in the following locations?\n" "\n" -"{}" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"Selecione um driver de vídeo ou deixe em branco para instalar os drivers completamente open-source" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" msgstr "" #, fuzzy -msgid "Saving {} configuration files to {}" -msgstr "Salvar configuração" +msgid "Disk configuration" +msgstr "Nenhuma configuração" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "Digite o setor de início (porcentagem ou número do bloco, padrão: {}): " +#, fuzzy +msgid "Profiles" +msgstr "Perfil" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "Digite o setor final da partição (porcentagem ou número de bloco, ex.: {}): " +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "Selecione um ou mais discos rígidos para usar e configurar" diff --git a/archinstall/locales/ru/LC_MESSAGES/base.mo b/archinstall/locales/ru/LC_MESSAGES/base.mo index 0fe08128..1da3a370 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 1a33881f..b99c6473 100644 --- a/archinstall/locales/ru/LC_MESSAGES/base.po +++ b/archinstall/locales/ru/LC_MESSAGES/base.po @@ -838,6 +838,125 @@ msgstr "Чтобы иметь возможность использовать э msgid "The font should be stored as {}" msgstr "Шрифт должен быть сохранен как {}" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "Для запуска Archinstall требуются привилегии root. Для получения дополнительной информации смотрите --help." + +#, fuzzy +msgid "Select an execution mode" +msgstr "Выберите действие для '{}'" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "Выберите один или несколько жестких дисков для использования и настройте их" + +#, fuzzy +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "Если вы сбросите выбор жесткого диска, это также сбросит текущую разметку диска. Вы уверены?" + +#, fuzzy +msgid "Existing Partitions" +msgstr "Добавление раздела...." + +#, fuzzy +msgid "Select a partitioning option" +msgstr "Удалить раздел" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "Введите каталог для сохранения конфигурации (-ций): " + +#, fuzzy +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "Минимальный размер раздела /home: {}GB\n" + +#, fuzzy +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Минимальный размер раздела Arch Linux: {}GB" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "Это список предварительно запрограммированных профилей, они могут облегчить установку таких вещей, как окружения рабочего стола" + +#, fuzzy +msgid "Current profile selection" +msgstr "Текущая разметка разделов" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "Создать новый раздел" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "Назначить точку монтирования для раздела" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Пометить/снять отметку с раздела, который будет отформатирован (стирание данных)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "Пометить/снять отметку с раздела как сжатый (только для btrfs)" + +#, fuzzy +msgid "Set subvolumes" +msgstr "Удалить подтом" + +#, fuzzy +msgid "Delete partition" +msgstr "Удалить раздел" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Точки монтирования разделов являются относительными внутри установки, например, загрузочный будет /boot." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "Недействительный каталог: {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "Введите начальный сектор (процент или номер блока, по умолчанию: {}): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Введите конечный сектор раздела (процент или номер блока, например: {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + #, fuzzy msgid "Encryption type" msgstr "Пароль шифрования" @@ -859,34 +978,142 @@ msgid "Select a FIDO2 device to use for HSM" msgstr "" #, fuzzy -msgid "All settings will be reset, are you sure?" -msgstr "{} содержит разделы в очереди, это удалит их, вы уверены?" +msgid "Use a best-effort default partition layout" +msgstr "Стереть все выбранные диски и использовать оптимальную схему разделов по умолчанию" -msgid "Back" +#, fuzzy +msgid "Manual Partitioning" +msgstr "Ручная конфигурация" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "Отсутствует конфигурация" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +#, fuzzy +msgid "Configuration" +msgstr "Отсутствует конфигурация" + #, fuzzy msgid "Password" msgstr "Пароль root" -msgid "Partition encryption" +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "{} содержит разделы в очереди, это удалит их, вы уверены?" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "Дополнительные пакеты" + +#, fuzzy +msgid "Add profile" +msgstr "Профиль" + +#, fuzzy +msgid "Edit profile" +msgstr "Профиль" + +#, fuzzy +msgid "Delete profile" +msgstr "Удалить интерфейс" + +#, fuzzy +msgid "Profile name: " +msgstr "Профиль" + +#, fuzzy +msgid "The profile name you entered is already in use. Try again" +msgstr "Введенное вами имя пользователя недействительно. Попробуйте еще раз" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Напишите дополнительные пакеты для установки (разделите пробелами, оставьте пустым, чтобы пропустить): " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Напишите дополнительные пакеты для установки (разделите пробелами, оставьте пустым, чтобы пропустить): " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"Выберите графический драйвер или оставьте пустым, чтобы установить все драйверы с открытым исходным кодом" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" msgstr "" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "Введите начальный сектор (процент или номер блока, по умолчанию: {}): " +#, fuzzy +msgid "Disk configuration" +msgstr "Отсутствует конфигурация" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "Введите конечный сектор раздела (процент или номер блока, например: {}): " +#, fuzzy +msgid "Profiles" +msgstr "Профиль" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "Выберите один или несколько жестких дисков для использования и настройте их" #, python-brace-format #~ msgid "Edit {origkey} :" #~ msgstr "Редактировать {origkey}:" -#~ 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/archinstall/locales/sv/LC_MESSAGES/base.po b/archinstall/locales/sv/LC_MESSAGES/base.po index 61755a2c..e114a266 100644 --- a/archinstall/locales/sv/LC_MESSAGES/base.po +++ b/archinstall/locales/sv/LC_MESSAGES/base.po @@ -846,6 +846,125 @@ msgstr "" msgid "The font should be stored as {}" msgstr "" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "Välj vad du vill göra med '{}'" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "Välj en eller flera hårddiskar som skall användas och konfigureras" + +#, fuzzy +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "Om du återställer hårddiskvalen kommer det också återställa diskuppsättningen. Är du säker?" + +#, fuzzy +msgid "Existing Partitions" +msgstr "Skapar en partition...." + +#, fuzzy +msgid "Select a partitioning option" +msgstr "Ta bort en partition" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "Välj vilken mapp du vill spara konfigurerationerna till: " + +#, fuzzy +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "Minsta kapaciteten för /home är: {}GB\n" + +#, fuzzy +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Minsta kapaciteten för Arch Linux partitionen är: {}GB" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "Detta är en lista med förprogrammerade profiler, dom kan göra installation av exempelvis skrivbordsmiljöer lite enklare" + +#, fuzzy +msgid "Current profile selection" +msgstr "Nuvarande partioneringslayout" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "Skapa en ny partition" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "Välj monteringspunkt för en partition" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Markera/Avmarkera en partition för formatering (tar bort alla data)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "Markera/Avmarkera en partition för komprimering (BTRFS enbart)" + +#, fuzzy +msgid "Set subvolumes" +msgstr "Ta bort användare" + +#, fuzzy +msgid "Delete partition" +msgstr "Ta bort en partition" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Partitionens monteringsplats är relativa till insidan av installationen, boot är exempelvis /boot." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "Inte en giltig mapp: {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "Mata in startsektor (procent eller block-nummer, standard: {}): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Mata in slutsektor för partitionen (procent eller block-nummer, ex: {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + #, fuzzy msgid "Encryption type" msgstr "Välj ett krypterings-lösenord" @@ -868,24 +987,134 @@ msgid "Select a FIDO2 device to use for HSM" msgstr "" #, fuzzy -msgid "All settings will be reset, are you sure?" -msgstr "{} innehåller uppköade partitionen och detta kommer ta bort dessa. Är du säker?" +msgid "Use a best-effort default partition layout" +msgstr "Töm alla partitioner och använd en generiskt rekommenderad partitionslayout" -msgid "Back" +#, fuzzy +msgid "Manual Partitioning" +msgstr "Manuell konfiguration" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "Ingen konfiguration" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +#, fuzzy +msgid "Configuration" +msgstr "Ingen konfiguration" + #, fuzzy msgid "Password" msgstr "root-lösenord" -msgid "Partition encryption" +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "{} innehåller uppköade partitionen och detta kommer ta bort dessa. Är du säker?" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "Extra paket" + +#, fuzzy +msgid "Add profile" +msgstr "Profil" + +#, fuzzy +msgid "Edit profile" +msgstr "Profil" + +#, fuzzy +msgid "Delete profile" +msgstr "Ta bort interface" + +#, fuzzy +msgid "Profile name: " +msgstr "Profil" + +msgid "The profile name you entered is already in use. Try again" +msgstr "" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Skriv ytterligare paket som skall installeras (separerade med mellanslag, lämna tom för att skippa): " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Skriv ytterligare paket som skall installeras (separerade med mellanslag, lämna tom för att skippa): " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"Välj en grafikdrivrutin eller lämna blank för att installera alla med publika drivrutiner" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" msgstr "" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "Mata in startsektor (procent eller block-nummer, standard: {}): " +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +#, fuzzy +msgid "Disk configuration" +msgstr "Ingen konfiguration" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "Mata in slutsektor för partitionen (procent eller block-nummer, ex: {}): " +#, fuzzy +msgid "Profiles" +msgstr "Profil" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "Välj en eller flera hårddiskar som skall användas och konfigureras" diff --git a/archinstall/locales/ta/LC_MESSAGES/base.po b/archinstall/locales/ta/LC_MESSAGES/base.po index 4f1d6762..d430c8e5 100644 --- a/archinstall/locales/ta/LC_MESSAGES/base.po +++ b/archinstall/locales/ta/LC_MESSAGES/base.po @@ -837,6 +837,125 @@ msgstr "" msgid "The font should be stored as {}" msgstr "" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "'{}'க்கான செயலைத் தேர்ந்தெடுக்கவும்" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "பயன்படுத்த மற்றும் கட்டமைக்க ஒன்று அல்லது அதற்கு மேற்பட்ட ஹார்டு டிரைவ்களைத் தேர்ந்தெடுக்கவும்" + +#, fuzzy +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "நீங்கள் ஹார்ட் டிரைவ் தேர்வை மீட்டமைத்தால், இது தற்போதைய வட்டு அமைப்பையும் மீட்டமைக்கும். நீங்கள் உறுதியாக இருக்கிறீர்களா?" + +#, fuzzy +msgid "Existing Partitions" +msgstr "பகிர்வை சேர்க்கிறது...." + +#, fuzzy +msgid "Select a partitioning option" +msgstr "ஒரு பகிர்வை நீக்கவும்" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "உள்ளமைவு(களை) சேமிக்க ஒரு கோப்பகத்தை உள்ளிடவும்: " + +#, fuzzy +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "/home பகிர்வுக்கான குறைந்த பட்ச கொள்ளளவு: {}GB\n" + +#, fuzzy +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "ஆர்ச் லினக்ஸ் பகிர்வுக்கான குறைந்தபட்ச கொள்ளளவு: {}GB" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "இது முன்-திட்டமிடப்பட்ட சுயவிவரங்களின் பட்டியல், அவை டெஸ்க்டாப் சூழல்கள் போன்றவற்றை நிறுவுவதை எளிதாக்கலாம்" + +#, fuzzy +msgid "Current profile selection" +msgstr "தற்போதைய பகிர்வு தளவமைப்பு" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "புதிய பகிர்வை உருவாக்கவும்" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "ஒரு பகிர்வுக்கு ஏற்ற-புள்ளியை ஒதுக்கவும்" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "குறி/குறிநீக்கு வடிவமைக்கப்பட வேண்டிய பகிர்வை (தரவை அழிக்கிறது)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "ஒரு பகிர்வை சுருக்கப்பட்டதாகக் குறிக்கவும்/குறி நீக்கவும் (btrfs மட்டும்)" + +#, fuzzy +msgid "Set subvolumes" +msgstr "துணைத்தொகுதியை நீக்கவும்" + +#, fuzzy +msgid "Delete partition" +msgstr "ஒரு பகிர்வை நீக்கவும்" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * பகிர்வு மவுண்ட்-பாயிண்ட்கள் நிறுவலின் உள்ளே தொடர்புடையவை, துவக்கம் /boot எடுத்துக்காட்டாக இருக்கும்." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "சரியான கோப்பகம் இல்லை: {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "தொடக்கப் பிரிவை உள்ளிடவும் (சதவீதம் அல்லது தொகுதி எண், இயல்புநிலை: {}): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "பகிர்வின் இறுதிப் பகுதியை உள்ளிடவும் (சதவீதம் அல்லது தொகுதி எண், எ.கா: {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + #, fuzzy msgid "Encryption type" msgstr "குறியாக்கம் கடவுச்சொல்" @@ -858,24 +977,135 @@ msgid "Select a FIDO2 device to use for HSM" msgstr "" #, fuzzy -msgid "All settings will be reset, are you sure?" -msgstr "{} வரிசைப்படுத்தப்பட்ட பகிர்வுகளைக் கொண்டுள்ளது, இது அவற்றை அகற்றும், நீங்கள் உறுதியாக இருக்கிறீர்களா?" +msgid "Use a best-effort default partition layout" +msgstr "தேர்ந்தெடுக்கப்பட்ட அனைத்து இயக்கிகளையும் துடைத்து, சிறந்த முயற்சி இயல்புநிலை பகிர்வு அமைப்பைப் பயன்படுத்தவும்" -msgid "Back" +#, fuzzy +msgid "Manual Partitioning" +msgstr "கைமுறை கட்டமைப்பு" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "கட்டமைப்பு இல்லை" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +#, fuzzy +msgid "Configuration" +msgstr "கட்டமைப்பு இல்லை" + #, fuzzy msgid "Password" msgstr "ரூட் கடவுச்சொல்" -msgid "Partition encryption" +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "{} வரிசைப்படுத்தப்பட்ட பகிர்வுகளைக் கொண்டுள்ளது, இது அவற்றை அகற்றும், நீங்கள் உறுதியாக இருக்கிறீர்களா?" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "கூடுதல் தொகுப்புகள்" + +#, fuzzy +msgid "Add profile" +msgstr "சுயவிவரம்" + +#, fuzzy +msgid "Edit profile" +msgstr "சுயவிவரம்" + +#, fuzzy +msgid "Delete profile" +msgstr "இடைமுகத்தை நீக்கு" + +#, fuzzy +msgid "Profile name: " +msgstr "சுயவிவரம்" + +#, fuzzy +msgid "The profile name you entered is already in use. Try again" +msgstr "நீங்கள் உள்ளிட்ட பயனர்பெயர் தவறானது. மீண்டும் முயற்சிக்கவும்" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "நிறுவ கூடுதல் தொகுப்புகளை எழுதவும் (இடம் பிரிக்கப்பட்டது, தவிர்க்க காலியாக விடவும்): " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "நிறுவ கூடுதல் தொகுப்புகளை எழுதவும் (இடம் பிரிக்கப்பட்டது, தவிர்க்க காலியாக விடவும்): " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"கிராபிக்ஸ் இயக்கியைத் தேர்ந்தெடுக்கவும் அல்லது அனைத்து திறந்த மூல இயக்கிகளையும் நிறுவ காலியாக விடவும்" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" msgstr "" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "தொடக்கப் பிரிவை உள்ளிடவும் (சதவீதம் அல்லது தொகுதி எண், இயல்புநிலை: {}): " +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +#, fuzzy +msgid "Disk configuration" +msgstr "கட்டமைப்பு இல்லை" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "பகிர்வின் இறுதிப் பகுதியை உள்ளிடவும் (சதவீதம் அல்லது தொகுதி எண், எ.கா: {}): " +#, fuzzy +msgid "Profiles" +msgstr "சுயவிவரம்" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "பயன்படுத்த மற்றும் கட்டமைக்க ஒன்று அல்லது அதற்கு மேற்பட்ட ஹார்டு டிரைவ்களைத் தேர்ந்தெடுக்கவும்" diff --git a/archinstall/locales/tr/LC_MESSAGES/base.po b/archinstall/locales/tr/LC_MESSAGES/base.po index f17efd3f..df978da8 100644 --- a/archinstall/locales/tr/LC_MESSAGES/base.po +++ b/archinstall/locales/tr/LC_MESSAGES/base.po @@ -846,6 +846,125 @@ msgstr "" msgid "The font should be stored as {}" msgstr "" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "'{}' için bir eylem seçin" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "Kullanmak ve yapılandırmak için bir veya daha fazla sabit disk seçin" + +#, fuzzy +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "Eğer sabit disk seçimini sıfırlarsanız bu ayrıca mevcut disk şemasını da sıfırlayacaktır. Emin misiniz?" + +#, fuzzy +msgid "Existing Partitions" +msgstr "Disk bölümü ekleniyor…." + +#, fuzzy +msgid "Select a partitioning option" +msgstr "Disk bölümü sil" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "Yapılandırma(lar)ın kaydedilmesi için bir dizin girin: " + +#, fuzzy +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "/home disk bölümü için minimum kapasite: {}GB\n" + +#, fuzzy +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Arch Linux disk bölümü için minimum kapasite: {}GB" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "Bu ön-programlanmış profillerin bir listesidir, bunlar masaüstü ortamları gibi şeyler kurmayı kolaylaştırabilir" + +#, fuzzy +msgid "Current profile selection" +msgstr "Mevcut disk bölümü düzeni" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "Yeni disk bölümü oluştur" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "Bir disk bölümü için mount (monte) noktası ata" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Bir disk bölümünü biçimlendirilmek üzere işaretle/işareti kaldır (veriyi temizler)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "Bir disk bölümünü sıkıştırılmış olarak işaretle/işareti kaldır (sadece btrfs)" + +#, fuzzy +msgid "Set subvolumes" +msgstr "Kullanıcı Sil" + +#, fuzzy +msgid "Delete partition" +msgstr "Disk bölümü sil" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Disk bölümü mount (monte) noktaları iç kurulumla ilişkilidir, örnek olarak boot, /boot olacaktır." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "Geçerli bir dizin değil: {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "Başlangıç kesimini girin (yüzde ya da blok numarası, varsayılan: {}): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Disk bölümünün bitiş kesimini girin (yüzde ya da blok numarası, ör: {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + #, fuzzy msgid "Encryption type" msgstr "Şifreleme şifresi" @@ -867,24 +986,134 @@ msgid "Select a FIDO2 device to use for HSM" msgstr "" #, fuzzy -msgid "All settings will be reset, are you sure?" -msgstr "{} işlem sırasında bekleyen disk bölümleri bulunduruyor, bu onları kaldıracak, emin misiniz?" +msgid "Use a best-effort default partition layout" +msgstr "Bütün seçilmiş diskleri temizle ve elden gelen en iyi varsayılan disk bölümü düzenini kullan" -msgid "Back" +#, fuzzy +msgid "Manual Partitioning" +msgstr "Manuel yapılandırma" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "Yapılandırma yok" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +#, fuzzy +msgid "Configuration" +msgstr "Yapılandırma yok" + #, fuzzy msgid "Password" msgstr "Root (kök) şifresi" -msgid "Partition encryption" +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "{} işlem sırasında bekleyen disk bölümleri bulunduruyor, bu onları kaldıracak, emin misiniz?" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "Ek paketler" + +#, fuzzy +msgid "Add profile" +msgstr "Profil" + +#, fuzzy +msgid "Edit profile" +msgstr "Profil" + +#, fuzzy +msgid "Delete profile" +msgstr "Arayüz sil" + +#, fuzzy +msgid "Profile name: " +msgstr "Profil" + +msgid "The profile name you entered is already in use. Try again" +msgstr "" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Kurulacak ek paketleri yazınız (boşlukla ayrılmış, geçmek için boş bırakın): " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Kurulacak ek paketleri yazınız (boşlukla ayrılmış, geçmek için boş bırakın): " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"Bir grafik sürücüsü seçin ya da bütün açık-kaynak sürücüleri kurmak için boş bırakın" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" msgstr "" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "Başlangıç kesimini girin (yüzde ya da blok numarası, varsayılan: {}): " +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +#, fuzzy +msgid "Disk configuration" +msgstr "Yapılandırma yok" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "Disk bölümünün bitiş kesimini girin (yüzde ya da blok numarası, ör: {}): " +#, fuzzy +msgid "Profiles" +msgstr "Profil" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "Kullanmak ve yapılandırmak için bir veya daha fazla sabit disk seçin" diff --git a/archinstall/locales/uk/LC_MESSAGES/base.po b/archinstall/locales/uk/LC_MESSAGES/base.po index ee9740fc..ca019ff3 100644 --- a/archinstall/locales/uk/LC_MESSAGES/base.po +++ b/archinstall/locales/uk/LC_MESSAGES/base.po @@ -837,6 +837,126 @@ msgstr "Щоб мати можливість використовувати це msgid "The font should be stored as {}" msgstr "Шрифт слід зберігати як {}" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "Оберіть дію для \"{}\"" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "Оберіть один або кілька жорстких дисків для використання та налаштування" + +#, fuzzy +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "Якщо ви скинете вибір жорсткого диска, це також скине поточну схему розмітки диска. Ви впевнені?" + +#, fuzzy +msgid "Existing Partitions" +msgstr "Розділи" + +#, fuzzy +msgid "Select a partitioning option" +msgstr "Оберіть параметр шифрування диска" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "Введіть каталог для конфігурацій, які потрібно зберегти: " + +#, fuzzy +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "Мінімальна ємність для розділу /home: {} Гб\n" + +#, fuzzy +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Мінімальна ємність для розділу Arch Linux: {} Гб" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "Це список попередньо запрограмованих профілів, вони можуть спростити встановлення таких речей, як середовища робочого столу" + +#, fuzzy +msgid "Current profile selection" +msgstr "Поточна схема розмітки розділу" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "Створіть новий розділ" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "Призначити точку монтування для розділу" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Позначити/зняти позначку з розділу для форматування (видалить дані)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "Позначити/зняти позначку розділу як стисненого (лише btrfs)" + +#, fuzzy +msgid "Set subvolumes" +msgstr "Видалити підтом" + +#, fuzzy +msgid "Delete partition" +msgstr "Видалити розділ" + +#, fuzzy +msgid "Partition" +msgstr "Розділи" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Точки монтування розділів відносяться до внутрішньої інсталяції, наприклад, завантажувач буде /boot." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "Недійсний каталог: {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "Введіть початковий сектор (відсоток або номер блоку, за замовчуванням: {}): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Введіть кінцевий сектор розділу (відсоток або номер блоку, наприклад: {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + msgid "Encryption type" msgstr "Тип шифрування" @@ -855,23 +975,135 @@ msgstr "Оберіть параметр шифрування диска" msgid "Select a FIDO2 device to use for HSM" msgstr "Оберіть пристрій FIDO2 для використання для HSM" -msgid "All settings will be reset, are you sure?" -msgstr "Усі налаштування буде скинуто, ви впевнені?" +#, fuzzy +msgid "Use a best-effort default partition layout" +msgstr "Стерти усі вибрані диски та використовувати отпимальну схему розмітки розділів за замовчуванням" -msgid "Back" +#, fuzzy +msgid "Manual Partitioning" +msgstr "Розділи" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "Конфігурація відсутня" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "Шифрування розділу" + +msgid " ! Formatting {} in " +msgstr "" + +#, fuzzy +msgid "← Back" msgstr "Назад" msgid "Disk encryption" msgstr "Disk encryption" +#, fuzzy +msgid "Configuration" +msgstr "Конфігурація відсутня" + msgid "Password" msgstr "Пароль" -msgid "Partition encryption" -msgstr "Шифрування розділу" +msgid "All settings will be reset, are you sure?" +msgstr "Усі налаштування буде скинуто, ви впевнені?" + +msgid "Back" +msgstr "Назад" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "Введіть початковий сектор (відсоток або номер блоку, за замовчуванням: {}): " +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "Додаткові пакунки" + +#, fuzzy +msgid "Add profile" +msgstr "Профіль" + +#, fuzzy +msgid "Edit profile" +msgstr "Профіль" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "Введіть кінцевий сектор розділу (відсоток або номер блоку, наприклад: {}): " +#, fuzzy +msgid "Delete profile" +msgstr "Видалити інтерфейс" + +#, fuzzy +msgid "Profile name: " +msgstr "Профіль" + +#, fuzzy +msgid "The profile name you entered is already in use. Try again" +msgstr "Введене вами ім'я користувача недійсне. Спробуйте знову" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Напишіть додаткові пакети для інсталяції (розділені пробілами, залиште порожнім, щоб пропустити): " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Напишіть додаткові пакети для інсталяції (розділені пробілами, залиште порожнім, щоб пропустити): " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"Оберіть графічний драйвер або залиште поле пустим, щоб установити всі драйвери з відкритим вихідним кодом" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +#, fuzzy +msgid "Disk configuration" +msgstr "Конфігурація відсутня" + +#, fuzzy +msgid "Profiles" +msgstr "Профіль" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "Оберіть один або кілька жорстких дисків для використання та налаштування" diff --git a/archinstall/locales/ur/LC_MESSAGES/base.po b/archinstall/locales/ur/LC_MESSAGES/base.po index 4e3d7a10..1a715d90 100644 --- a/archinstall/locales/ur/LC_MESSAGES/base.po +++ b/archinstall/locales/ur/LC_MESSAGES/base.po @@ -867,6 +867,122 @@ msgstr "" msgid "The font should be stored as {}" msgstr "" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "'{}' کے لیے ایک عمل منتخب کریں" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "استعمال کرنے اور کنفیگر کے لیے ایک یا زیادہ ہارڈ ڈرائیوز منتخب کریں" + +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "" + +#, fuzzy +msgid "Existing Partitions" +msgstr "پارٹیشن شامل ہو رہی ہے..." + +#, fuzzy +msgid "Select a partitioning option" +msgstr "ایک پارٹیشن کو حذف کریں" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "محفوظ کیے جانے والے کنفیگریشن کے لیے ایک ڈائرکٹری درج کریں:" + +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "" + +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "یہ پہلے سے پروگرام شدہ پروفائلز کی فہرست ہے، وہ ڈیسک ٹاپ انسٹالیشن جیسی چیزوں کو آسان بناتے ہیں" + +#, fuzzy +msgid "Current profile selection" +msgstr "موجودہ پارٹیشن کی ترتیب" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "ایک نیا پارٹیشن بنائیں" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "پارٹیشن کے لیے ماؤنٹ پوائنٹ تفویض کریں" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "فارمیٹ کرنے کے لیے پارٹیشن کو مارک​​ /انمارک کریں (ڈیٹا صاف کرتا ہے)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "انکرپٹڈ کرنے کے لیے پارٹیشن کو مارک​​ /انمارک کریں" + +#, fuzzy +msgid "Set subvolumes" +msgstr "صارف کو حذف کریں" + +#, fuzzy +msgid "Delete partition" +msgstr "ایک پارٹیشن کو حذف کریں" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr "* پارٹیشن ماؤنٹ پوائنٹس انسٹالیشن کی نسبت سے ہیں، بوٹ مثال کے طور /boot پرہوگا۔" + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "درست ڈائریکٹری نہیں ہے: {}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "اسٹارٹ سیکٹر درج کریں (فیصد یا بلاک نمبر، ڈیفالٹ: {}):" + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "پارٹیشن کا آخری سیکٹر درج کریں (فیصد یا بلاک نمبر، مثال کے طور پر: {}):" + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + #, fuzzy msgid "Encryption type" msgstr "انکرپشن پاس ورڈ سیٹ کریں" @@ -889,27 +1005,138 @@ msgid "Select a FIDO2 device to use for HSM" msgstr "" #, fuzzy -msgid "All settings will be reset, are you sure?" -msgstr "{} پارٹیشنزکے گروپ پر مشتمل ہے، یہ ان کو مٹا دے گا، کیا آپ پر اعتماد ہیں؟" +msgid "Use a best-effort default partition layout" +msgstr "تمام منتخب ڈرائیوز کو صاف کریں اور ایک بہترین ڈیفالٹ پارٹیشن لے آؤٹ استعمال کریں" -msgid "Back" +#, fuzzy +msgid "Manual Partitioning" +msgstr "ترتیب کو محفوظ کریں" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "کوئی کنفیگریشن نہیں" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +#, fuzzy +msgid "Configuration" +msgstr "کوئی کنفیگریشن نہیں" + #, fuzzy msgid "Password" msgstr "روٹ پاس ورڈ" -msgid "Partition encryption" +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "{} پارٹیشنزکے گروپ پر مشتمل ہے، یہ ان کو مٹا دے گا، کیا آپ پر اعتماد ہیں؟" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" msgstr "" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "اسٹارٹ سیکٹر درج کریں (فیصد یا بلاک نمبر، ڈیفالٹ: {}):" +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "اضافی پیکجز" + +#, fuzzy +msgid "Add profile" +msgstr "پروفائل" + +#, fuzzy +msgid "Edit profile" +msgstr "پروفائل" + +#, fuzzy +msgid "Delete profile" +msgstr "صارف کو حذف کریں" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "پارٹیشن کا آخری سیکٹر درج کریں (فیصد یا بلاک نمبر، مثال کے طور پر: {}):" +#, fuzzy +msgid "Profile name: " +msgstr "پروفائل" + +msgid "The profile name you entered is already in use. Try again" +msgstr "" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "انسٹال کرنےکے لیے اضافی پیکجز لکھیں (الگ الگ لیکھیں، نہیں کی صورت میں خالی چھوڑیں):" + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "انسٹال کرنےکے لیے اضافی پیکجز لکھیں (الگ الگ لیکھیں، نہیں کی صورت میں خالی چھوڑیں):" + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"\n" +"ایک گرافکس ڈرائیور منتخب کریں یا تمام اوپن سورس ڈرائیورز کو انسٹال کرنے کے لیے خالی چھوڑ دیں" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +#, fuzzy +msgid "Disk configuration" +msgstr "کوئی کنفیگریشن نہیں" + +#, fuzzy +msgid "Profiles" +msgstr "پروفائل" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "استعمال کرنے اور کنفیگر کے لیے ایک یا زیادہ ہارڈ ڈرائیوز منتخب کریں" #~ msgid "Add :" #~ msgstr "شامل:" diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.po b/archinstall/locales/zh-CN/LC_MESSAGES/base.po index d957204b..f1cee304 100644 --- a/archinstall/locales/zh-CN/LC_MESSAGES/base.po +++ b/archinstall/locales/zh-CN/LC_MESSAGES/base.po @@ -836,6 +836,125 @@ msgstr "为了能够使用此翻译,请手动安装支持该语言的字体。 msgid "The font should be stored as {}" msgstr "字体应存储为 {}" +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +#, fuzzy +msgid "Select an execution mode" +msgstr "为“{}”选择一个操作" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +#, fuzzy +msgid "Select one or more devices to use and configure" +msgstr "选择一个或多个硬盘驱动器来使用和配置" + +#, fuzzy +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "如果您重置硬盘驱动器选择,这也将重置当前磁盘布局。 你确定吗?" + +#, fuzzy +msgid "Existing Partitions" +msgstr "添加分区...." + +#, fuzzy +msgid "Select a partitioning option" +msgstr "删除一个分区" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "输入要保存配置的目录: " + +#, fuzzy +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "/home 分区的最小容量:{}GB\n" + +#, fuzzy +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Arch Linux 分区的最小容量:{}GB" + +#, fuzzy +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "这是预编程配置文件的列表,它们可能使安装桌面环境之类的东西变得更容易" + +#, fuzzy +msgid "Current profile selection" +msgstr "当前分区布局" + +#, fuzzy +msgid "Remove all newly added partitions" +msgstr "创建新分区" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "为分区分配挂载点" + +#, fuzzy +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "标记/取消标记要格式化的分区(擦除数据)" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +#, fuzzy +msgid "Mark/Unmark as compressed" +msgstr "将分区标记/取消标记为压缩(仅限 btrfs)" + +#, fuzzy +msgid "Set subvolumes" +msgstr "删除子卷" + +#, fuzzy +msgid "Delete partition" +msgstr "删除一个分区" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +#, fuzzy +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * 分区挂载点是相对于安装内部的,例如 boot 应该为 /boot。" + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +#, fuzzy +msgid "Total sectors: {}" +msgstr "不是有效的目录:{}" + +#, fuzzy +msgid "Enter the start sector (default: {}): " +msgstr "输入起始扇区(百分比或块号,默认:{}): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "输入分区的结束扇区(百分比或块号,例如:{}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + #, fuzzy msgid "Encryption type" msgstr "加密密码" @@ -857,24 +976,135 @@ msgid "Select a FIDO2 device to use for HSM" msgstr "" #, fuzzy -msgid "All settings will be reset, are you sure?" -msgstr "{} 包含排队分区,这将删除这些分区,您确定吗?" +msgid "Use a best-effort default partition layout" +msgstr "擦除所有选定的驱动器并使用最优的默认分区布局" -msgid "Back" +#, fuzzy +msgid "Manual Partitioning" +msgstr "手动配置" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "无配置" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid "← Back" msgstr "" msgid "Disk encryption" msgstr "" +#, fuzzy +msgid "Configuration" +msgstr "无配置" + #, fuzzy msgid "Password" msgstr "Root 密码" -msgid "Partition encryption" +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "{} 包含排队分区,这将删除这些分区,您确定吗?" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +#, fuzzy +msgid "Installed packages" +msgstr "附加包" + +#, fuzzy +msgid "Add profile" +msgstr "配置文件" + +#, fuzzy +msgid "Edit profile" +msgstr "配置文件" + +#, fuzzy +msgid "Delete profile" +msgstr "删除接口" + +#, fuzzy +msgid "Profile name: " +msgstr "配置文件" + +#, fuzzy +msgid "The profile name you entered is already in use. Try again" +msgstr "您输入的用户名无效。 再试一次" + +#, fuzzy +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "编写要安装的附加软件包(空格分隔,留空跳过): " + +#, fuzzy +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "编写要安装的附加软件包(空格分隔,留空跳过): " + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"选择图形驱动程序或留空以安装所有开源驱动程序" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" msgstr "" -#~ msgid "Enter the start sector (percentage or block number, default: {}): " -#~ msgstr "输入起始扇区(百分比或块号,默认:{}): " +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +#, fuzzy +msgid "Disk configuration" +msgstr "无配置" -#~ msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " -#~ msgstr "输入分区的结束扇区(百分比或块号,例如:{}): " +#, fuzzy +msgid "Profiles" +msgstr "配置文件" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "选择一个或多个硬盘驱动器来使用和配置" diff --git a/archinstall/profiles b/archinstall/profiles deleted file mode 120000 index c2968eea..00000000 --- a/archinstall/profiles +++ /dev/null @@ -1 +0,0 @@ -../profiles/ \ No newline at end of file diff --git a/archinstall/scripts/__init__.py b/archinstall/scripts/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py new file mode 100644 index 00000000..d9c5837c --- /dev/null +++ b/archinstall/scripts/guided.py @@ -0,0 +1,276 @@ +import logging +import os +from pathlib import Path +from typing import Any, TYPE_CHECKING + +import archinstall +from archinstall.lib import disk +from archinstall.lib.global_menu import GlobalMenu +from archinstall.default_profiles.applications.pipewire import PipewireProfile +from archinstall.lib.configuration import ConfigurationOutput +from archinstall.lib.installer import Installer +from archinstall.lib.menu import Menu +from archinstall.lib.mirrors import use_mirrors +from archinstall.lib.models.bootloader import Bootloader +from archinstall.lib.models.network_configuration import NetworkConfigurationHandler +from archinstall.lib.output import log +from archinstall.lib.profile.profiles_handler import profile_handler + +if TYPE_CHECKING: + _: Any + + +if archinstall.arguments.get('help'): + print("See `man archinstall` for help.") + exit(0) + +if os.getuid() != 0: + print(_("Archinstall requires root privileges to run. See --help for more.")) + exit(1) + +# Log various information about hardware before starting the installation. This might assist in troubleshooting +archinstall.log(f"Hardware model detected: {archinstall.sys_vendor()} {archinstall.product_name()}; UEFI mode: {archinstall.has_uefi()}", level=logging.DEBUG) +archinstall.log(f"Processor model detected: {archinstall.cpu_model()}", level=logging.DEBUG) +archinstall.log(f"Memory statistics: {archinstall.mem_available()} available out of {archinstall.mem_total()} total installed", level=logging.DEBUG) +archinstall.log(f"Virtualization detected: {archinstall.virtualization()}; is VM: {archinstall.is_vm()}", level=logging.DEBUG) +archinstall.log(f"Graphics devices detected: {archinstall.graphics_devices().keys()}", level=logging.DEBUG) + +# For support reasons, we'll log the disk layout pre installation to match against post-installation layout +archinstall.log(f"Disk states before installing: {disk.disk_layouts()}", level=logging.DEBUG) + + +def ask_user_questions(): + """ + First, we'll ask the user for a bunch of user input. + Not until we're satisfied with what we want to install + will we continue with the actual installation steps. + """ + + # ref: https://github.com/archlinux/archinstall/pull/831 + # we'll set NTP to true by default since this is also + # the default value specified in the menu options; in + # case it will be changed by the user we'll also update + # the system immediately + global_menu = GlobalMenu(data_store=archinstall.arguments) + + global_menu.enable('archinstall-language') + + global_menu.enable('keyboard-layout') + + # Set which region to download packages from during the installation + global_menu.enable('mirror-region') + + global_menu.enable('sys-language') + + global_menu.enable('sys-encoding') + + global_menu.enable('disk_config', mandatory=True) + + # Specify disk encryption options + global_menu.enable('disk_encryption') + + # Ask which boot-loader to use (will only ask if we're in UEFI mode, otherwise will default to GRUB) + global_menu.enable('bootloader') + + global_menu.enable('swap') + + # Get the hostname for the machine + global_menu.enable('hostname') + + # Ask for a root password (optional, but triggers requirement for super-user if skipped) + global_menu.enable('!root-password', mandatory=True) + + global_menu.enable('!users', mandatory=True) + + # Ask for archinstall-specific profiles_bck (such as desktop environments etc) + global_menu.enable('profile_config') + + # Ask about audio server selection if one is not already set + global_menu.enable('audio') + + # Ask for preferred kernel: + global_menu.enable('kernels') + + global_menu.enable('packages') + + if archinstall.arguments.get('advanced', False): + # Enable parallel downloads + global_menu.enable('parallel downloads') + + # Ask or Call the helper function that asks the user to optionally configure a network. + global_menu.enable('nic') + + global_menu.enable('timezone') + + global_menu.enable('ntp') + + global_menu.enable('additional-repositories') + + global_menu.enable('__separator__') + + global_menu.enable('save_config') + global_menu.enable('install') + global_menu.enable('abort') + + global_menu.run() + + +def perform_installation(mountpoint: Path): + """ + Performs the installation steps on a block device. + Only requirement is that the block devices are + formatted and setup prior to entering this function. + """ + log('Starting installation', level=logging.INFO) + disk_config: disk.DiskLayoutConfiguration = archinstall.arguments['disk_config'] + + # Retrieve list of additional repositories and set boolean values appropriately + enable_testing = 'testing' in archinstall.arguments.get('additional-repositories', []) + enable_multilib = 'multilib' in archinstall.arguments.get('additional-repositories', []) + + locale = f"{archinstall.arguments.get('sys-language', 'en_US')} {archinstall.arguments.get('sys-encoding', 'UTF-8').upper()}" + + disk_encryption: disk.DiskEncryption = archinstall.arguments.get('disk_encryption', None) + + with Installer( + mountpoint, + disk_config, + disk_encryption=disk_encryption, + kernels=archinstall.arguments.get('kernels', ['linux']) + ) as installation: + # Mount all the drives to the desired mountpoint + if disk_config.config_type != disk.DiskLayoutType.Pre_mount: + installation.mount_ordered_layout() + + installation.sanity_check() + + if disk_config.config_type != disk.DiskLayoutType.Pre_mount: + if disk_encryption and disk_encryption.encryption_type != disk.EncryptionType.NoEncryption: + # generate encryption key files for the mounted luks devices + installation.generate_key_files() + + if archinstall.arguments.get('ntp', False): + installation.activate_ntp() + + # Set mirrors used by pacstrap (outside of installation) + if archinstall.arguments.get('mirror-region', None): + use_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors for the live medium + + installation.minimal_installation( + testing=enable_testing, + multilib=enable_multilib, + hostname=archinstall.arguments.get('hostname', 'archlinux'), + locales=[locale] + ) + + if archinstall.arguments.get('mirror-region') is not None: + if archinstall.arguments.get("mirrors", None) is not None: + installation.set_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors in the installation medium + + if archinstall.arguments.get('swap'): + installation.setup_swap('zram') + + if archinstall.arguments.get("bootloader") == Bootloader.Grub and archinstall.has_uefi(): + installation.add_additional_packages("grub") + + installation.add_bootloader(archinstall.arguments["bootloader"]) + + # If user selected to copy the current ISO network configuration + # Perform a copy of the config + network_config = archinstall.arguments.get('nic', None) + + if network_config: + handler = NetworkConfigurationHandler(network_config) + handler.config_installer(installation) + + if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '': + installation.add_additional_packages(archinstall.arguments.get('packages', None)) + + if users := archinstall.arguments.get('!users', None): + installation.create_users(users) + + if audio := archinstall.arguments.get('audio', None): + log(f'Installing audio server: {audio}', level=logging.INFO) + if audio == 'pipewire': + PipewireProfile().install(installation) + elif audio == 'pulseaudio': + installation.add_additional_packages("pulseaudio") + else: + installation.log("No audio server will be installed.", level=logging.INFO) + + if profile_config := archinstall.arguments.get('profile_config', None): + profile_handler.install_profile_config(installation, profile_config) + + if timezone := archinstall.arguments.get('timezone', None): + installation.set_timezone(timezone) + + if archinstall.arguments.get('ntp', False): + installation.activate_time_syncronization() + + if archinstall.accessibility_tools_in_use(): + installation.enable_espeakup() + + if (root_pw := archinstall.arguments.get('!root-password', None)) and len(root_pw): + installation.user_set_pw('root', root_pw) + + # This step must be after profile installs to allow profiles_bck to install language pre-requisits. + # After which, this step will set the language both for console and x11 if x11 was installed for instance. + installation.set_keyboard_language(archinstall.arguments['keyboard-layout']) + + if profile_config := archinstall.arguments.get('profile_config', None): + profile_config.profile.post_install(installation) + + # If the user provided a list of services to be enabled, pass the list to the enable_service function. + # Note that while it's called enable_service, it can actually take a list of services and iterate it. + if archinstall.arguments.get('services', None): + installation.enable_service(*archinstall.arguments['services']) + + # If the user provided custom commands to be run post-installation, execute them now. + if archinstall.arguments.get('custom-commands', None): + archinstall.run_custom_user_commands(archinstall.arguments['custom-commands'], installation) + + installation.genfstab() + + 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 = 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.value == Menu.yes(): + try: + installation.drop_to_shell() + except: + pass + + archinstall.log(f"Disk states after installing: {disk.disk_layouts()}", level=logging.DEBUG) + + +if archinstall.arguments.get('skip-mirror-check', False) is False and archinstall.check_mirror_reachable() is False: + log_file = os.path.join(archinstall.storage.get('LOG_PATH', None), archinstall.storage.get('LOG_FILE', None)) + archinstall.log(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'.", level=logging.INFO, fg="red") + exit(1) + +if not archinstall.arguments.get('silent'): + ask_user_questions() + +config_output = ConfigurationOutput(archinstall.arguments) + +if not archinstall.arguments.get('silent'): + config_output.show() + +config_output.save() + +if archinstall.arguments.get('dry_run'): + exit(0) + +if not archinstall.arguments.get('silent'): + input(str(_('Press Enter to continue.'))) + +fs_handler = disk.FilesystemHandler( + archinstall.arguments['disk_config'], + archinstall.arguments.get('disk_encryption', None) +) + +fs_handler.perform_filesystem_operations() + +perform_installation(archinstall.storage.get('MOUNT_POINT', Path('/mnt'))) diff --git a/archinstall/scripts/minimal.py b/archinstall/scripts/minimal.py new file mode 100644 index 00000000..0cdbdcef --- /dev/null +++ b/archinstall/scripts/minimal.py @@ -0,0 +1,104 @@ +from pathlib import Path +from typing import TYPE_CHECKING, Any, List + +import archinstall +from archinstall import ConfigurationOutput, Installer, ProfileConfiguration, profile_handler +from archinstall.default_profiles.minimal import MinimalProfile +from archinstall import disk +from archinstall import models +from archinstall.lib.user_interaction.disk_conf import select_devices, suggest_single_disk_layout + +if TYPE_CHECKING: + _: Any + + +archinstall.log("Minimal only supports:") +archinstall.log(" * Being installed to a single disk") + +if archinstall.arguments.get('help', None): + archinstall.log(" - Optional disk encryption via --!encryption-password=") + archinstall.log(" - Optional filesystem type via --filesystem=") + archinstall.log(" - Optional systemd network via --network") + + +def perform_installation(mountpoint: Path): + disk_config: disk.DiskLayoutConfiguration = archinstall.arguments['disk_config'] + disk_encryption: disk.DiskEncryption = archinstall.arguments.get('disk_encryption', None) + + with Installer( + mountpoint, + disk_config, + disk_encryption=disk_encryption, + kernels=archinstall.arguments.get('kernels', ['linux']) + ) as installation: + # Strap in the base system, add a boot loader and configure + # some other minor details as specified by this profile and user. + if installation.minimal_installation(): + installation.set_hostname('minimal-arch') + installation.add_bootloader(models.Bootloader.Systemd) + + # Optionally enable networking: + if archinstall.arguments.get('network', None): + installation.copy_iso_network_config(enable_services=True) + + installation.add_additional_packages(['nano', 'wget', 'git']) + + profile_config = ProfileConfiguration(MinimalProfile()) + profile_handler.install_profile_config(installation, profile_config) + + user = models.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. + archinstall.log("There are two new accounts in your installation after reboot:") + archinstall.log(" * root (password: airoot)") + archinstall.log(" * devel (password: devel)") + + +def prompt_disk_layout(): + fs_type = None + if filesystem := archinstall.arguments.get('filesystem', None): + fs_type = disk.FilesystemType(filesystem) + + devices = select_devices() + modifications = suggest_single_disk_layout(devices[0], filesystem_type=fs_type) + + archinstall.arguments['disk_config'] = disk.DiskLayoutConfiguration( + config_type=disk.DiskLayoutType.Default, + device_modifications=[modifications] + ) + + +def parse_disk_encryption(): + if enc_password := archinstall.arguments.get('!encryption-password', None): + modification: List[disk.DeviceModification] = archinstall.arguments['disk_config'] + partitions: List[disk.PartitionModification] = [] + + # encrypt all partitions except the /boot + for mod in modification: + partitions += list(filter(lambda x: x.mountpoint != Path('/boot'), mod.partitions)) + + archinstall.arguments['disk_encryption'] = disk.DiskEncryption( + encryption_type=disk.EncryptionType.Partition, + encryption_password=enc_password, + partitions=partitions + ) + + +prompt_disk_layout() +parse_disk_encryption() + +config_output = ConfigurationOutput(archinstall.arguments) +config_output.show() + +input(str(_('Press Enter to continue.'))) + +fs_handler = disk.FilesystemHandler( + archinstall.arguments['disk_config'], + archinstall.arguments.get('disk_encryption', None) +) + +fs_handler.perform_filesystem_operations() + +perform_installation(archinstall.storage.get('MOUNT_POINT', Path('/mnt'))) diff --git a/archinstall/scripts/only_hd.py b/archinstall/scripts/only_hd.py new file mode 100644 index 00000000..a903c5fe --- /dev/null +++ b/archinstall/scripts/only_hd.py @@ -0,0 +1,104 @@ +import logging +import os +from pathlib import Path + +import archinstall +from archinstall import Installer +from archinstall.lib.configuration import ConfigurationOutput +from archinstall import disk + +if archinstall.arguments.get('help'): + print("See `man archinstall` for help.") + exit(0) + + +if os.getuid() != 0: + print("Archinstall requires root privileges to run. See --help for more.") + exit(1) + + +def ask_user_questions(): + global_menu = archinstall.GlobalMenu(data_store=archinstall.arguments) + + global_menu.enable('archinstall-language') + + global_menu.enable('disk_config', mandatory=True) + global_menu.enable('disk_encryption') + global_menu.enable('swap') + + global_menu.enable('save_config') + global_menu.enable('install') + global_menu.enable('abort') + + global_menu.run() + + +def perform_installation(mountpoint: Path): + """ + Performs the installation steps on a block device. + Only requirement is that the block devices are + formatted and setup prior to entering this function. + """ + disk_config: disk.DiskLayoutConfiguration = archinstall.arguments['disk_config'] + disk_encryption: disk.DiskEncryption = archinstall.arguments.get('disk_encryption', None) + + with Installer( + mountpoint, + disk_config, + disk_encryption=disk_encryption, + kernels=archinstall.arguments.get('kernels', ['linux']) + ) as installation: + # Mount all the drives to the desired mountpoint + # This *can* be done outside of the installation, but the installer can deal with it. + if archinstall.arguments.get('disk_config'): + installation.mount_ordered_layout() + + # to generate a fstab directory holder. Avoids an error on exit and at the same time checks the procedure + target = Path(f"{mountpoint}/etc/fstab") + if not target.parent.exists(): + target.parent.mkdir(parents=True) + + # For support reasons, we'll log the disk layout post installation (crash or no crash) + archinstall.log(f"Disk states after installing: {disk.disk_layouts()}", level=logging.DEBUG) + + +# Log various information about hardware before starting the installation. This might assist in troubleshooting +archinstall.log(f"Hardware model detected: {archinstall.sys_vendor()} {archinstall.product_name()}; UEFI mode: {archinstall.has_uefi()}", level=logging.DEBUG) +archinstall.log(f"Processor model detected: {archinstall.cpu_model()}", level=logging.DEBUG) +archinstall.log(f"Memory statistics: {archinstall.mem_available()} available out of {archinstall.mem_total()} total installed", level=logging.DEBUG) +archinstall.log(f"Virtualization detected: {archinstall.virtualization()}; is VM: {archinstall.is_vm()}", level=logging.DEBUG) +archinstall.log(f"Graphics devices detected: {archinstall.graphics_devices().keys()}", level=logging.DEBUG) + +# For support reasons, we'll log the disk layout pre installation to match against post-installation layout +archinstall.log(f"Disk states before installing: {disk.disk_layouts()}", level=logging.DEBUG) + + +if archinstall.arguments.get('skip-mirror-check', False) is False and archinstall.check_mirror_reachable() is False: + log_file = os.path.join(archinstall.storage.get('LOG_PATH', None), archinstall.storage.get('LOG_FILE', None)) + archinstall.log(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'.", level=logging.INFO, fg="red") + exit(1) + +if not archinstall.arguments.get('silent'): + ask_user_questions() + +config_output = ConfigurationOutput(archinstall.arguments) +if not archinstall.arguments.get('silent'): + config_output.show() + +config_output.save() + +if archinstall.arguments.get('dry_run'): + exit(0) + +if not archinstall.arguments.get('silent'): + input('Press Enter to continue.') + +fs_handler = disk.FilesystemHandler( + archinstall.arguments['disk_config'], + archinstall.arguments.get('disk_encryption', None) +) + +fs_handler.perform_filesystem_operations() + + +perform_installation(archinstall.storage.get('MOUNT_POINT', Path('/mnt'))) diff --git a/archinstall/scripts/swiss.py b/archinstall/scripts/swiss.py new file mode 100644 index 00000000..e2ee6fcb --- /dev/null +++ b/archinstall/scripts/swiss.py @@ -0,0 +1,353 @@ +import logging +import os +from enum import Enum +from pathlib import Path +from typing import TYPE_CHECKING, Any, Dict + +import archinstall +from archinstall.lib.mirrors import use_mirrors +from archinstall import models +from archinstall import disk +from archinstall.lib.profile.profiles_handler import profile_handler +from archinstall import menu +from archinstall.lib.global_menu import GlobalMenu +from archinstall.lib.output import log +from archinstall import Installer +from archinstall.lib.configuration import ConfigurationOutput +from archinstall.default_profiles.applications.pipewire import PipewireProfile + +if TYPE_CHECKING: + _: Any + + +if archinstall.arguments.get('help'): + print("See `man archinstall` for help.") + exit(0) + + +if os.getuid() != 0: + print("Archinstall requires root privileges to run. See --help for more.") + exit(1) + + +class ExecutionMode(Enum): + Full = 'full' + Lineal = 'lineal' + Only_HD = 'only-hd' + Only_OS = 'only-os' + Minimal = 'minimal' + + +def select_mode() -> ExecutionMode: + options = [str(e.value) for e in ExecutionMode] + choice = menu.Menu( + str(_('Select an execution mode')), + options, + default_option=ExecutionMode.Full.value, + skip=False + ).run() + + return ExecutionMode(choice.single_value) + + +class SetupMenu(GlobalMenu): + def __init__(self, storage_area: Dict[str, Any]): + super().__init__(data_store=storage_area) + + def setup_selection_menu_options(self): + super().setup_selection_menu_options() + + self._menu_options['mode'] = menu.Selector( + 'Excution mode', + lambda x : select_mode(), + display_func=lambda x: x.value if x else '', + default=ExecutionMode.Full) + + self._menu_options['continue'] = menu.Selector( + 'Continue', + exec_func=lambda n,v: True) + + self.enable('archinstall-language') + self.enable('ntp') + self.enable('mode') + self.enable('continue') + self.enable('abort') + + def exit_callback(self): + if self._data_store.get('ntp', False): + archinstall.SysCommand('timedatectl set-ntp true') + + if self._data_store.get('mode', None): + archinstall.arguments['mode'] = self._data_store['mode'] + log(f"Archinstall will execute under {archinstall.arguments['mode']} mode") + + +class SwissMainMenu(GlobalMenu): + def __init__( + self, + data_store: Dict[str, Any], + exec_mode: ExecutionMode = ExecutionMode.Full + ): + self._execution_mode = exec_mode + super().__init__(data_store) + + def setup_selection_menu_options(self): + super().setup_selection_menu_options() + + options_list = [] + mandatory_list = [] + + match self._execution_mode: + case ExecutionMode.Full | ExecutionMode.Lineal: + options_list = [ + 'keyboard-layout', 'mirror-region', 'disk_config', + 'disk_encryption', 'swap', 'bootloader', 'hostname', '!root-password', + '!users', 'profile_config', 'audio', 'kernels', 'packages', 'additional-repositories', 'nic', + 'timezone', 'ntp' + ] + + if archinstall.arguments.get('advanced', False): + options_list.extend(['sys-language', 'sys-encoding']) + + mandatory_list = ['disk_config', 'bootloader', 'hostname'] + case ExecutionMode.Only_HD: + options_list = ['disk_config', 'disk_encryption','swap'] + mandatory_list = ['disk_config'] + case ExecutionMode.Only_OS: + options_list = [ + 'keyboard-layout', 'mirror-region','bootloader', 'hostname', + '!root-password', '!users', 'profile_config', 'audio', 'kernels', + 'packages', 'additional-repositories', 'nic', 'timezone', 'ntp' + ] + + mandatory_list = ['hostname'] + + if archinstall.arguments.get('advanced', False): + options_list += ['sys-language','sys-encoding'] + case ExecutionMode.Minimal: + pass + case _: + archinstall.log(f' Execution mode {self._execution_mode} not supported') + exit(1) + + if self._execution_mode != ExecutionMode.Lineal: + options_list += ['save_config', 'install'] + + if not archinstall.arguments.get('advanced', False): + options_list.append('archinstall-language') + + options_list += ['abort'] + + for entry in mandatory_list: + self.enable(entry, mandatory=True) + + for entry in options_list: + self.enable(entry) + + +def ask_user_questions(exec_mode: ExecutionMode = ExecutionMode.Full): + """ + First, we'll ask the user for a bunch of user input. + Not until we're satisfied with what we want to install + will we continue with the actual installation steps. + """ + if archinstall.arguments.get('advanced', None): + setup_area: Dict[str, Any] = {} + setup = SetupMenu(setup_area) + + if exec_mode == ExecutionMode.Lineal: + for entry in setup.list_enabled_options(): + if entry in ('continue', 'abort'): + continue + if not setup.option(entry).enabled: + continue + setup.exec_option(entry) + else: + setup.run() + + archinstall.arguments['archinstall-language'] = setup_area.get('archinstall-language') + + with SwissMainMenu(data_store=archinstall.arguments, exec_mode=exec_mode) as menu: + if mode == ExecutionMode.Lineal: + for entry in menu.list_enabled_options(): + if entry in ('install', 'abort'): + continue + menu.exec_option(entry) + archinstall.arguments[entry] = menu.option(entry).get_selection() + else: + menu.run() + + +def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): + disk_config: disk.DiskLayoutConfiguration = archinstall.arguments['disk_config'] + disk_encryption: disk.DiskEncryption = archinstall.arguments.get('disk_encryption', None) + + enable_testing = 'testing' in archinstall.arguments.get('additional-repositories', []) + enable_multilib = 'multilib' in archinstall.arguments.get('additional-repositories', []) + + locale = f"{archinstall.arguments.get('sys-language', 'en_US')} {archinstall.arguments.get('sys-encoding', 'UTF-8').upper()}" + + with Installer( + mountpoint, + disk_config, + disk_encryption=disk_encryption, + kernels=archinstall.arguments.get('kernels', ['linux']) + ) as installation: + if exec_mode in [ExecutionMode.Full, ExecutionMode.Only_HD]: + installation.mount_ordered_layout() + + installation.sanity_check() + + if disk_config.config_type != disk.DiskLayoutType.Pre_mount: + if disk_encryption and disk_encryption.encryption_type != disk.EncryptionType.NoEncryption: + # generate encryption key files for the mounted luks devices + installation.generate_key_files() + + if archinstall.arguments.get('ntp', False): + installation.activate_ntp() + + # Set mirrors used by pacstrap (outside of installation) + if archinstall.arguments.get('mirror-region', None): + use_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors for the live medium + + installation.minimal_installation( + testing=enable_testing, + multilib=enable_multilib, + hostname=archinstall.arguments.get('hostname', 'archlinux'), + locales=[locale] + ) + + if archinstall.arguments.get('mirror-region') is not None: + if archinstall.arguments.get("mirrors", None) is not None: + installation.set_mirrors( + archinstall.arguments['mirror-region']) # Set the mirrors in the installation medium + + if archinstall.arguments.get('swap'): + installation.setup_swap('zram') + + if archinstall.arguments.get("bootloader") == models.Bootloader.Grub and archinstall.has_uefi(): + installation.add_additional_packages("grub") + + installation.add_bootloader(archinstall.arguments["bootloader"]) + + # If user selected to copy the current ISO network configuration + # Perform a copy of the config + network_config = archinstall.arguments.get('nic', None) + + if network_config: + handler = models.NetworkConfigurationHandler(network_config) + handler.config_installer(installation) + + if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '': + installation.add_additional_packages(archinstall.arguments.get('packages', None)) + + if users := archinstall.arguments.get('!users', None): + installation.create_users(users) + + if audio := archinstall.arguments.get('audio', None): + log(f'Installing audio server: {audio}', level=logging.INFO) + if audio == 'pipewire': + PipewireProfile().install(installation) + elif audio == 'pulseaudio': + installation.add_additional_packages("pulseaudio") + else: + installation.log("No audio server will be installed.", level=logging.INFO) + + if profile_config := archinstall.arguments.get('profile_config', None): + profile_handler.install_profile_config(installation, profile_config) + + if timezone := archinstall.arguments.get('timezone', None): + installation.set_timezone(timezone) + + if archinstall.arguments.get('ntp', False): + installation.activate_time_syncronization() + + if archinstall.accessibility_tools_in_use(): + installation.enable_espeakup() + + if (root_pw := archinstall.arguments.get('!root-password', None)) and len(root_pw): + installation.user_set_pw('root', root_pw) + + # This step must be after profile installs to allow profiles_bck to install language pre-requisits. + # After which, this step will set the language both for console and x11 if x11 was installed for instance. + installation.set_keyboard_language(archinstall.arguments['keyboard-layout']) + + if profile_config := archinstall.arguments.get('profile_config', None): + profile_config.profile.post_install(installation) + + # If the user provided a list of services to be enabled, pass the list to the enable_service function. + # Note that while it's called enable_service, it can actually take a list of services and iterate it. + if archinstall.arguments.get('services', None): + installation.enable_service(*archinstall.arguments['services']) + + # If the user provided custom commands to be run post-installation, execute them now. + if archinstall.arguments.get('custom-commands', None): + archinstall.run_custom_user_commands(archinstall.arguments['custom-commands'], installation) + + installation.genfstab() + + 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 = str( + _('Would you like to chroot into the newly created installation and perform post-installation configuration?')) + choice = menu.Menu(prompt, menu.Menu.yes_no(), default_option=menu.Menu.yes()).run() + if choice.value == menu.Menu.yes(): + try: + installation.drop_to_shell() + except: + pass + + archinstall.log(f"Disk states after installing: {disk.disk_layouts()}", level=logging.DEBUG) + + +# Log various information about hardware before starting the installation. This might assist in troubleshooting +archinstall.log(f"Hardware model detected: {archinstall.sys_vendor()} {archinstall.product_name()}; UEFI mode: {archinstall.has_uefi()}", level=logging.DEBUG) +archinstall.log(f"Processor model detected: {archinstall.cpu_model()}", level=logging.DEBUG) +archinstall.log(f"Memory statistics: {archinstall.mem_available()} available out of {archinstall.mem_total()} total installed", level=logging.DEBUG) +archinstall.log(f"Virtualization detected: {archinstall.virtualization()}; is VM: {archinstall.is_vm()}", level=logging.DEBUG) +archinstall.log(f"Graphics devices detected: {archinstall.graphics_devices().keys()}", level=logging.DEBUG) + +# For support reasons, we'll log the disk layout pre installation to match against post-installation layout +archinstall.log(f"Disk states before installing: {disk.disk_layouts()}", level=logging.DEBUG) + + +if not archinstall.check_mirror_reachable(): + log_file = os.path.join(archinstall.storage.get('LOG_PATH', None), archinstall.storage.get('LOG_FILE', None)) + archinstall.log(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'.", level=logging.INFO, fg="red") + exit(1) + +param_mode = archinstall.arguments.get('mode', ExecutionMode.Full.value).lower() + +try: + mode = ExecutionMode(param_mode) +except KeyError: + log(f'Mode "{param_mode}" is not supported') + exit(1) + +if not archinstall.arguments.get('silent'): + ask_user_questions(mode) + +config_output = ConfigurationOutput(archinstall.arguments) +if not archinstall.arguments.get('silent'): + config_output.show() + +config_output.save() + +if archinstall.arguments.get('dry_run'): + exit(0) + +if not archinstall.arguments.get('silent'): + input('Press Enter to continue.') + +if mode in (ExecutionMode.Full, ExecutionMode.Only_HD): + fs_handler = disk.FilesystemHandler( + archinstall.arguments['disk_config'], + archinstall.arguments.get('disk_encryption', None) + ) + + fs_handler.perform_filesystem_operations() + +perform_installation(archinstall.storage.get('MOUNT_POINT', Path('/mnt')), mode) diff --git a/archinstall/scripts/unattended.py b/archinstall/scripts/unattended.py new file mode 100644 index 00000000..0a1c5160 --- /dev/null +++ b/archinstall/scripts/unattended.py @@ -0,0 +1,18 @@ +import time + +import archinstall +from archinstall.lib.profile.profiles_handler import profile_handler + +for profile in profile_handler.get_mac_addr_profiles(): + # Tailored means it's a match for this machine + # based on it's MAC address (or some other criteria + # that fits the requirements for this machine specifically). + archinstall.log(f'Found a tailored profile for this machine called: "{profile.name}"') + + print('Starting install in:') + for i in range(10, 0, -1): + print(f'{i}...') + time.sleep(1) + + install_session = archinstall.storage['installation_session'] + profile.install(install_session) diff --git a/build_iso.sh b/build_iso.sh new file mode 100755 index 00000000..74e815ee --- /dev/null +++ b/build_iso.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +zprofile="/tmp/archlive/airootfs/root/.zprofile" + +mkdir -p /tmp/archlive/airootfs/root/archinstall-git +cp -r . /tmp/archlive/airootfs/root/archinstall-git + +echo "pip uninstall archinstall -y" > $zprofile +echo "cd archinstall-git" >> $zprofile +echo "rm -rf dist" >> $zprofile + +echo "python -m build --wheel --no-isolation" >> $zprofile +echo "pip install dist/archinstall*.whl" >> $zprofile + +echo "echo \"This is an unofficial ISO for development and testing of archinstall. No support will be provided.\"" >> $zprofile +echo "echo \"This ISO was built from Git SHA $GITHUB_SHA\"" >> $zprofile +echo "echo \"Type archinstall to launch the installer.\"" >> $zprofile + +cat $zprofile + +pacman -Sy +pacman --noconfirm -S git archiso + +cp -r /usr/share/archiso/configs/releng/* /tmp/archlive + +echo -e "git\npython\npython-pip\npython-build\npython-flit\npython-setuptools\npython-wheel\npython-pyparted" >> /tmp/archlive/packages.x86_64 + +find /tmp/archlive +cd /tmp/archlive + +mkarchiso -v -w work/ -o out/ ./ diff --git a/docs/examples/python.rst b/docs/examples/python.rst index dfc0abb3..b5478fe2 100644 --- a/docs/examples/python.rst +++ b/docs/examples/python.rst @@ -34,11 +34,11 @@ To do this, we'll begin by importing `archinstall` in our `./archinstall/example .. code-block:: python import archinstall - + all_drives = archinstall.all_blockdevices(partitions=False) print(list(all_drives.keys())) -This should print out a list of drives. +This should print out a list of drives and some meta-information about them. As an example, this will do just fine. Now, go ahead and install the library either as a user-module or system-wide. diff --git a/examples/__init__.py b/examples/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/auto_discovery_mounted.py b/examples/auto_discovery_mounted.py new file mode 100644 index 00000000..0bd30cd1 --- /dev/null +++ b/examples/auto_discovery_mounted.py @@ -0,0 +1,13 @@ +from pathlib import Path + +from archinstall import disk + +root_mount_dir = Path('/mnt/archinstall') + +mods = disk.device_handler.detect_pre_mounted_mods(root_mount_dir) + +disk_config = disk.DiskLayoutConfiguration( + disk.DiskLayoutType.Pre_mount, + device_modifications=mods, + relative_mountpoint=Path('/mnt/archinstall') +) diff --git a/examples/config-sample.json b/examples/config-sample.json index dc8693a7..a7c5d537 100644 --- a/examples/config-sample.json +++ b/examples/config-sample.json @@ -1,28 +1,132 @@ { - "audio": null, - "bootloader": "systemd-bootctl", - "harddrives": [ - "/dev/loop0" - ], - "hostname": "", + "config_version": "2.5.2", + "additional-repositories": [], + "archinstall-language": "English", + "audio": "pipewire", + "bootloader": "Systemd-boot", + "debug": false, + "disk_config": { + "config_type": "default_layout", + "device_modifications": [ + { + "device": "/dev/sda", + "partitions": [ + { + "btrfs": [], + "flags": [ + "Boot" + ], + "fs_type": "fat32", + "length": { + "sector_size": null, + "total_size": null, + "unit": "MiB", + "value": 512 + }, + "mount_options": [], + "mountpoint": "/boot", + "obj_id": "2c3fa2d5-2c79-4fab-86ec-22d0ea1543c0", + "start": { + "sector_size": null, + "total_size": null, + "unit": "MiB", + "value": 1 + }, + "status": "create", + "type": "primary" + }, + { + "btrfs": [], + "flags": [], + "fs_type": "ext4", + "length": { + "sector_size": null, + "total_size": null, + "unit": "GiB", + "value": 20 + }, + "mount_options": [], + "mountpoint": "/", + "obj_id": "3e7018a0-363b-4d05-ab83-8e82d13db208", + "start": { + "sector_size": null, + "total_size": null, + "unit": "MiB", + "value": 513 + }, + "status": "create", + "type": "primary" + }, + { + "btrfs": [], + "flags": [], + "fs_type": "ext4", + "length": { + "sector_size": null, + "total_size": { + "sector_size": null, + "total_size": null, + "unit": "B", + "value": 250148290560 + }, + "unit": "Percent", + "value": 100 + }, + "mount_options": [], + "mountpoint": "/home", + "obj_id": "ce58b139-f041-4a06-94da-1f8bad775d3f", + "start": { + "sector_size": null, + "total_size": null, + "unit": "GiB", + "value": 20 + }, + "status": "create", + "type": "primary" + } + ], + "wipe": true + } + ] + }, + "hostname": "archlinux", "kernels": [ "linux" ], "keyboard-layout": "us", "mirror-region": { - "Worldwide": { - "https://mirror.rackspace.com/archlinux/$repo/os/$arch": true + "Australia": { + "http://archlinux.mirror.digitalpacific.com.au/$repo/os/$arch": true, } }, "nic": { - "type": "NM" + "dhcp": true, + "dns": null, + "gateway": null, + "iface": null, + "ip": null, + "type": "nm" }, + "no_pkg_lookups": false, "ntp": true, + "offline": false, "packages": [], - "profile": null, + "parallel downloads": 0, + "profile_config": { + "gfx_driver": "All open-source (default)", + "greeter": "sddm", + "profile": { + "details": [ + "Kde" + ], + "main": "Desktop" + } + }, "script": "guided", + "silent": false, "swap": true, "sys-encoding": "utf-8", "sys-language": "en_US", - "timezone": "UTC" + "timezone": "UTC", + "version": "2.5.2" } diff --git a/examples/creds-sample.json b/examples/creds-sample.json index 0681e16f..530a2465 100644 --- a/examples/creds-sample.json +++ b/examples/creds-sample.json @@ -1,15 +1,8 @@ { - "!root-password": "", - "!users": [ - { - "username": "", - "!password": "", - "sudo": false - }, - { - "username": "", - "!password": "", - "sudo": true - } - ] + "!users": [ + { + "sudo": true, + "username": "archinstall" + } + ] } diff --git a/examples/full_automated_installation.py b/examples/full_automated_installation.py new file mode 100644 index 00000000..a169dd50 --- /dev/null +++ b/examples/full_automated_installation.py @@ -0,0 +1,95 @@ +from pathlib import Path + +from archinstall import Installer, ProfileConfiguration, profile_handler +from archinstall.default_profiles.minimal import MinimalProfile +from archinstall import disk +from archinstall.lib.models import User + +# we're creating a new ext4 filesystem installation +fs_type = disk.FilesystemType('ext4') +device_path = Path('/dev/sda') + +# get the physical disk device +device = disk.device_handler.get_device(device_path) + +if not device: + raise ValueError('No device found for given path') + +# create a new modification for the specific device +device_modification = disk.DeviceModification(device, wipe=True) + +# create a new boot partition +boot_partition = disk.PartitionModification( + status=disk.ModificationStatus.Create, + type=disk.PartitionType.Primary, + start=disk.Size(1, disk.Unit.MiB), + length=disk.Size(512, disk.Unit.MiB), + mountpoint=Path('/boot'), + fs_type=disk.FilesystemType.Fat32, + flags=[disk.PartitionFlag.Boot] +) +device_modification.add_partition(boot_partition) + +# create a root partition +root_partition = disk.PartitionModification( + status=disk.ModificationStatus.Create, + type=disk.PartitionType.Primary, + start=disk.Size(513, disk.Unit.MiB), + length=disk.Size(20, disk.Unit.GiB), + mountpoint=None, + fs_type=fs_type, + mount_options=[], +) +device_modification.add_partition(root_partition) + +# create a new home partition +home_partition = disk.PartitionModification( + status=disk.ModificationStatus.Create, + type=disk.PartitionType.Primary, + start=root_partition.length, + length=disk.Size(100, disk.Unit.Percent, total_size=device.device_info.total_size), + mountpoint=Path('/home'), + fs_type=fs_type, + mount_options=[] +) +device_modification.add_partition(home_partition) + +disk_config = disk.DiskLayoutConfiguration( + config_type=disk.DiskLayoutType.Default, + device_modifications=[device_modification] +) + +# disk encryption configuration (Optional) +disk_encryption = disk.DiskEncryption( + encryption_password="enc_password", + encryption_type=disk.EncryptionType.Partition, + partitions=[home_partition], + hsm_device=None +) + +# initiate file handler with the disk config and the optional disk encryption config +fs_handler = disk.FilesystemHandler(disk_config, disk_encryption) + +# perform all file operations +# WARNING: this will potentially format the filesystem and delete all data +fs_handler.perform_filesystem_operations(show_countdown=False) + +mountpoint = Path('/tmp') + +with Installer( + mountpoint, + disk_config, + disk_encryption=disk_encryption, + kernels=['linux'] +) as installation: + installation.mount_ordered_layout() + installation.minimal_installation(hostname='minimal-arch') + installation.add_additional_packages(['nano', 'wget', 'git']) + +# Optionally, install a profile of choice. +# In this case, we install a minimal profile that is empty +profile_config = ProfileConfiguration(MinimalProfile()) +profile_handler.install_profile_config(installation, profile_config) + +user = User('archinstall', 'password', True) +installation.create_users(user) diff --git a/examples/guided.py b/examples/guided.py deleted file mode 100644 index e9240c03..00000000 --- a/examples/guided.py +++ /dev/null @@ -1,306 +0,0 @@ -import logging -import os -import time - -import archinstall -from archinstall import ConfigurationOutput, Menu -from archinstall.lib.models.network_configuration import NetworkConfigurationHandler - -if archinstall.arguments.get('help'): - print("See `man archinstall` for help.") - exit(0) -if os.getuid() != 0: - print(_("Archinstall requires root privileges to run. See --help for more.")) - exit(1) - -# Log various information about hardware before starting the installation. This might assist in troubleshooting -archinstall.log(f"Hardware model detected: {archinstall.sys_vendor()} {archinstall.product_name()}; UEFI mode: {archinstall.has_uefi()}", level=logging.DEBUG) -archinstall.log(f"Processor model detected: {archinstall.cpu_model()}", level=logging.DEBUG) -archinstall.log(f"Memory statistics: {archinstall.mem_available()} available out of {archinstall.mem_total()} total installed", level=logging.DEBUG) -archinstall.log(f"Virtualization detected: {archinstall.virtualization()}; is VM: {archinstall.is_vm()}", level=logging.DEBUG) -archinstall.log(f"Graphics devices detected: {archinstall.graphics_devices().keys()}", level=logging.DEBUG) - -# For support reasons, we'll log the disk layout pre installation to match against post-installation layout -archinstall.log(f"Disk states before installing: {archinstall.disk_layouts()}", level=logging.DEBUG) - - -def ask_user_questions(): - """ - First, we'll ask the user for a bunch of user input. - Not until we're satisfied with what we want to install - will we continue with the actual installation steps. - """ - - # ref: https://github.com/archlinux/archinstall/pull/831 - # we'll set NTP to true by default since this is also - # the default value specified in the menu options; in - # case it will be changed by the user we'll also update - # the system immediately - global_menu = archinstall.GlobalMenu(data_store=archinstall.arguments) - - global_menu.enable('archinstall-language') - - global_menu.enable('keyboard-layout') - - # Set which region to download packages from during the installation - global_menu.enable('mirror-region') - - 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. - global_menu.enable('harddrives') - - global_menu.enable('disk_layouts') - - # Specify disk encryption options - global_menu.enable('disk_encryption') - - # Ask which boot-loader to use (will only ask if we're in UEFI mode, otherwise will default to GRUB) - global_menu.enable('bootloader') - - global_menu.enable('swap') - - # Get the hostname for the machine - global_menu.enable('hostname') - - # Ask for a root password (optional, but triggers requirement for super-user if skipped) - global_menu.enable('!root-password') - - global_menu.enable('!users') - - # Ask for archinstall-specific profiles (such as desktop environments etc) - global_menu.enable('profile') - - # Ask about audio server selection if one is not already set - global_menu.enable('audio') - - # Ask for preferred kernel: - global_menu.enable('kernels') - - global_menu.enable('packages') - - if archinstall.arguments.get('advanced', False): - # Enable parallel downloads - global_menu.enable('parallel downloads') - - # Ask or Call the helper function that asks the user to optionally configure a network. - global_menu.enable('nic') - - global_menu.enable('timezone') - - global_menu.enable('ntp') - - global_menu.enable('additional-repositories') - - global_menu.enable('__separator__') - - global_menu.enable('save_config') - global_menu.enable('install') - global_menu.enable('abort') - - global_menu.run() - - -def perform_filesystem_operations(): - """ - Issue a final warning before we continue with something un-revertable. - We mention the drive one last time, and count from 5 to 0. - """ - - if archinstall.arguments.get('harddrives', None): - print(_(f" ! Formatting {archinstall.arguments['harddrives']} in "), end='') - archinstall.do_countdown() - - """ - Setup the blockdevice, filesystem (and optionally encryption). - Once that's done, we'll hand over to perform_installation() - """ - mode = archinstall.GPT - if archinstall.has_uefi() is False: - mode = archinstall.MBR - - for drive in archinstall.arguments.get('harddrives', []): - if archinstall.arguments.get('disk_layouts', {}).get(drive.path): - with archinstall.Filesystem(drive, mode) as fs: - fs.load_layout(archinstall.arguments['disk_layouts'][drive.path]) - - -def perform_installation(mountpoint): - """ - Performs the installation steps on a block device. - Only requirement is that the block devices are - formatted and setup prior to entering this function. - """ - - with archinstall.Installer(mountpoint, kernels=archinstall.arguments.get('kernels', ['linux'])) as installation: - # Mount all the drives to the desired mountpoint - # This *can* be done outside of the installation, but the installer can deal with it. - if archinstall.arguments.get('disk_layouts'): - installation.mount_ordered_layout(archinstall.arguments['disk_layouts']) - - # Placing /boot check during installation because this will catch both re-use and wipe scenarios. - for partition in installation.partitions: - if partition.mountpoint == installation.target + '/boot': - if partition.size < 0.19: # ~200 MiB in GiB - raise archinstall.DiskError(f"The selected /boot partition in use is not large enough to properly install a boot loader. Please resize it to at least 200MiB and re-run the installation.") - - # If we've activated NTP, make sure it's active in the ISO too and - # make sure at least one time-sync finishes before we continue with the installation - if archinstall.arguments.get('ntp', False): - # Activate NTP in the ISO - archinstall.SysCommand('timedatectl set-ntp true') - - # TODO: This block might be redundant, but this service is not activated unless - # `timedatectl set-ntp true` is executed. - logged = False - while archinstall.service_state('dbus-org.freedesktop.timesync1.service') not in ('running'): - if not logged: - installation.log(f"Waiting for dbus-org.freedesktop.timesync1.service to enter running state", level=logging.INFO) - logged = True - time.sleep(1) - - logged = False - while 'Server: n/a' in archinstall.SysCommand('timedatectl timesync-status --no-pager --property=Server --value'): - if not logged: - installation.log(f"Waiting for timedatectl timesync-status to report a timesync against a server", level=logging.INFO) - logged = True - time.sleep(1) - - # if len(mirrors): - # Certain services might be running that affects the system during installation. - # Currently, only one such service is "reflector.service" which updates /etc/pacman.d/mirrorlist - # We need to wait for it before we continue since we opted in to use a custom mirror/region. - installation.log('Waiting for automatic mirror selection (reflector) to complete.', level=logging.INFO) - while archinstall.service_state('reflector') not in ('dead', 'failed', 'exited'): - time.sleep(1) - - installation.log('Waiting pacman-init.service to complete.', level=logging.INFO) - while archinstall.service_state('pacman-init') not in ('dead', 'failed', 'exited'): - time.sleep(1) - - installation.log('Waiting Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete.', level=logging.INFO) - while archinstall.service_state('archlinux-keyring-wkd-sync') not in ('dead', 'failed', 'exited'): - time.sleep(1) - - # Set mirrors used by pacstrap (outside of installation) - if archinstall.arguments.get('mirror-region', None): - archinstall.use_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors for the live medium - - # Retrieve list of additional repositories and set boolean values appropriately - if archinstall.arguments.get('additional-repositories', None) is not None: - enable_testing = 'testing' in archinstall.arguments.get('additional-repositories', None) - enable_multilib = 'multilib' in archinstall.arguments.get('additional-repositories', None) - else: - enable_testing = False - enable_multilib = False - - if installation.minimal_installation( - testing=enable_testing, multilib=enable_multilib, hostname=archinstall.arguments['hostname'], - locales=[f"{archinstall.arguments['sys-language']} {archinstall.arguments['sys-encoding'].upper()}"]): - if archinstall.arguments.get('mirror-region') is not None: - if archinstall.arguments.get("mirrors", None) is not None: - installation.set_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors in the installation medium - if archinstall.arguments.get('swap'): - installation.setup_swap('zram') - if archinstall.arguments.get("bootloader") == "grub-install" and archinstall.has_uefi(): - installation.add_additional_packages("grub") - installation.add_bootloader(archinstall.arguments["bootloader"]) - - # If user selected to copy the current ISO network configuration - # Perform a copy of the config - network_config = archinstall.arguments.get('nic', None) - - if network_config: - handler = NetworkConfigurationHandler(network_config) - handler.config_installer(installation) - - if archinstall.arguments.get('audio', None) is not None: - installation.log(f"This audio server will be used: {archinstall.arguments.get('audio', None)}", level=logging.INFO) - if archinstall.arguments.get('audio', None) == 'pipewire': - archinstall.Application(installation, 'pipewire').install() - elif archinstall.arguments.get('audio', None) == 'pulseaudio': - print('Installing pulseaudio ...') - installation.add_additional_packages("pulseaudio") - else: - installation.log("No audio server will be installed.", level=logging.INFO) - - if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '': - installation.add_additional_packages(archinstall.arguments.get('packages', None)) - - if archinstall.arguments.get('profile', None): - installation.install_profile(archinstall.arguments.get('profile', None)) - - if users := archinstall.arguments.get('!users', None): - installation.create_users(users) - - if timezone := archinstall.arguments.get('timezone', None): - installation.set_timezone(timezone) - - if archinstall.arguments.get('ntp', False): - installation.activate_time_syncronization() - - if archinstall.accessibility_tools_in_use(): - installation.enable_espeakup() - - if (root_pw := archinstall.arguments.get('!root-password', None)) and len(root_pw): - installation.user_set_pw('root', root_pw) - - # This step must be after profile installs to allow profiles to install language pre-requisits. - # After which, this step will set the language both for console and x11 if x11 was installed for instance. - installation.set_keyboard_language(archinstall.arguments['keyboard-layout']) - - if archinstall.arguments['profile'] and archinstall.arguments['profile'].has_post_install(): - with archinstall.arguments['profile'].load_instructions(namespace=f"{archinstall.arguments['profile'].namespace}.py") as imported: - if not imported._post_install(): - archinstall.log(' * Profile\'s post configuration requirements was not fulfilled.', fg='red') - exit(1) - - # If the user provided a list of services to be enabled, pass the list to the enable_service function. - # Note that while it's called enable_service, it can actually take a list of services and iterate it. - if archinstall.arguments.get('services', None): - installation.enable_service(*archinstall.arguments['services']) - - # If the user provided custom commands to be run post-installation, execute them now. - if archinstall.arguments.get('custom-commands', None): - archinstall.run_custom_user_commands(archinstall.arguments['custom-commands'], installation) - - installation.genfstab() - - 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 = 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.value == Menu.yes(): - try: - installation.drop_to_shell() - except: - pass - - # For support reasons, we'll log the disk layout post installation (crash or no crash) - archinstall.log(f"Disk states after installing: {archinstall.disk_layouts()}", level=logging.DEBUG) - - -if archinstall.arguments.get('skip-mirror-check', False) is False and archinstall.check_mirror_reachable() is False: - log_file = os.path.join(archinstall.storage.get('LOG_PATH', None), archinstall.storage.get('LOG_FILE', None)) - archinstall.log(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'.", level=logging.INFO, fg="red") - exit(1) - -if not archinstall.arguments.get('silent'): - ask_user_questions() - -config_output = ConfigurationOutput(archinstall.arguments) -if not archinstall.arguments.get('silent'): - config_output.show() -config_output.save() - -if archinstall.arguments.get('dry_run'): - exit(0) - -if not archinstall.arguments.get('silent'): - input(str(_('Press Enter to continue.'))) - -archinstall.configuration_sanity_check() -perform_filesystem_operations() -perform_installation(archinstall.storage.get('MOUNT_POINT', '/mnt')) diff --git a/examples/interactive_installation.py b/examples/interactive_installation.py new file mode 100644 index 00000000..a78b1712 --- /dev/null +++ b/examples/interactive_installation.py @@ -0,0 +1,220 @@ +import logging +from pathlib import Path +from typing import TYPE_CHECKING, Any + +import archinstall +from archinstall import log, Installer, use_mirrors, profile_handler +from archinstall.default_profiles.applications.pipewire import PipewireProfile +from archinstall import disk +from archinstall import menu +from archinstall.lib.models import Bootloader, NetworkConfigurationHandler + +if TYPE_CHECKING: + _: Any + + +def ask_user_questions(): + global_menu = archinstall.GlobalMenu(data_store=archinstall.arguments) + + global_menu.enable('archinstall-language') + + global_menu.enable('keyboard-layout') + + # Set which region to download packages from during the installation + global_menu.enable('mirror-region') + + global_menu.enable('sys-language') + + global_menu.enable('sys-encoding') + + global_menu.enable('disk_config', mandatory=True) + + # Specify disk encryption options + global_menu.enable('disk_encryption') + + # Ask which boot-loader to use (will only ask if we're in UEFI mode, otherwise will default to GRUB) + global_menu.enable('bootloader') + + global_menu.enable('swap') + + # Get the hostname for the machine + global_menu.enable('hostname') + + # Ask for a root password (optional, but triggers requirement for super-user if skipped) + global_menu.enable('!root-password', mandatory=True) + + global_menu.enable('!users', mandatory=True) + + # Ask for archinstall-specific profiles_bck (such as desktop environments etc) + global_menu.enable('profile_config') + + # Ask about audio server selection if one is not already set + global_menu.enable('audio') + + # Ask for preferred kernel: + global_menu.enable('kernels') + + global_menu.enable('packages') + + if archinstall.arguments.get('advanced', False): + # Enable parallel downloads + global_menu.enable('parallel downloads') + + # Ask or Call the helper function that asks the user to optionally configure a network. + global_menu.enable('nic') + + global_menu.enable('timezone') + + global_menu.enable('ntp') + + global_menu.enable('additional-repositories') + + global_menu.enable('__separator__') + + global_menu.enable('save_config') + global_menu.enable('install') + global_menu.enable('abort') + + global_menu.run() + + +def perform_installation(mountpoint: Path): + """ + Performs the installation steps on a block device. + Only requirement is that the block devices are + formatted and setup prior to entering this function. + """ + log('Starting installation', level=logging.INFO) + disk_config: disk.DiskLayoutConfiguration = archinstall.arguments['disk_config'] + + # Retrieve list of additional repositories and set boolean values appropriately + enable_testing = 'testing' in archinstall.arguments.get('additional-repositories', []) + enable_multilib = 'multilib' in archinstall.arguments.get('additional-repositories', []) + + locale = f"{archinstall.arguments.get('sys-language', 'en_US')} {archinstall.arguments.get('sys-encoding', 'UTF-8').upper()}" + + disk_encryption: disk.DiskEncryption = archinstall.arguments.get('disk_encryption', None) + + with Installer( + mountpoint, + disk_config, + disk_encryption=disk_encryption, + kernels=archinstall.arguments.get('kernels', ['linux']) + ) as installation: + # Mount all the drives to the desired mountpoint + if disk_config.config_type != disk.DiskLayoutType.Pre_mount: + installation.mount_ordered_layout() + + installation.sanity_check() + + if disk_config.config_type != disk.DiskLayoutType.Pre_mount: + if disk_encryption and disk_encryption.encryption_type != disk.EncryptionType.NoEncryption: + # generate encryption key files for the mounted luks devices + installation.generate_key_files() + + if archinstall.arguments.get('ntp', False): + installation.activate_ntp() + + # Set mirrors used by pacstrap (outside of installation) + if archinstall.arguments.get('mirror-region', None): + use_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors for the live medium + + installation.minimal_installation( + testing=enable_testing, + multilib=enable_multilib, + hostname=archinstall.arguments.get('hostname', 'archlinux'), + locales=[locale] + ) + + if archinstall.arguments.get('mirror-region') is not None: + if archinstall.arguments.get("mirrors", None) is not None: + installation.set_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors in the installation medium + + if archinstall.arguments.get('swap'): + installation.setup_swap('zram') + + if archinstall.arguments.get("bootloader") == Bootloader.Grub and archinstall.has_uefi(): + installation.add_additional_packages("grub") + + installation.add_bootloader(archinstall.arguments["bootloader"]) + + # If user selected to copy the current ISO network configuration + # Perform a copy of the config + network_config = archinstall.arguments.get('nic', None) + + if network_config: + handler = NetworkConfigurationHandler(network_config) + handler.config_installer(installation) + + if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '': + installation.add_additional_packages(archinstall.arguments.get('packages', None)) + + if users := archinstall.arguments.get('!users', None): + installation.create_users(users) + + if audio := archinstall.arguments.get('audio', None): + log(f'Installing audio server: {audio}', level=logging.INFO) + if audio == 'pipewire': + PipewireProfile().install(installation) + elif audio == 'pulseaudio': + installation.add_additional_packages("pulseaudio") + else: + installation.log("No audio server will be installed.", level=logging.INFO) + + if profile_config := archinstall.arguments.get('profile_config', None): + profile_handler.install_profile_config(installation, profile_config) + + if timezone := archinstall.arguments.get('timezone', None): + installation.set_timezone(timezone) + + if archinstall.arguments.get('ntp', False): + installation.activate_time_syncronization() + + if archinstall.accessibility_tools_in_use(): + installation.enable_espeakup() + + if (root_pw := archinstall.arguments.get('!root-password', None)) and len(root_pw): + installation.user_set_pw('root', root_pw) + + # This step must be after profile installs to allow profiles_bck to install language pre-requisits. + # After which, this step will set the language both for console and x11 if x11 was installed for instance. + installation.set_keyboard_language(archinstall.arguments['keyboard-layout']) + + if profile_config := archinstall.arguments.get('profile_config', None): + profile_config.profile.post_install(installation) + + # If the user provided a list of services to be enabled, pass the list to the enable_service function. + # Note that while it's called enable_service, it can actually take a list of services and iterate it. + if archinstall.arguments.get('services', None): + installation.enable_service(*archinstall.arguments['services']) + + # If the user provided custom commands to be run post-installation, execute them now. + if archinstall.arguments.get('custom-commands', None): + archinstall.run_custom_user_commands(archinstall.arguments['custom-commands'], installation) + + installation.genfstab() + + 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 = str(_('Would you like to chroot into the newly created installation and perform post-installation configuration?')) + choice = menu.Menu(prompt, menu.Menu.yes_no(), default_option=menu.Menu.yes()).run() + if choice.value == menu.Menu.yes(): + try: + installation.drop_to_shell() + except: + pass + + archinstall.log(f"Disk states after installing: {disk.disk_layouts()}", level=logging.DEBUG) + + +ask_user_questions() + +fs_handler = disk.FilesystemHandler( + archinstall.arguments['disk_config'], + archinstall.arguments.get('disk_encryption', None) +) + +fs_handler.perform_filesystem_operations() + +perform_installation(archinstall.storage.get('MOUNT_POINT', Path('/mnt'))) diff --git a/examples/mac_address_installation.py b/examples/mac_address_installation.py new file mode 100644 index 00000000..0a1c5160 --- /dev/null +++ b/examples/mac_address_installation.py @@ -0,0 +1,18 @@ +import time + +import archinstall +from archinstall.lib.profile.profiles_handler import profile_handler + +for profile in profile_handler.get_mac_addr_profiles(): + # Tailored means it's a match for this machine + # based on it's MAC address (or some other criteria + # that fits the requirements for this machine specifically). + archinstall.log(f'Found a tailored profile for this machine called: "{profile.name}"') + + print('Starting install in:') + for i in range(10, 0, -1): + print(f'{i}...') + time.sleep(1) + + install_session = archinstall.storage['installation_session'] + profile.install(install_session) diff --git a/examples/minimal.py b/examples/minimal.py deleted file mode 100644 index 8b4c847f..00000000 --- a/examples/minimal.py +++ /dev/null @@ -1,75 +0,0 @@ -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") - -if archinstall.arguments.get('help', None): - archinstall.log(" - Optional disk encryption via --!encryption-password=") - archinstall.log(" - Optional filesystem type via --filesystem=") - archinstall.log(" - Optional systemd network via --network") - -archinstall.arguments['harddrive'] = archinstall.select_disk(archinstall.all_blockdevices()) - - -def install_on(mountpoint): - # We kick off the installer by telling it where the - with archinstall.Installer(mountpoint) as installation: - # Strap in the base system, add a boot loader and configure - # some other minor details as specified by this profile and user. - if installation.minimal_installation(): - installation.set_hostname('minimal-arch') - installation.add_bootloader() - - # Optionally enable networking: - if archinstall.arguments.get('network', None): - installation.copy_iso_network_config(enable_services=True) - - installation.add_additional_packages(['nano', 'wget', 'git']) - installation.install_profile('minimal') - - 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. - archinstall.log("There are two new accounts in your installation after reboot:") - archinstall.log(" * root (password: airoot)") - archinstall.log(" * devel (password: devel)") - - -if archinstall.arguments['harddrive']: - archinstall.arguments['harddrive'].keep_partitions = False - - print(f" ! Formatting {archinstall.arguments['harddrive']} in ", end='') - archinstall.do_countdown() - - # First, we configure the basic filesystem layout - with archinstall.Filesystem(archinstall.arguments['harddrive'], archinstall.GPT) as fs: - # We use the entire disk instead of setting up partitions on your own - if archinstall.arguments['harddrive'].keep_partitions is False: - fs.use_entire_disk(root_filesystem_type=archinstall.arguments.get('filesystem', 'btrfs')) - - boot = fs.find_partition('/boot') - root = fs.find_partition('/') - - boot.format('fat32') - - # We encrypt the root partition if we got a password to do so with, - # Otherwise we just skip straight to formatting and installation - if archinstall.arguments.get('!encryption-password', None): - root.encrypted = True - root.encrypt(password=archinstall.arguments.get('!encryption-password', None)) - - with archinstall.luks2(root, 'luksloop', archinstall.arguments.get('!encryption-password', None)) as unlocked_root: - unlocked_root.format(root.filesystem) - unlocked_root.mount('/mnt') - else: - root.format(root.filesystem) - root.mount('/mnt') - - boot.mount('/mnt/boot') - -install_on('/mnt') diff --git a/examples/minimal_installation.py b/examples/minimal_installation.py new file mode 100644 index 00000000..8bd6fd55 --- /dev/null +++ b/examples/minimal_installation.py @@ -0,0 +1,85 @@ +from pathlib import Path +from typing import TYPE_CHECKING, Any, List + +import archinstall +from archinstall.lib import disk +from archinstall import Installer, ProfileConfiguration, profile_handler +from archinstall.default_profiles.minimal import MinimalProfile +from archinstall.lib.models import Bootloader, User +from archinstall.lib.user_interaction.disk_conf import select_devices, suggest_single_disk_layout + +if TYPE_CHECKING: + _: Any + + +def perform_installation(mountpoint: Path): + disk_config: disk.DiskLayoutConfiguration = archinstall.arguments['disk_config'] + disk_encryption: disk.DiskEncryption = archinstall.arguments.get('disk_encryption', None) + + with Installer( + mountpoint, + disk_config, + disk_encryption=disk_encryption, + kernels=archinstall.arguments.get('kernels', ['linux']) + ) as installation: + # Strap in the base system, add a boot loader and configure + # some other minor details as specified by this profile and user. + if installation.minimal_installation(): + installation.set_hostname('minimal-arch') + installation.add_bootloader(Bootloader.Systemd) + + # Optionally enable networking: + if archinstall.arguments.get('network', None): + installation.copy_iso_network_config(enable_services=True) + + installation.add_additional_packages(['nano', 'wget', 'git']) + + profile_config = ProfileConfiguration(MinimalProfile()) + profile_handler.install_profile_config(installation, profile_config) + + user = User('devel', 'devel', False) + installation.create_users(user) + + +def prompt_disk_layout(): + fs_type = None + if filesystem := archinstall.arguments.get('filesystem', None): + fs_type = disk.FilesystemType(filesystem) + + devices = select_devices() + modifications = suggest_single_disk_layout(devices[0], filesystem_type=fs_type) + + archinstall.arguments['disk_config'] = disk.DiskLayoutConfiguration( + config_type=disk.DiskLayoutType.Default, + device_modifications=[modifications] + ) + + +def parse_disk_encryption(): + if enc_password := archinstall.arguments.get('!encryption-password', None): + modification: List[disk.DeviceModification] = archinstall.arguments['disk_config'] + partitions: List[disk.PartitionModification] = [] + + # encrypt all partitions except the /boot + for mod in modification: + partitions += list(filter(lambda x: x.mountpoint != Path('/boot'), mod.partitions)) + + archinstall.arguments['disk_encryption'] = disk.DiskEncryption( + encryption_type=disk.EncryptionType.Partition, + encryption_password=enc_password, + partitions=partitions + ) + + +prompt_disk_layout() +parse_disk_encryption() + +fs_handler = disk.FilesystemHandler( + archinstall.arguments['disk_config'], + archinstall.arguments.get('disk_encryption', None) +) + +fs_handler.perform_filesystem_operations() + +mount_point = Path('/mnt') +perform_installation(mount_point) diff --git a/examples/only_hd.py b/examples/only_hd.py deleted file mode 100644 index e3d18f0a..00000000 --- a/examples/only_hd.py +++ /dev/null @@ -1,151 +0,0 @@ - -import logging -import os -import pathlib - -import archinstall -from archinstall import ConfigurationOutput - - -class OnlyHDMenu(archinstall.GlobalMenu): - def _setup_selection_menu_options(self): - super()._setup_selection_menu_options() - options_list = [] - mandatory_list = [] - options_list = ['harddrives', 'disk_layouts', 'disk_encryption','swap'] - mandatory_list = ['harddrives'] - options_list.extend(['save_config','install','abort']) - - for entry in self._menu_options: - if entry in options_list: - # for not lineal executions, only self.option(entry).set_enabled and set_mandatory are necessary - if entry in mandatory_list: - self.enable(entry,mandatory=True) - else: - self.enable(entry) - else: - self.option(entry).set_enabled(False) - self._update_install_text() - - def mandatory_lacking(self) -> [int, list]: - mandatory_fields = [] - mandatory_waiting = 0 - for field in self._menu_options: - option = self._menu_options[field] - if option.is_mandatory(): - if not option.has_selection(): - mandatory_waiting += 1 - mandatory_fields += [field,] - return mandatory_fields, mandatory_waiting - - def _missing_configs(self): - """ overloaded method """ - def check(s): - return self.option(s).has_selection() - - missing, missing_cnt = self.mandatory_lacking() - if check('harddrives'): - if not self.option('harddrives').is_empty() and not check('disk_layouts'): - missing_cnt += 1 - missing += ['disk_layout'] - return missing - -def ask_user_questions(): - """ - First, we'll ask the user for a bunch of user input. - Not until we're satisfied with what we want to install - will we continue with the actual installation steps. - """ - with OnlyHDMenu(data_store=archinstall.arguments) as menu: - # We select the execution language separated - menu.exec_option('archinstall-language') - menu.option('archinstall-language').set_enabled(False) - menu.run() - -def perform_disk_operations(): - """ - Issue a final warning before we continue with something un-revertable. - We mention the drive one last time, and count from 5 to 0. - """ - if archinstall.arguments.get('harddrives', None): - print(f" ! Formatting {archinstall.arguments['harddrives']} in ", end='') - archinstall.do_countdown() - """ - Setup the blockdevice, filesystem (and optionally encryption). - Once that's done, we'll hand over to perform_installation() - """ - mode = archinstall.GPT - if archinstall.has_uefi() is False: - mode = archinstall.MBR - - for drive in archinstall.arguments.get('harddrives', []): - if archinstall.arguments.get('disk_layouts', {}).get(drive.path): - with archinstall.Filesystem(drive, mode) as fs: - fs.load_layout(archinstall.arguments['disk_layouts'][drive.path]) - -def perform_installation(mountpoint): - """ - Performs the installation steps on a block device. - Only requirement is that the block devices are - formatted and setup prior to entering this function. - """ - with archinstall.Installer(mountpoint, kernels=None) as installation: - # Mount all the drives to the desired mountpoint - # This *can* be done outside of the installation, but the installer can deal with it. - if archinstall.arguments.get('disk_layouts'): - installation.mount_ordered_layout(archinstall.arguments['disk_layouts']) - - # Placing /boot check during installation because this will catch both re-use and wipe scenarios. - for partition in installation.partitions: - if partition.mountpoint == installation.target + '/boot': - if partition.size <= 0.25: # in GB - raise archinstall.DiskError(f"The selected /boot partition in use is not large enough to properly install a boot loader. Please resize it to at least 256MB and re-run the installation.") - # to generate a fstab directory holder. Avoids an error on exit and at the same time checks the procedure - target = pathlib.Path(f"{mountpoint}/etc/fstab") - if not target.parent.exists(): - target.parent.mkdir(parents=True) - - # For support reasons, we'll log the disk layout post installation (crash or no crash) - archinstall.log(f"Disk states after installing: {archinstall.disk_layouts()}", level=logging.DEBUG) - -def log_execution_environment(): - # Log various information about hardware before starting the installation. This might assist in troubleshooting - archinstall.log(f"Hardware model detected: {archinstall.sys_vendor()} {archinstall.product_name()}; UEFI mode: {archinstall.has_uefi()}", level=logging.DEBUG) - archinstall.log(f"Processor model detected: {archinstall.cpu_model()}", level=logging.DEBUG) - archinstall.log(f"Memory statistics: {archinstall.mem_available()} available out of {archinstall.mem_total()} total installed", level=logging.DEBUG) - archinstall.log(f"Virtualization detected: {archinstall.virtualization()}; is VM: {archinstall.is_vm()}", level=logging.DEBUG) - archinstall.log(f"Graphics devices detected: {archinstall.graphics_devices().keys()}", level=logging.DEBUG) - - # For support reasons, we'll log the disk layout pre installation to match against post-installation layout - archinstall.log(f"Disk states before installing: {archinstall.disk_layouts()}", level=logging.DEBUG) - - -if archinstall.arguments.get('help'): - print("See `man archinstall` for help.") - exit(0) -if os.getuid() != 0: - print("Archinstall requires root privileges to run. See --help for more.") - exit(1) - -log_execution_environment() - -if not archinstall.check_mirror_reachable(): - log_file = os.path.join(archinstall.storage.get('LOG_PATH', None), archinstall.storage.get('LOG_FILE', None)) - archinstall.log(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'.", level=logging.INFO, fg="red") - exit(1) - -if not archinstall.arguments.get('silent'): - ask_user_questions() - -config_output = ConfigurationOutput(archinstall.arguments) -if not archinstall.arguments.get('silent'): - config_output.show() -config_output.save() - -if archinstall.arguments.get('dry_run'): - exit(0) -if not archinstall.arguments.get('silent'): - input('Press Enter to continue.') - -perform_disk_operations() -perform_installation(archinstall.storage.get('MOUNT_POINT', '/mnt')) diff --git a/examples/only_hd_installation.py b/examples/only_hd_installation.py new file mode 100644 index 00000000..2fc74bf0 --- /dev/null +++ b/examples/only_hd_installation.py @@ -0,0 +1,63 @@ +import logging +from pathlib import Path + +import archinstall +from archinstall import Installer +from archinstall.lib import disk + + +def ask_user_questions(): + global_menu = archinstall.GlobalMenu(data_store=archinstall.arguments) + + global_menu.enable('archinstall-language') + + global_menu.enable('disk_config', mandatory=True) + global_menu.enable('disk_encryption') + global_menu.enable('swap') + + global_menu.enable('save_config') + global_menu.enable('install') + global_menu.enable('abort') + + global_menu.run() + + +def perform_installation(mountpoint: Path): + """ + Performs the installation steps on a block device. + Only requirement is that the block devices are + formatted and setup prior to entering this function. + """ + disk_config: disk.DiskLayoutConfiguration = archinstall.arguments['disk_config'] + disk_encryption: disk.DiskEncryption = archinstall.arguments.get('disk_encryption', None) + + with Installer( + mountpoint, + disk_config, + disk_encryption=disk_encryption, + kernels=archinstall.arguments.get('kernels', ['linux']) + ) as installation: + # Mount all the drives to the desired mountpoint + # This *can* be done outside of the installation, but the installer can deal with it. + if archinstall.arguments.get('disk_config'): + installation.mount_ordered_layout() + + # to generate a fstab directory holder. Avoids an error on exit and at the same time checks the procedure + target = Path(f"{mountpoint}/etc/fstab") + if not target.parent.exists(): + target.parent.mkdir(parents=True) + + # For support reasons, we'll log the disk layout post installation (crash or no crash) + archinstall.log(f"Disk states after installing: {disk.disk_layouts()}", level=logging.DEBUG) + + +ask_user_questions() + +fs_handler = disk.FilesystemHandler( + archinstall.arguments['disk_config'], + archinstall.arguments.get('disk_encryption', None) +) + +fs_handler.perform_filesystem_operations() + +perform_installation(archinstall.storage.get('MOUNT_POINT', Path('/mnt'))) diff --git a/examples/swiss.py b/examples/swiss.py deleted file mode 100644 index 442281de..00000000 --- a/examples/swiss.py +++ /dev/null @@ -1,526 +0,0 @@ -""" - -Script swiss (army knife) -Designed to make different workflows for the installation process. Which is controlled by the argument --mode -mode full guides the full process of installation -mode only_hd only proceeds to the creation of the disk infraestructure (partition, mount points, encryption) -mode only_os processes only the installation of Archlinux and software at --mountpoint (or /mnt/archinstall) -mode minimal (still not implemented) -mode lineal. Instead of a menu, shows a sequence of selection screens (eq. to the old mode for guided.py) - -When using the argument --advanced. an additional menu for several special parameters needed during installation appears - -This script respects the --dry_run argument - -""" -import logging -import os -import time -import pathlib -from typing import TYPE_CHECKING, Any - -import archinstall -from archinstall import ConfigurationOutput, NetworkConfigurationHandler, Menu - -if TYPE_CHECKING: - _: Any - -if archinstall.arguments.get('help'): - print("See `man archinstall` for help.") - exit(0) -if os.getuid() != 0: - print("Archinstall requires root privileges to run. See --help for more.") - exit(1) - -""" -particular routines to SetupMenu -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 = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run() - if choice == Menu.yes(): - return True - else: - return False - - -def select_mode(): - return archinstall.generic_select(['full','only_hd','only_os','minimal','lineal'], - 'Select one execution mode', - default=archinstall.arguments.get('mode','full')) - - -""" -following functions will be at locale_helpers, so they will have to be called prefixed by archinstall -""" -def get_locale_mode_text(mode): - if mode == 'LC_ALL': - mode_text = "general (LC_ALL)" - elif mode == "LC_CTYPE": - mode_text = "Character set" - elif mode == "LC_NUMERIC": - mode_text = "Numeric values" - elif mode == "LC_TIME": - mode_text = "Time Values" - elif mode == "LC_COLLATE": - mode_text = "sort order" - elif mode == "LC_MESSAGES": - mode_text = "text messages" - else: - mode_text = "Unassigned" - return mode_text - -def reset_cmd_locale(): - """ sets the cmd_locale to its saved default """ - archinstall.storage['CMD_LOCALE'] = archinstall.storage.get('CMD_LOCALE_DEFAULT',{}) - -def unset_cmd_locale(): - """ archinstall will use the execution environment default """ - archinstall.storage['CMD_LOCALE'] = {} - -def set_cmd_locale(general :str = None, - charset :str = 'C', - numbers :str = 'C', - time :str = 'C', - collate :str = 'C', - messages :str = 'C'): - """ - Set the cmd locale. - If the parameter general is specified, it takes precedence over the rest (might as well not exist) - The rest define some specific settings above the installed default language. If anyone of this parameters is none means the installation default - """ - installed_locales = list_installed_locales() - result = {} - if general: - if general in installed_locales: - archinstall.storage['CMD_LOCALE'] = {'LC_ALL':general} - else: - archinstall.log(f"{get_locale_mode_text('LC_ALL')} {general} is not installed. Defaulting to C",fg="yellow",level=logging.WARNING) - return - - if numbers: - if numbers in installed_locales: - result["LC_NUMERIC"] = numbers - else: - archinstall.log(f"{get_locale_mode_text('LC_NUMERIC')} {numbers} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING) - if charset: - if charset in installed_locales: - result["LC_CTYPE"] = charset - else: - archinstall.log(f"{get_locale_mode_text('LC_CTYPE')} {charset} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING) - if time: - if time in installed_locales: - result["LC_TIME"] = time - else: - archinstall.log(f"{get_locale_mode_text('LC_TIME')} {time} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING) - if collate: - if collate in installed_locales: - result["LC_COLLATE"] = collate - else: - archinstall.log(f"{get_locale_mode_text('LC_COLLATE')} {collate} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING) - if messages: - if messages in installed_locales: - result["LC_MESSAGES"] = messages - else: - archinstall.log(f"{get_locale_mode_text('LC_MESSAGES')} {messages} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING) - archinstall.storage['CMD_LOCALE'] = result - -def list_installed_locales() -> list[str]: - lista = [] - for line in archinstall.SysCommand('locale -a'): - lista.append(line.decode('UTF-8').strip()) - return lista - - -""" -end of locale helpers -""" - -def select_installed_locale(mode): - mode_text = get_locale_mode_text(mode) - if mode == 'LC_ALL': - texto = "Select the default execution locale \nIf none, you will be prompted for specific settings" - else: - texto = f"Select the {mode_text} ({mode}) execution locale \nIf none, you will get the installation default" - return archinstall.generic_select([None] + list_installed_locales(), - texto, - allow_empty_input=True, - default=archinstall.storage.get('CMD_LOCALE',{}).get(mode,'C')) - - -""" - _menus -""" - -class SetupMenu(archinstall.AbstractMenu): - def __init__(self,storage_area): - super().__init__(data_store=storage_area) - - def _setup_selection_menu_options(self): - self.set_option( - 'archinstall-language', - archinstall.Selector( - _('Archinstall language'), - lambda x: self._select_archinstall_language(x), - display_func=lambda x: x.display_name, - default=self.translation_handler.get_language_by_abbr('en'), - enabled=True - ) - ) - - self.set_option( - 'ntp', - archinstall.Selector( - 'Activate NTP', - lambda x: select_activate_NTP(), - default='Y', - enabled=True - ) - ) - - self.set_option( - 'mode', - archinstall.Selector( - 'Excution mode', - lambda x : select_mode(), - default='full', - enabled=True) - ) - - for item in ['LC_ALL','LC_CTYPE','LC_NUMERIC','LC_TIME','LC_MESSAGES','LC_COLLATE']: - self.set_option(item, - archinstall.Selector( - f'{get_locale_mode_text(item)} locale', - lambda x,item=item: select_installed_locale(item), # the parameter is needed for the lambda in the loop - enabled=True, - dependencies_not=['LC_ALL'] if item != 'LC_ALL' else [])) - self.option('LC_ALL').set_enabled(True) - self.set_option('continue', - archinstall.Selector( - 'Continue', - exec_func=lambda n,v: True, - enabled=True)) - - def exit_callback(self): - if self._data_store.get('ntp',False): - archinstall.log("Hardware time and other post-configuration steps might be required in order for NTP to work. For more information, please check the Arch wiki.", fg="yellow") - archinstall.SysCommand('timedatectl set-ntp true') - if self._data_store.get('mode',None): - archinstall.arguments['mode'] = self._data_store['mode'] - archinstall.log(f"Archinstall will execute under {archinstall.arguments['mode']} mode") - if self._data_store.get('LC_ALL',None): - archinstall.storage['CMD_LOCALE'] = {'LC_ALL':self._data_store['LC_ALL']} - else: - exec_locale = {} - for item in ['LC_COLLATE','LC_CTYPE','LC_MESSAGES','LC_NUMERIC','LC_TIME']: - if self._data_store.get(item,None): - exec_locale[item] = self._data_store[item] - archinstall.storage['CMD_LOCALE'] = exec_locale - archinstall.log(f"Archinstall will execute with {archinstall.storage.get('CMD_LOCALE',None)} locale") - -class MyMenu(archinstall.GlobalMenu): - def __init__(self,data_store=archinstall.arguments,mode='full'): - self._execution_mode = mode - super().__init__(data_store) - - def _setup_selection_menu_options(self): - super()._setup_selection_menu_options() - options_list = [] - mandatory_list = [] - if self._execution_mode in ('full','lineal'): - options_list = ['keyboard-layout', 'mirror-region', 'harddrives', 'disk_layouts', - 'disk_encryption','swap', 'bootloader', 'hostname', '!root-password', - '!users', 'profile', 'audio', 'kernels', 'packages','additional-repositories','nic', - 'timezone', 'ntp'] - if archinstall.arguments.get('advanced',False): - options_list.extend(['sys-language','sys-encoding']) - mandatory_list = ['harddrives','bootloader','hostname'] - elif self._execution_mode == 'only_hd': - options_list = ['harddrives', 'disk_layouts', 'disk_encryption','swap'] - mandatory_list = ['harddrives'] - elif self._execution_mode == 'only_os': - options_list = ['keyboard-layout', 'mirror-region','bootloader', 'hostname', - '!root-password', '!users', 'profile', 'audio', 'kernels', - 'packages', 'additional-repositories', 'nic', 'timezone', 'ntp'] - mandatory_list = ['hostname'] - if archinstall.arguments.get('advanced',False): - options_list.expand(['sys-language','sys-encoding']) - elif self._execution_mode == 'minimal': - pass - else: - archinstall.log(f"self._execution_mode {self._execution_mode} not supported") - exit(1) - if self._execution_mode != 'lineal': - options_list.extend(['save_config','install','abort']) - if not archinstall.arguments.get('advanced'): - options_list.append('archinstall-language') - - for entry in self._menu_options: - if entry in options_list: - # for not lineal executions, only self.option(entry).set_enabled and set_mandatory are necessary - if entry in mandatory_list: - self.enable(entry,mandatory=True) - else: - self.enable(entry) - else: - self.option(entry).set_enabled(False) - self._update_install_text() - - def post_callback(self,option=None,value=None): - self._update_install_text(self._execution_mode) - - def _missing_configs(self,mode='full'): - 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 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'): - missing += 1 - return missing - - def _install_text(self,mode='full'): - missing = self._missing_configs(mode) - if missing > 0: - return f'Instalation ({missing} config(s) missing)' - return 'Install' - - def _update_install_text(self, mode='full'): - text = self._install_text(mode) - self.option('install').update_description(text) - - -""" -Installation general subroutines -""" - -def get_current_status(): - # Log various information about hardware before starting the installation. This might assist in troubleshooting - archinstall.log(f"Hardware model detected: {archinstall.sys_vendor()} {archinstall.product_name()}; UEFI mode: {archinstall.has_uefi()}", level=logging.DEBUG) - archinstall.log(f"Processor model detected: {archinstall.cpu_model()}", level=logging.DEBUG) - archinstall.log(f"Memory statistics: {archinstall.mem_available()} available out of {archinstall.mem_total()} total installed", level=logging.DEBUG) - archinstall.log(f"Virtualization detected: {archinstall.virtualization()}; is VM: {archinstall.is_vm()}", level=logging.DEBUG) - archinstall.log(f"Graphics devices detected: {archinstall.graphics_devices().keys()}", level=logging.DEBUG) - - # For support reasons, we'll log the disk layout pre installation to match against post-installation layout - archinstall.log(f"Disk states before installing: {archinstall.disk_layouts()}", level=logging.DEBUG) - -def ask_user_questions(mode): - """ - First, we'll ask the user for a bunch of user input. - Not until we're satisfied with what we want to install - will we continue with the actual installation steps. - """ - if archinstall.arguments.get('advanced',None): - # 3.9 syntax. former x = {**y,**z} or x.update(y) - set_cmd_locale(charset='es_ES.utf8',collate='es_ES.utf8') - setup_area = archinstall.storage.get('CMD_LOCALE',{}) | {} - with SetupMenu(setup_area) as setup: - if mode == 'lineal': - for entry in setup.list_enabled_options(): - if entry in ('continue','abort'): - continue - if not setup.option(entry).enabled: - continue - setup.exec_option(entry) - else: - setup.run() - archinstall.arguments['archinstall-language'] = setup_area.get('archinstall-language') - else: - archinstall.log("Hardware time and other post-configuration steps might be required in order for NTP to work. For more information, please check the Arch wiki.", fg="yellow") - archinstall.SysCommand('timedatectl set-ntp true') - - with MyMenu(data_store=archinstall.arguments,mode=mode) as global_menu: - - if mode == 'lineal': - for entry in global_menu.list_enabled_options(): - if entry in ('install','abort'): - continue - global_menu.exec_option(entry) - archinstall.arguments[entry] = global_menu.option(entry).get_selection() - else: - global_menu.set_option('install', - archinstall.Selector( - global_menu._install_text(mode), - exec_func=lambda n,v: True if global_menu._missing_configs(mode) == 0 else False, - enabled=True)) - - global_menu.run() - -def perform_filesystem_operations(): - """ - Issue a final warning before we continue with something un-revertable. - We mention the drive one last time, and count from 5 to 0. - """ - - if archinstall.arguments.get('harddrives', None): - print(f" ! Formatting {archinstall.arguments['harddrives']} in ", end='') - archinstall.do_countdown() - - """ - Setup the blockdevice, filesystem (and optionally encryption). - Once that's done, we'll hand over to perform_installation() - """ - - mode = archinstall.GPT - if archinstall.has_uefi() is False: - mode = archinstall.MBR - - for drive in archinstall.arguments.get('harddrives', []): - if archinstall.arguments.get('disk_layouts', {}).get(drive.path): - with archinstall.Filesystem(drive, mode) as fs: - fs.load_layout(archinstall.arguments['disk_layouts'][drive.path]) - -def disk_setup(installation): - # Mount all the drives to the desired mountpoint - # This *can* be done outside of the installation, but the installer can deal with it. - if archinstall.arguments.get('disk_layouts'): - installation.mount_ordered_layout(archinstall.arguments['disk_layouts']) - - # Placing /boot check during installation because this will catch both re-use and wipe scenarios. - for partition in installation.partitions: - if partition.mountpoint == installation.target + '/boot': - if partition.size < 0.19: # ~200 MiB in GiB - raise archinstall.DiskError( - f"The selected /boot partition in use is not large enough to properly install a boot loader. Please resize it to at least 200MiB and re-run the installation.") - -def os_setup(installation): - # if len(mirrors): - # Certain services might be running that affects the system during installation. - # Currently, only one such service is "reflector.service" which updates /etc/pacman.d/mirrorlist - # We need to wait for it before we continue since we opted in to use a custom mirror/region. - installation.log('Waiting for automatic mirror selection (reflector) to complete.', level=logging.INFO) - while archinstall.service_state('reflector') not in ('dead', 'failed'): - time.sleep(1) - # Set mirrors used by pacstrap (outside of installation) - if archinstall.arguments.get('mirror-region', None): - archinstall.use_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors for the live medium - if installation.minimal_installation( - hostname=archinstall.arguments['hostname'], - locales=[f"{archinstall.arguments['sys-language']} {archinstall.arguments['sys-encoding'].upper()}"]): - if archinstall.arguments['mirror-region'].get("mirrors", None) is not None: - installation.set_mirrors( - archinstall.arguments['mirror-region']) # Set the mirrors in the installation medium - if archinstall.arguments["bootloader"] == "grub-install" and archinstall.has_uefi(): - installation.add_additional_packages("grub") - installation.add_bootloader(archinstall.arguments["bootloader"]) - if archinstall.arguments['swap']: - installation.setup_swap('zram') - - network_config = archinstall.arguments.get('nic', None) - - if network_config: - handler = NetworkConfigurationHandler(network_config) - handler.config_installer(installation) - - if archinstall.arguments.get('audio', None) is not None: - installation.log(f"This audio server will be used: {archinstall.arguments.get('audio', None)}",level=logging.INFO) - if archinstall.arguments.get('audio', None) == 'pipewire': - archinstall.Application(installation, 'pipewire').install() - elif archinstall.arguments.get('audio', None) == 'pulseaudio': - print('Installing pulseaudio ...') - installation.add_additional_packages("pulseaudio") - else: - installation.log("No audio server will be installed.", level=logging.INFO) - - if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '': - installation.add_additional_packages(archinstall.arguments.get('packages', None)) - - if archinstall.arguments.get('profile', None): - installation.install_profile(archinstall.arguments.get('profile', None)) - - if users := archinstall.arguments.get('!users', None): - installation.create_users(users) - - if timezone := archinstall.arguments.get('timezone', None): - installation.set_timezone(timezone) - - if archinstall.arguments.get('ntp', False): - installation.activate_time_syncronization() - - if archinstall.accessibility_tools_in_use(): - installation.enable_espeakup() - - if (root_pw := archinstall.arguments.get('!root-password', None)) and len(root_pw): - installation.user_set_pw('root', root_pw) - - # This step must be after profile installs to allow profiles to install language pre-requisits. - # After which, this step will set the language both for console and x11 if x11 was installed for instance. - installation.set_keyboard_language(archinstall.arguments['keyboard-layout']) - - if archinstall.arguments['profile'] and archinstall.arguments['profile'].has_post_install(): - with archinstall.arguments['profile'].load_instructions( - namespace=f"{archinstall.arguments['profile'].namespace}.py") as imported: - if not imported._post_install(): - archinstall.log(' * Profile\'s post configuration requirements was not fulfilled.', fg='red') - exit(1) - - # If the user provided a list of services to be enabled, pass the list to the enable_service function. - # Note that while it's called enable_service, it can actually take a list of services and iterate it. - if archinstall.arguments.get('services', None): - installation.enable_service(*archinstall.arguments['services']) - - # If the user provided custom commands to be run post-installation, execute them now. - if archinstall.arguments.get('custom-commands', None): - archinstall.run_custom_user_commands(archinstall.arguments['custom-commands'], installation) - - -def perform_installation(mountpoint, mode): - """ - Performs the installation steps on a block device. - Only requirement is that the block devices are - formatted and setup prior to entering this function. - """ - with archinstall.Installer(mountpoint, kernels=archinstall.arguments.get('kernels', ['linux'])) as installation: - if mode in ('full','only_hd'): - disk_setup(installation) - if mode == 'only_hd': - target = pathlib.Path(f"{mountpoint}/etc/fstab") - if not target.parent.exists(): - target.parent.mkdir(parents=True) - - if mode in ('full','only_os'): - os_setup(installation) - 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 = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run() - if choice == Menu.yes(): - try: - installation.drop_to_shell() - except: - pass - - # For support reasons, we'll log the disk layout post installation (crash or no crash) - archinstall.log(f"Disk states after installing: {archinstall.disk_layouts()}", level=logging.DEBUG) - - -if not archinstall.check_mirror_reachable(): - log_file = os.path.join(archinstall.storage.get('LOG_PATH', None), archinstall.storage.get('LOG_FILE', None)) - archinstall.log(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'.", level=logging.INFO, fg="red") - exit(1) - -mode = archinstall.arguments.get('mode', 'full').lower() -if not archinstall.arguments.get('silent'): - ask_user_questions(mode) - -config_output = ConfigurationOutput(archinstall.arguments) -if not archinstall.arguments.get('silent'): - config_output.show() -config_output.save() - -if archinstall.arguments.get('dry_run'): - exit(0) -if not archinstall.arguments.get('silent'): - input('Press Enter to continue.') - -if mode in ('full','only_hd'): - perform_filesystem_operations() -perform_installation(archinstall.storage.get('MOUNT_POINT', '/mnt'), mode) diff --git a/examples/unattended.py b/examples/unattended.py deleted file mode 100644 index f1ed4c94..00000000 --- a/examples/unattended.py +++ /dev/null @@ -1,21 +0,0 @@ -import time - -import archinstall - -archinstall.storage['UPSTREAM_URL'] = 'https://archlinux.life/profiles' -archinstall.storage['PROFILE_DB'] = 'index.json' - -for name, info in archinstall.list_profiles().items(): - # Tailored means it's a match for this machine - # based on it's MAC address (or some other criteria - # that fits the requirements for this machine specifically). - if info['tailored']: - print(f'Found a tailored profile for this machine called: "{name}".') - print('Starting install in:') - for i in range(10, 0, -1): - print(f'{i}...') - time.sleep(1) - - profile = archinstall.Profile(None, info['path']) - profile.install() - break diff --git a/mypy-strict.ini b/mypy-strict.ini new file mode 100644 index 00000000..be7ddf57 --- /dev/null +++ b/mypy-strict.ini @@ -0,0 +1,102 @@ +[mypy] +python_version = 3.10 +follow_imports = silent +exclude = (?x)( + | ^archinstall/lib/configuration\.py$ + | ^archinstall/lib/disk/btrfs/btrfssubvolumeinfo\.py$ + | ^archinstall/lib/disk/helpers\.py$ + | ^archinstall/lib/hsm/fido\.py$ + | ^archinstall/lib/menu/list_manager\.py$ + | ^archinstall/lib/menu/menu\.py$ + | ^archinstall/lib/menu/simple_menu\.py$ + | ^archinstall/lib/menu/text_input\.py$ + | ^archinstall/lib/models/dataclasses\.py$ + | ^archinstall/lib/models/network_configuration\.py$ + | ^archinstall/lib/models/password_strength\.py$ + | ^archinstall/lib/models/pydantic\.py$ + | ^archinstall/lib/models/subvolume\.py$ + | ^archinstall/lib/models/users\.py$ + | ^archinstall/lib/output\.py$ + | ^archinstall/lib/plugins\.py$ + | ^archinstall/examples/guided\.py$ + | ^archinstall/examples/minimal\.py$ + | ^archinstall/examples/only_hd\.py$ + | ^archinstall/examples/swiss\.py$ + | ^archinstall/__init__\.py$ + | ^archinstall/lib/disk/blockdevice\.py$ + | ^archinstall/lib/disk/btrfs/btrfs_helpers\.py$ + | ^archinstall/lib/disk/btrfs/btrfspartition\.py$ + | ^archinstall/lib/disk/dmcryptdev\.py$ + | ^archinstall/lib/disk/filesystem\.py$ + | ^archinstall/lib/disk/mapperdev\.py$ + | ^archinstall/lib/disk/partition\.py$ + | ^archinstall/lib/disk/user_guides\.py$ + | ^archinstall/lib/general\.py$ + | ^archinstall/lib/hardware\.py$ + | ^archinstall/lib/installer\.py$ + | ^archinstall/lib/locale_helpers\.py$ + | ^archinstall/lib/luks\.py$ + | ^archinstall/lib/menu/global_menu\.py$ + | ^archinstall/lib/menu/selection_menu\.py$ + | ^archinstall/lib/mirrors\.py$ + | ^archinstall/lib/networking\.py$ + | ^archinstall/lib/packages/packages\.py$ + | ^archinstall/lib/pacman\.py$ + | ^archinstall/lib/profiles\.py$ + | ^archinstall/lib/systemd\.py$ + | ^archinstall/lib/translation\.py$ + | ^archinstall/lib/user_interaction/backwards_compatible_conf\.py$ + | ^archinstall/lib/user_interaction/disk_conf\.py$ + | ^archinstall/lib/user_interaction/general_conf\.py$ + | ^archinstall/lib/user_interaction/locale_conf\.py$ + | ^archinstall/lib/user_interaction/manage_users_conf\.py$ + | ^archinstall/lib/user_interaction/network_conf\.py$ + | ^archinstall/lib/user_interaction/partitioning_conf\.py$ + | ^archinstall/lib/user_interaction/save_conf\.py$ + | ^archinstall/lib/user_interaction/system_conf\.py$ + | ^archinstall/lib/user_interaction/utils\.py$ + | ^archinstall/profiles/applications/pipewire\.py$ + | ^archinstall/profiles/awesome\.py$ + | ^archinstall/profiles/bspwm\.py$ + | ^archinstall/profiles/budgie\.py$ + | ^archinstall/profiles/cinnamon\.py$ + | ^archinstall/profiles/cutefish\.py$ + | ^archinstall/profiles/deepin\.py$ + | ^archinstall/profiles/desktop\.py$ + | ^archinstall/profiles/enlightenment\.py$ + | ^archinstall/profiles/gnome\.py$ + | ^archinstall/profiles/i3\.py$ + | ^archinstall/profiles/kde\.py$ + | ^archinstall/profiles/lxqt\.py$ + | ^archinstall/profiles/mate\.py$ + | ^archinstall/profiles/minimal\.py$ + | ^archinstall/profiles/qtile\.py$ + | ^archinstall/profiles/server\.py$ + | ^archinstall/profiles/sway\.py$ + | ^archinstall/profiles/xfce4\.py$ + | ^archinstall/profiles/xorg\.py$ + | ^profiles/applications/pipewire\.py$ + | ^profiles/awesome\.py$ + | ^profiles/bspwm\.py$ + | ^profiles/budgie\.py$ + | ^profiles/cinnamon\.py$ + | ^profiles/cutefish\.py$ + | ^profiles/deepin\.py$ + | ^profiles/desktop\.py$ + | ^profiles/enlightenment\.py$ + | ^profiles/gnome\.py$ + | ^profiles/i3\.py$ + | ^profiles/kde\.py$ + | ^profiles/lxqt\.py$ + | ^profiles/mate\.py$ + | ^profiles/minimal\.py$ + | ^profiles/qtile\.py$ + | ^profiles/server\.py$ + | ^profiles/sway\.py$ + | ^profiles/xfce4\.py$ + | ^profiles/xorg\.py$ + | ^examples/guided\.py$ + | ^examples/only_hd\.py$ + | ^examples/minimal\.py$ + | ^examples/swiss\.py$) +files = archinstall/, profiles/, examples/ diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 00000000..0add1eb1 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,15 @@ +[mypy] +python_version = 3.10 +follow_imports = silent +exclude = (?x)(^archinstall/lib/disk/btrfs/btrfssubvolumeinfo\.py$ + | ^archinstall/lib/general\.py$ + | ^archinstall/lib/hardware\.py$ + | ^archinstall/lib/menu/menu\.py$ + | ^archinstall/lib/mirrors\.py$ + | ^archinstall/lib/plugins\.py$ + | ^archinstall/lib/installer\.py$ + | ^archinstall/lib/systemd\.py$ + | ^archinstall/lib/user_interaction/general_conf\.py$ + | ^archinstall/lib/user_interaction/locale_conf\.py$ + | ^archinstall/default_profiles/custom\.py$) +files = archinstall/ diff --git a/profiles/52-54-00-12-34-56.py b/profiles/52-54-00-12-34-56.py deleted file mode 100644 index 3b074629..00000000 --- a/profiles/52-54-00-12-34-56.py +++ /dev/null @@ -1,62 +0,0 @@ -import archinstall - -# import json -# import urllib.request - -__packages__ = ['nano', 'wget', 'git'] - -if __name__ == '52-54-00-12-34-56': - awesome = archinstall.Application(archinstall.storage['installation_session'], 'postgresql') - awesome.install() - -""" -# Unmount and close previous runs (Mainly only used for re-runs, but won't hurt.) -archinstall.sys_command(f'umount -R /mnt', suppress_errors=True) -archinstall.sys_command(f'cryptsetup close /dev/mapper/luksloop', suppress_errors=True) - -# Select a harddrive and a disk password -harddrive = archinstall.all_blockdevices()['/dev/sda'] -disk_password = '1234' - -with archinstall.Filesystem(harddrive) as fs: - # Use the entire disk instead of setting up partitions on your own - fs.use_entire_disk('luks2') - - if harddrive.partition[1].size == '512M': - raise OSError('Trying to encrypt the boot partition for Pete's sake..') - harddrive.partition[0].format('fat32') - - with archinstall.luks2(harddrive.partition[1], 'luksloop', disk_password) as unlocked_device: - unlocked_device.format('btrfs') - - with archinstall.Installer( - unlocked_device, - boot_partition=harddrive.partition[0], - hostname="testmachine" - ) as installation: - if installation.minimal_installation(): - installation.add_bootloader() - - installation.add_additional_packages(__packages__) - installation.install_profile('awesome') - - user = User('devel', 'devel', False) - installation.create_users(user) - installation.user_set_pw('root', 'toor') - - print(f'Submitting {archinstall.__version__}: success') - - conditions = { - "project": "archinstall", - "profile": "52-54-00-12-34-56", - "status": "success", - "version": archinstall.__version__ - } - req = urllib.request.Request("https://api.archlinux.life/build/success", - data=json.dumps(conditions).encode('utf8'), - headers={'content-type': 'application/json'}) - try: - urllib.request.urlopen(req, timeout=5) - except: - pass -""" diff --git a/profiles/__init__.py b/profiles/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/profiles/applications/__init__.py b/profiles/applications/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/profiles/applications/awesome.py b/profiles/applications/awesome.py deleted file mode 100644 index 33526fd7..00000000 --- a/profiles/applications/awesome.py +++ /dev/null @@ -1,34 +0,0 @@ -import archinstall - -__packages__ = [ - "awesome", - "xorg-xrandr", - "xterm", - "feh", - "slock", - "terminus-font", - "gnu-free-fonts", - "ttf-liberation", - "xsel", -] - -archinstall.storage['installation_session'].install_profile('xorg') - -archinstall.storage['installation_session'].add_additional_packages(__packages__) - -with open(f"{archinstall.storage['installation_session'].target}/etc/X11/xinit/xinitrc", 'r') as xinitrc: - xinitrc_data = xinitrc.read() - -for line in xinitrc_data.split('\n'): - if "twm &" in line: - xinitrc_data = xinitrc_data.replace(line, f"# {line}") - if "xclock" in line: - xinitrc_data = xinitrc_data.replace(line, f"# {line}") - if "xterm" in line: - xinitrc_data = xinitrc_data.replace(line, f"# {line}") - -xinitrc_data += '\n' -xinitrc_data += 'exec awesome\n' - -with open(f"{archinstall.storage['installation_session'].target}/etc/X11/xinit/xinitrc", 'w') as xinitrc: - xinitrc.write(xinitrc_data) diff --git a/profiles/applications/cockpit.py b/profiles/applications/cockpit.py deleted file mode 100644 index d8aa0fd1..00000000 --- a/profiles/applications/cockpit.py +++ /dev/null @@ -1,13 +0,0 @@ -import archinstall - -# Define the package list in order for lib to source -# which packages will be installed by this profile -__packages__ = [ - "cockpit", - "udisks2", - "packagekit", -] - -archinstall.storage['installation_session'].add_additional_packages(__packages__) - -archinstall.storage['installation_session'].enable_service('cockpit.socket') diff --git a/profiles/applications/docker.py b/profiles/applications/docker.py deleted file mode 100644 index afbde1a5..00000000 --- a/profiles/applications/docker.py +++ /dev/null @@ -1,9 +0,0 @@ -import archinstall - -# Define the package list in order for lib to source -# which packages will be installed by this profile -__packages__ = ["docker"] - -archinstall.storage['installation_session'].add_additional_packages(__packages__) - -archinstall.storage['installation_session'].enable_service('docker') diff --git a/profiles/applications/httpd.py b/profiles/applications/httpd.py deleted file mode 100644 index 23b3fefa..00000000 --- a/profiles/applications/httpd.py +++ /dev/null @@ -1,9 +0,0 @@ -import archinstall - -# Define the package list in order for lib to source -# which packages will be installed by this profile -__packages__ = ["apache"] - -archinstall.storage['installation_session'].add_additional_packages(__packages__) - -archinstall.storage['installation_session'].enable_service('httpd') diff --git a/profiles/applications/lighttpd.py b/profiles/applications/lighttpd.py deleted file mode 100644 index 71158861..00000000 --- a/profiles/applications/lighttpd.py +++ /dev/null @@ -1,9 +0,0 @@ -import archinstall - -# Define the package list in order for lib to source -# which packages will be installed by this profile -__packages__ = ["lighttpd"] - -archinstall.storage['installation_session'].add_additional_packages(__packages__) - -archinstall.storage['installation_session'].enable_service('lighttpd') diff --git a/profiles/applications/mariadb.py b/profiles/applications/mariadb.py deleted file mode 100644 index bdde18b5..00000000 --- a/profiles/applications/mariadb.py +++ /dev/null @@ -1,11 +0,0 @@ -import archinstall - -# Define the package list in order for lib to source -# which packages will be installed by this profile -__packages__ = ["mariadb"] - -archinstall.storage['installation_session'].add_additional_packages(__packages__) - -archinstall.storage['installation_session'].arch_chroot("mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql") - -archinstall.storage['installation_session'].enable_service('mariadb') diff --git a/profiles/applications/nginx.py b/profiles/applications/nginx.py deleted file mode 100644 index 6f63b15c..00000000 --- a/profiles/applications/nginx.py +++ /dev/null @@ -1,9 +0,0 @@ -import archinstall - -# Define the package list in order for lib to source -# which packages will be installed by this profile -__packages__ = ["nginx"] - -archinstall.storage['installation_session'].add_additional_packages(__packages__) - -archinstall.storage['installation_session'].enable_service('nginx') diff --git a/profiles/applications/pipewire.py b/profiles/applications/pipewire.py deleted file mode 100644 index b6e79347..00000000 --- a/profiles/applications/pipewire.py +++ /dev/null @@ -1,14 +0,0 @@ -import archinstall -import logging - -# Define the package list in order for lib to source -# which packages will be installed by this profile -__packages__ = ["pipewire", "pipewire-alsa", "pipewire-jack", "pipewire-pulse", "gst-plugin-pipewire", "libpulse", "wireplumber"] - -archinstall.log('Installing pipewire', level=logging.INFO) -archinstall.storage['installation_session'].add_additional_packages(__packages__) - -@archinstall.plugin -def on_user_created(installation :archinstall.Installer, user :str): - archinstall.log(f"Enabling pipewire-pulse for {user}", level=logging.INFO) - installation.chroot('systemctl enable --user pipewire-pulse.service', run_as=user) diff --git a/profiles/applications/postgresql.py b/profiles/applications/postgresql.py deleted file mode 100644 index 80ad4b66..00000000 --- a/profiles/applications/postgresql.py +++ /dev/null @@ -1,11 +0,0 @@ -import archinstall - -# Define the package list in order for lib to source -# which packages will be installed by this profile -__packages__ = ["postgresql"] - -archinstall.storage['installation_session'].add_additional_packages(__packages__) - -archinstall.storage['installation_session'].arch_chroot("initdb -D /var/lib/postgres/data", run_as='postgres') - -archinstall.storage['installation_session'].enable_service('postgresql') diff --git a/profiles/applications/sshd.py b/profiles/applications/sshd.py deleted file mode 100644 index 4199ecb0..00000000 --- a/profiles/applications/sshd.py +++ /dev/null @@ -1,9 +0,0 @@ -import archinstall - -# Define the package list in order for lib to source -# which packages will be installed by this profile -__packages__ = ["openssh"] - -archinstall.storage['installation_session'].add_additional_packages(__packages__) - -archinstall.storage['installation_session'].enable_service('sshd') diff --git a/profiles/applications/tomcat.py b/profiles/applications/tomcat.py deleted file mode 100644 index ae6d1c2a..00000000 --- a/profiles/applications/tomcat.py +++ /dev/null @@ -1,12 +0,0 @@ -import archinstall - -# This is using Tomcat 10 as that is the latest release at the time of implementation. -# This should probably be updated to use newer releases as they come out. - -# Define the package list in order for lib to source -# which packages will be installed by this profile -__packages__ = ["tomcat10"] - -archinstall.storage['installation_session'].add_additional_packages(__packages__) - -archinstall.storage['installation_session'].enable_service('tomcat10') diff --git a/profiles/awesome.py b/profiles/awesome.py deleted file mode 100644 index 11c8de3b..00000000 --- a/profiles/awesome.py +++ /dev/null @@ -1,51 +0,0 @@ -# A desktop environment using "Awesome" window manager. - -import archinstall - -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__ = [ - "alacritty", -] - - -def _prep_function(*args, **kwargs): - """ - 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. - """ - - # Awesome WM requires that xorg is installed - 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') - - -# Ensures that this code only gets executed if executed -# through importlib.util.spec_from_file_location("awesome", "/somewhere/awesome.py") -# or through conventional import awesome -if __name__ == 'awesome': - # Install the application awesome from the template under /applications/ - awesome = archinstall.Application(archinstall.storage['installation_session'], 'awesome') - awesome.install() - - archinstall.storage['installation_session'].add_additional_packages(__packages__) - - # TODO: Copy a full configuration to ~/.config/awesome/rc.lua instead. - with open(f"{archinstall.storage['installation_session'].target}/etc/xdg/awesome/rc.lua", 'r') as fh: - awesome_lua = fh.read() - - # Replace xterm with alacritty for a smoother experience. - awesome_lua = awesome_lua.replace('"xterm"', '"alacritty"') - - 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) \ No newline at end of file diff --git a/profiles/bspwm.py b/profiles/bspwm.py deleted file mode 100644 index 0fb67ad6..00000000 --- a/profiles/bspwm.py +++ /dev/null @@ -1,43 +0,0 @@ -# A desktop environment using the bspwm window manager. - -import archinstall - -is_top_level_profile = False - -__packages__ = [ - 'bspwm', - 'sxhkd', - 'dmenu', - 'xdo', - 'rxvt-unicode', - 'lightdm', - 'lightdm-gtk-greeter', -] - -def _prep_function(*args, **kwargs): - """ - 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. - """ - - # bspwm 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') - - -# Ensures that this code only gets executed if executed -# through importlib.util.spec_from_file_location("bspwm", "/somewhere/bspwm.py") -# or through conventional import bspwm -if __name__ == 'bspwm': - # Install dependency profiles - archinstall.storage['installation_session'].install_profile('xorg') - # Install bspwm packages - archinstall.storage['installation_session'].add_additional_packages(__packages__) - # Set up LightDM for login - archinstall.storage['installation_session'].enable_service('lightdm') diff --git a/profiles/budgie.py b/profiles/budgie.py deleted file mode 100644 index 33484680..00000000 --- a/profiles/budgie.py +++ /dev/null @@ -1,45 +0,0 @@ -# A desktop environment using "budgie" - -import archinstall - -is_top_level_profile = False - -__packages__ = [ - "arc-gtk-theme", - "budgie", - "lightdm", - "lightdm-gtk-greeter", - "mate-terminal", - "nemo", - "papirus-icon-theme", -] - - -def _prep_function(*args, **kwargs): - """ - 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. - """ - - # budgie 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') - - -# Ensures that this code only gets executed if executed -# through importlib.util.spec_from_file_location("budgie", "/somewhere/budgie.py") -# or through conventional import budgie -if __name__ == 'budgie': - # Install dependency profiles - archinstall.storage['installation_session'].install_profile('xorg') - - # Install the Budgie packages - archinstall.storage['installation_session'].add_additional_packages(__packages__) - - archinstall.storage['installation_session'].enable_service('lightdm') # Light Display Manager diff --git a/profiles/cinnamon.py b/profiles/cinnamon.py deleted file mode 100644 index 0122677a..00000000 --- a/profiles/cinnamon.py +++ /dev/null @@ -1,46 +0,0 @@ -# A desktop environment using "Cinnamon" - -import archinstall - -is_top_level_profile = False - -__packages__ = [ - "cinnamon", - "system-config-printer", - "gnome-keyring", - "gnome-terminal", - "blueberry", - "metacity", - "lightdm", - "lightdm-gtk-greeter", -] - - -def _prep_function(*args, **kwargs): - """ - 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. - """ - - # Cinnamon 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') - - -# Ensures that this code only gets executed if executed -# through importlib.util.spec_from_file_location("cinnamon", "/somewhere/cinnamon.py") -# or through conventional import cinnamon -if __name__ == 'cinnamon': - # Install dependency profiles - archinstall.storage['installation_session'].install_profile('xorg') - - # Install the Cinnamon packages - archinstall.storage['installation_session'].add_additional_packages(__packages__) - - archinstall.storage['installation_session'].enable_service('lightdm') # Light Display Manager diff --git a/profiles/cutefish.py b/profiles/cutefish.py deleted file mode 100644 index 486fa389..00000000 --- a/profiles/cutefish.py +++ /dev/null @@ -1,41 +0,0 @@ -# A desktop environment using "Cutefish" - -import archinstall - -is_top_level_profile = False - -__packages__ = [ - "cutefish", - "noto-fonts", - "sddm" -] - - -def _prep_function(*args, **kwargs): - """ - 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. - """ - - # Cutefish requires a functional 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") - - -# Ensures that this code only gets executed if executed -# through importlib.util.spec_from_file_location("cutefish", "/somewhere/cutefish.py") -# or through conventional import cutefish -if __name__ == "cutefish": - # Install dependency profiles - archinstall.storage["installation_session"].install_profile("xorg") - - # Install the Cutefish packages - archinstall.storage["installation_session"].add_additional_packages(__packages__) - - archinstall.storage["installation_session"].enable_service("sddm") diff --git a/profiles/deepin.py b/profiles/deepin.py deleted file mode 100644 index 8196bc4b..00000000 --- a/profiles/deepin.py +++ /dev/null @@ -1,44 +0,0 @@ -# A desktop environment using "Deepin". - -import archinstall - -is_top_level_profile = False - -__packages__ = [ - "deepin", - "deepin-terminal", - "deepin-editor", - "lightdm", - "lightdm-deepin-greeter", -] - - -def _prep_function(*args, **kwargs): - """ - 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. - """ - - # Deepin 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') - - -# Ensures that this code only gets executed if executed -# through importlib.util.spec_from_file_location("deepin", "/somewhere/deepin.py") -# or through conventional import deepin -if __name__ == 'deepin': - # Install dependency profiles - archinstall.storage['installation_session'].install_profile('xorg') - - # Install the Deepin packages - archinstall.storage['installation_session'].add_additional_packages(__packages__) - - # Enable autostart of Deepin for all users - archinstall.storage['installation_session'].enable_service('lightdm') diff --git a/profiles/desktop.py b/profiles/desktop.py deleted file mode 100644 index e94d3505..00000000 --- a/profiles/desktop.py +++ /dev/null @@ -1,97 +0,0 @@ -# A desktop environment selector. -from typing import Any, TYPE_CHECKING - -import archinstall -from archinstall import log, Menu -from archinstall.lib.menu.menu import MenuSelectionType - -if TYPE_CHECKING: - _: Any - -is_top_level_profile = True - -__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". -__packages__ = [ - 'nano', - 'vim', - 'openssh', - 'htop', - 'wget', - 'iwd', - 'wireless_tools', - 'wpa_supplicant', - 'smartmontools', - 'xdg-utils', -] - -__supported__ = [ - 'gnome', - 'kde', - 'awesome', - 'sway', - 'cinnamon', - 'xfce4', - 'lxqt', - 'i3', - 'bspwm', - 'budgie', - 'mate', - 'deepin', - 'enlightenment', - 'qtile' -] - - -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. - """ - choice = Menu(str(_('Select your desired desktop environment')), __supported__).run() - - 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. - if not archinstall.storage.get('_desktop_profile', None): - archinstall.storage['_desktop_profile'] = choice.value - if not archinstall.arguments.get('desktop-environment', None): - 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"{choice.value}.py") as imported: - if hasattr(imported, '_prep_function'): - return imported._prep_function() - else: - log(f"Deprecated (??): {choice.value} profile has no _prep_function() anymore") - exit(1) - - return False - - -if __name__ == 'desktop': - """ - This "profile" is a meta-profile. - There are no desktop-specific steps, it simply routes - the installer to whichever desktop environment/window manager was chosen. - - Maybe in the future, a network manager or similar things *could* be added here. - We should honor that Arch Linux does not officially endorse a desktop-setup, nor is - it trying to be a turn-key desktop distribution. - - There are plenty of desktop-turn-key-solutions based on Arch Linux, - this is therefore just a helper to get started - """ - - # Install common packages for all desktop environments - archinstall.storage['installation_session'].add_additional_packages(__packages__) - - archinstall.storage['installation_session'].install_profile(archinstall.storage['_desktop_profile']) diff --git a/profiles/enlightenment.py b/profiles/enlightenment.py deleted file mode 100644 index 3850fed0..00000000 --- a/profiles/enlightenment.py +++ /dev/null @@ -1,43 +0,0 @@ -# A desktop environment using "Enlightenment". - -import archinstall - -is_top_level_profile = False - -__packages__ = [ - "enlightenment", - "terminology", - "lightdm", - "lightdm-gtk-greeter", -] - - -def _prep_function(*args, **kwargs): - """ - 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. - """ - - # Enlightenment 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') - - -# Ensures that this code only gets executed if executed -# through importlib.util.spec_from_file_location("enlightenment", "/somewhere/enlightenment.py") -# or through conventional import enlightenment -if __name__ == 'enlightenment': - # Install dependency profiles - archinstall.storage['installation_session'].install_profile('xorg') - - # Install the enlightenment packages - archinstall.storage['installation_session'].add_additional_packages(__packages__) - - # Enable autostart of enlightenment for all users - archinstall.storage['installation_session'].enable_service('lightdm') diff --git a/profiles/gnome.py b/profiles/gnome.py deleted file mode 100644 index 5e3a8da6..00000000 --- a/profiles/gnome.py +++ /dev/null @@ -1,45 +0,0 @@ -# A desktop environment using "Gnome" - -import archinstall - -is_top_level_profile = False - -# Note: GDM should be part of the gnome group, but adding it here for clarity -__packages__ = [ - "gnome", - "gnome-tweaks", - "gdm" -] - - -def _prep_function(*args, **kwargs): - """ - 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. - """ - - # Gnome optionally supports xorg, we'll install it since it also - # includes graphic driver setups (this might change in the future) - 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') - - -# Ensures that this code only gets executed if executed -# through importlib.util.spec_from_file_location("gnome", "/somewhere/gnome.py") -# or through conventional import gnome -if __name__ == 'gnome': - # Install dependency profiles - archinstall.storage['installation_session'].install_profile('xorg') - - # Install the GNOME packages - archinstall.storage['installation_session'].add_additional_packages(__packages__) - - archinstall.storage['installation_session'].enable_service('gdm') # Gnome Display Manager -# We could also start it via xinitrc since we do have Xorg, -# but for gnome that's deprecated and wayland is preferred. diff --git a/profiles/i3.py b/profiles/i3.py deleted file mode 100644 index d9b98b77..00000000 --- a/profiles/i3.py +++ /dev/null @@ -1,59 +0,0 @@ -# Common package for i3. - -import archinstall - -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__ = [ - 'i3-wm', - 'i3lock', - 'i3status', - 'i3blocks', - 'xterm', - 'lightdm-gtk-greeter', - 'lightdm', - 'dmenu', -] - - -def _prep_function(*args, **kwargs): - """ - 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. - """ - - # 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': - """ - This "profile" is a meta-profile. - There are no desktop-specific steps, it simply routes - the installer to whichever desktop environment/window manager was chosen. - - Maybe in the future, a network manager or similar things *could* be added here. - We should honor that Arch Linux does not officially endorse a desktop-setup, nor is - it trying to be a turn-key desktop distribution. - - There are plenty of desktop-turn-key-solutions based on Arch Linux, - this is therefore just a helper to get started - """ - - # Install dependency profiles - archinstall.storage['installation_session'].install_profile('xorg') - - # Install the i3 packages - archinstall.storage['installation_session'].add_additional_packages(__packages__) - - # Enable autostart of lightdm for all users - archinstall.storage['installation_session'].enable_service('lightdm') diff --git a/profiles/kde.py b/profiles/kde.py deleted file mode 100644 index d32bf31b..00000000 --- a/profiles/kde.py +++ /dev/null @@ -1,58 +0,0 @@ -# A desktop environment using "KDE". - -import archinstall - -is_top_level_profile = False - -__packages__ = [ - "plasma-meta", - "konsole", - "kwrite", - "dolphin", - "ark", - "sddm", - "plasma-wayland-session", - "egl-wayland" -] - - -# TODO: Remove hard dependency of bash (due to .bash_profile) - - -def _prep_function(*args, **kwargs): - """ - 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. - """ - - # KDE 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') - - -""" -def _post_install(*args, **kwargs): - if "nvidia" in _gfx_driver_packages: - print("Plasma Wayland has known compatibility issues with the proprietary Nvidia driver") - print("After booting, you can choose between Wayland and Xorg using the drop-down menu") - return True -""" - -# Ensures that this code only gets executed if executed -# through importlib.util.spec_from_file_location("kde", "/somewhere/kde.py") -# or through conventional import kde -if __name__ == 'kde': - # Install dependency profiles - archinstall.storage['installation_session'].install_profile('xorg') - - # Install the KDE packages - archinstall.storage['installation_session'].add_additional_packages(__packages__) - - # Enable autostart of KDE for all users - archinstall.storage['installation_session'].enable_service('sddm') diff --git a/profiles/lxqt.py b/profiles/lxqt.py deleted file mode 100644 index 2419b4fa..00000000 --- a/profiles/lxqt.py +++ /dev/null @@ -1,50 +0,0 @@ -# A desktop environment using "LXQt" - -import archinstall - -is_top_level_profile = False - -# NOTE: SDDM is the only officially supported greeter for LXQt, so unlike other DEs, lightdm is not used here. -# LXQt works with lightdm, but since this is not supported, we will not default to this. -# https://github.com/lxqt/lxqt/issues/795 -__packages__ = [ - "lxqt", - "breeze-icons", - "oxygen-icons", - "xdg-utils", - "ttf-freefont", - "leafpad", - "slock", - "sddm", -] - - -def _prep_function(*args, **kwargs): - """ - 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. - """ - - # LXQt requires a functional 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') - - -# Ensures that this code only gets executed if executed -# through importlib.util.spec_from_file_location("lxqt", "/somewhere/lxqt.py") -# or through conventional import lxqt -if __name__ == 'lxqt': - # Install dependency profiles - archinstall.storage['installation_session'].install_profile('xorg') - - # Install the LXQt packages - archinstall.storage['installation_session'].add_additional_packages(__packages__) - - # Enable autostart of LXQt for all users - archinstall.storage['installation_session'].enable_service('sddm') diff --git a/profiles/mate.py b/profiles/mate.py deleted file mode 100644 index 94b91f81..00000000 --- a/profiles/mate.py +++ /dev/null @@ -1,42 +0,0 @@ -# A desktop environment using "MATE" - -import archinstall - -is_top_level_profile = False - -__packages__ = [ - "mate", - "mate-extra", - "lightdm", - "lightdm-gtk-greeter", -] - - -def _prep_function(*args, **kwargs): - """ - 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. - """ - - # MATE requires a functional 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') - - -# Ensures that this code only gets executed if executed -# through importlib.util.spec_from_file_location("mate", "/somewhere/mate.py") -# or through conventional import mate -if __name__ == 'mate': - # Install dependency profiles - archinstall.storage['installation_session'].install_profile('xorg') - - # Install the MATE packages - archinstall.storage['installation_session'].add_additional_packages(__packages__) - - archinstall.storage['installation_session'].enable_service('lightdm') # Light Display Manager diff --git a/profiles/minimal.py b/profiles/minimal.py deleted file mode 100644 index a412aa81..00000000 --- a/profiles/minimal.py +++ /dev/null @@ -1,24 +0,0 @@ -# Used to do a minimal install -import archinstall - -is_top_level_profile = True - -__description__ = str(_('A very basic installation that allows you to customize Arch Linux as you see fit.')) - - -def _prep_function(*args, **kwargs): - """ - Magic function called by the importing installer - before continuing any further. For minimal install, - 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 - - -if __name__ == 'minimal': - """ - This "profile" is a meta-profile. - It is used for a custom minimal installation, without any desktop-specific packages. - """ diff --git a/profiles/qtile.py b/profiles/qtile.py deleted file mode 100644 index ace13dcc..00000000 --- a/profiles/qtile.py +++ /dev/null @@ -1,42 +0,0 @@ -# A desktop environment using "qtile" window manager with common packages. - -import archinstall - -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__ = [ - 'qtile', - 'alacritty', - 'lightdm-gtk-greeter', - 'lightdm', -] - -def _prep_function(*args, **kwargs): - """ - 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. - """ - - # qtile optionally supports xorg, we'll install it since it also - # includes graphic driver setups (this might change in the future) - 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__ == 'qtile': - # Install dependency profiles - archinstall.storage['installation_session'].install_profile('xorg') - - # Install packages for qtile - archinstall.storage['installation_session'].add_additional_packages(__packages__) - - # Auto start lightdm for all users - archinstall.storage['installation_session'].enable_service('lightdm') # Light Display Manager diff --git a/profiles/server.py b/profiles/server.py deleted file mode 100644 index f3e32d26..00000000 --- a/profiles/server.py +++ /dev/null @@ -1,63 +0,0 @@ -# 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 - -__description__ = str(_('Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb')) - -available_servers = [ - "cockpit", - "docker", - "httpd", - "lighttpd", - "mariadb", - "nginx", - "postgresql", - "sshd", - "tomcat", -] - - -def _prep_function(*args, **kwargs): - """ - Magic function called by the importing installer - before continuing any further. - """ - choice = Menu(str(_( - 'Choose which servers to install, if none then a minimal installation will be done')), - available_servers, - preset_values=kwargs['servers'], - multi=True - ).run() - - if choice.type_ != MenuSelectionType.Selection: - return False - - if choice.value: - archinstall.storage['_selected_servers'] = choice.value - return True - - return False - - -if __name__ == 'server': - """ - This "profile" is a meta-profile. - """ - archinstall.log('Now installing the selected servers.', level=logging.INFO) - archinstall.log(archinstall.storage['_selected_servers'], level=logging.DEBUG) - for server in archinstall.storage['_selected_servers']: - archinstall.log(f'Installing {server} ...', level=logging.INFO) - app = archinstall.Application(archinstall.storage['installation_session'], server) - app.install() - - archinstall.log('If your selections included multiple servers with the same port, you may have to reconfigure them.', fg="yellow", level=logging.INFO) diff --git a/profiles/sway.py b/profiles/sway.py deleted file mode 100644 index f69f73ce..00000000 --- a/profiles/sway.py +++ /dev/null @@ -1,100 +0,0 @@ -# A desktop environment using "Sway" -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 = False - -__packages__ = [ - "sway", - "swaybg", - "swaylock", - "swayidle", - "waybar", - "dmenu", - "brightnessctl", - "grim", - "slurp", - "pavucontrol", - "foot", - "xorg-xwayland", -] - - -def _check_driver() -> bool: - 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 = Menu(prompt, Menu.yes_no(), default_option=Menu.no(), skip=False).run() - - if choice.value == Menu.no(): - return False - - return True - -def _get_system_privelege_control_preference(): - # need to activate seat service and add to seat group - title = str(_('Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)')) - title += str(_('\n\nChoose an option to give Sway access to your hardware')) - choice = Menu(title, ["polkit", "seatd"]).run() - - if choice.type_ != MenuSelectionType.Selection: - return False - - archinstall.storage['sway_sys_priv_ctrl'] = [choice.value] - archinstall.arguments['sway_sys_priv_ctrl'] = [choice.value] - return True - -def _prep_function(*args, **kwargs): - """ - 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. - """ - if not _get_system_privelege_control_preference(): - return False - - driver = archinstall.select_driver() - - if driver: - archinstall.storage["gfx_driver_packages"] = driver - if not _check_driver(): - return _prep_function(args, kwargs) - return True - - return False - - -""" -def _post_install(*args, **kwargs): - if "seatd" in sway_sys_priv_ctrl: - print(_('After booting, add user(s) to the `seat` user group and re-login to use Sway')) - return True -""" - -# Ensures that this code only gets executed if executed -# through importlib.util.spec_from_file_location("sway", "/somewhere/sway.py") -# or through conventional import sway -if __name__ == "sway": - if not _check_driver(): - raise archinstall.lib.exceptions.HardwareIncompatibilityError(_('Sway does not support the proprietary nvidia drivers.')) - - # Install the Sway packages - archinstall.storage['installation_session'].add_additional_packages(__packages__) - if "seatd" in archinstall.storage['sway_sys_priv_ctrl']: - archinstall.storage['installation_session'].add_additional_packages(['seatd']) - archinstall.storage['installation_session'].enable_service('seatd') - elif "polkit" in archinstall.storage['sway_sys_priv_ctrl']: - archinstall.storage['installation_session'].add_additional_packages(['polkit']) - else: - raise archinstall.lib.exceptions.ProfileError(_('Sway requires either seatd or polkit to run')) - - # Install the graphics driver packages - archinstall.storage['installation_session'].add_additional_packages(f"xorg-server xorg-xinit {' '.join(archinstall.storage.get('gfx_driver_packages', None))}") diff --git a/profiles/xfce4.py b/profiles/xfce4.py deleted file mode 100644 index fbc68c10..00000000 --- a/profiles/xfce4.py +++ /dev/null @@ -1,45 +0,0 @@ -# A desktop environment using "Xfce4" - -import archinstall - -is_top_level_profile = False - -__packages__ = [ - "xfce4", - "xfce4-goodies", - "pavucontrol", - "lightdm", - "lightdm-gtk-greeter", - "gvfs", - "xarchiver" -] - - -def _prep_function(*args, **kwargs): - """ - 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. - """ - - # XFCE requires a functional 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') - - -# Ensures that this code only gets executed if executed -# through importlib.util.spec_from_file_location("xfce4", "/somewhere/xfce4.py") -# or through conventional import xfce4 -if __name__ == 'xfce4': - # Install dependency profiles - archinstall.storage['installation_session'].install_profile('xorg') - - # Install the XFCE4 packages - archinstall.storage['installation_session'].add_additional_packages(__packages__) - - archinstall.storage['installation_session'].enable_service('lightdm') # Light Display Manager diff --git a/profiles/xorg.py b/profiles/xorg.py deleted file mode 100644 index de45acd3..00000000 --- a/profiles/xorg.py +++ /dev/null @@ -1,68 +0,0 @@ -# A system with "xorg" installed - -import archinstall -import logging -from archinstall.lib.hardware import __packages__ as __hwd__packages__ - -is_top_level_profile = True - -__description__ = str(_('Installs a minimal system as well as xorg and graphics drivers.')) - -__packages__ = [ - 'dkms', - 'xorg-server', - 'xorg-xinit', - 'nvidia-dkms', - *__hwd__packages__, -] - - -def _prep_function(*args, **kwargs): - """ - 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. - """ - - 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 False - - -# Ensures that this code only gets executed if executed -# through importlib.util.spec_from_file_location("xorg", "/somewhere/xorg.py") -# or through conventional import xorg -if __name__ == 'xorg': - try: - if "nvidia" in archinstall.storage.get("gfx_driver_packages", []): - if "linux-zen" in archinstall.storage['installation_session'].base_packages or "linux-lts" in archinstall.storage['installation_session'].base_packages: - for kernel in archinstall.storage['installation_session'].kernels: - archinstall.storage['installation_session'].add_additional_packages(f"{kernel}-headers") # Fixes https://github.com/archlinux/archinstall/issues/585 - archinstall.storage['installation_session'].add_additional_packages("dkms") # I've had kernel regen fail if it wasn't installed before nvidia-dkms - archinstall.storage['installation_session'].add_additional_packages("xorg-server", "xorg-xinit", "nvidia-dkms") - else: - archinstall.storage['installation_session'].add_additional_packages(f"xorg-server", "xorg-xinit", *archinstall.storage.get('gfx_driver_packages', [])) - elif 'amdgpu' in archinstall.storage.get("gfx_driver_packages", []): - # The order of these two are important if amdgpu is installed #808 - if 'amdgpu' in archinstall.storage['installation_session'].MODULES: - archinstall.storage['installation_session'].MODULES.remove('amdgpu') - archinstall.storage['installation_session'].MODULES.append('amdgpu') - - if 'radeon' in archinstall.storage['installation_session'].MODULES: - archinstall.storage['installation_session'].MODULES.remove('radeon') - archinstall.storage['installation_session'].MODULES.append('radeon') - - archinstall.storage['installation_session'].add_additional_packages(f"xorg-server", "xorg-xinit", *archinstall.storage.get('gfx_driver_packages', [])) - else: - archinstall.storage['installation_session'].add_additional_packages(f"xorg-server", "xorg-xinit", *archinstall.storage.get('gfx_driver_packages', [])) - except Exception as err: - archinstall.log(f"Could not handle nvidia and linuz-zen specific situations during xorg installation: {err}", level=logging.WARNING, fg="yellow") - archinstall.storage['installation_session'].add_additional_packages("xorg-server", "xorg-xinit") # Prep didn't run, so there's no driver to install diff --git a/pyproject.toml b/pyproject.toml index 6e0fcb99..557418cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "archinstall" -dynamic = ["version", "entry-points"] +dynamic = ["version"] description = "Arch Linux installer - guided, templates etc." authors = [ {name = "Anton Hvornum", email = "anton@hvornum.se"}, @@ -12,15 +12,17 @@ authors = [ license = {text = "GPL-3.0-only"} readme = "README.md" requires-python = ">=3.10" - keywords = ["linux", "arch", "archinstall", "installer"] - classifiers = [ "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Operating System :: POSIX :: Linux", ] +dependencies = [ + "pyparted==3.12.0", + "simple-term-menu==1.6.1", +] [project.urls] Home = "https://archlinux.org" @@ -28,23 +30,33 @@ Documentation = "https://archinstall.readthedocs.io/" Source = "https://github.com/archlinux/archinstall" [project.optional-dependencies] +dev = [ + "mypy==1.1.1", +] doc = ["sphinx"] [project.scripts] archinstall = "archinstall:run_as_a_module" +[tool.setuptools.dynamic] +version = {attr = "archinstall.__version__"} +readme = {file = ["README.rst", "USAGE.rst"]} + [tool.setuptools] -packages = ["archinstall", "profiles", "examples"] +packages = ["archinstall"] [tool.setuptools.package-data] -archinstall = [ - "examples/*.py", - "profiles/*.py", - "profiles/applications/*.py" +# We could specify locales/lancuages.json etc instead, but catchall works too. +"archinstall" = [ + "**/*.py", + "**/*.mo", + "**/*.po", + "**/*.pot", + "**/*.json", ] -[tool.setuptools.dynamic] -version = {attr = "archinstall.__version__"} +# [tool.setuptools.packages.find] +# where = ["archinstall"] [tool.mypy] python_version = "3.10" diff --git a/schema.json b/schema.json index 9269e8e8..0a41ebf0 100644 --- a/schema.json +++ b/schema.json @@ -37,17 +37,6 @@ "type": "string" } }, - "gfx_driver": { - "description": "Graphics Drivers to install if a desktop profile is used, ignored otherwise.", - "type": "string", - "enum": [ - "VMware / VirtualBox (open-source)", - "Nvidia", - "Intel (open-source)", - "AMD / ATI (open-source)", - "All open-source (default)" - ] - }, "harddrives": { "description": "Path of device to be used", "type": "array", @@ -110,29 +99,83 @@ } }, "profile": { - "description": "Profiles are present in profiles/, use the name of a profile to install it", - "type": "string", - "enum": [ - "awesome", - "bspwm", - "budgie", - "cinnamon", - "cutefish", - "deepin", - "desktop", - "enlightenment", - "gnome", - "i3", - "kde", - "lxqt", - "mate", - "minimal", - "server", - "sway", - "xfce4", - "xorg", - "qtile" - ] + "path": { + "description": "Local path or Url that points to a python file containing profile definitions", + "type": "string" + }, + "main": { + "description": "Main top level profile selection", + "type": "string", + "enum": [ + "desktop", + "minimal", + "server", + "xorg", + "custom" + ] + }, + "details": { + "description": "Specific profile to be installed based on the 'main' selection; these profiles are present in profiles_v2/, use the name of a profile to install it (case insensitive)", + "type": "string", + "enum": [ + "awesome", + "bspwm", + "budgie", + "cinnamon", + "cutefish", + "deepin", + "desktop", + "enlightenment", + "gnome", + "i3-wm", + "i3-gasp", + "kde", + "lxqt", + "mate", + "sway", + "xfce4", + "qtile", + "cockpit", + "docker", + "httpd", + "lighttpd", + "mariadb", + "nginx", + "postgresql", + "sshd", + "tomcat" + ] + }, + "custom": { + "description": "Specific profile definitions for custom setup profiles)", + "type": "array", + "items": { + "type": "object", + "properties": { + "packages": "string", + "services": "string", + "enabled": "boolean" + } + } + }, + "gfx_driver": { + "description": "Graphics Drivers to install if a desktop profile is used, ignored otherwise.", + "type": "string", + "enum": [ + "VMware / VirtualBox (open-source)", + "Intel (open-source)", + "AMD / ATI (open-source)", + "All open-source (default)", + "Nvidia (open kernel module for newer GPUs, Turing+)", + "Nvidia (open-source nouveau driver)", + "Nvidia (proprietary)" + ] + }, + "greeter_type": { + "description": "Greeter type to install if a desktop profile is used, ignored otherwise.", + "type": "string", + "enum": ["lightdm", "sddm", "gdm"] + } }, "services": { "description": "Services to enable post-installation", -- cgit v1.2.3-70-g09d2 From 32982b880bd4e76b158442e444c001f8ebd8bb06 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Sat, 22 Apr 2023 16:04:36 +1000 Subject: Fix import and configuration loading issues (#1764) * Fix import and configuration loading issues * Fix mypy --------- Co-authored-by: Daniel Girtler --- archinstall/__init__.py | 8 +- archinstall/default_profiles/custom.py | 436 ++++++++++++++-------------- archinstall/lib/profile/profiles_handler.py | 44 +-- mypy.ini | 3 +- 4 files changed, 245 insertions(+), 246 deletions(-) diff --git a/archinstall/__init__.py b/archinstall/__init__.py index 8cb6ced9..3d0768a5 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -4,7 +4,7 @@ from argparse import ArgumentParser, Namespace from .lib import disk from .lib import menu -from .lib import models as models +from .lib import models from .lib import packages from .lib.exceptions import * @@ -203,17 +203,17 @@ def load_config(): storage['_selected_servers'] = arguments.get('servers', None) if arguments.get('nic', None) is not None: - handler = NetworkConfigurationHandler() + handler = models.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) + arguments['!users'] = models.User.parse_arguments(users, superusers) if arguments.get('bootloader', None) is not None: - arguments['bootloader'] = Bootloader.from_arg(arguments['bootloader']) + arguments['bootloader'] = models.Bootloader.from_arg(arguments['bootloader']) if arguments.get('disk_encryption', None) is not None and disk_config is not None: password = arguments.get('encryption_password', '') diff --git a/archinstall/default_profiles/custom.py b/archinstall/default_profiles/custom.py index f7e100ed..5f9db620 100644 --- a/archinstall/default_profiles/custom.py +++ b/archinstall/default_profiles/custom.py @@ -1,218 +1,218 @@ -from typing import List, Dict, Optional, TYPE_CHECKING, Any - -from ..lib import menu -from archinstall.lib.output import log, FormattedOutput -from archinstall.lib.profile.profiles_handler import profile_handler -from archinstall.default_profiles.profile import Profile, ProfileType, SelectResult, ProfileInfo, TProfile - -if TYPE_CHECKING: - from archinstall.lib.installer import Installer - _: Any - - -class CustomProfileList(menu.ListManager): - def __init__(self, prompt: str, profiles: List[TProfile]): - self._actions = [ - str(_('Add profile')), - str(_('Edit profile')), - str(_('Delete profile')) - ] - super().__init__(prompt, profiles, [self._actions[0]], self._actions[1:]) - - def reformat(self, data: List[TProfile]) -> Dict[str, Optional[TProfile]]: - table = FormattedOutput.as_table(data) - rows = table.split('\n') - - # these are the header rows of the table and do not map to any profile obviously - # we're adding 2 spaces as prefix because the menu selector '> ' will be put before - # the selectable rows so the header has to be aligned - display_data: Dict[str, Optional[TProfile]] = {f' {rows[0]}': None, f' {rows[1]}': None} - - for row, profile in zip(rows[2:], data): - row = row.replace('|', '\\|') - display_data[row] = profile - - return display_data - - def selected_action_display(self, profile: TProfile) -> str: - return profile.name - - def handle_action( - self, - action: str, - entry: Optional['CustomTypeProfile'], - data: List['CustomTypeProfile'] - ) -> List['CustomTypeProfile']: - if action == self._actions[0]: # add - new_profile = self._add_profile() - if new_profile is not None: - # in case a profile with the same name as an existing profile - # was created we'll replace the existing one - data = [d for d in data if d.name != new_profile.name] - data += [new_profile] - elif entry is not None: - if action == self._actions[1]: # edit - new_profile = self._add_profile(entry) - if new_profile is not None: - # we'll remove the original profile and add the modified version - data = [d for d in data if d.name != entry.name and d.name != new_profile.name] - data += [new_profile] - elif action == self._actions[2]: # delete - data = [d for d in data if d != entry] - - return data - - def _is_new_profile_name(self, name: str) -> bool: - existing_profile = profile_handler.get_profile_by_name(name) - if existing_profile is not None and existing_profile.profile_type != ProfileType.CustomType: - return False - return True - - def _add_profile(self, editing: Optional['CustomTypeProfile'] = None) -> Optional['CustomTypeProfile']: - name_prompt = '\n\n' + str(_('Profile name: ')) - - while True: - profile_name = menu.TextInput(name_prompt, editing.name if editing else '').run().strip() - - if not profile_name: - return None - - if not self._is_new_profile_name(profile_name): - error_prompt = str(_("The profile name you entered is already in use. Try again")) - print(error_prompt) - else: - break - - packages_prompt = str(_('Packages to be install with this profile (space separated, leave blank to skip): ')) - edit_packages = ' '.join(editing.packages) if editing else '' - packages = menu.TextInput(packages_prompt, edit_packages).run().strip() - - services_prompt = str(_('Services to be enabled with this profile (space separated, leave blank to skip): ')) - edit_services = ' '.join(editing.services) if editing else '' - services = menu.TextInput(services_prompt, edit_services).run().strip() - - choice = menu.Menu( - str(_('Should this profile be enabled for installation?')), - menu.Menu.yes_no(), - skip=False, - default_option=menu.Menu.no(), - clear_screen=False, - show_search_hint=False - ).run() - - enable_profile = True if choice.value == menu.Menu.yes() else False - - profile = CustomTypeProfile( - profile_name, - enabled=enable_profile, - packages=packages.split(' '), - services=services.split(' ') - ) - - return profile - - -# TODO -# Still needs some ironing out -class CustomProfile(): - def __init__(self): - super().__init__( - 'Custom', - ProfileType.Custom, - description=str(_('Create your own')) - ) - - def json(self) -> Dict[str, Any]: - data: Dict[str, Any] = {'main': self.name, 'gfx_driver': self.gfx_driver, 'custom': []} - - for profile in self._current_selection: - data['custom'].append({ - 'name': profile.name, - 'packages': profile.packages, - 'services': profile.services, - 'enabled': profile.custom_enabled - }) - - return data - - def do_on_select(self) -> SelectResult: - custom_profile_list = CustomProfileList('', profile_handler.get_custom_profiles()) - custom_profiles = custom_profile_list.run() - - # we'll first remove existing custom default_profiles with - # the same name and then add the new ones this - # will avoid errors of default_profiles with duplicate naming - profile_handler.remove_custom_profiles(custom_profiles) - profile_handler.add_custom_profiles(custom_profiles) - - self.set_current_selection(custom_profiles) - - if custom_profile_list.is_last_choice_cancel(): - return SelectResult.SameSelection - - enabled_profiles = [p for p in self._current_selection if p.custom_enabled] - # in case we only created inactive default_profiles we wanna store them but - # we want to reset the original setting - if not enabled_profiles: - return SelectResult.ResetCurrent - - return SelectResult.NewSelection - - def post_install(self, install_session: 'Installer'): - for profile in self._current_selection: - profile.post_install(install_session) - - def install(self, install_session: 'Installer'): - driver_packages = self.gfx_driver_packages() - install_session.add_additional_packages(driver_packages) - - for profile in self._current_selection: - if profile.custom_enabled: - log(f'Installing custom profile {profile.name}...') - - install_session.add_additional_packages(profile.packages) - install_session.enable_service(profile.services) - - profile.install(install_session) - - def info(self) -> Optional[ProfileInfo]: - enabled_profiles = [p for p in self._current_selection if p.custom_enabled] - if enabled_profiles: - details = ', '.join([p.name for p in enabled_profiles]) - gfx_driver = self.gfx_driver - return ProfileInfo(self.name, details, gfx_driver) - - return None - - def reset(self): - for profile in self._current_selection: - profile.set_enabled(False) - - self.gfx_driver = None - - -class CustomTypeProfile(Profile): - def __init__( - self, - name: str, - enabled: bool = False, - packages: List[str] = [], - services: List[str] = [] - ): - super().__init__( - name, - ProfileType.CustomType, - packages=packages, - services=services, - support_gfx_driver=True - ) - - self.custom_enabled = enabled - - def json(self) -> Dict[str, Any]: - return { - 'name': self.name, - 'packages': self.packages, - 'services': self.services, - 'enabled': self.custom_enabled - } +# from typing import List, Dict, Optional, TYPE_CHECKING, Any +# +# from ..lib import menu +# from archinstall.lib.output import log, FormattedOutput +# from archinstall.lib.profile.profiles_handler import profile_handler +# from archinstall.default_profiles.profile import Profile, ProfileType, SelectResult, ProfileInfo, TProfile +# +# if TYPE_CHECKING: +# from archinstall.lib.installer import Installer +# _: Any +# +# +# class CustomProfileList(menu.ListManager): +# def __init__(self, prompt: str, profiles: List[TProfile]): +# self._actions = [ +# str(_('Add profile')), +# str(_('Edit profile')), +# str(_('Delete profile')) +# ] +# super().__init__(prompt, profiles, [self._actions[0]], self._actions[1:]) +# +# def reformat(self, data: List[TProfile]) -> Dict[str, Optional[TProfile]]: +# table = FormattedOutput.as_table(data) +# rows = table.split('\n') +# +# # these are the header rows of the table and do not map to any profile obviously +# # we're adding 2 spaces as prefix because the menu selector '> ' will be put before +# # the selectable rows so the header has to be aligned +# display_data: Dict[str, Optional[TProfile]] = {f' {rows[0]}': None, f' {rows[1]}': None} +# +# for row, profile in zip(rows[2:], data): +# row = row.replace('|', '\\|') +# display_data[row] = profile +# +# return display_data +# +# def selected_action_display(self, profile: TProfile) -> str: +# return profile.name +# +# def handle_action( +# self, +# action: str, +# entry: Optional['CustomTypeProfile'], +# data: List['CustomTypeProfile'] +# ) -> List['CustomTypeProfile']: +# if action == self._actions[0]: # add +# new_profile = self._add_profile() +# if new_profile is not None: +# # in case a profile with the same name as an existing profile +# # was created we'll replace the existing one +# data = [d for d in data if d.name != new_profile.name] +# data += [new_profile] +# elif entry is not None: +# if action == self._actions[1]: # edit +# new_profile = self._add_profile(entry) +# if new_profile is not None: +# # we'll remove the original profile and add the modified version +# data = [d for d in data if d.name != entry.name and d.name != new_profile.name] +# data += [new_profile] +# elif action == self._actions[2]: # delete +# data = [d for d in data if d != entry] +# +# return data +# +# def _is_new_profile_name(self, name: str) -> bool: +# existing_profile = profile_handler.get_profile_by_name(name) +# if existing_profile is not None and existing_profile.profile_type != ProfileType.CustomType: +# return False +# return True +# +# def _add_profile(self, editing: Optional['CustomTypeProfile'] = None) -> Optional['CustomTypeProfile']: +# name_prompt = '\n\n' + str(_('Profile name: ')) +# +# while True: +# profile_name = menu.TextInput(name_prompt, editing.name if editing else '').run().strip() +# +# if not profile_name: +# return None +# +# if not self._is_new_profile_name(profile_name): +# error_prompt = str(_("The profile name you entered is already in use. Try again")) +# print(error_prompt) +# else: +# break +# +# packages_prompt = str(_('Packages to be install with this profile (space separated, leave blank to skip): ')) +# edit_packages = ' '.join(editing.packages) if editing else '' +# packages = menu.TextInput(packages_prompt, edit_packages).run().strip() +# +# services_prompt = str(_('Services to be enabled with this profile (space separated, leave blank to skip): ')) +# edit_services = ' '.join(editing.services) if editing else '' +# services = menu.TextInput(services_prompt, edit_services).run().strip() +# +# choice = menu.Menu( +# str(_('Should this profile be enabled for installation?')), +# menu.Menu.yes_no(), +# skip=False, +# default_option=menu.Menu.no(), +# clear_screen=False, +# show_search_hint=False +# ).run() +# +# enable_profile = True if choice.value == menu.Menu.yes() else False +# +# profile = CustomTypeProfile( +# profile_name, +# enabled=enable_profile, +# packages=packages.split(' '), +# services=services.split(' ') +# ) +# +# return profile +# +# +# # TODO +# # Still needs some ironing out +# class CustomProfile(): +# def __init__(self): +# super().__init__( +# 'Custom', +# ProfileType.Custom, +# description=str(_('Create your own')) +# ) +# +# def json(self) -> Dict[str, Any]: +# data: Dict[str, Any] = {'main': self.name, 'gfx_driver': self.gfx_driver, 'custom': []} +# +# for profile in self._current_selection: +# data['custom'].append({ +# 'name': profile.name, +# 'packages': profile.packages, +# 'services': profile.services, +# 'enabled': profile.custom_enabled +# }) +# +# return data +# +# def do_on_select(self) -> SelectResult: +# custom_profile_list = CustomProfileList('', profile_handler.get_custom_profiles()) +# custom_profiles = custom_profile_list.run() +# +# # we'll first remove existing custom default_profiles with +# # the same name and then add the new ones this +# # will avoid errors of default_profiles with duplicate naming +# profile_handler.remove_custom_profiles(custom_profiles) +# profile_handler.add_custom_profiles(custom_profiles) +# +# self.set_current_selection(custom_profiles) +# +# if custom_profile_list.is_last_choice_cancel(): +# return SelectResult.SameSelection +# +# enabled_profiles = [p for p in self._current_selection if p.custom_enabled] +# # in case we only created inactive default_profiles we wanna store them but +# # we want to reset the original setting +# if not enabled_profiles: +# return SelectResult.ResetCurrent +# +# return SelectResult.NewSelection +# +# def post_install(self, install_session: 'Installer'): +# for profile in self._current_selection: +# profile.post_install(install_session) +# +# def install(self, install_session: 'Installer'): +# driver_packages = self.gfx_driver_packages() +# install_session.add_additional_packages(driver_packages) +# +# for profile in self._current_selection: +# if profile.custom_enabled: +# log(f'Installing custom profile {profile.name}...') +# +# install_session.add_additional_packages(profile.packages) +# install_session.enable_service(profile.services) +# +# profile.install(install_session) +# +# def info(self) -> Optional[ProfileInfo]: +# enabled_profiles = [p for p in self._current_selection if p.custom_enabled] +# if enabled_profiles: +# details = ', '.join([p.name for p in enabled_profiles]) +# gfx_driver = self.gfx_driver +# return ProfileInfo(self.name, details, gfx_driver) +# +# return None +# +# def reset(self): +# for profile in self._current_selection: +# profile.set_enabled(False) +# +# self.gfx_driver = None +# +# +# class CustomTypeProfile(Profile): +# def __init__( +# self, +# name: str, +# enabled: bool = False, +# packages: List[str] = [], +# services: List[str] = [] +# ): +# super().__init__( +# name, +# ProfileType.CustomType, +# packages=packages, +# services=services, +# support_gfx_driver=True +# ) +# +# self.custom_enabled = enabled +# +# def json(self) -> Dict[str, Any]: +# return { +# 'name': self.name, +# 'packages': self.packages, +# 'services': self.services, +# 'enabled': self.custom_enabled +# } diff --git a/archinstall/lib/profile/profiles_handler.py b/archinstall/lib/profile/profiles_handler.py index 063b12ea..a8b5cc22 100644 --- a/archinstall/lib/profile/profiles_handler.py +++ b/archinstall/lib/profile/profiles_handler.py @@ -71,28 +71,28 @@ class ProfileHandler: else: self._import_profile_from_url(url_path) - if custom := profile_config.get('custom', None): - from archinstall.default_profiles.custom import CustomTypeProfile - custom_types = [] - - for entry in custom: - custom_types.append( - CustomTypeProfile( - entry['name'], - entry['enabled'], - entry.get('packages', []), - entry.get('services', []) - ) - ) - - self.remove_custom_profiles(custom_types) - self.add_custom_profiles(custom_types) - - # this doesn't mean it's actual going to be set as a selection - # but we are simply populating the custom profile with all - # possible custom definitions - if custom_profile := self.get_profile_by_name('Custom'): - custom_profile.set_current_selection(custom_types) + # if custom := profile_config.get('custom', None): + # from archinstall.default_profiles.custom import CustomTypeProfile + # custom_types = [] + # + # for entry in custom: + # custom_types.append( + # CustomTypeProfile( + # entry['name'], + # entry['enabled'], + # entry.get('packages', []), + # entry.get('services', []) + # ) + # ) + # + # self.remove_custom_profiles(custom_types) + # self.add_custom_profiles(custom_types) + # + # # this doesn't mean it's actual going to be set as a selection + # # but we are simply populating the custom profile with all + # # possible custom definitions + # if custom_profile := self.get_profile_by_name('Custom'): + # custom_profile.set_current_selection(custom_types) if main := profile_config.get('main', None): profile = self.get_profile_by_name(main) if main else None diff --git a/mypy.ini b/mypy.ini index 0add1eb1..a08b2d88 100644 --- a/mypy.ini +++ b/mypy.ini @@ -10,6 +10,5 @@ exclude = (?x)(^archinstall/lib/disk/btrfs/btrfssubvolumeinfo\.py$ | ^archinstall/lib/installer\.py$ | ^archinstall/lib/systemd\.py$ | ^archinstall/lib/user_interaction/general_conf\.py$ - | ^archinstall/lib/user_interaction/locale_conf\.py$ - | ^archinstall/default_profiles/custom\.py$) + | ^archinstall/lib/user_interaction/locale_conf\.py$) files = archinstall/ -- cgit v1.2.3-70-g09d2 From 02af5fb6b2e134b78e45266b82c1689a96606c19 Mon Sep 17 00:00:00 2001 From: Daemon Coder <11915375+codefiles@users.noreply.github.com> Date: Thu, 27 Apr 2023 11:16:05 -0400 Subject: Fix README.md links (#1768) --- README.md | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 517621bc..720bd487 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ The installer also doubles as a python library to install Arch Linux and manage Or simply `git clone` the repo as it has no external dependencies *(but there are optional ones)*.
Or use `pip install --upgrade archinstall` to use as a library. -## Running the [guided](https://github.com/archlinux/archinstall/blob/master/examples/guided.py) installer +## Running the [guided](https://github.com/archlinux/archinstall/blob/master/archinstall/scripts/guided.py) installer Assuming you are on an Arch Linux live-ISO: @@ -86,7 +86,7 @@ When doing so, attach the `/var/log/archinstall/install.log` to the issue ticket # Mission Statement -Archinstall promises to ship a [guided installer](https://github.com/archlinux/archinstall/blob/master/examples/guided.py) that follows +Archinstall promises to ship a [guided installer](https://github.com/archlinux/archinstall/blob/master/archinstall/scripts/guided.py) that follows the [Arch Principles](https://wiki.archlinux.org/index.php/Arch_Linux#Principles) as well as a library to manage services, packages and other Arch Linux aspects. The guided installer will provide user-friendly options along the way, but the keyword here is options, they are optional and will never be forced upon anyone. @@ -178,33 +178,18 @@ For an example of a fully scripted, automated installation please see the exampl Archinstall comes with an [unattended](https://github.com/archlinux/archinstall/blob/master/examples/mac_address_installation.py) example which will look for a matching profile for the machine it is being run on, based on any local MAC address. For instance, if the machine the code is executed on has the MAC address `52:54:00:12:34:56` it will look for a profile called -[52-54-00-12-34-56.py](https://github.com/archlinux/archinstall/default_profiles/tailored.py). +[52-54-00-12-34-56.py](https://github.com/archlinux/archinstall/blob/master/archinstall/default_profiles/tailored.py). If it's found, the unattended installation will commence and source that profile as its installation procedure. # Profiles `archinstall` ships with a set of pre-defined profiles that can be chosen during the installation process. -| *Desktop* | *Server* | -|---------------|------------| -| Awesome | Cockpit | -| Bspwm | Docker | -| Budgie | Lighttpd | -| Cinnamon | Mariadb | -| Cutefish | Nginx | -| Deepin | Postgresql | -| Enlightenment | Tomcat | -| Gnome | httpd | -| Kde | sshd | -| Lxqt | | -| Mate | | -| Qtile | | -| Sway | | -| Xfce4 | | -| i3-wm | | +- [Desktop](https://github.com/archlinux/archinstall/tree/master/archinstall/default_profiles/desktops) +- [Server](https://github.com/archlinux/archinstall/tree/master/archinstall/default_profiles/servers) The definitions of the profiles and what packages they will install can be seen directly in the menu or -[default profiles](https://github.com/archlinux/archinstall/default_profiles) +[default profiles](https://github.com/archlinux/archinstall/tree/master/archinstall/default_profiles) # Testing -- cgit v1.2.3-70-g09d2 From e78ddb03e1bbc46e59fd6a9889699b12808d0fec Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Fri, 28 Apr 2023 22:18:48 +1000 Subject: Attempt to fetch partuuid multiple times (#1770) * Attempt to fetch partuuid multiple times * Update --------- Co-authored-by: Daniel Girtler --- archinstall/lib/disk/device_handler.py | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index 12cf18ea..ba325cda 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -341,16 +341,35 @@ class DeviceHandler(object): # the partition has a real path now as it was created part_mod.dev_path = Path(partition.path) - info = get_lsblk_info(part_mod.dev_path) - - if not info.partuuid: - raise DiskError(f'Unable to determine new partition uuid: {part_mod.dev_path}') + info = self._fetch_partuuid(part_mod.dev_path) part_mod.partuuid = info.partuuid part_mod.uuid = info.uuid except PartitionException as ex: raise DiskError(f'Unable to add partition, most likely due to overlapping sectors: {ex}') from ex + def _fetch_partuuid(self, path: Path) -> LsblkInfo: + attempts = 3 + info: Optional[LsblkInfo] = None + + self.partprobe(path) + for attempt_nr in range(attempts): + time.sleep(attempt_nr + 1) + info = get_lsblk_info(path) + + if info.partuuid: + break + + self.partprobe(path) + + if not info or not info.partuuid: + log(f'Unable to determine new partition uuid: {path}\n{info}', level=logging.DEBUG) + raise DiskError(f'Unable to determine new partition uuid: {path}') + + log(f'partuuid found: {info.json()}', level=logging.DEBUG) + + return info + def create_btrfs_volumes( self, part_mod: PartitionModification, @@ -555,12 +574,13 @@ class DeviceHandler(object): command = 'partprobe' try: + log(f'Calling partprobe: {command}', level=logging.DEBUG) result = SysCommand(command) + if result.exit_code != 0: - log(f'Error calling partprobe: {result.decode()}', level=logging.DEBUG) - raise DiskError(f'Could not perform partprobe on {path}: {result.decode()}') + log(f'"{command}" returned a failure: {result.decode()}', level=logging.DEBUG) except SysCallError as error: - log(f"partprobe experienced an error with {path}: {error}", level=logging.DEBUG) + log(f'"{command}" failed to run: {error}', level=logging.DEBUG) def _wipe(self, dev_path: Path): """ -- cgit v1.2.3-70-g09d2 From ec4ecbcb7a839ab06b739f01ce42bfd18376c620 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Thu, 4 May 2023 00:36:46 +1000 Subject: Full mypy compliance and small fixes (#1777) * Fix mypy compliance --------- Co-authored-by: Daniel Girtler --- .github/workflows/mypy.yaml | 2 +- archinstall/__init__.py | 3 +- archinstall/lib/disk/device_handler.py | 18 +- archinstall/lib/disk/device_model.py | 2 +- archinstall/lib/disk/fido.py | 9 +- archinstall/lib/general.py | 55 +++--- archinstall/lib/hardware.py | 55 +++--- archinstall/lib/installer.py | 239 +++++++++++------------ archinstall/lib/menu/abstract_menu.py | 4 +- archinstall/lib/menu/menu.py | 104 ++++++---- archinstall/lib/mirrors.py | 46 +++-- archinstall/lib/models/network_configuration.py | 78 ++++---- archinstall/lib/plugins.py | 66 ++++--- archinstall/lib/profile/profiles_handler.py | 16 +- archinstall/lib/systemd.py | 71 ++----- archinstall/lib/user_interaction/general_conf.py | 68 ++++--- archinstall/lib/user_interaction/locale_conf.py | 22 ++- archinstall/scripts/guided.py | 2 +- archinstall/scripts/swiss.py | 4 +- examples/interactive_installation.py | 4 +- mypy.ini | 14 -- pyproject.toml | 1 + 22 files changed, 454 insertions(+), 429 deletions(-) delete mode 100644 mypy.ini diff --git a/.github/workflows/mypy.yaml b/.github/workflows/mypy.yaml index 8689570f..e0db6f06 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 --config-file mypy.ini + run: mypy --config-file pyproject.toml diff --git a/archinstall/__init__.py b/archinstall/__init__.py index 3d0768a5..29b70b7a 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -233,7 +233,8 @@ def post_process_arguments(arguments): log(f"Warning: --debug mode will write certain credentials to {storage['LOG_PATH']}/{storage['LOG_FILE']}!", fg="red", level=logging.WARNING) if arguments.get('plugin', None): - load_plugin(arguments['plugin']) + path = arguments['plugin'] + load_plugin(path) load_config() diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index ba325cda..8f92cf3b 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -269,13 +269,13 @@ class DeviceHandler(object): # partition will be encrypted if enc_conf is not None and part_mod in enc_conf.partitions: self._perform_enc_formatting( - part_mod.real_dev_path, + part_mod.safe_dev_path, part_mod.mapper_name, part_mod.fs_type, enc_conf ) else: - self._perform_formatting(part_mod.fs_type, part_mod.real_dev_path) + self._perform_formatting(part_mod.fs_type, part_mod.safe_dev_path) def _perform_partitioning( self, @@ -287,11 +287,11 @@ class DeviceHandler(object): # when we require a delete and the partition to be (re)created # already exists then we have to delete it first if requires_delete and part_mod.status in [ModificationStatus.Modify, ModificationStatus.Delete]: - log(f'Delete existing partition: {part_mod.real_dev_path}', level=logging.INFO) - part_info = self.find_partition(part_mod.real_dev_path) + log(f'Delete existing partition: {part_mod.safe_dev_path}', level=logging.INFO) + part_info = self.find_partition(part_mod.safe_dev_path) if not part_info: - raise DiskError(f'No partition for dev path found: {part_mod.real_dev_path}') + raise DiskError(f'No partition for dev path found: {part_mod.safe_dev_path}') disk.deletePartition(part_info.partition) disk.commit() @@ -375,7 +375,7 @@ class DeviceHandler(object): part_mod: PartitionModification, enc_conf: Optional['DiskEncryption'] = None ): - log(f'Creating subvolumes: {part_mod.real_dev_path}', level=logging.INFO) + log(f'Creating subvolumes: {part_mod.safe_dev_path}', level=logging.INFO) luks_handler = None @@ -385,7 +385,7 @@ class DeviceHandler(object): raise ValueError('No device path specified for modification') luks_handler = self.unlock_luks2_dev( - part_mod.real_dev_path, + part_mod.safe_dev_path, part_mod.mapper_name, enc_conf.encryption_password ) @@ -395,7 +395,7 @@ class DeviceHandler(object): self.mount(luks_handler.mapper_dev, self._TMP_BTRFS_MOUNT, create_target_mountpoint=True) else: - self.mount(part_mod.real_dev_path, self._TMP_BTRFS_MOUNT, create_target_mountpoint=True) + self.mount(part_mod.safe_dev_path, self._TMP_BTRFS_MOUNT, create_target_mountpoint=True) for sub_vol in part_mod.btrfs_subvols: log(f'Creating subvolume: {sub_vol.name}', level=logging.DEBUG) @@ -419,7 +419,7 @@ class DeviceHandler(object): self.umount(luks_handler.mapper_dev) luks_handler.lock() else: - self.umount(part_mod.real_dev_path) + self.umount(part_mod.safe_dev_path) def unlock_luks2_dev(self, dev_path: Path, mapper_name: str, enc_password: str) -> Luks2: luks_handler = Luks2(dev_path, mapper_name=mapper_name, password=enc_password) diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index 0270a4dd..987a1e8a 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -603,7 +603,7 @@ class PartitionModification: return '' @property - def real_dev_path(self) -> Path: + def safe_dev_path(self) -> Path: if self.dev_path is None: raise ValueError('Device path was not set') return self.dev_path diff --git a/archinstall/lib/disk/fido.py b/archinstall/lib/disk/fido.py index 436be4d4..2a53b551 100644 --- a/archinstall/lib/disk/fido.py +++ b/archinstall/lib/disk/fido.py @@ -2,7 +2,8 @@ from __future__ import annotations import getpass import logging -from typing import List +from pathlib import Path +from typing import List, Optional from .device_model import PartitionModification, Fido2Device from ..general import SysCommand, SysCommandWorker, clear_vt100_escape_codes @@ -36,12 +37,12 @@ class Fido2: # to prevent continous reloading which will slow # down moving the cursor in the menu if not cls._loaded or reload: - ret = SysCommand(f"systemd-cryptenroll --fido2-device=list").decode('UTF-8') + ret: Optional[str] = SysCommand(f"systemd-cryptenroll --fido2-device=list").decode('UTF-8') if not ret: log('Unable to retrieve fido2 devices', level=logging.ERROR) return [] - fido_devices = clear_vt100_escape_codes(ret) + fido_devices: str = clear_vt100_escape_codes(ret) # type: ignore manufacturer_pos = 0 product_pos = 0 @@ -58,7 +59,7 @@ class Fido2: product = line[product_pos:] devices.append( - Fido2Device(path, manufacturer, product) + Fido2Device(Path(path), manufacturer, product) ) cls._loaded = True diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py index 57f13288..997b7d67 100644 --- a/archinstall/lib/general.py +++ b/archinstall/lib/general.py @@ -19,9 +19,15 @@ import pathlib from datetime import datetime, date from typing import Callable, Optional, Dict, Any, List, Union, Iterator, TYPE_CHECKING +from .exceptions import RequirementError, SysCallError +from .output import log +from .storage import storage + + if TYPE_CHECKING: from .installer import Installer + if sys.platform == 'linux': from select import epoll, EPOLLIN, EPOLLHUP else: @@ -53,30 +59,15 @@ else: except OSError: return [] -from .exceptions import RequirementError, SysCallError -from .output import log -from .storage import storage def gen_uid(entropy_length :int = 256) -> str: return hashlib.sha512(os.urandom(entropy_length)).hexdigest() + def generate_password(length :int = 64) -> str: haystack = string.printable # digits, ascii_letters, punctiation (!"#$[] etc) and whitespace return ''.join(secrets.choice(haystack) for i in range(length)) -def multisplit(s :str, splitters :List[str]) -> str: - s = [s, ] - for key in splitters: - ns = [] - for obj in s: - x = obj.split(key) - for index, part in enumerate(x): - if len(part): - ns.append(part) - if index < len(x) - 1: - ns.append(key) - s = ns - return s def locate_binary(name :str) -> str: for PATH in os.environ['PATH'].split(':'): @@ -88,20 +79,20 @@ def locate_binary(name :str) -> str: raise RequirementError(f"Binary {name} does not exist.") -def clear_vt100_escape_codes(data :Union[bytes, str]): + +def clear_vt100_escape_codes(data :Union[bytes, str]) -> Union[bytes, str]: # https://stackoverflow.com/a/43627833/929999 if type(data) == bytes: - vt100_escape_regex = bytes(r'\x1B\[[?0-9;]*[a-zA-Z]', 'UTF-8') - else: + byte_vt100_escape_regex = bytes(r'\x1B\[[?0-9;]*[a-zA-Z]', 'UTF-8') + data = re.sub(byte_vt100_escape_regex, b'', data) + elif type(data) == str: vt100_escape_regex = r'\x1B\[[?0-9;]*[a-zA-Z]' - - for match in re.findall(vt100_escape_regex, data, re.IGNORECASE): - data = data.replace(match, '' if type(data) == str else b'') + data = re.sub(vt100_escape_regex, '', data) + else: + raise ValueError(f'Unsupported data type: {type(data)}') return data -def json_dumps(*args :str, **kwargs :str) -> str: - return json.dumps(*args, **{**kwargs, 'cls': JSON}) class JsonEncoder: @staticmethod @@ -245,10 +236,12 @@ class SysCommandWorker: def __iter__(self, *args :str, **kwargs :Dict[str, Any]) -> Iterator[bytes]: for line in self._trace_log[self._trace_log_pos:self._trace_log.rfind(b'\n')].split(b'\n'): if line: + escaped_line: bytes = line + if self.remove_vt100_escape_codes_from_lines: - line = clear_vt100_escape_codes(line) + escaped_line = clear_vt100_escape_codes(line) # type: ignore - yield line + b'\n' + yield escaped_line + b'\n' self._trace_log_pos = self._trace_log.rfind(b'\n') @@ -279,7 +272,11 @@ class SysCommandWorker: log(args[1], level=logging.DEBUG, fg='red') if self.exit_code != 0: - raise SysCallError(f"{self.cmd} exited with abnormal exit code [{self.exit_code}]: {self._trace_log[-500:]}", self.exit_code, worker=self) + raise SysCallError( + f"{self.cmd} exited with abnormal exit code [{self.exit_code}]: {str(self._trace_log[-500:])}", + self.exit_code, + worker=self + ) def is_alive(self) -> bool: self.poll() @@ -328,7 +325,7 @@ class SysCommandWorker: change_perm = True with peak_logfile.open("a") as peek_output_log: - peek_output_log.write(output) + peek_output_log.write(str(output)) if change_perm: os.chmod(str(peak_logfile), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) @@ -497,7 +494,7 @@ class SysCommand: clears any printed output if ``.peek_output=True``. """ if self.session: - return self.session + return True with SysCommandWorker( self.cmd, diff --git a/archinstall/lib/hardware.py b/archinstall/lib/hardware.py index 9660ea95..3759725f 100644 --- a/archinstall/lib/hardware.py +++ b/archinstall/lib/hardware.py @@ -2,7 +2,7 @@ import os import logging from functools import partial from pathlib import Path -from typing import Iterator, Optional, Union +from typing import Iterator, Optional, Dict from .general import SysCommand from .networking import list_interfaces, enrich_iface_types @@ -61,15 +61,15 @@ AVAILABLE_GFX_DRIVERS = { "VMware / VirtualBox (open-source)": ["mesa", "xf86-video-vmware"], } -CPUINFO = Path("/proc/cpuinfo") -MEMINFO = Path("/proc/meminfo") - def cpuinfo() -> Iterator[dict[str, str]]: - """Yields information about the CPUs of the system.""" - cpu = {} + """ + Yields information about the CPUs of the system + """ + cpu_info_path = Path("/proc/cpuinfo") + cpu: Dict[str, str] = {} - with CPUINFO.open() as file: + with cpu_info_path.open() as file: for line in file: if not (line := line.strip()): yield cpu @@ -80,24 +80,31 @@ def cpuinfo() -> Iterator[dict[str, str]]: cpu[key.strip()] = value.strip() -def meminfo(key: Optional[str] = None) -> Union[dict[str, int], Optional[int]]: - """Returns a dict with memory info if called with no args +def all_meminfo() -> Dict[str, int]: + """ + Returns a dict with memory info if called with no args or the value of the given key of said dict. """ - with MEMINFO.open() as file: - mem_info = { - (columns := line.strip().split())[0].rstrip(':'): int(columns[1]) - for line in file - } + mem_info_path = Path("/proc/meminfo") + mem_info: Dict[str, int] = {} - if key is None: - return mem_info + with mem_info_path.open() as file: + for line in file: + key, value = line.strip().split(':') + num = value.split()[0] + mem_info[key] = int(num) + + return mem_info - return mem_info.get(key) + +def meminfo_for_key(key: str) -> int: + info = all_meminfo() + return info[key] def has_wifi() -> bool: - return 'WIRELESS' in enrich_iface_types(list_interfaces().values()).values() + ifaces = list(list_interfaces().values()) + return 'WIRELESS' in enrich_iface_types(ifaces).values() def has_cpu_vendor(vendor_id: str) -> bool: @@ -160,15 +167,15 @@ def product_name() -> Optional[str]: def mem_available() -> Optional[int]: - return meminfo('MemAvailable') + return meminfo_for_key('MemAvailable') def mem_free() -> Optional[int]: - return meminfo('MemFree') + return meminfo_for_key('MemFree') def mem_total() -> Optional[int]: - return meminfo('MemTotal') + return meminfo_for_key('MemTotal') def virtualization() -> Optional[str]: @@ -182,9 +189,9 @@ def virtualization() -> Optional[str]: def is_vm() -> bool: try: - return b"none" not in b"".join(SysCommand("systemd-detect-virt")).lower() + result = SysCommand("systemd-detect-virt") + return b"none" not in b"".join(result).lower() except SysCallError as error: log(f"System is not running in a VM: {error}", level=logging.DEBUG) - return None -# TODO: Add more identifiers + return False diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index ddbcc2f2..b6eaa797 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -7,7 +7,7 @@ import shutil import subprocess import time from pathlib import Path -from typing import Any, Iterator, List, Mapping, Optional, TYPE_CHECKING, Union, Dict +from typing import Any, List, Optional, TYPE_CHECKING, Union, Dict, Callable, Iterable from . import disk from .exceptions import DiskError, ServiceException, RequirementError, HardwareIncompatibilityError, SysCallError @@ -36,32 +36,6 @@ __packages__ = ["base", "base-devel", "linux-firmware", "linux", "linux-lts", "l __accessibility_packages__ = ["brltty", "espeakup", "alsa-utils"] -class InstallationFile: - def __init__(self, installation :'Installer', filename :str, owner :str, mode :str = "w"): - self.installation = installation - self.filename = filename - self.owner = owner - self.mode = mode - self.fh = None - - def __enter__(self) -> 'InstallationFile': - self.fh = open(self.filename, self.mode) - return self - - def __exit__(self, *args :str) -> None: - self.fh.close() - self.installation.chown(self.owner, self.filename) - - def write(self, data: Union[str, bytes]) -> int: - return self.fh.write(data) - - def read(self, *args) -> Union[str, bytes]: - return self.fh.read(*args) - -# def poll(self, *args) -> bool: -# return self.fh.poll(*args) - - def accessibility_tools_in_use() -> bool: return os.system('systemctl is-active --quiet espeakup.service') == 0 @@ -106,15 +80,17 @@ class Installer: self.kernels = kernels self._disk_config = disk_config - self._disk_encryption = disk_encryption - if self._disk_encryption is None: + if disk_encryption is None: self._disk_encryption = disk.DiskEncryption(disk.EncryptionType.NoEncryption) + else: + self._disk_encryption = disk_encryption + + self.target: Path = target - self.target = target self.init_time = time.strftime('%Y-%m-%d_%H-%M-%S') self.milliseconds = int(str(time.time()).split('.')[1]) - self.helper_flags = {'base': False, 'bootloader': False} + self.helper_flags: Dict[str, Any] = {'base': False, 'bootloader': None} self.base_packages = base_packages for kernel in self.kernels: @@ -124,31 +100,33 @@ class Installer: if accessibility_tools_in_use(): self.base_packages.extend(__accessibility_packages__) - self.post_base_install = [] + self.post_base_install: List[Callable] = [] # TODO: Figure out which one of these two we'll use.. But currently we're mixing them.. storage['session'] = self storage['installation_session'] = self - self.MODULES = [] - self.BINARIES = [] - self.FILES = [] + self.modules: List[str] = [] + self._binaries: List[str] = [] + self._files: List[str] = [] + # systemd, sd-vconsole and sd-encrypt will be replaced by udev, keymap and encrypt # if HSM is not used to encrypt the root volume. Check mkinitcpio() function for that override. - self.HOOKS = ["base", "systemd", "autodetect", "keyboard", "sd-vconsole", "modconf", "block", "filesystems", "fsck"] - self.KERNEL_PARAMS = [] - self.FSTAB_ENTRIES = [] + self._hooks: List[str] = [ + "base", "systemd", "autodetect", "keyboard", + "sd-vconsole", "modconf", "block", "filesystems", "fsck" + ] + self._kernel_params: List[str] = [] + self._fstab_entries: List[str] = [] self._zram_enabled = False - def __enter__(self, *args: str, **kwargs: str) -> 'Installer': + def __enter__(self) -> 'Installer': return self - def __exit__(self, *args :str, **kwargs :str) -> bool: - # TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager - - if len(args) >= 2 and args[1]: - self.log(args[1], level=logging.ERROR, fg='red') + def __exit__(self, exc_type, exc_val, exc_tb): + if exc_type is not None: + log(exc_val, fg='red', level=logging.ERROR) self.sync_log_to_install_medium() @@ -156,7 +134,7 @@ class Installer: # and then reboot, and a identical log file will be found in the ISO medium anyway. print(_("[!] A log file has been created here: {}").format(os.path.join(storage['LOG_PATH'], storage['LOG_FILE']))) print(_(" Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues")) - raise args[1] + raise exc_val if not (missing_steps := self.post_install_check()): self.log('Installation completed without any errors. You may now reboot.', fg='green', level=logging.INFO) @@ -164,6 +142,7 @@ class Installer: return True else: self.log('Some required steps were not successfully installed/configured before leaving the installer:', fg='red', level=logging.WARNING) + for step in missing_steps: self.log(f' - {step}', fg='red', level=logging.WARNING) @@ -247,31 +226,32 @@ class Installer: luks_handlers = {} for part_mod in partitions: - luks_handler = disk.device_handler.unlock_luks2_dev( - part_mod.dev_path, - part_mod.mapper_name, - self._disk_encryption.encryption_password - ) - luks_handlers[part_mod] = luks_handler + if part_mod.mapper_name and part_mod.dev_path: + luks_handler = disk.device_handler.unlock_luks2_dev( + part_mod.dev_path, + part_mod.mapper_name, + self._disk_encryption.encryption_password + ) + luks_handlers[part_mod] = luks_handler return luks_handlers def _mount_partition(self, part_mod: disk.PartitionModification): # it would be none if it's btrfs as the subvolumes will have the mountpoints defined - if part_mod.mountpoint is not None: + if part_mod.mountpoint and part_mod.dev_path: target = self.target / part_mod.relative_mountpoint disk.device_handler.mount(part_mod.dev_path, target, options=part_mod.mount_options) - if part_mod.fs_type == disk.FilesystemType.Btrfs: + if part_mod.fs_type == disk.FilesystemType.Btrfs and part_mod.dev_path: self._mount_btrfs_subvol(part_mod.dev_path, part_mod.btrfs_subvols) def _mount_luks_partiton(self, part_mod: disk.PartitionModification, luks_handler: Luks2): # it would be none if it's btrfs as the subvolumes will have the mountpoints defined - if part_mod.mountpoint is not None: + if part_mod.mountpoint and luks_handler.mapper_dev: target = self.target / part_mod.relative_mountpoint disk.device_handler.mount(luks_handler.mapper_dev, target, options=part_mod.mount_options) - if part_mod.fs_type == disk.FilesystemType.Btrfs: + if part_mod.fs_type == disk.FilesystemType.Btrfs and luks_handler.mapper_dev: self._mount_btrfs_subvol(luks_handler.mapper_dev, part_mod.btrfs_subvols) def _mount_btrfs_subvol(self, dev_path: Path, subvolumes: List[disk.SubvolumeModification]): @@ -346,15 +326,15 @@ class Installer: SysCommand(f'chmod 0600 {self.target}{file}') SysCommand(f'mkswap {self.target}{file}') - self.FSTAB_ENTRIES.append(f'{file} none swap defaults 0 0') + self._fstab_entries.append(f'{file} none swap defaults 0 0') if enable_resume: resume_uuid = SysCommand(f'findmnt -no UUID -T {self.target}{file}').decode('UTF-8').strip() resume_offset = SysCommand(f'/usr/bin/filefrag -v {self.target}{file}').decode('UTF-8').split('0:', 1)[1].split(":", 1)[1].split("..", 1)[0].strip() - self.HOOKS.append('resume') - self.KERNEL_PARAMS.append(f'resume=UUID={resume_uuid}') - self.KERNEL_PARAMS.append(f'resume_offset={resume_offset}') + self._hooks.append('resume') + self._kernel_params.append(f'resume=UUID={resume_uuid}') + self._kernel_params.append(f'resume_offset={resume_offset}') def post_install_check(self, *args :str, **kwargs :str) -> List[str]: return [step for step, flag in self.helper_flags.items() if flag is False] @@ -411,7 +391,7 @@ class Installer: else: pacman_conf.write(line) - def pacstrap(self, *packages: Union[str, List[str]], **kwargs :str) -> bool: + def _pacstrap(self, packages: Union[str, List[str]]) -> bool: if type(packages[0]) in (list, tuple): packages = packages[0] @@ -430,9 +410,9 @@ 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) + return self._pacstrap(packages) - raise RequirementError(f'Could not sync mirrors: {error}', level=logging.ERROR, fg="red") + raise RequirementError(f'Could not sync mirrors: {error}') try: SysCommand(f'/usr/bin/pacstrap -C /etc/pacman.conf -K {self.target} {" ".join(packages)} --noconfirm', peek_output=True) @@ -442,40 +422,44 @@ 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) + return self._pacstrap(packages) 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: + def set_mirrors(self, mirrors: Dict[str, Iterable[str]]): for plugin in plugins.values(): if hasattr(plugin, 'on_mirrors'): if result := plugin.on_mirrors(mirrors): mirrors = result - return use_mirrors(mirrors, destination=f'{self.target}/etc/pacman.d/mirrorlist') + destination = f'{self.target}/etc/pacman.d/mirrorlist' + use_mirrors(mirrors, destination=destination) def genfstab(self, flags :str = '-pU'): self.log(f"Updating {self.target}/etc/fstab", level=logging.INFO) try: - fstab = SysCommand(f'/usr/bin/genfstab {flags} {self.target}') + gen_fstab = SysCommand(f'/usr/bin/genfstab {flags} {self.target}').decode() except SysCallError as error: raise RequirementError(f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n Error: {error}') - with open(f"{self.target}/etc/fstab", 'a') as fstab_fh: - fstab_fh.write(fstab.decode()) + if not gen_fstab: + raise RequirementError(f'Genrating fstab returned empty value') + + with open(f"{self.target}/etc/fstab", 'a') as fp: + fp.write(gen_fstab) if not os.path.isfile(f'{self.target}/etc/fstab'): - raise RequirementError(f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n Error: {fstab}') + raise RequirementError(f'Could not create fstab file') for plugin in plugins.values(): if hasattr(plugin, 'on_genfstab'): if plugin.on_genfstab(self) is True: break - with open(f"{self.target}/etc/fstab", 'a') as fstab_fh: - for entry in self.FSTAB_ENTRIES: - fstab_fh.write(f'{entry}\n') + with open(f"{self.target}/etc/fstab", 'a') as fp: + for entry in self._fstab_entries: + fp.write(f'{entry}\n') for mod in self._disk_config.device_modifications: for part_mod in mod.partitions: @@ -583,7 +567,7 @@ class Installer: # fstrim is owned by util-linux, a dependency of both base and systemd. self.enable_service("fstrim.timer") - def enable_service(self, *services: Union[str, List[str]]) -> None: + def enable_service(self, services: Union[str, List[str]]) -> None: if type(services[0]) in (list, tuple): services = services[0] @@ -611,19 +595,7 @@ class Installer: subprocess.check_call(f"/usr/bin/arch-chroot {self.target}", shell=True) def configure_nic(self, network_config: NetworkConfiguration) -> None: - from .systemd import Networkd - - if network_config.dhcp: - conf = Networkd(Match={"Name": network_config.iface}, Network={"DHCP": "yes"}) - else: - network = {"Address": network_config.ip} - if network_config.gateway: - network["Gateway"] = network_config.gateway - if network_config.dns: - dns = network_config.dns - network["DNS"] = dns if isinstance(dns, list) else [dns] - - conf = Networkd(Match={"Name": network_config.iface}, Network=network) + conf = network_config.as_systemd_config() for plugin in plugins.values(): if hasattr(plugin, 'on_configure_nic'): @@ -663,7 +635,7 @@ class Installer: # Otherwise, we can go ahead and add the required package # and enable it's service: else: - self.pacstrap('iwd') + self._pacstrap('iwd') self.enable_service('iwd') for psk in psk_files: @@ -682,12 +654,12 @@ class Installer: if self.helper_flags.get('base', False) is False: def post_install_enable_networkd_resolved(*args :str, **kwargs :str): - self.enable_service('systemd-networkd', 'systemd-resolved') + self.enable_service(['systemd-networkd', 'systemd-resolved']) self.post_base_install.append(post_install_enable_networkd_resolved) # Otherwise, we can go ahead and enable the services else: - self.enable_service('systemd-networkd', 'systemd-resolved') + self.enable_service(['systemd-networkd', 'systemd-resolved']) return True @@ -704,9 +676,9 @@ class Installer: fh.write(f"KEYMAP={storage['arguments']['keyboard-layout']}\n") with open(f'{self.target}/etc/mkinitcpio.conf', 'w') as mkinit: - mkinit.write(f"MODULES=({' '.join(self.MODULES)})\n") - mkinit.write(f"BINARIES=({' '.join(self.BINARIES)})\n") - mkinit.write(f"FILES=({' '.join(self.FILES)})\n") + mkinit.write(f"MODULES=({' '.join(self.modules)})\n") + mkinit.write(f"BINARIES=({' '.join(self._binaries)})\n") + mkinit.write(f"FILES=({' '.join(self._files)})\n") if not self._disk_encryption.hsm_device: # For now, if we don't use HSM we revert to the old @@ -714,9 +686,9 @@ class Installer: # This is purely for stability reasons, we're going away from this. # * systemd -> udev # * sd-vconsole -> keymap - self.HOOKS = [hook.replace('systemd', 'udev').replace('sd-vconsole', 'keymap') for hook in self.HOOKS] + self._hooks = [hook.replace('systemd', 'udev').replace('sd-vconsole', 'keymap') for hook in self._hooks] - mkinit.write(f"HOOKS=({' '.join(self.HOOKS)})\n") + mkinit.write(f"HOOKS=({' '.join(self._hooks)})\n") try: SysCommand(f'/usr/bin/arch-chroot {self.target} mkinitcpio {" ".join(flags)}') @@ -736,25 +708,25 @@ class Installer: if (pkg := part.fs_type.installation_pkg) is not None: self.base_packages.append(pkg) if (module := part.fs_type.installation_module) is not None: - self.MODULES.append(module) + self.modules.append(module) if (binary := part.fs_type.installation_binary) is not None: - self.BINARIES.append(binary) + self._binaries.append(binary) # There is not yet an fsck tool for NTFS. If it's being used for the root filesystem, the hook should be removed. if part.fs_type.fs_type_mount == 'ntfs3' and part.mountpoint == self.target: - if 'fsck' in self.HOOKS: - self.HOOKS.remove('fsck') + if 'fsck' in self._hooks: + self._hooks.remove('fsck') if part in self._disk_encryption.partitions: if self._disk_encryption.hsm_device: # Required bby mkinitcpio to add support for fido2-device options - self.pacstrap('libfido2') + self._pacstrap('libfido2') - if 'sd-encrypt' not in self.HOOKS: - self.HOOKS.insert(self.HOOKS.index('filesystems'), 'sd-encrypt') + if 'sd-encrypt' not in self._hooks: + self._hooks.insert(self._hooks.index('filesystems'), 'sd-encrypt') else: - if 'encrypt' not in self.HOOKS: - self.HOOKS.insert(self.HOOKS.index('filesystems'), 'encrypt') + if 'encrypt' not in self._hooks: + self._hooks.insert(self._hooks.index('filesystems'), 'encrypt') if not has_uefi(): self.base_packages.append('grub') @@ -786,7 +758,7 @@ class Installer: else: self.log("The testing flag is not set. This system will be installed without testing repositories enabled.") - self.pacstrap(self.base_packages) + self._pacstrap(self.base_packages) self.helper_flags['base-strapped'] = True # This handles making sure that the repositories we enabled persist on the installed system @@ -826,7 +798,7 @@ class Installer: def setup_swap(self, kind :str = 'zram'): if kind == 'zram': self.log(f"Setting up swap on zram") - self.pacstrap('zram-generator') + self._pacstrap('zram-generator') # We could use the default example below, but maybe not the best idea: https://github.com/archlinux/archinstall/pull/678#issuecomment-962124813 # zram_example_location = '/usr/share/doc/zram-generator/zram-generator.conf.example' @@ -853,7 +825,7 @@ class Installer: return None def _add_systemd_bootloader(self, root_partition: disk.PartitionModification): - self.pacstrap('efibootmgr') + self._pacstrap('efibootmgr') if not has_uefi(): raise HardwareIncompatibilityError @@ -919,7 +891,7 @@ class Installer: # blkid doesn't trigger on loopback devices really well, # so we'll use the old manual method until we get that sorted out. - options_entry = f'rw rootfstype={root_partition.fs_type.fs_type_mount} {" ".join(self.KERNEL_PARAMS)}\n' + options_entry = f'rw rootfstype={root_partition.fs_type.fs_type_mount} {" ".join(self._kernel_params)}\n' for sub_vol in root_partition.btrfs_subvols: if sub_vol.is_root(): @@ -958,7 +930,7 @@ class Installer: boot_partition: disk.PartitionModification, root_partition: disk.PartitionModification ): - self.pacstrap('grub') # no need? + self._pacstrap('grub') # no need? _file = "/etc/default/grub" @@ -977,7 +949,7 @@ class Installer: log(f"GRUB boot partition: {boot_partition.dev_path}", level=logging.INFO) if has_uefi(): - self.pacstrap('efibootmgr') # TODO: Do we need? Yes, but remove from minimal_installation() instead? + self._pacstrap('efibootmgr') # TODO: Do we need? Yes, but remove from minimal_installation() instead? try: SysCommand(f'/usr/bin/arch-chroot {self.target} grub-install --debug --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB --removable', peek_output=True) @@ -987,8 +959,20 @@ class Installer: except SysCallError as error: raise DiskError(f"Could not install GRUB to {self.target}/boot: {error}") else: + device = disk.device_handler.get_device_by_partition_path(boot_partition.safe_dev_path) + + if not device: + raise ValueError(f'Can not find block device: {boot_partition.safe_dev_path}') + try: - SysCommand(f'/usr/bin/arch-chroot {self.target} grub-install --debug --target=i386-pc --recheck {boot_partition.parent}', peek_output=True) + cmd = f'/usr/bin/arch-chroot' \ + f' {self.target}' \ + f' grub-install' \ + f' --debug' \ + f' --target=i386-pc' \ + f' --recheck {device.device_info.path}' + + SysCommand(cmd, peek_output=True) except SysCallError as error: raise DiskError(f"Failed to install GRUB boot on {boot_partition.dev_path}: {error}") @@ -1004,7 +988,7 @@ class Installer: boot_partition: disk.PartitionModification, root_partition: disk.PartitionModification ): - self.pacstrap('efibootmgr') + self._pacstrap('efibootmgr') if not has_uefi(): raise HardwareIncompatibilityError @@ -1038,17 +1022,30 @@ class Installer: # TODO: We need to detect if the encrypted device is a whole disk encryption, # or simply a partition encryption. Right now we assume it's a partition (and we always have) log(f'Identifying root partition by PARTUUID: {root_partition.partuuid}', level=logging.DEBUG) - kernel_parameters.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:luksdev root=/dev/mapper/luksdev rw rootfstype={root_partition.fs_type.value} {" ".join(self.KERNEL_PARAMS)}') + kernel_parameters.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:luksdev root=/dev/mapper/luksdev rw rootfstype={root_partition.fs_type.value} {" ".join(self._kernel_params)}') else: log(f'Root partition is an encrypted device identifying by PARTUUID: {root_partition.partuuid}', level=logging.DEBUG) - kernel_parameters.append(f'root=PARTUUID={root_partition.partuuid} rw rootfstype={root_partition.fs_type.value} {" ".join(self.KERNEL_PARAMS)}') + kernel_parameters.append(f'root=PARTUUID={root_partition.partuuid} rw rootfstype={root_partition.fs_type.value} {" ".join(self._kernel_params)}') + + device = disk.device_handler.get_device_by_partition_path(boot_partition.safe_dev_path) - device = disk.device_handler.get_device_by_partition_path(boot_partition.dev_path) - SysCommand(f'efibootmgr --disk {device.path} --part {device.path} --create --label "{label}" --loader {loader} --unicode \'{" ".join(kernel_parameters)}\' --verbose') + if not device: + raise ValueError(f'Unable to find block device: {boot_partition.safe_dev_path}') + + cmd = f'efibootmgr ' \ + f'--disk {device.device_info.path} ' \ + f'--part {boot_partition.safe_dev_path} ' \ + f'--create ' \ + f'--label "{label}" ' \ + f'--loader {loader} ' \ + f'--unicode \'{" ".join(kernel_parameters)}\' ' \ + f'--verbose' + + SysCommand(cmd) self.helper_flags['bootloader'] = "efistub" - def add_bootloader(self, bootloader: Bootloader) -> bool: + def add_bootloader(self, bootloader: Bootloader): """ Adds a bootloader to the installation instance. Archinstall supports one of three types: @@ -1056,8 +1053,7 @@ class Installer: * grub * efistub (beta) - :param bootloader: Can be one of the three strings - 'systemd-bootctl', 'grub' or 'efistub' (beta) + :param bootloader: Type of bootloader to be added """ for plugin in plugins.values(): @@ -1089,8 +1085,8 @@ class Installer: case Bootloader.Efistub: self._add_efistub_bootloader(boot_partition, root_partition) - def add_additional_packages(self, *packages: Union[str, List[str]]) -> bool: - return self.pacstrap(*packages) + def add_additional_packages(self, packages: Union[str, List[str]]) -> bool: + return self._pacstrap(packages) def _enable_users(self, service: str, users: List[User]): for user in users: @@ -1201,9 +1197,6 @@ class Installer: except SysCallError: return False - def create_file(self, filename :str, owner :Optional[str] = None) -> InstallationFile: - return InstallationFile(self, filename, owner) - def set_keyboard_language(self, language: str) -> bool: log(f"Setting keyboard language to {language}", level=logging.INFO) if len(language.strip()): diff --git a/archinstall/lib/menu/abstract_menu.py b/archinstall/lib/menu/abstract_menu.py index 53816655..e44d65a4 100644 --- a/archinstall/lib/menu/abstract_menu.py +++ b/archinstall/lib/menu/abstract_menu.py @@ -482,9 +482,9 @@ class AbstractMenu: if item in self._menus_to_enable(): yield item - def _select_archinstall_language(self, preset_value: Language) -> Language: + def _select_archinstall_language(self, preset: Language) -> Language: from ..user_interaction.general_conf import select_archinstall_language - language = select_archinstall_language(self.translation_handler.translated_languages, preset_value) + language = select_archinstall_language(self.translation_handler.translated_languages, preset) self._translation_handler.activate(language) return language diff --git a/archinstall/lib/menu/menu.py b/archinstall/lib/menu/menu.py index 44ac33a6..12dbf1f5 100644 --- a/archinstall/lib/menu/menu.py +++ b/archinstall/lib/menu/menu.py @@ -3,7 +3,7 @@ from enum import Enum, auto from os import system from typing import Dict, List, Union, Any, TYPE_CHECKING, Optional, Callable -from simple_term_menu import TerminalMenu +from simple_term_menu import TerminalMenu # type: ignore from ..exceptions import RequirementError from ..output import log @@ -29,11 +29,11 @@ class MenuSelection: @property def single_value(self) -> Any: - return self.value + return self.value # type: ignore @property def multi_value(self) -> List[Any]: - return self.value + return self.value # type: ignore class Menu(TerminalMenu): @@ -67,7 +67,7 @@ class Menu(TerminalMenu): preview_command: Optional[Callable] = None, preview_size: float = 0.0, preview_title: str = 'Info', - header: Union[List[str],str] = None, + header: Union[List[str], str] = [], allow_reset: bool = False, allow_reset_warning_msg: Optional[str] = None, clear_screen: bool = True, @@ -141,8 +141,6 @@ 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: @@ -193,8 +191,7 @@ class Menu(TerminalMenu): 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} {self._default_str}' - self._menu_options = [default] + [o for o in self._menu_options if default_option != o] + self._menu_options = [self._default_menu_value] + [o for o in self._menu_options if default_option != o] if display_back_option and not multi and skip: skip_empty_entries = True @@ -204,7 +201,18 @@ class Menu(TerminalMenu): skip_empty_entries = True self._menu_options += [''] - self._preselection(preset_values,cursor_index) + preset_list: Optional[List[str]] = None + + if preset_values and isinstance(preset_values, str): + preset_list = [preset_values] + + calc_cursor_idx = self._determine_cursor_pos(preset_list, cursor_index) + + # when we're not in multi selection mode we don't care about + # passing the pre-selection list to the menu as the position + # of the cursor is the one determining the pre-selection + if not self._multi: + preset_values = None cursor = "> " main_menu_cursor_style = ("fg_cyan", "bold") @@ -217,8 +225,8 @@ class Menu(TerminalMenu): menu_cursor_style=main_menu_cursor_style, menu_highlight_style=main_menu_style, multi_select=multi, - preselected_entries=self.preset_values, - cursor_index=self.cursor_index, + preselected_entries=preset_values, + cursor_index=calc_cursor_idx, preview_command=lambda x: self._show_preview(preview_command, x), preview_size=preview_size, preview_title=preview_title, @@ -231,12 +239,17 @@ class Menu(TerminalMenu): skip_empty_entries=skip_empty_entries ) + @property + def _default_menu_value(self) -> str: + default_str = str(_('(default)')) + return f'{self._default_option} {default_str}' + def _show_preview(self, preview_command: Optional[Callable], selection: str) -> Optional[str]: if selection == self.back(): return None if preview_command: - if self._default_option is not None and f'{self._default_option} {self._default_str}' == selection: + if self._default_option is not None and self._default_menu_value == selection: selection = self._default_option return preview_command(selection) @@ -249,7 +262,7 @@ class Menu(TerminalMenu): return MenuSelection(type_=MenuSelectionType.Reset) def check_default(elem): - if self._default_option is not None and f'{self._default_option} {self._default_str}' in elem: + if self._default_option is not None and self._default_menu_value in elem: return self._default_option else: return elem @@ -297,31 +310,44 @@ class Menu(TerminalMenu): pos = self._menu_entries.index(value) self.set_cursor_pos(pos) - 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 + def _determine_cursor_pos( + self, + preset: Optional[List[str]] = None, + cursor_index: Optional[int] = None + ) -> Optional[int]: + """ + The priority order to determine the cursor position is: + 1. A static cursor position was provided + 2. Preset values have been provided so the cursor will be + positioned on those + 3. A default value for a selection is given so the cursor + will be placed on such + """ + if cursor_index: + return cursor_index + + if preset: + indexes = [] + + for p in preset: try: - if isinstance(preset_values,str): - 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]) - except ValueError: - self.cursor_index = 0 - - self.cursor_index = cursor_index - if not preset_values: - self.preset_values = None - return - - self.preset_values = preset_values + # the options of the table selection menu + # are already escaped so we have to escape + # the preset values as well for the comparison + if '|' in p: + p = p.replace('|', '\\|') + + idx = self._menu_options.index(p) + indexes.append(idx) + except (IndexError, ValueError): + log(f'Error finding index of {p}: {self._menu_options}', level=logging.DEBUG) + + if len(indexes) == 0: + indexes.append(0) + + return indexes[0] + 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 - self.preset_values = None + return self._menu_options.index(self._default_menu_value) + + return None diff --git a/archinstall/lib/mirrors.py b/archinstall/lib/mirrors.py index 4bae6d8b..15d0fd6b 100644 --- a/archinstall/lib/mirrors.py +++ b/archinstall/lib/mirrors.py @@ -3,12 +3,22 @@ import pathlib import urllib.error import urllib.request from typing import Union, Iterable, Dict, Any, List +from dataclasses import dataclass from .general import SysCommand from .output import log from .storage import storage -def sort_mirrorlist(raw_data :bytes, sort_order=["https", "http"]) -> bytes: + +@dataclass +class CustomMirror: + url: str + signcheck: str + signoptions: str + name: str + + +def sort_mirrorlist(raw_data :bytes, sort_order: List[str] = ['https', 'http']) -> bytes: """ This function can sort /etc/pacman.d/mirrorlist according to the mirror's URL prefix. By default places HTTPS before HTTP but it also @@ -28,8 +38,9 @@ def sort_mirrorlist(raw_data :bytes, sort_order=["https", "http"]) -> bytes: from server url definitions (commented or uncommented). """ comments_and_whitespaces = b"" + sort_order += ['Unknown'] + categories: Dict[str, List] = {key: [] for key in sort_order} - categories = {key: [] for key in sort_order + ["Unknown"]} for line in raw_data.split(b"\n"): if line[0:2] in (b'##', b''): comments_and_whitespaces += line + b'\n' @@ -82,18 +93,18 @@ def filter_mirrors_by_region(regions :str, return new_list.decode('UTF-8') -def add_custom_mirrors(mirrors: List[str], *args :str, **kwargs :str) -> bool: +def add_custom_mirrors(mirrors: List[CustomMirror]) -> bool: """ This will append custom mirror definitions in pacman.conf - :param mirrors: A list of mirror data according to: `{'url': 'http://url.com', 'signcheck': 'Optional', 'signoptions': 'TrustAll', 'name': 'testmirror'}` - :type mirrors: dict + :param mirrors: A list of custom mirrors + :type mirrors: List[CustomMirror] """ with open('/etc/pacman.conf', 'a') as pacman: for mirror in mirrors: - pacman.write(f"[{mirror['name']}]\n") - pacman.write(f"SigLevel = {mirror['signcheck']} {mirror['signoptions']}\n") - pacman.write(f"Server = {mirror['url']}\n") + pacman.write(f"[{mirror.name}]\n") + pacman.write(f"SigLevel = {mirror.signcheck} {mirror.signoptions}\n") + pacman.write(f"Server = {mirror.url}\n") return True @@ -123,7 +134,7 @@ def insert_mirrors(mirrors :Dict[str, Any], *args :str, **kwargs :str) -> bool: def use_mirrors( regions: Dict[str, Iterable[str]], destination: str = '/etc/pacman.d/mirrorlist' -) -> None: +): log(f'A new package mirror-list has been created: {destination}', level=logging.INFO) with open(destination, 'w') as mirrorlist: for region, mirrors in regions.items(): @@ -146,7 +157,7 @@ def re_rank_mirrors( def list_mirrors(sort_order :List[str] = ["https", "http"]) -> Dict[str, Any]: - regions = {} + regions: Dict[str, Dict[str, Any]] = {} if storage['arguments']['offline']: with pathlib.Path('/etc/pacman.d/mirrorlist').open('rb') as fh: @@ -170,18 +181,19 @@ def list_mirrors(sort_order :List[str] = ["https", "http"]) -> Dict[str, Any]: if len(line.strip()) == 0: continue - line = line.decode('UTF-8').strip('\n').strip('\r') - if line[:3] == '## ': - region = line[3:] - elif line[:10] == '#Server = ': + clean_line = line.decode('UTF-8').strip('\n').strip('\r') + + if clean_line[:3] == '## ': + region = clean_line[3:] + elif clean_line[:10] == '#Server = ': regions.setdefault(region, {}) - url = line.lstrip('#Server = ') + url = clean_line.lstrip('#Server = ') regions[region][url] = True - elif line.startswith('Server = '): + elif clean_line.startswith('Server = '): regions.setdefault(region, {}) - url = line.lstrip('Server = ') + url = clean_line.lstrip('Server = ') regions[region][url] = True return regions diff --git a/archinstall/lib/models/network_configuration.py b/archinstall/lib/models/network_configuration.py index b7ab690d..66230e24 100644 --- a/archinstall/lib/models/network_configuration.py +++ b/archinstall/lib/models/network_configuration.py @@ -1,8 +1,9 @@ from __future__ import annotations -from dataclasses import dataclass +import logging +from dataclasses import dataclass, field from enum import Enum -from typing import List, Optional, Dict, Union, Any, TYPE_CHECKING +from typing import List, Optional, Dict, Union, Any, TYPE_CHECKING, Tuple from ..output import log from ..storage import storage @@ -24,7 +25,7 @@ class NetworkConfiguration: ip: Optional[str] = None dhcp: bool = True gateway: Optional[str] = None - dns: Union[None, List[str]] = None + dns: List[str] = field(default_factory=list) def __str__(self): if self.is_iso(): @@ -53,6 +54,33 @@ class NetworkConfiguration: return data + def as_systemd_config(self) -> str: + match: List[Tuple[str, str]] = [] + network: List[Tuple[str, str]] = [] + + if self.iface: + match.append(('Name', self.iface)) + + if self.dhcp: + network.append(('DHCP', 'yes')) + else: + if self.ip: + network.append(('Address', self.ip)) + if self.gateway: + network.append(('Gateway', self.gateway)) + for dns in self.dns: + network.append(('DNS', dns)) + + config = {'Match': match, 'Network': network} + + config_str = '' + for top, entries in config.items(): + config_str += f'[{top}]\n' + config_str += '\n'.join([f'{k}={v}' for k, v in entries]) + config_str += '\n\n' + + return config_str + def json(self) -> Dict: # for json serialization when calling json.dumps(...) on this class return self.__dict__ @@ -90,41 +118,14 @@ class NetworkConfigurationHandler: # Perform a copy of the config if self._configuration.is_iso(): installation.copy_iso_network_config( - enable_services=True) # Sources the ISO network configuration to the install medium. + enable_services=True # Sources the ISO network configuration to the install medium. + ) elif self._configuration.is_network_manager(): installation.add_additional_packages(["networkmanager"]) if (profile := storage['arguments'].get('profile_config')) and profile.is_desktop_type_profile: installation.add_additional_packages(["network-manager-applet"]) installation.enable_service('NetworkManager.service') - def _backwards_compability_config(self, config: Union[str,Dict[str, str]]) -> Union[List[NetworkConfiguration], NetworkConfiguration, None]: - def get(config: Dict[str, str], key: str) -> List[str]: - if (value := config.get(key, None)) is not None: - return [value] - return [] - - if isinstance(config, str): # is a ISO network - return NetworkConfiguration(NicType.ISO) - elif config.get('NetworkManager'): # is a network manager configuration - return NetworkConfiguration(NicType.NM) - elif 'ip' in config: - return [NetworkConfiguration( - NicType.MANUAL, - iface=config.get('nic', ''), - ip=config.get('ip'), - gateway=config.get('gateway', ''), - dns=get(config, 'dns'), - dhcp=False - )] - elif 'nic' in config: - return [NetworkConfiguration( - NicType.MANUAL, - iface=config.get('nic', ''), - dhcp=True - )] - else: # not recognized - return None - def _parse_manual_config(self, configs: List[Dict[str, Any]]) -> Optional[List[NetworkConfiguration]]: configurations = [] @@ -145,13 +146,17 @@ class NetworkConfigurationHandler: log(_('Manual nic configuration with no auto DHCP requires an IP address'), fg='red') exit(1) + dns = manual_config.get('dns', []) + if not isinstance(dns, list): + dns = [dns] + configurations.append( NetworkConfiguration( NicType.MANUAL, iface=iface, ip=ip, gateway=manual_config.get('gateway', ''), - dns=manual_config.get('dns', []), + dns=dns, dhcp=False ) ) @@ -176,8 +181,5 @@ class NetworkConfigurationHandler: self._configuration = NetworkConfiguration(type_) else: # manual configuration settings self._configuration = self._parse_manual_config([config]) - else: # old style definitions - network_config = self._backwards_compability_config(config) - if network_config: - return network_config - return None + else: + log(f'Unable to parse network configuration: {config}', level=logging.DEBUG) diff --git a/archinstall/lib/plugins.py b/archinstall/lib/plugins.py index 0ff63610..b1ece04f 100644 --- a/archinstall/lib/plugins.py +++ b/archinstall/lib/plugins.py @@ -3,77 +3,86 @@ import importlib import logging import os import sys -import pathlib import urllib.parse import urllib.request from importlib import metadata +from pathlib import Path from typing import Optional, List -from types import ModuleType from .output import log from .storage import storage plugins = {} + # 1: List archinstall.plugin definitions # 2: Load the plugin entrypoint # 3: Initiate the plugin and store it as .name in plugins for plugin_definition in metadata.entry_points().select(group='archinstall.plugin'): plugin_entrypoint = plugin_definition.load() + try: plugins[plugin_definition.name] = plugin_entrypoint() except Exception as err: - log(err, level=logging.ERROR) + log(f'Error: {err}', level=logging.ERROR) log(f"The above error was detected when loading the plugin: {plugin_definition}", fg="red", level=logging.ERROR) -# The following functions and core are support structures for load_plugin() -def localize_path(profile_path :str) -> str: - if (url := urllib.parse.urlparse(profile_path)).scheme and url.scheme in ('https', 'http'): - converted_path = f"/tmp/{os.path.basename(profile_path).replace('.py', '')}_{hashlib.md5(os.urandom(12)).hexdigest()}.py" +def localize_path(path: Path) -> Path: + """ + Support structures for load_plugin() + """ + url = urllib.parse.urlparse(str(path)) + + if url.scheme and url.scheme in ('https', 'http'): + converted_path = Path(f'/tmp/{path.stem}_{hashlib.md5(os.urandom(12)).hexdigest()}.py') with open(converted_path, "w") as temp_file: temp_file.write(urllib.request.urlopen(url.geturl()).read().decode('utf-8')) return converted_path else: - return profile_path + return path -def import_via_path(path :str, namespace :Optional[str] = None) -> ModuleType: +def import_via_path(path: Path, namespace: Optional[str] = None) -> Optional[str]: if not namespace: namespace = os.path.basename(path) if namespace == '__init__.py': - path = pathlib.PurePath(path) namespace = path.parent.name try: spec = importlib.util.spec_from_file_location(namespace, path) - imported = importlib.util.module_from_spec(spec) - sys.modules[namespace] = imported - spec.loader.exec_module(sys.modules[namespace]) + if spec and spec.loader: + imported = importlib.util.module_from_spec(spec) + sys.modules[namespace] = imported + spec.loader.exec_module(sys.modules[namespace]) return namespace except Exception as err: - log(err, level=logging.ERROR) + log(f'Error: {err}', level=logging.ERROR) log(f"The above error was detected when loading the plugin: {path}", fg="red", level=logging.ERROR) try: - del(sys.modules[namespace]) # noqa: E275 - except: + del sys.modules[namespace] + except Exception: pass -def find_nth(haystack :List[str], needle :str, n :int) -> int: - start = haystack.find(needle) - while start >= 0 and n > 1: - start = haystack.find(needle, start + len(needle)) - n -= 1 - return start + return namespace + + +def find_nth(haystack: List[str], needle: str, n: int) -> Optional[int]: + indices = [idx for idx, elem in enumerate(haystack) if elem == needle] + if n <= len(indices): + return indices[n - 1] + return None + -def load_plugin(path :str) -> ModuleType: - parsed_url = urllib.parse.urlparse(path) - log(f"Loading plugin {parsed_url}.", fg="gray", level=logging.INFO) +def load_plugin(path: Path): + namespace: Optional[str] = None + parsed_url = urllib.parse.urlparse(str(path)) + log(f"Loading plugin from url {parsed_url}.", level=logging.INFO) # The Profile was not a direct match on a remote URL if not parsed_url.scheme: @@ -81,9 +90,10 @@ def load_plugin(path :str) -> ModuleType: if os.path.isfile(path): namespace = import_via_path(path) elif parsed_url.scheme in ('https', 'http'): - namespace = import_via_path(localize_path(path)) + localized = localize_path(path) + namespace = import_via_path(localized) - if namespace in sys.modules: + if namespace and namespace in sys.modules: # Version dependency via __archinstall__version__ variable (if present) in the plugin # Any errors in version inconsistency will be handled through normal error handling if not defined. if hasattr(sys.modules[namespace], '__archinstall__version__'): @@ -99,7 +109,7 @@ def load_plugin(path :str) -> ModuleType: plugins[namespace] = sys.modules[namespace].Plugin() log(f"Plugin {plugins[namespace]} has been loaded.", fg="gray", level=logging.INFO) except Exception as err: - log(err, level=logging.ERROR) + log(f'Error: {err}', level=logging.ERROR) log(f"The above error was detected when initiating the plugin: {path}", fg="red", level=logging.ERROR) else: log(f"Plugin '{path}' is missing a valid entry-point or is corrupt.", fg="yellow", level=logging.WARNING) diff --git a/archinstall/lib/profile/profiles_handler.py b/archinstall/lib/profile/profiles_handler.py index a8b5cc22..824849c3 100644 --- a/archinstall/lib/profile/profiles_handler.py +++ b/archinstall/lib/profile/profiles_handler.py @@ -194,23 +194,23 @@ class ProfileHandler: install_session.add_additional_packages(f"{kernel}-headers") # I've had kernel regen fail if it wasn't installed before nvidia-dkms - install_session.add_additional_packages("dkms xorg-server xorg-xinit nvidia-dkms") + install_session.add_additional_packages(['dkms', 'xorg-server', 'xorg-xinit', 'nvidia-dkms']) return elif 'amdgpu' in driver_pkgs: # The order of these two are important if amdgpu is installed #808 - if 'amdgpu' in install_session.MODULES: - install_session.MODULES.remove('amdgpu') - install_session.MODULES.append('amdgpu') + if 'amdgpu' in install_session.modules: + install_session.modules.remove('amdgpu') + install_session.modules.append('amdgpu') - if 'radeon' in install_session.MODULES: - install_session.MODULES.remove('radeon') - install_session.MODULES.append('radeon') + if 'radeon' in install_session.modules: + install_session.modules.remove('radeon') + install_session.modules.append('radeon') install_session.add_additional_packages(additional_pkg) except Exception as err: log(f"Could not handle nvidia and linuz-zen specific situations during xorg installation: {err}", level=logging.WARNING, fg="yellow") # Prep didn't run, so there's no driver to install - install_session.add_additional_packages("xorg-server xorg-xinit") + install_session.add_additional_packages(['xorg-server', 'xorg-xinit']) def install_profile_config(self, install_session: 'Installer', profile_config: ProfileConfiguration): profile = profile_config.profile diff --git a/archinstall/lib/systemd.py b/archinstall/lib/systemd.py index 64ffcae4..6ccbc5f6 100644 --- a/archinstall/lib/systemd.py +++ b/archinstall/lib/systemd.py @@ -1,6 +1,6 @@ import logging import time -from typing import Iterator +from typing import Iterator, Optional from .exceptions import SysCallError from .general import SysCommand, SysCommandWorker, locate_binary from .installer import Installer @@ -8,51 +8,11 @@ from .output import log from .storage import storage -class Ini: - def __init__(self, *args :str, **kwargs :str): - """ - Limited INI handler for now. - Supports multiple keywords through dictionary list items. - """ - self.kwargs = kwargs - - def __str__(self) -> str: - result = '' - first_row_done = False - for top_level in self.kwargs: - if first_row_done: - result += f"\n[{top_level}]\n" - else: - result += f"[{top_level}]\n" - first_row_done = True - - for key, val in self.kwargs[top_level].items(): - if type(val) == list: - for item in val: - result += f"{key}={item}\n" - else: - result += f"{key}={val}\n" - - return result - - -class Systemd(Ini): - """ - Placeholder class to do systemd specific setups. - """ - - -class Networkd(Systemd): - """ - Placeholder class to do systemd-network specific setups. - """ - - class Boot: def __init__(self, installation: Installer): self.instance = installation self.container_name = 'archinstall' - self.session = None + self.session: Optional[SysCommandWorker] = None self.ready = False def __enter__(self) -> 'Boot': @@ -63,17 +23,18 @@ class Boot: self.session = existing_session.session self.ready = existing_session.ready else: + # '-P' or --console=pipe could help us not having to do a bunch + # of os.write() calls, but instead use pipes (stdin, stdout and stderr) as usual. self.session = SysCommandWorker([ '/usr/bin/systemd-nspawn', - '-D', self.instance.target, + '-D', str(self.instance.target), '--timezone=off', '-b', '--no-pager', '--machine', self.container_name ]) - # '-P' or --console=pipe could help us not having to do a bunch of os.write() calls, but instead use pipes (stdin, stdout and stderr) as usual. - if not self.ready: + if not self.ready and self.session: while self.session.is_alive(): if b' login:' in self.session: self.ready = True @@ -91,25 +52,31 @@ class Boot: log(f"The error above occurred in a temporary boot-up of the installation {self.instance}", level=logging.ERROR, fg="red") shutdown = None - shutdown_exit_code = -1 + shutdown_exit_code: Optional[int] = -1 try: shutdown = SysCommand(f'systemd-run --machine={self.container_name} --pty shutdown now') except SysCallError as error: shutdown_exit_code = error.exit_code - while self.session.is_alive(): - time.sleep(0.25) + if self.session: + while self.session.is_alive(): + time.sleep(0.25) - if shutdown: + if shutdown and shutdown.exit_code: shutdown_exit_code = shutdown.exit_code - if self.session.exit_code == 0 or shutdown_exit_code == 0: + if self.session and (self.session.exit_code == 0 or shutdown_exit_code == 0): storage['active_boot'] = None else: - raise SysCallError(f"Could not shut down temporary boot of {self.instance}: {self.session.exit_code}/{shutdown_exit_code}", exit_code=next(filter(bool, [self.session.exit_code, shutdown_exit_code]))) + session_exit_code = self.session.exit_code if self.session else -1 + + raise SysCallError( + f"Could not shut down temporary boot of {self.instance}: {session_exit_code}/{shutdown_exit_code}", + exit_code=next(filter(bool, [session_exit_code, shutdown_exit_code])) + ) - def __iter__(self) -> Iterator[str]: + def __iter__(self) -> Iterator[bytes]: if self.session: for value in self.session: yield value diff --git a/archinstall/lib/user_interaction/general_conf.py b/archinstall/lib/user_interaction/general_conf.py index 7a6bb358..9722dc4d 100644 --- a/archinstall/lib/user_interaction/general_conf.py +++ b/archinstall/lib/user_interaction/general_conf.py @@ -3,7 +3,6 @@ from __future__ import annotations import logging import pathlib from typing import List, Any, Optional, Dict, TYPE_CHECKING -from typing import Union from ..locale_helpers import list_keyboard_languages, list_timezones from ..menu import MenuSelectionType, Menu, TextInput @@ -29,13 +28,18 @@ def ask_ntp(preset: bool = True) -> bool: return False if choice.value == Menu.no() else True -def ask_hostname(preset: str = None) -> str: +def ask_hostname(preset: str = '') -> str: while True: - hostname = TextInput(_('Desired hostname for the installation: '), preset).run().strip() + hostname = TextInput( + str(_('Desired hostname for the installation: ')), + preset + ).run().strip() + if hostname: return hostname -def ask_for_a_timezone(preset: str = None) -> str: + +def ask_for_a_timezone(preset: Optional[str] = None) -> Optional[str]: timezones = list_timezones() default = 'UTC' @@ -48,10 +52,12 @@ def ask_for_a_timezone(preset: str = None) -> str: match choice.type_: case MenuSelectionType.Skip: return preset - case MenuSelectionType.Selection: return choice.value + case MenuSelectionType.Selection: return choice.single_value + + return None -def ask_for_audio_selection(desktop: bool = True, preset: Union[str, None] = None) -> Union[str, None]: +def ask_for_audio_selection(desktop: bool = True, preset: Optional[str] = None) -> Optional[str]: no_audio = str(_('No audio server')) choices = ['pipewire', 'pulseaudio'] if desktop else ['pipewire', 'pulseaudio', no_audio] default = 'pipewire' if desktop else no_audio @@ -60,10 +66,12 @@ def ask_for_audio_selection(desktop: bool = True, preset: Union[str, None] = Non match choice.type_: case MenuSelectionType.Skip: return preset - case MenuSelectionType.Selection: return choice.value + case MenuSelectionType.Selection: return choice.single_value + return None -def select_language(preset_value: str = None) -> str: + +def select_language(preset: Optional[str] = None) -> Optional[str]: """ Asks the user to select a language Usually this is combined with :ref:`archinstall.list_keyboard_languages`. @@ -75,17 +83,18 @@ def select_language(preset_value: str = None) -> str: # sort alphabetically and then by length sorted_kb_lang = sorted(sorted(list(kb_lang)), key=len) - selected_lang = Menu( + choice = Menu( _('Select keyboard layout'), sorted_kb_lang, - preset_values=preset_value, + preset_values=preset, sort=False ).run() - if selected_lang.value is None: - return preset_value + match choice.type_: + case MenuSelectionType.Skip: return preset + case MenuSelectionType.Selection: return choice.single_value - return selected_lang.value + return None def select_mirror_regions(preset_values: Dict[str, Any] = {}) -> Dict[str, Any]: @@ -100,8 +109,10 @@ def select_mirror_regions(preset_values: Dict[str, Any] = {}) -> Dict[str, Any]: preselected = None else: preselected = list(preset_values.keys()) + mirrors = list_mirrors() - selected_mirror = Menu( + + choice = Menu( _('Select one of the regions to download packages from'), list(mirrors.keys()), preset_values=preselected, @@ -109,13 +120,18 @@ def select_mirror_regions(preset_values: Dict[str, Any] = {}) -> Dict[str, Any]: allow_reset=True ).run() - match selected_mirror.type_: - case MenuSelectionType.Reset: return {} - case MenuSelectionType.Skip: return preset_values - case _: return {selected: mirrors[selected] for selected in selected_mirror.value} + match choice.type_: + case MenuSelectionType.Reset: + return {} + case MenuSelectionType.Skip: + return preset_values + case MenuSelectionType.Selection: + return {selected: mirrors[selected] for selected in choice.multi_value} + + return {} -def select_archinstall_language(languages: List[Language], preset_value: Language) -> Language: +def select_archinstall_language(languages: List[Language], preset: Language) -> Language: # these are the displayed language names which can either be # the english name of a language or, if present, the # name of the language in its own language @@ -128,15 +144,15 @@ def select_archinstall_language(languages: List[Language], preset_value: Languag choice = Menu( title, list(options.keys()), - default_option=preset_value.display_name, + default_option=preset.display_name, preview_size=0.5 ).run() match choice.type_: - case MenuSelectionType.Skip: - return preset_value - case MenuSelectionType.Selection: - return options[choice.value] + case MenuSelectionType.Skip: return preset + case MenuSelectionType.Selection: return options[choice.single_value] + + raise ValueError('Language selection not handled') def ask_additional_packages_to_install(pre_set_packages: List[str] = []) -> List[str]: @@ -223,4 +239,6 @@ def select_additional_repositories(preset: List[str]) -> List[str]: match choice.type_: case MenuSelectionType.Skip: return preset case MenuSelectionType.Reset: return [] - case MenuSelectionType.Selection: return choice.value + case MenuSelectionType.Selection: return choice.single_value + + return [] diff --git a/archinstall/lib/user_interaction/locale_conf.py b/archinstall/lib/user_interaction/locale_conf.py index 88aec64e..cdc3423a 100644 --- a/archinstall/lib/user_interaction/locale_conf.py +++ b/archinstall/lib/user_interaction/locale_conf.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, TYPE_CHECKING +from typing import Any, TYPE_CHECKING, Optional from ..locale_helpers import list_locales from ..menu import Menu, MenuSelectionType @@ -9,33 +9,37 @@ if TYPE_CHECKING: _: Any -def select_locale_lang(preset: str = None) -> str: +def select_locale_lang(preset: Optional[str] = None) -> Optional[str]: locales = list_locales() locale_lang = set([locale.split()[0] for locale in locales]) - selected_locale = Menu( + choice = Menu( _('Choose which locale language to use'), list(locale_lang), sort=True, preset_values=preset ).run() - match selected_locale.type_: - case MenuSelectionType.Selection: return selected_locale.value + match choice.type_: + case MenuSelectionType.Selection: return choice.single_value case MenuSelectionType.Skip: return preset + return None -def select_locale_enc(preset: str = None) -> str: + +def select_locale_enc(preset: Optional[str] = None) -> Optional[str]: locales = list_locales() locale_enc = set([locale.split()[1] for locale in locales]) - selected_locale = Menu( + choice = Menu( _('Choose which locale encoding to use'), list(locale_enc), sort=True, preset_values=preset ).run() - match selected_locale.type_: - case MenuSelectionType.Selection: return selected_locale.value + match choice.type_: + case MenuSelectionType.Selection: return choice.single_value case MenuSelectionType.Skip: return preset + + return None diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index d9c5837c..48d141b7 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -223,7 +223,7 @@ def perform_installation(mountpoint: Path): # If the user provided a list of services to be enabled, pass the list to the enable_service function. # Note that while it's called enable_service, it can actually take a list of services and iterate it. if archinstall.arguments.get('services', None): - installation.enable_service(*archinstall.arguments['services']) + installation.enable_service(archinstall.arguments.get('services', [])) # If the user provided custom commands to be run post-installation, execute them now. if archinstall.arguments.get('custom-commands', None): diff --git a/archinstall/scripts/swiss.py b/archinstall/scripts/swiss.py index e2ee6fcb..34e4c022 100644 --- a/archinstall/scripts/swiss.py +++ b/archinstall/scripts/swiss.py @@ -239,7 +239,7 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): handler.config_installer(installation) if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '': - installation.add_additional_packages(archinstall.arguments.get('packages', None)) + installation.add_additional_packages(archinstall.arguments.get('packages', [])) if users := archinstall.arguments.get('!users', None): installation.create_users(users) @@ -278,7 +278,7 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): # If the user provided a list of services to be enabled, pass the list to the enable_service function. # Note that while it's called enable_service, it can actually take a list of services and iterate it. if archinstall.arguments.get('services', None): - installation.enable_service(*archinstall.arguments['services']) + installation.enable_service(archinstall.arguments.get('services', [])) # If the user provided custom commands to be run post-installation, execute them now. if archinstall.arguments.get('custom-commands', None): diff --git a/examples/interactive_installation.py b/examples/interactive_installation.py index a78b1712..f72f110b 100644 --- a/examples/interactive_installation.py +++ b/examples/interactive_installation.py @@ -147,7 +147,7 @@ def perform_installation(mountpoint: Path): handler.config_installer(installation) if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '': - installation.add_additional_packages(archinstall.arguments.get('packages', None)) + installation.add_additional_packages(archinstall.arguments.get('packages', [])) if users := archinstall.arguments.get('!users', None): installation.create_users(users) @@ -186,7 +186,7 @@ def perform_installation(mountpoint: Path): # If the user provided a list of services to be enabled, pass the list to the enable_service function. # Note that while it's called enable_service, it can actually take a list of services and iterate it. if archinstall.arguments.get('services', None): - installation.enable_service(*archinstall.arguments['services']) + installation.enable_service(archinstall.arguments.get('services', [])) # If the user provided custom commands to be run post-installation, execute them now. if archinstall.arguments.get('custom-commands', None): diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index a08b2d88..00000000 --- a/mypy.ini +++ /dev/null @@ -1,14 +0,0 @@ -[mypy] -python_version = 3.10 -follow_imports = silent -exclude = (?x)(^archinstall/lib/disk/btrfs/btrfssubvolumeinfo\.py$ - | ^archinstall/lib/general\.py$ - | ^archinstall/lib/hardware\.py$ - | ^archinstall/lib/menu/menu\.py$ - | ^archinstall/lib/mirrors\.py$ - | ^archinstall/lib/plugins\.py$ - | ^archinstall/lib/installer\.py$ - | ^archinstall/lib/systemd\.py$ - | ^archinstall/lib/user_interaction/general_conf\.py$ - | ^archinstall/lib/user_interaction/locale_conf\.py$) -files = archinstall/ diff --git a/pyproject.toml b/pyproject.toml index 557418cc..f837ebdf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,6 +60,7 @@ packages = ["archinstall"] [tool.mypy] python_version = "3.10" +files = "archinstall/" exclude = "tests" [tool.bandit] -- cgit v1.2.3-70-g09d2 From 2d06cf592a15e69911ffa82e7d86d832bf8ca89c Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Thu, 4 May 2023 00:37:55 +1000 Subject: Fix 1772 (#1773) Co-authored-by: Daniel Girtler --- archinstall/lib/models/network_configuration.py | 13 +++++++++---- archinstall/lib/profile/__init__.py | 3 +++ archinstall/scripts/guided.py | 5 ++++- archinstall/scripts/swiss.py | 5 ++++- examples/interactive_installation.py | 5 ++++- 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/archinstall/lib/models/network_configuration.py b/archinstall/lib/models/network_configuration.py index 66230e24..a8795fc1 100644 --- a/archinstall/lib/models/network_configuration.py +++ b/archinstall/lib/models/network_configuration.py @@ -6,7 +6,7 @@ from enum import Enum from typing import List, Optional, Dict, Union, Any, TYPE_CHECKING, Tuple from ..output import log -from ..storage import storage +from ..profile import ProfileConfiguration if TYPE_CHECKING: _: Any @@ -103,7 +103,11 @@ class NetworkConfigurationHandler: def configuration(self): return self._configuration - def config_installer(self, installation: Any): + def config_installer( + self, + installation: Any, + profile_config: Optional[ProfileConfiguration] = None + ): if self._configuration is None: return @@ -122,8 +126,9 @@ class NetworkConfigurationHandler: ) elif self._configuration.is_network_manager(): installation.add_additional_packages(["networkmanager"]) - if (profile := storage['arguments'].get('profile_config')) and profile.is_desktop_type_profile: - installation.add_additional_packages(["network-manager-applet"]) + if profile_config and profile_config.profile: + if profile_config.profile.is_desktop_type_profile(): + installation.add_additional_packages(["network-manager-applet"]) installation.enable_service('NetworkManager.service') def _parse_manual_config(self, configs: List[Dict[str, Any]]) -> Optional[List[NetworkConfiguration]]: diff --git a/archinstall/lib/profile/__init__.py b/archinstall/lib/profile/__init__.py index e69de29b..6e74b0d8 100644 --- a/archinstall/lib/profile/__init__.py +++ b/archinstall/lib/profile/__init__.py @@ -0,0 +1,3 @@ +from .profile_menu import ProfileMenu, select_greeter, select_profile +from .profiles_handler import profile_handler +from .profile_model import ProfileConfiguration diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 48d141b7..6419c1dc 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -181,7 +181,10 @@ def perform_installation(mountpoint: Path): if network_config: handler = NetworkConfigurationHandler(network_config) - handler.config_installer(installation) + handler.config_installer( + installation, + archinstall.arguments.get('profile_config', None) + ) if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '': installation.add_additional_packages(archinstall.arguments.get('packages', None)) diff --git a/archinstall/scripts/swiss.py b/archinstall/scripts/swiss.py index 34e4c022..2712807a 100644 --- a/archinstall/scripts/swiss.py +++ b/archinstall/scripts/swiss.py @@ -236,7 +236,10 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): if network_config: handler = models.NetworkConfigurationHandler(network_config) - handler.config_installer(installation) + handler.config_installer( + installation, + archinstall.arguments.get('profile_config', None) + ) if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '': installation.add_additional_packages(archinstall.arguments.get('packages', [])) diff --git a/examples/interactive_installation.py b/examples/interactive_installation.py index f72f110b..8b4e393f 100644 --- a/examples/interactive_installation.py +++ b/examples/interactive_installation.py @@ -144,7 +144,10 @@ def perform_installation(mountpoint: Path): if network_config: handler = NetworkConfigurationHandler(network_config) - handler.config_installer(installation) + handler.config_installer( + installation, + archinstall.arguments.get('profile_config', None) + ) if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '': installation.add_additional_packages(archinstall.arguments.get('packages', [])) -- cgit v1.2.3-70-g09d2 From 2531a5705004fdb04e6c3de79e8a9079f4a98218 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Wed, 3 May 2023 17:52:51 +0200 Subject: Fixing starting of service via str argument (#1779) --- archinstall/lib/installer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index b6eaa797..72ec534c 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -570,6 +570,8 @@ class Installer: def enable_service(self, services: Union[str, List[str]]) -> None: if type(services[0]) in (list, tuple): services = services[0] + if type(services) == str: + services = [services, ] for service in services: self.log(f'Enabling service {service}', level=logging.INFO) -- cgit v1.2.3-70-g09d2 From 9e5d45c5d8762bb9125a153c528957cef9bc7d24 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Thu, 4 May 2023 15:23:43 +1000 Subject: Fix 1780 (#1782) Co-authored-by: Daniel Girtler --- .pre-commit-config.yaml | 2 +- archinstall/lib/installer.py | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 87128289..002defbb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -38,5 +38,5 @@ repos: rev: v1.1.1 hooks: - id: mypy - args: [--config=mypy.ini] + args: [--config=pyproject.toml] fail_fast: true diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 72ec534c..c51019fd 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -392,8 +392,8 @@ class Installer: pacman_conf.write(line) def _pacstrap(self, packages: Union[str, List[str]]) -> bool: - if type(packages[0]) in (list, tuple): - packages = packages[0] + if isinstance(packages, str): + packages = [packages] for plugin in plugins.values(): if hasattr(plugin, 'on_pacstrap'): @@ -568,13 +568,12 @@ class Installer: self.enable_service("fstrim.timer") def enable_service(self, services: Union[str, List[str]]) -> None: - if type(services[0]) in (list, tuple): - services = services[0] - if type(services) == str: - services = [services, ] + if isinstance(services, str): + services = [services] for service in services: self.log(f'Enabling service {service}', level=logging.INFO) + try: self.arch_chroot(f'systemctl enable {service}') except SysCallError as error: -- cgit v1.2.3-70-g09d2 From adceed22ad3d8b6aa1e6d1aee56ae0c9a0a751aa Mon Sep 17 00:00:00 2001 From: Daemon Coder <11915375+codefiles@users.noreply.github.com> Date: Thu, 4 May 2023 03:45:43 -0400 Subject: Fix logic errors in `_fetch_lsblk_info()` (#1754) --- archinstall/lib/disk/device_model.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index 987a1e8a..8e26b1d7 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -974,32 +974,33 @@ def _fetch_lsblk_info(dev_path: Optional[Union[Path, str]] = None, retry: int = if retry == 0: retry = 1 - result = None - - for i in range(retry): + for retry_attempt in range(retry): try: result = SysCommand(f'lsblk --json -b -o+{lsblk_fields} {dev_path}') + break except SysCallError as error: # Get the output minus the message/info from lsblk if it returns a non-zero exit code. if error.worker: err = error.worker.decode('UTF-8') log(f'Error calling lsblk: {err}', level=logging.DEBUG) - time.sleep(1) else: raise error - if result and result.exit_code == 0: - try: - if decoded := result.decode('utf-8'): - block_devices = json.loads(decoded) - blockdevices = block_devices['blockdevices'] - return [LsblkInfo.from_json(device) for device in blockdevices] - except json.decoder.JSONDecodeError as err: - log(f"Could not decode lsblk JSON: {result}", fg="red", level=logging.ERROR) - raise err + if retry_attempt == retry - 1: + raise error - raise DiskError(f'Failed to read disk "{dev_path}" with lsblk') + time.sleep(1) + + try: + if decoded := result.decode('utf-8'): + block_devices = json.loads(decoded) + blockdevices = block_devices['blockdevices'] + return [LsblkInfo.from_json(device) for device in blockdevices] + except json.decoder.JSONDecodeError as err: + log(f"Could not decode lsblk JSON: {result}", fg="red", level=logging.ERROR) + raise err + raise DiskError(f'Failed to read disk "{dev_path}" with lsblk') def get_lsblk_info(dev_path: Union[Path, str]) -> LsblkInfo: if infos := _fetch_lsblk_info(dev_path): -- cgit v1.2.3-70-g09d2 From fd83f073f3e84feb1388ef739c1096f7d4a741de Mon Sep 17 00:00:00 2001 From: Daemon Coder <11915375+codefiles@users.noreply.github.com> Date: Thu, 4 May 2023 04:42:37 -0400 Subject: Update `SysCommand()` calls in remaining files (#1707) --- archinstall/lib/disk/device_handler.py | 50 ++++++++++++++++------------------ archinstall/lib/locale_helpers.py | 8 ++++-- archinstall/lib/luks.py | 48 ++++++++++++++------------------ archinstall/lib/mirrors.py | 6 ++-- archinstall/lib/networking.py | 23 +++++++++------- 5 files changed, 66 insertions(+), 69 deletions(-) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index 8f92cf3b..d4ad9075 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -139,19 +139,18 @@ class DeviceHandler(object): log(f'Failed to read btrfs subvolume information: {err}', level=logging.DEBUG) return subvol_infos - if result.exit_code == 0: - try: - if decoded := result.decode('utf-8'): - # ID 256 gen 16 top level 5 path @ - for line in decoded.splitlines(): - # expected output format: - # ID 257 gen 8 top level 5 path @home - name = Path(line.split(' ')[-1]) - sub_vol_mountpoint = lsblk_info.btrfs_subvol_info.get(name, None) - subvol_infos.append(_BtrfsSubvolumeInfo(name, sub_vol_mountpoint)) - except json.decoder.JSONDecodeError as err: - log(f"Could not decode lsblk JSON: {result}", fg="red", level=logging.ERROR) - raise err + try: + if decoded := result.decode('utf-8'): + # ID 256 gen 16 top level 5 path @ + for line in decoded.splitlines(): + # expected output format: + # ID 257 gen 8 top level 5 path @home + name = Path(line.split(' ')[-1]) + sub_vol_mountpoint = lsblk_info.btrfs_subvol_info.get(name, None) + subvol_infos.append(_BtrfsSubvolumeInfo(name, sub_vol_mountpoint)) + except json.decoder.JSONDecodeError as err: + log(f"Could not decode lsblk JSON: {result}", fg="red", level=logging.ERROR) + raise err if not lsblk_info.mountpoint: self.umount(dev_path) @@ -206,9 +205,7 @@ class DeviceHandler(object): log(f'Formatting filesystem: /usr/bin/{command} {options_str} {path}') try: - if (handle := SysCommand(f"/usr/bin/{command} {options_str} {path}")).exit_code != 0: - mkfs_error = handle.decode() - raise DiskError(f'Could not format {path} with {fs_type.value}: {mkfs_error}') + SysCommand(f"/usr/bin/{command} {options_str} {path}") except SysCallError as error: msg = f'Could not format {path} with {fs_type.value}: {error.message}' log(msg, fg='red') @@ -408,12 +405,16 @@ class DeviceHandler(object): SysCommand(f"btrfs subvolume create {subvol_path}") if sub_vol.nodatacow: - if (result := SysCommand(f'chattr +C {subvol_path}')).exit_code != 0: - raise DiskError(f'Could not set nodatacow attribute at {subvol_path}: {result.decode()}') + try: + SysCommand(f'chattr +C {subvol_path}') + except SysCallError as error: + raise DiskError(f'Could not set nodatacow attribute at {subvol_path}: {error}') if sub_vol.compress: - if (result := SysCommand(f'chattr +c {subvol_path}')).exit_code != 0: - raise DiskError(f'Could not set compress attribute at {subvol_path}: {result}') + try: + SysCommand(f'chattr +c {subvol_path}') + except SysCallError as error: + raise DiskError(f'Could not set compress attribute at {subvol_path}: {error}') if luks_handler is not None and luks_handler.mapper_dev is not None: self.umount(luks_handler.mapper_dev) @@ -518,9 +519,7 @@ class DeviceHandler(object): log(f'Mounting {dev_path}: command', level=logging.DEBUG) try: - result = SysCommand(command) - if result.exit_code != 0: - raise DiskError(f'Could not mount {dev_path}: {command}\n{result.decode()}') + SysCommand(command) except SysCallError as err: raise DiskError(f'Could not mount {dev_path}: {command}\n{err.message}') @@ -575,10 +574,7 @@ class DeviceHandler(object): try: log(f'Calling partprobe: {command}', level=logging.DEBUG) - result = SysCommand(command) - - if result.exit_code != 0: - log(f'"{command}" returned a failure: {result.decode()}', level=logging.DEBUG) + SysCommand(command) except SysCallError as error: log(f'"{command}" failed to run: {error}', level=logging.DEBUG) diff --git a/archinstall/lib/locale_helpers.py b/archinstall/lib/locale_helpers.py index d1fb4562..efb0365f 100644 --- a/archinstall/lib/locale_helpers.py +++ b/archinstall/lib/locale_helpers.py @@ -1,7 +1,7 @@ import logging from typing import Iterator, List, Callable, Optional -from .exceptions import ServiceException +from .exceptions import ServiceException, SysCallError from .general import SysCommand from .output import log from .storage import storage @@ -161,8 +161,10 @@ def set_keyboard_language(locale :str) -> bool: log(f"Invalid keyboard locale specified: {locale}", fg="red", level=logging.ERROR) return False - if (output := SysCommand(f'localectl set-keymap {locale}')).exit_code != 0: - raise ServiceException(f"Unable to set locale '{locale}' for console: {output}") + try: + SysCommand(f'localectl set-keymap {locale}') + except SysCallError as error: + raise ServiceException(f"Unable to set locale '{locale}' for console: {error}") return True diff --git a/archinstall/lib/luks.py b/archinstall/lib/luks.py index fc531a06..53a5e8d2 100644 --- a/archinstall/lib/luks.py +++ b/archinstall/lib/luks.py @@ -88,30 +88,28 @@ class Luks2: 'luksFormat', str(self.luks_dev_path), ]) - try: - # Retry formatting the volume because archinstall can some times be too quick - # which generates a "Device /dev/sdX does not exist or access denied." between - # setting up partitions and us trying to encrypt it. - cmd_handle = None - for i in range(storage['DISK_RETRY_ATTEMPTS']): - if (cmd_handle := SysCommand(cryptsetup_args)).exit_code != 0: - time.sleep(storage['DISK_TIMEOUTS']) - else: - break + # Retry formatting the volume because archinstall can some times be too quick + # which generates a "Device /dev/sdX does not exist or access denied." between + # setting up partitions and us trying to encrypt it. + for retry_attempt in range(storage['DISK_RETRY_ATTEMPTS']): + try: + SysCommand(cryptsetup_args) + break + except SysCallError as error: + time.sleep(storage['DISK_TIMEOUTS']) - if cmd_handle is not None and cmd_handle.exit_code != 0: - output = str(b''.join(cmd_handle)) - raise DiskError(f'Could not encrypt volume "{self.luks_dev_path}": {output}') - except SysCallError as err: - if err.exit_code == 1: - log(f'luks2 partition currently in use: {self.luks_dev_path}') - log('Attempting to unmount, crypt-close and trying encryption again') + if retry_attempt != storage['DISK_RETRY_ATTEMPTS'] - 1: + continue - self.lock() - # Then try again to set up the crypt-device - SysCommand(cryptsetup_args) - else: - raise err + if error.exit_code == 1: + log(f'luks2 partition currently in use: {self.luks_dev_path}') + log('Attempting to unmount, crypt-close and trying encryption again') + + self.lock() + # Then try again to set up the crypt-device + SysCommand(cryptsetup_args) + else: + raise DiskError(f'Could not encrypt volume "{self.luks_dev_path}": {error}') return key_file @@ -119,11 +117,7 @@ class Luks2: command = f'/usr/bin/cryptsetup luksUUID {self.luks_dev_path}' try: - result = SysCommand(command) - if result.exit_code != 0: - raise DiskError(f'Unable to get UUID for Luks device: {result.decode()}') - - return result.decode() # type: ignore + return SysCommand(command).decode().strip() # type: ignore except SysCallError as err: log(f'Unable to get UUID for Luks device: {self.luks_dev_path}', level=logging.INFO) raise err diff --git a/archinstall/lib/mirrors.py b/archinstall/lib/mirrors.py index 15d0fd6b..c6c5c8e4 100644 --- a/archinstall/lib/mirrors.py +++ b/archinstall/lib/mirrors.py @@ -7,6 +7,7 @@ from dataclasses import dataclass from .general import SysCommand from .output import log +from .exceptions import SysCallError from .storage import storage @@ -148,8 +149,9 @@ def re_rank_mirrors( src: str = '/etc/pacman.d/mirrorlist', dst: str = '/etc/pacman.d/mirrorlist', ) -> bool: - cmd = SysCommand(f"/usr/bin/rankmirrors -n {top} {src}") - if cmd.exit_code != 0: + try: + cmd = SysCommand(f"/usr/bin/rankmirrors -n {top} {src}") + except SysCallError: return False with open(dst, 'w') as f: f.write(str(cmd)) diff --git a/archinstall/lib/networking.py b/archinstall/lib/networking.py index 3516aac4..b858daaf 100644 --- a/archinstall/lib/networking.py +++ b/archinstall/lib/networking.py @@ -38,11 +38,11 @@ def list_interfaces(skip_loopback :bool = True) -> Dict[str, str]: def check_mirror_reachable() -> bool: log("Testing connectivity to the Arch Linux mirrors ...", level=logging.INFO) try: - if run_pacman("-Sy").exit_code == 0: - return True - elif os.geteuid() != 0: - log("check_mirror_reachable() uses 'pacman -Sy' which requires root.", level=logging.ERROR, fg="red") + run_pacman("-Sy") + return True except SysCallError as err: + if os.geteuid() != 0: + log("check_mirror_reachable() uses 'pacman -Sy' which requires root.", level=logging.ERROR, fg="red") log(f'exit_code: {err.exit_code}, Error: {err.message}', level=logging.DEBUG) return False @@ -50,11 +50,12 @@ def check_mirror_reachable() -> bool: def update_keyring() -> bool: log("Updating archlinux-keyring ...", level=logging.INFO) - if run_pacman("-Sy --noconfirm archlinux-keyring").exit_code == 0: + try: + run_pacman("-Sy --noconfirm archlinux-keyring") return True - - elif os.geteuid() != 0: - log("update_keyring() uses 'pacman -Sy archlinux-keyring' which requires root.", level=logging.ERROR, fg="red") + except SysCallError: + if os.geteuid() != 0: + log("update_keyring() uses 'pacman -Sy archlinux-keyring' which requires root.", level=logging.ERROR, fg="red") return False @@ -84,8 +85,10 @@ def wireless_scan(interface :str) -> None: if interfaces[interface] != 'WIRELESS': raise HardwareIncompatibilityError(f"Interface {interface} is not a wireless interface: {interfaces}") - if not (output := SysCommand(f"iwctl station {interface} scan")).exit_code == 0: - raise SystemError(f"Could not scan for wireless networks: {output}") + try: + SysCommand(f"iwctl station {interface} scan") + except SysCallError as error: + raise SystemError(f"Could not scan for wireless networks: {error}") if '_WIFI' not in storage: storage['_WIFI'] = {} -- cgit v1.2.3-70-g09d2 From f211906a5afcfad553070748a779c3166d2b3b4b Mon Sep 17 00:00:00 2001 From: Daemon Coder <11915375+codefiles@users.noreply.github.com> Date: Thu, 4 May 2023 10:04:35 -0400 Subject: Remove obsolete enabling of NTP in ISO (#1729) * Remove obsolete enabling of NTP in ISO * Fixed flake8 linting * Remove `activate_ntp()` * Update comment --------- Co-authored-by: Anton Hvornum --- archinstall/lib/global_menu.py | 12 +----------- archinstall/lib/installer.py | 28 ++++++---------------------- archinstall/scripts/guided.py | 3 --- archinstall/scripts/swiss.py | 6 ------ examples/interactive_installation.py | 3 --- 5 files changed, 7 insertions(+), 45 deletions(-) diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index bc9164ee..0ec057f3 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Any, List, Optional, Union, Dict, TYPE_CHECKING from . import disk -from .general import SysCommand, secret +from .general import secret from .menu import Selector, AbstractMenu from .models import NetworkConfiguration from .models.bootloader import Bootloader @@ -18,7 +18,6 @@ from .user_interaction import ask_for_audio_selection from .user_interaction import ask_for_bootloader from .user_interaction import ask_for_swap from .user_interaction import ask_hostname -from .user_interaction import ask_ntp from .user_interaction import ask_to_configure_network from .user_interaction import get_password, ask_for_a_timezone from .user_interaction import select_additional_repositories @@ -164,7 +163,6 @@ class GlobalMenu(AbstractMenu): self._menu_options['ntp'] = \ Selector( _('Automatic time sync (NTP)'), - lambda preset: self._select_ntp(preset), default=True) self._menu_options['__separator__'] = \ Selector('') @@ -323,14 +321,6 @@ class GlobalMenu(AbstractMenu): password = get_password(prompt=prompt) return password - def _select_ntp(self, preset :bool = True) -> bool: - ntp = ask_ntp(preset) - - value = str(ntp).lower() - SysCommand(f'timedatectl set-ntp {value}') - - return ntp - def _select_disk_config( self, preset: Optional[disk.DiskLayoutConfiguration] = None diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index c51019fd..726ff3d0 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -162,10 +162,14 @@ class Installer: def _verify_service_stop(self): """ Certain services might be running that affects the system during installation. - Currently, only one such service is "reflector.service" which updates /etc/pacman.d/mirrorlist + One such service is "reflector.service" which updates /etc/pacman.d/mirrorlist We need to wait for it before we continue since we opted in to use a custom mirror/region. """ - log('Waiting for automatic mirror selection (reflector) to complete...', level=logging.INFO) + log('Waiting for time sync (systemd-timesyncd.service) to complete.', level=logging.INFO) + while SysCommand('timedatectl show --property=NTPSynchronized --value').decode().rstrip() != 'yes': + time.sleep(1) + + log('Waiting for automatic mirror selection (reflector) to complete.', level=logging.INFO) while service_state('reflector') not in ('dead', 'failed', 'exited'): time.sleep(1) @@ -282,26 +286,6 @@ class Installer: self._disk_encryption.encryption_password ) - def activate_ntp(self): - """ - If NTP is activated, confirm activiation in the ISO and at least one time-sync finishes - """ - SysCommand('timedatectl set-ntp true') - - logged = False - while service_state('dbus-org.freedesktop.timesync1.service') not in ['running']: - if not logged: - log(f"Waiting for dbus-org.freedesktop.timesync1.service to enter running state", level=logging.INFO) - logged = True - time.sleep(1) - - logged = False - while 'Server: n/a' in SysCommand('timedatectl timesync-status --no-pager --property=Server --value'): - if not logged: - log(f"Waiting for timedatectl timesync-status to report a timesync against a server", level=logging.INFO) - logged = True - time.sleep(1) - def sync_log_to_install_medium(self) -> bool: # Copy over the install log (if there is one) to the install medium if # at least the base has been strapped in, otherwise we won't have a filesystem/structure to copy to. diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 6419c1dc..9906e0a9 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -149,9 +149,6 @@ def perform_installation(mountpoint: Path): # generate encryption key files for the mounted luks devices installation.generate_key_files() - if archinstall.arguments.get('ntp', False): - installation.activate_ntp() - # Set mirrors used by pacstrap (outside of installation) if archinstall.arguments.get('mirror-region', None): use_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors for the live medium diff --git a/archinstall/scripts/swiss.py b/archinstall/scripts/swiss.py index 2712807a..3bf847b1 100644 --- a/archinstall/scripts/swiss.py +++ b/archinstall/scripts/swiss.py @@ -74,9 +74,6 @@ class SetupMenu(GlobalMenu): self.enable('abort') def exit_callback(self): - if self._data_store.get('ntp', False): - archinstall.SysCommand('timedatectl set-ntp true') - if self._data_store.get('mode', None): archinstall.arguments['mode'] = self._data_store['mode'] log(f"Archinstall will execute under {archinstall.arguments['mode']} mode") @@ -203,9 +200,6 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): # generate encryption key files for the mounted luks devices installation.generate_key_files() - if archinstall.arguments.get('ntp', False): - installation.activate_ntp() - # Set mirrors used by pacstrap (outside of installation) if archinstall.arguments.get('mirror-region', None): use_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors for the live medium diff --git a/examples/interactive_installation.py b/examples/interactive_installation.py index 8b4e393f..a27ec0f9 100644 --- a/examples/interactive_installation.py +++ b/examples/interactive_installation.py @@ -112,9 +112,6 @@ def perform_installation(mountpoint: Path): # generate encryption key files for the mounted luks devices installation.generate_key_files() - if archinstall.arguments.get('ntp', False): - installation.activate_ntp() - # Set mirrors used by pacstrap (outside of installation) if archinstall.arguments.get('mirror-region', None): use_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors for the live medium -- cgit v1.2.3-70-g09d2 From 15199081e542caf38c9c0f163af764616d9420ef Mon Sep 17 00:00:00 2001 From: xenium <76689618+xeniumcode@users.noreply.github.com> Date: Thu, 4 May 2023 21:00:40 +0530 Subject: Adding Hyperland wm (#1785) --- archinstall/default_profiles/desktops/hyperland.py | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 archinstall/default_profiles/desktops/hyperland.py diff --git a/archinstall/default_profiles/desktops/hyperland.py b/archinstall/default_profiles/desktops/hyperland.py new file mode 100644 index 00000000..e55dd7c4 --- /dev/null +++ b/archinstall/default_profiles/desktops/hyperland.py @@ -0,0 +1,31 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class HyperlandProfile(XorgProfile): + def __init__(self): + super().__init__('Hyperland', ProfileType.DesktopEnv, description='') + + @property + def packages(self) -> List[str]: + return [ + "hyprland", + "dunst", + "xdg-desktop-portal-hyprland", + "kitty", + "qt5-wayland", + "qt6-wayland" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Sddm + + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + return text + '\n' + self.packages_text() -- cgit v1.2.3-70-g09d2 From 1330c68084580a3ed42a799c3f846cb78a520269 Mon Sep 17 00:00:00 2001 From: Daemon Coder <11915375+codefiles@users.noreply.github.com> Date: Fri, 5 May 2023 01:47:35 -0400 Subject: Fix `LsblkInfo` size field (#1787) --- archinstall/lib/disk/device_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index 8e26b1d7..d57347b7 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -851,7 +851,7 @@ class LsblkInfo: name: str = '' path: Path = Path() pkname: str = '' - size: Size = Size(0, Unit.B) + size: Size = field(default_factory=lambda: Size(0, Unit.B)) log_sec: int = 0 pttype: str = '' ptuuid: str = '' -- cgit v1.2.3-70-g09d2 From f0fc6a77f21aabacc1c9ef772875f8dd97344af8 Mon Sep 17 00:00:00 2001 From: Daemon Coder <11915375+codefiles@users.noreply.github.com> Date: Sat, 6 May 2023 02:09:34 -0400 Subject: Fix NTP option (#1788) --- archinstall/lib/global_menu.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 0ec057f3..db7af63b 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -18,6 +18,7 @@ from .user_interaction import ask_for_audio_selection from .user_interaction import ask_for_bootloader from .user_interaction import ask_for_swap from .user_interaction import ask_hostname +from .user_interaction import ask_ntp from .user_interaction import ask_to_configure_network from .user_interaction import get_password, ask_for_a_timezone from .user_interaction import select_additional_repositories @@ -163,6 +164,7 @@ class GlobalMenu(AbstractMenu): self._menu_options['ntp'] = \ Selector( _('Automatic time sync (NTP)'), + lambda preset: ask_ntp(preset), default=True) self._menu_options['__separator__'] = \ Selector('') -- cgit v1.2.3-70-g09d2 From 235c1190b003b8f69ff344d6e2ac31f4bd1d0ba2 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Sat, 6 May 2023 16:09:58 +1000 Subject: Fix 1763 (#1790) * Fix 1763 --------- Co-authored-by: Daniel Girtler --- archinstall/default_profiles/desktops/sway.py | 37 +++++++++++++++++++-------- archinstall/default_profiles/profile.py | 25 ++++++++---------- archinstall/lib/profile/profiles_handler.py | 8 ++++++ 3 files changed, 44 insertions(+), 26 deletions(-) diff --git a/archinstall/default_profiles/desktops/sway.py b/archinstall/default_profiles/desktops/sway.py index 519f5bbb..ae814e46 100644 --- a/archinstall/default_profiles/desktops/sway.py +++ b/archinstall/default_profiles/desktops/sway.py @@ -1,3 +1,4 @@ +from enum import Enum from typing import List, Optional, TYPE_CHECKING, Any from archinstall.default_profiles.profile import ProfileType, GreeterType @@ -9,6 +10,11 @@ if TYPE_CHECKING: _: Any +class SeatAccess(Enum): + seatd = 'seatd' + polkit = 'polkit' + + class SwayProfile(XorgProfile): def __init__(self): super().__init__( @@ -16,10 +22,15 @@ class SwayProfile(XorgProfile): ProfileType.WindowMgr, description='' ) - self._control_preference = [] + + self.custom_settings = {'seat_access': None} @property def packages(self) -> List[str]: + additional = [] + if seat := self.custom_settings.get('seat_access', None): + additional = [seat] + return [ "sway", "swaybg", @@ -33,7 +44,7 @@ class SwayProfile(XorgProfile): "pavucontrol", "foot", "xorg-xwayland" - ] + self._control_preference + ] + additional @property def default_greeter_type(self) -> Optional[GreeterType]: @@ -41,22 +52,26 @@ class SwayProfile(XorgProfile): @property def services(self) -> List[str]: - if "seatd" in self._control_preference: - return ['seatd'] - elif "polkit" in self._control_preference: - return ['polkit'] - + if pref := self.custom_settings.get('seat_access', None): + return [pref.value] return [] - def _get_system_privelege_control_preference(self): + def _ask_seat_access(self): # need to activate seat service and add to seat group title = str(_('Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)')) title += str(_('\n\nChoose an option to give Sway access to your hardware')) - choice = Menu(title, ["polkit", "seatd"], skip=False).run() - self._control_preference = [choice.value] + + options = [e.value for e in SeatAccess] + default = None + + if seat := self.custom_settings.get('seat_access', None): + default = seat + + choice = Menu(title, options, skip=False, preset_values=default).run() + self.custom_settings['seat_access'] = choice.single_value def do_on_select(self): - self._get_system_privelege_control_preference() + self._ask_seat_access() def preview_text(self) -> Optional[str]: text = str(_('Environment type: {}')).format(self.profile_type.value) diff --git a/archinstall/default_profiles/profile.py b/archinstall/default_profiles/profile.py index c7d6b3dc..b1ad1f50 100644 --- a/archinstall/default_profiles/profile.py +++ b/archinstall/default_profiles/profile.py @@ -1,6 +1,5 @@ from __future__ import annotations -from dataclasses import dataclass from enum import Enum, auto from typing import List, Optional, Any, Dict, TYPE_CHECKING, TypeVar @@ -43,20 +42,6 @@ class SelectResult(Enum): ResetCurrent = auto() -@dataclass -class ProfileInfo: - name: str - details: Optional[str] - gfx_driver: Optional[str] = None - greeter: Optional[str] = None - - @property - def absolute_name(self) -> str: - if self.details is not None: - return self.details - return self.name - - class Profile: def __init__( self, @@ -72,6 +57,8 @@ class Profile: self.name = name self.description = description self.profile_type = profile_type + self.custom_settings: Dict[str, Any] = {} + self._support_gfx_driver = support_gfx_driver self._support_greeter = support_greeter @@ -135,6 +122,14 @@ class Profile: """ return SelectResult.NewSelection + def set_custom_settings(self, settings: Dict[str, Any]): + """ + Set the custom settings for the profile. + This is also called when the settings are parsed from the config + and can be overriden to perform further actions based on the profile + """ + self.custom_settings = settings + def current_selection_names(self) -> List[str]: if self._current_selection: return [s.name for s in self._current_selection] diff --git a/archinstall/lib/profile/profiles_handler.py b/archinstall/lib/profile/profiles_handler.py index 824849c3..6ed95f8e 100644 --- a/archinstall/lib/profile/profiles_handler.py +++ b/archinstall/lib/profile/profiles_handler.py @@ -43,6 +43,7 @@ class ProfileHandler: data = { 'main': profile.name, 'details': [profile.name for profile in profile.current_selection], + 'custom_settings': {profile.name: profile.custom_settings for profile in profile.current_selection} } if self._url_path is not None: @@ -98,6 +99,7 @@ class ProfileHandler: profile = self.get_profile_by_name(main) if main else None valid: List[Profile] = [] + if details := profile_config.get('details', []): resolved = {detail: self.get_profile_by_name(detail) for detail in details if detail} valid = [p for p in resolved.values() if p is not None] @@ -106,6 +108,12 @@ class ProfileHandler: if invalid: log(f'No profile definition found: {invalid}') + custom_settings = profile_config.get('custom_settings', {}) + for profile in valid: + profile.set_custom_settings( + custom_settings.get(profile.name, {}) + ) + if profile is not None: profile.set_current_selection(valid) -- cgit v1.2.3-70-g09d2 From eb23b2d7c991829fc1b4c3aa16fc6a6290aef3e0 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Sun, 7 May 2023 16:38:29 +1000 Subject: Update version 2.5.6 (#1791) Co-authored-by: Daniel Girtler --- PKGBUILD | 2 +- archinstall/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index 69f81f49..03160a1c 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -4,7 +4,7 @@ # Contributor: demostanis worlds pkgname=archinstall -pkgver=2.5.4 +pkgver=2.5.6 pkgrel=1 pkgdesc="Just another guided/automated Arch Linux installer with a twist" arch=(any) diff --git a/archinstall/__init__.py b/archinstall/__init__.py index 29b70b7a..6f67d20f 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -30,7 +30,7 @@ from .lib.configuration import * parser = ArgumentParser() -__version__ = "2.5.4" +__version__ = "2.5.6" storage['__version__'] = __version__ # add the custome _ as a builtin, it can now be used anywhere in the -- cgit v1.2.3-70-g09d2 From 525c94bf36edcc47b9af4d025785ec9681e3aa37 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Sun, 7 May 2023 16:39:25 +1000 Subject: Fix 1793 (#1794) Co-authored-by: Daniel Girtler --- archinstall/lib/disk/device_handler.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index d4ad9075..13bde77a 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -10,7 +10,7 @@ from typing import List, Dict, Any, Optional, TYPE_CHECKING from parted import ( # type: ignore Disk, Geometry, FileSystem, PartitionException, DiskLabelException, - getAllDevices, freshDisk, Partition, + getAllDevices, freshDisk, Partition, Device ) from .device_model import ( @@ -103,7 +103,8 @@ class DeviceHandler(object): def get_device_by_partition_path(self, partition_path: Path) -> Optional[BDevice]: partition = self.find_partition(partition_path) if partition: - return partition.disk.device + device: Device = partition.disk.device + return self.get_device(Path(device.path)) return None def find_partition(self, path: Path) -> Optional[_PartitionInfo]: -- cgit v1.2.3-70-g09d2 From c0a7d45a83a8a6a8a7497362364d899a7ae8843d Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Sun, 7 May 2023 16:40:44 +1000 Subject: Fix 1792 (#1795) Co-authored-by: Daniel Girtler --- archinstall/default_profiles/applications/pipewire.py | 2 +- archinstall/default_profiles/servers/docker.py | 2 +- archinstall/lib/configuration.py | 2 +- archinstall/lib/global_menu.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/archinstall/default_profiles/applications/pipewire.py b/archinstall/default_profiles/applications/pipewire.py index 5d2e5ea3..4cb75968 100644 --- a/archinstall/default_profiles/applications/pipewire.py +++ b/archinstall/default_profiles/applications/pipewire.py @@ -27,7 +27,7 @@ class PipewireProfile(Profile): ] def _enable_pipewire_for_all(self, install_session: 'Installer'): - users: Union[User, List[User]] = archinstall.arguments.get('!users', None) + users: Union[User, List[User]] = archinstall.arguments.get('!users', []) if not isinstance(users, list): users = [users] diff --git a/archinstall/default_profiles/servers/docker.py b/archinstall/default_profiles/servers/docker.py index e6e17831..f4800916 100644 --- a/archinstall/default_profiles/servers/docker.py +++ b/archinstall/default_profiles/servers/docker.py @@ -25,7 +25,7 @@ class DockerProfile(Profile): return ['docker'] def post_install(self, install_session: 'Installer'): - users: Union[User, List[User]] = archinstall.arguments.get('!users', None) + users: Union[User, List[User]] = archinstall.arguments.get('!users', []) if not isinstance(users, list): users = [users] diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py index 77fed755..22c41c0d 100644 --- a/archinstall/lib/configuration.py +++ b/archinstall/lib/configuration.py @@ -29,7 +29,7 @@ class ConfigurationOutput: self._user_config_file = 'user_configuration.json' self._user_creds_file = "user_credentials.json" - self._sensitive = ['!users'] + self._sensitive = ['!users', '!root-password'] self._ignore = ['abort', 'install', 'config', 'creds', 'dry_run'] self._process_config() diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index db7af63b..a969d93f 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -107,7 +107,7 @@ class GlobalMenu(AbstractMenu): Selector( _('User account'), lambda x: self._create_user_account(x), - default={}, + default=[], display_func=lambda x: f'{len(x)} {_("User(s)")}' if len(x) > 0 else None, preview_func=self._prev_users) self._menu_options['profile_config'] = \ -- cgit v1.2.3-70-g09d2 From 232939e910433ceb2c426a3de224587473c03182 Mon Sep 17 00:00:00 2001 From: Daemon Coder <11915375+codefiles@users.noreply.github.com> Date: Sun, 7 May 2023 02:41:50 -0400 Subject: Refactor `build_iso.sh` added packages (#1797) --- build_iso.sh | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/build_iso.sh b/build_iso.sh index 74e815ee..c63c20d4 100755 --- a/build_iso.sh +++ b/build_iso.sh @@ -2,6 +2,18 @@ zprofile="/tmp/archlive/airootfs/root/.zprofile" +# Packages to add to the archiso profile packages +packages=( + git + python + python-pip + python-build + python-flit + python-setuptools + python-wheel + python-pyparted +) + mkdir -p /tmp/archlive/airootfs/root/archinstall-git cp -r . /tmp/archlive/airootfs/root/archinstall-git @@ -23,7 +35,10 @@ pacman --noconfirm -S git archiso cp -r /usr/share/archiso/configs/releng/* /tmp/archlive -echo -e "git\npython\npython-pip\npython-build\npython-flit\npython-setuptools\npython-wheel\npython-pyparted" >> /tmp/archlive/packages.x86_64 +# Add packages to the archiso profile packages +for package in "${packages[@]}"; do + echo "$package" >> /tmp/archlive/packages.x86_64 +done find /tmp/archlive cd /tmp/archlive -- cgit v1.2.3-70-g09d2 From a968dccbf1b9c83c8abc40e709c5b1c3142a5f2c Mon Sep 17 00:00:00 2001 From: Daemon Coder <11915375+codefiles@users.noreply.github.com> Date: Sun, 7 May 2023 02:48:46 -0400 Subject: Fix default value idx in `_determine_cursor_pos()` (#1800) Co-authored-by: Daniel Girtler Co-authored-by: Daniel Girtler --- archinstall/lib/menu/menu.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/archinstall/lib/menu/menu.py b/archinstall/lib/menu/menu.py index 12dbf1f5..f3fdb85f 100644 --- a/archinstall/lib/menu/menu.py +++ b/archinstall/lib/menu/menu.py @@ -337,7 +337,10 @@ class Menu(TerminalMenu): if '|' in p: p = p.replace('|', '\\|') - idx = self._menu_options.index(p) + if p in self._menu_options: + idx = self._menu_options.index(p) + else: + idx = self._menu_options.index(self._default_menu_value) indexes.append(idx) except (IndexError, ValueError): log(f'Error finding index of {p}: {self._menu_options}', level=logging.DEBUG) -- cgit v1.2.3-70-g09d2 From c10acfa5aeda44db9393fbbbf75356c0ff411e43 Mon Sep 17 00:00:00 2001 From: Daemon Coder <11915375+codefiles@users.noreply.github.com> Date: Sun, 7 May 2023 03:04:44 -0400 Subject: Refactor `build_iso.sh` zprofile (#1799) --- build_iso.sh | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/build_iso.sh b/build_iso.sh index c63c20d4..df57b723 100755 --- a/build_iso.sh +++ b/build_iso.sh @@ -1,7 +1,5 @@ #!/bin/bash -zprofile="/tmp/archlive/airootfs/root/.zprofile" - # Packages to add to the archiso profile packages packages=( git @@ -17,18 +15,18 @@ packages=( mkdir -p /tmp/archlive/airootfs/root/archinstall-git cp -r . /tmp/archlive/airootfs/root/archinstall-git -echo "pip uninstall archinstall -y" > $zprofile -echo "cd archinstall-git" >> $zprofile -echo "rm -rf dist" >> $zprofile - -echo "python -m build --wheel --no-isolation" >> $zprofile -echo "pip install dist/archinstall*.whl" >> $zprofile +cat <<- _EOF_ | tee /tmp/archlive/airootfs/root/.zprofile + pip uninstall archinstall -y + cd archinstall-git + rm -rf dist -echo "echo \"This is an unofficial ISO for development and testing of archinstall. No support will be provided.\"" >> $zprofile -echo "echo \"This ISO was built from Git SHA $GITHUB_SHA\"" >> $zprofile -echo "echo \"Type archinstall to launch the installer.\"" >> $zprofile + python -m build --wheel --no-isolation + pip install dist/archinstall*.whl -cat $zprofile + echo "This is an unofficial ISO for development and testing of archinstall. No support will be provided." + echo "This ISO was built from Git SHA $GITHUB_SHA" + echo "Type archinstall to launch the installer." +_EOF_ pacman -Sy pacman --noconfirm -S git archiso -- cgit v1.2.3-70-g09d2 From dc19d5f0fa8283f6bdb954987f282a44fcb197a1 Mon Sep 17 00:00:00 2001 From: Daemon Coder <11915375+codefiles@users.noreply.github.com> Date: Sun, 7 May 2023 04:16:52 -0400 Subject: Refactor `build_iso.sh` archinstall archiso profile package (#1803) --- build_iso.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/build_iso.sh b/build_iso.sh index df57b723..073a5459 100755 --- a/build_iso.sh +++ b/build_iso.sh @@ -1,5 +1,7 @@ #!/bin/bash +packages_file="/tmp/archlive/packages.x86_64" + # Packages to add to the archiso profile packages packages=( git @@ -16,7 +18,6 @@ mkdir -p /tmp/archlive/airootfs/root/archinstall-git cp -r . /tmp/archlive/airootfs/root/archinstall-git cat <<- _EOF_ | tee /tmp/archlive/airootfs/root/.zprofile - pip uninstall archinstall -y cd archinstall-git rm -rf dist @@ -33,9 +34,11 @@ pacman --noconfirm -S git archiso cp -r /usr/share/archiso/configs/releng/* /tmp/archlive +sed -i /archinstall/d $packages_file + # Add packages to the archiso profile packages for package in "${packages[@]}"; do - echo "$package" >> /tmp/archlive/packages.x86_64 + echo "$package" >> $packages_file done find /tmp/archlive -- cgit v1.2.3-70-g09d2 From 1df53c1406960b5d7b17bb7ee84431f8de7bfbdf Mon Sep 17 00:00:00 2001 From: Daemon Coder <11915375+codefiles@users.noreply.github.com> Date: Sun, 7 May 2023 08:02:56 -0400 Subject: Remove Flit from `build_iso.sh` added packages (#1804) --- build_iso.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/build_iso.sh b/build_iso.sh index 073a5459..48f5bef6 100755 --- a/build_iso.sh +++ b/build_iso.sh @@ -8,7 +8,6 @@ packages=( python python-pip python-build - python-flit python-setuptools python-wheel python-pyparted -- cgit v1.2.3-70-g09d2 From 6e6b850a8f687b193172aaa321d49bd2956c1d4f Mon Sep 17 00:00:00 2001 From: Daemon Coder <11915375+codefiles@users.noreply.github.com> Date: Mon, 8 May 2023 10:06:34 -0400 Subject: Add `set -e` to `build_iso.sh` (#1806) --- build_iso.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build_iso.sh b/build_iso.sh index 48f5bef6..26adf95e 100755 --- a/build_iso.sh +++ b/build_iso.sh @@ -1,5 +1,7 @@ #!/bin/bash +set -e + packages_file="/tmp/archlive/packages.x86_64" # Packages to add to the archiso profile packages -- cgit v1.2.3-70-g09d2 From 89cefb9a1c7d4c4968e7d8645149078e601c9d1c Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Fri, 12 May 2023 02:30:09 +1000 Subject: Cleanup imports and unused code (#1801) * Cleanup imports and unused code * Update build check * Keep deprecation exception * Simplify logging --------- Co-authored-by: Daniel Girtler --- .github/workflows/python-build.yml | 5 +- README.md | 2 +- archinstall/__init__.py | 85 +++-- archinstall/default_profiles/desktop.py | 4 +- archinstall/default_profiles/server.py | 9 +- archinstall/lib/boot.py | 111 ++++++ archinstall/lib/configuration.py | 114 +++++- archinstall/lib/disk/device_handler.py | 99 +++--- archinstall/lib/disk/device_model.py | 50 +-- archinstall/lib/disk/encryption_menu.py | 2 +- archinstall/lib/disk/fido.py | 11 +- archinstall/lib/disk/filesystem.py | 11 +- archinstall/lib/disk/partitioning_menu.py | 9 +- archinstall/lib/exceptions.py | 23 +- archinstall/lib/general.py | 88 ++--- archinstall/lib/global_menu.py | 36 +- archinstall/lib/hardware.py | 274 +++++++------- archinstall/lib/installer.py | 225 ++++++------ archinstall/lib/interactions/__init__.py | 20 ++ archinstall/lib/interactions/disk_conf.py | 393 +++++++++++++++++++++ archinstall/lib/interactions/general_conf.py | 243 +++++++++++++ archinstall/lib/interactions/locale_conf.py | 43 +++ archinstall/lib/interactions/manage_users_conf.py | 106 ++++++ archinstall/lib/interactions/network_conf.py | 172 +++++++++ archinstall/lib/interactions/system_conf.py | 117 ++++++ archinstall/lib/interactions/utils.py | 34 ++ archinstall/lib/locale.py | 68 ++++ archinstall/lib/locale_helpers.py | 176 --------- archinstall/lib/luks.py | 31 +- archinstall/lib/menu/abstract_menu.py | 9 +- archinstall/lib/menu/menu.py | 27 +- archinstall/lib/mirrors.py | 7 +- archinstall/lib/models/bootloader.py | 9 +- archinstall/lib/models/network_configuration.py | 14 +- archinstall/lib/networking.py | 53 +-- archinstall/lib/output.py | 154 +++++--- archinstall/lib/pacman.py | 7 +- archinstall/lib/plugins.py | 43 ++- archinstall/lib/profile/profile_menu.py | 2 +- archinstall/lib/profile/profiles_handler.py | 21 +- archinstall/lib/services.py | 11 - archinstall/lib/storage.py | 4 +- archinstall/lib/systemd.py | 110 ------ archinstall/lib/translationhandler.py | 14 +- archinstall/lib/user_interaction/__init__.py | 10 - archinstall/lib/user_interaction/disk_conf.py | 391 -------------------- archinstall/lib/user_interaction/general_conf.py | 244 ------------- archinstall/lib/user_interaction/locale_conf.py | 45 --- .../lib/user_interaction/manage_users_conf.py | 106 ------ archinstall/lib/user_interaction/network_conf.py | 173 --------- archinstall/lib/user_interaction/save_conf.py | 113 ------ archinstall/lib/user_interaction/system_conf.py | 117 ------ archinstall/lib/user_interaction/utils.py | 34 -- archinstall/lib/utils/util.py | 4 +- archinstall/scripts/guided.py | 35 +- archinstall/scripts/minimal.py | 30 +- archinstall/scripts/only_hd.py | 29 +- archinstall/scripts/swiss.py | 50 +-- archinstall/scripts/unattended.py | 9 +- examples/full_automated_installation.py | 11 +- examples/interactive_installation.py | 29 +- examples/mac_address_installation.py | 8 +- examples/minimal_installation.py | 21 +- examples/only_hd_installation.py | 6 +- pyproject.toml | 1 + 65 files changed, 2124 insertions(+), 2388 deletions(-) create mode 100644 archinstall/lib/boot.py create mode 100644 archinstall/lib/interactions/__init__.py create mode 100644 archinstall/lib/interactions/disk_conf.py create mode 100644 archinstall/lib/interactions/general_conf.py create mode 100644 archinstall/lib/interactions/locale_conf.py create mode 100644 archinstall/lib/interactions/manage_users_conf.py create mode 100644 archinstall/lib/interactions/network_conf.py create mode 100644 archinstall/lib/interactions/system_conf.py create mode 100644 archinstall/lib/interactions/utils.py create mode 100644 archinstall/lib/locale.py delete mode 100644 archinstall/lib/locale_helpers.py delete mode 100644 archinstall/lib/services.py delete mode 100644 archinstall/lib/systemd.py delete mode 100644 archinstall/lib/user_interaction/__init__.py delete mode 100644 archinstall/lib/user_interaction/disk_conf.py delete mode 100644 archinstall/lib/user_interaction/general_conf.py delete mode 100644 archinstall/lib/user_interaction/locale_conf.py delete mode 100644 archinstall/lib/user_interaction/manage_users_conf.py delete mode 100644 archinstall/lib/user_interaction/network_conf.py delete mode 100644 archinstall/lib/user_interaction/save_conf.py delete mode 100644 archinstall/lib/user_interaction/system_conf.py delete mode 100644 archinstall/lib/user_interaction/utils.py diff --git a/.github/workflows/python-build.yml b/.github/workflows/python-build.yml index f98ce160..950ff8f4 100644 --- a/.github/workflows/python-build.yml +++ b/.github/workflows/python-build.yml @@ -36,7 +36,10 @@ jobs: - name: Run archinstall run: | python -V - archinstall -v + archinstall --script guided -v + archinstall --script swiss -v + archinstall --script only_hd -v + archinstall --script minimal -v - uses: actions/upload-artifact@v3 with: name: archinstall diff --git a/README.md b/README.md index 720bd487..15646170 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ from archinstall.default_profiles.minimal import MinimalProfile from archinstall.lib.disk.device_model import FilesystemType from archinstall.lib.disk.encryption_menu import DiskEncryptionMenu from archinstall.lib.disk.filesystem import FilesystemHandler -from archinstall.lib.user_interaction.disk_conf import select_disk_config +from archinstall.lib.interactions.disk_conf import select_disk_config fs_type = FilesystemType('ext4') diff --git a/archinstall/__init__.py b/archinstall/__init__.py index 6f67d20f..992bd9fa 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -1,42 +1,75 @@ """Arch Linux installer - guided, templates etc.""" import importlib +import os from argparse import ArgumentParser, Namespace +from pathlib import Path +from typing import TYPE_CHECKING, Any, Dict, Union from .lib import disk from .lib import menu from .lib import models from .lib import packages - -from .lib.exceptions import * -from .lib.general import * -from .lib.hardware import * -from .lib.installer import __packages__, Installer, accessibility_tools_in_use -from .lib.locale_helpers import * -from .lib.luks import * -from .lib.mirrors import * -from .lib.networking import * -from .lib.output import * -from archinstall.lib.profile.profiles_handler import ProfileHandler, profile_handler -from .lib.profile.profile_menu import ProfileConfiguration -from .lib.services import * -from .lib.storage import * -from .lib.systemd import * -from .lib.user_interaction import * +from .lib import exceptions +from .lib import luks +from .lib import locale +from .lib import mirrors +from .lib import networking +from .lib import profile +from .lib import interactions +from . import default_profiles + +from .lib.hardware import SysInfo, AVAILABLE_GFX_DRIVERS +from .lib.installer import Installer, accessibility_tools_in_use +from .lib.output import ( + FormattedOutput, log, error, + check_log_permissions, debug, warn, info +) +from .lib.storage import storage from .lib.global_menu import GlobalMenu -from .lib.translationhandler import TranslationHandler, DeferredTranslation -from .lib.plugins import plugins, load_plugin # This initiates the plugin loading ceremony -from .lib.configuration import * +from .lib.boot import Boot +from .lib.translationhandler import TranslationHandler, Language, DeferredTranslation +from .lib.plugins import plugins, load_plugin +from .lib.configuration import ConfigurationOutput +from .lib.general import ( + generate_password, locate_binary, clear_vt100_escape_codes, + JsonEncoder, JSON, UNSAFE_JSON, SysCommandWorker, SysCommand, + run_custom_user_commands, json_stream_to_structure, secret +) + + +if TYPE_CHECKING: + _: Any -parser = ArgumentParser() __version__ = "2.5.6" storage['__version__'] = __version__ + # add the custome _ as a builtin, it can now be used anywhere in the # project to mark strings as translatable with _('translate me') DeferredTranslation.install() +check_log_permissions() + +# Log various information about hardware before starting the installation. This might assist in troubleshooting +debug(f"Hardware model detected: {SysInfo.sys_vendor()} {SysInfo.product_name()}; UEFI mode: {SysInfo.has_uefi()}") +debug(f"Processor model detected: {SysInfo.cpu_model()}") +debug(f"Memory statistics: {SysInfo.mem_available()} available out of {SysInfo.mem_total()} total installed") +debug(f"Virtualization detected: {SysInfo.virtualization()}; is VM: {SysInfo.is_vm()}") +debug(f"Graphics devices detected: {SysInfo._graphics_devices().keys()}") + +# For support reasons, we'll log the disk layout pre installation to match against post-installation layout +debug(f"Disk states before installing: {disk.disk_layouts()}") + + +if os.getuid() != 0: + print(_("Archinstall requires root privileges to run. See --help for more.")) + exit(1) + + +parser = ArgumentParser() + def define_arguments(): """ @@ -61,7 +94,7 @@ def define_arguments(): parser.add_argument("--plugin", nargs="?", type=str) -def parse_unspecified_argument_list(unknowns :list, multiple :bool = False, error :bool = False) -> dict: +def parse_unspecified_argument_list(unknowns :list, multiple :bool = False, err :bool = False) -> dict: """We accept arguments not defined to the parser. (arguments "ad hoc"). Internally argparse return to us a list of words so we have to parse its contents, manually. We accept following individual syntax for each argument @@ -105,14 +138,14 @@ def parse_unspecified_argument_list(unknowns :list, multiple :bool = False, erro config[last_key] = [config[last_key],element] else: config[last_key].append(element) - elif error: + elif err: raise ValueError(f"Entry {element} is not related to any argument") else: print(f" We ignore the entry {element} as it isn't related to any argument") return config -def cleanup_empty_args(args: Union[Namespace, dict]) -> dict: +def cleanup_empty_args(args: Union[Namespace, Dict]) -> Dict: """ Takes arguments (dictionary or argparse Namespace) and removes any None values. This ensures clean mergers during dict.update(args) @@ -190,14 +223,14 @@ def load_config(): arguments['disk_config'] = disk.DiskLayoutConfiguration.parse_arg(disk_config) if profile_config := arguments.get('profile_config', None): - arguments['profile_config'] = ProfileConfiguration.parse_arg(profile_config) + arguments['profile_config'] = profile.ProfileConfiguration.parse_arg(profile_config) if arguments.get('mirror-region', None) is not None: if type(arguments.get('mirror-region', None)) is dict: arguments['mirror-region'] = arguments.get('mirror-region', None) else: selected_region = arguments.get('mirror-region', None) - arguments['mirror-region'] = {selected_region: list_mirrors()[selected_region]} + arguments['mirror-region'] = {selected_region: mirrors.list_mirrors()[selected_region]} if arguments.get('servers', None) is not None: storage['_selected_servers'] = arguments.get('servers', None) @@ -230,7 +263,7 @@ def post_process_arguments(arguments): storage['MOUNT_POINT'] = Path(mountpoint) if arguments.get('debug', False): - log(f"Warning: --debug mode will write certain credentials to {storage['LOG_PATH']}/{storage['LOG_FILE']}!", fg="red", level=logging.WARNING) + warn(f"Warning: --debug mode will write certain credentials to {storage['LOG_PATH']}/{storage['LOG_FILE']}!") if arguments.get('plugin', None): path = arguments['plugin'] diff --git a/archinstall/default_profiles/desktop.py b/archinstall/default_profiles/desktop.py index 2351bd08..9d92f822 100644 --- a/archinstall/default_profiles/desktop.py +++ b/archinstall/default_profiles/desktop.py @@ -1,7 +1,7 @@ from typing import Any, TYPE_CHECKING, List, Optional, Dict from archinstall.lib import menu -from archinstall.lib.output import log +from archinstall.lib.output import info from archinstall.lib.profile.profiles_handler import profile_handler from archinstall.default_profiles.profile import Profile, ProfileType, SelectResult, GreeterType @@ -79,7 +79,7 @@ class DesktopProfile(Profile): install_session.add_additional_packages(self.packages) for profile in self._current_selection: - log(f'Installing profile {profile.name}...') + info(f'Installing profile {profile.name}...') install_session.add_additional_packages(profile.packages) install_session.enable_service(profile.services) diff --git a/archinstall/default_profiles/server.py b/archinstall/default_profiles/server.py index e240b3ef..ab758975 100644 --- a/archinstall/default_profiles/server.py +++ b/archinstall/default_profiles/server.py @@ -1,7 +1,6 @@ -import logging from typing import Any, TYPE_CHECKING, List -from archinstall.lib.output import log +from archinstall.lib.output import info from archinstall.lib.menu import MenuSelectionType from archinstall.lib.profile.profiles_handler import profile_handler from archinstall.default_profiles.profile import ProfileType, Profile, SelectResult, TProfile @@ -46,12 +45,12 @@ class ServerProfile(Profile): def install(self, install_session: 'Installer'): server_info = self.current_selection_names() details = ', '.join(server_info) - log(f'Now installing the selected servers: {details}', level=logging.INFO) + info(f'Now installing the selected servers: {details}') for server in self._current_selection: - log(f'Installing {server.name}...', level=logging.INFO) + info(f'Installing {server.name}...') install_session.add_additional_packages(server.packages) install_session.enable_service(server.services) server.install(install_session) - log('If your selections included multiple servers with the same port, you may have to reconfigure them.', fg="yellow", level=logging.INFO) + info('If your selections included multiple servers with the same port, you may have to reconfigure them.') diff --git a/archinstall/lib/boot.py b/archinstall/lib/boot.py new file mode 100644 index 00000000..62c50df3 --- /dev/null +++ b/archinstall/lib/boot.py @@ -0,0 +1,111 @@ +import time +from typing import Iterator, Optional +from .exceptions import SysCallError +from .general import SysCommand, SysCommandWorker, locate_binary +from .installer import Installer +from .output import error +from .storage import storage + + +class Boot: + def __init__(self, installation: Installer): + self.instance = installation + self.container_name = 'archinstall' + self.session: Optional[SysCommandWorker] = None + self.ready = False + + def __enter__(self) -> 'Boot': + if (existing_session := storage.get('active_boot', None)) and existing_session.instance != self.instance: + raise KeyError("Archinstall only supports booting up one instance, and a active session is already active and it is not this one.") + + if existing_session: + self.session = existing_session.session + self.ready = existing_session.ready + else: + # '-P' or --console=pipe could help us not having to do a bunch + # of os.write() calls, but instead use pipes (stdin, stdout and stderr) as usual. + self.session = SysCommandWorker([ + '/usr/bin/systemd-nspawn', + '-D', str(self.instance.target), + '--timezone=off', + '-b', + '--no-pager', + '--machine', self.container_name + ]) + + if not self.ready and self.session: + while self.session.is_alive(): + if b' login:' in self.session: + self.ready = True + break + + storage['active_boot'] = self + return self + + def __exit__(self, *args :str, **kwargs :str) -> None: + # b''.join(sys_command('sync')) # No need to, since the underlying fs() object will call sync. + # TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager + + if len(args) >= 2 and args[1]: + error( + args[1], + f"The error above occurred in a temporary boot-up of the installation {self.instance}" + ) + + shutdown = None + shutdown_exit_code: Optional[int] = -1 + + try: + shutdown = SysCommand(f'systemd-run --machine={self.container_name} --pty shutdown now') + except SysCallError as err: + shutdown_exit_code = err.exit_code + + if self.session: + while self.session.is_alive(): + time.sleep(0.25) + + if shutdown and shutdown.exit_code: + shutdown_exit_code = shutdown.exit_code + + if self.session and (self.session.exit_code == 0 or shutdown_exit_code == 0): + storage['active_boot'] = None + else: + session_exit_code = self.session.exit_code if self.session else -1 + + raise SysCallError( + f"Could not shut down temporary boot of {self.instance}: {session_exit_code}/{shutdown_exit_code}", + exit_code=next(filter(bool, [session_exit_code, shutdown_exit_code])) + ) + + def __iter__(self) -> Iterator[bytes]: + if self.session: + for value in self.session: + yield value + + def __contains__(self, key: bytes) -> bool: + if self.session is None: + return False + + return key in self.session + + def is_alive(self) -> bool: + if self.session is None: + return False + + return self.session.is_alive() + + def SysCommand(self, cmd: list, *args, **kwargs) -> SysCommand: + if cmd[0][0] != '/' and cmd[0][:2] != './': + # This check is also done in SysCommand & SysCommandWorker. + # However, that check is done for `machinectl` and not for our chroot command. + # So this wrapper for SysCommand will do this additionally. + + cmd[0] = locate_binary(cmd[0]) + + return SysCommand(["systemd-run", f"--machine={self.container_name}", "--pty", *cmd], *args, **kwargs) + + def SysCommandWorker(self, cmd: list, *args, **kwargs) -> SysCommandWorker: + if cmd[0][0] != '/' and cmd[0][:2] != './': + cmd[0] = locate_binary(cmd[0]) + + return SysCommandWorker(["systemd-run", f"--machine={self.container_name}", "--pty", *cmd], *args, **kwargs) diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py index 22c41c0d..c3af3a83 100644 --- a/archinstall/lib/configuration.py +++ b/archinstall/lib/configuration.py @@ -1,13 +1,13 @@ import os import json import stat -import logging from pathlib import Path from typing import Optional, Dict, Any, TYPE_CHECKING +from .menu import Menu, MenuSelectionType from .storage import storage -from .general import JSON, UNSAFE_JSON -from .output import log +from .general import JSON, UNSAFE_JSON, SysCommand +from .output import debug, info, warn if TYPE_CHECKING: _: Any @@ -69,18 +69,18 @@ class ConfigurationOutput: def show(self): print(_('\nThis is your chosen configuration:')) - log(" -- Chosen configuration --", level=logging.DEBUG) + debug(" -- Chosen configuration --") user_conig = self.user_config_to_json() - log(user_conig, level=logging.INFO) + info(user_conig) print() def _is_valid_path(self, dest_path: Path) -> bool: if (not dest_path.exists()) or not (dest_path.is_dir()): - log( - 'Destination directory {} does not exist or is not a directory,\n Configuration files can not be saved'.format(dest_path.resolve()), - fg="yellow" + warn( + f'Destination directory {dest_path.resolve()} does not exist or is not a directory\n.', + 'Configuration files can not be saved' ) return False return True @@ -111,3 +111,101 @@ class ConfigurationOutput: if self._is_valid_path(dest_path): self.save_user_config(dest_path) self.save_user_creds(dest_path) + + +def save_config(config: Dict): + def preview(selection: str): + if options['user_config'] == selection: + serialized = config_output.user_config_to_json() + return f'{config_output.user_configuration_file}\n{serialized}' + elif options['user_creds'] == selection: + if maybe_serial := config_output.user_credentials_to_json(): + return f'{config_output.user_credentials_file}\n{maybe_serial}' + else: + return str(_('No configuration')) + elif options['all'] == selection: + output = f'{config_output.user_configuration_file}\n' + if config_output.user_credentials_to_json(): + output += f'{config_output.user_credentials_file}\n' + return output[:-1] + return None + + config_output = ConfigurationOutput(config) + + options = { + 'user_config': str(_('Save user configuration')), + 'user_creds': str(_('Save user credentials')), + 'disk_layout': str(_('Save disk layout')), + 'all': str(_('Save all')) + } + + choice = Menu( + _('Choose which configuration to save'), + list(options.values()), + sort=False, + skip=True, + preview_size=0.75, + preview_command=preview + ).run() + + if choice.type_ == MenuSelectionType.Skip: + return + + save_config_value = choice.single_value + saving_key = [k for k, v in options.items() if v == save_config_value][0] + + dirs_to_exclude = [ + '/bin', + '/dev', + '/lib', + '/lib64', + '/lost+found', + '/opt', + '/proc', + '/run', + '/sbin', + '/srv', + '/sys', + '/usr', + '/var', + ] + + debug('Ignore configuration option folders: ' + ','.join(dirs_to_exclude)) + info(_('Finding possible directories to save configuration files ...')) + + find_exclude = '-path ' + ' -prune -o -path '.join(dirs_to_exclude) + ' -prune ' + file_picker_command = f'find / {find_exclude} -o -type d -print0' + + directories = SysCommand(file_picker_command).decode() + + if directories is None: + raise ValueError('Failed to retrieve possible configuration directories') + + possible_save_dirs = list(filter(None, directories.split('\x00'))) + + selection = Menu( + _('Select directory (or directories) for saving configuration files'), + possible_save_dirs, + multi=True, + skip=True, + allow_reset=False, + ).run() + + match selection.type_: + case MenuSelectionType.Skip: + return + + save_dirs = selection.multi_value + + debug(f'Saving {saving_key} configuration files to {save_dirs}') + + if save_dirs is not None: + for save_dir_str in save_dirs: + save_dir = Path(save_dir_str) + if options['user_config'] == save_config_value: + config_output.save_user_config(save_dir) + elif options['user_creds'] == save_config_value: + config_output.save_user_creds(save_dir) + elif options['all'] == save_config_value: + config_output.save_user_config(save_dir) + config_output.save_user_creds(save_dir) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index 13bde77a..4341c53c 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -1,7 +1,6 @@ from __future__ import annotations import json -import logging import os import time from pathlib import Path @@ -24,7 +23,7 @@ from .device_model import ( from ..exceptions import DiskError, UnknownFilesystemFormat from ..general import SysCommand, SysCallError, JSON from ..luks import Luks2 -from ..output import log +from ..output import debug, error, info, warn from ..utils.util import is_subpath if TYPE_CHECKING: @@ -48,11 +47,11 @@ class DeviceHandler(object): for device in getAllDevices(): try: disk = Disk(device) - except DiskLabelException as error: - if 'unrecognised disk label' in getattr(error, 'message', str(error)): + except DiskLabelException as err: + if 'unrecognised disk label' in getattr(error, 'message', str(err)): disk = freshDisk(device, PartitionTable.GPT.value) else: - log(f'Unable to get disk from device: {device}', level=logging.DEBUG) + debug(f'Unable to get disk from device: {device}') continue device_info = _DeviceInfo.from_disk(disk) @@ -93,7 +92,7 @@ class DeviceHandler(object): return FilesystemType(lsblk_info.fstype) if lsblk_info.fstype else None return None except ValueError: - log(f'Could not determine the filesystem: {partition.fileSystem}', level=logging.DEBUG) + debug(f'Could not determine the filesystem: {partition.fileSystem}') return None @@ -137,7 +136,7 @@ class DeviceHandler(object): try: result = SysCommand(f'btrfs subvolume list {mountpoint}') except SysCallError as err: - log(f'Failed to read btrfs subvolume information: {err}', level=logging.DEBUG) + debug(f'Failed to read btrfs subvolume information: {err}') return subvol_infos try: @@ -150,7 +149,7 @@ class DeviceHandler(object): sub_vol_mountpoint = lsblk_info.btrfs_subvol_info.get(name, None) subvol_infos.append(_BtrfsSubvolumeInfo(name, sub_vol_mountpoint)) except json.decoder.JSONDecodeError as err: - log(f"Could not decode lsblk JSON: {result}", fg="red", level=logging.ERROR) + error(f"Could not decode lsblk JSON: {result}") raise err if not lsblk_info.mountpoint: @@ -203,14 +202,14 @@ class DeviceHandler(object): options += additional_parted_options options_str = ' '.join(options) - log(f'Formatting filesystem: /usr/bin/{command} {options_str} {path}') + info(f'Formatting filesystem: /usr/bin/{command} {options_str} {path}') try: SysCommand(f"/usr/bin/{command} {options_str} {path}") - except SysCallError as error: - msg = f'Could not format {path} with {fs_type.value}: {error.message}' - log(msg, fg='red') - raise DiskError(msg) from error + except SysCallError as err: + msg = f'Could not format {path} with {fs_type.value}: {err.message}' + error(msg) + raise DiskError(msg) from err def _perform_enc_formatting( self, @@ -227,16 +226,16 @@ class DeviceHandler(object): key_file = luks_handler.encrypt() - log(f'Unlocking luks2 device: {dev_path}', level=logging.DEBUG) + debug(f'Unlocking luks2 device: {dev_path}') luks_handler.unlock(key_file=key_file) if not luks_handler.mapper_dev: raise DiskError('Failed to unlock luks device') - log(f'luks2 formatting mapper dev: {luks_handler.mapper_dev}', level=logging.INFO) + info(f'luks2 formatting mapper dev: {luks_handler.mapper_dev}') self._perform_formatting(fs_type, luks_handler.mapper_dev) - log(f'luks2 locking device: {dev_path}', level=logging.INFO) + info(f'luks2 locking device: {dev_path}') luks_handler.lock() def format( @@ -285,7 +284,7 @@ class DeviceHandler(object): # when we require a delete and the partition to be (re)created # already exists then we have to delete it first if requires_delete and part_mod.status in [ModificationStatus.Modify, ModificationStatus.Delete]: - log(f'Delete existing partition: {part_mod.safe_dev_path}', level=logging.INFO) + info(f'Delete existing partition: {part_mod.safe_dev_path}') part_info = self.find_partition(part_mod.safe_dev_path) if not part_info: @@ -325,9 +324,9 @@ class DeviceHandler(object): for flag in part_mod.flags: partition.setFlag(flag.value) - log(f'\tType: {part_mod.type.value}', level=logging.DEBUG) - log(f'\tFilesystem: {part_mod.fs_type.value}', level=logging.DEBUG) - log(f'\tGeometry: {start_sector.value} start sector, {length_sector.value} length', level=logging.DEBUG) + debug(f'\tType: {part_mod.type.value}') + debug(f'\tFilesystem: {part_mod.fs_type.value}') + debug(f'\tGeometry: {start_sector.value} start sector, {length_sector.value} length') try: disk.addPartition(partition=partition, constraint=disk.device.optimalAlignedConstraint) @@ -339,41 +338,41 @@ class DeviceHandler(object): # the partition has a real path now as it was created part_mod.dev_path = Path(partition.path) - info = self._fetch_partuuid(part_mod.dev_path) + lsblk_info = self._fetch_partuuid(part_mod.dev_path) - part_mod.partuuid = info.partuuid - part_mod.uuid = info.uuid + part_mod.partuuid = lsblk_info.partuuid + part_mod.uuid = lsblk_info.uuid except PartitionException as ex: raise DiskError(f'Unable to add partition, most likely due to overlapping sectors: {ex}') from ex def _fetch_partuuid(self, path: Path) -> LsblkInfo: attempts = 3 - info: Optional[LsblkInfo] = None + lsblk_info: Optional[LsblkInfo] = None self.partprobe(path) for attempt_nr in range(attempts): time.sleep(attempt_nr + 1) - info = get_lsblk_info(path) + lsblk_info = get_lsblk_info(path) - if info.partuuid: + if lsblk_info.partuuid: break self.partprobe(path) - if not info or not info.partuuid: - log(f'Unable to determine new partition uuid: {path}\n{info}', level=logging.DEBUG) + if not lsblk_info or not lsblk_info.partuuid: + debug(f'Unable to determine new partition uuid: {path}\n{lsblk_info}') raise DiskError(f'Unable to determine new partition uuid: {path}') - log(f'partuuid found: {info.json()}', level=logging.DEBUG) + debug(f'partuuid found: {lsblk_info.json()}') - return info + return lsblk_info def create_btrfs_volumes( self, part_mod: PartitionModification, enc_conf: Optional['DiskEncryption'] = None ): - log(f'Creating subvolumes: {part_mod.safe_dev_path}', level=logging.INFO) + info(f'Creating subvolumes: {part_mod.safe_dev_path}') luks_handler = None @@ -396,7 +395,7 @@ class DeviceHandler(object): self.mount(part_mod.safe_dev_path, self._TMP_BTRFS_MOUNT, create_target_mountpoint=True) for sub_vol in part_mod.btrfs_subvols: - log(f'Creating subvolume: {sub_vol.name}', level=logging.DEBUG) + debug(f'Creating subvolume: {sub_vol.name}') if luks_handler is not None: subvol_path = self._TMP_BTRFS_MOUNT / sub_vol.name @@ -408,14 +407,14 @@ class DeviceHandler(object): if sub_vol.nodatacow: try: SysCommand(f'chattr +C {subvol_path}') - except SysCallError as error: - raise DiskError(f'Could not set nodatacow attribute at {subvol_path}: {error}') + except SysCallError as err: + raise DiskError(f'Could not set nodatacow attribute at {subvol_path}: {err}') if sub_vol.compress: try: SysCommand(f'chattr +c {subvol_path}') - except SysCallError as error: - raise DiskError(f'Could not set compress attribute at {subvol_path}: {error}') + except SysCallError as err: + raise DiskError(f'Could not set compress attribute at {subvol_path}: {err}') if luks_handler is not None and luks_handler.mapper_dev is not None: self.umount(luks_handler.mapper_dev) @@ -435,12 +434,12 @@ class DeviceHandler(object): return luks_handler def _umount_all_existing(self, modification: DeviceModification): - log(f'Unmounting all partitions: {modification.device_path}', level=logging.INFO) + info(f'Unmounting all partitions: {modification.device_path}') existing_partitions = self._devices[modification.device_path].partition_infos for partition in existing_partitions: - log(f'Unmounting: {partition.path}', level=logging.DEBUG) + debug(f'Unmounting: {partition.path}') # un-mount for existing encrypted partitions if partition.fs_type == FilesystemType.Crypto_luks: @@ -472,10 +471,10 @@ class DeviceHandler(object): part_table = partition_table.value if partition_table else None disk = freshDisk(modification.device.disk.device, part_table) else: - log(f'Use existing device: {modification.device_path}') + info(f'Use existing device: {modification.device_path}') disk = modification.device.disk - log(f'Creating partitions: {modification.device_path}') + info(f'Creating partitions: {modification.device_path}') # TODO sort by delete first @@ -507,7 +506,7 @@ class DeviceHandler(object): lsblk_info = get_lsblk_info(dev_path) if target_mountpoint in lsblk_info.mountpoints: - log(f'Device already mounted at {target_mountpoint}') + info(f'Device already mounted at {target_mountpoint}') return str_options = ','.join(options) @@ -517,7 +516,7 @@ class DeviceHandler(object): command = f'mount {mount_fs} {str_options} {dev_path} {target_mountpoint}' - log(f'Mounting {dev_path}: command', level=logging.DEBUG) + debug(f'Mounting {dev_path}: command') try: SysCommand(command) @@ -536,10 +535,10 @@ class DeviceHandler(object): raise ex if len(lsblk_info.mountpoints) > 0: - log(f'Partition {mountpoint} is currently mounted at: {[str(m) for m in lsblk_info.mountpoints]}', level=logging.DEBUG) + debug(f'Partition {mountpoint} is currently mounted at: {[str(m) for m in lsblk_info.mountpoints]}') for mountpoint in lsblk_info.mountpoints: - log(f'Unmounting mountpoint: {mountpoint}', level=logging.DEBUG) + debug(f'Unmounting mountpoint: {mountpoint}') command = 'umount' @@ -574,10 +573,10 @@ class DeviceHandler(object): command = 'partprobe' try: - log(f'Calling partprobe: {command}', level=logging.DEBUG) + debug(f'Calling partprobe: {command}') SysCommand(command) - except SysCallError as error: - log(f'"{command}" failed to run: {error}', level=logging.DEBUG) + except SysCallError as err: + error(f'"{command}" failed to run: {err}') def _wipe(self, dev_path: Path): """ @@ -594,7 +593,7 @@ class DeviceHandler(object): This is not intended to be secure, but rather to ensure that auto-discovery tools don't recognize anything here. """ - log(f'Wiping partitions and metadata: {block_device.device_info.path}') + info(f'Wiping partitions and metadata: {block_device.device_info.path}') for partition in block_device.partition_infos: self._wipe(partition.path) @@ -609,8 +608,8 @@ def disk_layouts() -> str: lsblk_info = get_all_lsblk_info() return json.dumps(lsblk_info, indent=4, sort_keys=True, cls=JSON) except SysCallError as err: - log(f"Could not return disk layouts: {err}", level=logging.WARNING, fg="yellow") + warn(f"Could not return disk layouts: {err}") return '' except json.decoder.JSONDecodeError as err: - log(f"Could not return disk layouts: {err}", level=logging.WARNING, fg="yellow") + warn(f"Could not return disk layouts: {err}") return '' diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index d57347b7..36dd0c4f 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -2,7 +2,6 @@ from __future__ import annotations import dataclasses import json -import logging import math import time import uuid @@ -18,7 +17,7 @@ from parted import Disk, Geometry, Partition from ..exceptions import DiskError, SysCallError from ..general import SysCommand -from ..output import log +from ..output import debug, error from ..storage import storage if TYPE_CHECKING: @@ -282,7 +281,7 @@ class _PartitionInfo: btrfs_subvol_infos: List[_BtrfsSubvolumeInfo] = field(default_factory=list) def as_json(self) -> Dict[str, Any]: - info = { + part_info = { 'Name': self.name, 'Type': self.type.value, 'Filesystem': self.fs_type.value if self.fs_type else str(_('Unknown')), @@ -293,9 +292,9 @@ class _PartitionInfo: } if self.btrfs_subvol_infos: - info['Btrfs vol.'] = f'{len(self.btrfs_subvol_infos)} subvolumes' + part_info['Btrfs vol.'] = f'{len(self.btrfs_subvol_infos)} subvolumes' - return info + return part_info @classmethod def from_partition( @@ -392,7 +391,7 @@ class SubvolumeModification: mods = [] for entry in subvol_args: if not entry.get('name', None) or not entry.get('mountpoint', None): - log(f'Subvolume arg is missing name: {entry}', level=logging.DEBUG) + debug(f'Subvolume arg is missing name: {entry}') continue mountpoint = Path(entry['mountpoint']) if entry['mountpoint'] else None @@ -705,7 +704,7 @@ class PartitionModification: """ Called for displaying data in table format """ - info = { + part_mod = { 'Status': self.status.value, 'Device': str(self.dev_path) if self.dev_path else '', 'Type': self.type.value, @@ -718,9 +717,9 @@ class PartitionModification: } if self.btrfs_subvols: - info['Btrfs vol.'] = f'{len(self.btrfs_subvols)} subvolumes' + part_mod['Btrfs vol.'] = f'{len(self.btrfs_subvols)} subvolumes' - return info + return part_mod @dataclass @@ -916,36 +915,36 @@ class LsblkInfo: @classmethod def from_json(cls, blockdevice: Dict[str, Any]) -> LsblkInfo: - info = cls() + lsblk_info = cls() for f in cls.fields(): lsblk_field = _clean_field(f, CleanType.Blockdevice) data_field = _clean_field(f, CleanType.Dataclass) val: Any = None - if isinstance(getattr(info, data_field), Path): + if isinstance(getattr(lsblk_info, data_field), Path): val = Path(blockdevice[lsblk_field]) - elif isinstance(getattr(info, data_field), Size): + elif isinstance(getattr(lsblk_info, data_field), Size): val = Size(blockdevice[lsblk_field], Unit.B) else: val = blockdevice[lsblk_field] - setattr(info, data_field, val) + setattr(lsblk_info, data_field, val) - info.children = [LsblkInfo.from_json(child) for child in blockdevice.get('children', [])] + lsblk_info.children = [LsblkInfo.from_json(child) for child in blockdevice.get('children', [])] # sometimes lsblk returns 'mountpoints': [null] - info.mountpoints = [Path(mnt) for mnt in info.mountpoints if mnt] + lsblk_info.mountpoints = [Path(mnt) for mnt in lsblk_info.mountpoints if mnt] fs_roots = [] - for r in info.fsroots: + for r in lsblk_info.fsroots: if r: path = Path(r) # store the fsroot entries without the leading / fs_roots.append(path.relative_to(path.anchor)) - info.fsroots = fs_roots + lsblk_info.fsroots = fs_roots - return info + return lsblk_info class CleanType(Enum): @@ -978,16 +977,16 @@ def _fetch_lsblk_info(dev_path: Optional[Union[Path, str]] = None, retry: int = try: result = SysCommand(f'lsblk --json -b -o+{lsblk_fields} {dev_path}') break - except SysCallError as error: + except SysCallError as err: # Get the output minus the message/info from lsblk if it returns a non-zero exit code. - if error.worker: - err = error.worker.decode('UTF-8') - log(f'Error calling lsblk: {err}', level=logging.DEBUG) + if err.worker: + err_str = err.worker.decode('UTF-8') + debug(f'Error calling lsblk: {err_str}') else: - raise error + raise err if retry_attempt == retry - 1: - raise error + raise err time.sleep(1) @@ -997,11 +996,12 @@ def _fetch_lsblk_info(dev_path: Optional[Union[Path, str]] = None, retry: int = blockdevices = block_devices['blockdevices'] return [LsblkInfo.from_json(device) for device in blockdevices] except json.decoder.JSONDecodeError as err: - log(f"Could not decode lsblk JSON: {result}", fg="red", level=logging.ERROR) + error(f"Could not decode lsblk JSON: {result}") raise err raise DiskError(f'Failed to read disk "{dev_path}" with lsblk') + def get_lsblk_info(dev_path: Union[Path, str]) -> LsblkInfo: if infos := _fetch_lsblk_info(dev_path): return infos[0] diff --git a/archinstall/lib/disk/encryption_menu.py b/archinstall/lib/disk/encryption_menu.py index 285270fb..8c64e65e 100644 --- a/archinstall/lib/disk/encryption_menu.py +++ b/archinstall/lib/disk/encryption_menu.py @@ -13,7 +13,7 @@ from ..menu import ( MenuSelectionType, TableMenu ) -from ..user_interaction.utils import get_password +from ..interactions.utils import get_password from ..menu import Menu from ..general import secret from .fido import Fido2Device, Fido2 diff --git a/archinstall/lib/disk/fido.py b/archinstall/lib/disk/fido.py index 2a53b551..97c38d84 100644 --- a/archinstall/lib/disk/fido.py +++ b/archinstall/lib/disk/fido.py @@ -1,13 +1,12 @@ from __future__ import annotations import getpass -import logging from pathlib import Path from typing import List, Optional from .device_model import PartitionModification, Fido2Device from ..general import SysCommand, SysCommandWorker, clear_vt100_escape_codes -from ..output import log +from ..output import error, info class Fido2: @@ -39,7 +38,7 @@ class Fido2: if not cls._loaded or reload: ret: Optional[str] = SysCommand(f"systemd-cryptenroll --fido2-device=list").decode('UTF-8') if not ret: - log('Unable to retrieve fido2 devices', level=logging.ERROR) + error('Unable to retrieve fido2 devices') return [] fido_devices: str = clear_vt100_escape_codes(ret) # type: ignore @@ -88,8 +87,4 @@ class Fido2: worker.write(bytes(getpass.getpass(" "), 'UTF-8')) pin_inputted = True - log( - f"You might need to touch the FIDO2 device to unlock it if no prompt comes up after 3 seconds.", - level=logging.INFO, - fg="yellow" - ) + info('You might need to touch the FIDO2 device to unlock it if no prompt comes up after 3 seconds') diff --git a/archinstall/lib/disk/filesystem.py b/archinstall/lib/disk/filesystem.py index 6ea99340..dc99afce 100644 --- a/archinstall/lib/disk/filesystem.py +++ b/archinstall/lib/disk/filesystem.py @@ -1,6 +1,5 @@ from __future__ import annotations -import logging import signal import sys import time @@ -8,8 +7,8 @@ from typing import Any, Optional, TYPE_CHECKING from .device_model import DiskLayoutConfiguration, DiskLayoutType, PartitionTable, FilesystemType, DiskEncryption from .device_handler import device_handler -from ..hardware import has_uefi -from ..output import log +from ..hardware import SysInfo +from ..output import debug from ..menu import Menu if TYPE_CHECKING: @@ -27,13 +26,13 @@ class FilesystemHandler: def perform_filesystem_operations(self, show_countdown: bool = True): if self._disk_config.config_type == DiskLayoutType.Pre_mount: - log('Disk layout configuration is set to pre-mount, not performing any operations', level=logging.DEBUG) + debug('Disk layout configuration is set to pre-mount, not performing any operations') return device_mods = list(filter(lambda x: len(x.partitions) > 0, self._disk_config.device_modifications)) if not device_mods: - log('No modifications required', level=logging.DEBUG) + debug('No modifications required') return device_paths = ', '.join([str(mod.device.device_info.path) for mod in device_mods]) @@ -48,7 +47,7 @@ class FilesystemHandler: # Setup the blockdevice, filesystem (and optionally encryption). # Once that's done, we'll hand over to perform_installation() partition_table = PartitionTable.GPT - if has_uefi() is False: + if SysInfo.has_uefi() is False: partition_table = PartitionTable.MBR for mod in device_mods: diff --git a/archinstall/lib/disk/partitioning_menu.py b/archinstall/lib/disk/partitioning_menu.py index 686e8c29..89cf6293 100644 --- a/archinstall/lib/disk/partitioning_menu.py +++ b/archinstall/lib/disk/partitioning_menu.py @@ -1,13 +1,12 @@ from __future__ import annotations -import logging from pathlib import Path from typing import Any, Dict, TYPE_CHECKING, List, Optional, Tuple from .device_model import PartitionModification, FilesystemType, BDevice, Size, Unit, PartitionType, PartitionFlag, \ ModificationStatus from ..menu import Menu, ListManager, MenuSelection, TextInput -from ..output import FormattedOutput, log +from ..output import FormattedOutput, warn from .subvolume_menu import SubvolumeMenu if TYPE_CHECKING: @@ -229,7 +228,7 @@ class PartitioningList(ListManager): if not start_sector or self._validate_sector(start_sector): break - log(f'Invalid start sector entered: {start_sector}', fg='red', level=logging.INFO) + warn(f'Invalid start sector entered: {start_sector}') if not start_sector: start_sector = str(largest_free_area.start) @@ -245,7 +244,7 @@ class PartitioningList(ListManager): if not end_value or self._validate_sector(start_sector, end_value): break - log(f'Invalid end sector entered: {start_sector}', fg='red', level=logging.INFO) + warn(f'Invalid end sector entered: {start_sector}') # override the default value with the user value if end_value: @@ -300,7 +299,7 @@ class PartitioningList(ListManager): if choice.value == Menu.no(): return [] - from ..user_interaction.disk_conf import suggest_single_disk_layout + from ..interactions.disk_conf import suggest_single_disk_layout device_modification = suggest_single_disk_layout(self._device) return device_modification.partitions diff --git a/archinstall/lib/exceptions.py b/archinstall/lib/exceptions.py index a66e4e04..53458d2c 100644 --- a/archinstall/lib/exceptions.py +++ b/archinstall/lib/exceptions.py @@ -3,6 +3,7 @@ from typing import Optional, TYPE_CHECKING if TYPE_CHECKING: from .general import SysCommandWorker + class RequirementError(BaseException): pass @@ -15,10 +16,6 @@ class UnknownFilesystemFormat(BaseException): pass -class ProfileError(BaseException): - pass - - class SysCallError(BaseException): def __init__(self, message :str, exit_code :Optional[int] = None, worker :Optional['SysCommandWorker'] = None) -> None: super(SysCallError, self).__init__(message) @@ -27,22 +24,10 @@ class SysCallError(BaseException): self.worker = worker -class PermissionError(BaseException): - pass - - -class ProfileNotFound(BaseException): - pass - - class HardwareIncompatibilityError(BaseException): pass -class UserError(BaseException): - pass - - class ServiceException(BaseException): pass @@ -51,9 +36,5 @@ class PackageError(BaseException): pass -class TranslationError(BaseException): - pass - - class Deprecated(BaseException): - pass \ No newline at end of file + pass diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py index 997b7d67..777ee90e 100644 --- a/archinstall/lib/general.py +++ b/archinstall/lib/general.py @@ -1,8 +1,6 @@ from __future__ import annotations -import hashlib import json -import logging import os import secrets import shlex @@ -18,9 +16,10 @@ import urllib.error import pathlib from datetime import datetime, date from typing import Callable, Optional, Dict, Any, List, Union, Iterator, TYPE_CHECKING +from select import epoll, EPOLLIN, EPOLLHUP from .exceptions import RequirementError, SysCallError -from .output import log +from .output import debug, error, info from .storage import storage @@ -28,42 +27,6 @@ if TYPE_CHECKING: from .installer import Installer -if sys.platform == 'linux': - from select import epoll, EPOLLIN, EPOLLHUP -else: - import select - EPOLLIN = 0 - EPOLLHUP = 0 - - class epoll(): - """ #!if windows - Create a epoll() implementation that simulates the epoll() behavior. - This so that the rest of the code doesn't need to worry weither we're using select() or epoll(). - """ - def __init__(self) -> None: - self.sockets: Dict[str, Any] = {} - self.monitoring: Dict[int, Any] = {} - - def unregister(self, fileno :int, *args :List[Any], **kwargs :Dict[str, Any]) -> None: - try: - del(self.monitoring[fileno]) # noqa: E275 - except: - pass - - def register(self, fileno :int, *args :int, **kwargs :Dict[str, Any]) -> None: - self.monitoring[fileno] = True - - def poll(self, timeout: float = 0.05, *args :str, **kwargs :Dict[str, Any]) -> List[Any]: - try: - return [[fileno, 1] for fileno in select.select(list(self.monitoring.keys()), [], [], timeout)[0]] - except OSError: - return [] - - -def gen_uid(entropy_length :int = 256) -> str: - return hashlib.sha512(os.urandom(entropy_length)).hexdigest() - - def generate_password(length :int = 64) -> str: haystack = string.printable # digits, ascii_letters, punctiation (!"#$[] etc) and whitespace return ''.join(secrets.choice(haystack) for i in range(length)) @@ -156,6 +119,7 @@ class JsonEncoder: else: return JsonEncoder._encode(obj) + class JSON(json.JSONEncoder, json.JSONDecoder): """ A safe JSON encoder that will omit private information in dicts (starting with !) @@ -166,6 +130,7 @@ class JSON(json.JSONEncoder, json.JSONDecoder): def encode(self, obj :Any) -> Any: return super(JSON, self).encode(self._encode(obj)) + class UNSAFE_JSON(json.JSONEncoder, json.JSONDecoder): """ UNSAFE_JSON will call/encode and keep private information in dicts (starting with !) @@ -269,7 +234,7 @@ class SysCommandWorker: sys.stdout.flush() if len(args) >= 2 and args[1]: - log(args[1], level=logging.DEBUG, fg='red') + debug(args[1]) if self.exit_code != 0: raise SysCallError( @@ -350,7 +315,7 @@ class SysCommandWorker: self.ended = time.time() break - if self.ended or (got_output is False and pid_exists(self.pid) is False): + if self.ended or (got_output is False and _pid_exists(self.pid) is False): self.ended = time.time() try: wait_status = os.waitpid(self.pid, 0)[1] @@ -396,15 +361,15 @@ class SysCommandWorker: pass except Exception as e: exception_type = type(e).__name__ - log(f"Unexpected {exception_type} occurred in {self.cmd}: {e}", level=logging.ERROR) + error(f"Unexpected {exception_type} occurred in {self.cmd}: {e}") raise e os.execve(self.cmd[0], list(self.cmd), {**os.environ, **self.environment_vars}) if storage['arguments'].get('debug'): - log(f"Executing: {self.cmd}", level=logging.DEBUG) + debug(f"Executing: {self.cmd}") except FileNotFoundError: - log(f"{self.cmd[0]} does not exist.", level=logging.ERROR, fg="red") + error(f"{self.cmd[0]} does not exist.") self.exit_code = 1 return False else: @@ -455,7 +420,7 @@ class SysCommand: # TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager if len(args) >= 2 and args[1]: - log(args[1], level=logging.ERROR, fg='red') + error(args[1]) def __iter__(self, *args :List[Any], **kwargs :Dict[str, Any]) -> Iterator[bytes]: if self.session: @@ -535,22 +500,7 @@ class SysCommand: return None -def prerequisite_check() -> bool: - """ - This function is used as a safety check before - continuing with an installation. - - Could be anything from checking that /boot is big enough - to check if nvidia hardware exists when nvidia driver was chosen. - """ - - return True - -def reboot(): - SysCommand("/usr/bin/reboot") - - -def pid_exists(pid: int) -> bool: +def _pid_exists(pid: int) -> bool: try: return any(subprocess.check_output(['/usr/bin/ps', '--no-headers', '-o', 'pid', '-p', str(pid)]).strip()) except subprocess.CalledProcessError: @@ -559,7 +509,7 @@ def pid_exists(pid: int) -> bool: def run_custom_user_commands(commands :List[str], installation :Installer) -> None: for index, command in enumerate(commands): - log(f'Executing custom command "{command}" ...', level=logging.INFO) + info(f'Executing custom command "{command}" ...') with open(f"{installation.target}/var/tmp/user-command.{index}.sh", "w") as temp_script: temp_script.write(command) @@ -568,6 +518,7 @@ def run_custom_user_commands(commands :List[str], installation :Installer) -> No os.unlink(f"{installation.target}/var/tmp/user-command.{index}.sh") + def json_stream_to_structure(configuration_identifier : str, stream :str, target :dict) -> bool : """ Function to load a stream (file (as name) or valid JSON string into an existing dictionary @@ -582,16 +533,16 @@ def json_stream_to_structure(configuration_identifier : str, stream :str, target try: with urllib.request.urlopen(urllib.request.Request(stream, headers={'User-Agent': 'ArchInstall'})) as response: target.update(json.loads(response.read())) - except urllib.error.HTTPError as error: - log(f"Could not load {configuration_identifier} via {parsed_url} due to: {error}", level=logging.ERROR, fg="red") + except urllib.error.HTTPError as err: + error(f"Could not load {configuration_identifier} via {parsed_url} due to: {err}") return False else: if pathlib.Path(stream).exists(): try: with pathlib.Path(stream).open() as fh: target.update(json.load(fh)) - except Exception as error: - log(f"{configuration_identifier} = {stream} does not contain a valid JSON format: {error}", level=logging.ERROR, fg="red") + except Exception as err: + error(f"{configuration_identifier} = {stream} does not contain a valid JSON format: {err}") return False else: # NOTE: This is a rudimentary check if what we're trying parse is a dict structure. @@ -600,14 +551,15 @@ def json_stream_to_structure(configuration_identifier : str, stream :str, target try: target.update(json.loads(stream)) except Exception as e: - log(f" {configuration_identifier} Contains an invalid JSON format : {e}",level=logging.ERROR, fg="red") + error(f"{configuration_identifier} Contains an invalid JSON format: {e}") return False else: - log(f" {configuration_identifier} is neither a file nor is a JSON string:",level=logging.ERROR, fg="red") + error(f"{configuration_identifier} is neither a file nor is a JSON string") return False return True + def secret(x :str): """ return * with len equal to to the input string """ return '*' * len(x) diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index a969d93f..13595132 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -11,24 +11,24 @@ from .models.users import User from .output import FormattedOutput from .profile.profile_menu import ProfileConfiguration from .storage import storage -from .user_interaction import add_number_of_parrallel_downloads -from .user_interaction import ask_additional_packages_to_install -from .user_interaction import ask_for_additional_users -from .user_interaction import ask_for_audio_selection -from .user_interaction import ask_for_bootloader -from .user_interaction import ask_for_swap -from .user_interaction import ask_hostname -from .user_interaction import ask_ntp -from .user_interaction import ask_to_configure_network -from .user_interaction import get_password, ask_for_a_timezone -from .user_interaction import select_additional_repositories -from .user_interaction import select_kernel -from .user_interaction import select_language -from .user_interaction import select_locale_enc -from .user_interaction import select_locale_lang -from .user_interaction import select_mirror_regions -from .user_interaction.disk_conf import select_disk_config -from .user_interaction.save_conf import save_config +from .configuration import save_config +from .interactions import add_number_of_parrallel_downloads +from .interactions import ask_additional_packages_to_install +from .interactions import ask_for_additional_users +from .interactions import ask_for_audio_selection +from .interactions import ask_for_bootloader +from .interactions import ask_for_swap +from .interactions import ask_hostname +from .interactions import ask_to_configure_network +from .interactions import get_password, ask_for_a_timezone +from .interactions import select_additional_repositories +from .interactions import select_kernel +from .interactions import select_language +from .interactions import select_locale_enc +from .interactions import select_locale_lang +from .interactions import select_mirror_regions +from .interactions import ask_ntp +from .interactions.disk_conf import select_disk_config if TYPE_CHECKING: _: Any diff --git a/archinstall/lib/hardware.py b/archinstall/lib/hardware.py index 3759725f..b95301f9 100644 --- a/archinstall/lib/hardware.py +++ b/archinstall/lib/hardware.py @@ -1,27 +1,12 @@ import os -import logging -from functools import partial +from functools import cached_property from pathlib import Path -from typing import Iterator, Optional, Dict +from typing import Optional, Dict from .general import SysCommand from .networking import list_interfaces, enrich_iface_types from .exceptions import SysCallError -from .output import log - -__packages__ = [ - "mesa", - "xf86-video-amdgpu", - "xf86-video-ati", - "xf86-video-nouveau", - "xf86-video-vmware", - "libva-mesa-driver", - "libva-intel-driver", - "intel-media-driver", - "vulkan-radeon", - "vulkan-intel", - "nvidia", -] +from .output import debug AVAILABLE_GFX_DRIVERS = { # Sub-dicts are layer-2 options to be selected @@ -62,136 +47,125 @@ AVAILABLE_GFX_DRIVERS = { } -def cpuinfo() -> Iterator[dict[str, str]]: - """ - Yields information about the CPUs of the system - """ - cpu_info_path = Path("/proc/cpuinfo") - cpu: Dict[str, str] = {} +class _SysInfo: + def __init__(self): + pass - with cpu_info_path.open() as file: - for line in file: - if not (line := line.strip()): - yield cpu - cpu = {} - continue - - key, value = line.split(":", maxsplit=1) - cpu[key.strip()] = value.strip() - - -def all_meminfo() -> Dict[str, int]: - """ - Returns a dict with memory info if called with no args - or the value of the given key of said dict. - """ - mem_info_path = Path("/proc/meminfo") - mem_info: Dict[str, int] = {} - - with mem_info_path.open() as file: - for line in file: - key, value = line.strip().split(':') - num = value.split()[0] - mem_info[key] = int(num) - - return mem_info - - -def meminfo_for_key(key: str) -> int: - info = all_meminfo() - return info[key] - - -def has_wifi() -> bool: - ifaces = list(list_interfaces().values()) - return 'WIRELESS' in enrich_iface_types(ifaces).values() - - -def has_cpu_vendor(vendor_id: str) -> bool: - return any(cpu.get("vendor_id") == vendor_id for cpu in cpuinfo()) - - -has_amd_cpu = partial(has_cpu_vendor, "AuthenticAMD") - - -has_intel_cpu = partial(has_cpu_vendor, "GenuineIntel") - - -def has_uefi() -> bool: - return os.path.isdir('/sys/firmware/efi') - - -def graphics_devices() -> dict: - cards = {} - for line in SysCommand("lspci"): - if b' VGA ' in line or b' 3D ' in line: - _, identifier = line.split(b': ', 1) - cards[identifier.strip().decode('UTF-8')] = line - return cards - - -def has_nvidia_graphics() -> bool: - return any('nvidia' in x.lower() for x in graphics_devices()) - - -def has_amd_graphics() -> bool: - return any('amd' in x.lower() for x in graphics_devices()) - - -def has_intel_graphics() -> bool: - return any('intel' in x.lower() for x in graphics_devices()) - - -def cpu_vendor() -> Optional[str]: - for cpu in cpuinfo(): - return cpu.get("vendor_id") - - return None - - -def cpu_model() -> Optional[str]: - for cpu in cpuinfo(): - return cpu.get("model name") - - return None - - -def sys_vendor() -> Optional[str]: - with open(f"/sys/devices/virtual/dmi/id/sys_vendor") as vendor: - return vendor.read().strip() - - -def product_name() -> Optional[str]: - with open(f"/sys/devices/virtual/dmi/id/product_name") as product: - return product.read().strip() - - -def mem_available() -> Optional[int]: - return meminfo_for_key('MemAvailable') - - -def mem_free() -> Optional[int]: - return meminfo_for_key('MemFree') - - -def mem_total() -> Optional[int]: - return meminfo_for_key('MemTotal') - - -def virtualization() -> Optional[str]: - try: - return str(SysCommand("systemd-detect-virt")).strip('\r\n') - except SysCallError as error: - log(f"Could not detect virtual system: {error}", level=logging.DEBUG) - - return None - - -def is_vm() -> bool: - try: - result = SysCommand("systemd-detect-virt") - return b"none" not in b"".join(result).lower() - except SysCallError as error: - log(f"System is not running in a VM: {error}", level=logging.DEBUG) - - return False + @cached_property + def cpu_info(self) -> Dict[str, str]: + """ + Returns system cpu information + """ + cpu_info_path = Path("/proc/cpuinfo") + cpu: Dict[str, str] = {} + + with cpu_info_path.open() as file: + for line in file: + if line := line.strip(): + key, value = line.split(":", maxsplit=1) + cpu[key.strip()] = value.strip() + + return cpu + + @cached_property + def mem_info(self) -> Dict[str, int]: + """ + Returns system memory information + """ + mem_info_path = Path("/proc/meminfo") + mem_info: Dict[str, int] = {} + + with mem_info_path.open() as file: + for line in file: + key, value = line.strip().split(':') + num = value.split()[0] + mem_info[key] = int(num) + + return mem_info + + def mem_info_by_key(self, key: str) -> int: + return self.mem_info[key] + + +_sys_info = _SysInfo() + + +class SysInfo: + @staticmethod + def has_wifi() -> bool: + ifaces = list(list_interfaces().values()) + return 'WIRELESS' in enrich_iface_types(ifaces).values() + + @staticmethod + def has_uefi() -> bool: + return os.path.isdir('/sys/firmware/efi') + + @staticmethod + def _graphics_devices() -> Dict[str, str]: + cards: Dict[str, str] = {} + for line in SysCommand("lspci"): + if b' VGA ' in line or b' 3D ' in line: + _, identifier = line.split(b': ', 1) + cards[identifier.strip().decode('UTF-8')] = str(line) + return cards + + @staticmethod + def has_nvidia_graphics() -> bool: + return any('nvidia' in x.lower() for x in SysInfo._graphics_devices()) + + @staticmethod + def has_amd_graphics() -> bool: + return any('amd' in x.lower() for x in SysInfo._graphics_devices()) + + @staticmethod + def has_intel_graphics() -> bool: + return any('intel' in x.lower() for x in SysInfo._graphics_devices()) + + @staticmethod + def cpu_vendor() -> Optional[str]: + return _sys_info.cpu_info.get('vendor_id', None) + + @staticmethod + def cpu_model() -> Optional[str]: + return _sys_info.cpu_info.get('model name', None) + + @staticmethod + def sys_vendor() -> str: + with open(f"/sys/devices/virtual/dmi/id/sys_vendor") as vendor: + return vendor.read().strip() + + @staticmethod + def product_name() -> str: + with open(f"/sys/devices/virtual/dmi/id/product_name") as product: + return product.read().strip() + + @staticmethod + def mem_available() -> int: + return _sys_info.mem_info_by_key('MemAvailable') + + @staticmethod + def mem_free() -> int: + return _sys_info.mem_info_by_key('MemFree') + + @staticmethod + def mem_total() -> int: + return _sys_info.mem_info_by_key('MemTotal') + + @staticmethod + def virtualization() -> Optional[str]: + try: + return str(SysCommand("systemd-detect-virt")).strip('\r\n') + except SysCallError as err: + debug(f"Could not detect virtual system: {err}") + + return None + + @staticmethod + def is_vm() -> bool: + try: + result = SysCommand("systemd-detect-virt") + return b"none" not in b"".join(result).lower() + except SysCallError as err: + debug(f"System is not running in a VM: {err}") + + return False diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 726ff3d0..3c427ab2 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -1,5 +1,4 @@ import glob -import logging import os import re import shlex @@ -12,17 +11,16 @@ from typing import Any, List, Optional, TYPE_CHECKING, Union, Dict, Callable, It from . import disk from .exceptions import DiskError, ServiceException, RequirementError, HardwareIncompatibilityError, SysCallError from .general import SysCommand -from .hardware import has_uefi, is_vm, cpu_vendor -from .locale_helpers import verify_keyboard_layout, verify_x11_keyboard_layout +from .hardware import SysInfo +from .locale import verify_keyboard_layout, verify_x11_keyboard_layout from .luks import Luks2 from .mirrors import use_mirrors from .models.bootloader import Bootloader from .models.network_configuration import NetworkConfiguration from .models.users import User -from .output import log +from .output import log, error, info, warn, debug from .pacman import run_pacman from .plugins import plugins -from .services import service_state from .storage import storage if TYPE_CHECKING: @@ -41,28 +39,6 @@ def accessibility_tools_in_use() -> bool: class Installer: - """ - `Installer()` is the wrapper for most basic installation steps. - It also wraps :py:func:`~archinstall.Installer.pacstrap` among other things. - - :param partition: Requires a partition as the first argument, this is - so that the installer can mount to `mountpoint` and strap packages there. - :type partition: class:`archinstall.Partition` - - :param boot_partition: There's two reasons for needing a boot partition argument, - The first being so that `mkinitcpio` can place the `vmlinuz` kernel at the right place - during the `pacstrap` or `linux` and the base packages for a minimal installation. - The second being when :py:func:`~archinstall.Installer.add_bootloader` is called, - A `boot_partition` must be known to the installer before this is called. - :type boot_partition: class:`archinstall.Partition` - - :param profile: A profile to install, this is optional and can be called later manually. - This just simplifies the process by not having to call :py:func:`~archinstall.Installer.install_profile` later on. - :type profile: str, optional - - :param hostname: The given /etc/hostname for the machine. - :type hostname: str, optional - """ def __init__( self, target: Path, @@ -71,6 +47,10 @@ class Installer: base_packages: List[str] = [], kernels: Optional[List[str]] = None ): + """ + `Installer()` is the wrapper for most basic installation steps. + It also wraps :py:func:`~archinstall.Installer.pacstrap` among other things. + """ if not base_packages: base_packages = __packages__[:3] @@ -126,7 +106,7 @@ class Installer: def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is not None: - log(exc_val, fg='red', level=logging.ERROR) + error(exc_val) self.sync_log_to_install_medium() @@ -137,48 +117,41 @@ class Installer: raise exc_val if not (missing_steps := self.post_install_check()): - self.log('Installation completed without any errors. You may now reboot.', fg='green', level=logging.INFO) + log('Installation completed without any errors. You may now reboot.', fg='green') self.sync_log_to_install_medium() return True else: - self.log('Some required steps were not successfully installed/configured before leaving the installer:', fg='red', level=logging.WARNING) + warn('Some required steps were not successfully installed/configured before leaving the installer:') for step in missing_steps: - self.log(f' - {step}', fg='red', level=logging.WARNING) + warn(f' - {step}') - self.log(f"Detailed error logs can be found at: {storage['LOG_PATH']}", level=logging.WARNING) - self.log("Submit this zip file as an issue to https://github.com/archlinux/archinstall/issues", level=logging.WARNING) + warn(f"Detailed error logs can be found at: {storage['LOG_PATH']}") + warn("Submit this zip file as an issue to https://github.com/archlinux/archinstall/issues") self.sync_log_to_install_medium() return False - def log(self, *args :str, level :int = logging.DEBUG, **kwargs :str): - """ - installer.log() wraps output.log() mainly to set a default log-level for this install session. - Any manual override can be done per log() call. - """ - log(*args, level=level, **kwargs) - def _verify_service_stop(self): """ Certain services might be running that affects the system during installation. One such service is "reflector.service" which updates /etc/pacman.d/mirrorlist We need to wait for it before we continue since we opted in to use a custom mirror/region. """ - log('Waiting for time sync (systemd-timesyncd.service) to complete.', level=logging.INFO) + info('Waiting for time sync (systemd-timesyncd.service) to complete.') while SysCommand('timedatectl show --property=NTPSynchronized --value').decode().rstrip() != 'yes': time.sleep(1) - log('Waiting for automatic mirror selection (reflector) to complete.', level=logging.INFO) - while service_state('reflector') not in ('dead', 'failed', 'exited'): + info('Waiting for automatic mirror selection (reflector) to complete.') + while self._service_state('reflector') not in ('dead', 'failed', 'exited'): time.sleep(1) - log('Waiting pacman-init.service to complete.', level=logging.INFO) - while service_state('pacman-init') not in ('dead', 'failed', 'exited'): + info('Waiting pacman-init.service to complete.') + while self._service_state('pacman-init') not in ('dead', 'failed', 'exited'): time.sleep(1) - log('Waiting Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete.', level=logging.INFO) - while service_state('archlinux-keyring-wkd-sync') not in ('dead', 'failed', 'exited'): + info('Waiting Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete.') + while self._service_state('archlinux-keyring-wkd-sync') not in ('dead', 'failed', 'exited'): time.sleep(1) def _verify_boot_part(self): @@ -204,7 +177,7 @@ class Installer: self._verify_service_stop() def mount_ordered_layout(self): - log('Mounting partitions in order', level=logging.INFO) + info('Mounting partitions in order') for mod in self._disk_config.device_modifications: # partitions have to mounted in the right order on btrfs the mountpoint will @@ -275,7 +248,7 @@ class Installer: ) if gen_enc_file and not part_mod.is_root(): - log(f'Creating key-file: {part_mod.dev_path}', level=logging.INFO) + info(f'Creating key-file: {part_mod.dev_path}') luks_handler.create_keyfile(self.target) if part_mod.is_root() and not gen_enc_file: @@ -384,25 +357,25 @@ class Installer: if (result := plugin.on_pacstrap(packages)): packages = result - self.log(f'Installing packages: {packages}', level=logging.INFO) + info(f'Installing packages: {packages}') # TODO: We technically only need to run the -Syy once. try: run_pacman('-Syy', default_cmd='/usr/bin/pacman') - except SysCallError as error: - self.log(f'Could not sync a new package database: {error}', level=logging.ERROR, fg="red") + except SysCallError as err: + error(f'Could not sync a new package database: {err}') 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) - raise RequirementError(f'Could not sync mirrors: {error}') + raise RequirementError(f'Could not sync mirrors: {err}') try: SysCommand(f'/usr/bin/pacstrap -C /etc/pacman.conf -K {self.target} {" ".join(packages)} --noconfirm', peek_output=True) return True - except SysCallError as error: - self.log(f'Could not strap in packages: {error}', level=logging.ERROR, fg="red") + except SysCallError as err: + error(f'Could not strap in packages: {err}') if storage['arguments'].get('silent', False) is False: if input('Would you like to re-try this download? (Y/n): ').lower().strip() in ('', 'y'): @@ -420,12 +393,12 @@ class Installer: use_mirrors(mirrors, destination=destination) def genfstab(self, flags :str = '-pU'): - self.log(f"Updating {self.target}/etc/fstab", level=logging.INFO) + info(f"Updating {self.target}/etc/fstab") try: gen_fstab = SysCommand(f'/usr/bin/genfstab {flags} {self.target}').decode() - except SysCallError as error: - raise RequirementError(f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n Error: {error}') + except SysCallError as err: + raise RequirementError(f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n Error: {err}') if not gen_fstab: raise RequirementError(f'Genrating fstab returned empty value') @@ -530,24 +503,20 @@ class Installer: return True else: - self.log( - f"Time zone {zone} does not exist, continuing with system default.", - level=logging.WARNING, - fg='red' - ) + warn(f'Time zone {zone} does not exist, continuing with system default') return False def activate_time_syncronization(self) -> None: - self.log('Activating systemd-timesyncd for time synchronization using Arch Linux and ntp.org NTP servers.', level=logging.INFO) + info('Activating systemd-timesyncd for time synchronization using Arch Linux and ntp.org NTP servers') self.enable_service('systemd-timesyncd') def enable_espeakup(self) -> None: - self.log('Enabling espeakup.service for speech synthesis (accessibility).', level=logging.INFO) + info('Enabling espeakup.service for speech synthesis (accessibility)') self.enable_service('espeakup') def enable_periodic_trim(self) -> None: - self.log("Enabling periodic TRIM") + info("Enabling periodic TRIM") # fstrim is owned by util-linux, a dependency of both base and systemd. self.enable_service("fstrim.timer") @@ -556,12 +525,12 @@ class Installer: services = [services] for service in services: - self.log(f'Enabling service {service}', level=logging.INFO) + info(f'Enabling service {service}') try: self.arch_chroot(f'systemctl enable {service}') - except SysCallError as error: - raise ServiceException(f"Unable to start service {service}: {error}") + except SysCallError as err: + raise ServiceException(f"Unable to start service {service}: {err}") for plugin in plugins.values(): if hasattr(plugin, 'on_service'): @@ -713,11 +682,11 @@ class Installer: if 'encrypt' not in self._hooks: self._hooks.insert(self._hooks.index('filesystems'), 'encrypt') - if not has_uefi(): + if not SysInfo.has_uefi(): self.base_packages.append('grub') - if not is_vm(): - vendor = cpu_vendor() + if not SysInfo.is_vm(): + vendor = SysInfo.cpu_vendor() if vendor == "AuthenticAMD": self.base_packages.append("amd-ucode") if (ucode := Path(f"{self.target}/boot/amd-ucode.img")).exists(): @@ -727,21 +696,21 @@ class Installer: if (ucode := Path(f"{self.target}/boot/intel-ucode.img")).exists(): ucode.unlink() else: - self.log(f"Unknown CPU vendor '{vendor}' detected. Archinstall won't install any ucode.", level=logging.DEBUG) + debug(f"Unknown CPU vendor '{vendor}' detected. Archinstall won't install any ucode") # Determine whether to enable multilib/testing repositories before running pacstrap if testing flag is set. # This action takes place on the host system as pacstrap copies over package repository lists. if multilib: - self.log("The multilib flag is set. This system will be installed with the multilib repository enabled.") + info("The multilib flag is set. This system will be installed with the multilib repository enabled.") self.enable_multilib_repository() else: - self.log("The multilib flag is not set. This system will be installed without multilib repositories enabled.") + info("The multilib flag is not set. This system will be installed without multilib repositories enabled.") if testing: - self.log("The testing flag is set. This system will be installed with testing repositories enabled.") + info("The testing flag is set. This system will be installed with testing repositories enabled.") self.enable_testing_repositories(multilib) else: - self.log("The testing flag is not set. This system will be installed without testing repositories enabled.") + info("The testing flag is not set. This system will be installed without testing repositories enabled.") self._pacstrap(self.base_packages) self.helper_flags['base-strapped'] = True @@ -773,7 +742,7 @@ class Installer: # Run registered post-install hooks for function in self.post_base_install: - self.log(f"Running post-installation hook: {function}", level=logging.INFO) + info(f"Running post-installation hook: {function}") function(self) for plugin in plugins.values(): @@ -782,7 +751,7 @@ class Installer: def setup_swap(self, kind :str = 'zram'): if kind == 'zram': - self.log(f"Setting up swap on zram") + info(f"Setting up swap on zram") self._pacstrap('zram-generator') # We could use the default example below, but maybe not the best idea: https://github.com/archlinux/archinstall/pull/678#issuecomment-962124813 @@ -812,7 +781,7 @@ class Installer: def _add_systemd_bootloader(self, root_partition: disk.PartitionModification): self._pacstrap('efibootmgr') - if not has_uefi(): + if not SysInfo.has_uefi(): raise HardwareIncompatibilityError # TODO: Ideally we would want to check if another config @@ -862,16 +831,18 @@ class Installer: entry.write(f'# Created on: {self.init_time}\n') entry.write(f'title Arch Linux ({kernel}{variant})\n') entry.write(f"linux /vmlinuz-{kernel}\n") - if not is_vm(): - vendor = cpu_vendor() + if not SysInfo.is_vm(): + vendor = SysInfo.cpu_vendor() if vendor == "AuthenticAMD": entry.write("initrd /amd-ucode.img\n") elif vendor == "GenuineIntel": entry.write("initrd /intel-ucode.img\n") else: - self.log( - f"Unknown CPU vendor '{vendor}' detected. Archinstall won't add any ucode to systemd-boot config.", - level=logging.DEBUG) + debug( + f"Unknown CPU vendor '{vendor}' detected.", + "Archinstall won't add any ucode to systemd-boot config.", + ) + entry.write(f"initrd /initramfs-{kernel}{variant}.img\n") # blkid doesn't trigger on loopback devices really well, # so we'll use the old manual method until we get that sorted out. @@ -890,7 +861,7 @@ class Installer: if root_partition.fs_type.is_crypto(): # TODO: We need to detect if the encrypted device is a whole disk encryption, # or simply a partition encryption. Right now we assume it's a partition (and we always have) - log('Root partition is an encrypted device, identifying by PARTUUID: {root_partition.partuuid}', level=logging.DEBUG) + debug('Root partition is an encrypted device, identifying by PARTUUID: {root_partition.partuuid}') kernel_options = f"options" @@ -905,7 +876,7 @@ class Installer: entry.write(f'{kernel_options} root=/dev/mapper/luksdev {options_entry}') else: - log(f'Identifying root partition by PARTUUID: {root_partition.partuuid}', level=logging.DEBUG) + debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}') entry.write(f'options root=PARTUUID={root_partition.partuuid} {options_entry}') self.helper_flags['bootloader'] = 'systemd' @@ -920,7 +891,7 @@ class Installer: _file = "/etc/default/grub" if root_partition.fs_type.is_crypto(): - log(f"Using UUID {root_partition.uuid} as encrypted root identifier", level=logging.DEBUG) + debug(f"Using UUID {root_partition.uuid} as encrypted root identifier") cmd_line_linux = f"sed -i 's/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"cryptdevice=UUID={root_partition.uuid}:cryptlvm rootfstype={root_partition.fs_type.value}\"/'" enable_cryptdisk = "sed -i 's/#GRUB_ENABLE_CRYPTODISK=y/GRUB_ENABLE_CRYPTODISK=y/'" @@ -931,9 +902,9 @@ class Installer: SysCommand(f"/usr/bin/arch-chroot {self.target} {cmd_line_linux} {_file}") - log(f"GRUB boot partition: {boot_partition.dev_path}", level=logging.INFO) + info(f"GRUB boot partition: {boot_partition.dev_path}") - if has_uefi(): + if SysInfo.has_uefi(): self._pacstrap('efibootmgr') # TODO: Do we need? Yes, but remove from minimal_installation() instead? try: @@ -941,8 +912,8 @@ class Installer: except SysCallError: try: SysCommand(f'/usr/bin/arch-chroot {self.target} grub-install --debug --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB --removable', peek_output=True) - except SysCallError as error: - raise DiskError(f"Could not install GRUB to {self.target}/boot: {error}") + except SysCallError as err: + raise DiskError(f"Could not install GRUB to {self.target}/boot: {err}") else: device = disk.device_handler.get_device_by_partition_path(boot_partition.safe_dev_path) @@ -958,13 +929,13 @@ class Installer: f' --recheck {device.device_info.path}' SysCommand(cmd, peek_output=True) - except SysCallError as error: - raise DiskError(f"Failed to install GRUB boot on {boot_partition.dev_path}: {error}") + except SysCallError as err: + raise DiskError(f"Failed to install GRUB boot on {boot_partition.dev_path}: {err}") try: SysCommand(f'/usr/bin/arch-chroot {self.target} grub-mkconfig -o /boot/grub/grub.cfg') - except SysCallError as error: - raise DiskError(f"Could not configure GRUB: {error}") + except SysCallError as err: + raise DiskError(f"Could not configure GRUB: {err}") self.helper_flags['bootloader'] = "grub" @@ -975,7 +946,7 @@ class Installer: ): self._pacstrap('efibootmgr') - if not has_uefi(): + if not SysInfo.has_uefi(): raise HardwareIncompatibilityError # TODO: Ideally we would want to check if another config @@ -989,14 +960,14 @@ class Installer: kernel_parameters = [] - if not is_vm(): - vendor = cpu_vendor() + if not SysInfo.is_vm(): + vendor = SysInfo.cpu_vendor() if vendor == "AuthenticAMD": kernel_parameters.append("initrd=\\amd-ucode.img") elif vendor == "GenuineIntel": kernel_parameters.append("initrd=\\intel-ucode.img") else: - self.log(f"Unknown CPU vendor '{vendor}' detected. Archinstall won't add any ucode to firmware boot entry.", level=logging.DEBUG) + debug(f"Unknown CPU vendor '{vendor}' detected. Archinstall won't add any ucode to firmware boot entry.") kernel_parameters.append(f"initrd=\\initramfs-{kernel}.img") @@ -1006,10 +977,10 @@ class Installer: if root_partition.fs_type.is_crypto(): # TODO: We need to detect if the encrypted device is a whole disk encryption, # or simply a partition encryption. Right now we assume it's a partition (and we always have) - log(f'Identifying root partition by PARTUUID: {root_partition.partuuid}', level=logging.DEBUG) + debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}') kernel_parameters.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:luksdev root=/dev/mapper/luksdev rw rootfstype={root_partition.fs_type.value} {" ".join(self._kernel_params)}') else: - log(f'Root partition is an encrypted device identifying by PARTUUID: {root_partition.partuuid}', level=logging.DEBUG) + debug(f'Root partition is an encrypted device identifying by PARTUUID: {root_partition.partuuid}') kernel_parameters.append(f'root=PARTUUID={root_partition.partuuid} rw rootfstype={root_partition.fs_type.value} {" ".join(self._kernel_params)}') device = disk.device_handler.get_device_by_partition_path(boot_partition.safe_dev_path) @@ -1060,7 +1031,7 @@ class Installer: if root_partition is None: raise ValueError(f'Could not detect root at mountpoint {self.target}') - self.log(f'Adding bootloader {bootloader.value} to {boot_partition.dev_path}', level=logging.INFO) + info(f'Adding bootloader {bootloader.value} to {boot_partition.dev_path}') match bootloader: case Bootloader.Systemd: @@ -1078,7 +1049,7 @@ class Installer: self.arch_chroot(f'systemctl enable --user {service}', run_as=user.username) def enable_sudo(self, entity: str, group :bool = False): - self.log(f'Enabling sudo permissions for {entity}.', level=logging.INFO) + info(f'Enabling sudo permissions for {entity}') sudoers_dir = f"{self.target}/etc/sudoers.d" @@ -1127,11 +1098,11 @@ class Installer: handled_by_plugin = result if not handled_by_plugin: - self.log(f'Creating user {user}', level=logging.INFO) + info(f'Creating user {user}') try: SysCommand(f'/usr/bin/arch-chroot {self.target} useradd -m -G wheel {user}') - except SysCallError as error: - raise SystemError(f"Could not create user inside installation: {error}") + except SysCallError as err: + raise SystemError(f"Could not create user inside installation: {err}") for plugin in plugins.values(): if hasattr(plugin, 'on_user_created'): @@ -1149,7 +1120,7 @@ class Installer: self.helper_flags['user'] = True def user_set_pw(self, user :str, password :str) -> bool: - self.log(f'Setting password for {user}', level=logging.INFO) + info(f'Setting password for {user}') if user == 'root': # This means the root account isn't locked/disabled with * in /etc/passwd @@ -1166,7 +1137,7 @@ class Installer: return False def user_set_shell(self, user :str, shell :str) -> bool: - self.log(f'Setting shell for {user} to {shell}', level=logging.INFO) + info(f'Setting shell for {user} to {shell}') try: SysCommand(f"/usr/bin/arch-chroot {self.target} sh -c \"chsh -s {shell} {user}\"") @@ -1183,49 +1154,59 @@ class Installer: return False def set_keyboard_language(self, language: str) -> bool: - log(f"Setting keyboard language to {language}", level=logging.INFO) + info(f"Setting keyboard language to {language}") + if len(language.strip()): if not verify_keyboard_layout(language): - self.log(f"Invalid keyboard language specified: {language}", fg="red", level=logging.ERROR) + error(f"Invalid keyboard language specified: {language}") return False # In accordance with https://github.com/archlinux/archinstall/issues/107#issuecomment-841701968 # Setting an empty keymap first, allows the subsequent call to set layout for both console and x11. - from .systemd import Boot + from .boot import Boot with Boot(self) as session: os.system('/usr/bin/systemd-run --machine=archinstall --pty localectl set-keymap ""') try: session.SysCommand(["localectl", "set-keymap", language]) - except SysCallError as error: - raise ServiceException(f"Unable to set locale '{language}' for console: {error}") + except SysCallError as err: + raise ServiceException(f"Unable to set locale '{language}' for console: {err}") - self.log(f"Keyboard language for this installation is now set to: {language}") + info(f"Keyboard language for this installation is now set to: {language}") else: - self.log('Keyboard language was not changed from default (no language specified).', fg="yellow", level=logging.INFO) + info('Keyboard language was not changed from default (no language specified)') return True def set_x11_keyboard_language(self, language: str) -> bool: - log(f"Setting x11 keyboard language to {language}", level=logging.INFO) """ A fallback function to set x11 layout specifically and separately from console layout. This isn't strictly necessary since .set_keyboard_language() does this as well. """ + info(f"Setting x11 keyboard language to {language}") + if len(language.strip()): if not verify_x11_keyboard_layout(language): - self.log(f"Invalid x11-keyboard language specified: {language}", fg="red", level=logging.ERROR) + error(f"Invalid x11-keyboard language specified: {language}") return False - from .systemd import Boot + from .boot import Boot with Boot(self) as session: session.SysCommand(["localectl", "set-x11-keymap", '""']) try: session.SysCommand(["localectl", "set-x11-keymap", language]) - except SysCallError as error: - raise ServiceException(f"Unable to set locale '{language}' for X11: {error}") + except SysCallError as err: + raise ServiceException(f"Unable to set locale '{language}' for X11: {err}") else: - self.log(f'X11-Keyboard language was not changed from default (no language specified).', fg="yellow", level=logging.INFO) + info(f'X11-Keyboard language was not changed from default (no language specified)') return True + + def _service_state(self, service_name: str) -> str: + if os.path.splitext(service_name)[1] != '.service': + service_name += '.service' # Just to be safe + + state = b''.join(SysCommand(f'systemctl show --no-pager -p SubState --value {service_name}', environment_vars={'SYSTEMD_COLORS': '0'})) + + return state.strip().decode('UTF-8') diff --git a/archinstall/lib/interactions/__init__.py b/archinstall/lib/interactions/__init__.py new file mode 100644 index 00000000..b5691a10 --- /dev/null +++ b/archinstall/lib/interactions/__init__.py @@ -0,0 +1,20 @@ +from .locale_conf import select_locale_lang, select_locale_enc +from .manage_users_conf import UserList, ask_for_additional_users +from .network_conf import ManualNetworkConfig, ask_to_configure_network +from .utils import get_password + +from .disk_conf import ( + select_devices, select_disk_config, get_default_partition_layout, + select_main_filesystem_format, suggest_single_disk_layout, + suggest_multi_disk_layout +) + +from .general_conf import ( + ask_ntp, ask_hostname, ask_for_a_timezone, ask_for_audio_selection, select_language, + select_mirror_regions, select_archinstall_language, ask_additional_packages_to_install, + add_number_of_parrallel_downloads, select_additional_repositories +) + +from .system_conf import ( + select_kernel, ask_for_bootloader, select_driver, ask_for_swap +) diff --git a/archinstall/lib/interactions/disk_conf.py b/archinstall/lib/interactions/disk_conf.py new file mode 100644 index 00000000..78e4cff4 --- /dev/null +++ b/archinstall/lib/interactions/disk_conf.py @@ -0,0 +1,393 @@ +from __future__ import annotations + +from pathlib import Path +from typing import Any, TYPE_CHECKING +from typing import Optional, List, Tuple + +from .. import disk +from ..hardware import SysInfo +from ..menu import Menu +from ..menu import TableMenu +from ..menu.menu import MenuSelectionType +from ..output import FormattedOutput, debug +from ..utils.util import prompt_dir + +if TYPE_CHECKING: + _: Any + + +def select_devices(preset: List[disk.BDevice] = []) -> List[disk.BDevice]: + """ + Asks the user to select one or multiple devices + + :return: List of selected devices + :rtype: list + """ + + def _preview_device_selection(selection: disk._DeviceInfo) -> Optional[str]: + dev = disk.device_handler.get_device(selection.path) + if dev and dev.partition_infos: + return FormattedOutput.as_table(dev.partition_infos) + return None + + if preset is None: + preset = [] + + title = str(_('Select one or more devices to use and configure')) + warning = str(_('If you reset the device selection this will also reset the current disk layout. Are you sure?')) + + devices = disk.device_handler.devices + options = [d.device_info for d in devices] + preset_value = [p.device_info for p in preset] + + choice = TableMenu( + title, + data=options, + multi=True, + preset=preset_value, + preview_command=_preview_device_selection, + preview_title=str(_('Existing Partitions')), + preview_size=0.2, + allow_reset=True, + allow_reset_warning_msg=warning + ).run() + + match choice.type_: + case MenuSelectionType.Reset: return [] + case MenuSelectionType.Skip: return preset + case MenuSelectionType.Selection: + selected_device_info: List[disk._DeviceInfo] = choice.value # type: ignore + selected_devices = [] + + for device in devices: + if device.device_info in selected_device_info: + selected_devices.append(device) + + return selected_devices + + +def get_default_partition_layout( + devices: List[disk.BDevice], + filesystem_type: Optional[disk.FilesystemType] = None, + advanced_option: bool = False +) -> List[disk.DeviceModification]: + + if len(devices) == 1: + device_modification = suggest_single_disk_layout( + devices[0], + filesystem_type=filesystem_type, + advanced_options=advanced_option + ) + return [device_modification] + else: + return suggest_multi_disk_layout( + devices, + filesystem_type=filesystem_type, + advanced_options=advanced_option + ) + + +def _manual_partitioning( + preset: List[disk.DeviceModification], + devices: List[disk.BDevice] +) -> List[disk.DeviceModification]: + modifications = [] + for device in devices: + mod = next(filter(lambda x: x.device == device, preset), None) + if not mod: + mod = disk.DeviceModification(device, wipe=False) + + if partitions := disk.manual_partitioning(device, preset=mod.partitions): + mod.partitions = partitions + modifications.append(mod) + + return modifications + + +def select_disk_config( + preset: Optional[disk.DiskLayoutConfiguration] = None, + advanced_option: bool = False +) -> Optional[disk.DiskLayoutConfiguration]: + default_layout = disk.DiskLayoutType.Default.display_msg() + manual_mode = disk.DiskLayoutType.Manual.display_msg() + pre_mount_mode = disk.DiskLayoutType.Pre_mount.display_msg() + + options = [default_layout, manual_mode, pre_mount_mode] + preset_value = preset.config_type.display_msg() if preset else None + warning = str(_('Are you sure you want to reset this setting?')) + + choice = Menu( + _('Select a partitioning option'), + options, + allow_reset=True, + allow_reset_warning_msg=warning, + sort=False, + preview_size=0.2, + preset_values=preset_value + ).run() + + match choice.type_: + case MenuSelectionType.Skip: return preset + case MenuSelectionType.Reset: return None + case MenuSelectionType.Selection: + if choice.single_value == pre_mount_mode: + output = "You will use whatever drive-setup is mounted at the specified directory\n" + output += "WARNING: Archinstall won't check the suitability of this setup\n" + + path = prompt_dir(str(_('Enter the root directory of the mounted devices: ')), output) + mods = disk.device_handler.detect_pre_mounted_mods(path) + + return disk.DiskLayoutConfiguration( + config_type=disk.DiskLayoutType.Pre_mount, + relative_mountpoint=path, + device_modifications=mods + ) + + preset_devices = [mod.device for mod in preset.device_modifications] if preset else [] + + devices = select_devices(preset_devices) + + if not devices: + return None + + if choice.value == default_layout: + modifications = get_default_partition_layout(devices, advanced_option=advanced_option) + if modifications: + return disk.DiskLayoutConfiguration( + config_type=disk.DiskLayoutType.Default, + device_modifications=modifications + ) + elif choice.value == manual_mode: + preset_mods = preset.device_modifications if preset else [] + modifications = _manual_partitioning(preset_mods, devices) + + if modifications: + return disk.DiskLayoutConfiguration( + config_type=disk.DiskLayoutType.Manual, + device_modifications=modifications + ) + + return None + + +def _boot_partition() -> disk.PartitionModification: + if SysInfo.has_uefi(): + start = disk.Size(1, disk.Unit.MiB) + size = disk.Size(512, disk.Unit.MiB) + else: + start = disk.Size(3, disk.Unit.MiB) + size = disk.Size(203, disk.Unit.MiB) + + # boot partition + return disk.PartitionModification( + status=disk.ModificationStatus.Create, + type=disk.PartitionType.Primary, + start=start, + length=size, + mountpoint=Path('/boot'), + fs_type=disk.FilesystemType.Fat32, + flags=[disk.PartitionFlag.Boot] + ) + + +def select_main_filesystem_format(advanced_options=False) -> disk.FilesystemType: + options = { + 'btrfs': disk.FilesystemType.Btrfs, + 'ext4': disk.FilesystemType.Ext4, + 'xfs': disk.FilesystemType.Xfs, + 'f2fs': disk.FilesystemType.F2fs + } + + if advanced_options: + options.update({'ntfs': disk.FilesystemType.Ntfs}) + + prompt = _('Select which filesystem your main partition should use') + choice = Menu(prompt, options, skip=False, sort=False).run() + return options[choice.single_value] + + +def suggest_single_disk_layout( + device: disk.BDevice, + filesystem_type: Optional[disk.FilesystemType] = None, + advanced_options: bool = False, + separate_home: Optional[bool] = None +) -> disk.DeviceModification: + if not filesystem_type: + filesystem_type = select_main_filesystem_format(advanced_options) + + min_size_to_allow_home_part = disk.Size(40, disk.Unit.GiB) + root_partition_size = disk.Size(20, disk.Unit.GiB) + using_subvolumes = False + using_home_partition = False + compression = False + device_size_gib = device.device_info.total_size + + if filesystem_type == disk.FilesystemType.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.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.value == Menu.yes() + + device_modification = disk.DeviceModification(device, wipe=True) + + # Used for reference: https://wiki.archlinux.org/title/partitioning + # 2 MiB is unallocated for GRUB on BIOS. Potentially unneeded for other bootloaders? + + # TODO: On BIOS, /boot partition is only needed if the drive will + # be encrypted, otherwise it is not recommended. We should probably + # add a check for whether the drive will be encrypted or not. + + # Increase the UEFI partition if UEFI is detected. + # Also re-align the start to 1MiB since we don't need the first sectors + # like we do in MBR layouts where the boot loader is installed traditionally. + + boot_partition = _boot_partition() + device_modification.add_partition(boot_partition) + + if not using_subvolumes: + if device_size_gib >= min_size_to_allow_home_part: + if separate_home is None: + 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.value == Menu.yes() + elif separate_home is True: + using_home_partition = True + else: + using_home_partition = False + + # root partition + start = disk.Size(513, disk.Unit.MiB) if SysInfo.has_uefi() else disk.Size(206, disk.Unit.MiB) + + # Set a size for / (/root) + if using_subvolumes or device_size_gib < min_size_to_allow_home_part or not using_home_partition: + length = disk.Size(100, disk.Unit.Percent, total_size=device.device_info.total_size) + else: + length = min(device.device_info.total_size, root_partition_size) + + root_partition = disk.PartitionModification( + status=disk.ModificationStatus.Create, + type=disk.PartitionType.Primary, + start=start, + length=length, + mountpoint=Path('/') if not using_subvolumes else None, + fs_type=filesystem_type, + mount_options=['compress=zstd'] if compression else [], + ) + device_modification.add_partition(root_partition) + + if using_subvolumes: + # https://btrfs.wiki.kernel.org/index.php/FAQ + # https://unix.stackexchange.com/questions/246976/btrfs-subvolume-uuid-clash + # https://github.com/classy-giraffe/easy-arch/blob/main/easy-arch.sh + subvolumes = [ + disk.SubvolumeModification(Path('@'), Path('/')), + disk.SubvolumeModification(Path('@home'), Path('/home')), + disk.SubvolumeModification(Path('@log'), Path('/var/log')), + disk.SubvolumeModification(Path('@pkg'), Path('/var/cache/pacman/pkg')), + disk.SubvolumeModification(Path('@.snapshots'), Path('/.snapshots')) + ] + root_partition.btrfs_subvols = subvolumes + elif using_home_partition: + # If we don't want to use subvolumes, + # But we want to be able to re-use data between re-installs.. + # A second partition for /home would be nice if we have the space for it + home_partition = disk.PartitionModification( + status=disk.ModificationStatus.Create, + type=disk.PartitionType.Primary, + start=root_partition.length, + length=disk.Size(100, disk.Unit.Percent, total_size=device.device_info.total_size), + mountpoint=Path('/home'), + fs_type=filesystem_type, + mount_options=['compress=zstd'] if compression else [] + ) + device_modification.add_partition(home_partition) + + return device_modification + + +def suggest_multi_disk_layout( + devices: List[disk.BDevice], + filesystem_type: Optional[disk.FilesystemType] = None, + advanced_options: bool = False +) -> List[disk.DeviceModification]: + if not devices: + return [] + + # Not really a rock solid foundation of information to stand on, but it's a start: + # https://www.reddit.com/r/btrfs/comments/m287gp/partition_strategy_for_two_physical_disks/ + # https://www.reddit.com/r/btrfs/comments/9us4hr/what_is_your_btrfs_partitionsubvolumes_scheme/ + min_home_partition_size = disk.Size(40, disk.Unit.GiB) + # rough estimate taking in to account user desktops etc. TODO: Catch user packages to detect size? + desired_root_partition_size = disk.Size(20, disk.Unit.GiB) + compression = False + + if not filesystem_type: + filesystem_type = select_main_filesystem_format(advanced_options) + + # find proper disk for /home + possible_devices = list(filter(lambda x: x.device_info.total_size >= min_home_partition_size, devices)) + home_device = max(possible_devices, key=lambda d: d.device_info.total_size) if possible_devices else None + + # find proper device for /root + devices_delta = {} + for device in devices: + if device is not home_device: + delta = device.device_info.total_size - desired_root_partition_size + devices_delta[device] = delta + + sorted_delta: List[Tuple[disk.BDevice, Any]] = sorted(devices_delta.items(), key=lambda x: x[1]) + root_device: Optional[disk.BDevice] = sorted_delta[0][0] + + 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: {}GiB\n').format(min_home_partition_size.format_size(disk.Unit.GiB)) + text += _('Minimum capacity for Arch Linux partition: {}GiB').format(desired_root_partition_size.format_size(disk.Unit.GiB)) + Menu(str(text), [str(_('Continue'))], skip=False).run() + return [] + + if filesystem_type == disk.FilesystemType.Btrfs: + prompt = str(_('Would you like to use BTRFS compression?')) + choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run() + compression = choice.value == Menu.yes() + + device_paths = ', '.join([str(d.device_info.path) for d in devices]) + + debug(f"Suggesting multi-disk-layout for devices: {device_paths}") + debug(f"/root: {root_device.device_info.path}") + debug(f"/home: {home_device.device_info.path}") + + root_device_modification = disk.DeviceModification(root_device, wipe=True) + home_device_modification = disk.DeviceModification(home_device, wipe=True) + + # add boot partition to the root device + boot_partition = _boot_partition() + root_device_modification.add_partition(boot_partition) + + # add root partition to the root device + root_partition = disk.PartitionModification( + status=disk.ModificationStatus.Create, + type=disk.PartitionType.Primary, + start=disk.Size(513, disk.Unit.MiB) if SysInfo.has_uefi() else disk.Size(206, disk.Unit.MiB), + length=disk.Size(100, disk.Unit.Percent, total_size=root_device.device_info.total_size), + mountpoint=Path('/'), + mount_options=['compress=zstd'] if compression else [], + fs_type=filesystem_type + ) + root_device_modification.add_partition(root_partition) + + # add home partition to home device + home_partition = disk.PartitionModification( + status=disk.ModificationStatus.Create, + type=disk.PartitionType.Primary, + start=disk.Size(1, disk.Unit.MiB), + length=disk.Size(100, disk.Unit.Percent, total_size=home_device.device_info.total_size), + mountpoint=Path('/home'), + mount_options=['compress=zstd'] if compression else [], + fs_type=filesystem_type, + ) + home_device_modification.add_partition(home_partition) + + return [root_device_modification, home_device_modification] diff --git a/archinstall/lib/interactions/general_conf.py b/archinstall/lib/interactions/general_conf.py new file mode 100644 index 00000000..5fcfa633 --- /dev/null +++ b/archinstall/lib/interactions/general_conf.py @@ -0,0 +1,243 @@ +from __future__ import annotations + +import pathlib +from typing import List, Any, Optional, Dict, TYPE_CHECKING + +from ..locale import list_keyboard_languages, list_timezones +from ..menu import MenuSelectionType, Menu, TextInput +from ..mirrors import list_mirrors +from ..output import warn +from ..packages.packages import validate_package_list +from ..storage import storage +from ..translationhandler import Language + +if TYPE_CHECKING: + _: Any + + +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 = Menu.yes() + 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.value == Menu.no() else True + + +def ask_hostname(preset: str = '') -> str: + while True: + hostname = TextInput( + str(_('Desired hostname for the installation: ')), + preset + ).run().strip() + + if hostname: + return hostname + + +def ask_for_a_timezone(preset: Optional[str] = None) -> Optional[str]: + timezones = list_timezones() + default = 'UTC' + + choice = Menu( + _('Select a timezone'), + list(timezones), + preset_values=preset, + default_option=default + ).run() + + match choice.type_: + case MenuSelectionType.Skip: return preset + case MenuSelectionType.Selection: return choice.single_value + + return None + + +def ask_for_audio_selection(desktop: bool = True, preset: Optional[str] = None) -> Optional[str]: + 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.Skip: return preset + case MenuSelectionType.Selection: return choice.single_value + + return None + + +def select_language(preset: Optional[str] = None) -> Optional[str]: + """ + Asks the user to select a language + Usually this is combined with :ref:`archinstall.list_keyboard_languages`. + + :return: The language/dictionary key of the selected language + :rtype: str + """ + kb_lang = list_keyboard_languages() + # sort alphabetically and then by length + sorted_kb_lang = sorted(sorted(list(kb_lang)), key=len) + + choice = Menu( + _('Select keyboard layout'), + sorted_kb_lang, + preset_values=preset, + sort=False + ).run() + + match choice.type_: + case MenuSelectionType.Skip: return preset + case MenuSelectionType.Selection: return choice.single_value + + return None + + +def select_mirror_regions(preset_values: Dict[str, Any] = {}) -> Dict[str, Any]: + """ + Asks the user to select a mirror or region + Usually this is combined with :ref:`archinstall.list_mirrors`. + + :return: The dictionary information about a mirror/region. + :rtype: dict + """ + if preset_values is None: + preselected = None + else: + preselected = list(preset_values.keys()) + + mirrors = list_mirrors() + + choice = Menu( + _('Select one of the regions to download packages from'), + list(mirrors.keys()), + preset_values=preselected, + multi=True, + allow_reset=True + ).run() + + match choice.type_: + case MenuSelectionType.Reset: + return {} + case MenuSelectionType.Skip: + return preset_values + case MenuSelectionType.Selection: + return {selected: mirrors[selected] for selected in choice.multi_value} + + return {} + + +def select_archinstall_language(languages: List[Language], preset: Language) -> Language: + # these are the displayed language names which can either be + # the english name of a language or, if present, the + # name of the language in its own language + options = {lang.display_name: lang for lang in languages} + + title = 'NOTE: If a language can not displayed properly, a proper font must be set manually in the console.\n' + title += 'All available fonts can be found in "/usr/share/kbd/consolefonts"\n' + title += 'e.g. setfont LatGrkCyr-8x16 (to display latin/greek/cyrillic characters)\n' + + choice = Menu( + title, + list(options.keys()), + default_option=preset.display_name, + preview_size=0.5 + ).run() + + match choice.type_: + case MenuSelectionType.Skip: return preset + case MenuSelectionType.Selection: return options[choice.single_value] + + raise ValueError('Language selection not handled') + + +def ask_additional_packages_to_install(pre_set_packages: List[str] = []) -> List[str]: + # Additional packages (with some light weight error handling for invalid package names) + print(_('Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.')) + print(_('If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt.')) + + def read_packages(already_defined: list = []) -> list: + display = ' '.join(already_defined) + input_packages = TextInput(_('Write additional packages to install (space separated, leave blank to skip): '), display).run().strip() + return input_packages.split() if input_packages else [] + + pre_set_packages = pre_set_packages if pre_set_packages else [] + packages = read_packages(pre_set_packages) + + if not storage['arguments']['offline'] and not storage['arguments']['no_pkg_lookups']: + while True: + if len(packages): + # Verify packages that were given + print(_("Verifying that additional packages exist (this might take a few seconds)")) + valid, invalid = validate_package_list(packages) + + if invalid: + warn(f"Some packages could not be found in the repository: {invalid}") + packages = read_packages(valid) + continue + break + + return packages + + +def add_number_of_parrallel_downloads(input_number :Optional[int] = None) -> Optional[int]: + max_downloads = 5 + print(_(f"This option enables the number of parallel downloads that can occur during installation")) + print(_(f"Enter the number of parallel downloads to be enabled.\n (Enter a value between 1 to {max_downloads})\nNote:")) + print(_(f" - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )")) + print(_(f" - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )")) + print(_(f" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )")) + + while True: + try: + input_number = int(TextInput(_("[Default value: 0] > ")).run().strip() or 0) + if input_number <= 0: + input_number = 0 + elif input_number > max_downloads: + input_number = max_downloads + break + except: + print(_(f"Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]")) + + pacman_conf_path = pathlib.Path("/etc/pacman.conf") + with pacman_conf_path.open() as f: + pacman_conf = f.read().split("\n") + + with pacman_conf_path.open("w") as fwrite: + for line in pacman_conf: + if "ParallelDownloads" in line: + fwrite.write(f"ParallelDownloads = {input_number+1}\n") if not input_number == 0 else fwrite.write("#ParallelDownloads = 0\n") + else: + fwrite.write(f"{line}\n") + + return input_number + + +def select_additional_repositories(preset: List[str]) -> List[str]: + """ + Allows the user to select additional repositories (multilib, and testing) if desired. + + :return: The string as a selected repository + :rtype: string + """ + + repositories = ["multilib", "testing"] + + choice = Menu( + _('Choose which optional additional repositories to enable'), + repositories, + sort=False, + multi=True, + preset_values=preset, + allow_reset=True + ).run() + + match choice.type_: + case MenuSelectionType.Skip: return preset + case MenuSelectionType.Reset: return [] + case MenuSelectionType.Selection: return choice.single_value + + return [] diff --git a/archinstall/lib/interactions/locale_conf.py b/archinstall/lib/interactions/locale_conf.py new file mode 100644 index 00000000..de115202 --- /dev/null +++ b/archinstall/lib/interactions/locale_conf.py @@ -0,0 +1,43 @@ +from typing import Any, TYPE_CHECKING, Optional + +from ..locale import list_locales +from ..menu import Menu, MenuSelectionType + +if TYPE_CHECKING: + _: Any + + +def select_locale_lang(preset: Optional[str] = None) -> Optional[str]: + locales = list_locales() + locale_lang = set([locale.split()[0] for locale in locales]) + + choice = Menu( + _('Choose which locale language to use'), + list(locale_lang), + sort=True, + preset_values=preset + ).run() + + match choice.type_: + case MenuSelectionType.Selection: return choice.single_value + case MenuSelectionType.Skip: return preset + + return None + + +def select_locale_enc(preset: Optional[str] = None) -> Optional[str]: + locales = list_locales() + locale_enc = set([locale.split()[1] for locale in locales]) + + choice = Menu( + _('Choose which locale encoding to use'), + list(locale_enc), + sort=True, + preset_values=preset + ).run() + + match choice.type_: + case MenuSelectionType.Selection: return choice.single_value + case MenuSelectionType.Skip: return preset + + return None diff --git a/archinstall/lib/interactions/manage_users_conf.py b/archinstall/lib/interactions/manage_users_conf.py new file mode 100644 index 00000000..879578da --- /dev/null +++ b/archinstall/lib/interactions/manage_users_conf.py @@ -0,0 +1,106 @@ +from __future__ import annotations + +import re +from typing import Any, Dict, TYPE_CHECKING, List, Optional + +from .utils import get_password +from ..menu import Menu, ListManager +from ..models.users import User +from ..output import FormattedOutput + +if TYPE_CHECKING: + _: Any + + +class UserList(ListManager): + """ + subclass of ListManager for the managing of user accounts + """ + + def __init__(self, prompt: str, lusers: List[User]): + self._actions = [ + str(_('Add a user')), + str(_('Change password')), + str(_('Promote/Demote user')), + str(_('Delete User')) + ] + super().__init__(prompt, lusers, [self._actions[0]], self._actions[1:]) + + def reformat(self, data: List[User]) -> Dict[str, Any]: + table = FormattedOutput.as_table(data) + rows = table.split('\n') + + # these are the header rows of the table and do not map to any User obviously + # we're adding 2 spaces as prefix because the menu selector '> ' will be put before + # the selectable rows so the header has to be aligned + display_data: Dict[str, Optional[User]] = {f' {rows[0]}': None, f' {rows[1]}': None} + + for row, user in zip(rows[2:], data): + row = row.replace('|', '\\|') + display_data[row] = user + + return display_data + + def selected_action_display(self, user: User) -> str: + return user.username + + def handle_action(self, action: str, entry: Optional[User], data: List[User]) -> List[User]: + if 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 action == self._actions[1] and entry: # change password + prompt = str(_('Password for user "{}": ').format(entry.username)) + new_password = get_password(prompt=prompt) + if new_password: + user = next(filter(lambda x: x == entry, data)) + user.password = new_password + elif action == self._actions[2] and entry: # promote/demote + user = next(filter(lambda x: x == entry, data)) + user.sudo = False if user.sudo else True + elif action == self._actions[3] and entry: # delete + data = [d for d in data if d != entry] + + 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 + return False + + def _add_user(self) -> Optional[User]: + prompt = '\n\n' + str(_('Enter username (leave blank to skip): ')) + + while True: + username = input(prompt).strip(' ') + if not username: + return None + if not self._check_for_correct_username(username): + error_prompt = str(_("The username you entered is invalid. Try again")) + print(error_prompt) + else: + break + + password = get_password(prompt=str(_('Password for user "{}": ').format(username))) + + if not password: + return None + + choice = Menu( + str(_('Should "{}" be a superuser (sudo)?')).format(username), Menu.yes_no(), + skip=False, + default_option=Menu.yes(), + clear_screen=False, + show_search_hint=False + ).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]: + users = UserList(prompt, defined_users).run() + return users diff --git a/archinstall/lib/interactions/network_conf.py b/archinstall/lib/interactions/network_conf.py new file mode 100644 index 00000000..18a834a1 --- /dev/null +++ b/archinstall/lib/interactions/network_conf.py @@ -0,0 +1,172 @@ +from __future__ import annotations + +import ipaddress +from typing import Any, Optional, TYPE_CHECKING, List, Union, Dict + +from ..menu import MenuSelectionType, TextInput +from ..models.network_configuration import NetworkConfiguration, NicType + +from ..networking import list_interfaces +from ..output import FormattedOutput, warn +from ..menu import ListManager, Menu + +if TYPE_CHECKING: + _: Any + + +class ManualNetworkConfig(ListManager): + """ + subclass of ListManager for the managing of network configurations + """ + + def __init__(self, prompt: str, ifaces: List[NetworkConfiguration]): + self._actions = [ + str(_('Add interface')), + str(_('Edit interface')), + str(_('Delete interface')) + ] + + super().__init__(prompt, ifaces, [self._actions[0]], self._actions[1:]) + + def reformat(self, data: List[NetworkConfiguration]) -> Dict[str, Optional[NetworkConfiguration]]: + table = FormattedOutput.as_table(data) + rows = table.split('\n') + + # these are the header rows of the table and do not map to any User obviously + # we're adding 2 spaces as prefix because the menu selector '> ' will be put before + # the selectable rows so the header has to be aligned + display_data: Dict[str, Optional[NetworkConfiguration]] = {f' {rows[0]}': None, f' {rows[1]}': None} + + for row, iface in zip(rows[2:], data): + row = row.replace('|', '\\|') + display_data[row] = iface + + return display_data + + def selected_action_display(self, iface: NetworkConfiguration) -> str: + return iface.iface if iface.iface else '' + + def handle_action(self, action: str, entry: Optional[NetworkConfiguration], data: List[NetworkConfiguration]): + if action == self._actions[0]: # add + iface_name = self._select_iface(data) + if iface_name: + iface = NetworkConfiguration(NicType.MANUAL, iface=iface_name) + iface = self._edit_iface(iface) + data += [iface] + elif entry: + if action == self._actions[1]: # edit interface + data = [d for d in data if d.iface != entry.iface] + data.append(self._edit_iface(entry)) + elif action == self._actions[2]: # delete + data = [d for d in data if d != entry] + + return data + + def _select_iface(self, data: List[NetworkConfiguration]) -> Optional[Any]: + all_ifaces = list_interfaces().values() + existing_ifaces = [d.iface for d in data] + available = set(all_ifaces) - set(existing_ifaces) + choice = Menu(str(_('Select interface to add')), list(available), skip=True).run() + + if choice.type_ == MenuSelectionType.Skip: + return None + + return choice.value + + def _edit_iface(self, edit_iface: NetworkConfiguration): + iface_name = edit_iface.iface + modes = ['DHCP (auto detect)', 'IP (static)'] + 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, skip=False).run() + + 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() + # Implemented new check for correct IP/subnet input + try: + ipaddress.ip_interface(ip) + break + except ValueError: + warn("You need to enter a valid IP in IP-config mode") + + # Implemented new check for correct gateway IP address + gateway = None + + while 1: + gateway = TextInput( + _('Enter your gateway (router) IP address or leave blank for none: '), + edit_iface.gateway + ).run().strip() + try: + if len(gateway) > 0: + ipaddress.ip_address(gateway) + break + except ValueError: + warn("You need to enter a valid gateway (router) IP address") + + if edit_iface.dns: + display_dns = ' '.join(edit_iface.dns) + else: + 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(' ') + + return NetworkConfiguration(NicType.MANUAL, iface=iface_name, ip=ip, gateway=gateway, dns=dns, dhcp=False) + else: + # this will contain network iface names + return NetworkConfiguration(NicType.MANUAL, iface=iface_name) + + +def ask_to_configure_network( + preset: Union[NetworkConfiguration, List[NetworkConfiguration]] +) -> Optional[NetworkConfiguration | List[NetworkConfiguration]]: + """ + Configure the network on the newly installed system + """ + network_options = { + 'none': str(_('No network configuration')), + 'iso_config': str(_('Copy ISO network configuration to installation')), + 'network_manager': str(_('Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)')), + 'manual': str(_('Manual configuration')) + } + # for this routine it's easier to set the cursor position rather than a preset value + cursor_idx = None + + if preset and not isinstance(preset, list): + if preset.type == 'iso_config': + cursor_idx = 0 + elif preset.type == 'network_manager': + cursor_idx = 1 + + 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, + allow_reset=True, + allow_reset_warning_msg=warning + ).run() + + match choice.type_: + case MenuSelectionType.Skip: return preset + case MenuSelectionType.Reset: return None + + if choice.value == network_options['none']: + return None + elif choice.value == network_options['iso_config']: + return NetworkConfiguration(NicType.ISO) + elif choice.value == network_options['network_manager']: + return NetworkConfiguration(NicType.NM) + elif choice.value == network_options['manual']: + preset_ifaces = preset if isinstance(preset, list) else [] + return ManualNetworkConfig('Configure interfaces', preset_ifaces).run() + + return preset diff --git a/archinstall/lib/interactions/system_conf.py b/archinstall/lib/interactions/system_conf.py new file mode 100644 index 00000000..bbcb5b23 --- /dev/null +++ b/archinstall/lib/interactions/system_conf.py @@ -0,0 +1,117 @@ +from __future__ import annotations + +from typing import List, Any, Dict, TYPE_CHECKING, Optional + +from ..hardware import AVAILABLE_GFX_DRIVERS, SysInfo +from ..menu import MenuSelectionType, Menu +from ..models.bootloader import Bootloader + +if TYPE_CHECKING: + _: Any + + +def select_kernel(preset: List[str] = []) -> List[str]: + """ + Asks the user to select a kernel for system. + + :return: The string as a selected kernel + :rtype: string + """ + + kernels = ["linux", "linux-lts", "linux-zen", "linux-hardened"] + default_kernel = "linux" + + 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, + allow_reset=True, + allow_reset_warning_msg=warning + ).run() + + match choice.type_: + case MenuSelectionType.Skip: return preset + case MenuSelectionType.Reset: return [] + case MenuSelectionType.Selection: return choice.value # type: ignore + + +def ask_for_bootloader(preset: Bootloader) -> Bootloader: + # when the system only supports grub + if not SysInfo.has_uefi(): + options = [Bootloader.Grub.value] + default = Bootloader.Grub.value + else: + options = Bootloader.values() + default = Bootloader.Systemd.value + + preset_value = preset.value if preset else None + + choice = Menu( + _('Choose a bootloader'), + options, + preset_values=preset_value, + sort=False, + default_option=default + ).run() + + match choice.type_: + case MenuSelectionType.Skip: return preset + case MenuSelectionType.Selection: return Bootloader(choice.value) + + return preset + + +def select_driver(options: Dict[str, Any] = {}, current_value: Optional[str] = None) -> Optional[str]: + """ + Some what convoluted function, whose job is simple. + Select a graphics driver from a pre-defined set of popular options. + + (The template xorg is for beginner users, not advanced, and should + there for appeal to the general public first and edge cases later) + """ + + if not options: + options = AVAILABLE_GFX_DRIVERS + + drivers = sorted(list(options.keys())) + + if drivers: + title = '' + if SysInfo.has_amd_graphics(): + 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' + if SysInfo.has_intel_graphics(): + 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 SysInfo.has_nvidia_graphics(): + title += str(_('For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n')) + + title += str(_('\nSelect a graphics driver or leave blank to install all open-source drivers')) + + preset = current_value if current_value else None + choice = Menu(title, drivers, preset_values=preset).run() + + if choice.type_ != MenuSelectionType.Selection: + return None + + return choice.value # type: ignore + + return current_value + + +def ask_for_swap(preset: bool = True) -> bool: + if preset: + 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() + + match choice.type_: + case MenuSelectionType.Skip: return preset + case MenuSelectionType.Selection: return False if choice.value == Menu.no() else True + + return preset diff --git a/archinstall/lib/interactions/utils.py b/archinstall/lib/interactions/utils.py new file mode 100644 index 00000000..f6b5b2d3 --- /dev/null +++ b/archinstall/lib/interactions/utils.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +import getpass +from typing import Any, Optional, TYPE_CHECKING + +from ..models import PasswordStrength +from ..output import log, error + +if TYPE_CHECKING: + _: Any + +# used for signal handler +SIG_TRIGGER = None + + +def get_password(prompt: str = '') -> Optional[str]: + if not prompt: + prompt = _("Enter a password: ") + + while password := getpass.getpass(prompt): + if len(password.strip()) <= 0: + break + + strength = PasswordStrength.strength(password) + log(f'Password strength: {strength.value}', fg=strength.color()) + + passwd_verification = getpass.getpass(prompt=_('And one more time for verification: ')) + if password != passwd_verification: + error(' * Passwords did not match * ') + continue + + return password + + return None diff --git a/archinstall/lib/locale.py b/archinstall/lib/locale.py new file mode 100644 index 00000000..0a36c072 --- /dev/null +++ b/archinstall/lib/locale.py @@ -0,0 +1,68 @@ +from typing import Iterator, List + +from .exceptions import ServiceException, SysCallError +from .general import SysCommand +from .output import error + + +def list_keyboard_languages() -> Iterator[str]: + for line in SysCommand("localectl --no-pager list-keymaps", environment_vars={'SYSTEMD_COLORS': '0'}): + yield line.decode('UTF-8').strip() + + +def list_locales() -> List[str]: + with open('/etc/locale.gen', 'r') as fp: + locales = [] + # before the list of locales begins there's an empty line with a '#' in front + # so we'll collect the localels from bottom up and halt when we're donw + entries = fp.readlines() + entries.reverse() + + for entry in entries: + text = entry.replace('#', '').strip() + if text == '': + break + locales.append(text) + + locales.reverse() + return locales + + +def list_x11_keyboard_languages() -> Iterator[str]: + for line in SysCommand("localectl --no-pager list-x11-keymap-layouts", environment_vars={'SYSTEMD_COLORS': '0'}): + yield line.decode('UTF-8').strip() + + +def verify_keyboard_layout(layout :str) -> bool: + for language in list_keyboard_languages(): + if layout.lower() == language.lower(): + return True + return False + + +def verify_x11_keyboard_layout(layout :str) -> bool: + for language in list_x11_keyboard_languages(): + if layout.lower() == language.lower(): + return True + return False + + +def set_keyboard_language(locale :str) -> bool: + if len(locale.strip()): + if not verify_keyboard_layout(locale): + error(f"Invalid keyboard locale specified: {locale}") + return False + + try: + SysCommand(f'localectl set-keymap {locale}') + except SysCallError as err: + raise ServiceException(f"Unable to set locale '{locale}' for console: {err}") + + return True + + return False + + +def list_timezones() -> Iterator[str]: + for line in SysCommand("timedatectl --no-pager list-timezones", environment_vars={'SYSTEMD_COLORS': '0'}): + yield line.decode('UTF-8').strip() diff --git a/archinstall/lib/locale_helpers.py b/archinstall/lib/locale_helpers.py deleted file mode 100644 index efb0365f..00000000 --- a/archinstall/lib/locale_helpers.py +++ /dev/null @@ -1,176 +0,0 @@ -import logging -from typing import Iterator, List, Callable, Optional - -from .exceptions import ServiceException, SysCallError -from .general import SysCommand -from .output import log -from .storage import storage - - -def list_keyboard_languages() -> Iterator[str]: - for line in SysCommand("localectl --no-pager list-keymaps", environment_vars={'SYSTEMD_COLORS': '0'}): - yield line.decode('UTF-8').strip() - - -def list_locales() -> List[str]: - with open('/etc/locale.gen', 'r') as fp: - locales = [] - # before the list of locales begins there's an empty line with a '#' in front - # so we'll collect the localels from bottom up and halt when we're donw - entries = fp.readlines() - entries.reverse() - - for entry in entries: - text = entry.replace('#', '').strip() - if text == '': - break - locales.append(text) - - locales.reverse() - return locales - -def get_locale_mode_text(mode): - if mode == 'LC_ALL': - mode_text = "general (LC_ALL)" - elif mode == "LC_CTYPE": - mode_text = "Character set" - elif mode == "LC_NUMERIC": - mode_text = "Numeric values" - elif mode == "LC_TIME": - mode_text = "Time Values" - elif mode == "LC_COLLATE": - mode_text = "sort order" - elif mode == "LC_MESSAGES": - mode_text = "text messages" - else: - mode_text = "Unassigned" - return mode_text - - -def reset_cmd_locale(): - """ sets the cmd_locale to its saved default """ - storage['CMD_LOCALE'] = storage.get('CMD_LOCALE_DEFAULT',{}) - - -def unset_cmd_locale(): - """ archinstall will use the execution environment default """ - storage['CMD_LOCALE'] = {} - - -def set_cmd_locale( - general: Optional[str] = None, - charset :str = 'C', - numbers :str = 'C', - time :str = 'C', - collate :str = 'C', - messages :str = 'C' -): - """ - Set the cmd locale. - If the parameter general is specified, it takes precedence over the rest (might as well not exist) - The rest define some specific settings above the installed default language. If anyone of this parameters is none means the installation default - """ - installed_locales = list_installed_locales() - result = {} - if general: - if general in installed_locales: - storage['CMD_LOCALE'] = {'LC_ALL':general} - else: - log(f"{get_locale_mode_text('LC_ALL')} {general} is not installed. Defaulting to C",fg="yellow",level=logging.WARNING) - return - - if numbers: - if numbers in installed_locales: - result["LC_NUMERIC"] = numbers - else: - log(f"{get_locale_mode_text('LC_NUMERIC')} {numbers} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING) - if charset: - if charset in installed_locales: - result["LC_CTYPE"] = charset - else: - log(f"{get_locale_mode_text('LC_CTYPE')} {charset} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING) - if time: - if time in installed_locales: - result["LC_TIME"] = time - else: - log(f"{get_locale_mode_text('LC_TIME')} {time} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING) - if collate: - if collate in installed_locales: - result["LC_COLLATE"] = collate - else: - log(f"{get_locale_mode_text('LC_COLLATE')} {collate} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING) - if messages: - if messages in installed_locales: - result["LC_MESSAGES"] = messages - else: - log(f"{get_locale_mode_text('LC_MESSAGES')} {messages} is not installed. Defaulting to installation language",fg="yellow",level=logging.WARNING) - storage['CMD_LOCALE'] = result - -def host_locale_environ(func :Callable): - """ decorator when we want a function executing in the host's locale environment """ - def wrapper(*args, **kwargs): - unset_cmd_locale() - result = func(*args,**kwargs) - reset_cmd_locale() - return result - return wrapper - -def c_locale_environ(func :Callable): - """ decorator when we want a function executing in the C locale environment """ - def wrapper(*args, **kwargs): - set_cmd_locale(general='C') - result = func(*args,**kwargs) - reset_cmd_locale() - return result - return wrapper - -def list_installed_locales() -> List[str]: - lista = [] - for line in SysCommand('locale -a'): - lista.append(line.decode('UTF-8').strip()) - return lista - -def list_x11_keyboard_languages() -> Iterator[str]: - for line in SysCommand("localectl --no-pager list-x11-keymap-layouts", environment_vars={'SYSTEMD_COLORS': '0'}): - yield line.decode('UTF-8').strip() - - -def verify_keyboard_layout(layout :str) -> bool: - for language in list_keyboard_languages(): - if layout.lower() == language.lower(): - return True - return False - - -def verify_x11_keyboard_layout(layout :str) -> bool: - for language in list_x11_keyboard_languages(): - if layout.lower() == language.lower(): - return True - return False - - -def search_keyboard_layout(layout :str) -> Iterator[str]: - for language in list_keyboard_languages(): - if layout.lower() in language.lower(): - yield language - - -def set_keyboard_language(locale :str) -> bool: - if len(locale.strip()): - if not verify_keyboard_layout(locale): - log(f"Invalid keyboard locale specified: {locale}", fg="red", level=logging.ERROR) - return False - - try: - SysCommand(f'localectl set-keymap {locale}') - except SysCallError as error: - raise ServiceException(f"Unable to set locale '{locale}' for console: {error}") - - return True - - return False - - -def list_timezones() -> Iterator[str]: - for line in SysCommand("timedatectl --no-pager list-timezones", environment_vars={'SYSTEMD_COLORS': '0'}): - yield line.decode('UTF-8').strip() diff --git a/archinstall/lib/luks.py b/archinstall/lib/luks.py index 53a5e8d2..f9b09b53 100644 --- a/archinstall/lib/luks.py +++ b/archinstall/lib/luks.py @@ -1,6 +1,5 @@ from __future__ import annotations -import logging import shlex import time from dataclasses import dataclass @@ -9,7 +8,7 @@ from typing import Optional, List from . import disk from .general import SysCommand, generate_password, SysCommandWorker -from .output import log +from .output import info, debug from .exceptions import SysCallError, DiskError from .storage import storage @@ -61,7 +60,7 @@ class Luks2: iter_time: int = 10000, key_file: Optional[Path] = None ) -> Path: - log(f'Luks2 encrypting: {self.luks_dev_path}', level=logging.INFO) + info(f'Luks2 encrypting: {self.luks_dev_path}') byte_password = self._password_bytes() @@ -95,21 +94,21 @@ class Luks2: try: SysCommand(cryptsetup_args) break - except SysCallError as error: + except SysCallError as err: time.sleep(storage['DISK_TIMEOUTS']) if retry_attempt != storage['DISK_RETRY_ATTEMPTS'] - 1: continue - if error.exit_code == 1: - log(f'luks2 partition currently in use: {self.luks_dev_path}') - log('Attempting to unmount, crypt-close and trying encryption again') + if err.exit_code == 1: + info(f'luks2 partition currently in use: {self.luks_dev_path}') + info('Attempting to unmount, crypt-close and trying encryption again') self.lock() # Then try again to set up the crypt-device SysCommand(cryptsetup_args) else: - raise DiskError(f'Could not encrypt volume "{self.luks_dev_path}": {error}') + raise DiskError(f'Could not encrypt volume "{self.luks_dev_path}": {err}') return key_file @@ -119,7 +118,7 @@ class Luks2: try: return SysCommand(command).decode().strip() # type: ignore except SysCallError as err: - log(f'Unable to get UUID for Luks device: {self.luks_dev_path}', level=logging.INFO) + info(f'Unable to get UUID for Luks device: {self.luks_dev_path}') raise err def is_unlocked(self) -> bool: @@ -133,7 +132,7 @@ class Luks2: :param key_file: An alternative key file :type key_file: Path """ - log(f'Unlocking luks2 device: {self.luks_dev_path}', level=logging.DEBUG) + debug(f'Unlocking luks2 device: {self.luks_dev_path}') if not self.mapper_name: raise ValueError('mapper name missing') @@ -170,11 +169,11 @@ class Luks2: for child in lsblk_info.children: # Unmount the child location for mountpoint in child.mountpoints: - log(f'Unmounting {mountpoint}', level=logging.DEBUG) + debug(f'Unmounting {mountpoint}') disk.device_handler.umount(mountpoint, recursive=True) # And close it if possible. - log(f"Closing crypt device {child.name}", level=logging.DEBUG) + debug(f"Closing crypt device {child.name}") SysCommand(f"cryptsetup close {child.name}") self._mapper_dev = None @@ -194,10 +193,10 @@ class Luks2: if key_file.exists(): if not override: - log(f'Key file {key_file} already exists, keeping existing') + info(f'Key file {key_file} already exists, keeping existing') return else: - log(f'Key file {key_file} already exists, overriding') + info(f'Key file {key_file} already exists, overriding') key_file_path.mkdir(parents=True, exist_ok=True) @@ -210,7 +209,7 @@ class Luks2: self._crypttab(crypttab_path, key_file, options=["luks", "key-slot=1"]) def _add_key(self, key_file: Path): - log(f'Adding additional key-file {key_file}', level=logging.INFO) + info(f'Adding additional key-file {key_file}') command = f'/usr/bin/cryptsetup -q -v luksAddKey {self.luks_dev_path} {key_file}' worker = SysCommandWorker(command, environment_vars={'LC_ALL': 'C'}) @@ -230,7 +229,7 @@ class Luks2: key_file: Path, options: List[str] ) -> None: - log(f'Adding crypttab entry for key {key_file}', level=logging.INFO) + info(f'Adding crypttab entry for key {key_file}') with open(crypttab_path, 'a') as crypttab: opt = ','.join(options) diff --git a/archinstall/lib/menu/abstract_menu.py b/archinstall/lib/menu/abstract_menu.py index e44d65a4..2bd56374 100644 --- a/archinstall/lib/menu/abstract_menu.py +++ b/archinstall/lib/menu/abstract_menu.py @@ -1,11 +1,10 @@ from __future__ import annotations -import logging from typing import Callable, Any, List, Iterator, Tuple, Optional, Dict, TYPE_CHECKING from .menu import Menu, MenuSelectionType -from ..locale_helpers import set_keyboard_language -from ..output import log +from ..locale import set_keyboard_language +from ..output import error from ..translationhandler import TranslationHandler, Language if TYPE_CHECKING: @@ -211,7 +210,7 @@ class AbstractMenu: # TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager # TODO: skip processing when it comes from a planified exit if len(args) >= 2 and args[1]: - log(args[1], level=logging.ERROR, fg='red') + error(args[1]) print(" Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues") raise args[1] @@ -483,7 +482,7 @@ class AbstractMenu: yield item def _select_archinstall_language(self, preset: Language) -> Language: - from ..user_interaction.general_conf import select_archinstall_language + from ..interactions.general_conf import select_archinstall_language language = select_archinstall_language(self.translation_handler.translated_languages, preset) self._translation_handler.activate(language) return language diff --git a/archinstall/lib/menu/menu.py b/archinstall/lib/menu/menu.py index f3fdb85f..768dfe55 100644 --- a/archinstall/lib/menu/menu.py +++ b/archinstall/lib/menu/menu.py @@ -6,11 +6,8 @@ from typing import Dict, List, Union, Any, TYPE_CHECKING, Optional, Callable from simple_term_menu import TerminalMenu # type: ignore from ..exceptions import RequirementError -from ..output import log +from ..output import debug -from collections.abc import Iterable -import sys -import logging if TYPE_CHECKING: _: Any @@ -127,33 +124,15 @@ class Menu(TerminalMenu): :param extra_bottom_space: Add an extra empty line at the end of the menu :type extra_bottom_space: bool """ - # we guarantee the inmutability of the options outside the class. - # an unknown number of iterables (.keys(),.values(),generator,...) can't be directly copied, in this case - # we recourse to make them lists before, but thru an exceptions - # this is the old code, which is not maintenable with more types - # options = copy(list(p_options) if isinstance(p_options,(type({}.keys()),type({}.values()))) else p_options) - # We check that the options are iterable. If not we abort. Else we copy them to lists - # it options is a dictionary we use the values as entries of the list - # if options is a string object, each character becomes an entry - # if options is a list, we implictily build a copy to maintain immutability - if not isinstance(p_options,Iterable): - log(f"Objects of type {type(p_options)} is not iterable, and are not supported at Menu",fg="red") - 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.") - - if isinstance(p_options,dict): + if isinstance(p_options, Dict): options = list(p_options.keys()) else: options = list(p_options) if not options: - log(" * Menu didn't find any options to choose from * ", fg='red') - log(f"invalid parameter at Menu() call was at <{sys._getframe(1).f_code.co_name}>",level=logging.WARNING) raise RequirementError('Menu.__init__() requires at least one option to proceed.') if any([o for o in options if not isinstance(o, str)]): - log(" * Menu options must be of type string * ", fg='red') - log(f"invalid parameter at Menu() call was at <{sys._getframe(1).f_code.co_name}>",level=logging.WARNING) raise RequirementError('Menu.__init__() requires the options to be of type string') if sort: @@ -343,7 +322,7 @@ class Menu(TerminalMenu): idx = self._menu_options.index(self._default_menu_value) indexes.append(idx) except (IndexError, ValueError): - log(f'Error finding index of {p}: {self._menu_options}', level=logging.DEBUG) + debug(f'Error finding index of {p}: {self._menu_options}') if len(indexes) == 0: indexes.append(0) diff --git a/archinstall/lib/mirrors.py b/archinstall/lib/mirrors.py index c6c5c8e4..62a0b081 100644 --- a/archinstall/lib/mirrors.py +++ b/archinstall/lib/mirrors.py @@ -1,4 +1,3 @@ -import logging import pathlib import urllib.error import urllib.request @@ -6,7 +5,7 @@ from typing import Union, Iterable, Dict, Any, List from dataclasses import dataclass from .general import SysCommand -from .output import log +from .output import info, warn from .exceptions import SysCallError from .storage import storage @@ -136,7 +135,7 @@ def use_mirrors( regions: Dict[str, Iterable[str]], destination: str = '/etc/pacman.d/mirrorlist' ): - log(f'A new package mirror-list has been created: {destination}', level=logging.INFO) + info(f'A new package mirror-list has been created: {destination}') with open(destination, 'w') as mirrorlist: for region, mirrors in regions.items(): for mirror in mirrors: @@ -170,7 +169,7 @@ def list_mirrors(sort_order :List[str] = ["https", "http"]) -> Dict[str, Any]: try: response = urllib.request.urlopen(url) except urllib.error.URLError as err: - log(f'Could not fetch an active mirror-list: {err}', level=logging.WARNING, fg="orange") + warn(f'Could not fetch an active mirror-list: {err}') return regions mirrorlist = response.read() diff --git a/archinstall/lib/models/bootloader.py b/archinstall/lib/models/bootloader.py index 38254c99..e21cda33 100644 --- a/archinstall/lib/models/bootloader.py +++ b/archinstall/lib/models/bootloader.py @@ -1,12 +1,11 @@ from __future__ import annotations -import logging import sys from enum import Enum from typing import List -from ..hardware import has_uefi -from ..output import log +from ..hardware import SysInfo +from ..output import warn class Bootloader(Enum): @@ -23,7 +22,7 @@ class Bootloader(Enum): @classmethod def get_default(cls) -> Bootloader: - if has_uefi(): + if SysInfo.has_uefi(): return Bootloader.Systemd else: return Bootloader.Grub @@ -35,6 +34,6 @@ class Bootloader(Enum): if bootloader not in cls.values(): values = ', '.join(cls.values()) - log(f'Invalid bootloader value "{bootloader}". Allowed values: {values}', level=logging.WARN) + warn(f'Invalid bootloader value "{bootloader}". Allowed values: {values}') sys.exit(1) return Bootloader(bootloader) diff --git a/archinstall/lib/models/network_configuration.py b/archinstall/lib/models/network_configuration.py index a8795fc1..93dd1c44 100644 --- a/archinstall/lib/models/network_configuration.py +++ b/archinstall/lib/models/network_configuration.py @@ -1,11 +1,10 @@ from __future__ import annotations -import logging from dataclasses import dataclass, field from enum import Enum from typing import List, Optional, Dict, Union, Any, TYPE_CHECKING, Tuple -from ..output import log +from ..output import debug from ..profile import ProfileConfiguration if TYPE_CHECKING: @@ -138,8 +137,7 @@ class NetworkConfigurationHandler: iface = manual_config.get('iface', None) if iface is None: - log(_('No iface specified for manual configuration')) - exit(1) + raise ValueError('No iface specified for manual configuration') if manual_config.get('dhcp', False) or not any([manual_config.get(v, '') for v in ['ip', 'gateway', 'dns']]): configurations.append( @@ -148,8 +146,7 @@ class NetworkConfigurationHandler: else: ip = manual_config.get('ip', '') if not ip: - log(_('Manual nic configuration with no auto DHCP requires an IP address'), fg='red') - exit(1) + raise ValueError('Manual nic configuration with no auto DHCP requires an IP address') dns = manual_config.get('dns', []) if not isinstance(dns, list): @@ -173,8 +170,7 @@ class NetworkConfigurationHandler: return NicType(nic_type) except ValueError: options = [e.value for e in NicType] - log(_('Unknown nic type: {}. Possible values are {}').format(nic_type, options), fg='red') - exit(1) + raise ValueError(f'Unknown nic type: {nic_type}. Possible values are {options}') def parse_arguments(self, config: Any): if isinstance(config, list): # new data format @@ -187,4 +183,4 @@ class NetworkConfigurationHandler: else: # manual configuration settings self._configuration = self._parse_manual_config([config]) else: - log(f'Unable to parse network configuration: {config}', level=logging.DEBUG) + debug(f'Unable to parse network configuration: {config}') diff --git a/archinstall/lib/networking.py b/archinstall/lib/networking.py index b858daaf..6906c320 100644 --- a/archinstall/lib/networking.py +++ b/archinstall/lib/networking.py @@ -1,4 +1,3 @@ -import logging import os import socket import ssl @@ -8,18 +7,16 @@ from urllib.error import URLError from urllib.parse import urlencode from urllib.request import urlopen -from .exceptions import HardwareIncompatibilityError, SysCallError -from .general import SysCommand -from .output import log +from .exceptions import SysCallError +from .output import error, info, debug from .pacman import run_pacman -from .storage import storage def get_hw_addr(ifname :str) -> str: import fcntl s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', bytes(ifname, 'utf-8')[:15])) - return ':'.join('%02x' % b for b in info[18:24]) + ret = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', bytes(ifname, 'utf-8')[:15])) + return ':'.join('%02x' % b for b in ret[18:24]) def list_interfaces(skip_loopback :bool = True) -> Dict[str, str]: @@ -36,26 +33,26 @@ def list_interfaces(skip_loopback :bool = True) -> Dict[str, str]: def check_mirror_reachable() -> bool: - log("Testing connectivity to the Arch Linux mirrors ...", level=logging.INFO) + info("Testing connectivity to the Arch Linux mirrors...") try: run_pacman("-Sy") return True except SysCallError as err: if os.geteuid() != 0: - log("check_mirror_reachable() uses 'pacman -Sy' which requires root.", level=logging.ERROR, fg="red") - log(f'exit_code: {err.exit_code}, Error: {err.message}', level=logging.DEBUG) + error("check_mirror_reachable() uses 'pacman -Sy' which requires root.") + debug(f'exit_code: {err.exit_code}, Error: {err.message}') return False def update_keyring() -> bool: - log("Updating archlinux-keyring ...", level=logging.INFO) + info("Updating archlinux-keyring ...") try: run_pacman("-Sy --noconfirm archlinux-keyring") return True except SysCallError: if os.geteuid() != 0: - log("update_keyring() uses 'pacman -Sy archlinux-keyring' which requires root.", level=logging.ERROR, fg="red") + error("update_keyring() uses 'pacman -Sy archlinux-keyring' which requires root.") return False @@ -80,38 +77,6 @@ def enrich_iface_types(interfaces: Union[Dict[str, Any], List[str]]) -> Dict[str return result -def wireless_scan(interface :str) -> None: - interfaces = enrich_iface_types(list(list_interfaces().values())) - if interfaces[interface] != 'WIRELESS': - raise HardwareIncompatibilityError(f"Interface {interface} is not a wireless interface: {interfaces}") - - try: - SysCommand(f"iwctl station {interface} scan") - except SysCallError as error: - raise SystemError(f"Could not scan for wireless networks: {error}") - - if '_WIFI' not in storage: - storage['_WIFI'] = {} - if interface not in storage['_WIFI']: - storage['_WIFI'][interface] = {} - - storage['_WIFI'][interface]['scanning'] = True - - -# TODO: Full WiFi experience might get evolved in the future, pausing for now 2021-01-25 -def get_wireless_networks(interface :str) -> None: - # TODO: Make this oneliner pritter to check if the interface is scanning or not. - # TODO: Rename this to list_wireless_networks() as it doesn't return anything - if '_WIFI' not in storage or interface not in storage['_WIFI'] or storage['_WIFI'][interface].get('scanning', False) is False: - import time - - wireless_scan(interface) - time.sleep(5) - - for line in SysCommand(f"iwctl station {interface} get-networks"): - print(line) - - def fetch_data_from_url(url: str, params: Optional[Dict] = None) -> str: ssl_context = ssl.create_default_context() ssl_context.check_hostname = False diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py index d65f835f..bd31b5b3 100644 --- a/archinstall/lib/output.py +++ b/archinstall/lib/output.py @@ -1,15 +1,16 @@ import logging import os import sys +from enum import Enum + from pathlib import Path from typing import Dict, Union, List, Any, Callable, Optional +from dataclasses import asdict, is_dataclass from .storage import storage -from dataclasses import asdict, is_dataclass class FormattedOutput: - @classmethod def values( cls, @@ -118,7 +119,7 @@ class FormattedOutput: class Journald: @staticmethod - def log(message :str, level :int = logging.DEBUG) -> None: + def log(message: str, level: int = logging.DEBUG) -> None: try: import systemd.journal # type: ignore except ModuleNotFoundError: @@ -134,16 +135,37 @@ class Journald: log_adapter.log(level, message) -# TODO: Replace log() for session based logging. -class SessionLogging: - def __init__(self): - pass +def check_log_permissions(): + filename = storage.get('LOG_FILE', None) + + if not filename: + return + + log_dir = storage.get('LOG_PATH', Path('./')) + absolute_logfile = log_dir / filename + + try: + log_dir.mkdir(exist_ok=True, parents=True) + with absolute_logfile.open('a') as fp: + fp.write('') + except PermissionError: + # Fallback to creating the log file in the current folder + fallback_log_file = Path('./').absolute() / filename + absolute_logfile = fallback_log_file + absolute_logfile.mkdir(exist_ok=True, parents=True) + storage['LOG_PATH'] = Path('./').absolute() + err_string = f"Not enough permission to place log file at {absolute_logfile}, creating it in {fallback_log_file} instead." + warn(err_string) -# Found first reference here: https://stackoverflow.com/questions/7445658/how-to-detect-if-the-console-does-support-ansi-escape-codes-in-python -# And re-used this: https://github.com/django/django/blob/master/django/core/management/color.py#L12 -def supports_color() -> bool: + +def _supports_color() -> bool: """ + Found first reference here: + https://stackoverflow.com/questions/7445658/how-to-detect-if-the-console-does-support-ansi-escape-codes-in-python + And re-used this: + https://github.com/django/django/blob/master/django/core/management/color.py#L12 + Return True if the running system's terminal supports color, and False otherwise. """ @@ -154,13 +176,30 @@ def supports_color() -> bool: return supported_platform and is_a_tty -# Heavily influenced by: https://github.com/django/django/blob/ae8338daf34fd746771e0678081999b656177bae/django/utils/termcolors.py#L13 -# Color options here: https://askubuntu.com/questions/528928/how-to-do-underline-bold-italic-strikethrough-color-background-and-size-i -def stylize_output(text: str, *opts :str, **kwargs) -> str: +class Font(Enum): + bold = '1' + italic = '3' + underscore = '4' + blink = '5' + reverse = '7' + conceal = '8' + + +def _stylize_output( + text: str, + fg: str, + bg: Optional[str], + reset: bool, + font: List[Font] = [], +) -> str: """ + Heavily influenced by: + https://github.com/django/django/blob/ae8338daf34fd746771e0678081999b656177bae/django/utils/termcolors.py#L13 + Color options here: + https://askubuntu.com/questions/528928/how-to-do-underline-bold-italic-strikethrough-color-background-and-size-i + Adds styling to a text given a set of color arguments. """ - opt_dict = {'bold': '1', 'italic': '3', 'underscore': '4', 'blink': '5', 'reverse': '7', 'conceal': '8'} colors = { 'black' : '0', 'red' : '1', @@ -178,65 +217,72 @@ def stylize_output(text: str, *opts :str, **kwargs) -> str: 'darkgray' : '8;5;240', 'lightgray' : '8;5;256' } + foreground = {key: f'3{colors[key]}' for key in colors} background = {key: f'4{colors[key]}' for key in colors} - reset = '0' - code_list = [] - if text == '' and len(opts) == 1 and opts[0] == 'reset': - return '\x1b[%sm' % reset - for k, v in kwargs.items(): - if k == 'fg': - code_list.append(foreground[str(v)]) - elif k == 'bg': - code_list.append(background[str(v)]) + if text == '' and reset: + return '\x1b[%sm' % '0' + + code_list.append(foreground[str(fg)]) + + if bg: + code_list.append(background[str(bg)]) + + for o in font: + code_list.append(o.value) + + ansi = ';'.join(code_list) + + return f'\033[{ansi}m{text}\033[0m' + - for o in opts: - if o in opt_dict: - code_list.append(opt_dict[o]) +def info(*msgs: str): + log(*msgs, level=logging.INFO) - if 'noreset' not in opts: - text = '%s\x1b[%sm' % (text or '', reset) - return '%s%s' % (('\x1b[%sm' % ';'.join(code_list)), text or '') +def debug(*msgs: str): + log(*msgs, level=logging.DEBUG) -def log(*args :str, **kwargs :Union[str, int, Dict[str, Union[str, int]]]) -> None: - string = orig_string = ' '.join([str(x) for x in args]) +def error(*msgs: str): + log(*msgs, level=logging.ERROR, fg='red') + + +def warn(*msgs: str): + log(*msgs, level=logging.WARNING, fg='yellow') + + +def log( + *msgs: str, + level: int = logging.INFO, + fg: str = 'white', + bg: Optional[str] = None, + reset: bool = False, + font: List[Font] = [] +): + text = orig_string = ' '.join([str(x) for x in msgs]) # Attempt to colorize the output if supported # Insert default colors and override with **kwargs - if supports_color(): - kwargs = {'fg': 'white', **kwargs} - string = stylize_output(string, **kwargs) + if _supports_color(): + text = _stylize_output(text, fg, bg, reset, font) # If a logfile is defined in storage, # we use that one to output everything if filename := storage.get('LOG_FILE', None): - absolute_logfile = os.path.join(storage.get('LOG_PATH', './'), filename) + log_dir = storage.get('LOG_PATH', Path('./')) + absolute_logfile = log_dir / filename - try: - Path(absolute_logfile).parents[0].mkdir(exist_ok=True, parents=True) - with open(absolute_logfile, 'a') as log_file: - log_file.write("") - except PermissionError: - # Fallback to creating the log file in the current folder - err_string = f"Not enough permission to place log file at {absolute_logfile}, creating it in {Path('./').absolute() / filename} instead." - absolute_logfile = Path('./').absolute() / filename - absolute_logfile.parents[0].mkdir(exist_ok=True) - absolute_logfile = str(absolute_logfile) - storage['LOG_PATH'] = './' - log(err_string, fg="red") - - with open(absolute_logfile, 'a') as log_file: - log_file.write(f"{orig_string}\n") - - Journald.log(string, level=int(str(kwargs.get('level', logging.INFO)))) + with open(absolute_logfile, 'a') as fp: + fp.write(f"{orig_string}\n") + + Journald.log(text, level=level) # Finally, print the log unless we skipped it based on level. # We use sys.stdout.write()+flush() instead of print() to try and # fix issue #94 - if kwargs.get('level', logging.INFO) != logging.DEBUG or storage.get('arguments', {}).get('verbose', False): - sys.stdout.write(f"{string}\n") + if level != logging.DEBUG or storage.get('arguments', {}).get('verbose', False): + sys.stdout.write(f"{text}\n") sys.stdout.flush() diff --git a/archinstall/lib/pacman.py b/archinstall/lib/pacman.py index 0dfd5afa..f5514f05 100644 --- a/archinstall/lib/pacman.py +++ b/archinstall/lib/pacman.py @@ -1,10 +1,9 @@ -import logging import pathlib import time from typing import TYPE_CHECKING, Any from .general import SysCommand -from .output import log +from .output import warn, error if TYPE_CHECKING: _: Any @@ -19,14 +18,14 @@ def run_pacman(args :str, default_cmd :str = 'pacman') -> SysCommand: pacman_db_lock = pathlib.Path('/var/lib/pacman/db.lck') if pacman_db_lock.exists(): - log(_('Pacman is already running, waiting maximum 10 minutes for it to terminate.'), level=logging.WARNING, fg="red") + warn(_('Pacman is already running, waiting maximum 10 minutes for it to terminate.')) started = time.time() while pacman_db_lock.exists(): time.sleep(0.25) if time.time() - started > (60 * 10): - log(_('Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall.'), level=logging.WARNING, fg="red") + error(_('Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall.')) exit(1) return SysCommand(f'{default_cmd} {args}') diff --git a/archinstall/lib/plugins.py b/archinstall/lib/plugins.py index b1ece04f..4ccb0666 100644 --- a/archinstall/lib/plugins.py +++ b/archinstall/lib/plugins.py @@ -1,6 +1,5 @@ import hashlib import importlib -import logging import os import sys import urllib.parse @@ -9,7 +8,7 @@ from importlib import metadata from pathlib import Path from typing import Optional, List -from .output import log +from .output import error, info, warn from .storage import storage plugins = {} @@ -24,11 +23,13 @@ for plugin_definition in metadata.entry_points().select(group='archinstall.plugi try: plugins[plugin_definition.name] = plugin_entrypoint() except Exception as err: - log(f'Error: {err}', level=logging.ERROR) - log(f"The above error was detected when loading the plugin: {plugin_definition}", fg="red", level=logging.ERROR) + error( + f'Error: {err}', + f"The above error was detected when loading the plugin: {plugin_definition}" + ) -def localize_path(path: Path) -> Path: +def _localize_path(path: Path) -> Path: """ Support structures for load_plugin() """ @@ -45,7 +46,7 @@ def localize_path(path: Path) -> Path: return path -def import_via_path(path: Path, namespace: Optional[str] = None) -> Optional[str]: +def _import_via_path(path: Path, namespace: Optional[str] = None) -> Optional[str]: if not namespace: namespace = os.path.basename(path) @@ -61,8 +62,10 @@ def import_via_path(path: Path, namespace: Optional[str] = None) -> Optional[str return namespace except Exception as err: - log(f'Error: {err}', level=logging.ERROR) - log(f"The above error was detected when loading the plugin: {path}", fg="red", level=logging.ERROR) + error( + f'Error: {err}', + f"The above error was detected when loading the plugin: {path}" + ) try: del sys.modules[namespace] @@ -72,7 +75,7 @@ def import_via_path(path: Path, namespace: Optional[str] = None) -> Optional[str return namespace -def find_nth(haystack: List[str], needle: str, n: int) -> Optional[int]: +def _find_nth(haystack: List[str], needle: str, n: int) -> Optional[int]: indices = [idx for idx, elem in enumerate(haystack) if elem == needle] if n <= len(indices): return indices[n - 1] @@ -82,34 +85,36 @@ def find_nth(haystack: List[str], needle: str, n: int) -> Optional[int]: def load_plugin(path: Path): namespace: Optional[str] = None parsed_url = urllib.parse.urlparse(str(path)) - log(f"Loading plugin from url {parsed_url}.", level=logging.INFO) + info(f"Loading plugin from url {parsed_url}") # The Profile was not a direct match on a remote URL if not parsed_url.scheme: # Path was not found in any known examples, check if it's an absolute path if os.path.isfile(path): - namespace = import_via_path(path) + namespace = _import_via_path(path) elif parsed_url.scheme in ('https', 'http'): - localized = localize_path(path) - namespace = import_via_path(localized) + localized = _localize_path(path) + namespace = _import_via_path(localized) if namespace and namespace in sys.modules: # Version dependency via __archinstall__version__ variable (if present) in the plugin # Any errors in version inconsistency will be handled through normal error handling if not defined. if hasattr(sys.modules[namespace], '__archinstall__version__'): - archinstall_major_and_minor_version = float(storage['__version__'][:find_nth(storage['__version__'], '.', 2)]) + archinstall_major_and_minor_version = float(storage['__version__'][:_find_nth(storage['__version__'], '.', 2)]) if sys.modules[namespace].__archinstall__version__ < archinstall_major_and_minor_version: - log(f"Plugin {sys.modules[namespace]} does not support the current Archinstall version.", fg="red", level=logging.ERROR) + error(f"Plugin {sys.modules[namespace]} does not support the current Archinstall version.") # Locate the plugin entry-point called Plugin() # This in accordance with the entry_points() from setup.cfg above if hasattr(sys.modules[namespace], 'Plugin'): try: plugins[namespace] = sys.modules[namespace].Plugin() - log(f"Plugin {plugins[namespace]} has been loaded.", fg="gray", level=logging.INFO) + info(f"Plugin {plugins[namespace]} has been loaded.") except Exception as err: - log(f'Error: {err}', level=logging.ERROR) - log(f"The above error was detected when initiating the plugin: {path}", fg="red", level=logging.ERROR) + error( + f'Error: {err}', + f"The above error was detected when initiating the plugin: {path}" + ) else: - log(f"Plugin '{path}' is missing a valid entry-point or is corrupt.", fg="yellow", level=logging.WARNING) + warn(f"Plugin '{path}' is missing a valid entry-point or is corrupt.") diff --git a/archinstall/lib/profile/profile_menu.py b/archinstall/lib/profile/profile_menu.py index 6462685a..213466a6 100644 --- a/archinstall/lib/profile/profile_menu.py +++ b/archinstall/lib/profile/profile_menu.py @@ -6,7 +6,7 @@ from archinstall.default_profiles.profile import Profile, GreeterType from .profile_model import ProfileConfiguration from ..hardware import AVAILABLE_GFX_DRIVERS from ..menu import Menu, MenuSelectionType, AbstractSubMenu, Selector -from ..user_interaction.system_conf import select_driver +from ..interactions.system_conf import select_driver if TYPE_CHECKING: _: Any diff --git a/archinstall/lib/profile/profiles_handler.py b/archinstall/lib/profile/profiles_handler.py index 6ed95f8e..16fef251 100644 --- a/archinstall/lib/profile/profiles_handler.py +++ b/archinstall/lib/profile/profiles_handler.py @@ -1,7 +1,6 @@ from __future__ import annotations import importlib.util -import logging import sys from collections import Counter from functools import cached_property @@ -15,7 +14,7 @@ from .profile_model import ProfileConfiguration from ..hardware import AVAILABLE_GFX_DRIVERS from ..menu import MenuSelectionType, Menu, MenuSelection from ..networking import list_interfaces, fetch_data_from_url -from ..output import log +from ..output import error, debug, info, warn from ..storage import storage if TYPE_CHECKING: @@ -106,7 +105,7 @@ class ProfileHandler: invalid = ', '.join([k for k, v in resolved.items() if v is None]) if invalid: - log(f'No profile definition found: {invalid}') + info(f'No profile definition found: {invalid}') custom_settings = profile_config.get('custom_settings', {}) for profile in valid: @@ -216,7 +215,7 @@ class ProfileHandler: install_session.add_additional_packages(additional_pkg) except Exception as err: - log(f"Could not handle nvidia and linuz-zen specific situations during xorg installation: {err}", level=logging.WARNING, fg="yellow") + warn(f"Could not handle nvidia and linuz-zen specific situations during xorg installation: {err}") # Prep didn't run, so there's no driver to install install_session.add_additional_packages(['xorg-server', 'xorg-xinit']) @@ -250,7 +249,7 @@ class ProfileHandler: self.add_custom_profiles(profiles) except ValueError: err = str(_('Unable to fetch profile from specified url: {}')).format(url) - log(err, level=logging.ERROR, fg="red") + error(err) def _load_profile_class(self, module: ModuleType) -> List[Profile]: """ @@ -264,7 +263,7 @@ class ProfileHandler: if isinstance(cls_, Profile): profiles.append(cls_) except Exception: - log(f'Cannot import {module}, it does not appear to be a Profile class', level=logging.DEBUG) + debug(f'Cannot import {module}, it does not appear to be a Profile class') return profiles @@ -278,7 +277,7 @@ class ProfileHandler: if len(duplicates) > 0: err = str(_('Profiles must have unique name, but profile definitions with duplicate name found: {}')).format(duplicates[0][0]) - log(err, level=logging.ERROR, fg="red") + error(err) sys.exit(1) def _is_legacy(self, file: Path) -> bool: @@ -297,15 +296,15 @@ class ProfileHandler: Process a file for profile definitions """ if self._is_legacy(file): - log(f'Cannot import {file} because it is no longer supported, please use the new profile format') + info(f'Cannot import {file} because it is no longer supported, please use the new profile format') return [] if not file.is_file(): - log(f'Cannot find profile file {file}') + info(f'Cannot find profile file {file}') return [] name = file.name.removesuffix(file.suffix) - log(f'Importing profile: {file}', level=logging.DEBUG) + debug(f'Importing profile: {file}') try: spec = importlib.util.spec_from_file_location(name, file) @@ -315,7 +314,7 @@ class ProfileHandler: spec.loader.exec_module(imported) return self._load_profile_class(imported) except Exception as e: - log(f'Unable to parse file {file}: {e}', level=logging.ERROR) + error(f'Unable to parse file {file}: {e}') return [] diff --git a/archinstall/lib/services.py b/archinstall/lib/services.py deleted file mode 100644 index b177052b..00000000 --- a/archinstall/lib/services.py +++ /dev/null @@ -1,11 +0,0 @@ -import os -from .general import SysCommand - - -def service_state(service_name: str) -> str: - if os.path.splitext(service_name)[1] != '.service': - service_name += '.service' # Just to be safe - - state = b''.join(SysCommand(f'systemctl show --no-pager -p SubState --value {service_name}', environment_vars={'SYSTEMD_COLORS': '0'})) - - return state.strip().decode('UTF-8') diff --git a/archinstall/lib/storage.py b/archinstall/lib/storage.py index 5a54d816..2f256e5d 100644 --- a/archinstall/lib/storage.py +++ b/archinstall/lib/storage.py @@ -11,8 +11,8 @@ from pathlib import Path storage: Dict[str, Any] = { 'PROFILE': Path(__file__).parent.parent.joinpath('default_profiles'), - 'LOG_PATH': '/var/log/archinstall', - 'LOG_FILE': 'install.log', + 'LOG_PATH': Path('/var/log/archinstall'), + 'LOG_FILE': Path('install.log'), 'MOUNT_POINT': Path('/mnt/archinstall'), 'ENC_IDENTIFIER': 'ainst', 'DISK_TIMEOUTS' : 1, # seconds diff --git a/archinstall/lib/systemd.py b/archinstall/lib/systemd.py deleted file mode 100644 index 6ccbc5f6..00000000 --- a/archinstall/lib/systemd.py +++ /dev/null @@ -1,110 +0,0 @@ -import logging -import time -from typing import Iterator, Optional -from .exceptions import SysCallError -from .general import SysCommand, SysCommandWorker, locate_binary -from .installer import Installer -from .output import log -from .storage import storage - - -class Boot: - def __init__(self, installation: Installer): - self.instance = installation - self.container_name = 'archinstall' - self.session: Optional[SysCommandWorker] = None - self.ready = False - - def __enter__(self) -> 'Boot': - if (existing_session := storage.get('active_boot', None)) and existing_session.instance != self.instance: - raise KeyError("Archinstall only supports booting up one instance, and a active session is already active and it is not this one.") - - if existing_session: - self.session = existing_session.session - self.ready = existing_session.ready - else: - # '-P' or --console=pipe could help us not having to do a bunch - # of os.write() calls, but instead use pipes (stdin, stdout and stderr) as usual. - self.session = SysCommandWorker([ - '/usr/bin/systemd-nspawn', - '-D', str(self.instance.target), - '--timezone=off', - '-b', - '--no-pager', - '--machine', self.container_name - ]) - - if not self.ready and self.session: - while self.session.is_alive(): - if b' login:' in self.session: - self.ready = True - break - - storage['active_boot'] = self - return self - - def __exit__(self, *args :str, **kwargs :str) -> None: - # b''.join(sys_command('sync')) # No need to, since the underlying fs() object will call sync. - # TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager - - if len(args) >= 2 and args[1]: - log(args[1], level=logging.ERROR, fg='red') - log(f"The error above occurred in a temporary boot-up of the installation {self.instance}", level=logging.ERROR, fg="red") - - shutdown = None - shutdown_exit_code: Optional[int] = -1 - - try: - shutdown = SysCommand(f'systemd-run --machine={self.container_name} --pty shutdown now') - except SysCallError as error: - shutdown_exit_code = error.exit_code - - if self.session: - while self.session.is_alive(): - time.sleep(0.25) - - if shutdown and shutdown.exit_code: - shutdown_exit_code = shutdown.exit_code - - if self.session and (self.session.exit_code == 0 or shutdown_exit_code == 0): - storage['active_boot'] = None - else: - session_exit_code = self.session.exit_code if self.session else -1 - - raise SysCallError( - f"Could not shut down temporary boot of {self.instance}: {session_exit_code}/{shutdown_exit_code}", - exit_code=next(filter(bool, [session_exit_code, shutdown_exit_code])) - ) - - def __iter__(self) -> Iterator[bytes]: - if self.session: - for value in self.session: - yield value - - def __contains__(self, key: bytes) -> bool: - if self.session is None: - return False - - return key in self.session - - def is_alive(self) -> bool: - if self.session is None: - return False - - return self.session.is_alive() - - def SysCommand(self, cmd: list, *args, **kwargs) -> SysCommand: - if cmd[0][0] != '/' and cmd[0][:2] != './': - # This check is also done in SysCommand & SysCommandWorker. - # However, that check is done for `machinectl` and not for our chroot command. - # So this wrapper for SysCommand will do this additionally. - - cmd[0] = locate_binary(cmd[0]) - - return SysCommand(["systemd-run", f"--machine={self.container_name}", "--pty", *cmd], *args, **kwargs) - - def SysCommandWorker(self, cmd: list, *args, **kwargs) -> SysCommandWorker: - if cmd[0][0] != '/' and cmd[0][:2] != './': - cmd[0] = locate_binary(cmd[0]) - - return SysCommandWorker(["systemd-run", f"--machine={self.container_name}", "--pty", *cmd], *args, **kwargs) diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py index 0d74f974..5f0f0695 100644 --- a/archinstall/lib/translationhandler.py +++ b/archinstall/lib/translationhandler.py @@ -1,14 +1,14 @@ from __future__ import annotations import json -import logging import os import gettext from dataclasses import dataclass from pathlib import Path from typing import List, Dict, Any, TYPE_CHECKING, Optional -from .exceptions import TranslationError + +from .output import error, debug if TYPE_CHECKING: _: Any @@ -80,8 +80,8 @@ class TranslationHandler: language = Language(abbr, lang, translation, percent, translated_lang) languages.append(language) - except FileNotFoundError as error: - raise TranslationError(f"Could not locate language file for '{lang}': {error}") + except FileNotFoundError as err: + raise FileNotFoundError(f"Could not locate language file for '{lang}': {err}") return languages @@ -89,12 +89,12 @@ class TranslationHandler: """ Set the provided font as the new terminal font """ - from .general import SysCommand, log + from .general import SysCommand try: - log(f'Setting font: {font}', level=logging.DEBUG) + debug(f'Setting font: {font}') SysCommand(f'setfont {font}') except Exception: - log(f'Unable to set font {font}', level=logging.ERROR) + error(f'Unable to set font {font}') def _load_language_mappings(self) -> List[Dict[str, Any]]: """ diff --git a/archinstall/lib/user_interaction/__init__.py b/archinstall/lib/user_interaction/__init__.py deleted file mode 100644 index 5ee89de0..00000000 --- a/archinstall/lib/user_interaction/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from .manage_users_conf import ask_for_additional_users -from .locale_conf import select_locale_lang, select_locale_enc -from .system_conf import select_kernel, select_driver, ask_for_bootloader, ask_for_swap -from .network_conf import ask_to_configure_network -from .general_conf import ( - ask_ntp, ask_for_a_timezone, ask_for_audio_selection, select_language, select_mirror_regions, - select_archinstall_language, ask_additional_packages_to_install, - select_additional_repositories, ask_hostname, add_number_of_parrallel_downloads -) -from .utils import get_password diff --git a/archinstall/lib/user_interaction/disk_conf.py b/archinstall/lib/user_interaction/disk_conf.py deleted file mode 100644 index a77e950a..00000000 --- a/archinstall/lib/user_interaction/disk_conf.py +++ /dev/null @@ -1,391 +0,0 @@ -from __future__ import annotations - -import logging -from pathlib import Path -from typing import Any, TYPE_CHECKING, Optional, List, Tuple - -from .. import disk -from ..hardware import has_uefi -from ..menu import Menu, MenuSelectionType, TableMenu -from ..output import FormattedOutput -from ..output import log -from ..utils.util import prompt_dir - -if TYPE_CHECKING: - _: Any - - -def select_devices(preset: List[disk.BDevice] = []) -> List[disk.BDevice]: - """ - Asks the user to select one or multiple devices - - :return: List of selected devices - :rtype: list - """ - - def _preview_device_selection(selection: disk._DeviceInfo) -> Optional[str]: - dev = disk.device_handler.get_device(selection.path) - if dev and dev.partition_infos: - return FormattedOutput.as_table(dev.partition_infos) - return None - - if preset is None: - preset = [] - - title = str(_('Select one or more devices to use and configure')) - warning = str(_('If you reset the device selection this will also reset the current disk layout. Are you sure?')) - - devices = disk.device_handler.devices - options = [d.device_info for d in devices] - preset_value = [p.device_info for p in preset] - - choice = TableMenu( - title, - data=options, - multi=True, - preset=preset_value, - preview_command=_preview_device_selection, - preview_title=str(_('Existing Partitions')), - preview_size=0.2, - allow_reset=True, - allow_reset_warning_msg=warning - ).run() - - match choice.type_: - case MenuSelectionType.Reset: return [] - case MenuSelectionType.Skip: return preset - case MenuSelectionType.Selection: - selected_device_info: List[disk._DeviceInfo] = choice.value # type: ignore - selected_devices = [] - - for device in devices: - if device.device_info in selected_device_info: - selected_devices.append(device) - - return selected_devices - - -def get_default_partition_layout( - devices: List[disk.BDevice], - filesystem_type: Optional[disk.FilesystemType] = None, - advanced_option: bool = False -) -> List[disk.DeviceModification]: - - if len(devices) == 1: - device_modification = suggest_single_disk_layout( - devices[0], - filesystem_type=filesystem_type, - advanced_options=advanced_option - ) - return [device_modification] - else: - return suggest_multi_disk_layout( - devices, - filesystem_type=filesystem_type, - advanced_options=advanced_option - ) - - -def _manual_partitioning( - preset: List[disk.DeviceModification], - devices: List[disk.BDevice] -) -> List[disk.DeviceModification]: - modifications = [] - for device in devices: - mod = next(filter(lambda x: x.device == device, preset), None) - if not mod: - mod = disk.DeviceModification(device, wipe=False) - - if partitions := disk.manual_partitioning(device, preset=mod.partitions): - mod.partitions = partitions - modifications.append(mod) - - return modifications - - -def select_disk_config( - preset: Optional[disk.DiskLayoutConfiguration] = None, - advanced_option: bool = False -) -> Optional[disk.DiskLayoutConfiguration]: - default_layout = disk.DiskLayoutType.Default.display_msg() - manual_mode = disk.DiskLayoutType.Manual.display_msg() - pre_mount_mode = disk.DiskLayoutType.Pre_mount.display_msg() - - options = [default_layout, manual_mode, pre_mount_mode] - preset_value = preset.config_type.display_msg() if preset else None - warning = str(_('Are you sure you want to reset this setting?')) - - choice = Menu( - _('Select a partitioning option'), - options, - allow_reset=True, - allow_reset_warning_msg=warning, - sort=False, - preview_size=0.2, - preset_values=preset_value - ).run() - - match choice.type_: - case MenuSelectionType.Skip: return preset - case MenuSelectionType.Reset: return None - case MenuSelectionType.Selection: - if choice.single_value == pre_mount_mode: - output = "You will use whatever drive-setup is mounted at the specified directory\n" - output += "WARNING: Archinstall won't check the suitability of this setup\n" - - path = prompt_dir(str(_('Enter the root directory of the mounted devices: ')), output) - mods = disk.device_handler.detect_pre_mounted_mods(path) - - return disk.DiskLayoutConfiguration( - config_type=disk.DiskLayoutType.Pre_mount, - relative_mountpoint=path, - device_modifications=mods - ) - - preset_devices = [mod.device for mod in preset.device_modifications] if preset else [] - - devices = select_devices(preset_devices) - - if not devices: - return None - - if choice.value == default_layout: - modifications = get_default_partition_layout(devices, advanced_option=advanced_option) - if modifications: - return disk.DiskLayoutConfiguration( - config_type=disk.DiskLayoutType.Default, - device_modifications=modifications - ) - elif choice.value == manual_mode: - preset_mods = preset.device_modifications if preset else [] - modifications = _manual_partitioning(preset_mods, devices) - - if modifications: - return disk.DiskLayoutConfiguration( - config_type=disk.DiskLayoutType.Manual, - device_modifications=modifications - ) - - return None - - -def _boot_partition() -> disk.PartitionModification: - if has_uefi(): - start = disk.Size(1, disk.Unit.MiB) - size = disk.Size(512, disk.Unit.MiB) - else: - start = disk.Size(3, disk.Unit.MiB) - size = disk.Size(203, disk.Unit.MiB) - - # boot partition - return disk.PartitionModification( - status=disk.ModificationStatus.Create, - type=disk.PartitionType.Primary, - start=start, - length=size, - mountpoint=Path('/boot'), - fs_type=disk.FilesystemType.Fat32, - flags=[disk.PartitionFlag.Boot] - ) - - -def ask_for_main_filesystem_format(advanced_options=False) -> disk.FilesystemType: - options = { - 'btrfs': disk.FilesystemType.Btrfs, - 'ext4': disk.FilesystemType.Ext4, - 'xfs': disk.FilesystemType.Xfs, - 'f2fs': disk.FilesystemType.F2fs - } - - if advanced_options: - options.update({'ntfs': disk.FilesystemType.Ntfs}) - - prompt = _('Select which filesystem your main partition should use') - choice = Menu(prompt, options, skip=False, sort=False).run() - return options[choice.single_value] - - -def suggest_single_disk_layout( - device: disk.BDevice, - filesystem_type: Optional[disk.FilesystemType] = None, - advanced_options: bool = False, - separate_home: Optional[bool] = None -) -> disk.DeviceModification: - if not filesystem_type: - filesystem_type = ask_for_main_filesystem_format(advanced_options) - - min_size_to_allow_home_part = disk.Size(40, disk.Unit.GiB) - root_partition_size = disk.Size(20, disk.Unit.GiB) - using_subvolumes = False - using_home_partition = False - compression = False - device_size_gib = device.device_info.total_size - - if filesystem_type == disk.FilesystemType.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.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.value == Menu.yes() - - device_modification = disk.DeviceModification(device, wipe=True) - - # Used for reference: https://wiki.archlinux.org/title/partitioning - # 2 MiB is unallocated for GRUB on BIOS. Potentially unneeded for other bootloaders? - - # TODO: On BIOS, /boot partition is only needed if the drive will - # be encrypted, otherwise it is not recommended. We should probably - # add a check for whether the drive will be encrypted or not. - - # Increase the UEFI partition if UEFI is detected. - # Also re-align the start to 1MiB since we don't need the first sectors - # like we do in MBR layouts where the boot loader is installed traditionally. - - boot_partition = _boot_partition() - device_modification.add_partition(boot_partition) - - if not using_subvolumes: - if device_size_gib >= min_size_to_allow_home_part: - if separate_home is None: - 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.value == Menu.yes() - elif separate_home is True: - using_home_partition = True - else: - using_home_partition = False - - # root partition - start = disk.Size(513, disk.Unit.MiB) if has_uefi() else disk.Size(206, disk.Unit.MiB) - - # Set a size for / (/root) - if using_subvolumes or device_size_gib < min_size_to_allow_home_part or not using_home_partition: - length = disk.Size(100, disk.Unit.Percent, total_size=device.device_info.total_size) - else: - length = min(device.device_info.total_size, root_partition_size) - - root_partition = disk.PartitionModification( - status=disk.ModificationStatus.Create, - type=disk.PartitionType.Primary, - start=start, - length=length, - mountpoint=Path('/') if not using_subvolumes else None, - fs_type=filesystem_type, - mount_options=['compress=zstd'] if compression else [], - ) - device_modification.add_partition(root_partition) - - if using_subvolumes: - # https://btrfs.wiki.kernel.org/index.php/FAQ - # https://unix.stackexchange.com/questions/246976/btrfs-subvolume-uuid-clash - # https://github.com/classy-giraffe/easy-arch/blob/main/easy-arch.sh - subvolumes = [ - disk.SubvolumeModification(Path('@'), Path('/')), - disk.SubvolumeModification(Path('@home'), Path('/home')), - disk.SubvolumeModification(Path('@log'), Path('/var/log')), - disk.SubvolumeModification(Path('@pkg'), Path('/var/cache/pacman/pkg')), - disk.SubvolumeModification(Path('@.snapshots'), Path('/.snapshots')) - ] - root_partition.btrfs_subvols = subvolumes - elif using_home_partition: - # If we don't want to use subvolumes, - # But we want to be able to re-use data between re-installs.. - # A second partition for /home would be nice if we have the space for it - home_partition = disk.PartitionModification( - status=disk.ModificationStatus.Create, - type=disk.PartitionType.Primary, - start=root_partition.length, - length=disk.Size(100, disk.Unit.Percent, total_size=device.device_info.total_size), - mountpoint=Path('/home'), - fs_type=filesystem_type, - mount_options=['compress=zstd'] if compression else [] - ) - device_modification.add_partition(home_partition) - - return device_modification - - -def suggest_multi_disk_layout( - devices: List[disk.BDevice], - filesystem_type: Optional[disk.FilesystemType] = None, - advanced_options: bool = False -) -> List[disk.DeviceModification]: - if not devices: - return [] - - # Not really a rock solid foundation of information to stand on, but it's a start: - # https://www.reddit.com/r/btrfs/comments/m287gp/partition_strategy_for_two_physical_disks/ - # https://www.reddit.com/r/btrfs/comments/9us4hr/what_is_your_btrfs_partitionsubvolumes_scheme/ - min_home_partition_size = disk.Size(40, disk.Unit.GiB) - # rough estimate taking in to account user desktops etc. TODO: Catch user packages to detect size? - desired_root_partition_size = disk.Size(20, disk.Unit.GiB) - compression = False - - if not filesystem_type: - filesystem_type = ask_for_main_filesystem_format(advanced_options) - - # find proper disk for /home - possible_devices = list(filter(lambda x: x.device_info.total_size >= min_home_partition_size, devices)) - home_device = max(possible_devices, key=lambda d: d.device_info.total_size) if possible_devices else None - - # find proper device for /root - devices_delta = {} - for device in devices: - if device is not home_device: - delta = device.device_info.total_size - desired_root_partition_size - devices_delta[device] = delta - - sorted_delta: List[Tuple[disk.BDevice, Any]] = sorted(devices_delta.items(), key=lambda x: x[1]) - root_device: Optional[disk.BDevice] = sorted_delta[0][0] - - 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: {}GiB\n').format(min_home_partition_size.format_size(disk.Unit.GiB)) - text += _('Minimum capacity for Arch Linux partition: {}GiB').format(desired_root_partition_size.format_size(disk.Unit.GiB)) - Menu(str(text), [str(_('Continue'))], skip=False).run() - return [] - - if filesystem_type == disk.FilesystemType.Btrfs: - prompt = str(_('Would you like to use BTRFS compression?')) - choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run() - compression = choice.value == Menu.yes() - - device_paths = ', '.join([str(d.device_info.path) for d in devices]) - log(f"Suggesting multi-disk-layout for devices: {device_paths}", level=logging.DEBUG) - log(f"/root: {root_device.device_info.path}", level=logging.DEBUG) - log(f"/home: {home_device.device_info.path}", level=logging.DEBUG) - - root_device_modification = disk.DeviceModification(root_device, wipe=True) - home_device_modification = disk.DeviceModification(home_device, wipe=True) - - # add boot partition to the root device - boot_partition = _boot_partition() - root_device_modification.add_partition(boot_partition) - - # add root partition to the root device - root_partition = disk.PartitionModification( - status=disk.ModificationStatus.Create, - type=disk.PartitionType.Primary, - start=disk.Size(513, disk.Unit.MiB) if has_uefi() else disk.Size(206, disk.Unit.MiB), - length=disk.Size(100, disk.Unit.Percent, total_size=root_device.device_info.total_size), - mountpoint=Path('/'), - mount_options=['compress=zstd'] if compression else [], - fs_type=filesystem_type - ) - root_device_modification.add_partition(root_partition) - - # add home partition to home device - home_partition = disk.PartitionModification( - status=disk.ModificationStatus.Create, - type=disk.PartitionType.Primary, - start=disk.Size(1, disk.Unit.MiB), - length=disk.Size(100, disk.Unit.Percent, total_size=home_device.device_info.total_size), - mountpoint=Path('/home'), - mount_options=['compress=zstd'] if compression else [], - fs_type=filesystem_type, - ) - home_device_modification.add_partition(home_partition) - - return [root_device_modification, home_device_modification] diff --git a/archinstall/lib/user_interaction/general_conf.py b/archinstall/lib/user_interaction/general_conf.py deleted file mode 100644 index 9722dc4d..00000000 --- a/archinstall/lib/user_interaction/general_conf.py +++ /dev/null @@ -1,244 +0,0 @@ -from __future__ import annotations - -import logging -import pathlib -from typing import List, Any, Optional, Dict, TYPE_CHECKING - -from ..locale_helpers import list_keyboard_languages, list_timezones -from ..menu import MenuSelectionType, Menu, TextInput -from ..mirrors import list_mirrors -from ..output import log -from ..packages.packages import validate_package_list -from ..storage import storage -from ..translationhandler import Language - -if TYPE_CHECKING: - _: Any - - -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 = Menu.yes() - 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.value == Menu.no() else True - - -def ask_hostname(preset: str = '') -> str: - while True: - hostname = TextInput( - str(_('Desired hostname for the installation: ')), - preset - ).run().strip() - - if hostname: - return hostname - - -def ask_for_a_timezone(preset: Optional[str] = None) -> Optional[str]: - timezones = list_timezones() - default = 'UTC' - - choice = Menu( - _('Select a timezone'), - list(timezones), - preset_values=preset, - default_option=default - ).run() - - match choice.type_: - case MenuSelectionType.Skip: return preset - case MenuSelectionType.Selection: return choice.single_value - - return None - - -def ask_for_audio_selection(desktop: bool = True, preset: Optional[str] = None) -> Optional[str]: - 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.Skip: return preset - case MenuSelectionType.Selection: return choice.single_value - - return None - - -def select_language(preset: Optional[str] = None) -> Optional[str]: - """ - Asks the user to select a language - Usually this is combined with :ref:`archinstall.list_keyboard_languages`. - - :return: The language/dictionary key of the selected language - :rtype: str - """ - kb_lang = list_keyboard_languages() - # sort alphabetically and then by length - sorted_kb_lang = sorted(sorted(list(kb_lang)), key=len) - - choice = Menu( - _('Select keyboard layout'), - sorted_kb_lang, - preset_values=preset, - sort=False - ).run() - - match choice.type_: - case MenuSelectionType.Skip: return preset - case MenuSelectionType.Selection: return choice.single_value - - return None - - -def select_mirror_regions(preset_values: Dict[str, Any] = {}) -> Dict[str, Any]: - """ - Asks the user to select a mirror or region - Usually this is combined with :ref:`archinstall.list_mirrors`. - - :return: The dictionary information about a mirror/region. - :rtype: dict - """ - if preset_values is None: - preselected = None - else: - preselected = list(preset_values.keys()) - - mirrors = list_mirrors() - - choice = Menu( - _('Select one of the regions to download packages from'), - list(mirrors.keys()), - preset_values=preselected, - multi=True, - allow_reset=True - ).run() - - match choice.type_: - case MenuSelectionType.Reset: - return {} - case MenuSelectionType.Skip: - return preset_values - case MenuSelectionType.Selection: - return {selected: mirrors[selected] for selected in choice.multi_value} - - return {} - - -def select_archinstall_language(languages: List[Language], preset: Language) -> Language: - # these are the displayed language names which can either be - # the english name of a language or, if present, the - # name of the language in its own language - options = {lang.display_name: lang for lang in languages} - - title = 'NOTE: If a language can not displayed properly, a proper font must be set manually in the console.\n' - title += 'All available fonts can be found in "/usr/share/kbd/consolefonts"\n' - title += 'e.g. setfont LatGrkCyr-8x16 (to display latin/greek/cyrillic characters)\n' - - choice = Menu( - title, - list(options.keys()), - default_option=preset.display_name, - preview_size=0.5 - ).run() - - match choice.type_: - case MenuSelectionType.Skip: return preset - case MenuSelectionType.Selection: return options[choice.single_value] - - raise ValueError('Language selection not handled') - - -def ask_additional_packages_to_install(pre_set_packages: List[str] = []) -> List[str]: - # Additional packages (with some light weight error handling for invalid package names) - print(_('Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.')) - print(_('If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt.')) - - def read_packages(already_defined: list = []) -> list: - display = ' '.join(already_defined) - input_packages = TextInput(_('Write additional packages to install (space separated, leave blank to skip): '), display).run().strip() - return input_packages.split() if input_packages else [] - - pre_set_packages = pre_set_packages if pre_set_packages else [] - packages = read_packages(pre_set_packages) - - if not storage['arguments']['offline'] and not storage['arguments']['no_pkg_lookups']: - while True: - if len(packages): - # Verify packages that were given - print(_("Verifying that additional packages exist (this might take a few seconds)")) - valid, invalid = validate_package_list(packages) - - if invalid: - log(f"Some packages could not be found in the repository: {invalid}", level=logging.WARNING, fg='red') - packages = read_packages(valid) - continue - break - - return packages - - -def add_number_of_parrallel_downloads(input_number :Optional[int] = None) -> Optional[int]: - max_downloads = 5 - print(_(f"This option enables the number of parallel downloads that can occur during installation")) - print(_(f"Enter the number of parallel downloads to be enabled.\n (Enter a value between 1 to {max_downloads})\nNote:")) - print(_(f" - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )")) - print(_(f" - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )")) - print(_(f" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )")) - - while True: - try: - input_number = int(TextInput(_("[Default value: 0] > ")).run().strip() or 0) - if input_number <= 0: - input_number = 0 - elif input_number > max_downloads: - input_number = max_downloads - break - except: - print(_(f"Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]")) - - pacman_conf_path = pathlib.Path("/etc/pacman.conf") - with pacman_conf_path.open() as f: - pacman_conf = f.read().split("\n") - - with pacman_conf_path.open("w") as fwrite: - for line in pacman_conf: - if "ParallelDownloads" in line: - fwrite.write(f"ParallelDownloads = {input_number+1}\n") if not input_number == 0 else fwrite.write("#ParallelDownloads = 0\n") - else: - fwrite.write(f"{line}\n") - - return input_number - - -def select_additional_repositories(preset: List[str]) -> List[str]: - """ - Allows the user to select additional repositories (multilib, and testing) if desired. - - :return: The string as a selected repository - :rtype: string - """ - - repositories = ["multilib", "testing"] - - choice = Menu( - _('Choose which optional additional repositories to enable'), - repositories, - sort=False, - multi=True, - preset_values=preset, - allow_reset=True - ).run() - - match choice.type_: - case MenuSelectionType.Skip: return preset - case MenuSelectionType.Reset: return [] - case MenuSelectionType.Selection: return choice.single_value - - return [] diff --git a/archinstall/lib/user_interaction/locale_conf.py b/archinstall/lib/user_interaction/locale_conf.py deleted file mode 100644 index cdc3423a..00000000 --- a/archinstall/lib/user_interaction/locale_conf.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import annotations - -from typing import Any, TYPE_CHECKING, Optional - -from ..locale_helpers import list_locales -from ..menu import Menu, MenuSelectionType - -if TYPE_CHECKING: - _: Any - - -def select_locale_lang(preset: Optional[str] = None) -> Optional[str]: - locales = list_locales() - locale_lang = set([locale.split()[0] for locale in locales]) - - choice = Menu( - _('Choose which locale language to use'), - list(locale_lang), - sort=True, - preset_values=preset - ).run() - - match choice.type_: - case MenuSelectionType.Selection: return choice.single_value - case MenuSelectionType.Skip: return preset - - return None - - -def select_locale_enc(preset: Optional[str] = None) -> Optional[str]: - locales = list_locales() - locale_enc = set([locale.split()[1] for locale in locales]) - - choice = Menu( - _('Choose which locale encoding to use'), - list(locale_enc), - sort=True, - preset_values=preset - ).run() - - match choice.type_: - case MenuSelectionType.Selection: return choice.single_value - case MenuSelectionType.Skip: return preset - - return None diff --git a/archinstall/lib/user_interaction/manage_users_conf.py b/archinstall/lib/user_interaction/manage_users_conf.py deleted file mode 100644 index 879578da..00000000 --- a/archinstall/lib/user_interaction/manage_users_conf.py +++ /dev/null @@ -1,106 +0,0 @@ -from __future__ import annotations - -import re -from typing import Any, Dict, TYPE_CHECKING, List, Optional - -from .utils import get_password -from ..menu import Menu, ListManager -from ..models.users import User -from ..output import FormattedOutput - -if TYPE_CHECKING: - _: Any - - -class UserList(ListManager): - """ - subclass of ListManager for the managing of user accounts - """ - - def __init__(self, prompt: str, lusers: List[User]): - self._actions = [ - str(_('Add a user')), - str(_('Change password')), - str(_('Promote/Demote user')), - str(_('Delete User')) - ] - super().__init__(prompt, lusers, [self._actions[0]], self._actions[1:]) - - def reformat(self, data: List[User]) -> Dict[str, Any]: - table = FormattedOutput.as_table(data) - rows = table.split('\n') - - # these are the header rows of the table and do not map to any User obviously - # we're adding 2 spaces as prefix because the menu selector '> ' will be put before - # the selectable rows so the header has to be aligned - display_data: Dict[str, Optional[User]] = {f' {rows[0]}': None, f' {rows[1]}': None} - - for row, user in zip(rows[2:], data): - row = row.replace('|', '\\|') - display_data[row] = user - - return display_data - - def selected_action_display(self, user: User) -> str: - return user.username - - def handle_action(self, action: str, entry: Optional[User], data: List[User]) -> List[User]: - if 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 action == self._actions[1] and entry: # change password - prompt = str(_('Password for user "{}": ').format(entry.username)) - new_password = get_password(prompt=prompt) - if new_password: - user = next(filter(lambda x: x == entry, data)) - user.password = new_password - elif action == self._actions[2] and entry: # promote/demote - user = next(filter(lambda x: x == entry, data)) - user.sudo = False if user.sudo else True - elif action == self._actions[3] and entry: # delete - data = [d for d in data if d != entry] - - 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 - return False - - def _add_user(self) -> Optional[User]: - prompt = '\n\n' + str(_('Enter username (leave blank to skip): ')) - - while True: - username = input(prompt).strip(' ') - if not username: - return None - if not self._check_for_correct_username(username): - error_prompt = str(_("The username you entered is invalid. Try again")) - print(error_prompt) - else: - break - - password = get_password(prompt=str(_('Password for user "{}": ').format(username))) - - if not password: - return None - - choice = Menu( - str(_('Should "{}" be a superuser (sudo)?')).format(username), Menu.yes_no(), - skip=False, - default_option=Menu.yes(), - clear_screen=False, - show_search_hint=False - ).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]: - 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 deleted file mode 100644 index b682c1d2..00000000 --- a/archinstall/lib/user_interaction/network_conf.py +++ /dev/null @@ -1,173 +0,0 @@ -from __future__ import annotations - -import ipaddress -import logging -from typing import Any, Optional, TYPE_CHECKING, List, Union, Dict - -from ..menu import MenuSelectionType, TextInput -from ..models.network_configuration import NetworkConfiguration, NicType - -from ..networking import list_interfaces -from ..output import log, FormattedOutput -from ..menu import ListManager, Menu - -if TYPE_CHECKING: - _: Any - - -class ManualNetworkConfig(ListManager): - """ - subclass of ListManager for the managing of network configurations - """ - - def __init__(self, prompt: str, ifaces: List[NetworkConfiguration]): - self._actions = [ - str(_('Add interface')), - str(_('Edit interface')), - str(_('Delete interface')) - ] - - super().__init__(prompt, ifaces, [self._actions[0]], self._actions[1:]) - - def reformat(self, data: List[NetworkConfiguration]) -> Dict[str, Optional[NetworkConfiguration]]: - table = FormattedOutput.as_table(data) - rows = table.split('\n') - - # these are the header rows of the table and do not map to any User obviously - # we're adding 2 spaces as prefix because the menu selector '> ' will be put before - # the selectable rows so the header has to be aligned - display_data: Dict[str, Optional[NetworkConfiguration]] = {f' {rows[0]}': None, f' {rows[1]}': None} - - for row, iface in zip(rows[2:], data): - row = row.replace('|', '\\|') - display_data[row] = iface - - return display_data - - def selected_action_display(self, iface: NetworkConfiguration) -> str: - return iface.iface if iface.iface else '' - - def handle_action(self, action: str, entry: Optional[NetworkConfiguration], data: List[NetworkConfiguration]): - if action == self._actions[0]: # add - iface_name = self._select_iface(data) - if iface_name: - iface = NetworkConfiguration(NicType.MANUAL, iface=iface_name) - iface = self._edit_iface(iface) - data += [iface] - elif entry: - if action == self._actions[1]: # edit interface - data = [d for d in data if d.iface != entry.iface] - data.append(self._edit_iface(entry)) - elif action == self._actions[2]: # delete - data = [d for d in data if d != entry] - - return data - - def _select_iface(self, data: List[NetworkConfiguration]) -> Optional[Any]: - all_ifaces = list_interfaces().values() - existing_ifaces = [d.iface for d in data] - available = set(all_ifaces) - set(existing_ifaces) - choice = Menu(str(_('Select interface to add')), list(available), skip=True).run() - - if choice.type_ == MenuSelectionType.Skip: - return None - - return choice.value - - def _edit_iface(self, edit_iface: NetworkConfiguration): - iface_name = edit_iface.iface - modes = ['DHCP (auto detect)', 'IP (static)'] - 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, skip=False).run() - - 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() - # Implemented new check for correct IP/subnet input - try: - ipaddress.ip_interface(ip) - break - except ValueError: - log("You need to enter a valid IP in IP-config mode.", level=logging.WARNING, fg='red') - - # Implemented new check for correct gateway IP address - gateway = None - - while 1: - gateway = TextInput( - _('Enter your gateway (router) IP address or leave blank for none: '), - edit_iface.gateway - ).run().strip() - try: - if len(gateway) > 0: - ipaddress.ip_address(gateway) - break - except ValueError: - log("You need to enter a valid gateway (router) IP address.", level=logging.WARNING, fg='red') - - if edit_iface.dns: - display_dns = ' '.join(edit_iface.dns) - else: - 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(' ') - - return NetworkConfiguration(NicType.MANUAL, iface=iface_name, ip=ip, gateway=gateway, dns=dns, dhcp=False) - else: - # this will contain network iface names - return NetworkConfiguration(NicType.MANUAL, iface=iface_name) - - -def ask_to_configure_network( - preset: Union[NetworkConfiguration, List[NetworkConfiguration]] -) -> Optional[NetworkConfiguration | List[NetworkConfiguration]]: - """ - Configure the network on the newly installed system - """ - network_options = { - 'none': str(_('No network configuration')), - 'iso_config': str(_('Copy ISO network configuration to installation')), - 'network_manager': str(_('Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)')), - 'manual': str(_('Manual configuration')) - } - # for this routine it's easier to set the cursor position rather than a preset value - cursor_idx = None - - if preset and not isinstance(preset, list): - if preset.type == 'iso_config': - cursor_idx = 0 - elif preset.type == 'network_manager': - cursor_idx = 1 - - 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, - allow_reset=True, - allow_reset_warning_msg=warning - ).run() - - match choice.type_: - case MenuSelectionType.Skip: return preset - case MenuSelectionType.Reset: return None - - if choice.value == network_options['none']: - return None - elif choice.value == network_options['iso_config']: - return NetworkConfiguration(NicType.ISO) - elif choice.value == network_options['network_manager']: - return NetworkConfiguration(NicType.NM) - elif choice.value == network_options['manual']: - preset_ifaces = preset if isinstance(preset, list) else [] - return ManualNetworkConfig('Configure interfaces', preset_ifaces).run() - - return preset diff --git a/archinstall/lib/user_interaction/save_conf.py b/archinstall/lib/user_interaction/save_conf.py deleted file mode 100644 index e05b9afe..00000000 --- a/archinstall/lib/user_interaction/save_conf.py +++ /dev/null @@ -1,113 +0,0 @@ -from __future__ import annotations - -import logging - -from pathlib import Path -from typing import Any, Dict, TYPE_CHECKING - -from ..general import SysCommand -from ..menu import Menu -from ..menu.menu import MenuSelectionType -from ..output import log -from ..configuration import ConfigurationOutput - -if TYPE_CHECKING: - _: Any - - -def save_config(config: Dict): - def preview(selection: str): - if options['user_config'] == selection: - serialized = config_output.user_config_to_json() - return f'{config_output.user_configuration_file}\n{serialized}' - elif options['user_creds'] == selection: - if maybe_serial := config_output.user_credentials_to_json(): - return f'{config_output.user_credentials_file}\n{maybe_serial}' - else: - return str(_('No configuration')) - elif options['all'] == selection: - output = f'{config_output.user_configuration_file}\n' - if config_output.user_credentials_to_json(): - output += f'{config_output.user_credentials_file}\n' - return output[:-1] - return None - - config_output = ConfigurationOutput(config) - - options = { - 'user_config': str(_('Save user configuration')), - 'user_creds': str(_('Save user credentials')), - 'disk_layout': str(_('Save disk layout')), - 'all': str(_('Save all')) - } - - choice = Menu( - _('Choose which configuration to save'), - list(options.values()), - sort=False, - skip=True, - preview_size=0.75, - preview_command=preview - ).run() - - if choice.type_ == MenuSelectionType.Skip: - return - - save_config_value = choice.single_value - saving_key = [k for k, v in options.items() if v == save_config_value][0] - - dirs_to_exclude = [ - '/bin', - '/dev', - '/lib', - '/lib64', - '/lost+found', - '/opt', - '/proc', - '/run', - '/sbin', - '/srv', - '/sys', - '/usr', - '/var', - ] - - log('Ignore configuration option folders: ' + ','.join(dirs_to_exclude), level=logging.DEBUG) - log(_('Finding possible directories to save configuration files ...'), level=logging.INFO) - - find_exclude = '-path ' + ' -prune -o -path '.join(dirs_to_exclude) + ' -prune ' - file_picker_command = f'find / {find_exclude} -o -type d -print0' - - directories = SysCommand(file_picker_command).decode() - - if directories is None: - raise ValueError('Failed to retrieve possible configuration directories') - - possible_save_dirs = list(filter(None, directories.split('\x00'))) - - selection = Menu( - _('Select directory (or directories) for saving configuration files'), - possible_save_dirs, - multi=True, - skip=True, - allow_reset=False, - ).run() - - match selection.type_: - case MenuSelectionType.Skip: - return - - save_dirs = selection.multi_value - - log(f'Saving {saving_key} configuration files to {save_dirs}', level=logging.DEBUG) - - if save_dirs is not None: - for save_dir_str in save_dirs: - save_dir = Path(save_dir_str) - if options['user_config'] == save_config_value: - config_output.save_user_config(save_dir) - elif options['user_creds'] == save_config_value: - config_output.save_user_creds(save_dir) - elif options['all'] == save_config_value: - config_output.save_user_config(save_dir) - config_output.save_user_creds(save_dir) diff --git a/archinstall/lib/user_interaction/system_conf.py b/archinstall/lib/user_interaction/system_conf.py deleted file mode 100644 index 3f57d0e7..00000000 --- a/archinstall/lib/user_interaction/system_conf.py +++ /dev/null @@ -1,117 +0,0 @@ -from __future__ import annotations - -from typing import List, Any, Dict, TYPE_CHECKING, Optional - -from ..hardware import AVAILABLE_GFX_DRIVERS, has_uefi, has_amd_graphics, has_intel_graphics, has_nvidia_graphics -from ..menu import MenuSelectionType, Menu -from ..models.bootloader import Bootloader - -if TYPE_CHECKING: - _: Any - - -def select_kernel(preset: List[str] = []) -> List[str]: - """ - Asks the user to select a kernel for system. - - :return: The string as a selected kernel - :rtype: string - """ - - kernels = ["linux", "linux-lts", "linux-zen", "linux-hardened"] - default_kernel = "linux" - - 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, - allow_reset=True, - allow_reset_warning_msg=warning - ).run() - - match choice.type_: - case MenuSelectionType.Skip: return preset - case MenuSelectionType.Reset: return [] - case MenuSelectionType.Selection: return choice.value # type: ignore - - -def ask_for_bootloader(preset: Bootloader) -> Bootloader: - # when the system only supports grub - if not has_uefi(): - options = [Bootloader.Grub.value] - default = Bootloader.Grub.value - else: - options = Bootloader.values() - default = Bootloader.Systemd.value - - preset_value = preset.value if preset else None - - choice = Menu( - _('Choose a bootloader'), - options, - preset_values=preset_value, - sort=False, - default_option=default - ).run() - - match choice.type_: - case MenuSelectionType.Skip: return preset - case MenuSelectionType.Selection: return Bootloader(choice.value) - - return preset - - -def select_driver(options: Dict[str, Any] = {}, current_value: Optional[str] = None) -> Optional[str]: - """ - Some what convoluted function, whose job is simple. - Select a graphics driver from a pre-defined set of popular options. - - (The template xorg is for beginner users, not advanced, and should - there for appeal to the general public first and edge cases later) - """ - - if not options: - options = AVAILABLE_GFX_DRIVERS - - drivers = sorted(list(options.keys())) - - if drivers: - title = '' - if has_amd_graphics(): - 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' - if has_intel_graphics(): - 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 += str(_('For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n')) - - title += str(_('\nSelect a graphics driver or leave blank to install all open-source drivers')) - - preset = current_value if current_value else None - choice = Menu(title, drivers, preset_values=preset).run() - - if choice.type_ != MenuSelectionType.Selection: - return None - - return choice.value # type: ignore - - return current_value - - -def ask_for_swap(preset: bool = True) -> bool: - if preset: - 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() - - match choice.type_: - case MenuSelectionType.Skip: return preset - case MenuSelectionType.Selection: return False if choice.value == Menu.no() else True - - return preset diff --git a/archinstall/lib/user_interaction/utils.py b/archinstall/lib/user_interaction/utils.py deleted file mode 100644 index 918945c0..00000000 --- a/archinstall/lib/user_interaction/utils.py +++ /dev/null @@ -1,34 +0,0 @@ -from __future__ import annotations - -import getpass -from typing import Any, Optional, TYPE_CHECKING - -from ..models import PasswordStrength -from ..output import log - -if TYPE_CHECKING: - _: Any - -# used for signal handler -SIG_TRIGGER = None - - -def get_password(prompt: str = '') -> Optional[str]: - if not prompt: - prompt = _("Enter a password: ") - - while password := getpass.getpass(prompt): - if len(password.strip()) <= 0: - break - - strength = PasswordStrength.strength(password) - log(f'Password strength: {strength.value}', fg=strength.color()) - - passwd_verification = getpass.getpass(prompt=_('And one more time for verification: ')) - if password != passwd_verification: - log(' * Passwords did not match * ', fg='red') - continue - - return password - - return None diff --git a/archinstall/lib/utils/util.py b/archinstall/lib/utils/util.py index ded480ae..34716f4a 100644 --- a/archinstall/lib/utils/util.py +++ b/archinstall/lib/utils/util.py @@ -1,7 +1,7 @@ from pathlib import Path from typing import Any, TYPE_CHECKING, Optional -from ..output import log +from ..output import info if TYPE_CHECKING: _: Any @@ -16,7 +16,7 @@ def prompt_dir(text: str, header: Optional[str] = None) -> Path: dest_path = Path(path) if dest_path.exists() and dest_path.is_dir(): return dest_path - log(_('Not a valid directory: {}').format(dest_path), fg='red') + info(_('Not a valid directory: {}').format(dest_path)) def is_subpath(first: Path, second: Path): diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 9906e0a9..37cc1cad 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -1,9 +1,10 @@ -import logging import os from pathlib import Path from typing import Any, TYPE_CHECKING import archinstall +from archinstall import info, debug +from archinstall import SysInfo from archinstall.lib import disk from archinstall.lib.global_menu import GlobalMenu from archinstall.default_profiles.applications.pipewire import PipewireProfile @@ -13,7 +14,7 @@ from archinstall.lib.menu import Menu from archinstall.lib.mirrors import use_mirrors from archinstall.lib.models.bootloader import Bootloader from archinstall.lib.models.network_configuration import NetworkConfigurationHandler -from archinstall.lib.output import log +from archinstall.lib.networking import check_mirror_reachable from archinstall.lib.profile.profiles_handler import profile_handler if TYPE_CHECKING: @@ -24,20 +25,6 @@ if archinstall.arguments.get('help'): print("See `man archinstall` for help.") exit(0) -if os.getuid() != 0: - print(_("Archinstall requires root privileges to run. See --help for more.")) - exit(1) - -# Log various information about hardware before starting the installation. This might assist in troubleshooting -archinstall.log(f"Hardware model detected: {archinstall.sys_vendor()} {archinstall.product_name()}; UEFI mode: {archinstall.has_uefi()}", level=logging.DEBUG) -archinstall.log(f"Processor model detected: {archinstall.cpu_model()}", level=logging.DEBUG) -archinstall.log(f"Memory statistics: {archinstall.mem_available()} available out of {archinstall.mem_total()} total installed", level=logging.DEBUG) -archinstall.log(f"Virtualization detected: {archinstall.virtualization()}; is VM: {archinstall.is_vm()}", level=logging.DEBUG) -archinstall.log(f"Graphics devices detected: {archinstall.graphics_devices().keys()}", level=logging.DEBUG) - -# For support reasons, we'll log the disk layout pre installation to match against post-installation layout -archinstall.log(f"Disk states before installing: {disk.disk_layouts()}", level=logging.DEBUG) - def ask_user_questions(): """ @@ -121,7 +108,7 @@ def perform_installation(mountpoint: Path): Only requirement is that the block devices are formatted and setup prior to entering this function. """ - log('Starting installation', level=logging.INFO) + info('Starting installation') disk_config: disk.DiskLayoutConfiguration = archinstall.arguments['disk_config'] # Retrieve list of additional repositories and set boolean values appropriately @@ -167,7 +154,7 @@ def perform_installation(mountpoint: Path): if archinstall.arguments.get('swap'): installation.setup_swap('zram') - if archinstall.arguments.get("bootloader") == Bootloader.Grub and archinstall.has_uefi(): + if archinstall.arguments.get("bootloader") == Bootloader.Grub and SysInfo.has_uefi(): installation.add_additional_packages("grub") installation.add_bootloader(archinstall.arguments["bootloader"]) @@ -190,13 +177,13 @@ def perform_installation(mountpoint: Path): installation.create_users(users) if audio := archinstall.arguments.get('audio', None): - log(f'Installing audio server: {audio}', level=logging.INFO) + info(f'Installing audio server: {audio}') if audio == 'pipewire': PipewireProfile().install(installation) elif audio == 'pulseaudio': installation.add_additional_packages("pulseaudio") else: - installation.log("No audio server will be installed.", level=logging.INFO) + info("No audio server will be installed") if profile_config := archinstall.arguments.get('profile_config', None): profile_handler.install_profile_config(installation, profile_config) @@ -231,7 +218,7 @@ def perform_installation(mountpoint: Path): installation.genfstab() - installation.log("For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation", fg="yellow") + info("For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation") if not archinstall.arguments.get('silent'): prompt = str(_('Would you like to chroot into the newly created installation and perform post-installation configuration?')) @@ -242,12 +229,12 @@ def perform_installation(mountpoint: Path): except: pass - archinstall.log(f"Disk states after installing: {disk.disk_layouts()}", level=logging.DEBUG) + debug(f"Disk states after installing: {disk.disk_layouts()}") -if archinstall.arguments.get('skip-mirror-check', False) is False and archinstall.check_mirror_reachable() is False: +if archinstall.arguments.get('skip-mirror-check', False) is False and check_mirror_reachable() is False: log_file = os.path.join(archinstall.storage.get('LOG_PATH', None), archinstall.storage.get('LOG_FILE', None)) - archinstall.log(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'.", level=logging.INFO, fg="red") + info(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'.") exit(1) if not archinstall.arguments.get('silent'): diff --git a/archinstall/scripts/minimal.py b/archinstall/scripts/minimal.py index 0cdbdcef..704759fc 100644 --- a/archinstall/scripts/minimal.py +++ b/archinstall/scripts/minimal.py @@ -2,23 +2,25 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, List import archinstall -from archinstall import ConfigurationOutput, Installer, ProfileConfiguration, profile_handler +from archinstall import info +from archinstall import Installer, ConfigurationOutput from archinstall.default_profiles.minimal import MinimalProfile -from archinstall import disk -from archinstall import models -from archinstall.lib.user_interaction.disk_conf import select_devices, suggest_single_disk_layout +from archinstall.lib.interactions import suggest_single_disk_layout, select_devices +from archinstall.lib.models import Bootloader, User +from archinstall.lib.profile import ProfileConfiguration, profile_handler +from archinstall.lib import disk if TYPE_CHECKING: _: Any -archinstall.log("Minimal only supports:") -archinstall.log(" * Being installed to a single disk") +info("Minimal only supports:") +info(" * Being installed to a single disk") if archinstall.arguments.get('help', None): - archinstall.log(" - Optional disk encryption via --!encryption-password=") - archinstall.log(" - Optional filesystem type via --filesystem=") - archinstall.log(" - Optional systemd network via --network") + info(" - Optional disk encryption via --!encryption-password=") + info(" - Optional filesystem type via --filesystem=") + info(" - Optional systemd network via --network") def perform_installation(mountpoint: Path): @@ -35,7 +37,7 @@ def perform_installation(mountpoint: Path): # some other minor details as specified by this profile and user. if installation.minimal_installation(): installation.set_hostname('minimal-arch') - installation.add_bootloader(models.Bootloader.Systemd) + installation.add_bootloader(Bootloader.Systemd) # Optionally enable networking: if archinstall.arguments.get('network', None): @@ -46,14 +48,14 @@ def perform_installation(mountpoint: Path): profile_config = ProfileConfiguration(MinimalProfile()) profile_handler.install_profile_config(installation, profile_config) - user = models.User('devel', 'devel', False) + 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. - archinstall.log("There are two new accounts in your installation after reboot:") - archinstall.log(" * root (password: airoot)") - archinstall.log(" * devel (password: devel)") + info("There are two new accounts in your installation after reboot:") + info(" * root (password: airoot)") + info(" * devel (password: devel)") def prompt_disk_layout(): diff --git a/archinstall/scripts/only_hd.py b/archinstall/scripts/only_hd.py index a903c5fe..d0ee1e39 100644 --- a/archinstall/scripts/only_hd.py +++ b/archinstall/scripts/only_hd.py @@ -1,22 +1,18 @@ -import logging import os from pathlib import Path import archinstall -from archinstall import Installer +from archinstall import info, debug +from archinstall.lib.installer import Installer from archinstall.lib.configuration import ConfigurationOutput -from archinstall import disk +from archinstall.lib import disk +from archinstall.lib.networking import check_mirror_reachable if archinstall.arguments.get('help'): print("See `man archinstall` for help.") exit(0) -if os.getuid() != 0: - print("Archinstall requires root privileges to run. See --help for more.") - exit(1) - - def ask_user_questions(): global_menu = archinstall.GlobalMenu(data_store=archinstall.arguments) @@ -59,23 +55,12 @@ def perform_installation(mountpoint: Path): target.parent.mkdir(parents=True) # For support reasons, we'll log the disk layout post installation (crash or no crash) - archinstall.log(f"Disk states after installing: {disk.disk_layouts()}", level=logging.DEBUG) - - -# Log various information about hardware before starting the installation. This might assist in troubleshooting -archinstall.log(f"Hardware model detected: {archinstall.sys_vendor()} {archinstall.product_name()}; UEFI mode: {archinstall.has_uefi()}", level=logging.DEBUG) -archinstall.log(f"Processor model detected: {archinstall.cpu_model()}", level=logging.DEBUG) -archinstall.log(f"Memory statistics: {archinstall.mem_available()} available out of {archinstall.mem_total()} total installed", level=logging.DEBUG) -archinstall.log(f"Virtualization detected: {archinstall.virtualization()}; is VM: {archinstall.is_vm()}", level=logging.DEBUG) -archinstall.log(f"Graphics devices detected: {archinstall.graphics_devices().keys()}", level=logging.DEBUG) - -# For support reasons, we'll log the disk layout pre installation to match against post-installation layout -archinstall.log(f"Disk states before installing: {disk.disk_layouts()}", level=logging.DEBUG) + debug(f"Disk states after installing: {disk.disk_layouts()}") -if archinstall.arguments.get('skip-mirror-check', False) is False and archinstall.check_mirror_reachable() is False: +if archinstall.arguments.get('skip-mirror-check', False) is False and check_mirror_reachable() is False: log_file = os.path.join(archinstall.storage.get('LOG_PATH', None), archinstall.storage.get('LOG_FILE', None)) - archinstall.log(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'.", level=logging.INFO, fg="red") + info(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'") exit(1) if not archinstall.arguments.get('silent'): diff --git a/archinstall/scripts/swiss.py b/archinstall/scripts/swiss.py index 3bf847b1..a49f568d 100644 --- a/archinstall/scripts/swiss.py +++ b/archinstall/scripts/swiss.py @@ -1,18 +1,18 @@ -import logging import os from enum import Enum from pathlib import Path from typing import TYPE_CHECKING, Any, Dict import archinstall +from archinstall import SysInfo, info, debug from archinstall.lib.mirrors import use_mirrors -from archinstall import models -from archinstall import disk +from archinstall.lib import models +from archinstall.lib import disk +from archinstall.lib.networking import check_mirror_reachable from archinstall.lib.profile.profiles_handler import profile_handler -from archinstall import menu +from archinstall.lib import menu from archinstall.lib.global_menu import GlobalMenu -from archinstall.lib.output import log -from archinstall import Installer +from archinstall.lib.installer import Installer from archinstall.lib.configuration import ConfigurationOutput from archinstall.default_profiles.applications.pipewire import PipewireProfile @@ -25,11 +25,6 @@ if archinstall.arguments.get('help'): exit(0) -if os.getuid() != 0: - print("Archinstall requires root privileges to run. See --help for more.") - exit(1) - - class ExecutionMode(Enum): Full = 'full' Lineal = 'lineal' @@ -76,7 +71,7 @@ class SetupMenu(GlobalMenu): def exit_callback(self): if self._data_store.get('mode', None): archinstall.arguments['mode'] = self._data_store['mode'] - log(f"Archinstall will execute under {archinstall.arguments['mode']} mode") + info(f"Archinstall will execute under {archinstall.arguments['mode']} mode") class SwissMainMenu(GlobalMenu): @@ -124,7 +119,7 @@ class SwissMainMenu(GlobalMenu): case ExecutionMode.Minimal: pass case _: - archinstall.log(f' Execution mode {self._execution_mode} not supported') + info(f' Execution mode {self._execution_mode} not supported') exit(1) if self._execution_mode != ExecutionMode.Lineal: @@ -219,7 +214,7 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): if archinstall.arguments.get('swap'): installation.setup_swap('zram') - if archinstall.arguments.get("bootloader") == models.Bootloader.Grub and archinstall.has_uefi(): + if archinstall.arguments.get("bootloader") == models.Bootloader.Grub and SysInfo.has_uefi(): installation.add_additional_packages("grub") installation.add_bootloader(archinstall.arguments["bootloader"]) @@ -242,13 +237,13 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): installation.create_users(users) if audio := archinstall.arguments.get('audio', None): - log(f'Installing audio server: {audio}', level=logging.INFO) + info(f'Installing audio server: {audio}') if audio == 'pipewire': PipewireProfile().install(installation) elif audio == 'pulseaudio': installation.add_additional_packages("pulseaudio") else: - installation.log("No audio server will be installed.", level=logging.INFO) + info("No audio server will be installed.") if profile_config := archinstall.arguments.get('profile_config', None): profile_handler.install_profile_config(installation, profile_config) @@ -283,9 +278,7 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): installation.genfstab() - installation.log( - "For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation", - fg="yellow") + info("For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation") if not archinstall.arguments.get('silent'): prompt = str( @@ -297,23 +290,12 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): except: pass - archinstall.log(f"Disk states after installing: {disk.disk_layouts()}", level=logging.DEBUG) - - -# Log various information about hardware before starting the installation. This might assist in troubleshooting -archinstall.log(f"Hardware model detected: {archinstall.sys_vendor()} {archinstall.product_name()}; UEFI mode: {archinstall.has_uefi()}", level=logging.DEBUG) -archinstall.log(f"Processor model detected: {archinstall.cpu_model()}", level=logging.DEBUG) -archinstall.log(f"Memory statistics: {archinstall.mem_available()} available out of {archinstall.mem_total()} total installed", level=logging.DEBUG) -archinstall.log(f"Virtualization detected: {archinstall.virtualization()}; is VM: {archinstall.is_vm()}", level=logging.DEBUG) -archinstall.log(f"Graphics devices detected: {archinstall.graphics_devices().keys()}", level=logging.DEBUG) - -# For support reasons, we'll log the disk layout pre installation to match against post-installation layout -archinstall.log(f"Disk states before installing: {disk.disk_layouts()}", level=logging.DEBUG) + debug(f"Disk states after installing: {disk.disk_layouts()}") -if not archinstall.check_mirror_reachable(): +if not check_mirror_reachable(): log_file = os.path.join(archinstall.storage.get('LOG_PATH', None), archinstall.storage.get('LOG_FILE', None)) - archinstall.log(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'.", level=logging.INFO, fg="red") + info(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'") exit(1) param_mode = archinstall.arguments.get('mode', ExecutionMode.Full.value).lower() @@ -321,7 +303,7 @@ param_mode = archinstall.arguments.get('mode', ExecutionMode.Full.value).lower() try: mode = ExecutionMode(param_mode) except KeyError: - log(f'Mode "{param_mode}" is not supported') + info(f'Mode "{param_mode}" is not supported') exit(1) if not archinstall.arguments.get('silent'): diff --git a/archinstall/scripts/unattended.py b/archinstall/scripts/unattended.py index 0a1c5160..5ae4ae3d 100644 --- a/archinstall/scripts/unattended.py +++ b/archinstall/scripts/unattended.py @@ -1,13 +1,14 @@ import time import archinstall -from archinstall.lib.profile.profiles_handler import profile_handler +from archinstall import info +from archinstall import profile -for profile in profile_handler.get_mac_addr_profiles(): +for p in profile.profile_handler.get_mac_addr_profiles(): # Tailored means it's a match for this machine # based on it's MAC address (or some other criteria # that fits the requirements for this machine specifically). - archinstall.log(f'Found a tailored profile for this machine called: "{profile.name}"') + info(f'Found a tailored profile for this machine called: "{p.name}"') print('Starting install in:') for i in range(10, 0, -1): @@ -15,4 +16,4 @@ for profile in profile_handler.get_mac_addr_profiles(): time.sleep(1) install_session = archinstall.storage['installation_session'] - profile.install(install_session) + p.install(install_session) diff --git a/examples/full_automated_installation.py b/examples/full_automated_installation.py index a169dd50..dcef731a 100644 --- a/examples/full_automated_installation.py +++ b/examples/full_automated_installation.py @@ -1,9 +1,10 @@ from pathlib import Path -from archinstall import Installer, ProfileConfiguration, profile_handler +from archinstall import Installer +from archinstall import profile from archinstall.default_profiles.minimal import MinimalProfile from archinstall import disk -from archinstall.lib.models import User +from archinstall import models # we're creating a new ext4 filesystem installation fs_type = disk.FilesystemType('ext4') @@ -88,8 +89,8 @@ with Installer( # Optionally, install a profile of choice. # In this case, we install a minimal profile that is empty -profile_config = ProfileConfiguration(MinimalProfile()) -profile_handler.install_profile_config(installation, profile_config) +profile_config = profile.ProfileConfiguration(MinimalProfile()) +profile.profile_handler.install_profile_config(installation, profile_config) -user = User('archinstall', 'password', True) +user = models.User('archinstall', 'password', True) installation.create_users(user) diff --git a/examples/interactive_installation.py b/examples/interactive_installation.py index a27ec0f9..72595048 100644 --- a/examples/interactive_installation.py +++ b/examples/interactive_installation.py @@ -1,13 +1,16 @@ -import logging from pathlib import Path from typing import TYPE_CHECKING, Any import archinstall -from archinstall import log, Installer, use_mirrors, profile_handler +from archinstall import Installer +from archinstall import profile +from archinstall import SysInfo +from archinstall import mirrors from archinstall.default_profiles.applications.pipewire import PipewireProfile from archinstall import disk from archinstall import menu -from archinstall.lib.models import Bootloader, NetworkConfigurationHandler +from archinstall import models +from archinstall import info, debug if TYPE_CHECKING: _: Any @@ -84,7 +87,7 @@ def perform_installation(mountpoint: Path): Only requirement is that the block devices are formatted and setup prior to entering this function. """ - log('Starting installation', level=logging.INFO) + info('Starting installation') disk_config: disk.DiskLayoutConfiguration = archinstall.arguments['disk_config'] # Retrieve list of additional repositories and set boolean values appropriately @@ -114,7 +117,7 @@ def perform_installation(mountpoint: Path): # Set mirrors used by pacstrap (outside of installation) if archinstall.arguments.get('mirror-region', None): - use_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors for the live medium + mirrors.use_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors for the live medium installation.minimal_installation( testing=enable_testing, @@ -130,7 +133,7 @@ def perform_installation(mountpoint: Path): if archinstall.arguments.get('swap'): installation.setup_swap('zram') - if archinstall.arguments.get("bootloader") == Bootloader.Grub and archinstall.has_uefi(): + if archinstall.arguments.get("bootloader") == models.Bootloader.Grub and SysInfo.has_uefi(): installation.add_additional_packages("grub") installation.add_bootloader(archinstall.arguments["bootloader"]) @@ -140,7 +143,7 @@ def perform_installation(mountpoint: Path): network_config = archinstall.arguments.get('nic', None) if network_config: - handler = NetworkConfigurationHandler(network_config) + handler = models.NetworkConfigurationHandler(network_config) handler.config_installer( installation, archinstall.arguments.get('profile_config', None) @@ -153,16 +156,16 @@ def perform_installation(mountpoint: Path): installation.create_users(users) if audio := archinstall.arguments.get('audio', None): - log(f'Installing audio server: {audio}', level=logging.INFO) + info(f'Installing audio server: {audio}') if audio == 'pipewire': PipewireProfile().install(installation) elif audio == 'pulseaudio': installation.add_additional_packages("pulseaudio") else: - installation.log("No audio server will be installed.", level=logging.INFO) + info("No audio server will be installed.") if profile_config := archinstall.arguments.get('profile_config', None): - profile_handler.install_profile_config(installation, profile_config) + profile.profile_handler.install_profile_config(installation, profile_config) if timezone := archinstall.arguments.get('timezone', None): installation.set_timezone(timezone) @@ -194,7 +197,7 @@ def perform_installation(mountpoint: Path): installation.genfstab() - installation.log("For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation", fg="yellow") + info("For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation") if not archinstall.arguments.get('silent'): prompt = str(_('Would you like to chroot into the newly created installation and perform post-installation configuration?')) @@ -202,10 +205,10 @@ def perform_installation(mountpoint: Path): if choice.value == menu.Menu.yes(): try: installation.drop_to_shell() - except: + except Exception: pass - archinstall.log(f"Disk states after installing: {disk.disk_layouts()}", level=logging.DEBUG) + debug(f"Disk states after installing: {disk.disk_layouts()}") ask_user_questions() diff --git a/examples/mac_address_installation.py b/examples/mac_address_installation.py index 0a1c5160..74a123c7 100644 --- a/examples/mac_address_installation.py +++ b/examples/mac_address_installation.py @@ -1,13 +1,13 @@ import time import archinstall -from archinstall.lib.profile.profiles_handler import profile_handler +from archinstall import profile, info -for profile in profile_handler.get_mac_addr_profiles(): +for _profile in profile.profile_handler.get_mac_addr_profiles(): # Tailored means it's a match for this machine # based on it's MAC address (or some other criteria # that fits the requirements for this machine specifically). - archinstall.log(f'Found a tailored profile for this machine called: "{profile.name}"') + info(f'Found a tailored profile for this machine called: "{_profile.name}"') print('Starting install in:') for i in range(10, 0, -1): @@ -15,4 +15,4 @@ for profile in profile_handler.get_mac_addr_profiles(): time.sleep(1) install_session = archinstall.storage['installation_session'] - profile.install(install_session) + _profile.install(install_session) diff --git a/examples/minimal_installation.py b/examples/minimal_installation.py index 8bd6fd55..e31adea4 100644 --- a/examples/minimal_installation.py +++ b/examples/minimal_installation.py @@ -2,11 +2,12 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, List import archinstall -from archinstall.lib import disk -from archinstall import Installer, ProfileConfiguration, profile_handler +from archinstall import disk +from archinstall import Installer +from archinstall import profile +from archinstall import models +from archinstall import interactions from archinstall.default_profiles.minimal import MinimalProfile -from archinstall.lib.models import Bootloader, User -from archinstall.lib.user_interaction.disk_conf import select_devices, suggest_single_disk_layout if TYPE_CHECKING: _: Any @@ -26,7 +27,7 @@ def perform_installation(mountpoint: Path): # some other minor details as specified by this profile and user. if installation.minimal_installation(): installation.set_hostname('minimal-arch') - installation.add_bootloader(Bootloader.Systemd) + installation.add_bootloader(models.Bootloader.Systemd) # Optionally enable networking: if archinstall.arguments.get('network', None): @@ -34,10 +35,10 @@ def perform_installation(mountpoint: Path): installation.add_additional_packages(['nano', 'wget', 'git']) - profile_config = ProfileConfiguration(MinimalProfile()) - profile_handler.install_profile_config(installation, profile_config) + profile_config = profile.ProfileConfiguration(MinimalProfile()) + profile.profile_handler.install_profile_config(installation, profile_config) - user = User('devel', 'devel', False) + user = models.User('devel', 'devel', False) installation.create_users(user) @@ -46,8 +47,8 @@ def prompt_disk_layout(): if filesystem := archinstall.arguments.get('filesystem', None): fs_type = disk.FilesystemType(filesystem) - devices = select_devices() - modifications = suggest_single_disk_layout(devices[0], filesystem_type=fs_type) + devices = interactions.select_devices() + modifications = interactions.suggest_single_disk_layout(devices[0], filesystem_type=fs_type) archinstall.arguments['disk_config'] = disk.DiskLayoutConfiguration( config_type=disk.DiskLayoutType.Default, diff --git a/examples/only_hd_installation.py b/examples/only_hd_installation.py index 2fc74bf0..075bde20 100644 --- a/examples/only_hd_installation.py +++ b/examples/only_hd_installation.py @@ -1,9 +1,7 @@ -import logging from pathlib import Path import archinstall -from archinstall import Installer -from archinstall.lib import disk +from archinstall import Installer, disk, debug def ask_user_questions(): @@ -48,7 +46,7 @@ def perform_installation(mountpoint: Path): target.parent.mkdir(parents=True) # For support reasons, we'll log the disk layout post installation (crash or no crash) - archinstall.log(f"Disk states after installing: {disk.disk_layouts()}", level=logging.DEBUG) + debug(f"Disk states after installing: {disk.disk_layouts()}") ask_user_questions() diff --git a/pyproject.toml b/pyproject.toml index f837ebdf..8b6ae4c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ Source = "https://github.com/archlinux/archinstall" [project.optional-dependencies] dev = [ "mypy==1.1.1", + "pre-commit==3.3.1", ] doc = ["sphinx"] -- cgit v1.2.3-70-g09d2 From 128db1cdf6698aba09915bb7c044404f713755ef Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Fri, 12 May 2023 02:24:14 -0400 Subject: Install the package `sof-firmware` if required (#1811) --- archinstall/lib/hardware.py | 21 ++++++++++++++++++++- archinstall/scripts/guided.py | 3 +++ examples/interactive_installation.py | 3 +++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/archinstall/lib/hardware.py b/archinstall/lib/hardware.py index b95301f9..8d0fb74f 100644 --- a/archinstall/lib/hardware.py +++ b/archinstall/lib/hardware.py @@ -1,7 +1,7 @@ import os from functools import cached_property from pathlib import Path -from typing import Optional, Dict +from typing import Optional, Dict, List from .general import SysCommand from .networking import list_interfaces, enrich_iface_types @@ -169,3 +169,22 @@ class SysInfo: debug(f"System is not running in a VM: {err}") return False + + @staticmethod + def _loaded_modules() -> List[str]: + """ + Returns loaded kernel modules + """ + modules_path = Path('/proc/modules') + modules: List[str] = [] + + with modules_path.open() as file: + for line in file: + module = line.split(maxsplit=1)[0] + modules.append(module) + + return modules + + @staticmethod + def requires_sof() -> bool: + return 'snd_sof' in SysInfo._loaded_modules() diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 37cc1cad..9cb4ec2a 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -182,6 +182,9 @@ def perform_installation(mountpoint: Path): PipewireProfile().install(installation) elif audio == 'pulseaudio': installation.add_additional_packages("pulseaudio") + + if SysInfo.requires_sof(): + installation.add_additional_packages('sof-firmware') else: info("No audio server will be installed") diff --git a/examples/interactive_installation.py b/examples/interactive_installation.py index 72595048..5738a9cc 100644 --- a/examples/interactive_installation.py +++ b/examples/interactive_installation.py @@ -161,6 +161,9 @@ def perform_installation(mountpoint: Path): PipewireProfile().install(installation) elif audio == 'pulseaudio': installation.add_additional_packages("pulseaudio") + + if SysInfo.requires_sof(): + installation.add_additional_packages('sof-firmware') else: info("No audio server will be installed.") -- cgit v1.2.3-70-g09d2 From 06f35fd289a06c5d142d65466820ff132e838365 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Fri, 12 May 2023 08:01:24 -0400 Subject: Install the package `alsa-firmware` if required (#1812) --- archinstall/lib/hardware.py | 34 +++++++++++++++++++--------------- archinstall/scripts/guided.py | 5 ++++- examples/interactive_installation.py | 5 ++++- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/archinstall/lib/hardware.py b/archinstall/lib/hardware.py index 8d0fb74f..220d3d37 100644 --- a/archinstall/lib/hardware.py +++ b/archinstall/lib/hardware.py @@ -86,6 +86,21 @@ class _SysInfo: def mem_info_by_key(self, key: str) -> int: return self.mem_info[key] + @cached_property + def loaded_modules(self) -> List[str]: + """ + Returns loaded kernel modules + """ + modules_path = Path('/proc/modules') + modules: List[str] = [] + + with modules_path.open() as file: + for line in file: + module = line.split(maxsplit=1)[0] + modules.append(module) + + return modules + _sys_info = _SysInfo() @@ -171,20 +186,9 @@ class SysInfo: return False @staticmethod - def _loaded_modules() -> List[str]: - """ - Returns loaded kernel modules - """ - modules_path = Path('/proc/modules') - modules: List[str] = [] - - with modules_path.open() as file: - for line in file: - module = line.split(maxsplit=1)[0] - modules.append(module) - - return modules + def requires_sof_fw() -> bool: + return 'snd_sof' in _sys_info.loaded_modules @staticmethod - def requires_sof() -> bool: - return 'snd_sof' in SysInfo._loaded_modules() + def requires_alsa_fw() -> bool: + return 'snd_emu10k1' in _sys_info.loaded_modules diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 9cb4ec2a..1e19c9a3 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -183,8 +183,11 @@ def perform_installation(mountpoint: Path): elif audio == 'pulseaudio': installation.add_additional_packages("pulseaudio") - if SysInfo.requires_sof(): + if SysInfo.requires_sof_fw(): installation.add_additional_packages('sof-firmware') + + if SysInfo.requires_alsa_fw(): + installation.add_additional_packages('alsa-firmware') else: info("No audio server will be installed") diff --git a/examples/interactive_installation.py b/examples/interactive_installation.py index 5738a9cc..487db4dd 100644 --- a/examples/interactive_installation.py +++ b/examples/interactive_installation.py @@ -162,8 +162,11 @@ def perform_installation(mountpoint: Path): elif audio == 'pulseaudio': installation.add_additional_packages("pulseaudio") - if SysInfo.requires_sof(): + if SysInfo.requires_sof_fw(): installation.add_additional_packages('sof-firmware') + + if SysInfo.requires_alsa_fw(): + installation.add_additional_packages('alsa-firmware') else: info("No audio server will be installed.") -- cgit v1.2.3-70-g09d2 From d65359896a60dc57eb9f18c86f692c9eced7f644 Mon Sep 17 00:00:00 2001 From: Pedro Dutra <93670432+pdutra145@users.noreply.github.com> Date: Fri, 12 May 2023 15:32:11 -0300 Subject: simple fix in README.md (#1813) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 15646170..7f48bfba 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ An example of the user configuration file can be found here and example of the credentials configuration here [credentials file](https://github.com/archlinux/archinstall/blob/master/examples/creds-sample.json). -**HINT:** The configuration files can be auto-generated by starting `archisntall`, configuring all desired menu +**HINT:** The configuration files can be auto-generated by starting `archinstall`, configuring all desired menu points and then going to `Save configuration`. To load the configuration file into `archinstall` run the following command -- cgit v1.2.3-70-g09d2 From 8a292a163ea2e643a8ac5d4cfada8a27076de630 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Mon, 15 May 2023 17:16:18 +1000 Subject: Add custom mirror support (#1816) Co-authored-by: Daniel Girtler --- archinstall/__init__.py | 8 +- archinstall/lib/global_menu.py | 34 ++- archinstall/lib/installer.py | 15 +- archinstall/lib/interactions/__init__.py | 2 +- archinstall/lib/interactions/general_conf.py | 37 +-- archinstall/lib/mirrors.py | 442 ++++++++++++++++++--------- archinstall/scripts/guided.py | 16 +- archinstall/scripts/swiss.py | 19 +- examples/interactive_installation.py | 14 +- 9 files changed, 358 insertions(+), 229 deletions(-) diff --git a/archinstall/__init__.py b/archinstall/__init__.py index 992bd9fa..e6fcb267 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -225,12 +225,8 @@ def load_config(): if profile_config := arguments.get('profile_config', None): arguments['profile_config'] = profile.ProfileConfiguration.parse_arg(profile_config) - if arguments.get('mirror-region', None) is not None: - if type(arguments.get('mirror-region', None)) is dict: - arguments['mirror-region'] = arguments.get('mirror-region', None) - else: - selected_region = arguments.get('mirror-region', None) - arguments['mirror-region'] = {selected_region: mirrors.list_mirrors()[selected_region]} + if mirror_config := arguments.get('mirror_config', None): + arguments['mirror_config'] = mirrors.MirrorConfiguration.parse_args(mirror_config) if arguments.get('servers', None) is not None: storage['_selected_servers'] = arguments.get('servers', None) diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 13595132..fc58a653 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -5,6 +5,7 @@ from typing import Any, List, Optional, Union, Dict, TYPE_CHECKING from . import disk from .general import secret from .menu import Selector, AbstractMenu +from .mirrors import MirrorConfiguration, MirrorMenu from .models import NetworkConfiguration from .models.bootloader import Bootloader from .models.users import User @@ -26,7 +27,6 @@ from .interactions import select_kernel from .interactions import select_language from .interactions import select_locale_enc from .interactions import select_locale_lang -from .interactions import select_mirror_regions from .interactions import ask_ntp from .interactions.disk_conf import select_disk_config @@ -51,12 +51,13 @@ class GlobalMenu(AbstractMenu): _('Keyboard layout'), lambda preset: select_language(preset), default='us') - self._menu_options['mirror-region'] = \ + self._menu_options['mirror_config'] = \ Selector( - _('Mirror region'), - lambda preset: select_mirror_regions(preset), - display_func=lambda x: list(x.keys()) if x else '[]', - default={}) + _('Mirrors'), + lambda preset: self._mirror_configuration(preset), + display_func=lambda x: str(_('Defined')) if x else '', + preview_func=self._prev_mirror_config + ) self._menu_options['sys-language'] = \ Selector( _('Locale language'), @@ -354,3 +355,24 @@ class GlobalMenu(AbstractMenu): def _create_user_account(self, defined_users: List[User]) -> List[User]: users = ask_for_additional_users(defined_users=defined_users) return users + + def _mirror_configuration(self, preset: Optional[MirrorConfiguration] = None) -> Optional[MirrorConfiguration]: + data_store: Dict[str, Any] = {} + mirror_configuration = MirrorMenu(data_store, preset=preset).run() + return mirror_configuration + + def _prev_mirror_config(self) -> Optional[str]: + selector = self._menu_options['mirror_config'] + + if selector.has_selection(): + mirror_config: MirrorConfiguration = selector.current_selection # type: ignore + output = '' + if mirror_config.regions: + output += '{}: {}\n\n'.format(str(_('Mirror regions')), mirror_config.regions) + if mirror_config.custom_mirrors: + table = FormattedOutput.as_table(mirror_config.custom_mirrors) + output += '{}\n{}'.format(str(_('Custom mirrors')), table) + + return output.strip() + + return None diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 3c427ab2..30442774 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -6,7 +6,7 @@ import shutil import subprocess import time from pathlib import Path -from typing import Any, List, Optional, TYPE_CHECKING, Union, Dict, Callable, Iterable +from typing import Any, List, Optional, TYPE_CHECKING, Union, Dict, Callable from . import disk from .exceptions import DiskError, ServiceException, RequirementError, HardwareIncompatibilityError, SysCallError @@ -14,7 +14,7 @@ from .general import SysCommand from .hardware import SysInfo from .locale import verify_keyboard_layout, verify_x11_keyboard_layout from .luks import Luks2 -from .mirrors import use_mirrors +from .mirrors import use_mirrors, MirrorConfiguration, add_custom_mirrors from .models.bootloader import Bootloader from .models.network_configuration import NetworkConfiguration from .models.users import User @@ -383,14 +383,17 @@ class Installer: raise RequirementError("Pacstrap failed. See /var/log/archinstall/install.log or above message for error details.") - def set_mirrors(self, mirrors: Dict[str, Iterable[str]]): + def set_mirrors(self, mirror_config: MirrorConfiguration): for plugin in plugins.values(): if hasattr(plugin, 'on_mirrors'): - if result := plugin.on_mirrors(mirrors): - mirrors = result + if result := plugin.on_mirrors(mirror_config): + mirror_config = result destination = f'{self.target}/etc/pacman.d/mirrorlist' - use_mirrors(mirrors, destination=destination) + if mirror_config.mirror_regions: + use_mirrors(mirror_config.mirror_regions, destination) + if mirror_config.custom_mirrors: + add_custom_mirrors(mirror_config.custom_mirrors) def genfstab(self, flags :str = '-pU'): info(f"Updating {self.target}/etc/fstab") diff --git a/archinstall/lib/interactions/__init__.py b/archinstall/lib/interactions/__init__.py index b5691a10..158750cc 100644 --- a/archinstall/lib/interactions/__init__.py +++ b/archinstall/lib/interactions/__init__.py @@ -11,7 +11,7 @@ from .disk_conf import ( from .general_conf import ( ask_ntp, ask_hostname, ask_for_a_timezone, ask_for_audio_selection, select_language, - select_mirror_regions, select_archinstall_language, ask_additional_packages_to_install, + select_archinstall_language, ask_additional_packages_to_install, add_number_of_parrallel_downloads, select_additional_repositories ) diff --git a/archinstall/lib/interactions/general_conf.py b/archinstall/lib/interactions/general_conf.py index 5fcfa633..0338c61e 100644 --- a/archinstall/lib/interactions/general_conf.py +++ b/archinstall/lib/interactions/general_conf.py @@ -1,11 +1,10 @@ from __future__ import annotations import pathlib -from typing import List, Any, Optional, Dict, TYPE_CHECKING +from typing import List, Any, Optional, TYPE_CHECKING from ..locale import list_keyboard_languages, list_timezones from ..menu import MenuSelectionType, Menu, TextInput -from ..mirrors import list_mirrors from ..output import warn from ..packages.packages import validate_package_list from ..storage import storage @@ -96,40 +95,6 @@ def select_language(preset: Optional[str] = None) -> Optional[str]: return None -def select_mirror_regions(preset_values: Dict[str, Any] = {}) -> Dict[str, Any]: - """ - Asks the user to select a mirror or region - Usually this is combined with :ref:`archinstall.list_mirrors`. - - :return: The dictionary information about a mirror/region. - :rtype: dict - """ - if preset_values is None: - preselected = None - else: - preselected = list(preset_values.keys()) - - mirrors = list_mirrors() - - choice = Menu( - _('Select one of the regions to download packages from'), - list(mirrors.keys()), - preset_values=preselected, - multi=True, - allow_reset=True - ).run() - - match choice.type_: - case MenuSelectionType.Reset: - return {} - case MenuSelectionType.Skip: - return preset_values - case MenuSelectionType.Selection: - return {selected: mirrors[selected] for selected in choice.multi_value} - - return {} - - def select_archinstall_language(languages: List[Language], preset: Language) -> Language: # these are the displayed language names which can either be # the english name of a language or, if present, the diff --git a/archinstall/lib/mirrors.py b/archinstall/lib/mirrors.py index 62a0b081..521a8e5b 100644 --- a/archinstall/lib/mirrors.py +++ b/archinstall/lib/mirrors.py @@ -1,99 +1,279 @@ import pathlib -import urllib.error -import urllib.request -from typing import Union, Iterable, Dict, Any, List -from dataclasses import dataclass - -from .general import SysCommand -from .output import info, warn -from .exceptions import SysCallError +from dataclasses import dataclass, field +from enum import Enum +from typing import Dict, Any, List, Optional, TYPE_CHECKING + +from .menu import AbstractSubMenu, Selector, MenuSelectionType, Menu, ListManager, TextInput +from .networking import fetch_data_from_url +from .output import info, warn, FormattedOutput from .storage import storage +if TYPE_CHECKING: + _: Any -@dataclass -class CustomMirror: - url: str - signcheck: str - signoptions: str - name: str +class SignCheck(Enum): + Never = 'Never' + Optional = 'Optional' + Required = 'Required' -def sort_mirrorlist(raw_data :bytes, sort_order: List[str] = ['https', 'http']) -> bytes: - """ - This function can sort /etc/pacman.d/mirrorlist according to the - mirror's URL prefix. By default places HTTPS before HTTP but it also - preserves the country/rank-order. - This assumes /etc/pacman.d/mirrorlist looks like the following: +class SignOption(Enum): + TrustedOnly = 'TrustedOnly' + TrustAll = 'TrustAll' - ## Comment - Server = url - or +@dataclass +class CustomMirror: + name: str + url: str + sign_check: SignCheck + sign_option: SignOption + + def as_json(self) -> Dict[str, str]: + return { + 'Name': self.name, + 'Url': self.url, + 'Sign check': self.sign_check.value, + 'Sign options': self.sign_option.value + } + + def json(self) -> Dict[str, str]: + return { + 'name': self.name, + 'url': self.url, + 'sign_check': self.sign_check.value, + 'sign_option': self.sign_option.value + } + + @classmethod + def parse_args(cls, args: List[Dict[str, str]]) -> List['CustomMirror']: + configs = [] + for arg in args: + configs.append( + CustomMirror( + arg['name'], + arg['url'], + SignCheck(arg['sign_check']), + SignOption(arg['sign_option']) + ) + ) + + return configs - ## Comment - #Server = url - But the Comments need to start with double-hashmarks to be distringuished - from server url definitions (commented or uncommented). - """ - comments_and_whitespaces = b"" - sort_order += ['Unknown'] - categories: Dict[str, List] = {key: [] for key in sort_order} - - for line in raw_data.split(b"\n"): - if line[0:2] in (b'##', b''): - comments_and_whitespaces += line + b'\n' - elif line[:6].lower() == b'server' or line[:7].lower() == b'#server': - opening, url = line.split(b'=', 1) - opening, url = opening.strip(), url.strip() - if (category := url.split(b'://',1)[0].decode('UTF-8')) in categories: - categories[category].append(comments_and_whitespaces) - categories[category].append(opening + b' = ' + url + b'\n') - else: - categories["Unknown"].append(comments_and_whitespaces) - categories["Unknown"].append(opening + b' = ' + url + b'\n') - - comments_and_whitespaces = b"" - - new_raw_data = b'' - for category in sort_order + ["Unknown"]: - for line in categories[category]: - new_raw_data += line - - return new_raw_data - - -def filter_mirrors_by_region(regions :str, - destination :str = '/etc/pacman.d/mirrorlist', - sort_order :List[str] = ["https", "http"], - *args :str, - **kwargs :str -) -> Union[bool, bytes]: +@dataclass +class MirrorConfiguration: + mirror_regions: Dict[str, List[str]] = field(default_factory=dict) + custom_mirrors: List[CustomMirror] = field(default_factory=list) + + @property + def regions(self) -> str: + return ', '.join(self.mirror_regions.keys()) + + def json(self) -> Dict[str, Any]: + return { + 'mirror_regions': self.mirror_regions, + 'custom_mirrors': [c.json() for c in self.custom_mirrors] + } + + @classmethod + def parse_args(cls, args: Dict[str, Any]) -> 'MirrorConfiguration': + config = MirrorConfiguration() + + if 'mirror_regions' in args: + config.mirror_regions = args['mirror_regions'] + + if 'custom_mirrors' in args: + config.custom_mirrors = CustomMirror.parse_args(args['custom_mirrors']) + + return config + + +class CustomMirrorList(ListManager): + def __init__(self, prompt: str, custom_mirrors: List[CustomMirror]): + self._actions = [ + str(_('Add a custom mirror')), + str(_('Change custom mirror')), + str(_('Delete custom mirror')) + ] + super().__init__(prompt, custom_mirrors, [self._actions[0]], self._actions[1:]) + + def reformat(self, data: List[CustomMirror]) -> Dict[str, Any]: + table = FormattedOutput.as_table(data) + rows = table.split('\n') + + # these are the header rows of the table and do not map to any User obviously + # we're adding 2 spaces as prefix because the menu selector '> ' will be put before + # the selectable rows so the header has to be aligned + display_data: Dict[str, Optional[CustomMirror]] = {f' {rows[0]}': None, f' {rows[1]}': None} + + for row, user in zip(rows[2:], data): + row = row.replace('|', '\\|') + display_data[row] = user + + return display_data + + def selected_action_display(self, mirror: CustomMirror) -> str: + return mirror.name + + def handle_action( + self, + action: str, + entry: Optional[CustomMirror], + data: List[CustomMirror] + ) -> List[CustomMirror]: + if action == self._actions[0]: # add + new_mirror = self._add_custom_mirror() + if new_mirror is not None: + data = [d for d in data if d.name != new_mirror.name] + data += [new_mirror] + elif action == self._actions[1] and entry: # modify mirror + new_mirror = self._add_custom_mirror(entry) + if new_mirror is not None: + data = [d for d in data if d.name != entry.name] + data += [new_mirror] + elif action == self._actions[2] and entry: # delete + data = [d for d in data if d != entry] + + return data + + def _add_custom_mirror(self, mirror: Optional[CustomMirror] = None) -> Optional[CustomMirror]: + prompt = '\n\n' + str(_('Enter name (leave blank to skip): ')) + existing_name = mirror.name if mirror else '' + + while True: + name = TextInput(prompt, existing_name).run() + if not name: + return mirror + break + + prompt = '\n' + str(_('Enter url (leave blank to skip): ')) + existing_url = mirror.url if mirror else '' + + while True: + url = TextInput(prompt, existing_url).run() + if not url: + return mirror + break + + sign_check_choice = Menu( + str(_('Select signature check option')), + [s.value for s in SignCheck], + skip=False, + clear_screen=False, + preset_values=mirror.sign_check.value if mirror else None + ).run() + + sign_option_choice = Menu( + str(_('Select signature option')), + [s.value for s in SignOption], + skip=False, + clear_screen=False, + preset_values=mirror.sign_option.value if mirror else None + ).run() + + return CustomMirror( + name, + url, + SignCheck(sign_check_choice.single_value), + SignOption(sign_option_choice.single_value) + ) + + +class MirrorMenu(AbstractSubMenu): + def __init__( + self, + data_store: Dict[str, Any], + preset: Optional[MirrorConfiguration] = None + ): + if preset: + self._preset = preset + else: + self._preset = MirrorConfiguration() + + super().__init__(data_store=data_store) + + def setup_selection_menu_options(self): + self._menu_options['mirror_regions'] = \ + Selector( + _('Mirror region'), + lambda preset: select_mirror_regions(preset), + display_func=lambda x: ', '.join(x.keys()) if x else '', + default=self._preset.mirror_regions, + enabled=True) + self._menu_options['custom_mirrors'] = \ + Selector( + _('Custom mirrors'), + lambda preset: select_custom_mirror(preset=preset), + display_func=lambda x: str(_('Defined')) if x else '', + preview_func=self._prev_custom_mirror, + default=self._preset.custom_mirrors, + enabled=True + ) + + def _prev_custom_mirror(self) -> Optional[str]: + selector = self._menu_options['custom_mirrors'] + + if selector.has_selection(): + custom_mirrors: List[CustomMirror] = selector.current_selection # type: ignore + output = FormattedOutput.as_table(custom_mirrors) + return output.strip() + + return None + + def run(self, allow_reset: bool = True) -> Optional[MirrorConfiguration]: + super().run(allow_reset=allow_reset) + + if self._data_store.get('mirror_regions', None) or self._data_store.get('custom_mirrors', None): + return MirrorConfiguration( + mirror_regions=self._data_store['mirror_regions'], + custom_mirrors=self._data_store['custom_mirrors'], + ) + + return None + + +def select_mirror_regions(preset_values: Dict[str, List[str]] = {}) -> Dict[str, List[str]]: """ - This function will change the active mirrors on the live medium by - filtering which regions are active based on `regions`. + Asks the user to select a mirror or region + Usually this is combined with :ref:`archinstall.list_mirrors`. - :param regions: A series of country codes separated by `,`. For instance `SE,US` for sweden and United States. - :type regions: str + :return: The dictionary information about a mirror/region. + :rtype: dict """ - region_list = [f'country={region}' for region in regions.split(',')] - response = urllib.request.urlopen(urllib.request.Request(f"https://archlinux.org/mirrorlist/?{'&'.join(region_list)}&protocol=https&protocol=http&ip_version=4&ip_version=6&use_mirror_status=on'", headers={'User-Agent': 'ArchInstall'})) - new_list = response.read().replace(b"#Server", b"Server") + if preset_values is None: + preselected = None + else: + preselected = list(preset_values.keys()) - if sort_order: - new_list = sort_mirrorlist(new_list, sort_order=sort_order) + mirrors = list_mirrors() - if destination: - with open(destination, "wb") as mirrorlist: - mirrorlist.write(new_list) + choice = Menu( + _('Select one of the regions to download packages from'), + list(mirrors.keys()), + preset_values=preselected, + multi=True, + allow_reset=True + ).run() - return True - else: - return new_list.decode('UTF-8') + match choice.type_: + case MenuSelectionType.Reset: + return {} + case MenuSelectionType.Skip: + return preset_values + case MenuSelectionType.Selection: + return {selected: mirrors[selected] for selected in choice.multi_value} + + return {} -def add_custom_mirrors(mirrors: List[CustomMirror]) -> bool: +def select_custom_mirror(prompt: str = '', preset: List[CustomMirror] = []): + custom_mirrors = CustomMirrorList(prompt, preset).run() + return custom_mirrors + + +def add_custom_mirrors(mirrors: List[CustomMirror]): """ This will append custom mirror definitions in pacman.conf @@ -102,99 +282,57 @@ def add_custom_mirrors(mirrors: List[CustomMirror]) -> bool: """ with open('/etc/pacman.conf', 'a') as pacman: for mirror in mirrors: - pacman.write(f"[{mirror.name}]\n") - pacman.write(f"SigLevel = {mirror.signcheck} {mirror.signoptions}\n") + pacman.write(f"\n\n[{mirror.name}]\n") + pacman.write(f"SigLevel = {mirror.sign_check.value} {mirror.sign_option.value}\n") pacman.write(f"Server = {mirror.url}\n") - return True - - -def insert_mirrors(mirrors :Dict[str, Any], *args :str, **kwargs :str) -> bool: - """ - This function will insert a given mirror-list at the top of `/etc/pacman.d/mirrorlist`. - It will not flush any other mirrors, just insert new ones. - - :param mirrors: A dictionary of `{'url' : 'country', 'url2' : 'country'}` - :type mirrors: dict - """ - original_mirrorlist = '' - with open('/etc/pacman.d/mirrorlist', 'r') as original: - original_mirrorlist = original.read() - - with open('/etc/pacman.d/mirrorlist', 'w') as new_mirrorlist: - for mirror, country in mirrors.items(): - new_mirrorlist.write(f'## {country}\n') - new_mirrorlist.write(f'Server = {mirror}\n') - new_mirrorlist.write('\n') - new_mirrorlist.write(original_mirrorlist) - - return True - def use_mirrors( - regions: Dict[str, Iterable[str]], + regions: Dict[str, List[str]], destination: str = '/etc/pacman.d/mirrorlist' ): - info(f'A new package mirror-list has been created: {destination}') - with open(destination, 'w') as mirrorlist: + with open(destination, 'w') as fp: for region, mirrors in regions.items(): for mirror in mirrors: - mirrorlist.write(f'## {region}\n') - mirrorlist.write(f'Server = {mirror}\n') + fp.write(f'## {region}\n') + fp.write(f'Server = {mirror}\n') + + info(f'A new package mirror-list has been created: {destination}') + +def _parse_mirror_list(mirrorlist: str) -> Dict[str, List[str]]: + file_content = mirrorlist.split('\n') + file_content = list(filter(lambda x: x, file_content)) # filter out empty lines + first_srv_idx = [idx for idx, line in enumerate(file_content) if 'server' in line.lower()][0] + mirrors = file_content[first_srv_idx - 1:] -def re_rank_mirrors( - top: int = 10, - src: str = '/etc/pacman.d/mirrorlist', - dst: str = '/etc/pacman.d/mirrorlist', -) -> bool: - try: - cmd = SysCommand(f"/usr/bin/rankmirrors -n {top} {src}") - except SysCallError: - return False - with open(dst, 'w') as f: - f.write(str(cmd)) - return True + mirror_list: Dict[str, List[str]] = {} + for idx in range(0, len(mirrors), 2): + region = mirrors[idx].removeprefix('## ') + url = mirrors[idx + 1].removeprefix('#').removeprefix('Server = ') + mirror_list.setdefault(region, []).append(url) -def list_mirrors(sort_order :List[str] = ["https", "http"]) -> Dict[str, Any]: - regions: Dict[str, Dict[str, Any]] = {} + return mirror_list + + +def list_mirrors() -> Dict[str, List[str]]: + regions: Dict[str, List[str]] = {} if storage['arguments']['offline']: - with pathlib.Path('/etc/pacman.d/mirrorlist').open('rb') as fh: - mirrorlist = fh.read() + with pathlib.Path('/etc/pacman.d/mirrorlist').open('r') as fp: + mirrorlist = fp.read() else: url = "https://archlinux.org/mirrorlist/?protocol=https&protocol=http&ip_version=4&ip_version=6&use_mirror_status=on" - try: - response = urllib.request.urlopen(url) - except urllib.error.URLError as err: + mirrorlist = fetch_data_from_url(url) + except ValueError as err: warn(f'Could not fetch an active mirror-list: {err}') return regions - mirrorlist = response.read() - - if sort_order: - mirrorlist = sort_mirrorlist(mirrorlist, sort_order=sort_order) - - region = 'Unknown region' - for line in mirrorlist.split(b'\n'): - if len(line.strip()) == 0: - continue - - clean_line = line.decode('UTF-8').strip('\n').strip('\r') - - if clean_line[:3] == '## ': - region = clean_line[3:] - elif clean_line[:10] == '#Server = ': - regions.setdefault(region, {}) - - url = clean_line.lstrip('#Server = ') - regions[region][url] = True - elif clean_line.startswith('Server = '): - regions.setdefault(region, {}) - - url = clean_line.lstrip('Server = ') - regions[region][url] = True + regions = _parse_mirror_list(mirrorlist) + sorted_regions = {} + for region, urls in regions.items(): + sorted_regions[region] = sorted(urls, reverse=True) - return regions + return sorted_regions diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 1e19c9a3..1aecc1cd 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -11,7 +11,7 @@ from archinstall.default_profiles.applications.pipewire import PipewireProfile from archinstall.lib.configuration import ConfigurationOutput from archinstall.lib.installer import Installer from archinstall.lib.menu import Menu -from archinstall.lib.mirrors import use_mirrors +from archinstall.lib.mirrors import use_mirrors, add_custom_mirrors from archinstall.lib.models.bootloader import Bootloader from archinstall.lib.models.network_configuration import NetworkConfigurationHandler from archinstall.lib.networking import check_mirror_reachable @@ -45,7 +45,7 @@ def ask_user_questions(): global_menu.enable('keyboard-layout') # Set which region to download packages from during the installation - global_menu.enable('mirror-region') + global_menu.enable('mirror_config') global_menu.enable('sys-language') @@ -137,8 +137,11 @@ def perform_installation(mountpoint: Path): installation.generate_key_files() # Set mirrors used by pacstrap (outside of installation) - if archinstall.arguments.get('mirror-region', None): - use_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors for the live medium + if mirror_config := archinstall.arguments.get('mirror_config', None): + if mirror_config.mirror_regions: + use_mirrors(mirror_config.mirror_regions) + if mirror_config.custom_mirrors: + add_custom_mirrors(mirror_config.custom_mirrors) installation.minimal_installation( testing=enable_testing, @@ -147,9 +150,8 @@ def perform_installation(mountpoint: Path): locales=[locale] ) - if archinstall.arguments.get('mirror-region') is not None: - if archinstall.arguments.get("mirrors", None) is not None: - installation.set_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors in the installation medium + if mirror_config := archinstall.arguments.get('mirror_config', None): + installation.set_mirrors(mirror_config) # Set the mirrors in the installation medium if archinstall.arguments.get('swap'): installation.setup_swap('zram') diff --git a/archinstall/scripts/swiss.py b/archinstall/scripts/swiss.py index a49f568d..1998f073 100644 --- a/archinstall/scripts/swiss.py +++ b/archinstall/scripts/swiss.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Any, Dict import archinstall from archinstall import SysInfo, info, debug -from archinstall.lib.mirrors import use_mirrors +from archinstall.lib import mirrors from archinstall.lib import models from archinstall.lib import disk from archinstall.lib.networking import check_mirror_reachable @@ -92,7 +92,7 @@ class SwissMainMenu(GlobalMenu): match self._execution_mode: case ExecutionMode.Full | ExecutionMode.Lineal: options_list = [ - 'keyboard-layout', 'mirror-region', 'disk_config', + 'keyboard-layout', 'mirror_config', 'disk_config', 'disk_encryption', 'swap', 'bootloader', 'hostname', '!root-password', '!users', 'profile_config', 'audio', 'kernels', 'packages', 'additional-repositories', 'nic', 'timezone', 'ntp' @@ -107,7 +107,7 @@ class SwissMainMenu(GlobalMenu): mandatory_list = ['disk_config'] case ExecutionMode.Only_OS: options_list = [ - 'keyboard-layout', 'mirror-region','bootloader', 'hostname', + 'keyboard-layout', 'mirror_config','bootloader', 'hostname', '!root-password', '!users', 'profile_config', 'audio', 'kernels', 'packages', 'additional-repositories', 'nic', 'timezone', 'ntp' ] @@ -196,8 +196,11 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): installation.generate_key_files() # Set mirrors used by pacstrap (outside of installation) - if archinstall.arguments.get('mirror-region', None): - use_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors for the live medium + if mirror_config := archinstall.arguments.get('mirror_config', None): + if mirror_config.mirror_regions: + mirrors.use_mirrors(mirror_config.mirror_regions) + if mirror_config.custom_mirrors: + mirrors.add_custom_mirrors(mirror_config.custom_mirrors) installation.minimal_installation( testing=enable_testing, @@ -206,10 +209,8 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): locales=[locale] ) - if archinstall.arguments.get('mirror-region') is not None: - if archinstall.arguments.get("mirrors", None) is not None: - installation.set_mirrors( - archinstall.arguments['mirror-region']) # Set the mirrors in the installation medium + if mirror_config := archinstall.arguments.get('mirror_config', None): + installation.set_mirrors(mirror_config) # Set the mirrors in the installation medium if archinstall.arguments.get('swap'): installation.setup_swap('zram') diff --git a/examples/interactive_installation.py b/examples/interactive_installation.py index 487db4dd..7c4ffed7 100644 --- a/examples/interactive_installation.py +++ b/examples/interactive_installation.py @@ -24,7 +24,7 @@ def ask_user_questions(): global_menu.enable('keyboard-layout') # Set which region to download packages from during the installation - global_menu.enable('mirror-region') + global_menu.enable('mirror_config') global_menu.enable('sys-language') @@ -116,8 +116,11 @@ def perform_installation(mountpoint: Path): installation.generate_key_files() # Set mirrors used by pacstrap (outside of installation) - if archinstall.arguments.get('mirror-region', None): - mirrors.use_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors for the live medium + if mirror_config := archinstall.arguments.get('mirror_config', None): + if mirror_config.mirror_regions: + mirrors.use_mirrors(mirror_config.mirror_regions) + if mirror_config.custom_mirrors: + mirrors.add_custom_mirrors(mirror_config.custom_mirrors) installation.minimal_installation( testing=enable_testing, @@ -126,9 +129,8 @@ def perform_installation(mountpoint: Path): locales=[locale] ) - if archinstall.arguments.get('mirror-region') is not None: - if archinstall.arguments.get("mirrors", None) is not None: - installation.set_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors in the installation medium + if mirror_config := archinstall.arguments.get('mirror_config', None): + installation.set_mirrors(mirror_config) # Set the mirrors in the installation medium if archinstall.arguments.get('swap'): installation.setup_swap('zram') -- cgit v1.2.3-70-g09d2 From b1f26d94e63a2af77803c364e137faad14b1a67e Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Mon, 15 May 2023 15:50:11 +0200 Subject: Allowing for fg='color' in info, warn, error and debug. (#1818) * Allowing for fg='color' in info, warn, error and debug. * Converted to a more static definition, mypy does not like magic. * Flake8 fixes --- archinstall/lib/output.py | 46 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py index bd31b5b3..0395e2e7 100644 --- a/archinstall/lib/output.py +++ b/archinstall/lib/output.py @@ -238,20 +238,46 @@ def _stylize_output( return f'\033[{ansi}m{text}\033[0m' -def info(*msgs: str): - log(*msgs, level=logging.INFO) - - -def debug(*msgs: str): - log(*msgs, level=logging.DEBUG) +def info( + *msgs: str, + level: int = logging.INFO, + fg: str = 'white', + bg: Optional[str] = None, + reset: bool = False, + font: List[Font] = [] +): + log(*msgs, level=level, fg=fg, bg=bg, reset=reset, font=font) +def debug( + *msgs: str, + level: int = logging.DEBUG, + fg: str = 'white', + bg: Optional[str] = None, + reset: bool = False, + font: List[Font] = [] +): + log(*msgs, level=level, fg=fg, bg=bg, reset=reset, font=font) -def error(*msgs: str): - log(*msgs, level=logging.ERROR, fg='red') +def error( + *msgs: str, + level: int = logging.ERROR, + fg: str = 'red', + bg: Optional[str] = None, + reset: bool = False, + font: List[Font] = [] +): + log(*msgs, level=level, fg=fg, bg=bg, reset=reset, font=font) -def warn(*msgs: str): - log(*msgs, level=logging.WARNING, fg='yellow') +def warn( + *msgs: str, + level: int = logging.WARN, + fg: str = 'yellow', + bg: Optional[str] = None, + reset: bool = False, + font: List[Font] = [] +): + log(*msgs, level=level, fg=fg, bg=bg, reset=reset, font=font) def log( -- cgit v1.2.3-70-g09d2 From 3267ee215ae7d4fcd0cc34a29c008c37c08e17f4 Mon Sep 17 00:00:00 2001 From: bd-g <49082060+bd-g@users.noreply.github.com> Date: Mon, 15 May 2023 03:53:06 -1000 Subject: Improve Save configuration UI - (re)allow for directly specifying custom path (#1728) * align with newly merged master * retry save config * fix mypy error with str type - this menu with allows return a string * Removed unused import --------- Co-authored-by: Anton Hvornum --- archinstall/lib/configuration.py | 153 ++++++++++++++++++--------------------- 1 file changed, 69 insertions(+), 84 deletions(-) diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py index c3af3a83..8c7b11fa 100644 --- a/archinstall/lib/configuration.py +++ b/archinstall/lib/configuration.py @@ -1,12 +1,13 @@ import os import json import stat +import readline from pathlib import Path from typing import Optional, Dict, Any, TYPE_CHECKING from .menu import Menu, MenuSelectionType from .storage import storage -from .general import JSON, UNSAFE_JSON, SysCommand +from .general import JSON, UNSAFE_JSON from .output import debug, info, warn if TYPE_CHECKING: @@ -115,97 +116,81 @@ class ConfigurationOutput: def save_config(config: Dict): def preview(selection: str): - if options['user_config'] == selection: + if options["user_config"] == selection: serialized = config_output.user_config_to_json() - return f'{config_output.user_configuration_file}\n{serialized}' - elif options['user_creds'] == selection: + return f"{config_output.user_configuration_file}\n{serialized}" + elif options["user_creds"] == selection: if maybe_serial := config_output.user_credentials_to_json(): - return f'{config_output.user_credentials_file}\n{maybe_serial}' + return f"{config_output.user_credentials_file}\n{maybe_serial}" else: - return str(_('No configuration')) - elif options['all'] == selection: - output = f'{config_output.user_configuration_file}\n' + return str(_("No configuration")) + elif options["all"] == selection: + output = f"{config_output.user_configuration_file}\n" if config_output.user_credentials_to_json(): - output += f'{config_output.user_credentials_file}\n' + output += f"{config_output.user_credentials_file}\n" return output[:-1] return None - config_output = ConfigurationOutput(config) - - options = { - 'user_config': str(_('Save user configuration')), - 'user_creds': str(_('Save user credentials')), - 'disk_layout': str(_('Save disk layout')), - 'all': str(_('Save all')) - } - - choice = Menu( - _('Choose which configuration to save'), - list(options.values()), - sort=False, - skip=True, - preview_size=0.75, - preview_command=preview - ).run() - - if choice.type_ == MenuSelectionType.Skip: - return - - save_config_value = choice.single_value - saving_key = [k for k, v in options.items() if v == save_config_value][0] - - dirs_to_exclude = [ - '/bin', - '/dev', - '/lib', - '/lib64', - '/lost+found', - '/opt', - '/proc', - '/run', - '/sbin', - '/srv', - '/sys', - '/usr', - '/var', - ] - - debug('Ignore configuration option folders: ' + ','.join(dirs_to_exclude)) - info(_('Finding possible directories to save configuration files ...')) - - find_exclude = '-path ' + ' -prune -o -path '.join(dirs_to_exclude) + ' -prune ' - file_picker_command = f'find / {find_exclude} -o -type d -print0' - - directories = SysCommand(file_picker_command).decode() - - if directories is None: - raise ValueError('Failed to retrieve possible configuration directories') - - possible_save_dirs = list(filter(None, directories.split('\x00'))) - - selection = Menu( - _('Select directory (or directories) for saving configuration files'), - possible_save_dirs, - multi=True, - skip=True, - allow_reset=False, - ).run() - - match selection.type_: - case MenuSelectionType.Skip: + try: + config_output = ConfigurationOutput(config) + + options = { + "user_config": str(_("Save user configuration (including disk layout)")), + "user_creds": str(_("Save user credentials")), + "all": str(_("Save all")), + } + + save_choice = Menu( + _("Choose which configuration to save"), + list(options.values()), + sort=False, + skip=True, + preview_size=0.75, + preview_command=preview, + ).run() + + if save_choice.type_ == MenuSelectionType.Skip: return - save_dirs = selection.multi_value + readline.set_completer_delims("\t\n=") + readline.parse_and_bind("tab: complete") + while True: + path = input( + _( + "Enter a directory for the configuration(s) to be saved (tab completion enabled)\nSave directory: " + ) + ).strip(" ") + dest_path = Path(path) + if dest_path.exists() and dest_path.is_dir(): + break + info(_("Not a valid directory: {}").format(dest_path), fg="red") + + if not path: + return - debug(f'Saving {saving_key} configuration files to {save_dirs}') + prompt = _( + "Do you want to save {} configuration file(s) in the following location?\n\n{}" + ).format( + list(options.keys())[list(options.values()).index(str(save_choice.value))], + dest_path.absolute(), + ) + save_confirmation = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run() + if save_confirmation == Menu.no(): + return - if save_dirs is not None: - for save_dir_str in save_dirs: - save_dir = Path(save_dir_str) - if options['user_config'] == save_config_value: - config_output.save_user_config(save_dir) - elif options['user_creds'] == save_config_value: - config_output.save_user_creds(save_dir) - elif options['all'] == save_config_value: - config_output.save_user_config(save_dir) - config_output.save_user_creds(save_dir) + debug( + _("Saving {} configuration files to {}").format( + list(options.keys())[list(options.values()).index(str(save_choice.value))], + dest_path.absolute(), + ) + ) + + if options["user_config"] == save_choice.value: + config_output.save_user_config(dest_path) + elif options["user_creds"] == save_choice.value: + config_output.save_user_creds(dest_path) + elif options["all"] == save_choice.value: + config_output.save_user_config(dest_path) + config_output.save_user_creds(dest_path) + except KeyboardInterrupt: + return -- cgit v1.2.3-70-g09d2 From 5276d95339368210e75791e2b88c1bf5aca4517b Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Mon, 15 May 2023 09:53:42 -0400 Subject: Remove partial upgrade from `build_iso.sh` (#1809) --- build_iso.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/build_iso.sh b/build_iso.sh index 26adf95e..80bd8bb0 100755 --- a/build_iso.sh +++ b/build_iso.sh @@ -30,7 +30,6 @@ cat <<- _EOF_ | tee /tmp/archlive/airootfs/root/.zprofile echo "Type archinstall to launch the installer." _EOF_ -pacman -Sy pacman --noconfirm -S git archiso cp -r /usr/share/archiso/configs/releng/* /tmp/archlive -- cgit v1.2.3-70-g09d2 From 06eadb31d4f0bca0c8cb95b6a9eb62ddd2d7cff2 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Mon, 5 Jun 2023 18:02:49 +1000 Subject: Move locales and cleanup menu (#1814) * Cleanup imports and unused code * Cleanup imports and unused code * Update build check * Keep deprecation exception * Simplify logging * Move locale into new sub-menu --------- Co-authored-by: Daniel Girtler --- archinstall/__init__.py | 3 +- archinstall/default_profiles/profile.py | 19 +--- archinstall/lib/global_menu.py | 95 +++++++++++----- archinstall/lib/hardware.py | 2 +- archinstall/lib/installer.py | 38 +++---- archinstall/lib/interactions/__init__.py | 3 +- archinstall/lib/interactions/general_conf.py | 12 +-- archinstall/lib/interactions/locale_conf.py | 43 -------- archinstall/lib/interactions/system_conf.py | 6 +- archinstall/lib/locale/__init__.py | 6 ++ archinstall/lib/locale/locale.py | 68 ++++++++++++ archinstall/lib/locale/locale_menu.py | 155 +++++++++++++++++++++++++++ archinstall/lib/menu/abstract_menu.py | 38 +------ archinstall/lib/utils/util.py | 23 +++- archinstall/scripts/guided.py | 17 ++- archinstall/scripts/swiss.py | 16 +-- examples/interactive_installation.py | 17 ++- 17 files changed, 374 insertions(+), 187 deletions(-) delete mode 100644 archinstall/lib/interactions/locale_conf.py create mode 100644 archinstall/lib/locale/__init__.py create mode 100644 archinstall/lib/locale/locale.py create mode 100644 archinstall/lib/locale/locale_menu.py diff --git a/archinstall/__init__.py b/archinstall/__init__.py index e6fcb267..ce58e255 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -213,8 +213,7 @@ def load_config(): """ from .lib.models import NetworkConfiguration - arguments.setdefault('sys-language', 'en_US') - arguments.setdefault('sys-encoding', 'utf-8') + arguments['locale_config'] = locale.LocaleConfiguration.parse_arg(arguments) if (archinstall_lang := arguments.get('archinstall-language', None)) is not None: arguments['archinstall-language'] = TranslationHandler().get_language_by_name(archinstall_lang) diff --git a/archinstall/default_profiles/profile.py b/archinstall/default_profiles/profile.py index b1ad1f50..ce07c286 100644 --- a/archinstall/default_profiles/profile.py +++ b/archinstall/default_profiles/profile.py @@ -3,7 +3,7 @@ from __future__ import annotations from enum import Enum, auto from typing import List, Optional, Any, Dict, TYPE_CHECKING, TypeVar -from archinstall.lib.output import FormattedOutput +from archinstall.lib.utils.util import format_cols if TYPE_CHECKING: from archinstall.lib.installer import Installer @@ -185,17 +185,6 @@ class Profile: return None def packages_text(self) -> str: - text = str(_('Installed packages')) + ':\n' - - nr_packages = len(self.packages) - if nr_packages <= 5: - col = 1 - elif nr_packages <= 10: - col = 2 - elif nr_packages <= 15: - col = 3 - else: - col = 4 - - text += FormattedOutput.as_columns(self.packages, col) - return text + header = str(_('Installed packages')) + output = format_cols(self.packages, header) + return output diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index fc58a653..91ebc6a0 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -4,6 +4,7 @@ from typing import Any, List, Optional, Union, Dict, TYPE_CHECKING from . import disk from .general import secret +from .locale.locale_menu import LocaleConfiguration, LocaleMenu from .menu import Selector, AbstractMenu from .mirrors import MirrorConfiguration, MirrorMenu from .models import NetworkConfiguration @@ -24,9 +25,7 @@ from .interactions import ask_to_configure_network from .interactions import get_password, ask_for_a_timezone from .interactions import select_additional_repositories from .interactions import select_kernel -from .interactions import select_language -from .interactions import select_locale_enc -from .interactions import select_locale_lang +from .utils.util import format_cols from .interactions import ask_ntp from .interactions.disk_conf import select_disk_config @@ -36,6 +35,7 @@ if TYPE_CHECKING: class GlobalMenu(AbstractMenu): def __init__(self, data_store: Dict[str, Any]): + self._defined_text = str(_('Defined')) super().__init__(data_store=data_store, auto_cursor=True, preview_size=0.3) def setup_selection_menu_options(self): @@ -46,28 +46,19 @@ class GlobalMenu(AbstractMenu): lambda x: self._select_archinstall_language(x), display_func=lambda x: x.display_name, default=self.translation_handler.get_language_by_abbr('en')) - self._menu_options['keyboard-layout'] = \ + self._menu_options['locale_config'] = \ Selector( - _('Keyboard layout'), - lambda preset: select_language(preset), - default='us') + _('Locales'), + lambda preset: self._locale_selection(preset), + preview_func=self._prev_locale, + display_func=lambda x: self._defined_text if x else '') self._menu_options['mirror_config'] = \ Selector( _('Mirrors'), lambda preset: self._mirror_configuration(preset), - display_func=lambda x: str(_('Defined')) if x else '', + display_func=lambda x: self._defined_text if x else '', preview_func=self._prev_mirror_config ) - self._menu_options['sys-language'] = \ - Selector( - _('Locale language'), - lambda preset: select_locale_lang(preset), - default='en_US') - self._menu_options['sys-encoding'] = \ - Selector( - _('Locale encoding'), - lambda preset: select_locale_enc(preset), - default='UTF-8') self._menu_options['disk_config'] = \ Selector( _('Disk configuration'), @@ -103,32 +94,32 @@ class GlobalMenu(AbstractMenu): Selector( _('Root password'), lambda preset:self._set_root_password(), - display_func=lambda x: secret(x) if x else 'None') + display_func=lambda x: secret(x) if x else '') self._menu_options['!users'] = \ Selector( _('User account'), lambda x: self._create_user_account(x), default=[], - display_func=lambda x: f'{len(x)} {_("User(s)")}' if len(x) > 0 else None, + display_func=lambda x: f'{len(x)} {_("User(s)")}' if len(x) > 0 else '', preview_func=self._prev_users) self._menu_options['profile_config'] = \ Selector( _('Profile'), lambda preset: self._select_profile(preset), - display_func=lambda x: x.profile.name if x else 'None', + display_func=lambda x: x.profile.name if x else '', preview_func=self._prev_profile ) self._menu_options['audio'] = \ Selector( _('Audio'), lambda preset: self._select_audio(preset), - display_func=lambda x: x if x else 'None', + display_func=lambda x: x if x else '', default=None ) self._menu_options['parallel downloads'] = \ Selector( _('Parallel Downloads'), - add_number_of_parrallel_downloads, + lambda preset: add_number_of_parrallel_downloads(preset), display_func=lambda x: x if x else '0', default=0 ) @@ -141,19 +132,20 @@ class GlobalMenu(AbstractMenu): self._menu_options['packages'] = \ Selector( _('Additional packages'), - # lambda x: ask_additional_packages_to_install(storage['arguments'].get('packages', None)), - ask_additional_packages_to_install, + lambda preset: ask_additional_packages_to_install(preset), + display_func=lambda x: self._defined_text if x else '', + preview_func=self._prev_additional_pkgs, default=[]) self._menu_options['additional-repositories'] = \ Selector( _('Optional repositories'), - select_additional_repositories, + lambda preset: select_additional_repositories(preset), display_func=lambda x: ', '.join(x) if x else None, default=[]) self._menu_options['nic'] = \ Selector( _('Network configuration'), - ask_to_configure_network, + lambda preset: ask_to_configure_network(preset), display_func=lambda x: self._display_network_conf(x), preview_func=self._prev_network_config, default={}) @@ -177,12 +169,37 @@ class GlobalMenu(AbstractMenu): self._menu_options['install'] = \ Selector( self._install_text(), - exec_func=lambda n,v: True if len(self._missing_configs()) == 0 else False, + exec_func=lambda n, v: True if len(self._missing_configs()) == 0 else False, preview_func=self._prev_install_missing_config, no_store=True) self._menu_options['abort'] = Selector(_('Abort'), exec_func=lambda n,v:exit(1)) + def _missing_configs(self) -> List[str]: + def check(s): + return self._menu_options.get(s).has_selection() + + def has_superuser() -> bool: + sel = self._menu_options['!users'] + if sel.current_selection: + return any([u.sudo for u in sel.current_selection]) + return False + + mandatory_fields = dict(filter(lambda x: x[1].is_mandatory(), self._menu_options.items())) + missing = set() + + for key, selector in mandatory_fields.items(): + if key in ['!root-password', '!users']: + if not check('!root-password') and not has_superuser(): + missing.add( + str(_('Either root-password or at least 1 user with sudo privileges must be specified')) + ) + elif key == 'disk_config': + if not check('disk_config'): + missing.add(self._menu_options['disk_config'].description) + + return list(missing) + def _update_install_text(self, name: str, value: str): text = self._install_text() self._menu_options['install'].update_description(text) @@ -216,6 +233,21 @@ class GlobalMenu(AbstractMenu): disk_encryption = disk.DiskEncryptionMenu(mods, data_store, preset=preset).run() return disk_encryption + def _locale_selection(self, preset: LocaleConfiguration) -> LocaleConfiguration: + data_store: Dict[str, Any] = {} + locale_config = LocaleMenu(data_store, preset).run() + return locale_config + + def _prev_locale(self) -> Optional[str]: + selector = self._menu_options['locale_config'] + if selector.has_selection(): + config: LocaleConfiguration = selector.current_selection # type: ignore + output = '{}: {}\n'.format(str(_('Keyboard layout')), config.kb_layout) + output += '{}: {}\n'.format(str(_('Locale language')), config.sys_lang) + output += '{}: {}'.format(str(_('Locale encoding')), config.sys_enc) + return output + return None + def _prev_network_config(self) -> Optional[str]: selector = self._menu_options['nic'] if selector.has_selection(): @@ -224,6 +256,13 @@ class GlobalMenu(AbstractMenu): return FormattedOutput.as_table(ifaces) return None + def _prev_additional_pkgs(self): + selector = self._menu_options['packages'] + if selector.has_selection(): + packages: List[str] = selector.current_selection + return format_cols(packages, None) + return None + def _prev_disk_layouts(self) -> Optional[str]: selector = self._menu_options['disk_config'] disk_layout_conf: Optional[disk.DiskLayoutConfiguration] = selector.current_selection diff --git a/archinstall/lib/hardware.py b/archinstall/lib/hardware.py index 220d3d37..2b65e07c 100644 --- a/archinstall/lib/hardware.py +++ b/archinstall/lib/hardware.py @@ -3,9 +3,9 @@ from functools import cached_property from pathlib import Path from typing import Optional, Dict, List +from .exceptions import SysCallError from .general import SysCommand from .networking import list_interfaces, enrich_iface_types -from .exceptions import SysCallError from .output import debug AVAILABLE_GFX_DRIVERS = { diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 30442774..6eac85fc 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -12,6 +12,7 @@ from . import disk from .exceptions import DiskError, ServiceException, RequirementError, HardwareIncompatibilityError, SysCallError from .general import SysCommand from .hardware import SysInfo +from .locale import LocaleConfiguration from .locale import verify_keyboard_layout, verify_x11_keyboard_layout from .luks import Luks2 from .mirrors import use_mirrors, MirrorConfiguration, add_custom_mirrors @@ -457,37 +458,36 @@ class Installer: with open(f'{self.target}/etc/hostname', 'w') as fh: fh.write(hostname + '\n') - def set_locale(self, locale :str, encoding :str = 'UTF-8', *args :str, **kwargs :str) -> bool: - if not len(locale): - return True - + def set_locale(self, locale_config: LocaleConfiguration): modifier = '' + lang = locale_config.sys_lang + encoding = locale_config.sys_enc # This is a temporary patch to fix #1200 - if '.' in locale: - locale, potential_encoding = locale.split('.', 1) + if '.' in locale_config.sys_lang: + lang, potential_encoding = locale_config.sys_lang.split('.', 1) # Override encoding if encoding is set to the default parameter # and the "found" encoding differs. - if encoding == 'UTF-8' and encoding != potential_encoding: + if locale_config.sys_enc == 'UTF-8' and locale_config.sys_enc != potential_encoding: encoding = potential_encoding # Make sure we extract the modifier, that way we can put it in if needed. - if '@' in locale: - locale, modifier = locale.split('@', 1) + if '@' in locale_config.sys_lang: + lang, modifier = locale_config.sys_lang.split('@', 1) modifier = f"@{modifier}" # - End patch with open(f'{self.target}/etc/locale.gen', 'a') as fh: - fh.write(f'{locale}.{encoding}{modifier} {encoding}\n') + fh.write(f'{lang}.{encoding}{modifier} {encoding}\n') + with open(f'{self.target}/etc/locale.conf', 'w') as fh: - fh.write(f'LANG={locale}.{encoding}{modifier}\n') + fh.write(f'LANG={lang}.{encoding}{modifier}\n') try: SysCommand(f'/usr/bin/arch-chroot {self.target} locale-gen') - return True - except SysCallError: - return False + except SysCallError as e: + error(f'Failed to run locale-gen on target: {e}') def set_timezone(self, zone :str, *args :str, **kwargs :str) -> bool: if not zone: @@ -620,7 +620,7 @@ class Installer: return True - def mkinitcpio(self, *flags :str) -> bool: + def mkinitcpio(self, flags: List[str], locale_config: LocaleConfiguration) -> bool: for plugin in plugins.values(): if hasattr(plugin, 'on_mkinitcpio'): # Allow plugins to override the usage of mkinitcpio altogether. @@ -630,7 +630,7 @@ class Installer: # mkinitcpio will error out if there's no vconsole. if (vconsole := Path(f"{self.target}/etc/vconsole.conf")).exists() is False: with vconsole.open('w') as fh: - fh.write(f"KEYMAP={storage['arguments']['keyboard-layout']}\n") + fh.write(f"KEYMAP={locale_config.kb_layout}\n") with open(f'{self.target}/etc/mkinitcpio.conf', 'w') as mkinit: mkinit.write(f"MODULES=({' '.join(self.modules)})\n") @@ -658,7 +658,7 @@ class Installer: testing: bool = False, multilib: bool = False, hostname: str = 'archinstall', - locales: List[str] = ['en_US.UTF-8 UTF-8'] + locale_config: LocaleConfiguration = LocaleConfiguration.default() ): for mod in self._disk_config.device_modifications: for part in mod.partitions: @@ -734,12 +734,12 @@ class Installer: # sys_command(f'/usr/bin/arch-chroot {self.target} ln -s /usr/share/zoneinfo/{localtime} /etc/localtime') # sys_command('/usr/bin/arch-chroot /mnt hwclock --hctosys --localtime') self.set_hostname(hostname) - self.set_locale(*locales[0].split()) + self.set_locale(locale_config) # TODO: Use python functions for this SysCommand(f'/usr/bin/arch-chroot {self.target} chmod 700 /root') - self.mkinitcpio('-P') + self.mkinitcpio(['-P'], locale_config) self.helper_flags['base'] = True diff --git a/archinstall/lib/interactions/__init__.py b/archinstall/lib/interactions/__init__.py index 158750cc..466cfa0b 100644 --- a/archinstall/lib/interactions/__init__.py +++ b/archinstall/lib/interactions/__init__.py @@ -1,4 +1,3 @@ -from .locale_conf import select_locale_lang, select_locale_enc from .manage_users_conf import UserList, ask_for_additional_users from .network_conf import ManualNetworkConfig, ask_to_configure_network from .utils import get_password @@ -10,7 +9,7 @@ from .disk_conf import ( ) from .general_conf import ( - ask_ntp, ask_hostname, ask_for_a_timezone, ask_for_audio_selection, select_language, + ask_ntp, ask_hostname, ask_for_a_timezone, ask_for_audio_selection, select_archinstall_language, ask_additional_packages_to_install, add_number_of_parrallel_downloads, select_additional_repositories ) diff --git a/archinstall/lib/interactions/general_conf.py b/archinstall/lib/interactions/general_conf.py index 0338c61e..3b78847b 100644 --- a/archinstall/lib/interactions/general_conf.py +++ b/archinstall/lib/interactions/general_conf.py @@ -3,7 +3,7 @@ from __future__ import annotations import pathlib from typing import List, Any, Optional, TYPE_CHECKING -from ..locale import list_keyboard_languages, list_timezones +from ..locale import list_timezones, list_keyboard_languages from ..menu import MenuSelectionType, Menu, TextInput from ..output import warn from ..packages.packages import validate_package_list @@ -119,18 +119,18 @@ def select_archinstall_language(languages: List[Language], preset: Language) -> raise ValueError('Language selection not handled') -def ask_additional_packages_to_install(pre_set_packages: List[str] = []) -> List[str]: +def ask_additional_packages_to_install(preset: List[str] = []) -> List[str]: # Additional packages (with some light weight error handling for invalid package names) print(_('Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.')) print(_('If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt.')) - def read_packages(already_defined: list = []) -> list: - display = ' '.join(already_defined) + def read_packages(p: List = []) -> list: + display = ' '.join(p) input_packages = TextInput(_('Write additional packages to install (space separated, leave blank to skip): '), display).run().strip() return input_packages.split() if input_packages else [] - pre_set_packages = pre_set_packages if pre_set_packages else [] - packages = read_packages(pre_set_packages) + preset = preset if preset else [] + packages = read_packages(preset) if not storage['arguments']['offline'] and not storage['arguments']['no_pkg_lookups']: while True: diff --git a/archinstall/lib/interactions/locale_conf.py b/archinstall/lib/interactions/locale_conf.py deleted file mode 100644 index de115202..00000000 --- a/archinstall/lib/interactions/locale_conf.py +++ /dev/null @@ -1,43 +0,0 @@ -from typing import Any, TYPE_CHECKING, Optional - -from ..locale import list_locales -from ..menu import Menu, MenuSelectionType - -if TYPE_CHECKING: - _: Any - - -def select_locale_lang(preset: Optional[str] = None) -> Optional[str]: - locales = list_locales() - locale_lang = set([locale.split()[0] for locale in locales]) - - choice = Menu( - _('Choose which locale language to use'), - list(locale_lang), - sort=True, - preset_values=preset - ).run() - - match choice.type_: - case MenuSelectionType.Selection: return choice.single_value - case MenuSelectionType.Skip: return preset - - return None - - -def select_locale_enc(preset: Optional[str] = None) -> Optional[str]: - locales = list_locales() - locale_enc = set([locale.split()[1] for locale in locales]) - - choice = Menu( - _('Choose which locale encoding to use'), - list(locale_enc), - sort=True, - preset_values=preset - ).run() - - match choice.type_: - case MenuSelectionType.Selection: return choice.single_value - case MenuSelectionType.Skip: return preset - - return None diff --git a/archinstall/lib/interactions/system_conf.py b/archinstall/lib/interactions/system_conf.py index bbcb5b23..ea7e5989 100644 --- a/archinstall/lib/interactions/system_conf.py +++ b/archinstall/lib/interactions/system_conf.py @@ -29,14 +29,14 @@ def select_kernel(preset: List[str] = []) -> List[str]: sort=True, multi=True, preset_values=preset, - allow_reset=True, allow_reset_warning_msg=warning ).run() match choice.type_: case MenuSelectionType.Skip: return preset - case MenuSelectionType.Reset: return [] - case MenuSelectionType.Selection: return choice.value # type: ignore + case MenuSelectionType.Selection: return choice.single_value + + return [] def ask_for_bootloader(preset: Bootloader) -> Bootloader: diff --git a/archinstall/lib/locale/__init__.py b/archinstall/lib/locale/__init__.py new file mode 100644 index 00000000..6c32d6f3 --- /dev/null +++ b/archinstall/lib/locale/__init__.py @@ -0,0 +1,6 @@ +from .locale_menu import LocaleConfiguration +from .locale import ( + list_keyboard_languages, list_locales, list_x11_keyboard_languages, + verify_keyboard_layout, verify_x11_keyboard_layout, set_kb_layout, + list_timezones +) diff --git a/archinstall/lib/locale/locale.py b/archinstall/lib/locale/locale.py new file mode 100644 index 00000000..c3294e83 --- /dev/null +++ b/archinstall/lib/locale/locale.py @@ -0,0 +1,68 @@ +from typing import Iterator, List + +from ..exceptions import ServiceException, SysCallError +from ..general import SysCommand +from ..output import error + + +def list_keyboard_languages() -> Iterator[str]: + for line in SysCommand("localectl --no-pager list-keymaps", environment_vars={'SYSTEMD_COLORS': '0'}): + yield line.decode('UTF-8').strip() + + +def list_locales() -> List[str]: + with open('/etc/locale.gen', 'r') as fp: + locales = [] + # before the list of locales begins there's an empty line with a '#' in front + # so we'll collect the localels from bottom up and halt when we're donw + entries = fp.readlines() + entries.reverse() + + for entry in entries: + text = entry.replace('#', '').strip() + if text == '': + break + locales.append(text) + + locales.reverse() + return locales + + +def list_x11_keyboard_languages() -> Iterator[str]: + for line in SysCommand("localectl --no-pager list-x11-keymap-layouts", environment_vars={'SYSTEMD_COLORS': '0'}): + yield line.decode('UTF-8').strip() + + +def verify_keyboard_layout(layout :str) -> bool: + for language in list_keyboard_languages(): + if layout.lower() == language.lower(): + return True + return False + + +def verify_x11_keyboard_layout(layout :str) -> bool: + for language in list_x11_keyboard_languages(): + if layout.lower() == language.lower(): + return True + return False + + +def set_kb_layout(locale :str) -> bool: + if len(locale.strip()): + if not verify_keyboard_layout(locale): + error(f"Invalid keyboard locale specified: {locale}") + return False + + try: + SysCommand(f'localectl set-keymap {locale}') + except SysCallError as err: + raise ServiceException(f"Unable to set locale '{locale}' for console: {err}") + + return True + + return False + + +def list_timezones() -> Iterator[str]: + for line in SysCommand("timedatectl --no-pager list-timezones", environment_vars={'SYSTEMD_COLORS': '0'}): + yield line.decode('UTF-8').strip() diff --git a/archinstall/lib/locale/locale_menu.py b/archinstall/lib/locale/locale_menu.py new file mode 100644 index 00000000..29dd775d --- /dev/null +++ b/archinstall/lib/locale/locale_menu.py @@ -0,0 +1,155 @@ +from dataclasses import dataclass +from typing import Dict, Any, TYPE_CHECKING, Optional + +from .locale import set_kb_layout, list_keyboard_languages, list_locales +from ..menu import Selector, AbstractSubMenu, MenuSelectionType, Menu + +if TYPE_CHECKING: + _: Any + + +@dataclass +class LocaleConfiguration: + kb_layout: str + sys_lang: str + sys_enc: str + + @staticmethod + def default() -> 'LocaleConfiguration': + return LocaleConfiguration('us', 'en_US', 'UTF-8') + + def json(self) -> Dict[str, str]: + return { + 'kb_layout': self.kb_layout, + 'sys_lang': self.sys_lang, + 'sys_enc': self.sys_enc + } + + @classmethod + def _load_config(cls, config: 'LocaleConfiguration', args: Dict[str, Any]) -> 'LocaleConfiguration': + if 'sys_lang' in args: + config.sys_lang = args['sys_lang'] + if 'sys_enc' in args: + config.sys_enc = args['sys_enc'] + if 'kb_layout' in args: + config.kb_layout = args['kb_layout'] + + return config + + @classmethod + def parse_arg(cls, args: Dict[str, Any]) -> 'LocaleConfiguration': + default = cls.default() + + if 'locale_config' in args: + default = cls._load_config(default, args['locale_config']) + else: + default = cls._load_config(default, args) + + return default + + +class LocaleMenu(AbstractSubMenu): + def __init__( + self, + data_store: Dict[str, Any], + locele_conf: LocaleConfiguration + ): + self._preset = locele_conf + super().__init__(data_store=data_store) + + def setup_selection_menu_options(self): + self._menu_options['keyboard-layout'] = \ + Selector( + _('Keyboard layout'), + lambda preset: self._select_kb_layout(preset), + default='us', + enabled=True) + self._menu_options['sys-language'] = \ + Selector( + _('Locale language'), + lambda preset: select_locale_lang(preset), + default='en_US', + enabled=True) + self._menu_options['sys-encoding'] = \ + Selector( + _('Locale encoding'), + lambda preset: select_locale_enc(preset), + default='UTF-8', + enabled=True) + + def run(self, allow_reset: bool = True) -> LocaleConfiguration: + super().run(allow_reset=allow_reset) + + return LocaleConfiguration( + self._data_store['keyboard-layout'], + self._data_store['sys-language'], + self._data_store['sys-encoding'] + ) + + def _select_kb_layout(self, preset: Optional[str]) -> Optional[str]: + kb_lang = select_kb_layout(preset) + if kb_lang: + set_kb_layout(kb_lang) + return kb_lang + + +def select_locale_lang(preset: Optional[str] = None) -> Optional[str]: + locales = list_locales() + locale_lang = set([locale.split()[0] for locale in locales]) + + choice = Menu( + _('Choose which locale language to use'), + list(locale_lang), + sort=True, + preset_values=preset + ).run() + + match choice.type_: + case MenuSelectionType.Selection: return choice.single_value + case MenuSelectionType.Skip: return preset + + return None + + +def select_locale_enc(preset: Optional[str] = None) -> Optional[str]: + locales = list_locales() + locale_enc = set([locale.split()[1] for locale in locales]) + + choice = Menu( + _('Choose which locale encoding to use'), + list(locale_enc), + sort=True, + preset_values=preset + ).run() + + match choice.type_: + case MenuSelectionType.Selection: return choice.single_value + case MenuSelectionType.Skip: return preset + + return None + + +def select_kb_layout(preset: Optional[str] = None) -> Optional[str]: + """ + Asks the user to select a language + Usually this is combined with :ref:`archinstall.list_keyboard_languages`. + + :return: The language/dictionary key of the selected language + :rtype: str + """ + kb_lang = list_keyboard_languages() + # sort alphabetically and then by length + sorted_kb_lang = sorted(sorted(list(kb_lang)), key=len) + + choice = Menu( + _('Select keyboard layout'), + sorted_kb_lang, + preset_values=preset, + sort=False + ).run() + + match choice.type_: + case MenuSelectionType.Skip: return preset + case MenuSelectionType.Selection: return choice.single_value + + return None diff --git a/archinstall/lib/menu/abstract_menu.py b/archinstall/lib/menu/abstract_menu.py index 2bd56374..eee99747 100644 --- a/archinstall/lib/menu/abstract_menu.py +++ b/archinstall/lib/menu/abstract_menu.py @@ -3,7 +3,6 @@ from __future__ import annotations from typing import Callable, Any, List, Iterator, Tuple, Optional, Dict, TYPE_CHECKING from .menu import Menu, MenuSelectionType -from ..locale import set_keyboard_language from ..output import error from ..translationhandler import TranslationHandler, Language @@ -130,7 +129,7 @@ class Selector: if current: padding += 5 description = str(self._description).ljust(padding, ' ') - current = str(_('set: {}').format(current)) + current = current else: description = self._description current = '' @@ -243,31 +242,6 @@ class AbstractMenu: elif selector is not None and selector.has_selection(): self._data_store[selector_name] = selector.current_selection - def _missing_configs(self) -> List[str]: - def check(s): - return self._menu_options.get(s).has_selection() - - def has_superuser() -> bool: - sel = self._menu_options['!users'] - if sel.current_selection: - return any([u.sudo for u in sel.current_selection]) - return False - - mandatory_fields = dict(filter(lambda x: x[1].is_mandatory(), self._menu_options.items())) - missing = set() - - for key, selector in mandatory_fields.items(): - if key in ['!root-password', '!users']: - if not check('!root-password') and not has_superuser(): - missing.add( - str(_('Either root-password or at least 1 user with sudo privileges must be specified')) - ) - elif key == 'disk_config': - if not check('disk_config'): - missing.add(self._menu_options['disk_config'].description) - - return list(missing) - def setup_selection_menu_options(self): """ Define the menu options. Menu options can be defined here in a subclass or done per program calling self.set_option() @@ -328,9 +302,6 @@ class AbstractMenu: cursor_pos = None while True: - # Before continuing, set the preferred keyboard layout/language in the current terminal. - # This will just help the user with the next following questions. - self._set_kb_language() enabled_menus = self._menus_to_enable() padding = self._get_menu_text_padding(list(enabled_menus.values())) @@ -425,13 +396,6 @@ class AbstractMenu: return True - def _set_kb_language(self): - """ general for ArchInstall""" - # Before continuing, set the preferred keyboard layout/language in the current terminal. - # This will just help the user with the next following questions. - if self._data_store.get('keyboard-layout', None) and len(self._data_store['keyboard-layout']): - set_keyboard_language(self._data_store['keyboard-layout']) - def _verify_selection_enabled(self, selection_name: str) -> bool: """ general """ if selection := self._menu_options.get(selection_name, None): diff --git a/archinstall/lib/utils/util.py b/archinstall/lib/utils/util.py index 34716f4a..8df75ab1 100644 --- a/archinstall/lib/utils/util.py +++ b/archinstall/lib/utils/util.py @@ -1,6 +1,7 @@ from pathlib import Path -from typing import Any, TYPE_CHECKING, Optional +from typing import Any, TYPE_CHECKING, Optional, List +from ..output import FormattedOutput from ..output import info if TYPE_CHECKING: @@ -28,3 +29,23 @@ def is_subpath(first: Path, second: Path): return True except ValueError: return False + + +def format_cols(items: List[str], header: Optional[str]) -> str: + if header: + text = f'{header}:\n' + else: + text = '' + + nr_items = len(items) + if nr_items <= 5: + col = 1 + elif nr_items <= 10: + col = 2 + elif nr_items <= 15: + col = 3 + else: + col = 4 + + text += FormattedOutput.as_columns(items, col) + return text diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 1aecc1cd..7f9b9fd6 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -5,6 +5,7 @@ from typing import Any, TYPE_CHECKING import archinstall from archinstall import info, debug from archinstall import SysInfo +from archinstall.lib import locale from archinstall.lib import disk from archinstall.lib.global_menu import GlobalMenu from archinstall.default_profiles.applications.pipewire import PipewireProfile @@ -42,14 +43,10 @@ def ask_user_questions(): global_menu.enable('archinstall-language') - global_menu.enable('keyboard-layout') - # Set which region to download packages from during the installation global_menu.enable('mirror_config') - global_menu.enable('sys-language') - - global_menu.enable('sys-encoding') + global_menu.enable('locale_config') global_menu.enable('disk_config', mandatory=True) @@ -76,7 +73,7 @@ def ask_user_questions(): global_menu.enable('audio') # Ask for preferred kernel: - global_menu.enable('kernels') + global_menu.enable('kernels', mandatory=True) global_menu.enable('packages') @@ -114,9 +111,7 @@ def perform_installation(mountpoint: Path): # Retrieve list of additional repositories and set boolean values appropriately enable_testing = 'testing' in archinstall.arguments.get('additional-repositories', []) enable_multilib = 'multilib' in archinstall.arguments.get('additional-repositories', []) - - locale = f"{archinstall.arguments.get('sys-language', 'en_US')} {archinstall.arguments.get('sys-encoding', 'UTF-8').upper()}" - + locale_config: locale.LocaleConfiguration = archinstall.arguments['locale_config'] disk_encryption: disk.DiskEncryption = archinstall.arguments.get('disk_encryption', None) with Installer( @@ -147,7 +142,7 @@ def perform_installation(mountpoint: Path): testing=enable_testing, multilib=enable_multilib, hostname=archinstall.arguments.get('hostname', 'archlinux'), - locales=[locale] + locale_config=locale_config ) if mirror_config := archinstall.arguments.get('mirror_config', None): @@ -210,7 +205,7 @@ def perform_installation(mountpoint: Path): # This step must be after profile installs to allow profiles_bck to install language pre-requisits. # After which, this step will set the language both for console and x11 if x11 was installed for instance. - installation.set_keyboard_language(archinstall.arguments['keyboard-layout']) + installation.set_keyboard_language(locale_config.kb_layout) if profile_config := archinstall.arguments.get('profile_config', None): profile_config.profile.post_install(installation) diff --git a/archinstall/scripts/swiss.py b/archinstall/scripts/swiss.py index 1998f073..375458a1 100644 --- a/archinstall/scripts/swiss.py +++ b/archinstall/scripts/swiss.py @@ -8,6 +8,7 @@ from archinstall import SysInfo, info, debug from archinstall.lib import mirrors from archinstall.lib import models from archinstall.lib import disk +from archinstall.lib import locale from archinstall.lib.networking import check_mirror_reachable from archinstall.lib.profile.profiles_handler import profile_handler from archinstall.lib import menu @@ -92,14 +93,14 @@ class SwissMainMenu(GlobalMenu): match self._execution_mode: case ExecutionMode.Full | ExecutionMode.Lineal: options_list = [ - 'keyboard-layout', 'mirror_config', 'disk_config', + 'mirror_config', 'disk_config', 'disk_encryption', 'swap', 'bootloader', 'hostname', '!root-password', '!users', 'profile_config', 'audio', 'kernels', 'packages', 'additional-repositories', 'nic', 'timezone', 'ntp' ] if archinstall.arguments.get('advanced', False): - options_list.extend(['sys-language', 'sys-encoding']) + options_list.extend(['locale_config']) mandatory_list = ['disk_config', 'bootloader', 'hostname'] case ExecutionMode.Only_HD: @@ -107,7 +108,7 @@ class SwissMainMenu(GlobalMenu): mandatory_list = ['disk_config'] case ExecutionMode.Only_OS: options_list = [ - 'keyboard-layout', 'mirror_config','bootloader', 'hostname', + 'mirror_config','bootloader', 'hostname', '!root-password', '!users', 'profile_config', 'audio', 'kernels', 'packages', 'additional-repositories', 'nic', 'timezone', 'ntp' ] @@ -115,7 +116,7 @@ class SwissMainMenu(GlobalMenu): mandatory_list = ['hostname'] if archinstall.arguments.get('advanced', False): - options_list += ['sys-language','sys-encoding'] + options_list += ['locale_config'] case ExecutionMode.Minimal: pass case _: @@ -176,8 +177,7 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): enable_testing = 'testing' in archinstall.arguments.get('additional-repositories', []) enable_multilib = 'multilib' in archinstall.arguments.get('additional-repositories', []) - - locale = f"{archinstall.arguments.get('sys-language', 'en_US')} {archinstall.arguments.get('sys-encoding', 'UTF-8').upper()}" + locale_config: locale.LocaleConfiguration = archinstall.arguments['locale_config'] with Installer( mountpoint, @@ -206,7 +206,7 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): testing=enable_testing, multilib=enable_multilib, hostname=archinstall.arguments.get('hostname', 'archlinux'), - locales=[locale] + locale_config=locale_config ) if mirror_config := archinstall.arguments.get('mirror_config', None): @@ -263,7 +263,7 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): # This step must be after profile installs to allow profiles_bck to install language pre-requisits. # After which, this step will set the language both for console and x11 if x11 was installed for instance. - installation.set_keyboard_language(archinstall.arguments['keyboard-layout']) + installation.set_keyboard_language(locale_config.kb_layout) if profile_config := archinstall.arguments.get('profile_config', None): profile_config.profile.post_install(installation) diff --git a/examples/interactive_installation.py b/examples/interactive_installation.py index 7c4ffed7..ce1a80ec 100644 --- a/examples/interactive_installation.py +++ b/examples/interactive_installation.py @@ -10,6 +10,7 @@ from archinstall.default_profiles.applications.pipewire import PipewireProfile from archinstall import disk from archinstall import menu from archinstall import models +from archinstall import locale from archinstall import info, debug if TYPE_CHECKING: @@ -21,14 +22,10 @@ def ask_user_questions(): global_menu.enable('archinstall-language') - global_menu.enable('keyboard-layout') - # Set which region to download packages from during the installation global_menu.enable('mirror_config') - global_menu.enable('sys-language') - - global_menu.enable('sys-encoding') + global_menu.enable('locale_config') global_menu.enable('disk_config', mandatory=True) @@ -55,7 +52,7 @@ def ask_user_questions(): global_menu.enable('audio') # Ask for preferred kernel: - global_menu.enable('kernels') + global_menu.enable('kernels', mandatory=True) global_menu.enable('packages') @@ -93,9 +90,7 @@ def perform_installation(mountpoint: Path): # Retrieve list of additional repositories and set boolean values appropriately enable_testing = 'testing' in archinstall.arguments.get('additional-repositories', []) enable_multilib = 'multilib' in archinstall.arguments.get('additional-repositories', []) - - locale = f"{archinstall.arguments.get('sys-language', 'en_US')} {archinstall.arguments.get('sys-encoding', 'UTF-8').upper()}" - + locale_config: locale.LocaleConfiguration = archinstall.arguments['locale_config'] disk_encryption: disk.DiskEncryption = archinstall.arguments.get('disk_encryption', None) with Installer( @@ -126,7 +121,7 @@ def perform_installation(mountpoint: Path): testing=enable_testing, multilib=enable_multilib, hostname=archinstall.arguments.get('hostname', 'archlinux'), - locales=[locale] + locale_config=locale_config ) if mirror_config := archinstall.arguments.get('mirror_config', None): @@ -189,7 +184,7 @@ def perform_installation(mountpoint: Path): # This step must be after profile installs to allow profiles_bck to install language pre-requisits. # After which, this step will set the language both for console and x11 if x11 was installed for instance. - installation.set_keyboard_language(archinstall.arguments['keyboard-layout']) + installation.set_keyboard_language(locale_config.kb_layout) if profile_config := archinstall.arguments.get('profile_config', None): profile_config.profile.post_install(installation) -- cgit v1.2.3-70-g09d2 From f51fd1a2780dae0e253f02b11e4b4915424893ba Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Mon, 5 Jun 2023 04:05:38 -0400 Subject: Detect more modules requiring `alsa-firmware` (#1822) --- archinstall/lib/hardware.py | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/archinstall/lib/hardware.py b/archinstall/lib/hardware.py index 2b65e07c..bd153a63 100644 --- a/archinstall/lib/hardware.py +++ b/archinstall/lib/hardware.py @@ -191,4 +191,33 @@ class SysInfo: @staticmethod def requires_alsa_fw() -> bool: - return 'snd_emu10k1' in _sys_info.loaded_modules + modules = ( + 'snd_asihpi', + 'snd_cs46xx', + 'snd_darla20', + 'snd_darla24', + 'snd_echo3g', + 'snd_emu10k1', + 'snd_gina20', + 'snd_gina24', + 'snd_hda_codec_ca0132', + 'snd_hdsp', + 'snd_indigo', + 'snd_indigodj', + 'snd_indigodjx', + 'snd_indigoio', + 'snd_indigoiox', + 'snd_layla20', + 'snd_layla24', + 'snd_mia', + 'snd_mixart', + 'snd_mona', + 'snd_pcxhr', + 'snd_vx_lib' + ) + + for loaded_module in _sys_info.loaded_modules: + if loaded_module in modules: + return True + + return False -- cgit v1.2.3-70-g09d2 From 11cecbb3dca3a9f8b9c2bc29fd72497432fe2cfc Mon Sep 17 00:00:00 2001 From: Xuan-Rui Fan Date: Mon, 5 Jun 2023 09:06:37 +0100 Subject: Add traditional Chinese translation (#1823) * Add Traditional Chinese (Taiwan) * Update languages.json --- archinstall/locales/languages.json | 2 +- archinstall/locales/zh-TW/LC_MESSAGES/base.mo | Bin 0 -> 32090 bytes archinstall/locales/zh-TW/LC_MESSAGES/base.po | 1132 +++++++++++++++++++++++++ 3 files changed, 1133 insertions(+), 1 deletion(-) create mode 100644 archinstall/locales/zh-TW/LC_MESSAGES/base.mo create mode 100644 archinstall/locales/zh-TW/LC_MESSAGES/base.po diff --git a/archinstall/locales/languages.json b/archinstall/locales/languages.json index 58a55373..954d5a26 100644 --- a/archinstall/locales/languages.json +++ b/archinstall/locales/languages.json @@ -181,6 +181,6 @@ {"abbr": "yi", "lang": "Yiddish"}, {"abbr": "yo", "lang": "Yoruba"}, {"abbr": "za", "lang": "Zhuang"}, - {"abbr": "zh-TW", "lang": "Traditional Chinese"}, + {"abbr": "zh-TW", "lang": "Traditional Chinese", "translated_lang": "繁體中文"}, {"abbr": "zh-CN", "lang": "Simplified Chinese"}, {"abbr": "zu", "lang": "Zulu"}] diff --git a/archinstall/locales/zh-TW/LC_MESSAGES/base.mo b/archinstall/locales/zh-TW/LC_MESSAGES/base.mo new file mode 100644 index 00000000..8473e868 Binary files /dev/null and b/archinstall/locales/zh-TW/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/zh-TW/LC_MESSAGES/base.po b/archinstall/locales/zh-TW/LC_MESSAGES/base.po new file mode 100644 index 00000000..4b50b2ec --- /dev/null +++ b/archinstall/locales/zh-TW/LC_MESSAGES/base.po @@ -0,0 +1,1132 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: Xuan-Rui Fan \n" +"Language-Team: \n" +"Language: zh_CN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.1.1\n" + +msgid "[!] A log file has been created here: {} {}" +msgstr "[!] 日誌文件已在此處創建:{} {}" + +msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" +msgstr " 請將此問題(以及文件)提交到 https://github.com/archlinux/archinstall/issues" + +msgid "Do you really want to abort?" +msgstr "您真的要中止嗎?" + +msgid "And one more time for verification: " +msgstr "請再輸入一次以進行驗證:" + +msgid "Would you like to use swap on zram?" +msgstr "您想在 zram 上使用交換分割區(swap)嗎?" + +msgid "Desired hostname for the installation: " +msgstr "請輸入安裝後預期使用的主機名稱(hostname):" + +msgid "Username for required superuser with sudo privileges: " +msgstr "請輸入需要超級使用者的使用者名稱(sudo 權限):" + +msgid "Any additional users to install (leave blank for no users): " +msgstr "要新增的其他使用者(留空表示不新建其他使用者):" + +msgid "Should this user be a superuser (sudoer)?" +msgstr "這個使用者應該成為超級使用者(sudoer)嗎?" + +msgid "Select a timezone" +msgstr "選擇時區" + +msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?" +msgstr "您希望使用 GRUB 作為開機引導程式,而不是 systemd-boot 嗎?" + +msgid "Choose a bootloader" +msgstr "選擇引導程式" + +msgid "Choose an audio server" +msgstr "請選擇音訊伺服器" + +msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." +msgstr "僅安裝基本套件,如 base、base-devel、linux、linux-firmware、efibootmgr 和選擇性的軟體設定套件。" + +msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." +msgstr "如果您想要一個網頁瀏覽器,例如 firefox 或 chromium,可以在下面的提示字元中指定。" + +msgid "Write additional packages to install (space separated, leave blank to skip): " +msgstr "請輸入您要安裝的其它套件(以空格分隔,留空以跳過):" + +msgid "Copy ISO network configuration to installation" +msgstr "將 ISO 中的網路設置複製到安裝中" + +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "使用 NetworkManager(在 GNOME 和 KDE 透過圖形界面設置網際網路連線所需)" + +msgid "Select one network interface to configure" +msgstr "請選擇要設定的網路介面" + +msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" +msgstr "請選擇要為“{}”設定的模式或直接使用預設模式“{}”" + +msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " +msgstr "請輸入 {} 的 IP 和子網路(例如:192.168.0.5/24):" + +msgid "Enter your gateway (router) IP address or leave blank for none: " +msgstr "請輸入您的網關(路由器)IP 地址或留空以跳過:" + +msgid "Enter your DNS servers (space separated, blank for none): " +msgstr "請輸入您的 DNS 伺服器(以空格分隔,留空以跳過):" + +msgid "Select which filesystem your main partition should use" +msgstr "請選擇您的主要分割區應該使用哪種檔案系統。" + +msgid "Current partition layout" +msgstr "目前的分割區配置" + +msgid "" +"Select what to do with\n" +"{}" +msgstr "" +"請選擇要執行的操作\n" +"{}" + +msgid "Enter a desired filesystem type for the partition" +msgstr "請輸入您想要的分割區檔案系統類型" + +msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " +msgstr "請輸入起始位置(以 parted 單位表示:s、GB、% 等等;預設值:{}):" + +msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " +msgstr "請輸入結束位置(以 parted 單位表示:s、GB、% 等等;例如:{}):" + +msgid "{} contains queued partitions, this will remove those, are you sure?" +msgstr "{} 包含佇列分割區,這將刪除這些分割區,您確定嗎?" + +msgid "" +"{}\n" +"\n" +"Select by index which partitions to delete" +msgstr "" +"{}\n" +"\n" +"請根據索引選擇欲刪除的分割區" + +msgid "" +"{}\n" +"\n" +"Select by index which partition to mount where" +msgstr "" +"{}\n" +"\n" +"請依據索引選擇要掛載的分割區以及掛載點" + +msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * 分割區掛載點是相對於安裝內部的,例如 boot 應該為 /boot。" + +msgid "Select where to mount partition (leave blank to remove mountpoint): " +msgstr "請選擇掛載分割區的位置(留空以移除掛載點):" + +msgid "" +"{}\n" +"\n" +"Select which partition to mask for formatting" +msgstr "" +"{}\n" +"\n" +"請選擇要格式化的分割區" + +msgid "" +"{}\n" +"\n" +"Select which partition to mark as encrypted" +msgstr "" +"{}\n" +"\n" +"請選擇要加密的分割區" + +msgid "" +"{}\n" +"\n" +"Select which partition to mark as bootable" +msgstr "" +"{}\n" +"\n" +"請選擇要標記為可引導的分割區" + +msgid "" +"{}\n" +"\n" +"Select which partition to set a filesystem on" +msgstr "" +"{}\n" +"\n" +"請選擇要設置為文件系統的分割區" + +msgid "Enter a desired filesystem type for the partition: " +msgstr "為分區選擇所需的文件系統類型:" + +msgid "Archinstall language" +msgstr "Archinstall 語言" + +msgid "Wipe all selected drives and use a best-effort default partition layout" +msgstr "移除所有選定的硬碟並使用最佳的預設分割佈局" + +msgid "Select what to do with each individual drive (followed by partition usage)" +msgstr "依序選擇硬碟(並設定分割佈局)" + +msgid "Select what you wish to do with the selected block devices" +msgstr "選擇您希望對所選區塊裝置執行的操作" + +msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments" +msgstr "以下是預編程設置檔案的列表,它們可以使安裝桌面環境等更加容易" + +msgid "Select keyboard layout" +msgstr "選擇鍵盤佈局" + +msgid "Select one of the regions to download packages from" +msgstr "請選擇一個地區以下載軟體套件" + +msgid "Select one or more hard drives to use and configure" +msgstr "選擇要使用和設定的硬碟(可多選)" + +msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." +msgstr "為了與您的 AMD 設備實現最佳兼容性,您可能需要使用開源或 AMD / ATI 選項。" + +msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" +msgstr "為了與您的 Intel 設備實現最佳兼容性,您可能需要使用開源或 Intel 選項。\n" + +msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" +msgstr "為了與您的 Nvidia 設備實現最佳兼容性,您可能需要使用 Nvidia 官方驅動程式。\n" + +msgid "" +"\n" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"選擇圖形驅動程式或留空以安裝開源驅動程式" + +msgid "All open-source (default)" +msgstr "全部開源(默認)" + +msgid "Choose which kernels to use or leave blank for default \"{}\"" +msgstr "選擇要使用的内核或留空以使用默認值“{}”" + +msgid "Choose which locale language to use" +msgstr "選擇要使用的區域語言" + +msgid "Choose which locale encoding to use" +msgstr "選擇要使用的區域編碼" + +msgid "Select one of the values shown below: " +msgstr "選擇如下所示的值之一:" + +msgid "Select one or more of the options below: " +msgstr "選擇以下一個或多個選項:" + +msgid "Adding partition...." +msgstr "新增分割區...." + +msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." +msgstr "您需要輸入有效的文件系統類型才能繼續。有關有效的文件系統類型,請參閱 `man parted`。" + +msgid "Error: Listing profiles on URL \"{}\" resulted in:" +msgstr "錯誤:在 URL “{}” 上列出設定檔案時出錯:" + +msgid "Error: Could not decode \"{}\" result as JSON:" +msgstr "錯誤:無法將“{}”結果解碼為 JSON:" + +msgid "Keyboard layout" +msgstr "鍵盤佈局" + +msgid "Mirror region" +msgstr "鏡像區域" + +msgid "Locale language" +msgstr "區域語言" + +msgid "Locale encoding" +msgstr "區域編碼" + +msgid "Drive(s)" +msgstr "硬碟" + +msgid "Disk layout" +msgstr "磁盤佈局" + +msgid "Encryption password" +msgstr "加密密碼" + +msgid "Swap" +msgstr "交換空間" + +msgid "Bootloader" +msgstr "引導加載程式" + +msgid "Root password" +msgstr "Root 密碼" + +msgid "Superuser account" +msgstr "超級使用者帳戶" + +msgid "User account" +msgstr "使用者帳戶" + +msgid "Profile" +msgstr "設定檔案" + +msgid "Audio" +msgstr "音訊" + +msgid "Kernels" +msgstr "内核" + +msgid "Additional packages" +msgstr "額外套件" + +msgid "Network configuration" +msgstr "網路設置" + +msgid "Automatic time sync (NTP)" +msgstr "自動同步時間 (NTP)" + +msgid "Install ({} config(s) missing)" +msgstr "安裝(缺少 {} 個配置)" + +msgid "" +"You decided to skip harddrive selection\n" +"and 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?" +msgstr "" +"您決定跳過硬碟選擇\n" +"並將使用掛載在 {}(實驗性)上任一的硬碟設置\n" +"警告:Archinstall 不會檢查此設置的適用性\n" +"您想繼續嗎?" + +msgid "Re-using partition instance: {}" +msgstr "重複使用分割區實例:{}" + +msgid "Create a new partition" +msgstr "創建新分割區" + +msgid "Delete a partition" +msgstr "刪除一個分割區" + +msgid "Clear/Delete all partitions" +msgstr "清除/刪除所有分割區" + +msgid "Assign mount-point for a partition" +msgstr "為分割區分配掛載點" + +msgid "Mark/Unmark a partition to be formatted (wipes data)" +msgstr "標記/取消標記要格式化的分割區(移除數據)" + +msgid "Mark/Unmark a partition as encrypted" +msgstr "將分割區標記/取消標記為加密" + +msgid "Mark/Unmark a partition as bootable (automatic for /boot)" +msgstr "將分割區標記/取消標記為可引导(自動為 /boot)" + +msgid "Set desired filesystem for a partition" +msgstr "為分割區設置所需的文件系統" + +msgid "Abort" +msgstr "中止" + +msgid "Hostname" +msgstr "主機名稱" + +msgid "Not configured, unavailable unless setup manually" +msgstr "未設置,除非手動設置,否則不可用" + +msgid "Timezone" +msgstr "時區" + +msgid "Set/Modify the below options" +msgstr "請設置/修改以下選項" + +msgid "Install" +msgstr "安裝" + +msgid "" +"Use ESC to skip\n" +"\n" +msgstr "" +"按 ESC 鍵以跳過\n" +"\n" + +msgid "Suggest partition layout" +msgstr "建議分割區佈局" + +msgid "Enter a password: " +msgstr "輸入密碼:" + +msgid "Enter a encryption password for {}" +msgstr "輸入 {} 的加密密碼" + +msgid "Enter disk encryption password (leave blank for no encryption): " +msgstr "輸入硬碟加密密碼(留空表示不加密):" + +msgid "Create a required super-user with sudo privileges: " +msgstr "創建具有 sudo 權限的超級使用者:" + +msgid "Enter root password (leave blank to disable root): " +msgstr "輸入 root 密碼(留空以禁用 root):" + +msgid "Password for user \"{}\": " +msgstr "使用者“{}”的密碼:" + +msgid "Verifying that additional packages exist (this might take a few seconds)" +msgstr "驗證是否存在其它套件(這可能需要幾秒鐘)" + +msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" +msgstr "是否要對默認時間伺服器使用自動時間同步 (NTP)?\n" + +msgid "" +"Hardware time and other post-configuration steps might be required in order for NTP to work.\n" +"For more information, please check the Arch wiki" +msgstr "" +"為了使 NTP 運作,可能需要硬體執行時間和其它設置後步驟。\n" +"有關更多資訊,請查看 Arch wiki" + +msgid "Enter a username to create an additional user (leave blank to skip): " +msgstr "輸入用名以創建其他使用者(留空以跳過):" + +msgid "Use ESC to skip\n" +msgstr "按 ESC 鍵以跳過\n" + +msgid "" +"\n" +" Choose an object from the list, and select one of the available actions for it to execute" +msgstr "" +"\n" +"從列表中選擇一個對象,並選擇要執行的操作" + +msgid "Cancel" +msgstr "取消" + +msgid "Confirm and exit" +msgstr "確認並退出" + +msgid "Add" +msgstr "新增" + +msgid "Copy" +msgstr "複製" + +msgid "Edit" +msgstr "編輯" + +msgid "Delete" +msgstr "刪除" + +msgid "Select an action for '{}'" +msgstr "為“{}”選擇一個操作" + +msgid "Copy to new key:" +msgstr "複製到新密鑰:" + +msgid "Unknown nic type: {}. Possible values are {}" +msgstr "未知網卡類型:{}。 可能的值為 {}" + +msgid "" +"\n" +"This is your chosen configuration:" +msgstr "" +"\n" +"這是您選擇的配置:" + +msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." +msgstr "Pacman 已經在運行,最多等候 10 分鐘直到其終止。" + +msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." +msgstr "預先存在的 pacman 鎖從未退出。請在使用 archinstall 之前清理所有現有的 pacman 工作階段。" + +msgid "Choose which optional additional repositories to enable" +msgstr "選擇要啟用的可選附加庫" + +msgid "Add a user" +msgstr "新增一個使用者" + +msgid "Change password" +msgstr "修改密碼" + +msgid "Promote/Demote user" +msgstr "升級/降級使用者" + +msgid "Delete User" +msgstr "刪除使用者" + +msgid "" +"\n" +"Define a new user\n" +msgstr "" +"\n" +"定義一個新使用者\n" + +msgid "User Name : " +msgstr "用戶名:" + +msgid "Should {} be a superuser (sudoer)?" +msgstr "是否將{}設置为超級使用者(sudoer)?" + +msgid "Define users with sudo privilege: " +msgstr "定義具有 sudo 權限的使用者:" + +msgid "No network configuration" +msgstr "無網路配置" + +msgid "Set desired subvolumes on a btrfs partition" +msgstr "在 btrfs 分割區上設置所需的子卷" + +msgid "" +"{}\n" +"\n" +"Select which partition to set subvolumes on" +msgstr "" +"{}\n" +"\n" +"選擇要在哪個分割區上設置子卷" + +msgid "Manage btrfs subvolumes for current partition" +msgstr "管理當前分割區的 btrfs 子卷" + +msgid "No configuration" +msgstr "無配置" + +msgid "Save user configuration" +msgstr "保存使用者配置" + +msgid "Save user credentials" +msgstr "保存使用者憑證" + +msgid "Save disk layout" +msgstr "保存硬碟佈局" + +msgid "Save all" +msgstr "全部保存" + +msgid "Choose which configuration to save" +msgstr "選擇要保存的配置" + +msgid "Enter a directory for the configuration(s) to be saved: " +msgstr "輸入要保存配置的目錄:" + +msgid "Not a valid directory: {}" +msgstr "不是有效的目錄:{}" + +msgid "The password you are using seems to be weak," +msgstr "您使用的密碼似乎很弱," + +msgid "are you sure you want to use it?" +msgstr "您確定要使用它嗎?" + +msgid "Optional repositories" +msgstr "可選目錄" + +msgid "Save configuration" +msgstr "保存配置" + +msgid "Missing configurations:\n" +msgstr "缺少配置:\n" + +msgid "Either root-password or at least 1 superuser must be specified" +msgstr "必須指定 root 密碼或至少 1 個超級使用者" + +msgid "Manage superuser accounts: " +msgstr "管理超級使用者帳號:" + +msgid "Manage ordinary user accounts: " +msgstr "管理普通使用者帳號:" + +msgid " Subvolume :{:16}" +msgstr " 子卷 :{:16}" + +msgid " mounted at {:16}" +msgstr " 掛載在 {:16}" + +msgid " with option {}" +msgstr " 與選項 {}" + +msgid "" +"\n" +" Fill the desired values for a new subvolume \n" +msgstr "" +"\n" +" 填寫新子卷所需的值\n" + +msgid "Subvolume name " +msgstr "子卷名" + +msgid "Subvolume mountpoint" +msgstr "子卷掛載點" + +msgid "Subvolume options" +msgstr "子卷選項" + +msgid "Save" +msgstr "保存" + +msgid "Subvolume name :" +msgstr "子卷名:" + +msgid "Select a mount point :" +msgstr "選擇掛載點:" + +msgid "Select the desired subvolume options " +msgstr "選擇所需的子卷選項" + +msgid "Define users with sudo privilege, by username: " +msgstr "以用戶名定義具有 sudo 權限的使用者:" + +msgid "[!] A log file has been created here: {}" +msgstr "[!] 日誌文件已在此處創建:{}" + +msgid "Would you like to use BTRFS subvolumes with a default structure?" +msgstr "您想使用具有默認結構的 BTRFS 子卷嗎?" + +msgid "Would you like to use BTRFS compression?" +msgstr "您想使用 BTRFS 壓縮嗎?" + +msgid "Would you like to create a separate partition for /home?" +msgstr "您想為 /home 創建一個單獨的分割區嗎?" + +msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" +msgstr "所選硬碟沒有自動建議所需的最小容量\n" + +msgid "Minimum capacity for /home partition: {}GB\n" +msgstr "/home 分割區的最小容量為:{}GB\n" + +msgid "Minimum capacity for Arch Linux partition: {}GB" +msgstr "Arch Linux 分割區的最小容量為:{}GB" + +msgid "Continue" +msgstr "繼續" + +msgid "yes" +msgstr "是" + +msgid "no" +msgstr "否" + +msgid "set: {}" +msgstr "設置:{}" + +msgid "Manual configuration setting must be a list" +msgstr "手動配置設置必須為一個列表" + +msgid "No iface specified for manual configuration" +msgstr "沒有為手動配置指定 iface" + +msgid "Manual nic configuration with no auto DHCP requires an IP address" +msgstr "沒有自動 DHCP 的手動 nic 配置需要 IP 地址" + +msgid "Add interface" +msgstr "新增接口" + +msgid "Edit interface" +msgstr "編輯接口" + +msgid "Delete interface" +msgstr "刪除接口" + +msgid "Select interface to add" +msgstr "選擇要新增的接口" + +msgid "Manual configuration" +msgstr "手動配置" + +msgid "Mark/Unmark a partition as compressed (btrfs only)" +msgstr "將分割區標記/取消標記為壓縮(僅限 btrfs)" + +msgid "The password you are using seems to be weak, are you sure you want to use it?" +msgstr "您使用的密碼似乎很弱,您確定要使用嗎?" + +msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" +msgstr "提供一系列桌面環境和平鋪視窗管理器,例如 gnome, kde, sway" + +msgid "Select your desired desktop environment" +msgstr "選擇您想要的桌面環境" + +msgid "A very basic installation that allows you to customize Arch Linux as you see fit." +msgstr "一個非常基本的安裝,允許您根據需要自定義 Arch Linux。" + +msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" +msgstr "提供一系列可供安裝和啟用的伺服器套件,例如 httpd、nginx、mariadb" + +msgid "Choose which servers to install, if none then a minimal installation will be done" +msgstr "選擇要安裝的顯示伺服器,如果沒有,則進行最小安裝" + +msgid "Installs a minimal system as well as xorg and graphics drivers." +msgstr "安裝最小系統以及 xorg 和圖形驅動程式。" + +msgid "Press Enter to continue." +msgstr "按 Enter 以繼續。" + +msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?" +msgstr "您想 chroot 進入新創建的安裝並執行安裝後的配置嗎?" + +msgid "Are you sure you want to reset this setting?" +msgstr "您確定要重置此設置嗎?" + +msgid "Select one or more hard drives to use and configure\n" +msgstr "選擇一個或多個硬碟来使用和配置\n" + +msgid "Any modifications to the existing setting will reset the disk layout!" +msgstr "對現有設置的任何修改都將重置硬碟佈局!" + +msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" +msgstr "如果重置硬碟選擇,當前硬碟佈局也將被重置。 您確定嗎?" + +msgid "Save and exit" +msgstr "保存並退出" + +msgid "" +"{}\n" +"contains queued partitions, this will remove those, are you sure?" +msgstr "" +"{}\n" +"包含佇列的分割區,這將刪除這些分割區,您確定嗎?" + +msgid "No audio server" +msgstr "沒有音訊伺服器" + +msgid "(default)" +msgstr "(默認)" + +msgid "Use ESC to skip" +msgstr "按 ESC 鍵以跳過" + +msgid "" +"Use CTRL+C to reset current selection\n" +"\n" +msgstr "使用 CTRL+C 可重置當前選項\n" + +msgid "Copy to: " +msgstr "複製到:" + +msgid "Edit: " +msgstr "編輯:" + +msgid "Key: " +msgstr "密鑰:" + +msgid "Edit {}: " +msgstr "編輯 {}: " + +msgid "Add: " +msgstr "新增:" + +msgid "Value: " +msgstr "值:" + +msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" +msgstr "您可以跳過選擇硬碟和分割區並使用任一掛載在 /mnt 的硬碟設置(實驗性)" + +msgid "Select one of the disks or skip and use /mnt as default" +msgstr "選擇一個硬碟或跳過並使用 /mnt 作為默認值" + +msgid "Select which partitions to mark for formatting:" +msgstr "選擇要標記為格式化的分割區:" + +msgid "Use HSM to unlock encrypted drive" +msgstr "使用 HSM 解鎖加密硬碟" + +msgid "Device" +msgstr "裝置" + +msgid "Size" +msgstr "大小" + +msgid "Free space" +msgstr "可用空間" + +msgid "Bus-type" +msgstr "匯流排類型" + +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "必須指定 root 密碼或至少 1 個具有 sudo 權限的使用者" + +msgid "Enter username (leave blank to skip): " +msgstr "輸入用戶名(留空以跳過):" + +msgid "The username you entered is invalid. Try again" +msgstr "您輸入的用戶名無效。請重試" + +msgid "Should \"{}\" be a superuser (sudo)?" +msgstr "是否將“{}”設置為超級使用者(sudo)?" + +msgid "Select which partitions to encrypt" +msgstr "選擇要加密的分割區:" + +msgid "very weak" +msgstr "非常弱" + +msgid "weak" +msgstr "弱" + +msgid "moderate" +msgstr "一般" + +msgid "strong" +msgstr "強" + +msgid "Add subvolume" +msgstr "新增子卷" + +msgid "Edit subvolume" +msgstr "編輯子卷" + +msgid "Delete subvolume" +msgstr "刪除子卷" + +msgid "Configured {} interfaces" +msgstr "已配置的 {} 接口" + +msgid "This option enables the number of parallel downloads that can occur during installation" +msgstr "此選項啟用安裝期間可能發生的並行下載數" + +#, python-brace-format +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +" (Enter a value between 1 to {max_downloads})\n" +"Note:" +msgstr "" +"輸入要啟用的並行下載數。\n" +" (輸入一個 1 到 {max_downloads} 之間的值)\n" +"提示:" + +msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - 最大值:{max_downloads}(允許 {max_downloads} 个並行下載,同時允許 {max_downloads+1} 个下載)" + +msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" +msgstr " - 最小值:1(允許 1 个並行下載,同時允許 2 个下載)" + +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" +msgstr " - 禁用/默認:0(禁用並行下載,同時只允許 1 个下載)" + +#, python-brace-format +msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" +msgstr "輸入無效! 請嘗試使用有效輸入重試 [1 到 {max_downloads},或 0 到禁用]" + +msgid "Parallel Downloads" +msgstr "並行下載" + +msgid "ESC to skip" +msgstr "按 ESC 鍵以跳過" + +msgid "CTRL+C to reset" +msgstr "按 CTRL+C 以重置" + +msgid "TAB to select" +msgstr "按 TAB 以選擇" + +msgid "[Default value: 0] > " +msgstr "[默認值: 0] > " + +msgid "To be able to use this translation, please install a font manually that supports the language." +msgstr "為了能夠使用此翻譯,請手動安裝支持該語言的字體。" + +msgid "The font should be stored as {}" +msgstr "字體應儲存為 {}" + +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "Archinstall 需要 root 權限才能運行。有關更多信息,請參閱 --help。" + +msgid "Select an execution mode" +msgstr "選擇執行模式" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "無法從指定的 URL 獲取配置文件:{}" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "配置文件必須具有唯一的名稱,但找到具有重新名稱的配置文件定義:{}" + +msgid "Select one or more devices to use and configure" +msgstr "選擇一個或多個硬碟来使用和配置" + +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "如果重置硬碟選擇,當前硬碟佈局也將被重置。 您確定嗎?" + +msgid "Existing Partitions" +msgstr "新增分割區...." + +msgid "Select a partitioning option" +msgstr "刪除一個分割區" + +msgid "Enter the root directory of the mounted devices: " +msgstr "輸入掛載裝置的根目錄:" + +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "/home 分割區的最小容量為:{}GB\n" + +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Arch Linux 分割區的最小容量為:{}GB" + +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "以下是預編程設置檔案的列表,它們可以使安裝桌面環境等更加容易" + +msgid "Current profile selection" +msgstr "當前分割區佈局" + +msgid "Remove all newly added partitions" +msgstr "創建新分割區" + +msgid "Assign mountpoint" +msgstr "分配掛載點" + +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "標記/取消標記要格式化的分割區(移除數據)" + +msgid "Mark/Unmark as bootable" +msgstr "標記/取消標記分割區為可引導" + +msgid "Change filesystem" +msgstr "更改文件系統" + +msgid "Mark/Unmark as compressed" +msgstr "將分割區標記/取消標記為壓縮" + +msgid "Set subvolumes" +msgstr "刪除子卷" + +msgid "Delete partition" +msgstr "刪除一個分割區" + +msgid "Partition" +msgstr "分割區" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "此分區當前已被加密,要將其格式化必須先指定文件系統" + +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * 分割區掛載點是相對於安裝内部的,例如 boot 應該為 /boot。" + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "如果設置了掛載點/boot,則該分區也將被標記為可引導。" + +msgid "Mountpoint: " +msgstr "掛載點:" + +msgid "Current free sectors on device {}:" +msgstr "目前 {} 設備上的可用區塊:" + +msgid "Total sectors: {}" +msgstr "總區塊數:{}" + +msgid "Enter the start sector (default: {}): " +msgstr "輸入起始區塊(默認:{}):" + +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "輸入分割區的结束區塊(以百分比或區塊編號表示,默認:{}):" + +msgid "This will remove all newly added partitions, continue?" +msgstr "這將刪除所有新增的分割區,是否繼續?" + +msgid "Partition management: {}" +msgstr "分割區管理:{}" + +msgid "Total length: {}" +msgstr "總長度:{}" + +msgid "Encryption type" +msgstr "加密類型" + +msgid "Partitions" +msgstr "分割區" + +msgid "No HSM devices available" +msgstr "沒有可用的 HSM 設備" + +msgid "Partitions to be encrypted" +msgstr "要加密的分割區:" + +msgid "Select disk encryption option" +msgstr "選擇硬盤加密選項" + +msgid "Select a FIDO2 device to use for HSM" +msgstr "選擇要用於 HSM 的 FIDO2 設備" + +msgid "Use a best-effort default partition layout" +msgstr "使用最佳的預設分割佈局" + +msgid "Manual Partitioning" +msgstr "手動分割" + +msgid "Pre-mounted configuration" +msgstr "預掛載配置" + +msgid "Unknown" +msgstr "未知" + +msgid "Partition encryption" +msgstr "分割區加密" + +msgid " ! Formatting {} in " +msgstr " ! 正在格式化 {} 為 " + +msgid "← Back" +msgstr "← 返回" + +msgid "Disk encryption" +msgstr "硬碟加密" + +msgid "Configuration" +msgstr "配置" + +msgid "Password" +msgstr "密碼" + +msgid "All settings will be reset, are you sure?" +msgstr "所有設置將被重新設置,你確定嗎?" + +msgid "Back" +msgstr "返回" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "請選擇依據配置文件安裝的登錄管理器:{}" + +msgid "Environment type: {}" +msgstr "環境類型:{}" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "Sway 不支持 Nvidia 的官方驅動。您可能會遇到一些問題,您確定要繼續嗎?" + +msgid "Installed packages" +msgstr "已安裝的套件" + +msgid "Add profile" +msgstr "新增配置文件" + +msgid "Edit profile" +msgstr "編輯配置文件" + +msgid "Delete profile" +msgstr "刪除配置文件" + +msgid "Profile name: " +msgstr "配置文件名稱:" + +msgid "The profile name you entered is already in use. Try again" +msgstr "您輸入的配置文件名稱已被使用。請重試" + +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "編寫要安裝的套件(以空格分隔,留空以跳過):" + +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "編寫要啟用的附加服務(以空格分隔,留空以跳過):" + +msgid "Should this profile be enabled for installation?" +msgstr "是否啟用此配置文件進行安裝?" + +msgid "Create your own" +msgstr "創建自己的" + +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"選擇圖形驅動程式或留空以安裝開源驅動程式" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "Sway 需要訪問您的用戶環境(硬體設備的集合,例如鍵盤,滑鼠等)" + +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" +"\n" +"\n" +"選擇一個選項以提供 Sway 對您硬件的訪問權限" + +msgid "Graphics driver" +msgstr "圖形驅動程式" + +msgid "Greeter" +msgstr "登錄管理器" + +msgid "Please chose which greeter to install" +msgstr "請選擇要安裝的登錄管理器" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "這是預設的默認配置文件列表" + +msgid "Disk configuration" +msgstr "硬碟配置" + +msgid "Profiles" +msgstr "配置文件" + +msgid "Finding possible directories to save configuration files ..." +msgstr "正在查找可能用於保存配置文件的目錄 ..." + +msgid "Select directory (or directories) for saving configuration files" +msgstr "選擇用於保存配置文件的目錄(或多個目錄)" + +#, fuzzy +msgid "Mirrors" +msgstr "鏡像區域" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Mirror regions" +msgstr "鏡像區域" + +msgid "Custom mirrors" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "保存使用者配置" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "輸入要保存配置的目錄:" + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "保存配置" + +#, fuzzy +msgid "Add a custom mirror" +msgstr "新增一個使用者" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "輸入用戶名(留空以跳過):" + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "輸入用戶名(留空以跳過):" + +#, fuzzy +msgid "Select signature check option" +msgstr "選擇硬盤加密選項" + +#, fuzzy +msgid "Select signature option" +msgstr "選擇硬盤加密選項" -- cgit v1.2.3-70-g09d2 From 4da7d537a5e37106e2ea228532912a9508e2a1d9 Mon Sep 17 00:00:00 2001 From: Error504TimeOut <126692020+Error504TimeOut@users.noreply.github.com> Date: Mon, 5 Jun 2023 10:11:02 +0200 Subject: Updated German Translation (#1825) * Add files via upload * Fixed errors and completed German translation * fixed minor error --- archinstall/locales/ar/LC_MESSAGES/base.po | 51 +++ archinstall/locales/base.pot | 51 +++ archinstall/locales/cs/LC_MESSAGES/base.po | 60 +++ archinstall/locales/de/LC_MESSAGES/base.mo | Bin 23304 -> 37641 bytes archinstall/locales/de/LC_MESSAGES/base.po | 575 +++++++++++++------------- archinstall/locales/el/LC_MESSAGES/base.po | 60 +++ archinstall/locales/en/LC_MESSAGES/base.po | 50 +++ archinstall/locales/es/LC_MESSAGES/base.po | 60 +++ archinstall/locales/fr/LC_MESSAGES/base.po | 60 +++ archinstall/locales/id/LC_MESSAGES/base.po | 60 +++ archinstall/locales/it/LC_MESSAGES/base.po | 60 +++ archinstall/locales/ka/LC_MESSAGES/base.po | 60 +++ archinstall/locales/ko/LC_MESSAGES/base.po | 60 +++ archinstall/locales/nl/LC_MESSAGES/base.po | 60 +++ archinstall/locales/pl/LC_MESSAGES/base.po | 60 +++ archinstall/locales/pt/LC_MESSAGES/base.po | 60 +++ archinstall/locales/pt_BR/LC_MESSAGES/base.po | 60 +++ archinstall/locales/ru/LC_MESSAGES/base.po | 60 +++ archinstall/locales/sv/LC_MESSAGES/base.po | 60 +++ archinstall/locales/ta/LC_MESSAGES/base.po | 60 +++ archinstall/locales/tr/LC_MESSAGES/base.po | 60 +++ archinstall/locales/uk/LC_MESSAGES/base.po | 60 +++ archinstall/locales/ur/LC_MESSAGES/base.po | 60 +++ archinstall/locales/zh-CN/LC_MESSAGES/base.po | 60 +++ 24 files changed, 1582 insertions(+), 285 deletions(-) diff --git a/archinstall/locales/ar/LC_MESSAGES/base.po b/archinstall/locales/ar/LC_MESSAGES/base.po index 9c3a6bff..d6d07b06 100644 --- a/archinstall/locales/ar/LC_MESSAGES/base.po +++ b/archinstall/locales/ar/LC_MESSAGES/base.po @@ -1027,3 +1027,54 @@ msgstr "" #, fuzzy msgid "Select directory (or directories) for saving configuration files" msgstr "حدِّد واجهة شبكة واحدة للإعداد" + +msgid "Add a custom mirror" +msgstr "" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +msgid "Enter name (leave blank to skip): " +msgstr "" + +msgid "Enter url (leave blank to skip): " +msgstr "" + +msgid "Select signature check option" +msgstr "" + +#, fuzzy +msgid "Select signature option" +msgstr "حدِّد منطقة زمنية" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +msgid "Save user configuration (including disk layout)" +msgstr "" + +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "" + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +msgid "Saving {} configuration files to {}" +msgstr "" + +msgid "Mirrors" +msgstr "" + +msgid "Mirror regions" +msgstr "" diff --git a/archinstall/locales/base.pot b/archinstall/locales/base.pot index 009dc382..2ca84604 100644 --- a/archinstall/locales/base.pot +++ b/archinstall/locales/base.pot @@ -1095,3 +1095,54 @@ msgstr "" msgid "Select directory (or directories) for saving configuration files" msgstr "" + +msgid "Add a custom mirror" +msgstr "" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +msgid "Enter name (leave blank to skip): " +msgstr "" + +msgid "Enter url (leave blank to skip): " +msgstr "" + +msgid "Select signature check option" +msgstr "" + +msgid "Select signature option" +msgstr "" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +msgid "Save user configuration (including disk layout)" +msgstr "" + +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion " +"enabled)\n" +"Save directory: " +msgstr "" + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +msgid "Saving {} configuration files to {}" +msgstr "" + +msgid "Mirrors" +msgstr "" + +msgid "Mirror regions" +msgstr "" diff --git a/archinstall/locales/cs/LC_MESSAGES/base.po b/archinstall/locales/cs/LC_MESSAGES/base.po index 76c885aa..cf6bd3a1 100644 --- a/archinstall/locales/cs/LC_MESSAGES/base.po +++ b/archinstall/locales/cs/LC_MESSAGES/base.po @@ -1103,3 +1103,63 @@ msgstr "" #, fuzzy msgid "Select directory (or directories) for saving configuration files" msgstr "Zvolte jeden nebo více pevných disků k použití a konfiguraci" + +#, fuzzy +msgid "Add a custom mirror" +msgstr "Přidat uživatele" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "Zadejte uživatelské jméno (ponechte prázdné k přeskočení): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "Zadejte uživatelské jméno (ponechte prázdné k přeskočení): " + +#, fuzzy +msgid "Select signature check option" +msgstr "Zvolit rozhraní k přidání" + +#, fuzzy +msgid "Select signature option" +msgstr "Zvolit rozhraní k přidání" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "Uložit uživatelskou konfiguraci" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "Zadejte adresář pro uložení konfigurace (konfigurací): " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "Uložit konfiguraci" + +#, fuzzy +msgid "Mirrors" +msgstr "Oblast zrcadla" + +#, fuzzy +msgid "Mirror regions" +msgstr "Oblast zrcadla" diff --git a/archinstall/locales/de/LC_MESSAGES/base.mo b/archinstall/locales/de/LC_MESSAGES/base.mo index 0572f28b..988eeb38 100644 Binary files a/archinstall/locales/de/LC_MESSAGES/base.mo and b/archinstall/locales/de/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/de/LC_MESSAGES/base.po b/archinstall/locales/de/LC_MESSAGES/base.po index e1d01ab8..c1f32f0f 100644 --- a/archinstall/locales/de/LC_MESSAGES/base.po +++ b/archinstall/locales/de/LC_MESSAGES/base.po @@ -9,76 +9,76 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.0.1\n" +"X-Generator: Poedit 3.3.1\n" msgid "[!] A log file has been created here: {} {}" -msgstr "[!] Eine Logdatei wurde erstellt: {} {}" +msgstr "[!] Eine Logdatei wurde hier erstellt: {} {}" msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" -msgstr "Bitte melden sie das Problem mit der erstellten Datei auf https://github.com/archlinux/archinstall/issues" +msgstr "Bitte melden Sie das Problem mit der erstellten Datei auf https://github.com/archlinux/archinstall/issues" msgid "Do you really want to abort?" msgstr "Wollen Sie wirklich abbrechen?" msgid "And one more time for verification: " -msgstr "Und nocheinmal zur Besätigung: " +msgstr "Und noch einmal zur Bestätigung: " msgid "Would you like to use swap on zram?" -msgstr "Möchten Sie swap mit zram verwenden?" +msgstr "Möchten Sie Swap mit zram verwenden?" msgid "Desired hostname for the installation: " -msgstr "Gewnüschter Hostname für die Installation: " +msgstr "Gewünschter Gerätename/Hostname für die Installation: " msgid "Username for required superuser with sudo privileges: " -msgstr "Benutzername für den erforderlichen superuser mit sudo Rechten: " +msgstr "Benutzername für den erforderlichen Superuser mit sudo Rechten: " msgid "Any additional users to install (leave blank for no users): " -msgstr "Geben Sie weitere Benutzernamen ein die installiert werden sollen (leer lassen für keine weiteren Benutzer): " +msgstr "Geben Sie weitere Benutzernamen ein, die installiert werden sollen (leer lassen für keine weiteren Benutzer): " msgid "Should this user be a superuser (sudoer)?" -msgstr "Soll dieser Benutzer ein superuser sein (sudoer)?" +msgstr "Soll dieser Benutzer ein Superuser sein (sudoer)?" msgid "Select a timezone" msgstr "Bitte wählen Sie eine Zeitzone aus" msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?" -msgstr "Möchten Sie GRUB als bootloader anstelle von system-boot verwenden?" +msgstr "Möchten Sie GRUB als Bootloader anstelle von systemd-boot verwenden?" msgid "Choose a bootloader" -msgstr "Bitte wählen Sie einen bootloader aus" +msgstr "Bitte wählen Sie einen Bootloader aus" msgid "Choose an audio server" -msgstr "Bitte wählen Sie einen Audio server aus" +msgstr "Bitte wählen Sie einen Audioserver aus" msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." -msgstr "Nur die Packete base, base-devel, linux, linux-firmware, efibootmgr und optionale Profilpackete werden installiert" +msgstr "Nur Pakete wie base, base-devel, linux, linux-firmware, efibootmgr und optionale Profilpakete werden installiert" msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." msgstr "Wenn Sie einen Webbrowser, z.B. Firefox oder Chromium, installieren möchten, können Sie diese nun eingeben." msgid "Write additional packages to install (space separated, leave blank to skip): " -msgstr "Schreiben Sie zusätzliche Packete die installiert werden sollen mit einem Leerzeichen getrennt (zum Überspringen leer lassen): " +msgstr "Schreiben Sie zusätzliche Pakete die installiert werden sollen (mit einem Leerzeichen getrennt, zum Überspringen leer lassen): " msgid "Copy ISO network configuration to installation" -msgstr "ISO netzwerk Einstellungen in die Installation kopieren" +msgstr "ISO-Netzwerk Einstellungen in die Installation kopieren" msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" -msgstr "NetworkManager benutzen (notwendig um Internet auf graphische Weise in GNOME und KDE einzustellen)" +msgstr "NetworkManager benutzen (notwendig um Internet auf grafische Weise in GNOME und KDE einzustellen)" msgid "Select one network interface to configure" -msgstr "Bitte wählen Sie ein netzwerk zur Konfiguration aus" +msgstr "Bitte wählen Sie ein Netzwerk zur Konfiguration aus" msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" msgstr "Bitte wählen Sie einen Modus zur Konfiguration von \"{}\" aus oder Überspringen um mit dem voreingestellten Modus \"{}\" fortzufahren" msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " -msgstr "Bitte geben Sie eine IP Adresse und ein Subnet für {} ein (z.B. 192.168.0.5/24)" +msgstr "Bitte geben Sie eine IP Adresse und ein Subnet für {} ein (z.B. 192.168.0.5/24):" msgid "Enter your gateway (router) IP address or leave blank for none: " -msgstr "Bitte geben Sie eine gateway (router) IP Adresse ein (leer lassen für kein Adresse): " +msgstr "Bitte geben Sie eine Gateway (Router) IP Adresse ein (leer lassen für kein Adresse): " msgid "Enter your DNS servers (space separated, blank for none): " -msgstr "Bitte geben Sie die DNS server ein (mit Leerzeichen getrennt oder leer lassen für keinen server): " +msgstr "Bitte geben Sie die DNS-Server ein (mit Leerzeichen getrennt oder leer lassen für keinen Server): " msgid "Select which filesystem your main partition should use" msgstr "Bitte wählen Sie ein Dateisystem aus, welches für die Hauptpartition verwendet werden soll" @@ -94,7 +94,7 @@ msgstr "" "{}" msgid "Enter a desired filesystem type for the partition" -msgstr "Bitte wählen Sie einen Dateisystemtyp für die Partition aus" +msgstr "Bitte wählen Sie einen gewünschten Dateisystemtyp für die Partition aus" msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " msgstr "Bitte geben Sie die Startposition ein (in parted Einheiten: s, GB, %, etc. ; default: {}): " @@ -103,7 +103,7 @@ msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " msgstr "Bitte geben Sie die Endposition ein (in parted Einheiten: s, GB, %, etc. ; default: {}): " msgid "{} contains queued partitions, this will remove those, are you sure?" -msgstr "{} enthält Partitionen in der Warteschlange, dies werden damit entfernt, sind sie sicher?" +msgstr "{} enthält Partitionen in der Warteschlange, diese werden damit entfernt, sind Sie sicher?" msgid "" "{}\n" @@ -112,7 +112,7 @@ msgid "" msgstr "" "{}\n" "\n" -"Wählen sie anhand vom index welche Partitionen gelöscht werden sollen" +"Wählen Sie anhand vom Index aus, welche Partitionen gelöscht werden sollen" msgid "" "{}\n" @@ -121,13 +121,13 @@ msgid "" msgstr "" "{}\n" "\n" -"Wählen sie anhand vom index welche Partitionen zu mounten" +"Wählen Sie anhand vom Index aus, welche Partitionen wo eingehängt werden sollen" msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * Die Mountorte sind relativ zur Installation, zum Beispiel boot würde gemountet auf /boot" +msgstr " * Die Einhängeorte sind relativ zur Installation, zum Beispiel boot würde gemountet auf /boot" msgid "Select where to mount partition (leave blank to remove mountpoint): " -msgstr "Bitte geben sie an wo die Partition gemounted werden soll (leer lassen um den Mountort zu entfernen): " +msgstr "Bitte geben Sie an wo die Partition eingehängt werden soll (leer lassen um den Einhängeort zu entfernen): " msgid "" "{}\n" @@ -136,7 +136,7 @@ msgid "" msgstr "" "{}\n" "\n" -"Bitte wählen sie welche Partition formatiert werden soll" +"Bitte wählen Sie aus, welche Partition formatiert werden soll" msgid "" "{}\n" @@ -145,7 +145,7 @@ msgid "" msgstr "" "{}\n" "\n" -"Bitte wählen sie welche Partition verschlüsselt werden soll" +"Bitte wählen Sie aus, welche Partition verschlüsselt werden soll" msgid "" "{}\n" @@ -154,7 +154,7 @@ msgid "" msgstr "" "{}\n" "\n" -"Bitte wählen sie welche Partition bootbar ist" +"Bitte wählen Sie aus, welche Partition bootbar ist" msgid "" "{}\n" @@ -163,43 +163,43 @@ msgid "" msgstr "" "{}\n" "\n" -"Bitte wählen sie auf welche Partition ein Dateisystem eingerichtet werden soll" +"Bitte wählen Sie aus, auf welcher Partition ein Dateisystem eingerichtet werden soll" msgid "Enter a desired filesystem type for the partition: " -msgstr "Bitte geben sie einen gewünschten Dateisystemtyp für die Partition ein: " +msgstr "Bitte geben Sie einen gewünschten Dateisystemtyp für die Partition ein: " msgid "Archinstall language" msgstr "Sprache für Archinstall" msgid "Wipe all selected drives and use a best-effort default partition layout" -msgstr "Alle Laufwerke löschen und ein vorgegebenes Partitionenlayout verwenden" +msgstr "Alle Laufwerke löschen und ein vorgegebenes Partitionslayout verwenden" msgid "Select what to do with each individual drive (followed by partition usage)" -msgstr "Bitte geben sie an was mit jedem individuellem Laufwerk geschehen soll" +msgstr "Bitte geben Sie individuell an, was mit jedem Laufwerk geschehen soll" msgid "Select what you wish to do with the selected block devices" -msgstr "Bitte wählen sie was mit dem ausgewählten Gerät geschehen soll" +msgstr "Bitte wählen Sie was mit den ausgewählten Geräten machen wollen" msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments" -msgstr "Dies ist eine Liste von bereits programmierten Profilen, diese ermöglichen es einfacher Desktop Umgebungen einzustellen" +msgstr "Dies ist eine Liste von bereits programmierten Profilen, diese ermöglichen einfacher Dinge wie Desktop Umgebungen zu installieren" msgid "Select keyboard layout" -msgstr "Bitte wählen sie ein Tastaturlayout aus" +msgstr "Bitte wählen Sie ein Tastaturlayout aus" msgid "Select one of the regions to download packages from" -msgstr "Bitte wählen sie eine Region zum downloaden von Packeten aus" +msgstr "Bitte wählen Sie eine Region zum herunterladen von Paketen aus" msgid "Select one or more hard drives to use and configure" -msgstr "Bitte wählen sie eine oder mehrere Laufwerke aus die konfiguriert werden sollen" +msgstr "Bitte wählen Sie ein oder mehrere Laufwerk(e) aus, die konfiguriert und verwendet werden sollen" msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." -msgstr "Für die beste kompabilität mit ihrer AMD hardware, sollten sie womöglich die open-source oder AMD / ATI optionen verwenden" +msgstr "Für die beste Kompatibilität mit ihrer AMD Hardware, sollten Sie womöglich die Quelloffenen oder AMD / ATI Optionen verwenden." msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" -msgstr "Für die beste kompabilität mit ihrer Intel hardware, sollten sie womöglich die open-source oder Intel optionen verwenden.\n" +msgstr "Für die beste Kompatibilität mit ihrer Intel Hardware, sollten Sie womöglich die Quelloffenen oder Intel Optionen verwenden.\n" msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" -msgstr "Für die beste kompabilität mit ihrer Nvidia hardware, sollten sie womöglich die Nvidia proprietary driver option verwenden.\n" +msgstr "Für die beste Kompatibilität mit ihrer Nvidia Hardware, sollten Sie womöglich den proprietären Nvidia-Treiber verwenden.\n" msgid "" "\n" @@ -208,43 +208,43 @@ msgid "" msgstr "" "\n" "\n" -"Bitte wählen sie einen Grafiktreiber aus oder leer lassen um alle open-source Treiber zu installieren" +"Bitte wählen Sie einen Grafikkartentreiber aus oder leer lassen um alle Quelloffenen Treiber zu installieren" msgid "All open-source (default)" -msgstr "Alle open-source (default)" +msgstr "Alle Quelloffene (Standard)" msgid "Choose which kernels to use or leave blank for default \"{}\"" -msgstr "Bitte wählen sie welche Kernel benutzt werden sollen oder leer lassen für default \"{}\"" +msgstr "Bitte wählen Sie welche Kernel benutzt werden sollen oder leer lassen für Standard \"{}\"" msgid "Choose which locale language to use" -msgstr "Bitte wählen sie eine lokale Sprache aus" +msgstr "Bitte wählen Sie aus, welche lokale Sprache verwendet werden soll" msgid "Choose which locale encoding to use" -msgstr "Bitte wählen sie eine lokale Kodierung aus" +msgstr "Bitte wählen Sie aus, welche lokale Kodierung verwendet werden soll" msgid "Select one of the values shown below: " -msgstr "Bitte wählen sie einen der folgenden Werte aus:" +msgstr "Bitte wählen Sie einen der folgenden Werte aus: " msgid "Select one or more of the options below: " -msgstr "Bitte wählen sie eine oder mehrere Optionen aus: " +msgstr "Bitte wählen Sie eine oder mehrere Option(en) aus: " msgid "Adding partition...." -msgstr "Partitionen werden hinzugefügt..." +msgstr "Partition wird hinzugefügt..." msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." -msgstr "Bitte geben sie einen gültigen Dateisystemtyp ein um fortzufahren. Wenden sie sich an \"man parted\" für eine Liste von gültigen Typen." +msgstr "Bitte geben Sie einen gültigen Dateisystemtyp ein um fortzufahren. Verwenden Sie `man parted` um eine Liste von gültigen Typen einzusehen." msgid "Error: Listing profiles on URL \"{}\" resulted in:" -msgstr "Fehler: Auflistung von Profilen mit URL \"{}\":" +msgstr "Fehler: Auflistung von Profilen mit URL \"{}\" resultierte in:" msgid "Error: Could not decode \"{}\" result as JSON:" -msgstr "Fehler: \"{}\" konnte nicht in ein JSON format dekodiert werden:" +msgstr "Fehler: \"{}\" konnte nicht in als JSON dekodiert werden:" msgid "Keyboard layout" msgstr "Tastaturlayout" msgid "Mirror region" -msgstr "Mirror-region" +msgstr "Spiegelregion" msgid "Locale language" msgstr "Lokale Sprache" @@ -253,15 +253,13 @@ msgid "Locale encoding" msgstr "Lokale Kodierung" msgid "Drive(s)" -msgstr "Laufwerke" +msgstr "Laufwerk(e)" -#, fuzzy msgid "Disk layout" -msgstr "Laufwerke-layout speichern" +msgstr "Laufwerkslayout" -#, fuzzy msgid "Encryption password" -msgstr "Verschlüsselungspasswort angeben" +msgstr "Verschlüsselungspasswort" msgid "Swap" msgstr "Swap" @@ -269,7 +267,6 @@ msgstr "Swap" msgid "Bootloader" msgstr "Bootloader" -#, fuzzy msgid "Root password" msgstr "Root Passwort" @@ -298,7 +295,7 @@ msgid "Automatic time sync (NTP)" msgstr "Autom. Zeitsynchronisierung (NTP)" msgid "Install ({} config(s) missing)" -msgstr "Installieren ({} konfiguration(en) ausständig)" +msgstr "Installieren ({} Konfiguration(en) ausständig)" msgid "" "You decided to skip harddrive selection\n" @@ -307,9 +304,9 @@ msgid "" "Do you wish to continue?" msgstr "" "Sie haben sich entschieden keine Laufwerke auszuwählen\n" -"und jene Einstellungen zu verwenden welche momentan auf {} verfügbar sind (experimentell)\n" +"und jene Einstellungen zu verwenden welche momentan auf {} eingehängt sind (experimentell)\n" "WARNUNG: Archinstall wird die Kompabilität der Einstellung nicht überprüfen\n" -"Wollen sie trotzdem fortfahren?" +"Wollen Sie trotzdem fortfahren?" msgid "Re-using partition instance: {}" msgstr "Wiederverwenden der Partitionsinstanz: {}" @@ -324,10 +321,10 @@ msgid "Clear/Delete all partitions" msgstr "Alle partitionen löschen" msgid "Assign mount-point for a partition" -msgstr "Mountort für Partition angeben" +msgstr "Einhängeort für Partition angeben" msgid "Mark/Unmark a partition to be formatted (wipes data)" -msgstr "Markieren welche Partition formattiert werden soll (alle Daten werden gelöscht)" +msgstr "Markieren welche Partition formatiert werden soll (alle Daten werden gelöscht)" msgid "Mark/Unmark a partition as encrypted" msgstr "Markieren welche Partitionen verschlüsselt werden sollen" @@ -342,16 +339,16 @@ msgid "Abort" msgstr "Abbrechen" msgid "Hostname" -msgstr "Hostnamen" +msgstr "Gerätename/Hostname" msgid "Not configured, unavailable unless setup manually" -msgstr "Nicht konfiguriert, unverfügbar wenn nicht selber eingestellt" +msgstr "Nicht konfiguriert, nicht verfügbar wenn nicht selber eingestellt" msgid "Timezone" msgstr "Zeitzone" msgid "Set/Modify the below options" -msgstr "Setzen sie die unten stehenden Einstellungen" +msgstr "Setzen/Modifizieren Sie die unten stehenden Einstellungen" msgid "Install" msgstr "Installieren" @@ -373,32 +370,32 @@ msgid "Enter a encryption password for {}" msgstr "Verschlüsselungspasswort angeben für {}" msgid "Enter disk encryption password (leave blank for no encryption): " -msgstr "Geben sie ein Verschlüsselungspasswort ein (leer lassen um zu Überspringen): " +msgstr "Geben Sie ein Verschlüsselungspasswort ein (leer lassen um zu Verschlüsselung zu deaktivieren): " msgid "Create a required super-user with sudo privileges: " -msgstr "Geben sie einen super-user mit sudo Privilegien an: " +msgstr "Geben Sie einen Superuser mit sudo Privilegien an: " msgid "Enter root password (leave blank to disable root): " -msgstr "Geben sie ein Root passwort ein (leer lassen um Root zu deaktivieren): " +msgstr "Geben sie ein Root Passwort ein (leer lassen um Root zu deaktivieren): " msgid "Password for user \"{}\": " msgstr "Passwort für Benutzer \"{}\": " msgid "Verifying that additional packages exist (this might take a few seconds)" -msgstr "Angegebene Packete werden verifiziert (dies könnte einige Sekunden dauern)" +msgstr "Angegebene Pakete werden verifiziert (dies könnte einige Sekunden dauern)" msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" -msgstr "Möchten sie automatische Zeitsynchronisierung mit dem default Server einschalten?\n" +msgstr "Möchten Sie die automatische Zeitsynchronisierung (NTP) mit dem Standard Server verwenden?\n" msgid "" "Hardware time and other post-configuration steps might be required in order for NTP to work.\n" "For more information, please check the Arch wiki" msgstr "" -"Hardware Zeit und andere Einstellungsschritte könnten notwendig sein um NTP zu benutzen.\n" -"Für weitere Informationen wenden sie sich bitte an das Arch wiki" +"Hardwarezeit und andere Einstellungsschritte könnten notwendig sein um NTP zu benutzen.\n" +"Für weitere Informationen wenden Sie sich bitte an das Arch wiki" msgid "Enter a username to create an additional user (leave blank to skip): " -msgstr "Geben sie einen weiteren Benutzernamen an der angelegt werden soll (leer lassen um zu Überspringen): " +msgstr "Geben Sie einen weiteren Benutzernamen an, um diesen anzulegen (leer lassen um zu Überspringen): " msgid "Use ESC to skip\n" msgstr "ESC um zu Überspringen\n" @@ -408,7 +405,7 @@ msgid "" " Choose an object from the list, and select one of the available actions for it to execute" msgstr "" "\n" -" Wählen sie ein Object aus der Liste aus und wählen sie anschließend eine Aktion dafür aus " +" Wählen Sie ein Objekt aus der Liste aus und wählen Sie anschließend eine Aktion zum ausführen dafür aus" msgid "Cancel" msgstr "Abbrechen" @@ -429,10 +426,10 @@ msgid "Delete" msgstr "Löschen" msgid "Select an action for '{}'" -msgstr "Wählen sie eine Aktion aus für '{}'" +msgstr "Wählen Sie eine Aktion aus für '{}'" msgid "Copy to new key:" -msgstr "Kopieren nach neuem Schlüssel:" +msgstr "Kopieren zum neuen Schlüssel:" msgid "Unknown nic type: {}. Possible values are {}" msgstr "Nicht erkannter Netzwerinterfacecontroller: {}. Erlaubte Werte {}" @@ -442,16 +439,16 @@ msgid "" "This is your chosen configuration:" msgstr "" "\n" -"Das ist ihre gewählte Konfiguration:" +"Dies ist ihre gewählte Konfiguration:" msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." -msgstr "Pacman läuft bereits, warten für maximal 10min auf Beendigung." +msgstr "Pacman läuft bereits, warten für maximal 10 Minuten auf Beendigung." msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." -msgstr "Existierendes Pacman lock wurde nicht beendet. Bitte beenden sie existierende Pacman Sessions um archinstall benützen zu können." +msgstr "Existierendes Pacman lock wurde nicht beendet. Bitte beenden Sie existierende Pacman Sessions bevor archinstall benutzt wird." msgid "Choose which optional additional repositories to enable" -msgstr "Wählen sie welche zusätzlichen Repositories eingeschaltet werden sollen" +msgstr "Wählen Sie aus, welche zusätzlichen Repositories verwendet werden sollen" msgid "Add a user" msgstr "Benutzerkonto hinzufügen" @@ -479,7 +476,7 @@ msgid "Should {} be a superuser (sudoer)?" msgstr "Soll {} ein superuser sein (sudoer)?" msgid "Define users with sudo privilege: " -msgstr "Geben sie super-user mit sudo Privilegien an: " +msgstr "Geben Sie Superuser mit sudo Privilegien an: " msgid "No network configuration" msgstr "Keine Netzwerkkonfiguration" @@ -494,7 +491,7 @@ msgid "" msgstr "" "{}\n" "\n" -"Bitte wählen sie auf welcher Partition subvolumes eingerichtet werden sollen" +"Bitte wählen Sie, auf welcher Partition Subvolumes eingerichtet werden sollen" msgid "Manage btrfs subvolumes for current partition" msgstr "Bearbeiten von Btrfs subvolumes für die aktuelle Partition" @@ -509,25 +506,25 @@ msgid "Save user credentials" msgstr "Benutzeranmeldedaten speichern" msgid "Save disk layout" -msgstr "Laufwerke-layout speichern" +msgstr "Laufwerklayout speichern" msgid "Save all" -msgstr "Alle speichern" +msgstr "Alles speichern" msgid "Choose which configuration to save" -msgstr "Bitte wählen sie eine Konfiguration aus welche gespeichert werden soll" +msgstr "Bitte wählen Sie eine Konfiguration aus, welche gespeichert werden soll" msgid "Enter a directory for the configuration(s) to be saved: " -msgstr "Geben sie eine Ordner an wo die Konfigurationen gespeichert werden sollen: " +msgstr "Geben Sie einen Ordner an, in dem Konfigurationen gespeichert werden sollen: " msgid "Not a valid directory: {}" msgstr "Ordner existiert nicht: {}" msgid "The password you are using seems to be weak," -msgstr "Das gewählte Passwort ist sehr schwach," +msgstr "Das gewählte Passwort ist schwach," msgid "are you sure you want to use it?" -msgstr "wollen sie dieses wirklich verwenden?" +msgstr "wollen Sie dieses Passwort wirklich verwenden?" msgid "Optional repositories" msgstr "Zus. Repositories" @@ -539,7 +536,7 @@ msgid "Missing configurations:\n" msgstr "Ausständige Konfigurationen:\n" msgid "Either root-password or at least 1 superuser must be specified" -msgstr "Entweder root Passwort oder wenigstens 1 super-user muss konfiguriert sein" +msgstr "Entweder root Passwort oder mindestens ein Superuser muss konfiguriert sein" msgid "Manage superuser accounts: " msgstr "Superuser Konto bearbeiten: " @@ -551,53 +548,53 @@ msgid " Subvolume :{:16}" msgstr " Subvolume :{:16}" msgid " mounted at {:16}" -msgstr " Mounted an {:16}" +msgstr " Eingehängt bei {:16}" msgid " with option {}" -msgstr "mit option {}" +msgstr "mit Option {}" msgid "" "\n" " Fill the desired values for a new subvolume \n" msgstr "" "\n" -" Geben sie die gewünschten Werte für ein neues Subvolumen an \n" +" Geben Sie die gewünschten Werte für ein neues Subvolume an \n" msgid "Subvolume name " -msgstr "Subvolumen name" +msgstr "Subvolume name " msgid "Subvolume mountpoint" -msgstr "Subvolumen mountpunkt" +msgstr "Subvolume Einhängepunkt" msgid "Subvolume options" -msgstr "Subvolumen Optionen" +msgstr "Subvolume Optionen" msgid "Save" msgstr "Speichern" msgid "Subvolume name :" -msgstr "Subvolumen name :" +msgstr "Subvolume name :" msgid "Select a mount point :" -msgstr "Bitte wählen Sie einen Mountpunkt aus" +msgstr "Bitte wählen Sie einen Einhängepunkt aus :" msgid "Select the desired subvolume options " -msgstr "Wählen sie die gewünschte Subvolumen Optionen" +msgstr "Wählen Sie die gewünschten Subvolumen Optionen" msgid "Define users with sudo privilege, by username: " -msgstr "Geben sie Benutzer mit sudo Privilegien an: " +msgstr "Geben Sie Benutzer mit sudo Privilegien an: " msgid "[!] A log file has been created here: {}" -msgstr "[!] Eine Logdatei wurde erstellt: {}" +msgstr "[!] Eine Logdatei wurde hier erstellt: {}" msgid "Would you like to use BTRFS subvolumes with a default structure?" -msgstr "Möchten sie Btrfs Subvolumen mit vorgebener Struktur?" +msgstr "Möchten Sie btrfs Subvolumen mit vorgebener Struktur verwenden?" msgid "Would you like to use BTRFS compression?" -msgstr "Möchten sie Btrfs kompression?" +msgstr "Möchten sie btrfs Komprimierung verwenden?" msgid "Would you like to create a separate partition for /home?" -msgstr "Möchten sie eine separate Partition für /home?" +msgstr "Möchten Sie eine separate Partition für /home erstellen?" msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" msgstr "Die ausgewählten Laufwerke haben nicht genug Speicherplatz für eine automatische Vorgabe\n" @@ -612,10 +609,10 @@ msgid "Continue" msgstr "Weiter" msgid "yes" -msgstr "ja" +msgstr "Ja" msgid "no" -msgstr "nein" +msgstr "Nein" msgid "set: {}" msgstr "gewählt: {}" @@ -624,10 +621,10 @@ msgid "Manual configuration setting must be a list" msgstr "Manuelle Konfiguration muss eine Liste sein" msgid "No iface specified for manual configuration" -msgstr "Kein Verbindung angegeben für eine manuelle Konfiguration" +msgstr "Keine Verbindung angegeben für eine manuelle Konfiguration" msgid "Manual nic configuration with no auto DHCP requires an IP address" -msgstr "Manuelle Konfiguration für Netzwerverbindung mit keinem auto DHCP benötigt eien IP Addresse" +msgstr "Manuelle Konfiguration für Netzwerverbindung mit keinem auto DHCP benötigt eine IP Addresse" msgid "Add interface" msgstr "Verbindung hinzufügen" @@ -639,69 +636,68 @@ msgid "Delete interface" msgstr "Verbindung löschen" msgid "Select interface to add" -msgstr "Wählen sie eine Verbindung welche hinzugefügt werden soll" +msgstr "Wählen Sie eine Verbindung aus, welche hinzugefügt werden soll" msgid "Manual configuration" -msgstr "Manuelle konfiguration" +msgstr "Manuelle Konfiguration" msgid "Mark/Unmark a partition as compressed (btrfs only)" -msgstr "Markieren/Unmarkieren Kompression von einer Partition (nur Btrfs) " +msgstr "Partition als komprimiert makieren bzw. nicht komprimiert makieren (nur btrfs)" msgid "The password you are using seems to be weak, are you sure you want to use it?" -msgstr "Das gewählte Passwort ist schwach, möchten sie trotzdem fortfahren?" +msgstr "Das gewählte Passwort ist schwach, möchten Sie trotzdem fortfahren?" msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" -msgstr "Auswahl von Desktopumgebungen und tiling window managern, z.B. gnome, kde, sway" +msgstr "Auswahl von Desktopumgebungen und tiling Fenstermanagern, z.B. gnome, kde, sway" msgid "Select your desired desktop environment" -msgstr "Wählen sie eine Desktopumgebung aus" +msgstr "Wählen Sie ihre gewünschte Desktopumgebung aus" msgid "A very basic installation that allows you to customize Arch Linux as you see fit." -msgstr "Eine sehr minimale Installation welche es erlaubt Arch Linux weitgehend anzupassen" +msgstr "Eine sehr minimale Installation, welche es erlaubt Arch Linux selber nach eigenen Wünschen anzupassen." msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" -msgstr "Auswahl von Serverpaketen welche installiert werden sollen, z.B. httpd, nginx, mariadb" +msgstr "Auswahl von Serverpaketen welche installiert und aktiviert werden sollen, z.B. httpd, nginx, mariadb" -#, fuzzy msgid "Choose which servers to install, if none then a minimal installation will be done" -msgstr "Wählen sie die gewünschten Server aus welche installiert werden sollen" +msgstr "Wählen Sie die gewünschten Server aus, welche installiert werden sollen. Sonst wird eine Minimalinstallation durchgeführt" msgid "Installs a minimal system as well as xorg and graphics drivers." -msgstr "Installiert ein minimales System inklusive xorg und Graphiktreibern" +msgstr "Installiert ein minimales System inklusive Xorg und Grafiktreibern" msgid "Press Enter to continue." msgstr "Drücken sie Enter um fortzufahren." msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?" -msgstr "Möchten sie chroot in das neu installierte System um noch weitere manuelle Konfigurationen forzunehmen?" +msgstr "Möchten Sie in das neu installierte System über chroot zugreifen um noch weitere, manuelle Konfigurationen vorzunehmen?" msgid "Are you sure you want to reset this setting?" msgstr "Wollen Sie wirklich diese Konfiguration zurücksetzen?" msgid "Select one or more hard drives to use and configure\n" -msgstr "Bitte wählen sie eine oder mehrere Laufwerke aus die konfiguriert werden sollen\n" +msgstr "Bitte wählen Sie ein oder mehrere Laufwerk(e) aus, die konfiguriert und verwendet werden sollen\n" msgid "Any modifications to the existing setting will reset the disk layout!" msgstr "Modifikationen zur momentanen Konfiguration führen zu einem Löschen der Laufwerkskonfiguration!" msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" -msgstr "Wenn sie die Laufwerkkonfiguration ändern, dann wird die Laufwer-layout zurückgesetzt. Sind sie sicher?" +msgstr "Wenn Sie die Laufwerkskonfiguration ändern, dann wird das Laufwerkslayout zurückgesetzt. Sind Sie sicher?" msgid "Save and exit" -msgstr "Speichern und zurück" +msgstr "Speichern und Beenden" msgid "" "{}\n" "contains queued partitions, this will remove those, are you sure?" msgstr "" "{}\n" -"enthält Partitionen in der Warteschlange, dies werden damit entfernt, sind sie sicher?" +"enthält Partitionen in der Warteschlange, diese werden damit entfernt, sind Sie sicher?" msgid "No audio server" -msgstr "Kein Audio server" +msgstr "Kein Audioserver" msgid "(default)" -msgstr "(vorgegeben)" +msgstr "(Standard)" msgid "Use ESC to skip" msgstr "ESC drücken um zu überspringen" @@ -710,7 +706,7 @@ msgid "" "Use CTRL+C to reset current selection\n" "\n" msgstr "" -"Ctrl+C drücken um die Einstellungen zurückzusetzen\n" +"Strg+C drücken um die aktuelle Auswahl zurückzusetzen\n" "\n" msgid "Copy to: " @@ -726,87 +722,76 @@ msgid "Edit {}: " msgstr "Bearbeiten {}: " msgid "Add: " -msgstr "" +msgstr "Hinzufügen: " -#, fuzzy msgid "Value: " -msgstr "Wert :" +msgstr "Wert: " msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" -msgstr "" +msgstr "Sie können das Auswählen eines Laufwerks überspringen und irgendeine Laufwerkskonfiguration verwenden, die bei /mnt eingehängt ist (experimentell)" msgid "Select one of the disks or skip and use /mnt as default" -msgstr "" +msgstr "Wählen Sie eine der Laufwerke aus oder überspringen Sie und verwenden Sie /mnt als Standard" -#, fuzzy msgid "Select which partitions to mark for formatting:" -msgstr "" -"{}\n" -"\n" -"Bitte wählen sie welche Partition formatiert werden soll" +msgstr "Bitte wählen Sie aus, welche Partitionen formatiert werden sollen:" msgid "Use HSM to unlock encrypted drive" -msgstr "" +msgstr "HSM verwenden, um verschlüsselte Platte zu entsperren" msgid "Device" -msgstr "" +msgstr "Gerät" msgid "Size" -msgstr "" +msgstr "Größe" msgid "Free space" -msgstr "" +msgstr "Freier Speicherplatz" msgid "Bus-type" -msgstr "" +msgstr "Bustyp" -#, fuzzy msgid "Either root-password or at least 1 user with sudo privileges must be specified" -msgstr "Entweder root Passwort oder wenigstens 1 super-user muss konfiguriert sein" +msgstr "Entweder root Passwort oder mindestens ein Superuser muss konfiguriert sein" -#, fuzzy msgid "Enter username (leave blank to skip): " -msgstr "Geben sie einen weiteren Benutzernamen an der angelegt werden soll (leer lassen um zu Überspringen): " +msgstr "Benutzernamen eingeben (leer lassen um zu Überspringen): " msgid "The username you entered is invalid. Try again" -msgstr "" +msgstr "Der Benutzername, den Sie eingegeben haben, ist nicht gültig. Bitte erneut versuchen" -#, fuzzy msgid "Should \"{}\" be a superuser (sudo)?" -msgstr "Soll {} ein superuser sein (sudoer)?" +msgstr "Soll {} ein Superuser sein (sudo)?" -#, fuzzy msgid "Select which partitions to encrypt" -msgstr "Bitte wählen sie welche Partition verschlüsselt werden soll" +msgstr "Bitte wählen Sie aus, welche Partitionen verschlüsselt werden sollen" msgid "very weak" -msgstr "" +msgstr "Sehr schwach" msgid "weak" -msgstr "" +msgstr "Schwach" msgid "moderate" -msgstr "" +msgstr "Moderat" msgid "strong" -msgstr "" +msgstr "Stark" -#, fuzzy msgid "Add subvolume" -msgstr " Subvolume :{:16}" +msgstr "Subvolume hinzufügen" msgid "Edit subvolume" -msgstr "" +msgstr "Subvolume bearbeiten" -#, fuzzy msgid "Delete subvolume" -msgstr "Benutzerkonto löschen" +msgstr "Subvolume löschen" msgid "Configured {} interfaces" -msgstr "" +msgstr "{} Schnittstellen konfiguriert" msgid "This option enables the number of parallel downloads that can occur during installation" -msgstr "" +msgstr "Diese Option setzt die Nummer an parallelen Downloads, die während der Installtion durchgeführt werden" #, python-brace-format msgid "" @@ -814,314 +799,334 @@ msgid "" " (Enter a value between 1 to {max_downloads})\n" "Note:" msgstr "" +"Geben Sie die Nummer an parallelen Downloads an.\n" +" (Wert zwischen 1 und {max_downloads})\n" +"Achtung:" msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr "" +msgstr " - Maximalwert :{max_downloads} (Erlaubt {max_downloads} parallele Downloads, erlaubt {max_downloads+1} Downloads gleichzeitig)" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" -msgstr "" +msgstr "- Minimalwert : 1 (Erlaubt einen parallelen Download, erlaubt zwei Downloads gleichzeitig)" msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" -msgstr "" +msgstr "- Deaktivieren/Standard : 0 (Deaktiviert parallele Downloads, erlaubt nur einen Download gleichzeitig)" #, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "" +msgstr "Ungültige Eingabe! Erneut mit gültiger Eingabe versuchen [1 bis {max_downloads}, oder 0 zum deaktivieren]" msgid "Parallel Downloads" -msgstr "" +msgstr "Parallele Downloads" -#, fuzzy msgid "ESC to skip" -msgstr "ESC drücken um zu überspringen" +msgstr "ESC zum überspringen" msgid "CTRL+C to reset" -msgstr "" +msgstr "Strg+C zum zurücksetzen" msgid "TAB to select" -msgstr "" +msgstr "TAB zum auswählen" msgid "[Default value: 0] > " -msgstr "" +msgstr "[Standardwert: 0] > " msgid "To be able to use this translation, please install a font manually that supports the language." -msgstr "" +msgstr "Um diese Übersetzung zu verwenden, installieren Sie bitte manuell eine Schrift, die diese Unterstüzt" msgid "The font should be stored as {}" -msgstr "" +msgstr "Die Schrift sollte als {} gespeichert werden" msgid "Archinstall requires root privileges to run. See --help for more." -msgstr "" +msgstr "Archinstall benötigt root Rechte um zu funktionieren. Verwenden Sie --help für mehr Informationen." -#, fuzzy msgid "Select an execution mode" -msgstr "Wählen sie eine Aktion aus für '{}'" +msgstr "Wählen Sie einen Ausführmodus" msgid "Unable to fetch profile from specified url: {}" -msgstr "" +msgstr "Profil von der URL \"{}\" abrufen nicht möglich" msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" -msgstr "" +msgstr "Profile benötigen einzigartige Namen, aber Profildefinitionen mit gleichen Namen gefunden: {}" -#, fuzzy msgid "Select one or more devices to use and configure" -msgstr "Bitte wählen sie eine oder mehrere Laufwerke aus die konfiguriert werden sollen" +msgstr "Bitte wählen Sie ein oder mehrere Geräte aus, die konfiguriert und verwendet werden sollen" -#, fuzzy msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" -msgstr "Wenn sie die Laufwerkkonfiguration ändern, dann wird die Laufwer-layout zurückgesetzt. Sind sie sicher?" +msgstr "Wenn Sie die Laufwerksauswahl zurücksetzen, dann wird das auch das Laufwerkslayout zurückgesetzt. Sind Sie sicher?" -#, fuzzy msgid "Existing Partitions" -msgstr "Partitionen werden hinzugefügt..." +msgstr "Existierende Partitionen" -#, fuzzy msgid "Select a partitioning option" -msgstr "Partition löschen" +msgstr "Wählen Sie eine Partitionierungsoption aus" -#, fuzzy msgid "Enter the root directory of the mounted devices: " -msgstr "Geben sie eine Ordner an wo die Konfigurationen gespeichert werden sollen: " +msgstr "Geben Sie das Stammverzeichnis der eingehängten Geräte an: " -#, fuzzy msgid "Minimum capacity for /home partition: {}GiB\n" msgstr "Minimaler Speicherplatz für /home Partition: {}GB\n" -#, fuzzy msgid "Minimum capacity for Arch Linux partition: {}GiB" msgstr "Minimaler Speicherplatz für Arch Linux Partition: {}GB" -#, fuzzy msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" -msgstr "Dies ist eine Liste von bereits programmierten Profilen, diese ermöglichen es einfacher Desktop Umgebungen einzustellen" +msgstr "Dies ist eine Liste von bereits programmierten Profilen (Backup), diese ermöglichen es Dinge wie Desktop Umgebungen einfacher zu installieren" -#, fuzzy msgid "Current profile selection" -msgstr "Momentanes Partitionslayout" +msgstr "Momentane Profilauswahl" -#, fuzzy msgid "Remove all newly added partitions" -msgstr "Neue Partition erstellen" +msgstr "Alle neu hinzugefügten Partitionen entfernen" -#, fuzzy msgid "Assign mountpoint" -msgstr "Mountort für Partition angeben" +msgstr "Einhängeort für Partition zuweisen" -#, fuzzy msgid "Mark/Unmark to be formatted (wipes data)" -msgstr "Markieren welche Partition formattiert werden soll (alle Daten werden gelöscht)" +msgstr "Markieren bzw. nicht makieren zum formatieren (alle Daten werden gelöscht)" msgid "Mark/Unmark as bootable" -msgstr "" +msgstr "Als bootbar makieren bzw. nicht makieren" msgid "Change filesystem" -msgstr "" +msgstr "Dateisystem ändern" -#, fuzzy msgid "Mark/Unmark as compressed" -msgstr "Markieren/Unmarkieren Kompression von einer Partition (nur Btrfs) " +msgstr "Als komprimiert makieren bzw. als nicht komprimiert makieren" -#, fuzzy msgid "Set subvolumes" -msgstr "Benutzerkonto löschen" +msgstr "Subvolumes setzen" -#, fuzzy msgid "Delete partition" msgstr "Partition löschen" msgid "Partition" -msgstr "" +msgstr "Partition" msgid "This partition is currently encrypted, to format it a filesystem has to be specified" -msgstr "" +msgstr "Diese Partition ist momentan verschlüsselt, um sie zu formatieren, muss ein Dateisystem angegeben werden" -#, fuzzy msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * Die Mountorte sind relativ zur Installation, zum Beispiel boot würde gemountet auf /boot" +msgstr "Die Einhängeorte sind relativ zur Installation, zum Beispiel boot würde bei /boot eingehängt werden" msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." -msgstr "" +msgstr "Wenn der Einhängeort auf /boot gesetzt ist, wird die Partition ebenfalls als bootbar makiert." msgid "Mountpoint: " -msgstr "" +msgstr "Einhängepunkt: " msgid "Current free sectors on device {}:" -msgstr "" +msgstr "Momentan freie Sektoren auf Gerät {}:" -#, fuzzy msgid "Total sectors: {}" -msgstr "Ordner existiert nicht: {}" +msgstr "Sektoren insgesamt: {}" -#, fuzzy msgid "Enter the start sector (default: {}): " -msgstr "Bitte geben Sie den start Sektor ein (in Prozent oder Blocknummer, default: {}): " +msgstr "Bitte geben Sie den Startsektor ein (Standard: {}): " -#, fuzzy msgid "Enter the end sector of the partition (percentage or block number, default: {}): " -msgstr "Bitte geben Sie den end Sektor ein (in Prozent oder Blocknummer, default: {}): " +msgstr "Bitte geben Sie den Endsektor der Partition ein (Prozent oder Blocknummer, Standard: {}): " msgid "This will remove all newly added partitions, continue?" -msgstr "" +msgstr "Dies wird alle neu hinzugefügten Partitionen entfernen. Fortfahren?" msgid "Partition management: {}" -msgstr "" +msgstr "Partitionsverwaltung: {}" msgid "Total length: {}" -msgstr "" +msgstr "Länge insgesamt: {}" -#, fuzzy msgid "Encryption type" -msgstr "Verschlüsselungspasswort angeben" +msgstr "Verschlüsselungstyp" msgid "Partitions" -msgstr "" +msgstr "Partitionen" msgid "No HSM devices available" -msgstr "" +msgstr "Keine HSM-Geräte verfügbar" -#, fuzzy msgid "Partitions to be encrypted" -msgstr "Bitte wählen sie welche Partition verschlüsselt werden soll" +msgstr "Partitionen die verschlüsselt werden" -#, fuzzy msgid "Select disk encryption option" -msgstr "Laufwerke-layout auswählen" +msgstr "Laufwerksverschlüsselungsoption auswählen" msgid "Select a FIDO2 device to use for HSM" -msgstr "" +msgstr "FIDO2-Gerät für HSM auswählen" -#, fuzzy msgid "Use a best-effort default partition layout" -msgstr "Alle Laufwerke löschen und ein vorgegebenes Partitionenlayout verwenden" +msgstr "Vorgegebendes Partitionslayout verwenden" -#, fuzzy msgid "Manual Partitioning" -msgstr "Manuelle konfiguration" +msgstr "Manuelle Partitionierung" -#, fuzzy msgid "Pre-mounted configuration" -msgstr "Keine Konfiguration" +msgstr "Voreingehängte Konfiguration" msgid "Unknown" -msgstr "" +msgstr "Unbekannt" msgid "Partition encryption" -msgstr "" +msgstr "Partitionsverschlüsselung" msgid " ! Formatting {} in " -msgstr "" +msgstr " ! Formatiere {} in " msgid "← Back" -msgstr "" +msgstr "← Zurück" msgid "Disk encryption" -msgstr "" +msgstr "Laufwerksverschlüsselung" -#, fuzzy msgid "Configuration" -msgstr "Keine Konfiguration" +msgstr "Konfiguration" -#, fuzzy msgid "Password" -msgstr "Root Passwort" +msgstr "Passwort" -#, fuzzy msgid "All settings will be reset, are you sure?" -msgstr "{} enthält Partitionen in der Warteschlange, dies werden damit entfernt, sind sie sicher?" +msgstr "Alle Einstellungen werden zurückgesetzt. Sind Sie sicher?" msgid "Back" -msgstr "" +msgstr "Zurück" msgid "Please chose which greeter to install for the chosen profiles: {}" -msgstr "" +msgstr "Bitte Greeter (Anmeldebildschirm) für das ausgewählte Profil auswählen: {}" msgid "Environment type: {}" -msgstr "" +msgstr "Umgebungstyp: {}" msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" -msgstr "" +msgstr "Der proprietäre Nvidiatreiber wird von Sway nicht unterstütz. Es ist wahrscheinlich, dass Probleme auftreten werden. Ist das Okay?" -#, fuzzy msgid "Installed packages" -msgstr "Zus. Packete" +msgstr "Installiere Pakete" -#, fuzzy msgid "Add profile" -msgstr "Profile" +msgstr "Profil hinzufügen" -#, fuzzy msgid "Edit profile" -msgstr "Profile" +msgstr "Profil bearbeiten" -#, fuzzy msgid "Delete profile" -msgstr "Verbindung löschen" +msgstr "Profil entfernen" -#, fuzzy msgid "Profile name: " -msgstr "Profile" +msgstr "Profilname: " msgid "The profile name you entered is already in use. Try again" -msgstr "" +msgstr "Der eingegebende Profilname ist bereits in Verwendung. Erneut versuchen" -#, fuzzy msgid "Packages to be install with this profile (space separated, leave blank to skip): " -msgstr "Schreiben Sie zusätzliche Packete die installiert werden sollen mit einem Leerzeichen getrennt (zum Überspringen leer lassen): " +msgstr "Zusätzliche Pakete die mit diesem Profil installiert werden sollen (Mit Leerzeichen trennen, leer lassen zum überspringen): " -#, fuzzy msgid "Services to be enabled with this profile (space separated, leave blank to skip): " -msgstr "Schreiben Sie zusätzliche Packete die installiert werden sollen mit einem Leerzeichen getrennt (zum Überspringen leer lassen): " +msgstr "Dienste die mit diesem Profil installiert werden sollen (Mit Leerzeichen trennen, leer lassen zum überspringen): " msgid "Should this profile be enabled for installation?" -msgstr "" +msgstr "Soll diese Profil für die Installation verwendet werden?" msgid "Create your own" -msgstr "" +msgstr "Eigenes erstellen" -#, fuzzy msgid "" "\n" "Select a graphics driver or leave blank to install all open-source drivers" msgstr "" "\n" "\n" -"Bitte wählen sie einen Grafiktreiber aus oder leer lassen um alle open-source Treiber zu installieren" +"Bitte wählen sie einen Grafiktreiber aus oder leer lassen um alle Quelloffenen Treiber zu installieren" msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" -msgstr "" +msgstr "Sway benötigt Zugriff auf ihren Seat (Sammlung von Hardwaregeräten wie Tastatur, Maus, usw.)" msgid "" "\n" "\n" "Choose an option to give Sway access to your hardware" msgstr "" +"\n" +"\n" +"Option um Sway Zugriff auf Hardware zu geben auswählen" msgid "Graphics driver" -msgstr "" +msgstr "Grafiktreiber" msgid "Greeter" -msgstr "" +msgstr "Greeter (Begrüßer/Anmeldebildschirm)" msgid "Please chose which greeter to install" -msgstr "" +msgstr "Bitte den zu installierenden Greeter (Begrüßer/Anmeldebildschirm) auswählen" msgid "This is a list of pre-programmed default_profiles" -msgstr "" +msgstr "Dies ist eine Liste an vorgefertigten Standardprofilen" -#, fuzzy msgid "Disk configuration" -msgstr "Keine Konfiguration" +msgstr "Laufwerkskonfiguration" -#, fuzzy msgid "Profiles" msgstr "Profile" msgid "Finding possible directories to save configuration files ..." -msgstr "" +msgstr "Suche mögliche Ordner zum Konfigurationen speichern..." -#, fuzzy msgid "Select directory (or directories) for saving configuration files" -msgstr "Bitte wählen sie eine oder mehrere Laufwerke aus die konfiguriert werden sollen" +msgstr "Ordner um Konfigurationsdateien zu erstellen auswählen" + +msgid "Add a custom mirror" +msgstr "Eigenen Spiegelserver hinzufügen" + +msgid "Change custom mirror" +msgstr "Eigenen Spiegelserver ändern" + +msgid "Delete custom mirror" +msgstr "Eigenen Spiegelserver löschen" + +msgid "Enter name (leave blank to skip): " +msgstr "Namen eingeben (leer lassen zum überspringen): " + +msgid "Enter url (leave blank to skip): " +msgstr "URL eingeben (leer lassen zum überspringen): " + +msgid "Select signature check option" +msgstr "Signatursüberprüfungsoptions auswählen" + +msgid "Select signature option" +msgstr "Signatursoption auswählen" + +msgid "Custom mirrors" +msgstr "Eigener Spiegelserver" + +msgid "Defined" +msgstr "Definiert" + +msgid "Save user configuration (including disk layout)" +msgstr "Benutzerkonfiguration (mit Laufwerkslayout) speichern" + +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "" +"Geben Sie einen Ordner an, in dem die Konfiguration(en) gespeichert werden sollen (TAB zum vervollständigen)\n" +"Ordner: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" +"Möchten Sie {} Konfigurationsdatei(en) in folgendem Ordner speichern?\n" +"\n" +"{}" + +msgid "Saving {} configuration files to {}" +msgstr "{} Konfigurationsdateien in {} speichern" + +msgid "Mirrors" +msgstr "Spiegelserver" + +msgid "Mirror regions" +msgstr "Spiegelserverregionen" #~ msgid "Add :" #~ msgstr "Hinzufügen :" diff --git a/archinstall/locales/el/LC_MESSAGES/base.po b/archinstall/locales/el/LC_MESSAGES/base.po index 2d2d04c7..4a33dd5e 100644 --- a/archinstall/locales/el/LC_MESSAGES/base.po +++ b/archinstall/locales/el/LC_MESSAGES/base.po @@ -1110,3 +1110,63 @@ msgstr "" #, fuzzy msgid "Select directory (or directories) for saving configuration files" msgstr "Επιλέξτε έναν ή περισσότερους σκληρούς δίσκους προς χρήση και διαμόρφωση" + +#, fuzzy +msgid "Add a custom mirror" +msgstr "Προσθήκη χρήστη" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "Εισάγετε όνομα χρήστη (αφήστε κενό για παράλειψη): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "Εισάγετε όνομα χρήστη (αφήστε κενό για παράλειψη): " + +#, fuzzy +msgid "Select signature check option" +msgstr "Επιλέξτε διεπαφή προς προσθήκη" + +#, fuzzy +msgid "Select signature option" +msgstr "Επιλέξτε διεπαφή προς προσθήκη" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "Αποθήκευση διαμόρφωσης χρήστη" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "Εισάγετε έναν φάκελο για την αποθήκευση της/ων διαμόρφωση/ων: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "Αποθήκευση διαμόρφωσης" + +#, fuzzy +msgid "Mirrors" +msgstr "Περιοχή mirror" + +#, fuzzy +msgid "Mirror regions" +msgstr "Περιοχή mirror" diff --git a/archinstall/locales/en/LC_MESSAGES/base.po b/archinstall/locales/en/LC_MESSAGES/base.po index c95cc951..b3e31383 100644 --- a/archinstall/locales/en/LC_MESSAGES/base.po +++ b/archinstall/locales/en/LC_MESSAGES/base.po @@ -1017,3 +1017,53 @@ msgstr "" msgid "Select directory (or directories) for saving configuration files" msgstr "" + +msgid "Add a custom mirror" +msgstr "" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +msgid "Enter name (leave blank to skip): " +msgstr "" + +msgid "Enter url (leave blank to skip): " +msgstr "" + +msgid "Select signature check option" +msgstr "" + +msgid "Select signature option" +msgstr "" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +msgid "Save user configuration (including disk layout)" +msgstr "" + +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "" + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +msgid "Saving {} configuration files to {}" +msgstr "" + +msgid "Mirrors" +msgstr "" + +msgid "Mirror regions" +msgstr "" diff --git a/archinstall/locales/es/LC_MESSAGES/base.po b/archinstall/locales/es/LC_MESSAGES/base.po index 1599e7f4..6ba5f7b6 100644 --- a/archinstall/locales/es/LC_MESSAGES/base.po +++ b/archinstall/locales/es/LC_MESSAGES/base.po @@ -1110,6 +1110,66 @@ msgstr "" msgid "Select directory (or directories) for saving configuration files" msgstr "Seleccione uno o más discos duros para usar y configurar" +#, fuzzy +msgid "Add a custom mirror" +msgstr "Añadir un usuario" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "Ingrese el nombre de usuario (déjelo en blanco para omitir): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "Ingrese el nombre de usuario (déjelo en blanco para omitir): " + +#, fuzzy +msgid "Select signature check option" +msgstr "Seleccione el diseño del disco" + +#, fuzzy +msgid "Select signature option" +msgstr "Seleccione el diseño del disco" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "Guardar configuración de usuario" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "Introduzca un directorio para guardar la(s) configuración(es): " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "Guardar configuración" + +#, fuzzy +msgid "Mirrors" +msgstr "Región del servidor" + +#, fuzzy +msgid "Mirror regions" +msgstr "Región del servidor" + #~ msgid "Add :" #~ msgstr "Añadir :" diff --git a/archinstall/locales/fr/LC_MESSAGES/base.po b/archinstall/locales/fr/LC_MESSAGES/base.po index 87b365e0..15aea6ac 100644 --- a/archinstall/locales/fr/LC_MESSAGES/base.po +++ b/archinstall/locales/fr/LC_MESSAGES/base.po @@ -1111,6 +1111,66 @@ msgstr "" msgid "Select directory (or directories) for saving configuration files" msgstr "Sélectionner un ou plusieurs disques durs à utiliser et à configurer" +#, fuzzy +msgid "Add a custom mirror" +msgstr "Ajouter un utilisateur" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "Entrer le nom d'utilisateur (laisser vide pour passer) : " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "Entrer le nom d'utilisateur (laisser vide pour passer) : " + +#, fuzzy +msgid "Select signature check option" +msgstr "Sélectionner la disposition du disque" + +#, fuzzy +msgid "Select signature option" +msgstr "Sélectionner la disposition du disque" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "Enregistrer la configuration utilisateur" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "Saisir un répertoire pour la ou les configuration(s) à enregistrer : " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "Enregistrer la configuration" + +#, fuzzy +msgid "Mirrors" +msgstr "Région miroir" + +#, fuzzy +msgid "Mirror regions" +msgstr "Région miroir" + #, python-brace-format #~ msgid "Edit {origkey} :" #~ msgstr "Modifier {origkey} :" diff --git a/archinstall/locales/id/LC_MESSAGES/base.po b/archinstall/locales/id/LC_MESSAGES/base.po index 5ed2f3c8..01986796 100644 --- a/archinstall/locales/id/LC_MESSAGES/base.po +++ b/archinstall/locales/id/LC_MESSAGES/base.po @@ -1109,3 +1109,63 @@ msgstr "" #, fuzzy msgid "Select directory (or directories) for saving configuration files" msgstr "Pilih satu atau lebih hard drive untuk digunakan dan dikonfigurasi" + +#, fuzzy +msgid "Add a custom mirror" +msgstr "Tambahkan pengguna" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "Masukkan nama pengguna (kosongkan untuk melewati): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "Masukkan nama pengguna (kosongkan untuk melewati): " + +#, fuzzy +msgid "Select signature check option" +msgstr "Pilih interface untuk ditambahkan" + +#, fuzzy +msgid "Select signature option" +msgstr "Pilih interface untuk ditambahkan" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "Simpan konfigurasi pengguna" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "Masukkan direktori untuk konfigurasi yang akan disimpan: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "Simpan konfigurasi" + +#, fuzzy +msgid "Mirrors" +msgstr "Wilayah mirror" + +#, fuzzy +msgid "Mirror regions" +msgstr "Wilayah mirror" diff --git a/archinstall/locales/it/LC_MESSAGES/base.po b/archinstall/locales/it/LC_MESSAGES/base.po index c4acc04d..c856a286 100644 --- a/archinstall/locales/it/LC_MESSAGES/base.po +++ b/archinstall/locales/it/LC_MESSAGES/base.po @@ -1109,3 +1109,63 @@ msgstr "" #, fuzzy msgid "Select directory (or directories) for saving configuration files" msgstr "Selezionare uno o più dischi rigidi da utilizzare e configurare" + +#, fuzzy +msgid "Add a custom mirror" +msgstr "Aggiungi un utente" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "Inserisci il nome utente (lascia vuoto per saltare): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "Inserisci il nome utente (lascia vuoto per saltare): " + +#, fuzzy +msgid "Select signature check option" +msgstr "Seleziona l'interfaccia da aggiungere" + +#, fuzzy +msgid "Select signature option" +msgstr "Seleziona l'interfaccia da aggiungere" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "Salva configurazione utente" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "Immettere una directory per le configurazioni da salvare: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "Salva configurazione" + +#, fuzzy +msgid "Mirrors" +msgstr "Regione dei mirror" + +#, fuzzy +msgid "Mirror regions" +msgstr "Regione dei mirror" diff --git a/archinstall/locales/ka/LC_MESSAGES/base.po b/archinstall/locales/ka/LC_MESSAGES/base.po index 7a88f96c..029e4256 100644 --- a/archinstall/locales/ka/LC_MESSAGES/base.po +++ b/archinstall/locales/ka/LC_MESSAGES/base.po @@ -1108,3 +1108,63 @@ msgstr "" #, fuzzy msgid "Select directory (or directories) for saving configuration files" msgstr "აირჩიეთ ერთი ან მეტი მყარი დისკი და მოირგეთ" + +#, fuzzy +msgid "Add a custom mirror" +msgstr "მომხმარებლის დამატება" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "შეიყვანეთ მომხმარებლი სახელი (გამოსატოვებლად ცარიელი დატოვეთ): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "შეიყვანეთ მომხმარებლი სახელი (გამოსატოვებლად ცარიელი დატოვეთ): " + +#, fuzzy +msgid "Select signature check option" +msgstr "აირჩიეთ დისკის დაშიფვრის პარამეტრი" + +#, fuzzy +msgid "Select signature option" +msgstr "აირჩიეთ დისკის დაშიფვრის პარამეტრი" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "მომხმარებლის კონფიგურაციის შენახვა" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "შეიყვანეთ საქაღალდე, სადაც კონფიგურაცი(ებ)-ი იქნება შენახული: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "კონფიგურაციი შენახვა" + +#, fuzzy +msgid "Mirrors" +msgstr "სარკის რეგიონი" + +#, fuzzy +msgid "Mirror regions" +msgstr "სარკის რეგიონი" diff --git a/archinstall/locales/ko/LC_MESSAGES/base.po b/archinstall/locales/ko/LC_MESSAGES/base.po index 3503a816..7d30a785 100644 --- a/archinstall/locales/ko/LC_MESSAGES/base.po +++ b/archinstall/locales/ko/LC_MESSAGES/base.po @@ -1110,3 +1110,63 @@ msgstr "" #, fuzzy msgid "Select directory (or directories) for saving configuration files" msgstr "사용하고 구성할 하드 드라이브를 하나 이상 선택하세요" + +#, fuzzy +msgid "Add a custom mirror" +msgstr "사용자 추가" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "사용자명 입력 (공백일 경우 스킵): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "사용자명 입력 (공백일 경우 스킵): " + +#, fuzzy +msgid "Select signature check option" +msgstr "추가할 인터페이스 선택" + +#, fuzzy +msgid "Select signature option" +msgstr "추가할 인터페이스 선택" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "사용자 구성 저장" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "저장할 구성의 디렉토리를 입력하세요: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "구성 저장" + +#, fuzzy +msgid "Mirrors" +msgstr "미러 위치" + +#, fuzzy +msgid "Mirror regions" +msgstr "미러 위치" diff --git a/archinstall/locales/nl/LC_MESSAGES/base.po b/archinstall/locales/nl/LC_MESSAGES/base.po index 3f97513a..e22a6d0f 100644 --- a/archinstall/locales/nl/LC_MESSAGES/base.po +++ b/archinstall/locales/nl/LC_MESSAGES/base.po @@ -1135,6 +1135,66 @@ msgstr "" msgid "Select directory (or directories) for saving configuration files" msgstr "Selecteer één of meer in te stellen harde schijven" +#, fuzzy +msgid "Add a custom mirror" +msgstr "Gebruiker toevoegen" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "Voer een gebruikersnaam in om een tweede account toe te voegen (laat leeg om over te slaan): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "Voer een gebruikersnaam in om een tweede account toe te voegen (laat leeg om over te slaan): " + +#, fuzzy +msgid "Select signature check option" +msgstr "Kies een schijfindeling" + +#, fuzzy +msgid "Select signature option" +msgstr "Kies een schijfindeling" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "Gebruikersconfiguratie vastleggen" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "Voer de naam in van de map waarin de configuratie(s) moet(en) worden vastgelegd: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "Configuratie vastleggen" + +#, fuzzy +msgid "Mirrors" +msgstr "Spiegelserverregio" + +#, fuzzy +msgid "Mirror regions" +msgstr "Spiegelserverregio" + #~ msgid "Add :" #~ msgstr "Toevoegen:" diff --git a/archinstall/locales/pl/LC_MESSAGES/base.po b/archinstall/locales/pl/LC_MESSAGES/base.po index 3f6c1dca..50348715 100644 --- a/archinstall/locales/pl/LC_MESSAGES/base.po +++ b/archinstall/locales/pl/LC_MESSAGES/base.po @@ -1113,6 +1113,66 @@ msgstr "" msgid "Select directory (or directories) for saving configuration files" msgstr "Wybierz jeden lub więcej dysków twardych do użycia i skonfiguruj je" +#, fuzzy +msgid "Add a custom mirror" +msgstr "Dodaj użytkownika" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "Wprowadź nazwę użytkownika (pozostaw puste, aby pominąć): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "Wprowadź nazwę użytkownika (pozostaw puste, aby pominąć): " + +#, fuzzy +msgid "Select signature check option" +msgstr "Wybierz układ dysku" + +#, fuzzy +msgid "Select signature option" +msgstr "Wybierz układ dysku" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "Zapisz konfiguracje użytkownika" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "Wprowadź katalog, w którym ma zostać zapisana konfiguracja: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "Zapisz konfiguracje" + +#, fuzzy +msgid "Mirrors" +msgstr "Region lustra" + +#, fuzzy +msgid "Mirror regions" +msgstr "Region lustra" + #~ msgid "Add :" #~ msgstr "Dodaj :" diff --git a/archinstall/locales/pt/LC_MESSAGES/base.po b/archinstall/locales/pt/LC_MESSAGES/base.po index 9f44f02f..35c3ca73 100644 --- a/archinstall/locales/pt/LC_MESSAGES/base.po +++ b/archinstall/locales/pt/LC_MESSAGES/base.po @@ -1157,6 +1157,66 @@ msgstr "" msgid "Select directory (or directories) for saving configuration files" msgstr "Seleciona um ou mais discos rígidos para usar e configurar" +#, fuzzy +msgid "Add a custom mirror" +msgstr "Adicionar um utilizador" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "Introduzir um nome de utilizador para criar um utilizador adicional (deixar em branco para saltar): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "Introduzir um nome de utilizador para criar um utilizador adicional (deixar em branco para saltar): " + +#, fuzzy +msgid "Select signature check option" +msgstr "Seleciona o esquema de disco" + +#, fuzzy +msgid "Select signature option" +msgstr "Seleciona o esquema de disco" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "Guardar configuração de utilizador" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "Introduz um diretório para as configurações a serem guardadas: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "Guardar configuração" + +#, fuzzy +msgid "Mirrors" +msgstr "Região do mirror" + +#, fuzzy +msgid "Mirror regions" +msgstr "Região do mirror" + #~ msgid "Add :" #~ msgstr "Adicionar :" diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.po b/archinstall/locales/pt_BR/LC_MESSAGES/base.po index d0881905..93b33002 100644 --- a/archinstall/locales/pt_BR/LC_MESSAGES/base.po +++ b/archinstall/locales/pt_BR/LC_MESSAGES/base.po @@ -1111,3 +1111,63 @@ msgstr "" #, fuzzy msgid "Select directory (or directories) for saving configuration files" msgstr "Selecione um ou mais discos rígidos para usar e configurar" + +#, fuzzy +msgid "Add a custom mirror" +msgstr "Adicionar usuário" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "Digite um nome de usuário (deixe em branco para pular): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "Digite um nome de usuário (deixe em branco para pular): " + +#, fuzzy +msgid "Select signature check option" +msgstr "Selecione a opção de encriptação de disco" + +#, fuzzy +msgid "Select signature option" +msgstr "Selecione a opção de encriptação de disco" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "Salvar configuração de usuário" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "Digite um diretório para as configurações serem salvas: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "Salvar configuração" + +#, fuzzy +msgid "Mirrors" +msgstr "Região do mirror" + +#, fuzzy +msgid "Mirror regions" +msgstr "Região do mirror" diff --git a/archinstall/locales/ru/LC_MESSAGES/base.po b/archinstall/locales/ru/LC_MESSAGES/base.po index b99c6473..a0263aa3 100644 --- a/archinstall/locales/ru/LC_MESSAGES/base.po +++ b/archinstall/locales/ru/LC_MESSAGES/base.po @@ -1111,6 +1111,66 @@ msgstr "" msgid "Select directory (or directories) for saving configuration files" msgstr "Выберите один или несколько жестких дисков для использования и настройте их" +#, fuzzy +msgid "Add a custom mirror" +msgstr "Добавить пользователя" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "Введите имя пользователя (оставьте пустым, чтобы пропустить): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "Введите имя пользователя (оставьте пустым, чтобы пропустить): " + +#, fuzzy +msgid "Select signature check option" +msgstr "Выберите интерфейс для добавления" + +#, fuzzy +msgid "Select signature option" +msgstr "Выберите интерфейс для добавления" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "Сохранить конфигурацию пользователя" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "Введите каталог для сохранения конфигурации (-ций): " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "Сохранить конфигурацию" + +#, fuzzy +msgid "Mirrors" +msgstr "Регион зеркала" + +#, fuzzy +msgid "Mirror regions" +msgstr "Регион зеркала" + #, python-brace-format #~ msgid "Edit {origkey} :" #~ msgstr "Редактировать {origkey}:" diff --git a/archinstall/locales/sv/LC_MESSAGES/base.po b/archinstall/locales/sv/LC_MESSAGES/base.po index e114a266..ea81ad80 100644 --- a/archinstall/locales/sv/LC_MESSAGES/base.po +++ b/archinstall/locales/sv/LC_MESSAGES/base.po @@ -1118,3 +1118,63 @@ msgstr "" #, fuzzy msgid "Select directory (or directories) for saving configuration files" msgstr "Välj en eller flera hårddiskar som skall användas och konfigureras" + +#, fuzzy +msgid "Add a custom mirror" +msgstr "Lägg till användare" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "Mata in ett användarnamn för att skapa ytterligare användare (lämna tom för att hoppa över): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "Mata in ett användarnamn för att skapa ytterligare användare (lämna tom för att hoppa över): " + +#, fuzzy +msgid "Select signature check option" +msgstr "Välj hårddisk-layout" + +#, fuzzy +msgid "Select signature option" +msgstr "Välj hårddisk-layout" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "Spara användarkonfigurering" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "Välj vilken mapp du vill spara konfigurerationerna till: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "Spara konfigurationen" + +#, fuzzy +msgid "Mirrors" +msgstr "Region för paketsynk" + +#, fuzzy +msgid "Mirror regions" +msgstr "Region för paketsynk" diff --git a/archinstall/locales/ta/LC_MESSAGES/base.po b/archinstall/locales/ta/LC_MESSAGES/base.po index d430c8e5..44aebb19 100644 --- a/archinstall/locales/ta/LC_MESSAGES/base.po +++ b/archinstall/locales/ta/LC_MESSAGES/base.po @@ -1109,3 +1109,63 @@ msgstr "" #, fuzzy msgid "Select directory (or directories) for saving configuration files" msgstr "பயன்படுத்த மற்றும் கட்டமைக்க ஒன்று அல்லது அதற்கு மேற்பட்ட ஹார்டு டிரைவ்களைத் தேர்ந்தெடுக்கவும்" + +#, fuzzy +msgid "Add a custom mirror" +msgstr "ஒரு பயனரைச் சேர்க்கவும்" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "பயனர்பெயரை உள்ளிடவும் (தவிர்க்க காலியாக விடவும்): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "பயனர்பெயரை உள்ளிடவும் (தவிர்க்க காலியாக விடவும்): " + +#, fuzzy +msgid "Select signature check option" +msgstr "சேர்க்க இடைமுகத்தைத் தேர்ந்தெடுக்கவும்" + +#, fuzzy +msgid "Select signature option" +msgstr "சேர்க்க இடைமுகத்தைத் தேர்ந்தெடுக்கவும்" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "பயனர் உள்ளமைவைச் சேமிக்கவும்" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "உள்ளமைவு(களை) சேமிக்க ஒரு கோப்பகத்தை உள்ளிடவும்: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "உள்ளமைவைச் சேமிக்கவும்" + +#, fuzzy +msgid "Mirrors" +msgstr "மிரர் பிராந்தியம்" + +#, fuzzy +msgid "Mirror regions" +msgstr "மிரர் பிராந்தியம்" diff --git a/archinstall/locales/tr/LC_MESSAGES/base.po b/archinstall/locales/tr/LC_MESSAGES/base.po index df978da8..40efea03 100644 --- a/archinstall/locales/tr/LC_MESSAGES/base.po +++ b/archinstall/locales/tr/LC_MESSAGES/base.po @@ -1117,3 +1117,63 @@ msgstr "" #, fuzzy msgid "Select directory (or directories) for saving configuration files" msgstr "Kullanmak ve yapılandırmak için bir veya daha fazla sabit disk seçin" + +#, fuzzy +msgid "Add a custom mirror" +msgstr "Kullanıcı ekle" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "Ek kullanıcı oluşturmak için bir kullanıcı adı girin (geçmek için boş bırakın): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "Ek kullanıcı oluşturmak için bir kullanıcı adı girin (geçmek için boş bırakın): " + +#, fuzzy +msgid "Select signature check option" +msgstr "Eklemek için arayüz seçin" + +#, fuzzy +msgid "Select signature option" +msgstr "Eklemek için arayüz seçin" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "Kullanıcı yapılandırmasını kaydet" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "Yapılandırma(lar)ın kaydedilmesi için bir dizin girin: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "Yapılandırmayı kaydet" + +#, fuzzy +msgid "Mirrors" +msgstr "İndirme sunucusu bölgesi" + +#, fuzzy +msgid "Mirror regions" +msgstr "İndirme sunucusu bölgesi" diff --git a/archinstall/locales/uk/LC_MESSAGES/base.po b/archinstall/locales/uk/LC_MESSAGES/base.po index ca019ff3..f2c7737c 100644 --- a/archinstall/locales/uk/LC_MESSAGES/base.po +++ b/archinstall/locales/uk/LC_MESSAGES/base.po @@ -1107,3 +1107,63 @@ msgstr "" #, fuzzy msgid "Select directory (or directories) for saving configuration files" msgstr "Оберіть один або кілька жорстких дисків для використання та налаштування" + +#, fuzzy +msgid "Add a custom mirror" +msgstr "Додати користувача" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "Введіть ім'я користувача (залиште порожнім, щоб пропустити): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "Введіть ім'я користувача (залиште порожнім, щоб пропустити): " + +#, fuzzy +msgid "Select signature check option" +msgstr "Оберіть параметр шифрування диска" + +#, fuzzy +msgid "Select signature option" +msgstr "Оберіть параметр шифрування диска" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "Зберегти конфігурацію користувача" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "Введіть каталог для конфігурацій, які потрібно зберегти: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "Зберегти конфігурацію" + +#, fuzzy +msgid "Mirrors" +msgstr "Регіон дзеркала" + +#, fuzzy +msgid "Mirror regions" +msgstr "Регіон дзеркала" diff --git a/archinstall/locales/ur/LC_MESSAGES/base.po b/archinstall/locales/ur/LC_MESSAGES/base.po index 1a715d90..6660f3b4 100644 --- a/archinstall/locales/ur/LC_MESSAGES/base.po +++ b/archinstall/locales/ur/LC_MESSAGES/base.po @@ -1138,6 +1138,66 @@ msgstr "" msgid "Select directory (or directories) for saving configuration files" msgstr "استعمال کرنے اور کنفیگر کے لیے ایک یا زیادہ ہارڈ ڈرائیوز منتخب کریں" +#, fuzzy +msgid "Add a custom mirror" +msgstr "ایک صارف شامل کریں" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "ایک اضافی صارف بنانے کے لیے صارف نام درج کریں (چھوڑنے کے لیے خالی چھوڑیں):" + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "ایک اضافی صارف بنانے کے لیے صارف نام درج کریں (چھوڑنے کے لیے خالی چھوڑیں):" + +#, fuzzy +msgid "Select signature check option" +msgstr "ڈسک لے آؤٹ کو منتخب کریں" + +#, fuzzy +msgid "Select signature option" +msgstr "ڈسک لے آؤٹ کو منتخب کریں" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "صارف کنفیگریشن کو محفوظ کریں" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "محفوظ کیے جانے والے کنفیگریشن کے لیے ایک ڈائرکٹری درج کریں:" + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "ترتیب کو محفوظ کریں" + +#, fuzzy +msgid "Mirrors" +msgstr "متبادل علاقہ" + +#, fuzzy +msgid "Mirror regions" +msgstr "متبادل علاقہ" + #~ msgid "Add :" #~ msgstr "شامل:" diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.po b/archinstall/locales/zh-CN/LC_MESSAGES/base.po index f1cee304..afa1716b 100644 --- a/archinstall/locales/zh-CN/LC_MESSAGES/base.po +++ b/archinstall/locales/zh-CN/LC_MESSAGES/base.po @@ -1108,3 +1108,63 @@ msgstr "" #, fuzzy msgid "Select directory (or directories) for saving configuration files" msgstr "选择一个或多个硬盘驱动器来使用和配置" + +#, fuzzy +msgid "Add a custom mirror" +msgstr "添加一个用户" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "输入用户名(留空跳过): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "输入用户名(留空跳过): " + +#, fuzzy +msgid "Select signature check option" +msgstr "选择要添加的接口" + +#, fuzzy +msgid "Select signature option" +msgstr "选择要添加的接口" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "保存用户配置" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "输入要保存配置的目录: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "保存配置" + +#, fuzzy +msgid "Mirrors" +msgstr "镜像区域" + +#, fuzzy +msgid "Mirror regions" +msgstr "镜像区域" -- cgit v1.2.3-70-g09d2 From 7af5490fef7e9d33103c0dad94a45ff118ac18d4 Mon Sep 17 00:00:00 2001 From: Hegert Taresalu <57842113+HegertTaresalu@users.noreply.github.com> Date: Mon, 5 Jun 2023 11:16:37 +0300 Subject: Add Estonian translation (#1827) * create et locale and added 56 translations * add more et translations(55-67) * add et translations * add translated_lang * update readme language list * Fixed merge conflicts --------- Co-authored-by: Hegert Taresalu Co-authored-by: Anton Hvornum --- README.md | 1 + archinstall/locales/ar/LC_MESSAGES/base.po | 12 +- archinstall/locales/de/LC_MESSAGES/base.po | 60 ++ archinstall/locales/en/LC_MESSAGES/base.mo | Bin 261 -> 259 bytes archinstall/locales/en/LC_MESSAGES/base.po | 2 +- archinstall/locales/et/LC_MESSAGES/base.mo | Bin 0 -> 35633 bytes archinstall/locales/et/LC_MESSAGES/base.po | 1141 ++++++++++++++++++++++++++++ archinstall/locales/languages.json | 2 +- archinstall/locales/pl/LC_MESSAGES/base.po | 8 + 9 files changed, 1218 insertions(+), 8 deletions(-) create mode 100644 archinstall/locales/et/LC_MESSAGES/base.mo create mode 100644 archinstall/locales/et/LC_MESSAGES/base.po diff --git a/README.md b/README.md index 7f48bfba..9893ddc3 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ Arabic Brazilian Portuguese Czech Dutch +Estonian French Georgian German diff --git a/archinstall/locales/ar/LC_MESSAGES/base.po b/archinstall/locales/ar/LC_MESSAGES/base.po index d6d07b06..6c37bdff 100644 --- a/archinstall/locales/ar/LC_MESSAGES/base.po +++ b/archinstall/locales/ar/LC_MESSAGES/base.po @@ -1056,6 +1056,12 @@ msgstr "" msgid "Defined" msgstr "" +msgid "Mirrors" +msgstr "" + +msgid "Mirror regions" +msgstr "" + msgid "Save user configuration (including disk layout)" msgstr "" @@ -1072,9 +1078,3 @@ msgstr "" msgid "Saving {} configuration files to {}" msgstr "" - -msgid "Mirrors" -msgstr "" - -msgid "Mirror regions" -msgstr "" diff --git a/archinstall/locales/de/LC_MESSAGES/base.po b/archinstall/locales/de/LC_MESSAGES/base.po index c1f32f0f..b20d6e90 100644 --- a/archinstall/locales/de/LC_MESSAGES/base.po +++ b/archinstall/locales/de/LC_MESSAGES/base.po @@ -1128,6 +1128,66 @@ msgstr "Spiegelserver" msgid "Mirror regions" msgstr "Spiegelserverregionen" +#, fuzzy +msgid "Add a custom mirror" +msgstr "Benutzerkonto hinzufügen" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "Geben sie einen weiteren Benutzernamen an der angelegt werden soll (leer lassen um zu Überspringen): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "Geben sie einen weiteren Benutzernamen an der angelegt werden soll (leer lassen um zu Überspringen): " + +#, fuzzy +msgid "Select signature check option" +msgstr "Laufwerke-layout auswählen" + +#, fuzzy +msgid "Select signature option" +msgstr "Laufwerke-layout auswählen" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Mirrors" +msgstr "Mirror-region" + +#, fuzzy +msgid "Mirror regions" +msgstr "Mirror-region" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "Benutzerkonfiguration speichern" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "Geben sie eine Ordner an wo die Konfigurationen gespeichert werden sollen: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "Konfiguration speichern" + #~ msgid "Add :" #~ msgstr "Hinzufügen :" diff --git a/archinstall/locales/en/LC_MESSAGES/base.mo b/archinstall/locales/en/LC_MESSAGES/base.mo index e6ac80c2..7275098e 100644 Binary files a/archinstall/locales/en/LC_MESSAGES/base.mo and b/archinstall/locales/en/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/en/LC_MESSAGES/base.po b/archinstall/locales/en/LC_MESSAGES/base.po index b3e31383..f08c5ffc 100644 --- a/archinstall/locales/en/LC_MESSAGES/base.po +++ b/archinstall/locales/en/LC_MESSAGES/base.po @@ -9,7 +9,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.0.1\n" +"X-Generator: Poedit 3.3\n" msgid "[!] A log file has been created here: {} {}" msgstr "" diff --git a/archinstall/locales/et/LC_MESSAGES/base.mo b/archinstall/locales/et/LC_MESSAGES/base.mo new file mode 100644 index 00000000..f9c9f7ce Binary files /dev/null and b/archinstall/locales/et/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/et/LC_MESSAGES/base.po b/archinstall/locales/et/LC_MESSAGES/base.po new file mode 100644 index 00000000..8c2582f9 --- /dev/null +++ b/archinstall/locales/et/LC_MESSAGES/base.po @@ -0,0 +1,1141 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: et\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.3\n" + +msgid "[!] A log file has been created here: {} {}" +msgstr "[!] Siia on loodud logifail: {} {}" + +msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" +msgstr " Esitage see probleem (ja fail) aadressil https://github.com/archlinux/archinstall/issues" + +msgid "Do you really want to abort?" +msgstr "Kas sa tahad katkestada?" + +msgid "And one more time for verification: " +msgstr "Ja veel kord kinnitamiseks: " + +msgid "Would you like to use swap on zram?" +msgstr "Kas soovite zramis kasutada swapi?" + +msgid "Desired hostname for the installation: " +msgstr "Installimiseks soovitud hostinimi: " + +msgid "Username for required superuser with sudo privileges: " +msgstr "Kasutajanimi nõutud superkasutajale sudo õigustega: " + +msgid "Any additional users to install (leave blank for no users): " +msgstr "Lisa kasutajate lisamine(jäta tühjaks kui ei soovi lisa kasutajaid): " + +msgid "Should this user be a superuser (sudoer)?" +msgstr "Kas see kasutaja peaks olema superkasutaja (sudo õigustega)" + +msgid "Select a timezone" +msgstr "Vali ajatsoon" + +msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?" +msgstr "Kas soovite systemd-booti asemel kasutada GRUBi buudilaadijana?" + +msgid "Choose a bootloader" +msgstr "Vali buudilaadija" + +msgid "Choose an audio server" +msgstr "Vali audio server" + +msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." +msgstr "Paigaldatakse ainult sellised paketid nagu base, base-devel, linux, linux-firmware, efibootmgr ja valikulised profiilipaketid." + +msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." +msgstr "Kui soovite veebibrauserit, näiteks firefox või chromium, saate selle määrata järgmises viipas." + +msgid "Write additional packages to install (space separated, leave blank to skip): " +msgstr "Kirjutage paigaldatavad lisapaketid (tühikutega eraldatuna, jätke tühjaks, kui soovite vahele jätta): " + +msgid "Copy ISO network configuration to installation" +msgstr "ISO-võrgu konfiguratsiooni kopeerimine paigaldusse" + +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Kasutage NetworkManagerit (vajalik interneti graafiliseks konfigureerimiseks GNOME-s ja KDE-s)." + +msgid "Select one network interface to configure" +msgstr "Valige üks võrguliides konfigureerimiseks" + +msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" +msgstr "Valige, millist režiimi soovite konfigureerida \"{}\" või jätke vahele, kasutada vaikimisi režiimi \"{}\"" + +msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " +msgstr "Sisestage IP ja alamvõrk {} (näide: 192.168.0.5/24): " + +msgid "Enter your gateway (router) IP address or leave blank for none: " +msgstr "Sisestage oma võrguvärava (ruuteri) IP-aadress või jätke tühjaks, kui see puudub: " + +msgid "Enter your DNS servers (space separated, blank for none): " +msgstr "Sisestage oma DNS-serverid (tühikuga eraldatud, jätke tühjaks, kui neid ei ole): " + +msgid "Select which filesystem your main partition should use" +msgstr "Valige, millist failisüsteemi teie peamine partitsioon peaks kasutama" + +msgid "Current partition layout" +msgstr "Praegune partitsiooni paigutus" + +msgid "" +"Select what to do with\n" +"{}" +msgstr "" +"Valige, mida teha\n" +"{}" + +msgid "Enter a desired filesystem type for the partition" +msgstr "Sisestage partitsiooni jaoks soovitud failisüsteemi tüüp" + +msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " +msgstr "Sisestage alguskoht (parted ühikutes: s, GB, % jne; vaikimisi: {}): " + +msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " +msgstr "Sisestage lõpu asukoht (parted ühikutes: s, GB, % jne; näiteks: {}): " + +msgid "{} contains queued partitions, this will remove those, are you sure?" +msgstr "{} sisaldab järjekorras olevaid partitsioone, see eemaldab need, kas olete kindel?" + +msgid "" +"{}\n" +"\n" +"Select by index which partitions to delete" +msgstr "" +"{}\n" +"\n" +"\n" +"Valige indeksi järgi, milliseid partitsioone kustutada" + +msgid "" +"{}\n" +"\n" +"Select by index which partition to mount where" +msgstr "" +"{}\n" +"\n" +"\n" +"Valige indeksi järgi, millise partitsiooni kuhu paigaldada" + +msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Partitsioonide kinnituspunktid on suhtelised installeerimise sees, näiteks boot oleks /boot." + +msgid "Select where to mount partition (leave blank to remove mountpoint): " +msgstr "Valige, kuhu partitsiooni paigaldada (jätke tühjaks, et eemaldada paigalduspunkt): " + +msgid "" +"{}\n" +"\n" +"Select which partition to mask for formatting" +msgstr "" +"{}\n" +"\n" +"Valige, millist partitsiooni soovite vormindamiseks maskeerida" + +msgid "" +"{}\n" +"\n" +"Select which partition to mark as encrypted" +msgstr "" +"{}\n" +"\n" +"\n" +"Valige, millist partitsiooni soovite krüpteerituks märkida" + +msgid "" +"{}\n" +"\n" +"Select which partition to mark as bootable" +msgstr "" +"{}\n" +"\n" +"\n" +"Valige, millist partitsiooni soovite märkida käivitatavaks" + +msgid "" +"{}\n" +"\n" +"Select which partition to set a filesystem on" +msgstr "" +"{}\n" +"\n" +"\n" +"Valige, millisele partitsioonile failisüsteemi määrata" + +msgid "Enter a desired filesystem type for the partition: " +msgstr "Sisestage partitsiooni jaoks soovitud failisüsteemi tüüp: " + +msgid "Archinstall language" +msgstr "Archinstalli keel" + +msgid "Wipe all selected drives and use a best-effort default partition layout" +msgstr "Pühkige kõik valitud kettad ja kasutage parimat võimalikku vaikimisi partitsiooni paigutust" + +msgid "Select what to do with each individual drive (followed by partition usage)" +msgstr "Valige, mida teha iga üksiku kettaga (järgneb partitsiooni kasutamine)" + +msgid "Select what you wish to do with the selected block devices" +msgstr "Valige, mida soovite valitud plokkseadmetega teha" + +msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments" +msgstr "See on nimekiri eelprogrammeeritud profiilidest, need võivad lihtsustada selliste asjade nagu töölauakeskkondade paigaldamist" + +msgid "Select keyboard layout" +msgstr "Valige klaviatuuri paigutus" + +msgid "Select one of the regions to download packages from" +msgstr "Valige üks piirkondadest, kust pakette alla laadida" + +msgid "Select one or more hard drives to use and configure" +msgstr "Valige üks või mitu kõvaketast kasutamiseks ja konfigureerimiseks" + +msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." +msgstr "Parima ühilduvuse saavutamiseks oma AMD riistvaraga võiksite kasutada kas kõiki avatud lähtekoodiga või AMD/ATI valikuid." + +msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" +msgstr "" +"Parima ühilduvuse saavutamiseks oma Inteli riistvaraga võiksite kasutada kas kõiki avatud lähtekoodiga või Inteli valikuid.\n" +"\n" + +msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" +msgstr "" +"Parima ühilduvuse saavutamiseks oma Nvidia riistvaraga peaksite kasutama Nvidia enda toodetud draiverit.\n" +"\n" +"\n" + +msgid "" +"\n" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"Valige graafikadraiver või jätke tühjaks, et paigaldada kõik avatud lähtekoodiga draiverid" + +msgid "All open-source (default)" +msgstr "Kõik avatud lähtekoodiga (vaikimisi)" + +msgid "Choose which kernels to use or leave blank for default \"{}\"" +msgstr "Valige, milliseid tuumasid soovite kasutada või jätke vaikimisi tühjaks \"{}\"" + +msgid "Choose which locale language to use" +msgstr "Valige, millist asukoha keelt kasutada" + +msgid "Choose which locale encoding to use" +msgstr "Valige, millist asukoha kodeeringut kasutada" + +msgid "Select one of the values shown below: " +msgstr "Valige üks allpool esitatud väärtustest: " + +msgid "Select one or more of the options below: " +msgstr "Valige üks või mitu järgmistest võimalustest: " + +msgid "Adding partition...." +msgstr "Partitsiooni lisamine...." + +msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." +msgstr "Partitsiooni lisamine...." + +msgid "Error: Listing profiles on URL \"{}\" resulted in:" +msgstr "Viga: URL-i \"{}\" profiilide loetlemine andis tulemuseks:" + +msgid "Error: Could not decode \"{}\" result as JSON:" +msgstr "Viga: {}\" tulemust JSON-ina dekodeerida:" + +msgid "Keyboard layout" +msgstr "Klaviatuuri paigutus" + +msgid "Mirror region" +msgstr "Peegelregioon" + +msgid "Locale language" +msgstr "Kohalik keel" + +msgid "Locale encoding" +msgstr "Kohaliku keele kodeering" + +msgid "Drive(s)" +msgstr "Draiv(id)" + +msgid "Disk layout" +msgstr "Ketta paigutus" + +msgid "Encryption password" +msgstr "Krüpteerimise parool" + +msgid "Swap" +msgstr "Swap" + +msgid "Bootloader" +msgstr "Buudilaadija" + +msgid "Root password" +msgstr "Juur parool" + +msgid "Superuser account" +msgstr "Superkasutaja konto" + +msgid "User account" +msgstr "Kasutaja konto" + +msgid "Profile" +msgstr "Profiil" + +msgid "Audio" +msgstr "Audio" + +msgid "Kernels" +msgstr "Kernelid" + +msgid "Additional packages" +msgstr "Täiendavad paketid" + +msgid "Network configuration" +msgstr "Võrgu konfiguratsioon" + +msgid "Automatic time sync (NTP)" +msgstr "Automaatne ajasünkroonimine" + +msgid "Install ({} config(s) missing)" +msgstr "Install ({} config(id) puudu(vad)" + +msgid "" +"You decided to skip harddrive selection\n" +"and 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?" +msgstr "" +"Sa otsustasid kõvaketta valiku vahele jätta\n" +"ja kasutad mis tahes draivi seadistust, mis on paigaldatud {} (eksperimentaalne)\n" +"HOIATUS: Archinstall ei kontrolli selle seadistuse sobivust\n" +"Kas soovite jätkata?" + +msgid "Re-using partition instance: {}" +msgstr "Partitsiooni instantsi taaskasutamine: {}" + +msgid "Create a new partition" +msgstr "Uue partitsiooni loomine" + +msgid "Delete a partition" +msgstr "Kustuta paritsioon" + +msgid "Clear/Delete all partitions" +msgstr "Puhasta/Kustuta kõik partitsioonid" + +msgid "Assign mount-point for a partition" +msgstr "Määrake partitsioonile kinnituspunkt" + +msgid "Mark/Unmark a partition to be formatted (wipes data)" +msgstr "Märgistage/mittemärgistage vormindatav partitsioon (kustutab andmed)" + +msgid "Mark/Unmark a partition as encrypted" +msgstr "Märgistage/mittemärgistage partitsiooni krüpteerituks" + +msgid "Mark/Unmark a partition as bootable (automatic for /boot)" +msgstr "Märgistage/mittemärgistage partitsiooni käivitatavaks (automaatne /boot jaoks)" + +msgid "Set desired filesystem for a partition" +msgstr "Soovitud failisüsteemi määramine partitsiooni jaoks" + +msgid "Abort" +msgstr "Katkesta" + +msgid "Hostname" +msgstr "Hostinimi" + +msgid "Not configured, unavailable unless setup manually" +msgstr "Ei ole konfigureeritud, ei ole saadaval, kui seda ei ole käsitsi seadistatud" + +msgid "Timezone" +msgstr "Ajatsoon" + +msgid "Set/Modify the below options" +msgstr "Määrake/muutke järgmisi valikuid" + +msgid "Install" +msgstr "Install" + +msgid "" +"Use ESC to skip\n" +"\n" +msgstr "" +"Kasutage ESC vahelejätmiseks\n" +"\n" +"\n" +"\n" +"\n" + +msgid "Suggest partition layout" +msgstr "Soovitage partitsiooni paigutust" + +msgid "Enter a password: " +msgstr "Sisestage parool: " + +msgid "Enter a encryption password for {}" +msgstr "Sisestage {}'le krüpteerimis parool" + +msgid "Enter disk encryption password (leave blank for no encryption): " +msgstr "Sisestage ketta krüpteerimise parool (krüpteerimise puudumisel jätke see tühjaks): " + +msgid "Create a required super-user with sudo privileges: " +msgstr "Looge nõutav sudo õigustega superkasutaja: " + +msgid "Enter root password (leave blank to disable root): " +msgstr "Sisestage juur parool (jätke tühjaks, et keelata root): " + +msgid "Password for user \"{}\": " +msgstr "Kasutaja \"{}\" parool: " + +msgid "Verifying that additional packages exist (this might take a few seconds)" +msgstr "Täiendavate pakettide olemasolu kontrollimine (see võib võtta paar sekundit)" + +msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" +msgstr "Kas soovite kasutada automaatset ajasünkroniseerimist (NTP) vaikimisi ajaserveritega?\n" + +msgid "" +"Hardware time and other post-configuration steps might be required in order for NTP to work.\n" +"For more information, please check the Arch wiki" +msgstr "" +"NTP toimimiseks võib olla vaja riistvara aega ja muid konfiguratsioonijärgseid samme.\n" +"Lisateavet leiate Archi wikist" + +msgid "Enter a username to create an additional user (leave blank to skip): " +msgstr "Sisestage kasutajanimi, et luua lisakasutaja (jätke tühjaks, et vahele jätta): " + +msgid "Use ESC to skip\n" +msgstr "Kasutage ESC'i et vahele jätta\n" + +msgid "" +"\n" +" Choose an object from the list, and select one of the available actions for it to execute" +msgstr "" +"\n" +" Valige loetelust objekt ja valige selle täitmiseks üks olemasolevatest toimingutest" + +msgid "Cancel" +msgstr "Tühista" + +msgid "Confirm and exit" +msgstr "Kinnita ja lahku" + +msgid "Add" +msgstr "Lisa" + +msgid "Copy" +msgstr "Kopeeri" + +msgid "Edit" +msgstr "Muuda" + +msgid "Delete" +msgstr "Kustuta" + +msgid "Select an action for '{}'" +msgstr "Valige tegevus '{}' jaoks" + +msgid "Copy to new key:" +msgstr "Kopeeri uude võtmesse:" + +msgid "Unknown nic type: {}. Possible values are {}" +msgstr "Tundmatu nic-tüüp: {}. Võimalikud väärtused on {}" + +msgid "" +"\n" +"This is your chosen configuration:" +msgstr "" +"\n" +"See on teie valitud konfiguratsioon:" + +msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." +msgstr "Pacman on juba käivitatud ja ootab maksimaalselt 10 minutit, kuni see lõpeb." + +msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." +msgstr "Eelnev pacmani lukk ei ole kunagi väljunud. Enne archinstall'i kasutamist puhastage olemasolevad pacmani sessioonid." + +msgid "Choose which optional additional repositories to enable" +msgstr "Valige, milliseid valikulisi lisa repositooriumeid soovite lubada" + +msgid "Add a user" +msgstr "Lisa kasutaja" + +msgid "Change password" +msgstr "Muuda parool" + +msgid "Promote/Demote user" +msgstr "Edenda/alanda kasutajat" + +msgid "Delete User" +msgstr "Kustuta kasutaja" + +msgid "" +"\n" +"Define a new user\n" +msgstr "" +"\n" +"Uue kasutaja määramine\n" + +msgid "User Name : " +msgstr "Kasutajanimi : " + +msgid "Should {} be a superuser (sudoer)?" +msgstr "Kas {} peaks olema superkasutaja (sudoer)?" + +msgid "Define users with sudo privilege: " +msgstr "Määrake sudo privileegiga kasutajad: " + +msgid "No network configuration" +msgstr "Puudub võrgu konfiguratsioon" + +msgid "Set desired subvolumes on a btrfs partition" +msgstr "Määrake soovitud alamköited btrfs-i partitsioonile" + +msgid "" +"{}\n" +"\n" +"Select which partition to set subvolumes on" +msgstr "" +"{}\n" +"\n" +"Valige, millisele partitsioonile alamkogumid määrata" + +msgid "Manage btrfs subvolumes for current partition" +msgstr "Hallake praeguse partitsiooni btrfs-i alamköiteid" + +msgid "No configuration" +msgstr "Puudub konfiguratsioon" + +msgid "Save user configuration" +msgstr "Salvesta kasutaja konfiguratsioon" + +msgid "Save user credentials" +msgstr "Salvesta kasutaja volitused" + +msgid "Save disk layout" +msgstr "Salvesta ketta paigutus" + +msgid "Save all" +msgstr "Salvesta kõik" + +msgid "Choose which configuration to save" +msgstr "Valige milline konfiguratsioon salvestada" + +msgid "Enter a directory for the configuration(s) to be saved: " +msgstr "Sisestage salvestatava(te) konfiguratsiooni(de) kataloog: " + +msgid "Not a valid directory: {}" +msgstr "Ei ole sobiv kataloog: {}" + +msgid "The password you are using seems to be weak," +msgstr "Teie kasutatav salasõna näib olevat nõrk," + +msgid "are you sure you want to use it?" +msgstr "oled sa kindel et soovid seda kasutada?" + +msgid "Optional repositories" +msgstr "Valikulised repositooriumid" + +msgid "Save configuration" +msgstr "Salvesta konfiguratsioon" + +msgid "Missing configurations:\n" +msgstr "Puuduvad konfiguratsioonid:\n" + +msgid "Either root-password or at least 1 superuser must be specified" +msgstr "Tuleb määrata kas juur-parool või vähemalt 1 superkasutaja" + +msgid "Manage superuser accounts: " +msgstr "Hallake superkasutaja kontosi " + +msgid "Manage ordinary user accounts: " +msgstr "Hallake tavalisi kasutajaid: " + +msgid " Subvolume :{:16}" +msgstr " Alamköide :{:16}" + +msgid " mounted at {:16}" +msgstr " paigaldatud {:16}" + +msgid " with option {}" +msgstr " koos valikuga {}" + +msgid "" +"\n" +" Fill the desired values for a new subvolume \n" +msgstr "" +"\n" +" Täida uue alamhulga soovitud väärtused \n" + +msgid "Subvolume name " +msgstr "Alamhulga nimi " + +msgid "Subvolume mountpoint" +msgstr "Alamhulga kinnituspunkt" + +msgid "Subvolume options" +msgstr "Alamhulga valikud" + +msgid "Save" +msgstr "Salvesta" + +msgid "Subvolume name :" +msgstr "Alamhulga nimi:" + +msgid "Select a mount point :" +msgstr "Valige kinnitus punkt :" + +msgid "Select the desired subvolume options " +msgstr "Valige soovitud alamhulga valikud " + +msgid "Define users with sudo privilege, by username: " +msgstr "Määrake sudo õigustega kasutajad kasutajanime järgi: " + +msgid "[!] A log file has been created here: {}" +msgstr "[!] Siin on loodud logifail: {}" + +msgid "Would you like to use BTRFS subvolumes with a default structure?" +msgstr "Kas soovite kasutada BTRFS-i alamkogusid vaikimisi struktuuriga?" + +msgid "Would you like to use BTRFS compression?" +msgstr "Kas soovite kasutada BTRFS-i tihendamist?" + +msgid "Would you like to create a separate partition for /home?" +msgstr "Kas soovite luua eraldi partitsiooni /home jaoks?" + +msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" +msgstr "Valitud ketastel ei ole automaatseks soovituseks vajalikku minimaalset mahtu\n" + +msgid "Minimum capacity for /home partition: {}GB\n" +msgstr "Minimaalne mahutavus /home partitsioonile: {}GB\n" + +msgid "Minimum capacity for Arch Linux partition: {}GB" +msgstr "Arch Linuxi partitsiooni minimaalne mahutavus: {}GB" + +msgid "Continue" +msgstr "Jätka" + +msgid "yes" +msgstr "jah" + +msgid "no" +msgstr "ei" + +msgid "set: {}" +msgstr "seadista: {}" + +msgid "Manual configuration setting must be a list" +msgstr "Käsitsi konfiguratsiooniseade peab olema loend" + +msgid "No iface specified for manual configuration" +msgstr "Käsitsi seadistamiseks pole iface'i määratud" + +msgid "Manual nic configuration with no auto DHCP requires an IP address" +msgstr "Manuaalne nici konfigureerimine ilma automaatse DHCPta nõuab IP-aadressi" + +msgid "Add interface" +msgstr "Lisa liides" + +msgid "Edit interface" +msgstr "Muuda liidest" + +msgid "Delete interface" +msgstr "Kustutage liides" + +msgid "Select interface to add" +msgstr "Valige millist liidest lisada" + +msgid "Manual configuration" +msgstr "Manuaalne konfiguratsioon" + +msgid "Mark/Unmark a partition as compressed (btrfs only)" +msgstr "Märgistada/mittemärgistada partitsiooni kompressiooniks (ainult btrfs)" + +msgid "The password you are using seems to be weak, are you sure you want to use it?" +msgstr "Teie kasutatav salasõna tundub olevat nõrk, kas olete kindel, et soovite seda kasutada?" + +msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" +msgstr "Pakub valikut töölauakeskkondi ja plaadistatavaid aknahaldureid, nt gnome, kde, sway" + +msgid "Select your desired desktop environment" +msgstr "Valige soovitud töölauakeskkonda" + +msgid "A very basic installation that allows you to customize Arch Linux as you see fit." +msgstr "Väga lihtne paigaldus, mis võimaldab teil Arch Linuxi kohandada vastavalt oma äranägemisele." + +msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" +msgstr "Annab valiku erinevate serveripakettide paigaldamiseks ja aktiveerimiseks, nt httpd, nginx, mariadb" + +msgid "Choose which servers to install, if none then a minimal installation will be done" +msgstr "Valige, milliseid servereid paigaldada, kui ühtegi ei ole, siis tehakse minimaalne paigaldus" + +msgid "Installs a minimal system as well as xorg and graphics drivers." +msgstr "Installeerib minimaalse süsteemi ning xorgi ja graafikadraiverid." + +msgid "Press Enter to continue." +msgstr "Jätkamiseks vajutage Enter." + +msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?" +msgstr "Kas soovite chrootida äsja loodud installatsiooni ja teostada installeerimisjärgset konfigureerimist?" + +msgid "Are you sure you want to reset this setting?" +msgstr "Kas olete kindel, et soovite seda seadistust lähtestada?" + +msgid "Select one or more hard drives to use and configure\n" +msgstr "Valige üks või mitu kasutatavat kõvaketast ja konfigureerige\n" + +msgid "Any modifications to the existing setting will reset the disk layout!" +msgstr "Olemasoleva seadistuse muutmine lähtestab ketta paigutuse!" + +msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" +msgstr "Kui lähtestate kõvaketta valiku, lähtestab see ka praeguse kettapaigutuse. Oled sa kindel?" + +msgid "Save and exit" +msgstr "Salvesta ja lahku" + +msgid "" +"{}\n" +"contains queued partitions, this will remove those, are you sure?" +msgstr "" +"{}\n" +"sisaldab järjekorras olevaid partitsioone, see eemaldab need, oled sa kindel?" + +msgid "No audio server" +msgstr "Puudub audio server" + +msgid "(default)" +msgstr "(vaikimisi)" + +msgid "Use ESC to skip" +msgstr "Kasutage ESC vahelejätmiseks" + +msgid "" +"Use CTRL+C to reset current selection\n" +"\n" +msgstr "" +"Kasuta CTRL+C praeguse valiku lähtestamiseks\n" +"\n" + +msgid "Copy to: " +msgstr "Kopeeri: " + +msgid "Edit: " +msgstr "Muuda: " + +msgid "Key: " +msgstr "Võti: " + +msgid "Edit {}: " +msgstr "Muuda {}: " + +msgid "Add: " +msgstr "Lisa: " + +msgid "Value: " +msgstr "Väärtus: " + +msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" +msgstr "Saad vahele jätta ketta valimise ja partitsioneerimise ning kasutada mis tahes draivi-komplekti, mis on paigaldatud /mnt (eksperimentaalne)." + +msgid "Select one of the disks or skip and use /mnt as default" +msgstr "Valige üks ketastest või jätke vahele ja kasutage vaikimisi /mnt" + +msgid "Select which partitions to mark for formatting:" +msgstr "Valige, milliseid partitsioone soovite vormindamiseks märkida:" + +msgid "Use HSM to unlock encrypted drive" +msgstr "Kasutage HSM-i krüpteeritud draivi avamiseks" + +msgid "Device" +msgstr "Seade" + +msgid "Size" +msgstr "Suurus" + +msgid "Free space" +msgstr "Vaba mälu" + +msgid "Bus-type" +msgstr "Bussi tüüp" + +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "Tuleb määrata kas root-sõna või vähemalt 1 kasutaja, kellel on sudo õigused" + +msgid "Enter username (leave blank to skip): " +msgstr "Sisestage kasutajanimi (jätke tühjaks, et vahele jätta): " + +msgid "The username you entered is invalid. Try again" +msgstr "Teie sisestatud kasutajanimi ei sobi. Proovige uuesti" + +msgid "Should \"{}\" be a superuser (sudo)?" +msgstr "Kas \"{}\" peaks olema superkasutaja (sudo)?" + +msgid "Select which partitions to encrypt" +msgstr "Valige, milliseid partitsioone krüpteerida" + +msgid "very weak" +msgstr "väga nõrk" + +msgid "weak" +msgstr "nõrk" + +msgid "moderate" +msgstr "keskmine" + +msgid "strong" +msgstr "tugev" + +msgid "Add subvolume" +msgstr "Lisage alamhulk" + +msgid "Edit subvolume" +msgstr "Osahulga redigeerimine" + +msgid "Delete subvolume" +msgstr "Kustuta osahulk" + +msgid "Configured {} interfaces" +msgstr "Konfigureeritud {} liidesed" + +msgid "This option enables the number of parallel downloads that can occur during installation" +msgstr "See valik võimaldab installimise ajal valida paralleelsete allalaadimiste arvu" + +#, python-brace-format +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +" (Enter a value between 1 to {max_downloads})\n" +"Note:" +msgstr "" +"Sisestage lubatavate paralleelsete allalaadimiste arv.\n" +" (Sisestage väärtus vahemikus 1 kuni {max_downloads})\n" +"note:" + +msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - Maksimaalne väärtus : {max_downloads} ( Võimaldab {max_downloads} paralleelset allalaadimist, lubab {max_downloads+1} allalaadimist korraga )" + +msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" +msgstr " - Minimaalne väärtus : 1 ( Võimaldab 1 paralleelset allalaadimist, võimaldab 2 allalaadimist korraga )" + +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" +msgstr " - Keela/Vaikimisi : 0 ( keelab paralleelse allalaadimise, võimaldab ainult 1 allalaadimist korraga )" + +#, python-brace-format +msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" +msgstr "Vale sisestus! Proovige uuesti kehtiva sisendiga [1 {max_downloads} või 0 keelamiseks]." + +msgid "Parallel Downloads" +msgstr "Paralleelsed allalaadimised" + +msgid "ESC to skip" +msgstr "ESC vahelejätmiseks" + +msgid "CTRL+C to reset" +msgstr "CTRL+C lähtestamiseks" + +msgid "TAB to select" +msgstr "TAB valimiseks" + +msgid "[Default value: 0] > " +msgstr "[Vaikimisi väärtus: 0] > " + +msgid "To be able to use this translation, please install a font manually that supports the language." +msgstr "Selleks, et seda tõlget kasutada, paigaldage käsitsi fondi, mis toetab seda keelt." + +msgid "The font should be stored as {}" +msgstr "Font tuleks salvestada kujul {}" + +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "Archinstall vajab käivitamiseks juurkasutaja õigusi. Vaata --help." + +msgid "Select an execution mode" +msgstr "Valige täitmisrežiim" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "Ei õnnestu profiili hankida määratud url'ist: {}" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "Profiilidel peab olema unikaalne nimi, kuid leiti topeltnimega profiilide määratlusi: {}" + +msgid "Select one or more devices to use and configure" +msgstr "Valige üks või mitu seadet mida kasutada ja konfigureerida" + +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "Kui lähtestate seadme valiku, siis lähtestate sellega ka praeguse draivi paigutuse. Kas olete kindel?" + +msgid "Existing Partitions" +msgstr "Olemasolevad Jaotused" + +msgid "Select a partitioning option" +msgstr "Valige jaotuste valik" + +msgid "Enter the root directory of the mounted devices: " +msgstr "Sisestage paigaldatud seadmete juurkataloog: " + +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "Minimaalne mahutavus /home partitsioonile: {}GiB\n" + +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Arch Linuxi partitsiooni minimaalne mahutavus: {}GiB" + +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "See on nimekiri eelprogrammeeritud profiilidest_bck, need võivad lihtsustada selliste asjade nagu töölauakeskkondade paigaldamist" + +msgid "Current profile selection" +msgstr "Praegune profiilivalik" + +msgid "Remove all newly added partitions" +msgstr "Eemaldage kõik äsja lisatud partitsioonid" + +msgid "Assign mountpoint" +msgstr "Määra paigaldamis punkt" + +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Märgistada/mittemärgistada vormindamiseks (kustutab andmed)" + +msgid "Mark/Unmark as bootable" +msgstr "Märgistada/mittemärgistada käivitatavak" + +msgid "Change filesystem" +msgstr "Failisüsteemi muutmine" + +msgid "Mark/Unmark as compressed" +msgstr "Märgistada/mittemärgistada kui tihendatud" + +msgid "Set subvolumes" +msgstr "Alamköite määramine" + +msgid "Delete partition" +msgstr "Kustuta partitsioon" + +msgid "Partition" +msgstr "Partitsioon" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "See partitsioon on praegu krüpteeritud, selle vormindamiseks tuleb määrata failisüsteem" + +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr "Partitsiooni kinnituspunktid on suhtelised installeerimise sees, näiteks boot oleks /boot." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "Kui paigalduspunkt /boot on määratud, siis märgitakse partitsioon ka käivitatavaks." + +msgid "Mountpoint: " +msgstr "Paigalduspunkt: " + +msgid "Current free sectors on device {}:" +msgstr "Praegused vabad sektorid seadmes {}:" + +msgid "Total sectors: {}" +msgstr "Sektorid kokku: {}" + +msgid "Enter the start sector (default: {}): " +msgstr "Sisestage algussektor (vaikimisi: {}): " + +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Sisestage partitsiooni lõppsektor (protsent või plokkide number, vaikimisi: {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "See eemaldab kõik äsja lisatud partitsioonid, jätka?" + +msgid "Partition management: {}" +msgstr "Partitsiooni haldamine:{}" + +msgid "Total length: {}" +msgstr "Kogupikkus: {}" + +msgid "Encryption type" +msgstr "Krüpteerimise tüüp" + +msgid "Partitions" +msgstr "Partitsioonid" + +msgid "No HSM devices available" +msgstr "Puuduvad HSM seadmed" + +msgid "Partitions to be encrypted" +msgstr "Krüpteeritavad partitsioonid" + +msgid "Select disk encryption option" +msgstr "Valige ketta krüpteerimise valik" + +msgid "Select a FIDO2 device to use for HSM" +msgstr "Valige HSM-i jaoks kasutatav FIDO2-seade" + +msgid "Use a best-effort default partition layout" +msgstr "Kasutage parimat võimalikku vaikimisi partitsiooni paigutust" + +msgid "Manual Partitioning" +msgstr "Käsitsi partitsioneerimine" + +msgid "Pre-mounted configuration" +msgstr "Eelnevalt paigaldatud konfiguratsioon" + +msgid "Unknown" +msgstr "Tundmatu" + +msgid "Partition encryption" +msgstr "Partitsiooni krüpteerimine" + +msgid " ! Formatting {} in " +msgstr " ! Vormindamine {} " + +msgid "← Back" +msgstr "Tagasi" + +msgid "Disk encryption" +msgstr "Ketta krüpteerimine" + +msgid "Configuration" +msgstr "Konfiguratsioon" + +msgid "Password" +msgstr "Parool" + +msgid "All settings will be reset, are you sure?" +msgstr "Kõik seaded lähtestatakse, kas sa oled kindel?" + +msgid "Back" +msgstr "Tagasi" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "Palun valige millist tervitajat installida valitud profiilidele" + +msgid "Environment type: {}" +msgstr "Keskkonna tüüp: {}" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "Sway ei toeta Nvidia enda draiverit. Tõenäoliselt tekib teil probleeme, kas teile sobib see?" + +msgid "Installed packages" +msgstr "Installeeritud paketid" + +msgid "Add profile" +msgstr "Lisa profiil" + +msgid "Edit profile" +msgstr "Muuda profiili" + +msgid "Delete profile" +msgstr "Kustuta profiil" + +msgid "Profile name: " +msgstr "Profiili nimi: " + +msgid "The profile name you entered is already in use. Try again" +msgstr "Sisestatud profiili nimi on juba kasutuses. Proovi uuesti" + +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Selle profiiliga paigaldatavad paketid (tühikuga eraldatud, jäta tühjaks, et jätta vahele): " + +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Selle profiiliga lubatavad teenused (tühikuga eraldatud, jäta tühjaks, kui soovid vahele jätta): " + +msgid "Should this profile be enabled for installation?" +msgstr "Kas see profiil peaks olema paigaldamiseks lubatud?" + +msgid "Create your own" +msgstr "Loo oma" + +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"Vali graafikadraiver või jäta tühjaks, et paigaldada kõik avatud lähtekoodiga draiverid" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "Sway vajab juurdepääsu teie seatile (riistvaraseadmete kogum, st klaviatuur, hiir jne." + +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" +"\n" +"\n" +"Valige valik, et anda Sway'le juurdepääs teie riistvarale" + +msgid "Graphics driver" +msgstr "Graafika draiver" + +msgid "Greeter" +msgstr "Tervitaja" + +msgid "Please chose which greeter to install" +msgstr "Palun valige millist tervitajat installida" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "See on eelnevalt programmeeritud vaikimisi_profiilide nimekiri" + +msgid "Disk configuration" +msgstr "Ketta konfiguratsioon" + +msgid "Profiles" +msgstr "Profiilid" + +msgid "Finding possible directories to save configuration files ..." +msgstr "Võimalike kataloogide leidmine konfiguratsioonifailide salvestamiseks ..." + +msgid "Select directory (or directories) for saving configuration files" +msgstr "Konfigureerimisfailide salvestamise kataloogi (või kataloogide) valimine" + +msgid "Add a custom mirror" +msgstr "Lisa kohandatud peegel" + +msgid "Change custom mirror" +msgstr "Muuda kohandatud peeglit" + +msgid "Delete custom mirror" +msgstr "Kustuta kohandadtud peeglit" + +msgid "Enter name (leave blank to skip): " +msgstr "Sisesta nimi (jätke tühjaks, et vahele jätta): " + +msgid "Enter url (leave blank to skip): " +msgstr "Sisesta url (jätke tühjaks, et vahele jätta): " + +msgid "Select signature check option" +msgstr "Valige allkirja kontrollimise võimalus" + +msgid "Select signature option" +msgstr "Valige allkirja valik" + +msgid "Custom mirrors" +msgstr "Kohandatud peegel" + +msgid "Defined" +msgstr "Defineeritud" + +msgid "Mirrors" +msgstr "Peeglid" + +msgid "Mirror regions" +msgstr "Peegel regioonid" + +msgid "Save user configuration (including disk layout)" +msgstr "Salvesta kasutaja konfiguratsioon (kaasa arvatud plaadi paigutus)" + +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "" +"Sisestage salvestatava(te) konfiguratsiooni(de) kataloog (vahekaartide täitmine lubatud)\n" +"Salvesta kataloog: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" +"Kas soovite salvestada {} konfiguratsioonifaili(d) järgmisesse asukohta?\n" +"\n" +"{}" + +msgid "Saving {} configuration files to {}" +msgstr "{} konfiguratsioonifailide salvestamine {}" diff --git a/archinstall/locales/languages.json b/archinstall/locales/languages.json index 954d5a26..271fbb36 100644 --- a/archinstall/locales/languages.json +++ b/archinstall/locales/languages.json @@ -36,7 +36,7 @@ {"abbr": "el", "lang": "Modern Greek (1453-)", "translated_lang": "Ελληνικά"}, {"abbr": "en", "lang": "English"}, {"abbr": "eo", "lang": "Esperanto"}, - {"abbr": "et", "lang": "Estonian"}, + {"abbr": "et", "lang": "Estonian", "translated_lang": "Eesti" }, {"abbr": "eu", "lang": "Basque"}, {"abbr": "ee", "lang": "Ewe"}, {"abbr": "fo", "lang": "Faroese"}, diff --git a/archinstall/locales/pl/LC_MESSAGES/base.po b/archinstall/locales/pl/LC_MESSAGES/base.po index 50348715..8c3ce4f5 100644 --- a/archinstall/locales/pl/LC_MESSAGES/base.po +++ b/archinstall/locales/pl/LC_MESSAGES/base.po @@ -1145,6 +1145,14 @@ msgstr "" msgid "Defined" msgstr "" +#, fuzzy +msgid "Mirrors" +msgstr "Region lustra" + +#, fuzzy +msgid "Mirror regions" +msgstr "Region lustra" + #, fuzzy msgid "Save user configuration (including disk layout)" msgstr "Zapisz konfiguracje użytkownika" -- cgit v1.2.3-70-g09d2 From 91ee3575d369becdf1f0b93a259929b6662170c7 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Mon, 5 Jun 2023 18:47:14 +1000 Subject: Fix 1749 (#1840) Co-authored-by: Daniel Girtler Co-authored-by: Anton Hvornum --- archinstall/lib/interactions/general_conf.py | 10 ++-- archinstall/locales/ar/LC_MESSAGES/base.po | 23 ++++---- archinstall/locales/base.pot | 15 ++++-- archinstall/locales/cs/LC_MESSAGES/base.mo | Bin 26115 -> 25981 bytes archinstall/locales/cs/LC_MESSAGES/base.po | 20 ++++--- archinstall/locales/de/LC_MESSAGES/base.po | 73 ++++++++++++++++++++++++-- archinstall/locales/el/LC_MESSAGES/base.mo | Bin 35562 -> 35428 bytes archinstall/locales/el/LC_MESSAGES/base.po | 20 ++++--- archinstall/locales/en/LC_MESSAGES/base.po | 11 ++-- archinstall/locales/es/LC_MESSAGES/base.po | 11 ++-- archinstall/locales/fr/LC_MESSAGES/base.mo | Bin 27562 -> 27428 bytes archinstall/locales/fr/LC_MESSAGES/base.po | 20 ++++--- archinstall/locales/id/LC_MESSAGES/base.mo | Bin 26392 -> 26258 bytes archinstall/locales/id/LC_MESSAGES/base.po | 20 ++++--- archinstall/locales/it/LC_MESSAGES/base.mo | Bin 26675 -> 26541 bytes archinstall/locales/it/LC_MESSAGES/base.po | 20 ++++--- archinstall/locales/ka/LC_MESSAGES/base.mo | Bin 44957 -> 44823 bytes archinstall/locales/ka/LC_MESSAGES/base.po | 20 ++++--- archinstall/locales/ko/LC_MESSAGES/base.mo | Bin 27355 -> 27221 bytes archinstall/locales/ko/LC_MESSAGES/base.po | 20 ++++--- archinstall/locales/nl/LC_MESSAGES/base.po | 11 ++-- archinstall/locales/pl/LC_MESSAGES/base.mo | Bin 25444 -> 25126 bytes archinstall/locales/pl/LC_MESSAGES/base.po | 21 +++++--- archinstall/locales/pt/LC_MESSAGES/base.po | 11 ++-- archinstall/locales/pt_BR/LC_MESSAGES/base.mo | Bin 27157 -> 27023 bytes archinstall/locales/pt_BR/LC_MESSAGES/base.po | 20 ++++--- archinstall/locales/ru/LC_MESSAGES/base.mo | Bin 36023 -> 35889 bytes archinstall/locales/ru/LC_MESSAGES/base.po | 20 ++++--- archinstall/locales/sv/LC_MESSAGES/base.po | 11 ++-- archinstall/locales/ta/LC_MESSAGES/base.mo | Bin 47610 -> 47476 bytes archinstall/locales/ta/LC_MESSAGES/base.po | 20 ++++--- archinstall/locales/tr/LC_MESSAGES/base.po | 11 ++-- archinstall/locales/uk/LC_MESSAGES/base.mo | Bin 36158 -> 36024 bytes archinstall/locales/uk/LC_MESSAGES/base.po | 20 ++++--- archinstall/locales/ur/LC_MESSAGES/base.po | 11 ++-- archinstall/locales/zh-CN/LC_MESSAGES/base.mo | Bin 24126 -> 23992 bytes archinstall/locales/zh-CN/LC_MESSAGES/base.po | 20 ++++--- 37 files changed, 326 insertions(+), 133 deletions(-) diff --git a/archinstall/lib/interactions/general_conf.py b/archinstall/lib/interactions/general_conf.py index 3b78847b..ad9ee386 100644 --- a/archinstall/lib/interactions/general_conf.py +++ b/archinstall/lib/interactions/general_conf.py @@ -151,10 +151,10 @@ def ask_additional_packages_to_install(preset: List[str] = []) -> List[str]: def add_number_of_parrallel_downloads(input_number :Optional[int] = None) -> Optional[int]: max_downloads = 5 print(_(f"This option enables the number of parallel downloads that can occur during installation")) - print(_(f"Enter the number of parallel downloads to be enabled.\n (Enter a value between 1 to {max_downloads})\nNote:")) - print(_(f" - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )")) - print(_(f" - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )")) - print(_(f" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )")) + print(str(_("Enter the number of parallel downloads to be enabled.\n (Enter a value between 1 to {})\nNote:")).format(max_downloads)) + print(str(_(" - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )")).format(max_downloads, max_downloads, max_downloads + 1)) + print(_(" - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )")) + print(_(" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )")) while True: try: @@ -165,7 +165,7 @@ def add_number_of_parrallel_downloads(input_number :Optional[int] = None) -> Opt input_number = max_downloads break except: - print(_(f"Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]")) + print(str(_("Invalid input! Try again with a valid input [1 to {}, or 0 to disable]")).format(max_downloads)) pacman_conf_path = pathlib.Path("/etc/pacman.conf") with pacman_conf_path.open() as f: diff --git a/archinstall/locales/ar/LC_MESSAGES/base.po b/archinstall/locales/ar/LC_MESSAGES/base.po index 6c37bdff..0944913a 100644 --- a/archinstall/locales/ar/LC_MESSAGES/base.po +++ b/archinstall/locales/ar/LC_MESSAGES/base.po @@ -751,14 +751,13 @@ msgstr "" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" msgstr "" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" @@ -1056,12 +1055,6 @@ msgstr "" msgid "Defined" msgstr "" -msgid "Mirrors" -msgstr "" - -msgid "Mirror regions" -msgstr "" - msgid "Save user configuration (including disk layout)" msgstr "" @@ -1078,3 +1071,15 @@ msgstr "" msgid "Saving {} configuration files to {}" msgstr "" + +msgid "Mirrors" +msgstr "" + +msgid "Mirror regions" +msgstr "" + +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr "" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "" diff --git a/archinstall/locales/base.pot b/archinstall/locales/base.pot index 2ca84604..cc01f5fc 100644 --- a/archinstall/locales/base.pot +++ b/archinstall/locales/base.pot @@ -793,16 +793,15 @@ msgid "" "installation" msgstr "" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" msgid "" -" - Maximum value : {max_downloads} ( Allows {max_downloads} parallel " -"downloads, allows {max_downloads+1} downloads at a time )" +" - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads " +"at a time )" msgstr "" msgid "" @@ -1146,3 +1145,11 @@ msgstr "" msgid "Mirror regions" msgstr "" + +msgid "" +" - Maximum value : {} ( Allows {} parallel downloads, allows " +"{max_downloads+1} downloads at a time )" +msgstr "" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "" diff --git a/archinstall/locales/cs/LC_MESSAGES/base.mo b/archinstall/locales/cs/LC_MESSAGES/base.mo index a256081c..b8808152 100644 Binary files a/archinstall/locales/cs/LC_MESSAGES/base.mo and b/archinstall/locales/cs/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/cs/LC_MESSAGES/base.po b/archinstall/locales/cs/LC_MESSAGES/base.po index cf6bd3a1..20f3c257 100644 --- a/archinstall/locales/cs/LC_MESSAGES/base.po +++ b/archinstall/locales/cs/LC_MESSAGES/base.po @@ -786,18 +786,17 @@ msgstr "Nakonfigurováno {} rozhraní" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "Tato možnost povolí specifikovaný počet paralelních stahování, která mohou nastat při instalaci" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" "Zadejte povolený počet paralelních stahování.\n" -" (Zadejte hodnotu mezi 1 a {max_downloads})\n" +" (Zadejte hodnotu mezi 1 a {})\n" "Poznámka:" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - Maximální hodnota : {max_downloads} (Povolí {max_downloads} paralelních stahování, povolí {max_downloads+1} stahování naráz )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - Maximální hodnota : {} (Povolí {} paralelních stahování, povolí {} stahování naráz )" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" msgstr " - Minimální hodnota : 1 (Povolí 1 paralelní stahování, povolí 2 stahování naráz)" @@ -805,9 +804,9 @@ msgstr " - Minimální hodnota : 1 (Povolí 1 paralelní stahování, povolí msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" msgstr " - Zakázáno/Výchozí : 0 (Zakáže paralelní stahování, povolí pouze 1 stahování naráz)" -#, python-brace-format +#, fuzzy, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "Neplatný vstup! Zkuste to, prosím, znovu s platným vstupem [1 až {max_downloads}, nebo 0 pro vypnutí]" +msgstr "Neplatný vstup! Zkuste to, prosím, znovu s platným vstupem [1 až {}, nebo 0 pro vypnutí]" msgid "Parallel Downloads" msgstr "Paralelní stahování" @@ -1163,3 +1162,10 @@ msgstr "Oblast zrcadla" #, fuzzy msgid "Mirror regions" msgstr "Oblast zrcadla" + +#, fuzzy +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - Maximální hodnota : {} (Povolí {} paralelních stahování, povolí {} stahování naráz )" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "Neplatný vstup! Zkuste to, prosím, znovu s platným vstupem [1 až {}, nebo 0 pro vypnutí]" diff --git a/archinstall/locales/de/LC_MESSAGES/base.po b/archinstall/locales/de/LC_MESSAGES/base.po index b20d6e90..6dfb0801 100644 --- a/archinstall/locales/de/LC_MESSAGES/base.po +++ b/archinstall/locales/de/LC_MESSAGES/base.po @@ -793,18 +793,17 @@ msgstr "{} Schnittstellen konfiguriert" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "Diese Option setzt die Nummer an parallelen Downloads, die während der Installtion durchgeführt werden" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" "Geben Sie die Nummer an parallelen Downloads an.\n" " (Wert zwischen 1 und {max_downloads})\n" "Achtung:" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - Maximalwert :{max_downloads} (Erlaubt {max_downloads} parallele Downloads, erlaubt {max_downloads+1} Downloads gleichzeitig)" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - Maximalwert :{} (Erlaubt {} parallele Downloads, erlaubt {} Downloads gleichzeitig)" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" msgstr "- Minimalwert : 1 (Erlaubt einen parallelen Download, erlaubt zwei Downloads gleichzeitig)" @@ -1188,6 +1187,72 @@ msgstr "" msgid "Saving {} configuration files to {}" msgstr "Konfiguration speichern" +#, fuzzy +msgid "Add a custom mirror" +msgstr "Benutzerkonto hinzufügen" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "Geben sie einen weiteren Benutzernamen an der angelegt werden soll (leer lassen um zu Überspringen): " + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "Geben sie einen weiteren Benutzernamen an der angelegt werden soll (leer lassen um zu Überspringen): " + +#, fuzzy +msgid "Select signature check option" +msgstr "Laufwerke-layout auswählen" + +#, fuzzy +msgid "Select signature option" +msgstr "Laufwerke-layout auswählen" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +#, fuzzy +msgid "Save user configuration (including disk layout)" +msgstr "Benutzerkonfiguration speichern" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "Geben sie eine Ordner an wo die Konfigurationen gespeichert werden sollen: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "Konfiguration speichern" + +#, fuzzy +msgid "Mirrors" +msgstr "Mirror-region" + +#, fuzzy +msgid "Mirror regions" +msgstr "Mirror-region" + +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr "" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "" + #~ msgid "Add :" #~ msgstr "Hinzufügen :" diff --git a/archinstall/locales/el/LC_MESSAGES/base.mo b/archinstall/locales/el/LC_MESSAGES/base.mo index 103eaf0c..9a8c99af 100644 Binary files a/archinstall/locales/el/LC_MESSAGES/base.mo and b/archinstall/locales/el/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/el/LC_MESSAGES/base.po b/archinstall/locales/el/LC_MESSAGES/base.po index 4a33dd5e..4b4db260 100644 --- a/archinstall/locales/el/LC_MESSAGES/base.po +++ b/archinstall/locales/el/LC_MESSAGES/base.po @@ -793,18 +793,17 @@ msgstr "Διαμορφωμένες {} διεπαφές" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "Αυτή η επιλογή θέτει τον αριθμό των παράλληλων λήψεων που μπορούν να συμβούν κατά την εγκατάσταση" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" "Εισάγετε τον αριθμό των παράλληλων λήψεων προς ενεργοποίηση.\n" -" (Εισάγετε μία τιμή από 1 μέχρι {max_downloads})\n" +" (Εισάγετε μία τιμή από 1 μέχρι {})\n" "Σημείωση:" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - Μέγιστη τιμή : {max_downloads} ( Επιτρέπει {max_downloads} παράλληλες λήψεις, επιτρέπει {max_downloads+1} λήψεις σε μία στιγμή )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - Μέγιστη τιμή : {} ( Επιτρέπει {} παράλληλες λήψεις, επιτρέπει {} λήψεις σε μία στιγμή )" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" msgstr " - Ελάχιστη τιμή : 1 ( Επιτρέπει 1 παράλληλη λήψη, επιτρέπει 2 λήψεις σε μία στιγμή )" @@ -812,9 +811,9 @@ msgstr " - Ελάχιστη τιμή : 1 ( Επιτρέπει 1 παράλλη msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" msgstr " - Απενεργοποίηση/Προκαθορισμένο : 0 ( Απενεργοποιεί τις παράλληλες λήψεις, επιτρέπει μόνο 1 λήψη σε μία στιγμή )" -#, python-brace-format +#, fuzzy, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "Μη έγκυρη είσοδος! Προσπαθήστε ξανά με μία έγκυρη είσοδο [1 μέχρι {max_downloads}, ή 0 για απενεργοποίηση]" +msgstr "Μη έγκυρη είσοδος! Προσπαθήστε ξανά με μία έγκυρη είσοδο [1 μέχρι {}, ή 0 για απενεργοποίηση]" msgid "Parallel Downloads" msgstr "Παράλληλες Λήψεις" @@ -1170,3 +1169,10 @@ msgstr "Περιοχή mirror" #, fuzzy msgid "Mirror regions" msgstr "Περιοχή mirror" + +#, fuzzy +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - Μέγιστη τιμή : {} ( Επιτρέπει {} παράλληλες λήψεις, επιτρέπει {} λήψεις σε μία στιγμή )" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "Μη έγκυρη είσοδος! Προσπαθήστε ξανά με μία έγκυρη είσοδο [1 μέχρι {}, ή 0 για απενεργοποίηση]" diff --git a/archinstall/locales/en/LC_MESSAGES/base.po b/archinstall/locales/en/LC_MESSAGES/base.po index f08c5ffc..5a38a4db 100644 --- a/archinstall/locales/en/LC_MESSAGES/base.po +++ b/archinstall/locales/en/LC_MESSAGES/base.po @@ -747,14 +747,13 @@ msgstr "" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" msgstr "" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" @@ -1067,3 +1066,9 @@ msgstr "" msgid "Mirror regions" msgstr "" + +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr "" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "" diff --git a/archinstall/locales/es/LC_MESSAGES/base.po b/archinstall/locales/es/LC_MESSAGES/base.po index 6ba5f7b6..22ab693c 100644 --- a/archinstall/locales/es/LC_MESSAGES/base.po +++ b/archinstall/locales/es/LC_MESSAGES/base.po @@ -794,14 +794,13 @@ msgstr "" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" msgstr "" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" @@ -1170,6 +1169,12 @@ msgstr "Región del servidor" msgid "Mirror regions" msgstr "Región del servidor" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr "" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "" + #~ msgid "Add :" #~ msgstr "Añadir :" diff --git a/archinstall/locales/fr/LC_MESSAGES/base.mo b/archinstall/locales/fr/LC_MESSAGES/base.mo index 22e004e0..81e6ce00 100644 Binary files a/archinstall/locales/fr/LC_MESSAGES/base.mo and b/archinstall/locales/fr/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/fr/LC_MESSAGES/base.po b/archinstall/locales/fr/LC_MESSAGES/base.po index 15aea6ac..ec5fc68e 100644 --- a/archinstall/locales/fr/LC_MESSAGES/base.po +++ b/archinstall/locales/fr/LC_MESSAGES/base.po @@ -793,18 +793,17 @@ msgstr "Interfaces {} configurées" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "Cette option active le nombre de téléchargements parallèles qui peuvent se produire pendant l'installation" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" "Saisir le nombre de téléchargements parallèles à activer.\n" -" (Entrer une valeur comprise entre 1 et {max_downloads})\n" +" (Entrer une valeur comprise entre 1 et {})\n" "Note :" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - Valeur maximale : {max_downloads} (Autorise {max_downloads} téléchargements parallèles, autorise {max_downloads+1} téléchargements à la fois)" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - Valeur maximale : {} (Autorise {} téléchargements parallèles, autorise {} téléchargements à la fois)" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" msgstr " - Valeur minimale : 1 (Autorise 1 téléchargement parallèle, autorise 2 téléchargements à la fois)" @@ -812,9 +811,9 @@ msgstr " - Valeur minimale : 1 (Autorise 1 téléchargement parallèle, autorise msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" msgstr " - Désactiver/Défaut : 0 (Désactive le téléchargement parallèle, n'autorise qu'un seul téléchargement à la fois)" -#, python-brace-format +#, fuzzy, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "Entrée invalide ! Réessayer avec une entrée valide [1 pour {max_downloads}, ou 0 pour désactiver]" +msgstr "Entrée invalide ! Réessayer avec une entrée valide [1 pour {}, ou 0 pour désactiver]" msgid "Parallel Downloads" msgstr "Téléchargements parallèles" @@ -1171,6 +1170,13 @@ msgstr "Région miroir" msgid "Mirror regions" msgstr "Région miroir" +#, fuzzy +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - Valeur maximale : {} (Autorise {} téléchargements parallèles, autorise {} téléchargements à la fois)" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "Entrée invalide ! Réessayer avec une entrée valide [1 pour {}, ou 0 pour désactiver]" + #, python-brace-format #~ msgid "Edit {origkey} :" #~ msgstr "Modifier {origkey} :" diff --git a/archinstall/locales/id/LC_MESSAGES/base.mo b/archinstall/locales/id/LC_MESSAGES/base.mo index b81fe108..db3649bd 100644 Binary files a/archinstall/locales/id/LC_MESSAGES/base.mo and b/archinstall/locales/id/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/id/LC_MESSAGES/base.po b/archinstall/locales/id/LC_MESSAGES/base.po index 01986796..f2ede55f 100644 --- a/archinstall/locales/id/LC_MESSAGES/base.po +++ b/archinstall/locales/id/LC_MESSAGES/base.po @@ -793,18 +793,17 @@ msgstr "Interface {} dikonfigurasi" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "Opsi ini memungkinkan jumlah unduhan paralel yang dapat terjadi selama instalasi" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" "Masukkan jumlah unduhan paralel yang akan diaktifkan.\n" -" (Masukkan nilai antara 1 hingga {max_downloads})\n" +" (Masukkan nilai antara 1 hingga {})\n" "Catatan:" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - Nilai maksimum : {max_downloads} ( Memungkinkan {max_downloads} unduhan paralel, memungkinkan {max_downloads+1} unduhan sekaligus)" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - Nilai maksimum : {} ( Memungkinkan {} unduhan paralel, memungkinkan {} unduhan sekaligus)" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" msgstr " - Nilai minimum : 1 (Mengizinkan 1 unduhan paralel, memungkinkan 2 unduhan sekaligus)" @@ -812,9 +811,9 @@ msgstr " - Nilai minimum : 1 (Mengizinkan 1 unduhan paralel, memungkinkan 2 un msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" msgstr " - Nonaktifkan/Default: 0 (Menonaktifkan pengunduhan paralel, hanya mengizinkan 1 unduhan pada satu waktu)" -#, python-brace-format +#, fuzzy, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "Input tidak valid! Coba lagi dengan input yang valid [1 untuk {max_downloads}, atau 0 untuk menonaktifkan]" +msgstr "Input tidak valid! Coba lagi dengan input yang valid [1 untuk {}, atau 0 untuk menonaktifkan]" msgid "Parallel Downloads" msgstr "Unduhan Paralel" @@ -1169,3 +1168,10 @@ msgstr "Wilayah mirror" #, fuzzy msgid "Mirror regions" msgstr "Wilayah mirror" + +#, fuzzy +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - Nilai maksimum : {} ( Memungkinkan {} unduhan paralel, memungkinkan {} unduhan sekaligus)" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "Input tidak valid! Coba lagi dengan input yang valid [1 untuk {}, atau 0 untuk menonaktifkan]" diff --git a/archinstall/locales/it/LC_MESSAGES/base.mo b/archinstall/locales/it/LC_MESSAGES/base.mo index f199746c..e63c377e 100644 Binary files a/archinstall/locales/it/LC_MESSAGES/base.mo and b/archinstall/locales/it/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/it/LC_MESSAGES/base.po b/archinstall/locales/it/LC_MESSAGES/base.po index c856a286..703f0000 100644 --- a/archinstall/locales/it/LC_MESSAGES/base.po +++ b/archinstall/locales/it/LC_MESSAGES/base.po @@ -793,18 +793,17 @@ msgstr "Configurate {} interfacce" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "Questa opzione consente di impostare il numero di download paralleli che possono avvenire durante l'installazione" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" "Inserisci il numero di download paralleli da abilitare.\n" -" (Inserisci un valore compreso tra 1 e {max_downloads})\n" +" (Inserisci un valore compreso tra 1 e {})\n" "Nota:" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - Valore massimo : {max_downloads} ( Consente {max_downloads} download parallelo, consente {max_downloads+1} download alla volta )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - Valore massimo : {} ( Consente {} download parallelo, consente {} download alla volta )" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" msgstr " - Valore minimo : 1 ( Consente 1 download parallelo, consente 2 download alla volta )" @@ -812,9 +811,9 @@ msgstr " - Valore minimo : 1 ( Consente 1 download parallelo, consente 2 downl msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" msgstr " - Disabilita/Predefinito : 0 ( Disabilita il download parallelo, consente solo 1 download alla volta )" -#, python-brace-format +#, fuzzy, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "Input non valido! Riprova con un input valido [da 1 a {max_downloads}, o 0 per disabilitare]." +msgstr "Input non valido! Riprova con un input valido [da 1 a {}, o 0 per disabilitare]." msgid "Parallel Downloads" msgstr "Download paralleli" @@ -1169,3 +1168,10 @@ msgstr "Regione dei mirror" #, fuzzy msgid "Mirror regions" msgstr "Regione dei mirror" + +#, fuzzy +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - Valore massimo : {} ( Consente {} download parallelo, consente {} download alla volta )" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "Input non valido! Riprova con un input valido [da 1 a {}, o 0 per disabilitare]." diff --git a/archinstall/locales/ka/LC_MESSAGES/base.mo b/archinstall/locales/ka/LC_MESSAGES/base.mo index b95a6e0e..31e14aa2 100644 Binary files a/archinstall/locales/ka/LC_MESSAGES/base.mo and b/archinstall/locales/ka/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ka/LC_MESSAGES/base.po b/archinstall/locales/ka/LC_MESSAGES/base.po index 029e4256..6e4823eb 100644 --- a/archinstall/locales/ka/LC_MESSAGES/base.po +++ b/archinstall/locales/ka/LC_MESSAGES/base.po @@ -794,18 +794,17 @@ msgstr "მორგებულია {} ინტერფეისი" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "ეს პარამეტრი დაყენებისას მითითებული რაოდენობის პარალელურ გადმოწერას დაუშვებს" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" "შეიყვანეთ დასაშვები პარალელური გადმოწერების რაოდენობა.\n" -" (შეიყვანეთ მნიშვნელობა 1-დან {max_downloads}-მდე)\n" +" (შეიყვანეთ მნიშვნელობა 1-დან {}-მდე)\n" "დაიმახსოვრეთ:" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - მინიმალური მნიშვნელობა : {max_downloads} ( დაუშვებს {max_downloads} პარალელურ გადმოწერას, დაუშვებს {max_downloads+1} ერთდროულ გადმოწერას )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - მინიმალური მნიშვნელობა : {} ( დაუშვებს {} პარალელურ გადმოწერას, დაუშვებს {} ერთდროულ გადმოწერას )" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" msgstr " - მინიმალური მნიშვნელობა : 1 ( დაუშვებს 1 პარალელურ გადმოწერას, დაუშვებს 2 ერთდროულ გადმოწერას )" @@ -813,9 +812,9 @@ msgstr " - მინიმალური მნიშვნელობა : msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" msgstr " - გამორთვა/ნაგულისხმები : 0 ( პარალელური გადმოწერების გათიშვა. დროის ერთ მომენტში მხოლოდ ერთი გადმოწერა მოხდება )" -#, python-brace-format +#, fuzzy, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "შეყვანილი რიცხვი არასწორია! თავიდან სცადეთ [1-დან {max_downloads}-მდე, ან 0, გასათიშად]" +msgstr "შეყვანილი რიცხვი არასწორია! თავიდან სცადეთ [1-დან {}-მდე, ან 0, გასათიშად]" msgid "Parallel Downloads" msgstr "პარალელური გადმოწერები" @@ -1168,3 +1167,10 @@ msgstr "სარკის რეგიონი" #, fuzzy msgid "Mirror regions" msgstr "სარკის რეგიონი" + +#, fuzzy +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - მინიმალური მნიშვნელობა : {} ( დაუშვებს {} პარალელურ გადმოწერას, დაუშვებს {} ერთდროულ გადმოწერას )" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "შეყვანილი რიცხვი არასწორია! თავიდან სცადეთ [1-დან {}-მდე, ან 0, გასათიშად]" diff --git a/archinstall/locales/ko/LC_MESSAGES/base.mo b/archinstall/locales/ko/LC_MESSAGES/base.mo index 4c89f1f8..ddec6151 100644 Binary files a/archinstall/locales/ko/LC_MESSAGES/base.mo and b/archinstall/locales/ko/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ko/LC_MESSAGES/base.po b/archinstall/locales/ko/LC_MESSAGES/base.po index 7d30a785..de46b698 100644 --- a/archinstall/locales/ko/LC_MESSAGES/base.po +++ b/archinstall/locales/ko/LC_MESSAGES/base.po @@ -794,18 +794,17 @@ msgstr "구성된 {} 인터페이스" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "이 옵션은 설치 중에 발생할 수 있는 병렬 다운로드 수를 활성화합니다" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" "활성화할 병렬 다운로드 수를 입력하세요.\n" -" (1 부터 {max_downloads} 까지의 숫자중 하나를 입력하세요)\n" +" (1 부터 {} 까지의 숫자중 하나를 입력하세요)\n" "메모:" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - 최댓값 : {max_downloads} ( {max_downloads} 개의 병렬 다운로드 허용, 한 번에 {max_downloads+1} 개의 다운로드 허용 )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - 최댓값 : {} ( {} 개의 병렬 다운로드 허용, 한 번에 {} 개의 다운로드 허용 )" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" msgstr " - 최솟값 : 1 ( 1 개의 병렬 다운로드 허용, 한 번에 2 개의 다운로드 허용 )" @@ -813,9 +812,9 @@ msgstr " - 최솟값 : 1 ( 1 개의 병렬 다운로드 허용, 한 번에 2 msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" msgstr " - 비활성화/기본 : - ( 병렬 다운로드 비활성화, 한 번에 1 개의 다운로드만 허용 )" -#, python-brace-format +#, fuzzy, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "잘못된 값입니다! 유효한 값으로 다시 시도해주세요 [1 부터 {max_downloads} 까지, 비활성화 하려면 0]" +msgstr "잘못된 값입니다! 유효한 값으로 다시 시도해주세요 [1 부터 {} 까지, 비활성화 하려면 0]" msgid "Parallel Downloads" msgstr "병렬 다운로드" @@ -1170,3 +1169,10 @@ msgstr "미러 위치" #, fuzzy msgid "Mirror regions" msgstr "미러 위치" + +#, fuzzy +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - 최댓값 : {} ( {} 개의 병렬 다운로드 허용, 한 번에 {} 개의 다운로드 허용 )" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "잘못된 값입니다! 유효한 값으로 다시 시도해주세요 [1 부터 {} 까지, 비활성화 하려면 0]" diff --git a/archinstall/locales/nl/LC_MESSAGES/base.po b/archinstall/locales/nl/LC_MESSAGES/base.po index e22a6d0f..6dfeed53 100644 --- a/archinstall/locales/nl/LC_MESSAGES/base.po +++ b/archinstall/locales/nl/LC_MESSAGES/base.po @@ -823,14 +823,13 @@ msgstr "" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" msgstr "" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" @@ -1195,6 +1194,12 @@ msgstr "Spiegelserverregio" msgid "Mirror regions" msgstr "Spiegelserverregio" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr "" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "" + #~ msgid "Add :" #~ msgstr "Toevoegen:" diff --git a/archinstall/locales/pl/LC_MESSAGES/base.mo b/archinstall/locales/pl/LC_MESSAGES/base.mo index 7ca4f9b0..0904a1a1 100644 Binary files a/archinstall/locales/pl/LC_MESSAGES/base.mo and b/archinstall/locales/pl/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pl/LC_MESSAGES/base.po b/archinstall/locales/pl/LC_MESSAGES/base.po index 8c3ce4f5..fa53c7f0 100644 --- a/archinstall/locales/pl/LC_MESSAGES/base.po +++ b/archinstall/locales/pl/LC_MESSAGES/base.po @@ -794,18 +794,19 @@ msgstr "Skonfigurowano {} interfejsów" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "Ta opcja pozwala określić maksymalną liczbę pobieranych plików podczas instalacji" -#, fuzzy, python-brace-format +#, fuzzy msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" "Wprowadź maksymalną liczbę plików pobieranych jednocześnie.\n" -" (Liczba z zakresu od 1 do {max_downloads})\n" +" (Liczba z zakresu od 1 do {})\n" "Zauważ:" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - Maksymalna wartość : {max_downloads} ( Zwiększa liczbę zadań o {max_downloads}, co pozwala na pobieranie {max_downloads+1} plików jednocześnie )" +#, fuzzy +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - Maksymalna wartość : {} ( Zwiększa liczbę zadań o {}, co pozwala na pobieranie {} plików jednocześnie )" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" msgstr " - Minimalna wartość : 1 ( Zwiększa liczbę zadań o 1, co pozwala na pobieranie 2 plików jednocześnie )" @@ -815,7 +816,7 @@ msgstr " - Wyłącz/domyślnie : 0 ( Wyłącza pobieranie równoległe, więc ty #, fuzzy, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "Nieprawidłowa wartość! Spróbuj wprowadzić wartość od 1 do {max_downloads}, lub 0 aby wyłączyć." +msgstr "Nieprawidłowa wartość! Spróbuj wprowadzić wartość od 1 do {}, lub 0 aby wyłączyć." msgid "Parallel Downloads" msgstr "Pobieranie równoległe" @@ -1181,6 +1182,14 @@ msgstr "Region lustra" msgid "Mirror regions" msgstr "Region lustra" +#, fuzzy +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - Maksymalna wartość : {} ( Zwiększa liczbę zadań o {}, co pozwala na pobieranie {} plików jednocześnie )" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "Nieprawidłowa wartość! Spróbuj wprowadzić wartość od 1 do {}, lub 0 aby wyłączyć." + #~ msgid "Add :" #~ msgstr "Dodaj :" diff --git a/archinstall/locales/pt/LC_MESSAGES/base.po b/archinstall/locales/pt/LC_MESSAGES/base.po index 35c3ca73..7f0d5eba 100644 --- a/archinstall/locales/pt/LC_MESSAGES/base.po +++ b/archinstall/locales/pt/LC_MESSAGES/base.po @@ -843,14 +843,13 @@ msgstr "" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" msgstr "" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" @@ -1217,6 +1216,12 @@ msgstr "Região do mirror" msgid "Mirror regions" msgstr "Região do mirror" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr "" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "" + #~ msgid "Add :" #~ msgstr "Adicionar :" diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo index 4dd57dba..51580dbb 100644 Binary files a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo and b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.po b/archinstall/locales/pt_BR/LC_MESSAGES/base.po index 93b33002..c36250f5 100644 --- a/archinstall/locales/pt_BR/LC_MESSAGES/base.po +++ b/archinstall/locales/pt_BR/LC_MESSAGES/base.po @@ -797,18 +797,17 @@ msgstr "{} interfaces configuradas" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "Esta opção habilita o número de downloads paralelos que podem ocorrer durante a instalação" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" "Insira o número de downloads paralelos para serem habilitados.\n" -" (Insira um valor entre 1 e {max_downloads})\n" +" (Insira um valor entre 1 e {})\n" "Observação:" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - Valor máximo : {max_downloads} ( Permite {max_donwloads} downloads paralelos, permite {max_donwloads+1} downloads por vez )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - Valor máximo : {} ( Permite {} downloads paralelos, permite {} downloads por vez )" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" msgstr " - Valor minimo : 1 ( Permite 1 download paralelo, permite 2 downloads por vez )" @@ -816,9 +815,9 @@ msgstr " - Valor minimo : 1 ( Permite 1 download paralelo, permite 2 downloads msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" msgstr " - Desativar/Padrão : 0 ( Desativa os downloads paralelos, permite apenas 1 download por vez )" -#, python-brace-format +#, fuzzy, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {max_downloads}, ou 0 para desativar]" +msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {}, ou 0 para desativar]" msgid "Parallel Downloads" msgstr "Downloads Paralelos" @@ -1171,3 +1170,10 @@ msgstr "Região do mirror" #, fuzzy msgid "Mirror regions" msgstr "Região do mirror" + +#, fuzzy +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - Valor máximo : {} ( Permite {} downloads paralelos, permite {} downloads por vez )" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {}, ou 0 para desativar]" diff --git a/archinstall/locales/ru/LC_MESSAGES/base.mo b/archinstall/locales/ru/LC_MESSAGES/base.mo index 1da3a370..4480d852 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 a0263aa3..90d78f2e 100644 --- a/archinstall/locales/ru/LC_MESSAGES/base.po +++ b/archinstall/locales/ru/LC_MESSAGES/base.po @@ -794,18 +794,17 @@ msgstr "Настроено интерфейсов: {}" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "Этот параметр определяет количество параллельных загрузок, которые могут происходить во время установки" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" "Введите количество параллельных загрузок, которые будут включены.\n" -" (Введите значение от 1 до {max_downloads})\n" +" (Введите значение от 1 до {})\n" "Примечание:" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - Максимальное значение: {max_downloads} ( Позволяет {max_downloads} параллельных загрузок, позволяет {max_downloads+1} загрузок одновременно )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - Максимальное значение: {} ( Позволяет {} параллельных загрузок, позволяет {} загрузок одновременно )" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" msgstr " - Минимальное значение: 1 ( Позволяет 1 параллельную загрузку, позволяет 2 загрузки одновременно )" @@ -813,9 +812,9 @@ msgstr " - Минимальное значение: 1 ( Позволяет 1 п msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" msgstr " - Отключить/по умолчанию: 0 ( Отключает параллельную загрузку, позволяет только 1 загрузку за один раз )" -#, python-brace-format +#, fuzzy, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "Неверный ввод! Повторите попытку с правильным вводом [1 - {max_downloads}, или 0 - отключить]" +msgstr "Неверный ввод! Повторите попытку с правильным вводом [1 - {}, или 0 - отключить]" msgid "Parallel Downloads" msgstr "Параллельные загрузки" @@ -1171,6 +1170,13 @@ msgstr "Регион зеркала" msgid "Mirror regions" msgstr "Регион зеркала" +#, fuzzy +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - Максимальное значение: {} ( Позволяет {} параллельных загрузок, позволяет {} загрузок одновременно )" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "Неверный ввод! Повторите попытку с правильным вводом [1 - {}, или 0 - отключить]" + #, python-brace-format #~ msgid "Edit {origkey} :" #~ msgstr "Редактировать {origkey}:" diff --git a/archinstall/locales/sv/LC_MESSAGES/base.po b/archinstall/locales/sv/LC_MESSAGES/base.po index ea81ad80..ebb7a275 100644 --- a/archinstall/locales/sv/LC_MESSAGES/base.po +++ b/archinstall/locales/sv/LC_MESSAGES/base.po @@ -804,14 +804,13 @@ msgstr "" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" msgstr "" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" @@ -1178,3 +1177,9 @@ msgstr "Region för paketsynk" #, fuzzy msgid "Mirror regions" msgstr "Region för paketsynk" + +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr "" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "" diff --git a/archinstall/locales/ta/LC_MESSAGES/base.mo b/archinstall/locales/ta/LC_MESSAGES/base.mo index 3f175509..fc2b70e6 100644 Binary files a/archinstall/locales/ta/LC_MESSAGES/base.mo and b/archinstall/locales/ta/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ta/LC_MESSAGES/base.po b/archinstall/locales/ta/LC_MESSAGES/base.po index 44aebb19..14ed4d98 100644 --- a/archinstall/locales/ta/LC_MESSAGES/base.po +++ b/archinstall/locales/ta/LC_MESSAGES/base.po @@ -793,18 +793,17 @@ msgstr "கட்டமைக்கப்பட்ட {} இடைமுகங msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "இந்த விருப்பம் நிறுவலின் போது நிகழக்கூடிய இணையான பதிவிறக்கங்களின் எண்ணிக்கையை செயல்படுத்துகிறது" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" "இயக்கப்பட வேண்டிய இணையான பதிவிறக்கங்களின் எண்ணிக்கையை உள்ளிடவும்.\n" -" (1 முதல் {max_downloads} வரையிலான மதிப்பை உள்ளிடவும்)\n" +" (1 முதல் {} வரையிலான மதிப்பை உள்ளிடவும்)\n" "குறிப்பு:" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - அதிகபட்ச மதிப்பு : {max_downloads} ( {max_downloads} இணையான பதிவிறக்கங்களை அனுமதிக்கிறது, ஒரே நேரத்தில் {max_downloads+1} பதிவிறக்கங்களை அனுமதிக்கிறது )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - அதிகபட்ச மதிப்பு : {} ( {} இணையான பதிவிறக்கங்களை அனுமதிக்கிறது, ஒரே நேரத்தில் {} பதிவிறக்கங்களை அனுமதிக்கிறது )" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" msgstr " - குறைந்தபட்ச மதிப்பு : 1 (1 இணை பதிவிறக்கத்தை அனுமதிக்கிறது, ஒரு நேரத்தில் 2 பதிவிறக்கங்களை அனுமதிக்கிறது )" @@ -812,9 +811,9 @@ msgstr " - குறைந்தபட்ச மதிப்பு : 1 (1 இ msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" msgstr " - முடக்கு/இயல்புநிலை: 0 (இணை பதிவிறக்கத்தை முடக்குகிறது, ஒரு நேரத்தில் 1 பதிவிறக்கத்தை மட்டுமே அனுமதிக்கிறது )" -#, python-brace-format +#, fuzzy, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "தவறான உள்ளீடு! சரியான உள்ளீட்டில் [1 முதல் {max_downloads} வரை அல்லது முடக்க 0 வரை] மீண்டும் முயற்சிக்கவும்" +msgstr "தவறான உள்ளீடு! சரியான உள்ளீட்டில் [1 முதல் {} வரை அல்லது முடக்க 0 வரை] மீண்டும் முயற்சிக்கவும்" msgid "Parallel Downloads" msgstr "இணையான பதிவிறக்கங்கள்" @@ -1169,3 +1168,10 @@ msgstr "மிரர் பிராந்தியம்" #, fuzzy msgid "Mirror regions" msgstr "மிரர் பிராந்தியம்" + +#, fuzzy +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - அதிகபட்ச மதிப்பு : {} ( {} இணையான பதிவிறக்கங்களை அனுமதிக்கிறது, ஒரே நேரத்தில் {} பதிவிறக்கங்களை அனுமதிக்கிறது )" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "தவறான உள்ளீடு! சரியான உள்ளீட்டில் [1 முதல் {} வரை அல்லது முடக்க 0 வரை] மீண்டும் முயற்சிக்கவும்" diff --git a/archinstall/locales/tr/LC_MESSAGES/base.po b/archinstall/locales/tr/LC_MESSAGES/base.po index 40efea03..20fa4f23 100644 --- a/archinstall/locales/tr/LC_MESSAGES/base.po +++ b/archinstall/locales/tr/LC_MESSAGES/base.po @@ -804,14 +804,13 @@ msgstr "" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" msgstr "" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" @@ -1177,3 +1176,9 @@ msgstr "İndirme sunucusu bölgesi" #, fuzzy msgid "Mirror regions" msgstr "İndirme sunucusu bölgesi" + +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr "" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "" diff --git a/archinstall/locales/uk/LC_MESSAGES/base.mo b/archinstall/locales/uk/LC_MESSAGES/base.mo index ae98dcfb..e7cb8b0b 100644 Binary files a/archinstall/locales/uk/LC_MESSAGES/base.mo and b/archinstall/locales/uk/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/uk/LC_MESSAGES/base.po b/archinstall/locales/uk/LC_MESSAGES/base.po index f2c7737c..a700c126 100644 --- a/archinstall/locales/uk/LC_MESSAGES/base.po +++ b/archinstall/locales/uk/LC_MESSAGES/base.po @@ -793,18 +793,17 @@ msgstr "Налаштовані інтерфейси {}" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "Цей параметр вмикає кількість паралельних завантажень, які можуть відбуватися під час встановлення" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" "Введіть кількість паралельних завантажень, які потрібно ввімкнути.\n" -" (Введіть значення від 1 до {max_downloads})\n" +" (Введіть значення від 1 до {})\n" "Примітка:" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - Максимальне значення : {max_downloads} ( Дозволяє {max_downloads} паралельних завантажень, дозволяє {max_downloads+1} завантажень за раз )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - Максимальне значення : {} ( Дозволяє {} паралельних завантажень, дозволяє {} завантажень за раз )" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" msgstr " - Мінімальне значення : 1 ( дозволяє 1 паралельне завантаження, дозволяє 2 завантаження одночасно )" @@ -812,9 +811,9 @@ msgstr " - Мінімальне значення : 1 ( дозволяє 1 па msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" msgstr " - Вимкнути/Типово : 0 ( Вимикає паралельне завантаження, дозволяє лише 1 завантаження за раз )" -#, python-brace-format +#, fuzzy, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "Некоректне введення! Повторіть спробу з валідним введенням [від 1 до {max_downloads} або 0 для вимкнення]" +msgstr "Некоректне введення! Повторіть спробу з валідним введенням [від 1 до {} або 0 для вимкнення]" msgid "Parallel Downloads" msgstr "Паралельні Завантаження" @@ -1167,3 +1166,10 @@ msgstr "Регіон дзеркала" #, fuzzy msgid "Mirror regions" msgstr "Регіон дзеркала" + +#, fuzzy +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - Максимальне значення : {} ( Дозволяє {} паралельних завантажень, дозволяє {} завантажень за раз )" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "Некоректне введення! Повторіть спробу з валідним введенням [від 1 до {} або 0 для вимкнення]" diff --git a/archinstall/locales/ur/LC_MESSAGES/base.po b/archinstall/locales/ur/LC_MESSAGES/base.po index 6660f3b4..ac648108 100644 --- a/archinstall/locales/ur/LC_MESSAGES/base.po +++ b/archinstall/locales/ur/LC_MESSAGES/base.po @@ -825,14 +825,13 @@ msgstr "" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" msgstr "" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" @@ -1198,6 +1197,12 @@ msgstr "متبادل علاقہ" msgid "Mirror regions" msgstr "متبادل علاقہ" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr "" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "" + #~ msgid "Add :" #~ msgstr "شامل:" diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.mo b/archinstall/locales/zh-CN/LC_MESSAGES/base.mo index 693308ab..4502c562 100644 Binary files a/archinstall/locales/zh-CN/LC_MESSAGES/base.mo and b/archinstall/locales/zh-CN/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.po b/archinstall/locales/zh-CN/LC_MESSAGES/base.po index afa1716b..04a703ed 100644 --- a/archinstall/locales/zh-CN/LC_MESSAGES/base.po +++ b/archinstall/locales/zh-CN/LC_MESSAGES/base.po @@ -792,18 +792,17 @@ msgstr "已配置的 {} 接口" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "此选项启用安装期间可能发生的并行下载次数" -#, python-brace-format msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" "输入要启用的并行下载数。\n" -" (输入一个介于 1 到 {max_downloads} 之间的值)\n" +" (输入一个介于 1 到 {} 之间的值)\n" "提示:" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - 最大值:{max_downloads}(允许 {max_downloads} 次并行下载,一次允许 {max_downloads+1} 次下载)" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - 最大值:{}(允许 {} 次并行下载,一次允许 {} 次下载)" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" msgstr " - 最小值:1(允许 1 次并行下载,一次允许 2 次下载)" @@ -811,9 +810,9 @@ msgstr " - 最小值:1(允许 1 次并行下载,一次允许 2 次下载 msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" msgstr " - 禁用/默认:0(禁用并行下载,一次只允许 1 个下载)" -#, python-brace-format +#, fuzzy, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "输入无效! 使用有效输入重试 [1 到 {max_downloads},或 0 到禁用]" +msgstr "输入无效! 使用有效输入重试 [1 到 {},或 0 到禁用]" msgid "Parallel Downloads" msgstr "并行下载" @@ -1168,3 +1167,10 @@ msgstr "镜像区域" #, fuzzy msgid "Mirror regions" msgstr "镜像区域" + +#, fuzzy +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - 最大值:{}(允许 {} 次并行下载,一次允许 {} 次下载)" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "输入无效! 使用有效输入重试 [1 到 {},或 0 到禁用]" -- cgit v1.2.3-70-g09d2 From 3bc624298aec09c2cba126984db6985bb0c600cc Mon Sep 17 00:00:00 2001 From: Kefaku <114934849+Kefaku@users.noreply.github.com> Date: Mon, 5 Jun 2023 10:54:20 +0200 Subject: Add some translations for locales/de (#1844) Co-authored-by: Anton Hvornum --- archinstall/locales/de/LC_MESSAGES/base.po | 40 +++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/archinstall/locales/de/LC_MESSAGES/base.po b/archinstall/locales/de/LC_MESSAGES/base.po index 6dfb0801..ed1657ee 100644 --- a/archinstall/locales/de/LC_MESSAGES/base.po +++ b/archinstall/locales/de/LC_MESSAGES/base.po @@ -746,10 +746,10 @@ msgid "Size" msgstr "Größe" msgid "Free space" -msgstr "Freier Speicherplatz" +msgstr "Freier Speicherplatz:" msgid "Bus-type" -msgstr "Bustyp" +msgstr "Bus-Typ" msgid "Either root-password or at least 1 user with sudo privileges must be specified" msgstr "Entweder root Passwort oder mindestens ein Superuser muss konfiguriert sein" @@ -758,7 +758,7 @@ msgid "Enter username (leave blank to skip): " msgstr "Benutzernamen eingeben (leer lassen um zu Überspringen): " msgid "The username you entered is invalid. Try again" -msgstr "Der Benutzername, den Sie eingegeben haben, ist nicht gültig. Bitte erneut versuchen" +msgstr "Der eingegebene Benutzername ist ungültig. Erneut versuchen" msgid "Should \"{}\" be a superuser (sudo)?" msgstr "Soll {} ein Superuser sein (sudo)?" @@ -831,22 +831,22 @@ msgid "[Default value: 0] > " msgstr "[Standardwert: 0] > " msgid "To be able to use this translation, please install a font manually that supports the language." -msgstr "Um diese Übersetzung zu verwenden, installieren Sie bitte manuell eine Schrift, die diese Unterstüzt" +msgstr "Um diese Übersetzung nutzen zu können, installieren Sie bitte manuell eine Schriftart, die diese Sprache unterstützt." msgid "The font should be stored as {}" -msgstr "Die Schrift sollte als {} gespeichert werden" +msgstr "Die Schriftart sollte als {} gespeichert werden" msgid "Archinstall requires root privileges to run. See --help for more." -msgstr "Archinstall benötigt root Rechte um zu funktionieren. Verwenden Sie --help für mehr Informationen." +msgstr "Archinstall benötigt Root-Rechte zum ausführen. Siehe --help für mehr." msgid "Select an execution mode" msgstr "Wählen Sie einen Ausführmodus" msgid "Unable to fetch profile from specified url: {}" -msgstr "Profil von der URL \"{}\" abrufen nicht möglich" +msgstr "Konnte Profil nicht von der angegebenen URL fetchen: {}" msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" -msgstr "Profile benötigen einzigartige Namen, aber Profildefinitionen mit gleichen Namen gefunden: {}" +msgstr "Profile müssen einen eindeutige Namen haben, aber Profildefinition mit gleichem Namen gefunden: {}" msgid "Select one or more devices to use and configure" msgstr "Bitte wählen Sie ein oder mehrere Geräte aus, die konfiguriert und verwendet werden sollen" @@ -903,7 +903,7 @@ msgid "Partition" msgstr "Partition" msgid "This partition is currently encrypted, to format it a filesystem has to be specified" -msgstr "Diese Partition ist momentan verschlüsselt, um sie zu formatieren, muss ein Dateisystem angegeben werden" +msgstr "Diese Partition ist aktuell verschlüsselt, zum formatieren muss ein Dateisystem angegeben werden" msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." msgstr "Die Einhängeorte sind relativ zur Installation, zum Beispiel boot würde bei /boot eingehängt werden" @@ -915,7 +915,7 @@ msgid "Mountpoint: " msgstr "Einhängepunkt: " msgid "Current free sectors on device {}:" -msgstr "Momentan freie Sektoren auf Gerät {}:" +msgstr "Aktuell freie Sektoren auf dem Gerät {}:" msgid "Total sectors: {}" msgstr "Sektoren insgesamt: {}" @@ -927,13 +927,13 @@ msgid "Enter the end sector of the partition (percentage or block number, defaul msgstr "Bitte geben Sie den Endsektor der Partition ein (Prozent oder Blocknummer, Standard: {}): " msgid "This will remove all newly added partitions, continue?" -msgstr "Dies wird alle neu hinzugefügten Partitionen entfernen. Fortfahren?" +msgstr "Dies wird alle neu hinzugefügten Partitionen entfernen, fortfahren?" msgid "Partition management: {}" msgstr "Partitionsverwaltung: {}" msgid "Total length: {}" -msgstr "Länge insgesamt: {}" +msgstr "Gesamtlänge: {}" msgid "Encryption type" msgstr "Verschlüsselungstyp" @@ -942,7 +942,7 @@ msgid "Partitions" msgstr "Partitionen" msgid "No HSM devices available" -msgstr "Keine HSM-Geräte verfügbar" +msgstr "Kein HSM-Gerät verfügbar" msgid "Partitions to be encrypted" msgstr "Partitionen die verschlüsselt werden" @@ -975,7 +975,7 @@ msgid "← Back" msgstr "← Zurück" msgid "Disk encryption" -msgstr "Laufwerksverschlüsselung" +msgstr "Festplattenverschlüsselung" msgid "Configuration" msgstr "Konfiguration" @@ -996,7 +996,7 @@ msgid "Environment type: {}" msgstr "Umgebungstyp: {}" msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" -msgstr "Der proprietäre Nvidiatreiber wird von Sway nicht unterstütz. Es ist wahrscheinlich, dass Probleme auftreten werden. Ist das Okay?" +msgstr "Der proprietäre Nvidia Treiber wird von Sway nicht unterstptzt. Es ist wahrscheinlich, dass Fehler auftreten werden, trotzdem fortfahren?" msgid "Installed packages" msgstr "Installiere Pakete" @@ -1014,7 +1014,7 @@ msgid "Profile name: " msgstr "Profilname: " msgid "The profile name you entered is already in use. Try again" -msgstr "Der eingegebende Profilname ist bereits in Verwendung. Erneut versuchen" +msgstr "Der eingegebene Profilname wird bereits verwendet. Erneut versuchen" msgid "Packages to be install with this profile (space separated, leave blank to skip): " msgstr "Zusätzliche Pakete die mit diesem Profil installiert werden sollen (Mit Leerzeichen trennen, leer lassen zum überspringen): " @@ -1023,10 +1023,10 @@ msgid "Services to be enabled with this profile (space separated, leave blank to msgstr "Dienste die mit diesem Profil installiert werden sollen (Mit Leerzeichen trennen, leer lassen zum überspringen): " msgid "Should this profile be enabled for installation?" -msgstr "Soll diese Profil für die Installation verwendet werden?" +msgstr "Soll dieses Profil für die Installation aktiviert werden?" msgid "Create your own" -msgstr "Eigenes erstellen" +msgstr "Erstelle ein eigenes" msgid "" "\n" @@ -1058,7 +1058,7 @@ msgid "Please chose which greeter to install" msgstr "Bitte den zu installierenden Greeter (Begrüßer/Anmeldebildschirm) auswählen" msgid "This is a list of pre-programmed default_profiles" -msgstr "Dies ist eine Liste an vorgefertigten Standardprofilen" +msgstr "Dies ist eine liste von vorprogrammierten Standardprofilen:" msgid "Disk configuration" msgstr "Laufwerkskonfiguration" @@ -1067,7 +1067,7 @@ msgid "Profiles" msgstr "Profile" msgid "Finding possible directories to save configuration files ..." -msgstr "Suche mögliche Ordner zum Konfigurationen speichern..." +msgstr "Finde mögliche Pfade um Konfigurationsdateien zu speichern..." msgid "Select directory (or directories) for saving configuration files" msgstr "Ordner um Konfigurationsdateien zu erstellen auswählen" -- cgit v1.2.3-70-g09d2 From b02e00d22e260af60832d4b69c92a41191914082 Mon Sep 17 00:00:00 2001 From: George Angelopoulos Date: Mon, 5 Jun 2023 13:27:58 +0300 Subject: Rephrase NetworkManager dependency message for clarity. (#1834) The phrase "X necessary to configure Y in Z" is at best ambiguous and could mean that Y and Z are required by X, while the intended message is the reverse. The phrase "necessary for configuring" makes the relationship clearer. --- archinstall/locales/ar/LC_MESSAGES/base.mo | Bin 3799 -> 3803 bytes archinstall/locales/ar/LC_MESSAGES/base.po | 2 +- archinstall/locales/base.pot | 2 +- archinstall/locales/cs/LC_MESSAGES/base.mo | Bin 25981 -> 25985 bytes archinstall/locales/cs/LC_MESSAGES/base.po | 2 +- archinstall/locales/de/LC_MESSAGES/base.mo | Bin 37641 -> 37645 bytes archinstall/locales/de/LC_MESSAGES/base.po | 2 +- archinstall/locales/el/LC_MESSAGES/base.mo | Bin 35428 -> 35432 bytes archinstall/locales/el/LC_MESSAGES/base.po | 2 +- archinstall/locales/en/LC_MESSAGES/base.po | 2 +- archinstall/locales/es/LC_MESSAGES/base.mo | Bin 24393 -> 24397 bytes archinstall/locales/es/LC_MESSAGES/base.po | 2 +- archinstall/locales/et/LC_MESSAGES/base.mo | Bin 35633 -> 35637 bytes archinstall/locales/et/LC_MESSAGES/base.po | 2 +- archinstall/locales/fr/LC_MESSAGES/base.mo | Bin 27428 -> 27432 bytes archinstall/locales/fr/LC_MESSAGES/base.po | 2 +- archinstall/locales/id/LC_MESSAGES/base.mo | Bin 26258 -> 26262 bytes archinstall/locales/id/LC_MESSAGES/base.po | 2 +- archinstall/locales/it/LC_MESSAGES/base.mo | Bin 26541 -> 26545 bytes archinstall/locales/it/LC_MESSAGES/base.po | 2 +- archinstall/locales/ka/LC_MESSAGES/base.mo | Bin 44823 -> 44827 bytes archinstall/locales/ka/LC_MESSAGES/base.po | 2 +- archinstall/locales/ko/LC_MESSAGES/base.mo | Bin 27221 -> 27225 bytes archinstall/locales/ko/LC_MESSAGES/base.po | 2 +- archinstall/locales/nl/LC_MESSAGES/base.mo | Bin 17688 -> 17692 bytes archinstall/locales/nl/LC_MESSAGES/base.po | 2 +- archinstall/locales/pl/LC_MESSAGES/base.mo | Bin 25126 -> 25130 bytes archinstall/locales/pl/LC_MESSAGES/base.po | 2 +- archinstall/locales/pt/LC_MESSAGES/base.mo | Bin 16333 -> 16337 bytes archinstall/locales/pt/LC_MESSAGES/base.po | 2 +- archinstall/locales/pt_BR/LC_MESSAGES/base.mo | Bin 27023 -> 27027 bytes archinstall/locales/pt_BR/LC_MESSAGES/base.po | 2 +- archinstall/locales/ru/LC_MESSAGES/base.mo | Bin 35889 -> 35893 bytes archinstall/locales/ru/LC_MESSAGES/base.po | 2 +- archinstall/locales/sv/LC_MESSAGES/base.mo | Bin 22729 -> 22733 bytes archinstall/locales/sv/LC_MESSAGES/base.po | 2 +- archinstall/locales/ta/LC_MESSAGES/base.mo | Bin 47476 -> 47480 bytes archinstall/locales/ta/LC_MESSAGES/base.po | 2 +- archinstall/locales/tr/LC_MESSAGES/base.mo | Bin 24422 -> 24426 bytes archinstall/locales/tr/LC_MESSAGES/base.po | 2 +- archinstall/locales/uk/LC_MESSAGES/base.mo | Bin 36024 -> 36028 bytes archinstall/locales/uk/LC_MESSAGES/base.po | 2 +- archinstall/locales/ur/LC_MESSAGES/base.mo | Bin 20487 -> 20491 bytes archinstall/locales/ur/LC_MESSAGES/base.po | 2 +- archinstall/locales/zh-CN/LC_MESSAGES/base.mo | Bin 23992 -> 23996 bytes archinstall/locales/zh-CN/LC_MESSAGES/base.po | 2 +- archinstall/locales/zh-TW/LC_MESSAGES/base.mo | Bin 32090 -> 32094 bytes archinstall/locales/zh-TW/LC_MESSAGES/base.po | 2 +- 48 files changed, 25 insertions(+), 25 deletions(-) diff --git a/archinstall/locales/ar/LC_MESSAGES/base.mo b/archinstall/locales/ar/LC_MESSAGES/base.mo index 6218cc97..4426a2dd 100644 Binary files a/archinstall/locales/ar/LC_MESSAGES/base.mo and b/archinstall/locales/ar/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ar/LC_MESSAGES/base.po b/archinstall/locales/ar/LC_MESSAGES/base.po index 0944913a..ab1e512f 100644 --- a/archinstall/locales/ar/LC_MESSAGES/base.po +++ b/archinstall/locales/ar/LC_MESSAGES/base.po @@ -65,7 +65,7 @@ msgstr "اكتب حزمًا إضافية لتثبيتها (تُفصَل بالم msgid "Copy ISO network configuration to installation" msgstr "انسخ إعداد شبكة الـISO للتثبيت" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "استخدم مُدير الشبكة (ضروري لإعداد الإنترنت باستخدام واجهة رسومية في جنوم و كيدي)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/base.pot b/archinstall/locales/base.pot index cc01f5fc..9c6bb2cb 100644 --- a/archinstall/locales/base.pot +++ b/archinstall/locales/base.pot @@ -63,7 +63,7 @@ msgid "Copy ISO network configuration to installation" msgstr "" msgid "" -"Use NetworkManager (necessary to configure internet graphically in GNOME and " +"Use NetworkManager ( necessary for configuring internet graphically in GNOME and " "KDE)" msgstr "" diff --git a/archinstall/locales/cs/LC_MESSAGES/base.mo b/archinstall/locales/cs/LC_MESSAGES/base.mo index b8808152..c5eee97a 100644 Binary files a/archinstall/locales/cs/LC_MESSAGES/base.mo and b/archinstall/locales/cs/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/cs/LC_MESSAGES/base.po b/archinstall/locales/cs/LC_MESSAGES/base.po index 20f3c257..fa9aec03 100644 --- a/archinstall/locales/cs/LC_MESSAGES/base.po +++ b/archinstall/locales/cs/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Zadejte další balíčky k instalaci (oddělené mezerou, ponechte prá msgid "Copy ISO network configuration to installation" msgstr "Zkopírovat do instalace konfiguraci sítě z ISO" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "Použít NetworkManager (potřebné pro grafickou konfiguraci v GNOME a KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/de/LC_MESSAGES/base.mo b/archinstall/locales/de/LC_MESSAGES/base.mo index 988eeb38..013ad598 100644 Binary files a/archinstall/locales/de/LC_MESSAGES/base.mo and b/archinstall/locales/de/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/de/LC_MESSAGES/base.po b/archinstall/locales/de/LC_MESSAGES/base.po index ed1657ee..75beee94 100644 --- a/archinstall/locales/de/LC_MESSAGES/base.po +++ b/archinstall/locales/de/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Schreiben Sie zusätzliche Pakete die installiert werden sollen (mit ein msgid "Copy ISO network configuration to installation" msgstr "ISO-Netzwerk Einstellungen in die Installation kopieren" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "NetworkManager benutzen (notwendig um Internet auf grafische Weise in GNOME und KDE einzustellen)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/el/LC_MESSAGES/base.mo b/archinstall/locales/el/LC_MESSAGES/base.mo index 9a8c99af..aa0eb25e 100644 Binary files a/archinstall/locales/el/LC_MESSAGES/base.mo and b/archinstall/locales/el/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/el/LC_MESSAGES/base.po b/archinstall/locales/el/LC_MESSAGES/base.po index 4b4db260..8ffb37fb 100644 --- a/archinstall/locales/el/LC_MESSAGES/base.po +++ b/archinstall/locales/el/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Γράψτε περαιτέρω πακέτα προς εγκατάστα msgid "Copy ISO network configuration to installation" msgstr "Αντιγραφή διαμόρφωση δικτύου ISO στην εγκατάσταση" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "Χρήση NetworkManager (απαραίτητος για τη διαμόρφωση του δικτύου γραφικά σε GNOME και KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/en/LC_MESSAGES/base.po b/archinstall/locales/en/LC_MESSAGES/base.po index 5a38a4db..8e98011e 100644 --- a/archinstall/locales/en/LC_MESSAGES/base.po +++ b/archinstall/locales/en/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "" msgid "Copy ISO network configuration to installation" msgstr "" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "" msgid "Select one network interface to configure" diff --git a/archinstall/locales/es/LC_MESSAGES/base.mo b/archinstall/locales/es/LC_MESSAGES/base.mo index 7194020e..816720b2 100644 Binary files a/archinstall/locales/es/LC_MESSAGES/base.mo and b/archinstall/locales/es/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/es/LC_MESSAGES/base.po b/archinstall/locales/es/LC_MESSAGES/base.po index 22ab693c..b74eccdb 100644 --- a/archinstall/locales/es/LC_MESSAGES/base.po +++ b/archinstall/locales/es/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Escriba paquetes adicionales para instalar (separados por espacios, deja msgid "Copy ISO network configuration to installation" msgstr "Copiar la configuración de red ISO a la instalación" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "Usar NetworkManager (necesario para configurar internet gráficamente en GNOME y KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/et/LC_MESSAGES/base.mo b/archinstall/locales/et/LC_MESSAGES/base.mo index f9c9f7ce..2cb48d14 100644 Binary files a/archinstall/locales/et/LC_MESSAGES/base.mo and b/archinstall/locales/et/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/et/LC_MESSAGES/base.po b/archinstall/locales/et/LC_MESSAGES/base.po index 8c2582f9..d03e51d7 100644 --- a/archinstall/locales/et/LC_MESSAGES/base.po +++ b/archinstall/locales/et/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Kirjutage paigaldatavad lisapaketid (tühikutega eraldatuna, jätke tüh msgid "Copy ISO network configuration to installation" msgstr "ISO-võrgu konfiguratsiooni kopeerimine paigaldusse" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "Kasutage NetworkManagerit (vajalik interneti graafiliseks konfigureerimiseks GNOME-s ja KDE-s)." msgid "Select one network interface to configure" diff --git a/archinstall/locales/fr/LC_MESSAGES/base.mo b/archinstall/locales/fr/LC_MESSAGES/base.mo index 81e6ce00..3889b469 100644 Binary files a/archinstall/locales/fr/LC_MESSAGES/base.mo and b/archinstall/locales/fr/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/fr/LC_MESSAGES/base.po b/archinstall/locales/fr/LC_MESSAGES/base.po index ec5fc68e..bc2c2c5a 100644 --- a/archinstall/locales/fr/LC_MESSAGES/base.po +++ b/archinstall/locales/fr/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Écrire des packages supplémentaires à installer (espaces séparés, l msgid "Copy ISO network configuration to installation" msgstr "Copier la configuration réseau ISO dans l'installation" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "Utiliser NetworkManager (nécessaire pour configurer graphiquement Internet dans GNOME et KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/id/LC_MESSAGES/base.mo b/archinstall/locales/id/LC_MESSAGES/base.mo index db3649bd..c9c5d191 100644 Binary files a/archinstall/locales/id/LC_MESSAGES/base.mo and b/archinstall/locales/id/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/id/LC_MESSAGES/base.po b/archinstall/locales/id/LC_MESSAGES/base.po index f2ede55f..791200b6 100644 --- a/archinstall/locales/id/LC_MESSAGES/base.po +++ b/archinstall/locales/id/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Ketik paket tambahan untuk diinstal (dipisahkan dengan spasi, biarkan ko msgid "Copy ISO network configuration to installation" msgstr "Salin konfigurasi jaringan ISO ke instalasi" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "Gunakan NetworkManager (diperlukan untuk mengkonfigurasi internet secara grafis di GNOME dan KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/it/LC_MESSAGES/base.mo b/archinstall/locales/it/LC_MESSAGES/base.mo index e63c377e..f20e4ca7 100644 Binary files a/archinstall/locales/it/LC_MESSAGES/base.mo and b/archinstall/locales/it/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/it/LC_MESSAGES/base.po b/archinstall/locales/it/LC_MESSAGES/base.po index 703f0000..b2701e2a 100644 --- a/archinstall/locales/it/LC_MESSAGES/base.po +++ b/archinstall/locales/it/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Scrivi pacchetti aggiuntivi da installare (separati da spazi, lascia vuo msgid "Copy ISO network configuration to installation" msgstr "Copia la configurazione di rete ISO nell'installazione" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "Usa NetworkManager (necessario per configurare graficamente Internet in GNOME e KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/ka/LC_MESSAGES/base.mo b/archinstall/locales/ka/LC_MESSAGES/base.mo index 31e14aa2..7f881a1e 100644 Binary files a/archinstall/locales/ka/LC_MESSAGES/base.mo and b/archinstall/locales/ka/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ka/LC_MESSAGES/base.po b/archinstall/locales/ka/LC_MESSAGES/base.po index 6e4823eb..5310b8e2 100644 --- a/archinstall/locales/ka/LC_MESSAGES/base.po +++ b/archinstall/locales/ka/LC_MESSAGES/base.po @@ -63,7 +63,7 @@ msgstr "დამატებითი პაკეტები დასაყ msgid "Copy ISO network configuration to installation" msgstr "ISO-ის ქსელის კონფიგურაციის კოპირება დაყენების დროს" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "NetworkManager-ის გამოყენება (აუცილებელია ინტერნეტის GNOME/KDE-დან მოსარგებად)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/ko/LC_MESSAGES/base.mo b/archinstall/locales/ko/LC_MESSAGES/base.mo index ddec6151..fd566d62 100644 Binary files a/archinstall/locales/ko/LC_MESSAGES/base.mo and b/archinstall/locales/ko/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ko/LC_MESSAGES/base.po b/archinstall/locales/ko/LC_MESSAGES/base.po index de46b698..5287b097 100644 --- a/archinstall/locales/ko/LC_MESSAGES/base.po +++ b/archinstall/locales/ko/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "설치할 추가 패키지 작성하세요 (띄어쓰기로 구분, 건 msgid "Copy ISO network configuration to installation" msgstr "ISO 네트워크 구성을 설치에 복사" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "NetworkManager 사용 (GNOME 이나 KDE 에서 그래픽으로 인터넷을 구성하는 데 필요)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/nl/LC_MESSAGES/base.mo b/archinstall/locales/nl/LC_MESSAGES/base.mo index 222f21e9..017c7645 100644 Binary files a/archinstall/locales/nl/LC_MESSAGES/base.mo and b/archinstall/locales/nl/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/nl/LC_MESSAGES/base.po b/archinstall/locales/nl/LC_MESSAGES/base.po index 6dfeed53..5c88cbf5 100644 --- a/archinstall/locales/nl/LC_MESSAGES/base.po +++ b/archinstall/locales/nl/LC_MESSAGES/base.po @@ -63,7 +63,7 @@ msgstr "Typ de namen van te installeren pakketten (spatiegescheiden - laat leeg msgid "Copy ISO network configuration to installation" msgstr "ISO-netwerkinstellingen overzetten naar fysieke installatie" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "NetworkManager gebruiken (benodigd om internetinstellingen grafisch in te stellen in GNOME en KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/pl/LC_MESSAGES/base.mo b/archinstall/locales/pl/LC_MESSAGES/base.mo index 0904a1a1..5892c23b 100644 Binary files a/archinstall/locales/pl/LC_MESSAGES/base.mo and b/archinstall/locales/pl/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pl/LC_MESSAGES/base.po b/archinstall/locales/pl/LC_MESSAGES/base.po index fa53c7f0..22e46ca5 100644 --- a/archinstall/locales/pl/LC_MESSAGES/base.po +++ b/archinstall/locales/pl/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Wpisz dodatkowe pakiety do zainstalowania (oddzielone spacjami, pozostaw msgid "Copy ISO network configuration to installation" msgstr "Skopiuj ustawienia sieciowe ISO do instalacji" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "Użyj programu NetworkManager (niezbędne do graficznej konfiguracji Internetu w środowiskach GNOME i KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/pt/LC_MESSAGES/base.mo b/archinstall/locales/pt/LC_MESSAGES/base.mo index 99fd4b02..7d3e1212 100644 Binary files a/archinstall/locales/pt/LC_MESSAGES/base.mo and b/archinstall/locales/pt/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pt/LC_MESSAGES/base.po b/archinstall/locales/pt/LC_MESSAGES/base.po index 7f0d5eba..761612e9 100644 --- a/archinstall/locales/pt/LC_MESSAGES/base.po +++ b/archinstall/locales/pt/LC_MESSAGES/base.po @@ -61,7 +61,7 @@ msgstr "Escreve pacotes adicionais para instalar (separados por espaço, deixa e msgid "Copy ISO network configuration to installation" msgstr "Copiar a configuração de rede do ISO para a instalação" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "Usar NetworkManager (necessário para configurar internet graficamente em GNOME e KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo index 51580dbb..4fdcf0ef 100644 Binary files a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo and b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.po b/archinstall/locales/pt_BR/LC_MESSAGES/base.po index c36250f5..72afd820 100644 --- a/archinstall/locales/pt_BR/LC_MESSAGES/base.po +++ b/archinstall/locales/pt_BR/LC_MESSAGES/base.po @@ -66,7 +66,7 @@ msgstr "Digite pacotes adicionais para instalar (separados por espaço, deixe em msgid "Copy ISO network configuration to installation" msgstr "Copiar a configuração de rede da ISO para a instalação" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "Usar NetworkManager (necessário para configurar internet graficamente no GNOME e KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/ru/LC_MESSAGES/base.mo b/archinstall/locales/ru/LC_MESSAGES/base.mo index 4480d852..7a46d3d0 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 90d78f2e..8d1ddefb 100644 --- a/archinstall/locales/ru/LC_MESSAGES/base.po +++ b/archinstall/locales/ru/LC_MESSAGES/base.po @@ -63,7 +63,7 @@ msgstr "Напишите дополнительные пакеты для уст msgid "Copy ISO network configuration to installation" msgstr "Копировать сетевую конфигурацию ISO в установку" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "Использовать NetworkManager (необходим для графической настройки интернета в GNOME и KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/sv/LC_MESSAGES/base.mo b/archinstall/locales/sv/LC_MESSAGES/base.mo index 243f1d9f..90c90265 100644 Binary files a/archinstall/locales/sv/LC_MESSAGES/base.mo and b/archinstall/locales/sv/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/sv/LC_MESSAGES/base.po b/archinstall/locales/sv/LC_MESSAGES/base.po index ebb7a275..7b1a4c3a 100644 --- a/archinstall/locales/sv/LC_MESSAGES/base.po +++ b/archinstall/locales/sv/LC_MESSAGES/base.po @@ -63,7 +63,7 @@ msgstr "Skriv ytterligare paket som skall installeras (separerade med mellanslag msgid "Copy ISO network configuration to installation" msgstr "Kopiera nätverkskonfigurationen från ISO till installationen" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "Använd NetworkManager (nödvändig för konfigurera internet i grafiska miljöerna GNOME och KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/ta/LC_MESSAGES/base.mo b/archinstall/locales/ta/LC_MESSAGES/base.mo index fc2b70e6..dd7d1632 100644 Binary files a/archinstall/locales/ta/LC_MESSAGES/base.mo and b/archinstall/locales/ta/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ta/LC_MESSAGES/base.po b/archinstall/locales/ta/LC_MESSAGES/base.po index 14ed4d98..b9276f2a 100644 --- a/archinstall/locales/ta/LC_MESSAGES/base.po +++ b/archinstall/locales/ta/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "நிறுவ கூடுதல் தொகுப்புகளை msgid "Copy ISO network configuration to installation" msgstr "நிறுவலுக்கு ISO பிணைய கட்டமைப்பு நகலெடுக்கவும்" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "பயன்படுத்துங்கள் NetworkManager ஐப்(GNOME மற்றும் KDE இல் இணையத்தை வரைகலை முறையில் கட்டமைக்க அவசியம்)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/tr/LC_MESSAGES/base.mo b/archinstall/locales/tr/LC_MESSAGES/base.mo index 2e9d1258..0e86c328 100644 Binary files a/archinstall/locales/tr/LC_MESSAGES/base.mo and b/archinstall/locales/tr/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/tr/LC_MESSAGES/base.po b/archinstall/locales/tr/LC_MESSAGES/base.po index 20fa4f23..ee7a6333 100644 --- a/archinstall/locales/tr/LC_MESSAGES/base.po +++ b/archinstall/locales/tr/LC_MESSAGES/base.po @@ -63,7 +63,7 @@ msgstr "Kurulacak ek paketleri yazınız (boşlukla ayrılmış, geçmek için b msgid "Copy ISO network configuration to installation" msgstr "Kuruluma ISO'dan ağ yapılandırmasını kopyala" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "NetworkManager'ı kullan (GNOME ya da KDE'de interneti grafik olarak yapılandırmak için gerekli)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/uk/LC_MESSAGES/base.mo b/archinstall/locales/uk/LC_MESSAGES/base.mo index e7cb8b0b..ad3be19d 100644 Binary files a/archinstall/locales/uk/LC_MESSAGES/base.mo and b/archinstall/locales/uk/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/uk/LC_MESSAGES/base.po b/archinstall/locales/uk/LC_MESSAGES/base.po index a700c126..f867dd02 100644 --- a/archinstall/locales/uk/LC_MESSAGES/base.po +++ b/archinstall/locales/uk/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Напишіть додаткові пакети для інсталяц msgid "Copy ISO network configuration to installation" msgstr "Скопіюйте конфігурацію мережі ISO для встановлення" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "Використовувати NetworkManager (необхідний для графічного налаштування Інтернету в GNOME та KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/ur/LC_MESSAGES/base.mo b/archinstall/locales/ur/LC_MESSAGES/base.mo index e788d932..c1385100 100644 Binary files a/archinstall/locales/ur/LC_MESSAGES/base.mo and b/archinstall/locales/ur/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ur/LC_MESSAGES/base.po b/archinstall/locales/ur/LC_MESSAGES/base.po index ac648108..b089bded 100644 --- a/archinstall/locales/ur/LC_MESSAGES/base.po +++ b/archinstall/locales/ur/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "انسٹال کرنےکے لیے اضافی پیکجز لکھیں (ال msgid "Copy ISO network configuration to installation" msgstr "آئی ایس او نیٹ ورک کنفیگریشن کو انسٹالیشن میں کاپی کریں" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "نیٹ ورک مینجر کا استعمال کریں (GNOME اور KDE میں انٹرنیٹ کو گرافیکلی ترتیب دینے کے لیے ضروری ہے)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.mo b/archinstall/locales/zh-CN/LC_MESSAGES/base.mo index 4502c562..95fefb95 100644 Binary files a/archinstall/locales/zh-CN/LC_MESSAGES/base.mo and b/archinstall/locales/zh-CN/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.po b/archinstall/locales/zh-CN/LC_MESSAGES/base.po index 04a703ed..aa24192e 100644 --- a/archinstall/locales/zh-CN/LC_MESSAGES/base.po +++ b/archinstall/locales/zh-CN/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "编写要安装的附加软件包(空格分隔,留空跳过): " msgid "Copy ISO network configuration to installation" msgstr "将 ISO 网络配置复制到安装" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "使用 NetworkManager(在 GNOME 和 KDE 中以图形方式配置 Internet 所必需的)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/zh-TW/LC_MESSAGES/base.mo b/archinstall/locales/zh-TW/LC_MESSAGES/base.mo index 8473e868..154e5fea 100644 Binary files a/archinstall/locales/zh-TW/LC_MESSAGES/base.mo and b/archinstall/locales/zh-TW/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/zh-TW/LC_MESSAGES/base.po b/archinstall/locales/zh-TW/LC_MESSAGES/base.po index 4b50b2ec..bfc9a2be 100644 --- a/archinstall/locales/zh-TW/LC_MESSAGES/base.po +++ b/archinstall/locales/zh-TW/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "請輸入您要安裝的其它套件(以空格分隔,留空以跳過 msgid "Copy ISO network configuration to installation" msgstr "將 ISO 中的網路設置複製到安裝中" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" msgstr "使用 NetworkManager(在 GNOME 和 KDE 透過圖形界面設置網際網路連線所需)" msgid "Select one network interface to configure" -- cgit v1.2.3-70-g09d2 From b60a055348b23d47fe18c70e6c375282e8e66a24 Mon Sep 17 00:00:00 2001 From: George Angelopoulos Date: Mon, 5 Jun 2023 17:58:35 +0300 Subject: remove accidentally added space (#1845) --- archinstall/locales/ar/LC_MESSAGES/base.mo | Bin 3803 -> 3802 bytes archinstall/locales/ar/LC_MESSAGES/base.po | 2 +- archinstall/locales/base.pot | 2 +- archinstall/locales/cs/LC_MESSAGES/base.mo | Bin 25985 -> 25984 bytes archinstall/locales/cs/LC_MESSAGES/base.po | 2 +- archinstall/locales/de/LC_MESSAGES/base.mo | Bin 37645 -> 37644 bytes archinstall/locales/de/LC_MESSAGES/base.po | 2 +- archinstall/locales/el/LC_MESSAGES/base.mo | Bin 35432 -> 35431 bytes archinstall/locales/el/LC_MESSAGES/base.po | 2 +- archinstall/locales/en/LC_MESSAGES/base.po | 2 +- archinstall/locales/es/LC_MESSAGES/base.mo | Bin 24397 -> 24396 bytes archinstall/locales/es/LC_MESSAGES/base.po | 2 +- archinstall/locales/et/LC_MESSAGES/base.mo | Bin 35637 -> 35636 bytes archinstall/locales/et/LC_MESSAGES/base.po | 2 +- archinstall/locales/fr/LC_MESSAGES/base.mo | Bin 27432 -> 27431 bytes archinstall/locales/fr/LC_MESSAGES/base.po | 2 +- archinstall/locales/id/LC_MESSAGES/base.mo | Bin 26262 -> 26261 bytes archinstall/locales/id/LC_MESSAGES/base.po | 2 +- archinstall/locales/it/LC_MESSAGES/base.mo | Bin 26545 -> 26544 bytes archinstall/locales/it/LC_MESSAGES/base.po | 2 +- archinstall/locales/ka/LC_MESSAGES/base.mo | Bin 44827 -> 44826 bytes archinstall/locales/ka/LC_MESSAGES/base.po | 2 +- archinstall/locales/ko/LC_MESSAGES/base.mo | Bin 27225 -> 27224 bytes archinstall/locales/ko/LC_MESSAGES/base.po | 2 +- archinstall/locales/nl/LC_MESSAGES/base.mo | Bin 17692 -> 17691 bytes archinstall/locales/nl/LC_MESSAGES/base.po | 2 +- archinstall/locales/pl/LC_MESSAGES/base.mo | Bin 25130 -> 25129 bytes archinstall/locales/pl/LC_MESSAGES/base.po | 2 +- archinstall/locales/pt/LC_MESSAGES/base.mo | Bin 16337 -> 16336 bytes archinstall/locales/pt/LC_MESSAGES/base.po | 2 +- archinstall/locales/pt_BR/LC_MESSAGES/base.mo | Bin 27027 -> 27026 bytes archinstall/locales/pt_BR/LC_MESSAGES/base.po | 2 +- archinstall/locales/ru/LC_MESSAGES/base.mo | Bin 35893 -> 35892 bytes archinstall/locales/ru/LC_MESSAGES/base.po | 2 +- archinstall/locales/sv/LC_MESSAGES/base.mo | Bin 22733 -> 22732 bytes archinstall/locales/sv/LC_MESSAGES/base.po | 2 +- archinstall/locales/ta/LC_MESSAGES/base.mo | Bin 47480 -> 47479 bytes archinstall/locales/ta/LC_MESSAGES/base.po | 2 +- archinstall/locales/tr/LC_MESSAGES/base.mo | Bin 24426 -> 24425 bytes archinstall/locales/tr/LC_MESSAGES/base.po | 2 +- archinstall/locales/uk/LC_MESSAGES/base.mo | Bin 36028 -> 36027 bytes archinstall/locales/uk/LC_MESSAGES/base.po | 2 +- archinstall/locales/ur/LC_MESSAGES/base.mo | Bin 20491 -> 20490 bytes archinstall/locales/ur/LC_MESSAGES/base.po | 2 +- archinstall/locales/zh-CN/LC_MESSAGES/base.mo | Bin 23996 -> 23995 bytes archinstall/locales/zh-CN/LC_MESSAGES/base.po | 2 +- archinstall/locales/zh-TW/LC_MESSAGES/base.mo | Bin 32094 -> 32093 bytes archinstall/locales/zh-TW/LC_MESSAGES/base.po | 2 +- 48 files changed, 25 insertions(+), 25 deletions(-) diff --git a/archinstall/locales/ar/LC_MESSAGES/base.mo b/archinstall/locales/ar/LC_MESSAGES/base.mo index 4426a2dd..d6e9ef26 100644 Binary files a/archinstall/locales/ar/LC_MESSAGES/base.mo and b/archinstall/locales/ar/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ar/LC_MESSAGES/base.po b/archinstall/locales/ar/LC_MESSAGES/base.po index ab1e512f..5633fbe6 100644 --- a/archinstall/locales/ar/LC_MESSAGES/base.po +++ b/archinstall/locales/ar/LC_MESSAGES/base.po @@ -65,7 +65,7 @@ msgstr "اكتب حزمًا إضافية لتثبيتها (تُفصَل بالم msgid "Copy ISO network configuration to installation" msgstr "انسخ إعداد شبكة الـISO للتثبيت" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "استخدم مُدير الشبكة (ضروري لإعداد الإنترنت باستخدام واجهة رسومية في جنوم و كيدي)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/base.pot b/archinstall/locales/base.pot index 9c6bb2cb..fd521883 100644 --- a/archinstall/locales/base.pot +++ b/archinstall/locales/base.pot @@ -63,7 +63,7 @@ msgid "Copy ISO network configuration to installation" msgstr "" msgid "" -"Use NetworkManager ( necessary for configuring internet graphically in GNOME and " +"Use NetworkManager (necessary for configuring internet graphically in GNOME and " "KDE)" msgstr "" diff --git a/archinstall/locales/cs/LC_MESSAGES/base.mo b/archinstall/locales/cs/LC_MESSAGES/base.mo index c5eee97a..d668d464 100644 Binary files a/archinstall/locales/cs/LC_MESSAGES/base.mo and b/archinstall/locales/cs/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/cs/LC_MESSAGES/base.po b/archinstall/locales/cs/LC_MESSAGES/base.po index fa9aec03..0a3aa43c 100644 --- a/archinstall/locales/cs/LC_MESSAGES/base.po +++ b/archinstall/locales/cs/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Zadejte další balíčky k instalaci (oddělené mezerou, ponechte prá msgid "Copy ISO network configuration to installation" msgstr "Zkopírovat do instalace konfiguraci sítě z ISO" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "Použít NetworkManager (potřebné pro grafickou konfiguraci v GNOME a KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/de/LC_MESSAGES/base.mo b/archinstall/locales/de/LC_MESSAGES/base.mo index 013ad598..05cb8081 100644 Binary files a/archinstall/locales/de/LC_MESSAGES/base.mo and b/archinstall/locales/de/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/de/LC_MESSAGES/base.po b/archinstall/locales/de/LC_MESSAGES/base.po index 75beee94..c3694e25 100644 --- a/archinstall/locales/de/LC_MESSAGES/base.po +++ b/archinstall/locales/de/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Schreiben Sie zusätzliche Pakete die installiert werden sollen (mit ein msgid "Copy ISO network configuration to installation" msgstr "ISO-Netzwerk Einstellungen in die Installation kopieren" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "NetworkManager benutzen (notwendig um Internet auf grafische Weise in GNOME und KDE einzustellen)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/el/LC_MESSAGES/base.mo b/archinstall/locales/el/LC_MESSAGES/base.mo index aa0eb25e..40a9c663 100644 Binary files a/archinstall/locales/el/LC_MESSAGES/base.mo and b/archinstall/locales/el/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/el/LC_MESSAGES/base.po b/archinstall/locales/el/LC_MESSAGES/base.po index 8ffb37fb..7c04c2ca 100644 --- a/archinstall/locales/el/LC_MESSAGES/base.po +++ b/archinstall/locales/el/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Γράψτε περαιτέρω πακέτα προς εγκατάστα msgid "Copy ISO network configuration to installation" msgstr "Αντιγραφή διαμόρφωση δικτύου ISO στην εγκατάσταση" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "Χρήση NetworkManager (απαραίτητος για τη διαμόρφωση του δικτύου γραφικά σε GNOME και KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/en/LC_MESSAGES/base.po b/archinstall/locales/en/LC_MESSAGES/base.po index 8e98011e..ae7f080b 100644 --- a/archinstall/locales/en/LC_MESSAGES/base.po +++ b/archinstall/locales/en/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "" msgid "Copy ISO network configuration to installation" msgstr "" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "" msgid "Select one network interface to configure" diff --git a/archinstall/locales/es/LC_MESSAGES/base.mo b/archinstall/locales/es/LC_MESSAGES/base.mo index 816720b2..08511f1d 100644 Binary files a/archinstall/locales/es/LC_MESSAGES/base.mo and b/archinstall/locales/es/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/es/LC_MESSAGES/base.po b/archinstall/locales/es/LC_MESSAGES/base.po index b74eccdb..f507683b 100644 --- a/archinstall/locales/es/LC_MESSAGES/base.po +++ b/archinstall/locales/es/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Escriba paquetes adicionales para instalar (separados por espacios, deja msgid "Copy ISO network configuration to installation" msgstr "Copiar la configuración de red ISO a la instalación" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "Usar NetworkManager (necesario para configurar internet gráficamente en GNOME y KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/et/LC_MESSAGES/base.mo b/archinstall/locales/et/LC_MESSAGES/base.mo index 2cb48d14..d4a59283 100644 Binary files a/archinstall/locales/et/LC_MESSAGES/base.mo and b/archinstall/locales/et/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/et/LC_MESSAGES/base.po b/archinstall/locales/et/LC_MESSAGES/base.po index d03e51d7..ca97f57c 100644 --- a/archinstall/locales/et/LC_MESSAGES/base.po +++ b/archinstall/locales/et/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Kirjutage paigaldatavad lisapaketid (tühikutega eraldatuna, jätke tüh msgid "Copy ISO network configuration to installation" msgstr "ISO-võrgu konfiguratsiooni kopeerimine paigaldusse" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "Kasutage NetworkManagerit (vajalik interneti graafiliseks konfigureerimiseks GNOME-s ja KDE-s)." msgid "Select one network interface to configure" diff --git a/archinstall/locales/fr/LC_MESSAGES/base.mo b/archinstall/locales/fr/LC_MESSAGES/base.mo index 3889b469..b34ab4d1 100644 Binary files a/archinstall/locales/fr/LC_MESSAGES/base.mo and b/archinstall/locales/fr/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/fr/LC_MESSAGES/base.po b/archinstall/locales/fr/LC_MESSAGES/base.po index bc2c2c5a..9f04a081 100644 --- a/archinstall/locales/fr/LC_MESSAGES/base.po +++ b/archinstall/locales/fr/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Écrire des packages supplémentaires à installer (espaces séparés, l msgid "Copy ISO network configuration to installation" msgstr "Copier la configuration réseau ISO dans l'installation" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "Utiliser NetworkManager (nécessaire pour configurer graphiquement Internet dans GNOME et KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/id/LC_MESSAGES/base.mo b/archinstall/locales/id/LC_MESSAGES/base.mo index c9c5d191..1d19f536 100644 Binary files a/archinstall/locales/id/LC_MESSAGES/base.mo and b/archinstall/locales/id/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/id/LC_MESSAGES/base.po b/archinstall/locales/id/LC_MESSAGES/base.po index 791200b6..39b50564 100644 --- a/archinstall/locales/id/LC_MESSAGES/base.po +++ b/archinstall/locales/id/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Ketik paket tambahan untuk diinstal (dipisahkan dengan spasi, biarkan ko msgid "Copy ISO network configuration to installation" msgstr "Salin konfigurasi jaringan ISO ke instalasi" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "Gunakan NetworkManager (diperlukan untuk mengkonfigurasi internet secara grafis di GNOME dan KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/it/LC_MESSAGES/base.mo b/archinstall/locales/it/LC_MESSAGES/base.mo index f20e4ca7..1fde5011 100644 Binary files a/archinstall/locales/it/LC_MESSAGES/base.mo and b/archinstall/locales/it/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/it/LC_MESSAGES/base.po b/archinstall/locales/it/LC_MESSAGES/base.po index b2701e2a..acb2ef11 100644 --- a/archinstall/locales/it/LC_MESSAGES/base.po +++ b/archinstall/locales/it/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Scrivi pacchetti aggiuntivi da installare (separati da spazi, lascia vuo msgid "Copy ISO network configuration to installation" msgstr "Copia la configurazione di rete ISO nell'installazione" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "Usa NetworkManager (necessario per configurare graficamente Internet in GNOME e KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/ka/LC_MESSAGES/base.mo b/archinstall/locales/ka/LC_MESSAGES/base.mo index 7f881a1e..dfb1dc92 100644 Binary files a/archinstall/locales/ka/LC_MESSAGES/base.mo and b/archinstall/locales/ka/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ka/LC_MESSAGES/base.po b/archinstall/locales/ka/LC_MESSAGES/base.po index 5310b8e2..727730c3 100644 --- a/archinstall/locales/ka/LC_MESSAGES/base.po +++ b/archinstall/locales/ka/LC_MESSAGES/base.po @@ -63,7 +63,7 @@ msgstr "დამატებითი პაკეტები დასაყ msgid "Copy ISO network configuration to installation" msgstr "ISO-ის ქსელის კონფიგურაციის კოპირება დაყენების დროს" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "NetworkManager-ის გამოყენება (აუცილებელია ინტერნეტის GNOME/KDE-დან მოსარგებად)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/ko/LC_MESSAGES/base.mo b/archinstall/locales/ko/LC_MESSAGES/base.mo index fd566d62..dc3fa713 100644 Binary files a/archinstall/locales/ko/LC_MESSAGES/base.mo and b/archinstall/locales/ko/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ko/LC_MESSAGES/base.po b/archinstall/locales/ko/LC_MESSAGES/base.po index 5287b097..0a26aeed 100644 --- a/archinstall/locales/ko/LC_MESSAGES/base.po +++ b/archinstall/locales/ko/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "설치할 추가 패키지 작성하세요 (띄어쓰기로 구분, 건 msgid "Copy ISO network configuration to installation" msgstr "ISO 네트워크 구성을 설치에 복사" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "NetworkManager 사용 (GNOME 이나 KDE 에서 그래픽으로 인터넷을 구성하는 데 필요)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/nl/LC_MESSAGES/base.mo b/archinstall/locales/nl/LC_MESSAGES/base.mo index 017c7645..4edd6bc9 100644 Binary files a/archinstall/locales/nl/LC_MESSAGES/base.mo and b/archinstall/locales/nl/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/nl/LC_MESSAGES/base.po b/archinstall/locales/nl/LC_MESSAGES/base.po index 5c88cbf5..f9753866 100644 --- a/archinstall/locales/nl/LC_MESSAGES/base.po +++ b/archinstall/locales/nl/LC_MESSAGES/base.po @@ -63,7 +63,7 @@ msgstr "Typ de namen van te installeren pakketten (spatiegescheiden - laat leeg msgid "Copy ISO network configuration to installation" msgstr "ISO-netwerkinstellingen overzetten naar fysieke installatie" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "NetworkManager gebruiken (benodigd om internetinstellingen grafisch in te stellen in GNOME en KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/pl/LC_MESSAGES/base.mo b/archinstall/locales/pl/LC_MESSAGES/base.mo index 5892c23b..ed3e216d 100644 Binary files a/archinstall/locales/pl/LC_MESSAGES/base.mo and b/archinstall/locales/pl/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pl/LC_MESSAGES/base.po b/archinstall/locales/pl/LC_MESSAGES/base.po index 22e46ca5..f0fda19f 100644 --- a/archinstall/locales/pl/LC_MESSAGES/base.po +++ b/archinstall/locales/pl/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Wpisz dodatkowe pakiety do zainstalowania (oddzielone spacjami, pozostaw msgid "Copy ISO network configuration to installation" msgstr "Skopiuj ustawienia sieciowe ISO do instalacji" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "Użyj programu NetworkManager (niezbędne do graficznej konfiguracji Internetu w środowiskach GNOME i KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/pt/LC_MESSAGES/base.mo b/archinstall/locales/pt/LC_MESSAGES/base.mo index 7d3e1212..fe43da98 100644 Binary files a/archinstall/locales/pt/LC_MESSAGES/base.mo and b/archinstall/locales/pt/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pt/LC_MESSAGES/base.po b/archinstall/locales/pt/LC_MESSAGES/base.po index 761612e9..6c4e71f6 100644 --- a/archinstall/locales/pt/LC_MESSAGES/base.po +++ b/archinstall/locales/pt/LC_MESSAGES/base.po @@ -61,7 +61,7 @@ msgstr "Escreve pacotes adicionais para instalar (separados por espaço, deixa e msgid "Copy ISO network configuration to installation" msgstr "Copiar a configuração de rede do ISO para a instalação" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "Usar NetworkManager (necessário para configurar internet graficamente em GNOME e KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo index 4fdcf0ef..fdd1a73e 100644 Binary files a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo and b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.po b/archinstall/locales/pt_BR/LC_MESSAGES/base.po index 72afd820..a4137edf 100644 --- a/archinstall/locales/pt_BR/LC_MESSAGES/base.po +++ b/archinstall/locales/pt_BR/LC_MESSAGES/base.po @@ -66,7 +66,7 @@ msgstr "Digite pacotes adicionais para instalar (separados por espaço, deixe em msgid "Copy ISO network configuration to installation" msgstr "Copiar a configuração de rede da ISO para a instalação" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "Usar NetworkManager (necessário para configurar internet graficamente no GNOME e KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/ru/LC_MESSAGES/base.mo b/archinstall/locales/ru/LC_MESSAGES/base.mo index 7a46d3d0..a8300a4a 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 8d1ddefb..815771e9 100644 --- a/archinstall/locales/ru/LC_MESSAGES/base.po +++ b/archinstall/locales/ru/LC_MESSAGES/base.po @@ -63,7 +63,7 @@ msgstr "Напишите дополнительные пакеты для уст msgid "Copy ISO network configuration to installation" msgstr "Копировать сетевую конфигурацию ISO в установку" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "Использовать NetworkManager (необходим для графической настройки интернета в GNOME и KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/sv/LC_MESSAGES/base.mo b/archinstall/locales/sv/LC_MESSAGES/base.mo index 90c90265..906c9de5 100644 Binary files a/archinstall/locales/sv/LC_MESSAGES/base.mo and b/archinstall/locales/sv/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/sv/LC_MESSAGES/base.po b/archinstall/locales/sv/LC_MESSAGES/base.po index 7b1a4c3a..fa54b97e 100644 --- a/archinstall/locales/sv/LC_MESSAGES/base.po +++ b/archinstall/locales/sv/LC_MESSAGES/base.po @@ -63,7 +63,7 @@ msgstr "Skriv ytterligare paket som skall installeras (separerade med mellanslag msgid "Copy ISO network configuration to installation" msgstr "Kopiera nätverkskonfigurationen från ISO till installationen" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "Använd NetworkManager (nödvändig för konfigurera internet i grafiska miljöerna GNOME och KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/ta/LC_MESSAGES/base.mo b/archinstall/locales/ta/LC_MESSAGES/base.mo index dd7d1632..6e7c6ac9 100644 Binary files a/archinstall/locales/ta/LC_MESSAGES/base.mo and b/archinstall/locales/ta/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ta/LC_MESSAGES/base.po b/archinstall/locales/ta/LC_MESSAGES/base.po index b9276f2a..968cb1fc 100644 --- a/archinstall/locales/ta/LC_MESSAGES/base.po +++ b/archinstall/locales/ta/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "நிறுவ கூடுதல் தொகுப்புகளை msgid "Copy ISO network configuration to installation" msgstr "நிறுவலுக்கு ISO பிணைய கட்டமைப்பு நகலெடுக்கவும்" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "பயன்படுத்துங்கள் NetworkManager ஐப்(GNOME மற்றும் KDE இல் இணையத்தை வரைகலை முறையில் கட்டமைக்க அவசியம்)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/tr/LC_MESSAGES/base.mo b/archinstall/locales/tr/LC_MESSAGES/base.mo index 0e86c328..de6dea40 100644 Binary files a/archinstall/locales/tr/LC_MESSAGES/base.mo and b/archinstall/locales/tr/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/tr/LC_MESSAGES/base.po b/archinstall/locales/tr/LC_MESSAGES/base.po index ee7a6333..2bbcae95 100644 --- a/archinstall/locales/tr/LC_MESSAGES/base.po +++ b/archinstall/locales/tr/LC_MESSAGES/base.po @@ -63,7 +63,7 @@ msgstr "Kurulacak ek paketleri yazınız (boşlukla ayrılmış, geçmek için b msgid "Copy ISO network configuration to installation" msgstr "Kuruluma ISO'dan ağ yapılandırmasını kopyala" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "NetworkManager'ı kullan (GNOME ya da KDE'de interneti grafik olarak yapılandırmak için gerekli)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/uk/LC_MESSAGES/base.mo b/archinstall/locales/uk/LC_MESSAGES/base.mo index ad3be19d..45bffd7e 100644 Binary files a/archinstall/locales/uk/LC_MESSAGES/base.mo and b/archinstall/locales/uk/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/uk/LC_MESSAGES/base.po b/archinstall/locales/uk/LC_MESSAGES/base.po index f867dd02..f2b84a49 100644 --- a/archinstall/locales/uk/LC_MESSAGES/base.po +++ b/archinstall/locales/uk/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "Напишіть додаткові пакети для інсталяц msgid "Copy ISO network configuration to installation" msgstr "Скопіюйте конфігурацію мережі ISO для встановлення" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "Використовувати NetworkManager (необхідний для графічного налаштування Інтернету в GNOME та KDE)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/ur/LC_MESSAGES/base.mo b/archinstall/locales/ur/LC_MESSAGES/base.mo index c1385100..67ce2f1f 100644 Binary files a/archinstall/locales/ur/LC_MESSAGES/base.mo and b/archinstall/locales/ur/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ur/LC_MESSAGES/base.po b/archinstall/locales/ur/LC_MESSAGES/base.po index b089bded..72555aec 100644 --- a/archinstall/locales/ur/LC_MESSAGES/base.po +++ b/archinstall/locales/ur/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "انسٹال کرنےکے لیے اضافی پیکجز لکھیں (ال msgid "Copy ISO network configuration to installation" msgstr "آئی ایس او نیٹ ورک کنفیگریشن کو انسٹالیشن میں کاپی کریں" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "نیٹ ورک مینجر کا استعمال کریں (GNOME اور KDE میں انٹرنیٹ کو گرافیکلی ترتیب دینے کے لیے ضروری ہے)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.mo b/archinstall/locales/zh-CN/LC_MESSAGES/base.mo index 95fefb95..7410cb7d 100644 Binary files a/archinstall/locales/zh-CN/LC_MESSAGES/base.mo and b/archinstall/locales/zh-CN/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.po b/archinstall/locales/zh-CN/LC_MESSAGES/base.po index aa24192e..8bda3ef0 100644 --- a/archinstall/locales/zh-CN/LC_MESSAGES/base.po +++ b/archinstall/locales/zh-CN/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "编写要安装的附加软件包(空格分隔,留空跳过): " msgid "Copy ISO network configuration to installation" msgstr "将 ISO 网络配置复制到安装" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "使用 NetworkManager(在 GNOME 和 KDE 中以图形方式配置 Internet 所必需的)" msgid "Select one network interface to configure" diff --git a/archinstall/locales/zh-TW/LC_MESSAGES/base.mo b/archinstall/locales/zh-TW/LC_MESSAGES/base.mo index 154e5fea..4a258a33 100644 Binary files a/archinstall/locales/zh-TW/LC_MESSAGES/base.mo and b/archinstall/locales/zh-TW/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/zh-TW/LC_MESSAGES/base.po b/archinstall/locales/zh-TW/LC_MESSAGES/base.po index bfc9a2be..0c3226ec 100644 --- a/archinstall/locales/zh-TW/LC_MESSAGES/base.po +++ b/archinstall/locales/zh-TW/LC_MESSAGES/base.po @@ -62,7 +62,7 @@ msgstr "請輸入您要安裝的其它套件(以空格分隔,留空以跳過 msgid "Copy ISO network configuration to installation" msgstr "將 ISO 中的網路設置複製到安裝中" -msgid "Use NetworkManager ( necessary for configuring internet graphically in GNOME and KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "使用 NetworkManager(在 GNOME 和 KDE 透過圖形界面設置網際網路連線所需)" msgid "Select one network interface to configure" -- cgit v1.2.3-70-g09d2 From bc4f80441f4c4c9ba084d4cba29cfaedf30b5490 Mon Sep 17 00:00:00 2001 From: Kefaku <114934849+Kefaku@users.noreply.github.com> Date: Wed, 7 Jun 2023 20:31:34 +0200 Subject: Add some german translations (#1848) --- archinstall/locales/de/LC_MESSAGES/base.po | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/archinstall/locales/de/LC_MESSAGES/base.po b/archinstall/locales/de/LC_MESSAGES/base.po index c3694e25..873e740a 100644 --- a/archinstall/locales/de/LC_MESSAGES/base.po +++ b/archinstall/locales/de/LC_MESSAGES/base.po @@ -1132,10 +1132,10 @@ msgid "Add a custom mirror" msgstr "Benutzerkonto hinzufügen" msgid "Change custom mirror" -msgstr "" +msgstr "Benutzerdefinierte Spiegelserver bearbeiten" msgid "Delete custom mirror" -msgstr "" +msgstr "Benutzerdefinierten Spiegelserver löschen" #, fuzzy msgid "Enter name (leave blank to skip): " @@ -1154,10 +1154,10 @@ msgid "Select signature option" msgstr "Laufwerke-layout auswählen" msgid "Custom mirrors" -msgstr "" +msgstr "Benutzerdefinierte Spiegelserver" msgid "Defined" -msgstr "" +msgstr "Definiert" #, fuzzy msgid "Mirrors" @@ -1192,10 +1192,10 @@ msgid "Add a custom mirror" msgstr "Benutzerkonto hinzufügen" msgid "Change custom mirror" -msgstr "" +msgstr "Benutzerdefinierte Spiegelserver bearbeiten" msgid "Delete custom mirror" -msgstr "" +msgstr "Benutzerdefinierte Spiegelserver bearbeiten" #, fuzzy msgid "Enter name (leave blank to skip): " @@ -1214,10 +1214,10 @@ msgid "Select signature option" msgstr "Laufwerke-layout auswählen" msgid "Custom mirrors" -msgstr "" +msgstr "Benutzerdefinierte Spiegel" msgid "Defined" -msgstr "" +msgstr "Definiert" #, fuzzy msgid "Save user configuration (including disk layout)" @@ -1234,6 +1234,9 @@ msgid "" "\n" "{}" msgstr "" +"Sollen {} Konfigurationsdateie(n) an der folgenden Stelle gespeichert werden?" +"\n" +"{}" #, fuzzy msgid "Saving {} configuration files to {}" @@ -1248,10 +1251,10 @@ msgid "Mirror regions" msgstr "Mirror-region" msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr "" +msgstr " - Maximalwert : {} ( Erlaubt {} parallele Downloads, erlaubt {max_downloads+1} Downloads gleichzeitig)" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" -msgstr "" +msgstr "Ungültige Eingabe! Erneut mit gültiger Eingabe versuchen [1 bis {}, oder 0 zum deaktivieren]" #~ msgid "Add :" #~ msgstr "Hinzufügen :" -- cgit v1.2.3-70-g09d2 From 96d0eb1de982acc7e66f2a72d38a83a3abbea29e Mon Sep 17 00:00:00 2001 From: Alexmelman88 <99257010+Alexmelman88@users.noreply.github.com> Date: Thu, 8 Jun 2023 11:47:22 +0300 Subject: Update Russian translation (#1849) --- archinstall/locales/ru/LC_MESSAGES/base.mo | Bin 35892 -> 49210 bytes archinstall/locales/ru/LC_MESSAGES/base.po | 654 ++++++++++++++++++----------- 2 files changed, 413 insertions(+), 241 deletions(-) diff --git a/archinstall/locales/ru/LC_MESSAGES/base.mo b/archinstall/locales/ru/LC_MESSAGES/base.mo index a8300a4a..a7b5fbd3 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 815771e9..b2743719 100644 --- a/archinstall/locales/ru/LC_MESSAGES/base.po +++ b/archinstall/locales/ru/LC_MESSAGES/base.po @@ -9,14 +9,19 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" -"X-Generator: Poedit 3.1\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" +"X-Generator: Poedit 3.3.1\n" msgid "[!] A log file has been created here: {} {}" msgstr "[!] Здесь был создан файл журнала: {} {}" -msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" -msgstr " Пожалуйста, отправьте эту проблему (и файл) по адресу https://github.com/archlinux/archinstall/issues" +msgid "" +" Please submit this issue (and file) to https://github.com/archlinux/" +"archinstall/issues" +msgstr "" +" Пожалуйста, отправьте эту проблему (и файл) по адресу https://github.com/" +"archlinux/archinstall/issues" msgid "Do you really want to abort?" msgstr "Вы действительно хотите прекратить?" @@ -31,10 +36,13 @@ msgid "Desired hostname for the installation: " msgstr "Желаемое имя хоста для установки: " msgid "Username for required superuser with sudo privileges: " -msgstr "Имя пользователя для требуемого суперпользователя с привилегиями sudo: " +msgstr "" +"Имя пользователя для требуемого суперпользователя с привилегиями sudo: " msgid "Any additional users to install (leave blank for no users): " -msgstr "Любые дополнительные пользователи для установки (оставьте пустым, если пользователей нет): " +msgstr "" +"Любые дополнительные пользователи для установки (оставьте пустым, если " +"пользователей нет): " msgid "Should this user be a superuser (sudoer)?" msgstr "Должен ли этот пользователь быть суперпользователем (sudoer)?" @@ -51,38 +59,59 @@ msgstr "Выберите загрузчик" msgid "Choose an audio server" msgstr "Выберите звуковой сервер" -msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." -msgstr "Устанавливаются только такие пакеты, как base, base-devel, linux, linux-firmware, efibootmgr и дополнительные пакеты профиля." +msgid "" +"Only packages such as base, base-devel, linux, linux-firmware, efibootmgr " +"and optional profile packages are installed." +msgstr "" +"Устанавливаются только такие пакеты, как base, base-devel, linux, linux-" +"firmware, efibootmgr и дополнительные пакеты профиля." -msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." -msgstr "Если вы хотите использовать веб-браузер, например, firefox или chromium, вы можете указать его в следующем запросе." +msgid "" +"If you desire a web browser, such as firefox or chromium, you may specify it " +"in the following prompt." +msgstr "" +"Если вы хотите использовать веб-браузер, например, firefox или chromium, вы " +"можете указать его в следующем запросе." -msgid "Write additional packages to install (space separated, leave blank to skip): " -msgstr "Напишите дополнительные пакеты для установки (разделите пробелами, оставьте пустым, чтобы пропустить): " +msgid "" +"Write additional packages to install (space separated, leave blank to skip): " +msgstr "" +"Напишите дополнительные пакеты для установки (разделите пробелами, оставьте " +"пустым, чтобы пропустить): " msgid "Copy ISO network configuration to installation" msgstr "Копировать сетевую конфигурацию ISO в установку" -msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" -msgstr "Использовать NetworkManager (необходим для графической настройки интернета в GNOME и KDE)" +msgid "" +"Use NetworkManager (necessary for configuring internet graphically in GNOME " +"and KDE)" +msgstr "" +"Использовать NetworkManager (необходим для графической настройки интернета в " +"GNOME и KDE)" msgid "Select one network interface to configure" msgstr "Выберите один сетевой интерфейс для настройки" -msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" -msgstr "Выберите режим для конфигурации \"{}\" или пропустите, чтобы использовать режим по умолчанию \"{}\"." +msgid "" +"Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" +msgstr "" +"Выберите режим для конфигурации \"{}\" или пропустите, чтобы использовать " +"режим по умолчанию \"{}\"." msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " msgstr "Введите IP-адрес и подсеть для {} (пример: 192.168.0.5/24): " msgid "Enter your gateway (router) IP address or leave blank for none: " -msgstr "Введите IP-адрес вашего шлюза (маршрутизатора) или оставьте пустым, если его нет: " +msgstr "" +"Введите IP-адрес вашего шлюза (маршрутизатора) или оставьте пустым, если его " +"нет: " msgid "Enter your DNS servers (space separated, blank for none): " msgstr "Введите ваши DNS-серверы (через пробел, пустой - нет): " msgid "Select which filesystem your main partition should use" -msgstr "Выберите, какую файловую систему должен использовать ваш основной раздел" +msgstr "" +"Выберите, какую файловую систему должен использовать ваш основной раздел" msgid "Current partition layout" msgstr "Текущая разметка разделов" @@ -97,11 +126,16 @@ msgstr "" msgid "Enter a desired filesystem type for the partition" msgstr "Введите желаемый тип файловой системы для раздела" -msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " +msgid "" +"Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " msgstr "" +"Введите начальное значение (в раздельных блоках: с, ГБ, % и т.д.; по " +"умолчанию: {}): " msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " msgstr "" +"Введите конечное значение (в раздельных блоках: с, Гб, % и т.д.; например: " +"{}): " msgid "{} contains queued partitions, this will remove those, are you sure?" msgstr "{} содержит разделы в очереди, это удалит их, вы уверены?" @@ -124,11 +158,17 @@ msgstr "" "\n" "Выберите по индексу, какой раздел куда монтировать" -msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * Точки монтирования разделов являются относительными внутри установки, например, загрузочный будет /boot." +msgid "" +" * Partition mount-points are relative to inside the installation, the boot " +"would be /boot as an example." +msgstr "" +" * Точки монтирования разделов являются относительными внутри установки, " +"например, загрузочный будет /boot." msgid "Select where to mount partition (leave blank to remove mountpoint): " -msgstr "Выберите куда монтировать раздел (оставьте пустым, чтобы удалить точку монтирования): " +msgstr "" +"Выберите куда монтировать раздел (оставьте пустым, чтобы удалить точку " +"монтирования): " msgid "" "{}\n" @@ -173,16 +213,25 @@ msgid "Archinstall language" msgstr "Язык Archinstall" msgid "Wipe all selected drives and use a best-effort default partition layout" -msgstr "Стереть все выбранные диски и использовать оптимальную схему разделов по умолчанию" +msgstr "" +"Стереть все выбранные диски и использовать оптимальную схему разделов по " +"умолчанию" -msgid "Select what to do with each individual drive (followed by partition usage)" -msgstr "Выберите, что делать с каждым отдельным диском (с последующим использованием разделов)" +msgid "" +"Select what to do with each individual drive (followed by partition usage)" +msgstr "" +"Выберите, что делать с каждым отдельным диском (с последующим использованием " +"разделов)" msgid "Select what you wish to do with the selected block devices" msgstr "Выберите, что вы хотите сделать с выбранными блочными устройствами" -msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments" -msgstr "Это список предварительно запрограммированных профилей, они могут облегчить установку таких вещей, как окружения рабочего стола" +msgid "" +"This is a list of pre-programmed profiles, they might make it easier to " +"install things like desktop environments" +msgstr "" +"Это список предварительно запрограммированных профилей, они могут облегчить " +"установку таких вещей, как окружения рабочего стола" msgid "Select keyboard layout" msgstr "Выберите раскладку клавиатуры" @@ -191,16 +240,29 @@ msgid "Select one of the regions to download packages from" msgstr "Выберите один из регионов для загрузки пакетов" msgid "Select one or more hard drives to use and configure" -msgstr "Выберите один или несколько жестких дисков для использования и настройте их" +msgstr "" +"Выберите один или несколько жестких дисков для использования и настройте их" -msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." -msgstr "Для наилучшей совместимости с оборудованием AMD вы можете использовать либо все варианты с открытым исходным кодом, либо AMD / ATI." +msgid "" +"For the best compatibility with your AMD hardware, you may want to use " +"either the all open-source or AMD / ATI options." +msgstr "" +"Для наилучшей совместимости с оборудованием AMD вы можете использовать либо " +"все варианты с открытым исходным кодом, либо AMD / ATI." -msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" -msgstr "Для лучшей совместимости с оборудованием Intel вы можете использовать либо все варианты с открытым исходным кодом, либо Intel.\n" +msgid "" +"For the best compatibility with your Intel hardware, you may want to use " +"either the all open-source or Intel options.\n" +msgstr "" +"Для лучшей совместимости с оборудованием Intel вы можете использовать либо " +"все варианты с открытым исходным кодом, либо Intel.\n" -msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" -msgstr "Для наилучшей совместимости с оборудованием Nvidia вы можете использовать проприетарный драйвер Nvidia.\n" +msgid "" +"For the best compatibility with your Nvidia hardware, you may want to use " +"the Nvidia proprietary driver.\n" +msgstr "" +"Для наилучшей совместимости с оборудованием Nvidia вы можете использовать " +"проприетарный драйвер Nvidia.\n" msgid "" "\n" @@ -209,13 +271,15 @@ msgid "" msgstr "" "\n" "\n" -"Выберите графический драйвер или оставьте пустым, чтобы установить все драйверы с открытым исходным кодом" +"Выберите графический драйвер или оставьте пустым, чтобы установить все " +"драйверы с открытым исходным кодом" msgid "All open-source (default)" msgstr "Все с открытым исходным кодом (по умолчанию)" msgid "Choose which kernels to use or leave blank for default \"{}\"" -msgstr "Выберите, какие ядра использовать, или оставьте пустым по умолчанию \"{}\"." +msgstr "" +"Выберите, какие ядра использовать, или оставьте пустым по умолчанию \"{}\"." msgid "Choose which locale language to use" msgstr "Выберите, какой язык локали использовать" @@ -232,8 +296,12 @@ msgstr "Выберите один или несколько из приведе msgid "Adding partition...." msgstr "Добавление раздела...." -msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." -msgstr "Чтобы продолжить, вам нужно ввести действительный fs-тип. Смотрите `man parted` для правильных fs-типов." +msgid "" +"You need to enter a valid fs-type in order to continue. See `man parted` for " +"valid fs-type's." +msgstr "" +"Чтобы продолжить, вам нужно ввести действительный fs-тип. Смотрите `man " +"parted` для правильных fs-типов." msgid "Error: Listing profiles on URL \"{}\" resulted in:" msgstr "Ошибка: Перечисление профилей по URL \"{}\" привело к:" @@ -305,7 +373,8 @@ msgid "" "Do you wish to continue?" msgstr "" "Вы решили пропустить выбор жесткого диска\n" -"и будете использовать любой диск, смонтированный по адресу {} (экспериментально)\n" +"и будете использовать любой диск, смонтированный по адресу {} " +"(экспериментально)\n" "ПРЕДУПРЕЖДЕНИЕ: Archinstall не будет проверять пригодность этой установки.\n" "Вы хотите продолжить?" @@ -325,13 +394,16 @@ msgid "Assign mount-point for a partition" msgstr "Назначить точку монтирования для раздела" msgid "Mark/Unmark a partition to be formatted (wipes data)" -msgstr "Пометить/снять отметку с раздела, который будет отформатирован (стирание данных)" +msgstr "" +"Пометить/снять отметку с раздела, который будет отформатирован (стирание " +"данных)" msgid "Mark/Unmark a partition as encrypted" msgstr "Пометить/снять отметку с раздела как зашифрованный" msgid "Mark/Unmark a partition as bootable (automatic for /boot)" -msgstr "Пометить/снять отметку с раздела как загрузочный (автоматически для /boot)" +msgstr "" +"Пометить/снять отметку с раздела как загрузочный (автоматически для /boot)" msgid "Set desired filesystem for a partition" msgstr "Установите желаемую файловую систему для раздела" @@ -371,7 +443,8 @@ msgid "Enter a encryption password for {}" msgstr "Введите пароль шифрования для {}" msgid "Enter disk encryption password (leave blank for no encryption): " -msgstr "Введите пароль шифрования диска (оставьте пустым для отсутствия шифрования): " +msgstr "" +"Введите пароль шифрования диска (оставьте пустым для отсутствия шифрования): " msgid "Create a required super-user with sudo privileges: " msgstr "Создайте необходимого суперпользователя с привилегиями sudo: " @@ -382,31 +455,43 @@ msgstr "Введите пароль root (оставьте пустым, что msgid "Password for user \"{}\": " msgstr "Пароль для пользователя \"{}\": " -msgid "Verifying that additional packages exist (this might take a few seconds)" -msgstr "Проверка наличия дополнительных пакетов (это может занять несколько секунд)" +msgid "" +"Verifying that additional packages exist (this might take a few seconds)" +msgstr "" +"Проверка наличия дополнительных пакетов (это может занять несколько секунд)" -msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" -msgstr "Вы хотите использовать автоматическую синхронизацию времени (NTP) с серверами времени по умолчанию?\n" +msgid "" +"Would you like to use automatic time synchronization (NTP) with the default " +"time servers?\n" +msgstr "" +"Вы хотите использовать автоматическую синхронизацию времени (NTP) с " +"серверами времени по умолчанию?\n" msgid "" -"Hardware time and other post-configuration steps might be required in order for NTP to work.\n" +"Hardware time and other post-configuration steps might be required in order " +"for NTP to work.\n" "For more information, please check the Arch wiki" msgstr "" -"Для работы NTP может потребоваться аппаратное время и другие шаги после конфигурации.\n" +"Для работы NTP может потребоваться аппаратное время и другие шаги после " +"конфигурации.\n" "Для получения дополнительной информации, пожалуйста, ознакомьтесь с ArchWiki" msgid "Enter a username to create an additional user (leave blank to skip): " -msgstr "Введите имя пользователя для создания дополнительного пользователя (оставьте пустым, чтобы пропустить): " +msgstr "" +"Введите имя пользователя для создания дополнительного пользователя (оставьте " +"пустым, чтобы пропустить): " msgid "Use ESC to skip\n" msgstr "Используйте ESC, чтобы пропустить\n" msgid "" "\n" -" Choose an object from the list, and select one of the available actions for it to execute" +" Choose an object from the list, and select one of the available actions for " +"it to execute" msgstr "" "\n" -" Выберите объект из списка и выберите одно из доступных действий для его выполнения" +" Выберите объект из списка и выберите одно из доступных действий для его " +"выполнения" msgid "Cancel" msgstr "Отменить" @@ -442,11 +527,17 @@ msgstr "" "\n" "Это выбранная вами конфигурация:" -msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." -msgstr "Pacman уже запущен, ожидание его завершения составляет максимум 10 минут." +msgid "" +"Pacman is already running, waiting maximum 10 minutes for it to terminate." +msgstr "" +"Pacman уже запущен, ожидание его завершения составляет максимум 10 минут." -msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." -msgstr "Существовавшая ранее блокировка pacman не завершилась. Пожалуйста, очистите все существующие сессии pacman перед использованием archinstall." +msgid "" +"Pre-existing pacman lock never exited. Please clean up any existing pacman " +"sessions before using archinstall." +msgstr "" +"Существовавшая ранее блокировка pacman не завершилась. Пожалуйста, очистите " +"все существующие сессии pacman перед использованием archinstall." msgid "Choose which optional additional repositories to enable" msgstr "Выберите, какие дополнительные репозитории следует включить" @@ -537,7 +628,8 @@ msgid "Missing configurations:\n" msgstr "Отсутствующие конфигурации:\n" msgid "Either root-password or at least 1 superuser must be specified" -msgstr "Должен быть указан либо пароль root, либо как минимум 1 суперпользователь" +msgstr "" +"Должен быть указан либо пароль root, либо как минимум 1 суперпользователь" msgid "Manage superuser accounts: " msgstr "Управление учетными записями суперпользователей: " @@ -597,8 +689,12 @@ msgstr "Хотите ли вы использовать сжатие BTRFS?" msgid "Would you like to create a separate partition for /home?" msgstr "Хотите ли вы создать отдельный раздел для /home?" -msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" -msgstr "Выбранные диски не имеют минимальной емкости, необходимой для автоматического предложения\n" +msgid "" +"The selected drives do not have the minimum capacity required for an " +"automatic suggestion\n" +msgstr "" +"Выбранные диски не имеют минимальной емкости, необходимой для " +"автоматического предложения\n" msgid "Minimum capacity for /home partition: {}GB\n" msgstr "Минимальный размер раздела /home: {}GB\n" @@ -625,7 +721,9 @@ msgid "No iface specified for manual configuration" msgstr "Не указан iface для ручной настройки" msgid "Manual nic configuration with no auto DHCP requires an IP address" -msgstr "Ручная конфигурация сетевого адаптера без автоматического DHCP требует IP-адреса" +msgstr "" +"Ручная конфигурация сетевого адаптера без автоматического DHCP требует IP-" +"адреса" msgid "Add interface" msgstr "Добавить интерфейс" @@ -645,44 +743,74 @@ msgstr "Ручная конфигурация" msgid "Mark/Unmark a partition as compressed (btrfs only)" msgstr "Пометить/снять отметку с раздела как сжатый (только для btrfs)" -msgid "The password you are using seems to be weak, are you sure you want to use it?" -msgstr "Пароль, который вы используете, кажется слабым, вы уверены, что хотите его использовать?" +msgid "" +"The password you are using seems to be weak, are you sure you want to use it?" +msgstr "" +"Пароль, который вы используете, кажется слабым, вы уверены, что хотите его " +"использовать?" -msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" -msgstr "Предоставляет выбор окружений рабочего стола и тайловых оконных менеджеров, например, gnome, kde, sway" +msgid "" +"Provides a selection of desktop environments and tiling window managers, e." +"g. gnome, kde, sway" +msgstr "" +"Предоставляет выбор окружений рабочего стола и тайловых оконных менеджеров, " +"например, gnome, kde, sway" msgid "Select your desired desktop environment" msgstr "Выберите желаемое окружение рабочего стола" -msgid "A very basic installation that allows you to customize Arch Linux as you see fit." -msgstr "Очень базовая установка, позволяющая настроить Arch Linux по своему усмотрению." +msgid "" +"A very basic installation that allows you to customize Arch Linux as you see " +"fit." +msgstr "" +"Очень базовая установка, позволяющая настроить Arch Linux по своему " +"усмотрению." -msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" -msgstr "Предоставляет выбор различных пакетов сервера для установки и включения, например, httpd, nginx, mariadb" +msgid "" +"Provides a selection of various server packages to install and enable, e.g. " +"httpd, nginx, mariadb" +msgstr "" +"Предоставляет выбор различных пакетов сервера для установки и включения, " +"например, httpd, nginx, mariadb" -msgid "Choose which servers to install, if none then a minimal installation will be done" -msgstr "Выберите серверы для установки, если их нет, то будет выполнена минимальная установка" +msgid "" +"Choose which servers to install, if none then a minimal installation will be " +"done" +msgstr "" +"Выберите серверы для установки, если их нет, то будет выполнена минимальная " +"установка" msgid "Installs a minimal system as well as xorg and graphics drivers." -msgstr "Устанавливает минимальную систему, а также xorg и графические драйверы." +msgstr "" +"Устанавливает минимальную систему, а также xorg и графические драйверы." msgid "Press Enter to continue." msgstr "Нажмите Enter, чтобы продолжить." -msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?" -msgstr "Хотите ли вы использовать chroot в новой созданной установке и выполнить настройку после установки?" +msgid "" +"Would you like to chroot into the newly created installation and perform " +"post-installation configuration?" +msgstr "" +"Хотите ли вы использовать chroot в новой созданной установке и выполнить " +"настройку после установки?" msgid "Are you sure you want to reset this setting?" msgstr "Вы уверены, что хотите сбросить эту настройку?" msgid "Select one or more hard drives to use and configure\n" -msgstr "Выберите один или несколько жестких дисков для использования и настройки\n" +msgstr "" +"Выберите один или несколько жестких дисков для использования и настройки\n" msgid "Any modifications to the existing setting will reset the disk layout!" -msgstr "Любые изменения существующей настройки приведут к сбросу разметки диска!" +msgstr "" +"Любые изменения существующей настройки приведут к сбросу разметки диска!" -msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" -msgstr "Если вы сбросите выбор жесткого диска, это также сбросит текущую разметку диска. Вы уверены?" +msgid "" +"If you reset the harddrive selection this will also reset the current disk " +"layout. Are you sure?" +msgstr "" +"Если вы сбросите выбор жесткого диска, это также сбросит текущую разметку " +"диска. Вы уверены?" msgid "Save and exit" msgstr "Сохранить и выйти" @@ -728,8 +856,12 @@ msgstr "Добавить: " msgid "Value: " msgstr "Значение: " -msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" -msgstr "Вы можете не выбирать диск и разметку и использовать любой диск, смонтированный в /mnt (экспериментально)" +msgid "" +"You can skip selecting a drive and partitioning and use whatever drive-setup " +"is mounted at /mnt (experimental)" +msgstr "" +"Вы можете не выбирать диск и разметку и использовать любой диск, " +"смонтированный в /mnt (экспериментально)" msgid "Select one of the disks or skip and use /mnt as default" msgstr "Выберите один из дисков или пропустите и используйте /mnt по умолчанию" @@ -752,8 +884,12 @@ msgstr "Свободное место" msgid "Bus-type" msgstr "Тип шины" -msgid "Either root-password or at least 1 user with sudo privileges must be specified" -msgstr "Должен быть указан либо пароль root, либо хотя бы 1 пользователь с привилегиями sudo" +msgid "" +"Either root-password or at least 1 user with sudo privileges must be " +"specified" +msgstr "" +"Должен быть указан либо пароль root, либо хотя бы 1 пользователь с " +"привилегиями sudo" msgid "Enter username (leave blank to skip): " msgstr "Введите имя пользователя (оставьте пустым, чтобы пропустить): " @@ -791,8 +927,12 @@ msgstr "Удалить подтом" msgid "Configured {} interfaces" msgstr "Настроено интерфейсов: {}" -msgid "This option enables the number of parallel downloads that can occur during installation" -msgstr "Этот параметр определяет количество параллельных загрузок, которые могут происходить во время установки" +msgid "" +"This option enables the number of parallel downloads that can occur during " +"installation" +msgstr "" +"Этот параметр определяет количество параллельных загрузок, которые могут " +"происходить во время установки" msgid "" "Enter the number of parallel downloads to be enabled.\n" @@ -803,18 +943,34 @@ msgstr "" " (Введите значение от 1 до {})\n" "Примечание:" -msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" -msgstr " - Максимальное значение: {} ( Позволяет {} параллельных загрузок, позволяет {} загрузок одновременно )" +msgid "" +" - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads " +"at a time )" +msgstr "" +" - Минимальное значение: {} ( Позволяет {} параллельную загрузку, позволяет " +"{} загрузки одновременно )" -msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" -msgstr " - Минимальное значение: 1 ( Позволяет 1 параллельную загрузку, позволяет 2 загрузки одновременно )" +msgid "" +" - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a " +"time )" +msgstr "" +" - Минимальное значение: 1 ( Позволяет 1 параллельную загрузку, позволяет 2 " +"загрузки одновременно )" -msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" -msgstr " - Отключить/по умолчанию: 0 ( Отключает параллельную загрузку, позволяет только 1 загрузку за один раз )" +msgid "" +" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 " +"download at a time )" +msgstr "" +" - Отключить/по умолчанию: 0 ( Отключает параллельную загрузку, позволяет " +"только 1 загрузку за один раз )" -#, fuzzy, python-brace-format -msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "Неверный ввод! Повторите попытку с правильным вводом [1 - {}, или 0 - отключить]" +#, python-brace-format +msgid "" +"Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to " +"disable]" +msgstr "" +"Неверный ввод! Повторите попытку с правильным вводом [1 - {max_downloads}, " +"или 0 - отключить]" msgid "Parallel Downloads" msgstr "Параллельные загрузки" @@ -831,351 +987,367 @@ msgstr "TAB, чтобы выбрать" msgid "[Default value: 0] > " msgstr "[Значение по умолчанию: 0] > " -msgid "To be able to use this translation, please install a font manually that supports the language." -msgstr "Чтобы иметь возможность использовать этот перевод, пожалуйста, установите вручную шрифт, поддерживающий данный язык." +msgid "" +"To be able to use this translation, please install a font manually that " +"supports the language." +msgstr "" +"Чтобы иметь возможность использовать этот перевод, пожалуйста, установите " +"вручную шрифт, поддерживающий данный язык." msgid "The font should be stored as {}" msgstr "Шрифт должен быть сохранен как {}" msgid "Archinstall requires root privileges to run. See --help for more." -msgstr "Для запуска Archinstall требуются привилегии root. Для получения дополнительной информации смотрите --help." +msgstr "" +"Для запуска Archinstall требуются привилегии root. Для получения " +"дополнительной информации смотрите --help." -#, fuzzy msgid "Select an execution mode" -msgstr "Выберите действие для '{}'" +msgstr "Выберите режим выполнения" msgid "Unable to fetch profile from specified url: {}" -msgstr "" +msgstr "Невозможно получить профиль из указанного url: {}" -msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgid "" +"Profiles must have unique name, but profile definitions with duplicate name " +"found: {}" msgstr "" +"Профили должны иметь уникальное имя, но найдены определения профиля с " +"дублирующимся именем: {}" -#, fuzzy msgid "Select one or more devices to use and configure" -msgstr "Выберите один или несколько жестких дисков для использования и настройте их" +msgstr "Выберите одно или несколько устройств для использования и настройки" -#, fuzzy -msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" -msgstr "Если вы сбросите выбор жесткого диска, это также сбросит текущую разметку диска. Вы уверены?" +msgid "" +"If you reset the device selection this will also reset the current disk " +"layout. Are you sure?" +msgstr "" +"Если вы сбросите выбор устройства, это также сбросит текущую разметку " +"дисков. Вы уверены?" -#, fuzzy msgid "Existing Partitions" -msgstr "Добавление раздела...." +msgstr "Существующие разделы" -#, fuzzy msgid "Select a partitioning option" -msgstr "Удалить раздел" +msgstr "Выберите вариант разбивки на разделы" -#, fuzzy msgid "Enter the root directory of the mounted devices: " -msgstr "Введите каталог для сохранения конфигурации (-ций): " +msgstr "Введите корневой каталог смонтированных устройств: " -#, fuzzy msgid "Minimum capacity for /home partition: {}GiB\n" -msgstr "Минимальный размер раздела /home: {}GB\n" +msgstr "Минимальный размер раздела /home: {}GiB\n" -#, fuzzy msgid "Minimum capacity for Arch Linux partition: {}GiB" -msgstr "Минимальный размер раздела Arch Linux: {}GB" +msgstr "Минимальный размер раздела Arch Linux: {}GiB" -#, fuzzy -msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" -msgstr "Это список предварительно запрограммированных профилей, они могут облегчить установку таких вещей, как окружения рабочего стола" +msgid "" +"This is a list of pre-programmed profiles_bck, they might make it easier to " +"install things like desktop environments" +msgstr "" +"Это список предварительно запрограммированных профилей, они могут облегчить " +"установку таких вещей, как окружения рабочего стола" -#, fuzzy msgid "Current profile selection" -msgstr "Текущая разметка разделов" +msgstr "Текущий выбор профиля" -#, fuzzy msgid "Remove all newly added partitions" -msgstr "Создать новый раздел" +msgstr "Удалить все вновь добавленные разделы" -#, fuzzy msgid "Assign mountpoint" -msgstr "Назначить точку монтирования для раздела" +msgstr "Назначить точку монтирования" -#, fuzzy msgid "Mark/Unmark to be formatted (wipes data)" -msgstr "Пометить/снять отметку с раздела, который будет отформатирован (стирание данных)" +msgstr "Пометить/снять отметку для форматирования (стирание данных)" msgid "Mark/Unmark as bootable" -msgstr "" +msgstr "Пометить/снять пометку как загрузочный" msgid "Change filesystem" -msgstr "" +msgstr "Изменить файловую систему" -#, fuzzy msgid "Mark/Unmark as compressed" -msgstr "Пометить/снять отметку с раздела как сжатый (только для btrfs)" +msgstr "Пометить/снять отметку как сжатый" -#, fuzzy msgid "Set subvolumes" -msgstr "Удалить подтом" +msgstr "Установить подтома" -#, fuzzy msgid "Delete partition" msgstr "Удалить раздел" msgid "Partition" -msgstr "" +msgstr "Раздел" -msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgid "" +"This partition is currently encrypted, to format it a filesystem has to be " +"specified" msgstr "" +"Этот раздел в настоящее время зашифрован, для его форматирования необходимо " +"указать файловую систему" -#, fuzzy -msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * Точки монтирования разделов являются относительными внутри установки, например, загрузочный будет /boot." +msgid "" +"Partition mount-points are relative to inside the installation, the boot " +"would be /boot as an example." +msgstr "" +"Точки монтирования разделов являются относительными внутри установки, " +"например, загрузочный раздел будет /boot." -msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgid "" +"If mountpoint /boot is set, then the partition will also be marked as " +"bootable." msgstr "" +"Если установлена точка монтирования /boot, то раздел также будет помечен как " +"загрузочный." msgid "Mountpoint: " -msgstr "" +msgstr "Точка монтирования: " msgid "Current free sectors on device {}:" -msgstr "" +msgstr "Текущие свободные секторы на устройстве {}:" -#, fuzzy msgid "Total sectors: {}" -msgstr "Недействительный каталог: {}" +msgstr "Всего секторов: {}" -#, fuzzy msgid "Enter the start sector (default: {}): " -msgstr "Введите начальный сектор (процент или номер блока, по умолчанию: {}): " +msgstr "Введите начальный сектор (по умолчанию: {}): " -#, fuzzy -msgid "Enter the end sector of the partition (percentage or block number, default: {}): " -msgstr "Введите конечный сектор раздела (процент или номер блока, например: {}): " +msgid "" +"Enter the end sector of the partition (percentage or block number, default: " +"{}): " +msgstr "" +"Введите конечный сектор раздела (процент или номер блока, по умолчанию: {}): " msgid "This will remove all newly added partitions, continue?" -msgstr "" +msgstr "Это приведет к удалению всех вновь добавленных разделов, продолжить?" msgid "Partition management: {}" -msgstr "" +msgstr "Управление разделом: {}" msgid "Total length: {}" -msgstr "" +msgstr "Весь размер: {}" -#, fuzzy msgid "Encryption type" -msgstr "Пароль шифрования" +msgstr "Тип шифрования" msgid "Partitions" -msgstr "" +msgstr "Разделы" msgid "No HSM devices available" -msgstr "" +msgstr "Нет доступных устройств HSM" -#, fuzzy msgid "Partitions to be encrypted" -msgstr "Выберите разделы для шифрования" +msgstr "Разделы, подлежащие шифрованию" msgid "Select disk encryption option" -msgstr "" +msgstr "Выбрать вариант шифрования диска" msgid "Select a FIDO2 device to use for HSM" -msgstr "" +msgstr "Выберите устройство FIDO2 для использования в качестве HSM" -#, fuzzy msgid "Use a best-effort default partition layout" -msgstr "Стереть все выбранные диски и использовать оптимальную схему разделов по умолчанию" +msgstr "Использовать оптимальную схему разделов по умолчанию" -#, fuzzy msgid "Manual Partitioning" -msgstr "Ручная конфигурация" +msgstr "Ручное разбиение на разделы" -#, fuzzy msgid "Pre-mounted configuration" -msgstr "Отсутствует конфигурация" +msgstr "Предварительно смонтированная конфигурация" msgid "Unknown" -msgstr "" +msgstr "Неизвестно" msgid "Partition encryption" -msgstr "" +msgstr "Шифрование раздела" msgid " ! Formatting {} in " -msgstr "" +msgstr " ! Форматирование {} в " msgid "← Back" -msgstr "" +msgstr "← Назад" msgid "Disk encryption" -msgstr "" +msgstr "Шифрование диска" -#, fuzzy msgid "Configuration" -msgstr "Отсутствует конфигурация" +msgstr "Конфигурация" -#, fuzzy msgid "Password" -msgstr "Пароль root" +msgstr "Пароль" -#, fuzzy msgid "All settings will be reset, are you sure?" -msgstr "{} содержит разделы в очереди, это удалит их, вы уверены?" +msgstr "Все настройки будут сброшены, вы уверены?" msgid "Back" -msgstr "" +msgstr "Назад" msgid "Please chose which greeter to install for the chosen profiles: {}" msgstr "" +"Пожалуйста, выберите, какой экран приветствия установить для выбранных " +"профилей: {}" msgid "Environment type: {}" -msgstr "" +msgstr "Тип окружения рабочего стола: {}" -msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgid "" +"The proprietary Nvidia driver is not supported by Sway. It is likely that " +"you will run into issues, are you okay with that?" msgstr "" +"Проприетарный драйвер Nvidia не поддерживается Sway. Вполне вероятно, что вы " +"столкнетесь с проблемами, вы согласны с этим?" -#, fuzzy msgid "Installed packages" -msgstr "Дополнительные пакеты" +msgstr "Устанавливаемые пакеты" -#, fuzzy msgid "Add profile" -msgstr "Профиль" +msgstr "Добавить профиль" -#, fuzzy msgid "Edit profile" -msgstr "Профиль" +msgstr "Изменить профиль" -#, fuzzy msgid "Delete profile" -msgstr "Удалить интерфейс" +msgstr "Удалить профиль" -#, fuzzy msgid "Profile name: " -msgstr "Профиль" +msgstr "Имя профиля: " -#, fuzzy msgid "The profile name you entered is already in use. Try again" -msgstr "Введенное вами имя пользователя недействительно. Попробуйте еще раз" +msgstr "Введенное вами имя профиля уже используется. Попробуйте еще раз" -#, fuzzy -msgid "Packages to be install with this profile (space separated, leave blank to skip): " -msgstr "Напишите дополнительные пакеты для установки (разделите пробелами, оставьте пустым, чтобы пропустить): " +msgid "" +"Packages to be install with this profile (space separated, leave blank to " +"skip): " +msgstr "" +"Пакеты, которые будут установлены с этим профилем (разделенные пробелами, " +"оставьте пустым, чтобы пропустить): " -#, fuzzy -msgid "Services to be enabled with this profile (space separated, leave blank to skip): " -msgstr "Напишите дополнительные пакеты для установки (разделите пробелами, оставьте пустым, чтобы пропустить): " +msgid "" +"Services to be enabled with this profile (space separated, leave blank to " +"skip): " +msgstr "" +"Службы, которые должны быть включены с помощью этого профиля (разделенные " +"пробелами, оставьте пустым, чтобы пропустить): " msgid "Should this profile be enabled for installation?" -msgstr "" +msgstr "Должен ли этот профиль быть включен для установки?" msgid "Create your own" -msgstr "" +msgstr "Создать свой собственный" -#, fuzzy msgid "" "\n" "Select a graphics driver or leave blank to install all open-source drivers" msgstr "" "\n" -"\n" -"Выберите графический драйвер или оставьте пустым, чтобы установить все драйверы с открытым исходным кодом" +"Выберите графический драйвер или оставьте пустым, чтобы установить все " +"драйверы с открытым исходным кодом" -msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgid "" +"Sway needs access to your seat (collection of hardware devices i.e. " +"keyboard, mouse, etc)" msgstr "" +"Sway необходим доступ к вашему компьютеру (набор аппаратных устройств, т.е. " +"клавиатура, мышь и т.д.)" msgid "" "\n" "\n" "Choose an option to give Sway access to your hardware" msgstr "" +"\n" +"\n" +"Выберите опцию, чтобы предоставить Sway доступ к вашему оборудованию" msgid "Graphics driver" -msgstr "" +msgstr "Графический драйвер" msgid "Greeter" -msgstr "" +msgstr "Экран приветствия" msgid "Please chose which greeter to install" -msgstr "" +msgstr "Пожалуйста, выберите, какой экран приветствия установить" msgid "This is a list of pre-programmed default_profiles" -msgstr "" +msgstr "Это список запрограммированных по умолчанию профилей" -#, fuzzy msgid "Disk configuration" -msgstr "Отсутствует конфигурация" +msgstr "Конфигурация диска" -#, fuzzy msgid "Profiles" -msgstr "Профиль" +msgstr "Профили" msgid "Finding possible directories to save configuration files ..." -msgstr "" +msgstr "Поиск возможных каталогов для сохранения файлов конфигурации ..." -#, fuzzy msgid "Select directory (or directories) for saving configuration files" -msgstr "Выберите один или несколько жестких дисков для использования и настройте их" +msgstr "Выберите каталог (или каталоги) для сохранения файлов конфигурации" -#, fuzzy msgid "Add a custom mirror" -msgstr "Добавить пользователя" +msgstr "Добавить пользовательское зеркало" msgid "Change custom mirror" -msgstr "" +msgstr "Изменить пользовательское зеркало" msgid "Delete custom mirror" -msgstr "" +msgstr "Удалить пользовательское зеркало" -#, fuzzy msgid "Enter name (leave blank to skip): " -msgstr "Введите имя пользователя (оставьте пустым, чтобы пропустить): " +msgstr "Введите имя (оставьте пустым, чтобы пропустить): " -#, fuzzy msgid "Enter url (leave blank to skip): " -msgstr "Введите имя пользователя (оставьте пустым, чтобы пропустить): " +msgstr "Введите адрес (оставьте пустым, чтобы пропустить): " -#, fuzzy msgid "Select signature check option" -msgstr "Выберите интерфейс для добавления" +msgstr "Выбрать вариант проверки подписи" -#, fuzzy msgid "Select signature option" -msgstr "Выберите интерфейс для добавления" +msgstr "Выбрать вариант подписи" msgid "Custom mirrors" -msgstr "" +msgstr "Пользовательские зеркала" msgid "Defined" -msgstr "" +msgstr "Определено" -#, fuzzy msgid "Save user configuration (including disk layout)" -msgstr "Сохранить конфигурацию пользователя" +msgstr "Сохранить конфигурацию пользователя (включая разметку диска)" -#, fuzzy msgid "" -"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Enter a directory for the configuration(s) to be saved (tab completion " +"enabled)\n" "Save directory: " -msgstr "Введите каталог для сохранения конфигурации (-ций): " +msgstr "" +"Введите каталог для сохранения конфигурации (-ций) (включено заполнение " +"вкладок)\n" +"Каталог сохранения: " msgid "" "Do you want to save {} configuration file(s) in the following location?\n" "\n" "{}" msgstr "" +"Хотите ли вы сохранить {} файл(-а) конфигурации в следующем месте?\n" +"\n" +"{}" -#, fuzzy msgid "Saving {} configuration files to {}" -msgstr "Сохранить конфигурацию" +msgstr "Сохранение {} файла(-ов) конфигурации в {}" -#, fuzzy msgid "Mirrors" -msgstr "Регион зеркала" +msgstr "Зеркала" -#, fuzzy msgid "Mirror regions" -msgstr "Регион зеркала" +msgstr "Регионы зеркала" -#, fuzzy -msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - Максимальное значение: {} ( Позволяет {} параллельных загрузок, позволяет {} загрузок одновременно )" +msgid "" +" - Maximum value : {} ( Allows {} parallel downloads, allows " +"{max_downloads+1} downloads at a time )" +msgstr "" +" - Максимальное значение: {} ( Позволяет {} параллельных загрузок, позволяет " +"{max_downloads+1} загрузок одновременно )" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" -msgstr "Неверный ввод! Повторите попытку с правильным вводом [1 - {}, или 0 - отключить]" +msgstr "" +"Неверный ввод! Повторите попытку с правильным вводом [1 - {}, или 0 - " +"отключить]" #, python-brace-format #~ msgid "Edit {origkey} :" -- cgit v1.2.3-70-g09d2 From de9b8d4f0e5852d7b4cd4d06e975a2e466fc00f4 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Thu, 8 Jun 2023 04:48:56 -0400 Subject: Fix paths used for steps in keyfile creation (#1851) * Fix paths used for steps in keyfile creation * Simplify --- archinstall/lib/luks.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/archinstall/lib/luks.py b/archinstall/lib/luks.py index f9b09b53..84b0e6fd 100644 --- a/archinstall/lib/luks.py +++ b/archinstall/lib/luks.py @@ -187,8 +187,8 @@ class Luks2: # Once we store the key as ../xyzloop.key systemd-cryptsetup can # automatically load this key if we name the device to "xyzloop" - key_file_path = target_path / 'etc/cryptsetup-keys.d/' / self.mapper_name - key_file = key_file_path / '.key' + kf_path = Path(f'/etc/cryptsetup-keys.d/{self.mapper_name}.key') + key_file = target_path / kf_path.relative_to(kf_path.root) crypttab_path = target_path / 'etc/crypttab' if key_file.exists(): @@ -198,15 +198,15 @@ class Luks2: else: info(f'Key file {key_file} already exists, overriding') - key_file_path.mkdir(parents=True, exist_ok=True) + key_file.parent.mkdir(parents=True, exist_ok=True) with open(key_file, "w") as keyfile: keyfile.write(generate_password(length=512)) - key_file_path.chmod(0o400) + key_file.chmod(0o400) self._add_key(key_file) - self._crypttab(crypttab_path, key_file, options=["luks", "key-slot=1"]) + self._crypttab(crypttab_path, kf_path, options=["luks", "key-slot=1"]) def _add_key(self, key_file: Path): info(f'Adding additional key-file {key_file}') -- cgit v1.2.3-70-g09d2 From 701d1e4ec38845fce7b3c687aed230b016d6b989 Mon Sep 17 00:00:00 2001 From: Himadri Bhattacharjee <107522312+lavafroth@users.noreply.github.com> Date: Thu, 8 Jun 2023 08:54:52 +0000 Subject: Installs correct dkms package for nvidia and nvidia-open (#1847) --- archinstall/lib/profile/profiles_handler.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/archinstall/lib/profile/profiles_handler.py b/archinstall/lib/profile/profiles_handler.py index 16fef251..2cc15d8e 100644 --- a/archinstall/lib/profile/profiles_handler.py +++ b/archinstall/lib/profile/profiles_handler.py @@ -194,14 +194,18 @@ class ProfileHandler: additional_pkg = ' '.join(['xorg-server', 'xorg-xinit'] + driver_pkgs) if driver is not None: - if 'nvidia' in driver: - if "linux-zen" in install_session.base_packages or "linux-lts" in install_session.base_packages: + # Find the intersection between the set of known nvidia drivers + # and the selected driver packages. Since valid intesections can + # only have one element or none, we iterate and try to take the + # first element. + if driver_pkg := next(iter({'nvidia','nvidia-open'} & set(driver_pkgs)), None): + if any(kernel in install_session.base_packages for kernel in ("linux-lts", "linux-zen")): for kernel in install_session.kernels: # Fixes https://github.com/archlinux/archinstall/issues/585 install_session.add_additional_packages(f"{kernel}-headers") # I've had kernel regen fail if it wasn't installed before nvidia-dkms - install_session.add_additional_packages(['dkms', 'xorg-server', 'xorg-xinit', 'nvidia-dkms']) + install_session.add_additional_packages(['dkms', 'xorg-server', 'xorg-xinit', f'{driver_pkg}-dkms']) return elif 'amdgpu' in driver_pkgs: # The order of these two are important if amdgpu is installed #808 -- cgit v1.2.3-70-g09d2 From 5087c86263c39bb13585c326c14f1f890900f42f Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Thu, 8 Jun 2023 19:00:21 +1000 Subject: Fix 1830 (#1831) Co-authored-by: Daniel Girtler --- archinstall/lib/output.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py index 0395e2e7..b406d624 100644 --- a/archinstall/lib/output.py +++ b/archinstall/lib/output.py @@ -137,26 +137,28 @@ class Journald: def check_log_permissions(): filename = storage.get('LOG_FILE', None) + log_dir = storage.get('LOG_PATH', Path('./')) if not filename: - return + raise ValueError('No log file name defined') - log_dir = storage.get('LOG_PATH', Path('./')) - absolute_logfile = log_dir / filename + log_file = log_dir / filename try: log_dir.mkdir(exist_ok=True, parents=True) - with absolute_logfile.open('a') as fp: + log_file.touch(exist_ok=True) + + with log_file.open('a') as fp: fp.write('') except PermissionError: # Fallback to creating the log file in the current folder - fallback_log_file = Path('./').absolute() / filename - absolute_logfile = fallback_log_file - absolute_logfile.mkdir(exist_ok=True, parents=True) - storage['LOG_PATH'] = Path('./').absolute() + fallback_dir = Path('./').absolute() + fallback_log_file = fallback_dir / filename - err_string = f"Not enough permission to place log file at {absolute_logfile}, creating it in {fallback_log_file} instead." - warn(err_string) + fallback_log_file.touch(exist_ok=True) + + storage['LOG_PATH'] = fallback_dir + warn(f'Not enough permission to place log file at {log_file}, creating it in {fallback_log_file} instead') def _supports_color() -> bool: @@ -248,6 +250,7 @@ def info( ): log(*msgs, level=level, fg=fg, bg=bg, reset=reset, font=font) + def debug( *msgs: str, level: int = logging.DEBUG, @@ -258,6 +261,7 @@ def debug( ): log(*msgs, level=level, fg=fg, bg=bg, reset=reset, font=font) + def error( *msgs: str, level: int = logging.ERROR, @@ -295,14 +299,10 @@ def log( if _supports_color(): text = _stylize_output(text, fg, bg, reset, font) - # If a logfile is defined in storage, - # we use that one to output everything - if filename := storage.get('LOG_FILE', None): - log_dir = storage.get('LOG_PATH', Path('./')) - absolute_logfile = log_dir / filename + log_file: Path = storage['LOG_PATH'] / storage['LOG_FILE'] - with open(absolute_logfile, 'a') as fp: - fp.write(f"{orig_string}\n") + with log_file.open('a') as fp: + fp.write(f"{orig_string}\n") Journald.log(text, level=level) -- cgit v1.2.3-70-g09d2 From 195d779d851106f1f8be2de76cfa73e5d130b441 Mon Sep 17 00:00:00 2001 From: Tuğşat Enes Date: Mon, 12 Jun 2023 10:33:27 +0300 Subject: Reverted changes to other languages (#1857) Co-authored-by: Anton Hvornum --- archinstall/locales/tr/LC_MESSAGES/base.po | 122 +++++++++++++++-------------- 1 file changed, 62 insertions(+), 60 deletions(-) diff --git a/archinstall/locales/tr/LC_MESSAGES/base.po b/archinstall/locales/tr/LC_MESSAGES/base.po index 2bbcae95..435f80a5 100644 --- a/archinstall/locales/tr/LC_MESSAGES/base.po +++ b/archinstall/locales/tr/LC_MESSAGES/base.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: 21.05.2022\n" "PO-Revision-Date: 2022-05-25 17:42+0300\n" -"Last-Translator: Abdullah Koyuncu @wiseweb-works \n" +"Last-Translator: Tuğşat Enes @tugsatenes \n" "Language-Team: \n" "Language: tr\n" "MIME-Version: 1.0\n" @@ -61,7 +61,7 @@ msgid "Write additional packages to install (space separated, leave blank to ski msgstr "Kurulacak ek paketleri yazınız (boşlukla ayrılmış, geçmek için boş bırakın): " msgid "Copy ISO network configuration to installation" -msgstr "Kuruluma ISO'dan ağ yapılandırmasını kopyala" +msgstr "ISO Ağ yapılandırmasını kuruluma kopyala" msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "NetworkManager'ı kullan (GNOME ya da KDE'de interneti grafik olarak yapılandırmak için gerekli)" @@ -98,10 +98,10 @@ msgid "Enter a desired filesystem type for the partition" msgstr "Disk bölümü için arzu edilen bir dosya systemi tipi girin" msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " -msgstr "" +msgstr "Başlangıç lokasyonunu girin (parted birimlerinde: s, GB, %, vb. ; default: {}): " msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " -msgstr "" +msgstr "Bitiş lokasyonunu girin (parted birimlerinde: s, GB, %, vb. ; ex: {}): " msgid "{} contains queued partitions, this will remove those, are you sure?" msgstr "{} işlem sırasında bekleyen disk bölümleri bulunduruyor, bu onları kaldıracak, emin misiniz?" @@ -155,7 +155,7 @@ msgid "" msgstr "" "{}\n" "\n" -"Hangi disk bölümünün boot edilebilir olarak işaretleneceğini seçin" +"Hangi disk bölümünün önyüklenebilir olarak işaretleneceğini seçin" msgid "" "{}\n" @@ -264,8 +264,9 @@ msgstr "Disk(ler)" msgid "Disk layout" msgstr "Disk şeması" +# Şifreleme şifresi bana bir tık garip geldi, o yüzden parolayla değiştirdim msgid "Encryption password" -msgstr "Şifreleme şifresi" +msgstr "Şifreleme parolası" msgid "Swap" msgstr "Swap (değişim)" @@ -336,7 +337,7 @@ msgid "Mark/Unmark a partition as encrypted" msgstr "Bir disk bölümünü şifrelenmiş olarak işaretle/işareti kaldır" msgid "Mark/Unmark a partition as bootable (automatic for /boot)" -msgstr "Bir disk bölümünü boot edilebilir olarak işaretle/işareti kaldır (/boot için otomatik)" +msgstr "Bir disk bölümünü önyüklenebilir olarak işaretle/işareti kaldır (/boot için otomatik)" msgid "Set desired filesystem for a partition" msgstr "Bir disk bölümü için arzu edilen dosya sistemini ayarla" @@ -370,11 +371,12 @@ msgstr "Disk bölümü şeması öner" msgid "Enter a password: " msgstr "Bir şifre girin: " +# şifreleme şifresi tam olmuyor sanki, değiştirdim msgid "Enter a encryption password for {}" -msgstr "{} için bir şifreleme şifresi girin" +msgstr "{} için bir şifreleme parolası girin" msgid "Enter disk encryption password (leave blank for no encryption): " -msgstr "Disk şifreleme şifresi girin (şifreleme olmaması için boş bırakın): " +msgstr "Disk şifreleme parolası girin (şifreleme olmaması için boş bırakın): " msgid "Create a required super-user with sudo privileges: " msgstr "Gerekli bir sudo yetkilerine sahip süper-kullanıcı oluşturun: " @@ -571,7 +573,7 @@ msgid "Subvolume mountpoint" msgstr "Alt disk bölümü mount (monte) noktası" msgid "Subvolume options" -msgstr "Alt dizin bölümü seçenekleri" +msgstr "Alt disk bölümü seçenekleri" msgid "Save" msgstr "Kaydet" @@ -583,7 +585,7 @@ msgid "Select a mount point :" msgstr "Bir mount (monte) noktası seçin :" msgid "Select the desired subvolume options " -msgstr "İstenilen alt dizin bölümü ayarlarını seçin " +msgstr "İstenilen alt disk bölümü ayarlarını seçin " msgid "Define users with sudo privilege, by username: " msgstr "Sudo yetkilerine sahip kullanıcıları tanımlayın, kullanıcı adı ile: " @@ -765,7 +767,7 @@ msgid "Enter username (leave blank to skip): " msgstr "Ek kullanıcı oluşturmak için bir kullanıcı adı girin (geçmek için boş bırakın): " msgid "The username you entered is invalid. Try again" -msgstr "" +msgstr "Girdiğiniz kullanıcı adı geçersiz. Tekrar deneyin" #, fuzzy msgid "Should \"{}\" be a superuser (sudo)?" @@ -776,33 +778,33 @@ msgid "Select which partitions to encrypt" msgstr "Hangi disk bölümünün şifrelenmiş olarak işaretleneceğini seçin" msgid "very weak" -msgstr "" +msgstr "çok zayıf" msgid "weak" -msgstr "" +msgstr "zayıf" msgid "moderate" -msgstr "" +msgstr "ortalama" msgid "strong" -msgstr "" +msgstr "güçlü" #, fuzzy msgid "Add subvolume" -msgstr " alt disk bölümü :{:16}" +msgstr "alt disk bölümü ekle :{:16}" msgid "Edit subvolume" -msgstr "" +msgstr "Alt disk bölümünü düzenle" #, fuzzy msgid "Delete subvolume" -msgstr "Kullanıcı Sil" +msgstr "Alt disk bölümü sil" msgid "Configured {} interfaces" -msgstr "" +msgstr "Yapılandırılmış {} arayüzler" msgid "This option enables the number of parallel downloads that can occur during installation" -msgstr "" +msgstr "Bu seçenek, kurulum sırasında gerçekleşebilecek paralel indirme sayısını etkinleştirir" msgid "" "Enter the number of parallel downloads to be enabled.\n" @@ -824,29 +826,29 @@ msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 t msgstr "" msgid "Parallel Downloads" -msgstr "" +msgstr "Paralel İndirmeler" #, fuzzy msgid "ESC to skip" -msgstr "Geçmek için ESC'yi kullanın" +msgstr "Geçmek için ESC'ye basın" msgid "CTRL+C to reset" -msgstr "" +msgstr "Resetlemek için CTRL+C'ye basın" msgid "TAB to select" -msgstr "" +msgstr "Seçmek için TAB'a basın" msgid "[Default value: 0] > " msgstr "" msgid "To be able to use this translation, please install a font manually that supports the language." -msgstr "" +msgstr "Bu çeviriyi kullanabilmek için lütfen bu dili destekleyen bir yazı tipini manuel olarak yükleyin." msgid "The font should be stored as {}" -msgstr "" +msgstr "Yazı fontu {} olarak depolanmalıdır" msgid "Archinstall requires root privileges to run. See --help for more." -msgstr "" +msgstr "Archinstall çalışmak için root (kök) ayrıcalıkları gerektirir. Daha fazlası için --help'e bakın." #, fuzzy msgid "Select an execution mode" @@ -896,7 +898,7 @@ msgstr "Mevcut disk bölümü düzeni" #, fuzzy msgid "Remove all newly added partitions" -msgstr "Yeni disk bölümü oluştur" +msgstr "Yeni eklenen tüm disk bölümleri kaldırın" #, fuzzy msgid "Assign mountpoint" @@ -907,10 +909,10 @@ msgid "Mark/Unmark to be formatted (wipes data)" msgstr "Bir disk bölümünü biçimlendirilmek üzere işaretle/işareti kaldır (veriyi temizler)" msgid "Mark/Unmark as bootable" -msgstr "" +msgstr "Önyüklenebilir olarak işaretle/işareti kaldır" msgid "Change filesystem" -msgstr "" +msgstr "Dosya sistemini değiştir" #, fuzzy msgid "Mark/Unmark as compressed" @@ -918,17 +920,17 @@ msgstr "Bir disk bölümünü sıkıştırılmış olarak işaretle/işareti kal #, fuzzy msgid "Set subvolumes" -msgstr "Kullanıcı Sil" +msgstr "Alt disk bölümü ayarla" #, fuzzy msgid "Delete partition" msgstr "Disk bölümü sil" msgid "Partition" -msgstr "" +msgstr "Disk bölümü" msgid "This partition is currently encrypted, to format it a filesystem has to be specified" -msgstr "" +msgstr "Bu disk bölümü şu anda şifrelenmiştir, biçimlendirmek için bir dosya sistemi belirtilmelidir" #, fuzzy msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." @@ -956,7 +958,7 @@ msgid "Enter the end sector of the partition (percentage or block number, defaul msgstr "Disk bölümünün bitiş kesimini girin (yüzde ya da blok numarası, ör: {}): " msgid "This will remove all newly added partitions, continue?" -msgstr "" +msgstr "Bu yeni eklenen tüm disk bölümlerini kaldıracaktır, devam ediyor musunuz?" msgid "Partition management: {}" msgstr "" @@ -966,10 +968,10 @@ msgstr "" #, fuzzy msgid "Encryption type" -msgstr "Şifreleme şifresi" +msgstr "Şifreleme parolası" msgid "Partitions" -msgstr "" +msgstr "Disk bölümleri" msgid "No HSM devices available" msgstr "" @@ -997,19 +999,19 @@ msgid "Pre-mounted configuration" msgstr "Yapılandırma yok" msgid "Unknown" -msgstr "" +msgstr "Bilinmeyen" msgid "Partition encryption" -msgstr "" +msgstr "Disk bölümü şifrelemesi" msgid " ! Formatting {} in " msgstr "" msgid "← Back" -msgstr "" +msgstr "← Geri" msgid "Disk encryption" -msgstr "" +msgstr "Disk şifrelemesi" #, fuzzy msgid "Configuration" @@ -1024,7 +1026,7 @@ msgid "All settings will be reset, are you sure?" msgstr "{} işlem sırasında bekleyen disk bölümleri bulunduruyor, bu onları kaldıracak, emin misiniz?" msgid "Back" -msgstr "" +msgstr "Geri" msgid "Please chose which greeter to install for the chosen profiles: {}" msgstr "" @@ -1033,30 +1035,30 @@ msgid "Environment type: {}" msgstr "" msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" -msgstr "" +msgstr "Patentli Nvidia sürücüsü Sway tarafından desteklenmiyor. Sıkıntılarla karşılaşmanız olası, emin misiniz?" #, fuzzy msgid "Installed packages" -msgstr "Ek paketler" +msgstr "İndirilmiş paketler" #, fuzzy msgid "Add profile" -msgstr "Profil" +msgstr "Profil ekle" #, fuzzy msgid "Edit profile" -msgstr "Profil" +msgstr "Profil düzenle" #, fuzzy msgid "Delete profile" -msgstr "Arayüz sil" +msgstr "Profil sil" #, fuzzy msgid "Profile name: " -msgstr "Profil" +msgstr "Profil ismi:" msgid "The profile name you entered is already in use. Try again" -msgstr "" +msgstr "Girdiğiniz profil adı zaten kullanılıyor. Tekrar deneyin" #, fuzzy msgid "Packages to be install with this profile (space separated, leave blank to skip): " @@ -1082,7 +1084,7 @@ msgstr "" "Bir grafik sürücüsü seçin ya da bütün açık-kaynak sürücüleri kurmak için boş bırakın" msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" -msgstr "" +msgstr "Sway'in seat'e erişmesi gerekir (klavye, fare vb. donanım aygıtlarının birleşimi)" msgid "" "\n" @@ -1091,7 +1093,7 @@ msgid "" msgstr "" msgid "Graphics driver" -msgstr "" +msgstr "Grafik sürücüsü" msgid "Greeter" msgstr "" @@ -1108,10 +1110,10 @@ msgstr "Yapılandırma yok" #, fuzzy msgid "Profiles" -msgstr "Profil" +msgstr "Profiller" msgid "Finding possible directories to save configuration files ..." -msgstr "" +msgstr "Yapılandırma dosyalarını kaydetmek için olası dizinler bulunuyor ..." #, fuzzy msgid "Select directory (or directories) for saving configuration files" @@ -1119,13 +1121,13 @@ msgstr "Kullanmak ve yapılandırmak için bir veya daha fazla sabit disk seçin #, fuzzy msgid "Add a custom mirror" -msgstr "Kullanıcı ekle" +msgstr "Özel indirme sunucusu ekleyin" msgid "Change custom mirror" -msgstr "" +msgstr "Özel indirme sunucusunu değiştirin" msgid "Delete custom mirror" -msgstr "" +msgstr "Özel indirme sunucusunu silin" #, fuzzy msgid "Enter name (leave blank to skip): " @@ -1141,13 +1143,13 @@ msgstr "Eklemek için arayüz seçin" #, fuzzy msgid "Select signature option" -msgstr "Eklemek için arayüz seçin" +msgstr "İmza seçeneğini seçin" msgid "Custom mirrors" -msgstr "" +msgstr "Özel indirme sunucusu bölgesi" msgid "Defined" -msgstr "" +msgstr "Tanımlı" #, fuzzy msgid "Save user configuration (including disk layout)" @@ -1181,4 +1183,4 @@ msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downl msgstr "" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" -msgstr "" +msgstr "Geçersiz girdi! Geçerli bir girişle tekrar deneyin [1 ila {} veya devre dışı bırakmak için 0]" -- cgit v1.2.3-70-g09d2 From f9ce7679fed5f28b165841fbae6f27581d1ca4b6 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Mon, 12 Jun 2023 10:49:53 +0200 Subject: Added a service-started wait timer for keyring.timer (#1858) * Added a service-started wait timer for keyring.timer, and then we check the service state for keyring.service. This is because the .service can be 'dead' right from the start without the timer ever have started. This ensures that we wait for the timer to kick in before we monitor for the .service execution * Removed pacman-init.service wait timer, as we can rely on keyring.timer instead: https://github.com/archlinux/archinstall/issues/1846#issuecomment-1586872920 --- archinstall/lib/installer.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 6eac85fc..e3dd3f04 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -147,12 +147,17 @@ class Installer: while self._service_state('reflector') not in ('dead', 'failed', 'exited'): time.sleep(1) - info('Waiting pacman-init.service to complete.') - while self._service_state('pacman-init') not in ('dead', 'failed', 'exited'): + # info('Waiting for pacman-init.service to complete.') + # while self._service_state('pacman-init') not in ('dead', 'failed', 'exited'): + # time.sleep(1) + + info('Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete.') + # Wait for the timer to kick in + while self._service_started('archlinux-keyring-wkd-sync.timer') is None: time.sleep(1) - info('Waiting Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete.') - while self._service_state('archlinux-keyring-wkd-sync') not in ('dead', 'failed', 'exited'): + # Wait for the service to enter a finished state + while self._service_state('archlinux-keyring-wkd-sync.service') not in ('dead', 'failed', 'exited'): time.sleep(1) def _verify_boot_part(self): @@ -1206,8 +1211,19 @@ class Installer: return True + def _service_started(self, service_name: str) -> str | None: + if os.path.splitext(service_name)[1] not in ('.service', '.target', '.timer'): + service_name += '.service' # Just to be safe + + last_execution_time = b''.join(SysCommand(f"systemctl show --property=ActiveEnterTimestamp --no-pager {service_name}", environment_vars={'SYSTEMD_COLORS': '0'})) + last_execution_time = last_execution_time.lstrip(b'ActiveEnterTimestamp=').strip() + if not last_execution_time: + return None + + return last_execution_time.decode('UTF-8') + def _service_state(self, service_name: str) -> str: - if os.path.splitext(service_name)[1] != '.service': + if os.path.splitext(service_name)[1] not in ('.service', '.target', '.timer'): service_name += '.service' # Just to be safe state = b''.join(SysCommand(f'systemctl show --no-pager -p SubState --value {service_name}', environment_vars={'SYSTEMD_COLORS': '0'})) -- cgit v1.2.3-70-g09d2 From 1b0c8f3ce3bc7887632f0d08b7afe8073f7a3958 Mon Sep 17 00:00:00 2001 From: Nikita Triblya <69369034+Tr3bleee@users.noreply.github.com> Date: Mon, 12 Jun 2023 10:54:38 +0200 Subject: Removed comma at the end of package listing in i3.py (#1839) Removed comma at the end of package listing in i3.py --- archinstall/default_profiles/desktops/i3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archinstall/default_profiles/desktops/i3.py b/archinstall/default_profiles/desktops/i3.py index 7c6f24ca..93c38906 100644 --- a/archinstall/default_profiles/desktops/i3.py +++ b/archinstall/default_profiles/desktops/i3.py @@ -21,7 +21,7 @@ class I3wmProfile(XorgProfile): 'xterm', 'lightdm-gtk-greeter', 'lightdm', - 'dmenu', + 'dmenu' ] @property -- cgit v1.2.3-70-g09d2 From 77710883b01f3675b38f2224e52f012da89775de Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Mon, 12 Jun 2023 05:36:19 -0400 Subject: Update bootctl --path option to --esp-path (#1854) --- archinstall/lib/installer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index e3dd3f04..3fcba39c 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -798,10 +798,10 @@ class Installer: # Install the boot loader try: - SysCommand(f'/usr/bin/arch-chroot {self.target} bootctl --path=/boot install') + SysCommand(f'/usr/bin/arch-chroot {self.target} bootctl --esp-path=/boot install') except SysCallError: # Fallback, try creating the boot loader without touching the EFI variables - SysCommand(f'/usr/bin/arch-chroot {self.target} bootctl --no-variables --path=/boot install') + SysCommand(f'/usr/bin/arch-chroot {self.target} bootctl --no-variables --esp-path=/boot install') # Ensure that the /boot/loader directory exists before we try to create files in it if not os.path.exists(f'{self.target}/boot/loader'): -- cgit v1.2.3-70-g09d2 From 84bf24cbbb224415ee24ea4dfef6ae83500f810e Mon Sep 17 00:00:00 2001 From: Eren İnce <63284001+eren-ince@users.noreply.github.com> Date: Mon, 12 Jun 2023 16:21:41 +0300 Subject: Updated Turkish translation (#1771) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update base.po * Update base.po * Delete base.mo * Add files via upload --------- Co-authored-by: Eren İnce <63284001+ernince@users.noreply.github.com> Co-authored-by: Anton Hvornum --- archinstall/locales/tr/LC_MESSAGES/base.mo | Bin 24425 -> 31241 bytes archinstall/locales/tr/LC_MESSAGES/base.po | 79 +++++++++++++++++------------ 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/archinstall/locales/tr/LC_MESSAGES/base.mo b/archinstall/locales/tr/LC_MESSAGES/base.mo index de6dea40..7cea76f3 100644 Binary files a/archinstall/locales/tr/LC_MESSAGES/base.mo and b/archinstall/locales/tr/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/tr/LC_MESSAGES/base.po b/archinstall/locales/tr/LC_MESSAGES/base.po index 435f80a5..1b355cfb 100644 --- a/archinstall/locales/tr/LC_MESSAGES/base.po +++ b/archinstall/locales/tr/LC_MESSAGES/base.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: 21.05.2022\n" "PO-Revision-Date: 2022-05-25 17:42+0300\n" -"Last-Translator: Tuğşat Enes @tugsatenes \n" +"Last-Translator: Eren İnce @ernince \n" "Language-Team: \n" "Language: tr\n" "MIME-Version: 1.0\n" @@ -804,26 +804,29 @@ msgid "Configured {} interfaces" msgstr "Yapılandırılmış {} arayüzler" msgid "This option enables the number of parallel downloads that can occur during installation" -msgstr "Bu seçenek, kurulum sırasında gerçekleşebilecek paralel indirme sayısını etkinleştirir" +msgstr "Bu seçenek, yükleme sırasında meydana gelebilecek paralel indirme sayısını etkinleştirir" msgid "" "Enter the number of parallel downloads to be enabled.\n" " (Enter a value between 1 to {})\n" "Note:" msgstr "" +"Etkinleştirilecek paralel indirme sayısını girin.\n" +" (1 ile {max_downloads} arasında bir değer girin)\n" +"Not:" msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" -msgstr "" +msgstr "- Maksimum değer : {} ( {} paralel indirmeye izin verir, bir seferde {} indirmeye izin verir)" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" -msgstr "" +msgstr "- Minimum değer : 1 ( 1 paralel indirmeye izin verir, bir seferde 2 indirmeye izin verir )" msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" -msgstr "" +msgstr "- Devre Dışı Bırak/Varsayılan : 0 (Paralel indirmeyi devre dışı bırakır, aynı anda yalnızca 1 indirmeye izin verir)" #, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "" +msgstr "Geçersiz Giriş! Geçerli bir girişle tekrar deneyin [1 - {max_downloads} veya 0 devre dışı bırakmak için]" msgid "Parallel Downloads" msgstr "Paralel İndirmeler" @@ -833,32 +836,32 @@ msgid "ESC to skip" msgstr "Geçmek için ESC'ye basın" msgid "CTRL+C to reset" -msgstr "Resetlemek için CTRL+C'ye basın" +msgstr "Sıfırlamak için CTRL+C" msgid "TAB to select" -msgstr "Seçmek için TAB'a basın" +msgstr "Seçmek için TAB" msgid "[Default value: 0] > " msgstr "" msgid "To be able to use this translation, please install a font manually that supports the language." -msgstr "Bu çeviriyi kullanabilmek için lütfen bu dili destekleyen bir yazı tipini manuel olarak yükleyin." +msgstr "Bu çeviriyi kullanabilmek için lütfen dili destekleyen bir yazı tipini manuel olarak yükleyin." msgid "The font should be stored as {}" -msgstr "Yazı fontu {} olarak depolanmalıdır" +msgstr "Yazı tipi {} olarak saklanmalıdır." msgid "Archinstall requires root privileges to run. See --help for more." -msgstr "Archinstall çalışmak için root (kök) ayrıcalıkları gerektirir. Daha fazlası için --help'e bakın." +msgstr "Archinstall'ın çalışması için kök ayrıcalıkları gerekir. Daha fazla bilgi için --help'e bakın." #, fuzzy msgid "Select an execution mode" msgstr "'{}' için bir eylem seçin" msgid "Unable to fetch profile from specified url: {}" -msgstr "" +msgstr "Belirtilen url'den profil getirilemiyor: {}" msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" -msgstr "" +msgstr "Profiller benzersiz ada sahip olmalıdır, ancak yinelenen ada sahip profil tanımları bulundu: {}" #, fuzzy msgid "Select one or more devices to use and configure" @@ -909,7 +912,7 @@ msgid "Mark/Unmark to be formatted (wipes data)" msgstr "Bir disk bölümünü biçimlendirilmek üzere işaretle/işareti kaldır (veriyi temizler)" msgid "Mark/Unmark as bootable" -msgstr "Önyüklenebilir olarak işaretle/işareti kaldır" +msgstr "Önyüklenebilir olarak İşaretle/İşareti kaldır" msgid "Change filesystem" msgstr "Dosya sistemini değiştir" @@ -937,13 +940,13 @@ msgid "Partition mount-points are relative to inside the installation, the boot msgstr " * Disk bölümü mount (monte) noktaları iç kurulumla ilişkilidir, örnek olarak boot, /boot olacaktır." msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." -msgstr "" +msgstr "mountpoint /boot ayarlanmışsa, bölüm de önyüklenebilir olarak işaretlenecektir." msgid "Mountpoint: " -msgstr "" +msgstr "Bağlama noktası: " msgid "Current free sectors on device {}:" -msgstr "" +msgstr "{} cihazındaki mevcut boş sektörler:" #, fuzzy msgid "Total sectors: {}" @@ -958,13 +961,13 @@ msgid "Enter the end sector of the partition (percentage or block number, defaul msgstr "Disk bölümünün bitiş kesimini girin (yüzde ya da blok numarası, ör: {}): " msgid "This will remove all newly added partitions, continue?" -msgstr "Bu yeni eklenen tüm disk bölümlerini kaldıracaktır, devam ediyor musunuz?" +msgstr "Bu işlem yeni eklenmiş tüm bölümleri kaldıracaktır, devam edilsin?" msgid "Partition management: {}" -msgstr "" +msgstr "Bölüm yönetimi: {}" msgid "Total length: {}" -msgstr "" +msgstr "Toplam uzunluk: {}" #, fuzzy msgid "Encryption type" @@ -974,17 +977,17 @@ msgid "Partitions" msgstr "Disk bölümleri" msgid "No HSM devices available" -msgstr "" +msgstr "Kullanılabilir HSM cihazı yok" #, fuzzy msgid "Partitions to be encrypted" msgstr "Hangi disk bölümünün şifrelenmiş olarak işaretleneceğini seçin" msgid "Select disk encryption option" -msgstr "" +msgstr "Disk şifreleme seçeneğini seçin" msgid "Select a FIDO2 device to use for HSM" -msgstr "" +msgstr "HSM için kullanılacak bir FIDO2 cihazı seçin" #, fuzzy msgid "Use a best-effort default partition layout" @@ -1005,7 +1008,7 @@ msgid "Partition encryption" msgstr "Disk bölümü şifrelemesi" msgid " ! Formatting {} in " -msgstr "" +msgstr " ! {} biçimlendiriliyor " msgid "← Back" msgstr "← Geri" @@ -1029,13 +1032,13 @@ msgid "Back" msgstr "Geri" msgid "Please chose which greeter to install for the chosen profiles: {}" -msgstr "" +msgstr "Lütfen seçilen profiller için hangi karşılayıcının kurulacağını seçin: {}" msgid "Environment type: {}" -msgstr "" +msgstr "Ortam türü: {}" msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" -msgstr "Patentli Nvidia sürücüsü Sway tarafından desteklenmiyor. Sıkıntılarla karşılaşmanız olası, emin misiniz?" +msgstr "Tescilli Nvidia sürücüsü Sway tarafından desteklenmiyor. Sorunlarla karşılaşmanız muhtemeldir, bu sizin için uygun mu?" #, fuzzy msgid "Installed packages" @@ -1069,10 +1072,10 @@ msgid "Services to be enabled with this profile (space separated, leave blank to msgstr "Kurulacak ek paketleri yazınız (boşlukla ayrılmış, geçmek için boş bırakın): " msgid "Should this profile be enabled for installation?" -msgstr "" +msgstr "Bu profil kurulum için etkinleştirilmeli mi?" msgid "Create your own" -msgstr "" +msgstr "Kendinizinkini oluşturun" #, fuzzy msgid "" @@ -1091,18 +1094,21 @@ msgid "" "\n" "Choose an option to give Sway access to your hardware" msgstr "" +"\n" +"\n" +"Sway'in donanımınıza erişmesine izin vermek için bir seçenek belirleyin" msgid "Graphics driver" msgstr "Grafik sürücüsü" msgid "Greeter" -msgstr "" +msgstr "Karşılayıcı" msgid "Please chose which greeter to install" -msgstr "" +msgstr "Lütfen hangi karşılayıcının kurulacağını seçin" msgid "This is a list of pre-programmed default_profiles" -msgstr "" +msgstr "Bu, önceden programlanmış default_profiles listesidir" #, fuzzy msgid "Disk configuration" @@ -1113,7 +1119,7 @@ msgid "Profiles" msgstr "Profiller" msgid "Finding possible directories to save configuration files ..." -msgstr "Yapılandırma dosyalarını kaydetmek için olası dizinler bulunuyor ..." +msgstr "Yapılandırma dosyalarını kaydetmek için olası dizinler aranıyor..." #, fuzzy msgid "Select directory (or directories) for saving configuration files" @@ -1184,3 +1190,10 @@ msgstr "" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "Geçersiz girdi! Geçerli bir girişle tekrar deneyin [1 ila {} veya devre dışı bırakmak için 0]" + +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "NetworkManager'ı kullan (GNOME ya da KDE'de interneti grafik olarak yapılandırmak için gerekli)" -- cgit v1.2.3-70-g09d2 From e0a3bd11142cb0ee5db10fe6ffe5d78bf6a595fa Mon Sep 17 00:00:00 2001 From: "K.B.Dharun Krishna" Date: Mon, 12 Jun 2023 18:54:55 +0530 Subject: Update Tamil translation (#1765) Signed-off-by: K.B.Dharun Krishna Co-authored-by: Anton Hvornum --- archinstall/locales/ta/LC_MESSAGES/base.mo | Bin 47479 -> 62599 bytes archinstall/locales/ta/LC_MESSAGES/base.po | 213 +++++++++++++---------------- 2 files changed, 92 insertions(+), 121 deletions(-) diff --git a/archinstall/locales/ta/LC_MESSAGES/base.mo b/archinstall/locales/ta/LC_MESSAGES/base.mo index 6e7c6ac9..8c163da6 100644 Binary files a/archinstall/locales/ta/LC_MESSAGES/base.mo and b/archinstall/locales/ta/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ta/LC_MESSAGES/base.po b/archinstall/locales/ta/LC_MESSAGES/base.po index 968cb1fc..f6b7533a 100644 --- a/archinstall/locales/ta/LC_MESSAGES/base.po +++ b/archinstall/locales/ta/LC_MESSAGES/base.po @@ -9,7 +9,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.1.1\n" +"X-Generator: Poedit 3.2.2\n" msgid "[!] A log file has been created here: {} {}" msgstr "[!] ஒரு பதிவு கோப்பு இங்கே உருவாக்கப்பட்டது: {} {}" @@ -97,10 +97,10 @@ msgid "Enter a desired filesystem type for the partition" msgstr "பகிர்வுக்கு தேவையான கோப்பு முறைமை வகையை உள்ளிடவும்" msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " -msgstr "" +msgstr "தொடக்க இடத்தை உள்ளிடவும் (பிரிக்கப்பட்ட அலகுகளில்: s, GB, %, முதலியன ; இயல்புநிலை: {}): " msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " -msgstr "" +msgstr "இறுதி இடத்தை உள்ளிடவும் (பிரிக்கப்பட்ட அலகுகளில்: s, GB, %, etc. ; ex: {}): " msgid "{} contains queued partitions, this will remove those, are you sure?" msgstr "{} வரிசைப்படுத்தப்பட்ட பகிர்வுகளைக் கொண்டுள்ளது, இது அவற்றை அகற்றும், நீங்கள் உறுதியாக இருக்கிறீர்களா?" @@ -828,286 +828,250 @@ msgid "TAB to select" msgstr "தேர்ந்தெடுக்க TAB" msgid "[Default value: 0] > " -msgstr "" +msgstr "[இயல்புநிலை மதிப்பு: 0] > " msgid "To be able to use this translation, please install a font manually that supports the language." -msgstr "" +msgstr "இந்த மொழிபெயர்ப்பைப் பயன்படுத்த, மொழியை ஆதரிக்கும் எழுத்துருவை கைமுறையாக நிறுவவும்." msgid "The font should be stored as {}" -msgstr "" +msgstr "எழுத்துரு {} ஆக சேமிக்கப்பட வேண்டும்" msgid "Archinstall requires root privileges to run. See --help for more." -msgstr "" +msgstr "Archinstall ஐ இயக்க ரூட் சிறப்புரிமைகள் தேவை. மேலும் பார்க்க --help." -#, fuzzy msgid "Select an execution mode" -msgstr "'{}'க்கான செயலைத் தேர்ந்தெடுக்கவும்" +msgstr "செயல்படுத்தும் பயன்முறையைத் தேர்ந்தெடுக்கவும்" msgid "Unable to fetch profile from specified url: {}" -msgstr "" +msgstr "குறிப்பிட்ட url இலிருந்து சுயவிவரத்தைப் பெற முடியவில்லை: {}" msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" -msgstr "" +msgstr "சுயவிவரங்கள் தனிப்பட்ட பெயரைக் கொண்டிருக்க வேண்டும், ஆனால் நகல் பெயருடன் சுயவிவர வரையறைகள் காணப்படுகின்றன: {}" -#, fuzzy msgid "Select one or more devices to use and configure" -msgstr "பயன்படுத்த மற்றும் கட்டமைக்க ஒன்று அல்லது அதற்கு மேற்பட்ட ஹார்டு டிரைவ்களைத் தேர்ந்தெடுக்கவும்" +msgstr "பயன்படுத்த மற்றும் கட்டமைக்க ஒன்று அல்லது அதற்கு மேற்பட்ட சாதனங்களைத் தேர்ந்தெடுக்கவும்" -#, fuzzy msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" -msgstr "நீங்கள் ஹார்ட் டிரைவ் தேர்வை மீட்டமைத்தால், இது தற்போதைய வட்டு அமைப்பையும் மீட்டமைக்கும். நீங்கள் உறுதியாக இருக்கிறீர்களா?" +msgstr "சாதனத் தேர்வை மீட்டமைத்தால், இது தற்போதைய வட்டு அமைப்பையும் மீட்டமைக்கும். நீங்கள் உறுதியாக இருக்கிறீர்களா?" -#, fuzzy msgid "Existing Partitions" -msgstr "பகிர்வை சேர்க்கிறது...." +msgstr "ஏற்கனவே உள்ள பகிர்வுகள்" -#, fuzzy msgid "Select a partitioning option" -msgstr "ஒரு பகிர்வை நீக்கவும்" +msgstr "பகிர்வு விருப்பத்தைத் தேர்ந்தெடுக்கவும்" -#, fuzzy msgid "Enter the root directory of the mounted devices: " -msgstr "உள்ளமைவு(களை) சேமிக்க ஒரு கோப்பகத்தை உள்ளிடவும்: " +msgstr "ஏற்றப்பட்ட சாதனங்களின் ரூட் கோப்பகத்தை உள்ளிடவும்: " -#, fuzzy msgid "Minimum capacity for /home partition: {}GiB\n" -msgstr "/home பகிர்வுக்கான குறைந்த பட்ச கொள்ளளவு: {}GB\n" +msgstr "" +"/home பகிர்வுக்கான குறைந்தபட்ச கொள்ளளவு: {}GiB\n" +"\n" -#, fuzzy msgid "Minimum capacity for Arch Linux partition: {}GiB" -msgstr "ஆர்ச் லினக்ஸ் பகிர்வுக்கான குறைந்தபட்ச கொள்ளளவு: {}GB" +msgstr "ஆர்ச் லினக்ஸ் பகிர்வுக்கான குறைந்தபட்ச கொள்ளளவு: {}ஜிபி" -#, fuzzy msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" -msgstr "இது முன்-திட்டமிடப்பட்ட சுயவிவரங்களின் பட்டியல், அவை டெஸ்க்டாப் சூழல்கள் போன்றவற்றை நிறுவுவதை எளிதாக்கலாம்" +msgstr "இது முன்-திட்டமிடப்பட்ட profiles_bck இன் பட்டியல், அவை டெஸ்க்டாப் சூழல்கள் போன்றவற்றை நிறுவுவதை எளிதாக்கலாம்" -#, fuzzy msgid "Current profile selection" -msgstr "தற்போதைய பகிர்வு தளவமைப்பு" +msgstr "தற்போதைய சுயவிவரத் தேர்வு" -#, fuzzy msgid "Remove all newly added partitions" -msgstr "புதிய பகிர்வை உருவாக்கவும்" +msgstr "புதிதாக சேர்க்கப்பட்ட அனைத்து பகிர்வுகளையும் அகற்றவும்" -#, fuzzy msgid "Assign mountpoint" -msgstr "ஒரு பகிர்வுக்கு ஏற்ற-புள்ளியை ஒதுக்கவும்" +msgstr "பகிர்வுக்கு ஏற்ற-புள்ளியை ஒதுக்கவும்" -#, fuzzy msgid "Mark/Unmark to be formatted (wipes data)" -msgstr "குறி/குறிநீக்கு வடிவமைக்கப்பட வேண்டிய பகிர்வை (தரவை அழிக்கிறது)" +msgstr "வடிவமைக்கப்பட வேண்டியவையை குறி/குறிநீக்கு (தரவை அழிக்கிறது)" msgid "Mark/Unmark as bootable" -msgstr "" +msgstr "துவக்கக்கூடியதாகக் குறி/குறிநீக்கு" msgid "Change filesystem" -msgstr "" +msgstr "கோப்பு முறைமையை மாற்றவும்" -#, fuzzy msgid "Mark/Unmark as compressed" -msgstr "ஒரு பகிர்வை சுருக்கப்பட்டதாகக் குறிக்கவும்/குறி நீக்கவும் (btrfs மட்டும்)" +msgstr "சுருக்கப்பட்டதாகக் குறி/குறிநீக்கு" -#, fuzzy msgid "Set subvolumes" -msgstr "துணைத்தொகுதியை நீக்கவும்" +msgstr "துணைத் தொகுதிகளை அமைக்கவும்" -#, fuzzy msgid "Delete partition" -msgstr "ஒரு பகிர்வை நீக்கவும்" +msgstr "பகிர்வை நீக்கு" msgid "Partition" -msgstr "" +msgstr "பகிர்வு" msgid "This partition is currently encrypted, to format it a filesystem has to be specified" -msgstr "" +msgstr "இந்த பகிர்வு தற்போது குறியாக்கம் செய்யப்பட்டுள்ளது, அதை வடிவமைக்க ஒரு கோப்பு முறைமை குறிப்பிடப்பட வேண்டும்" -#, fuzzy msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * பகிர்வு மவுண்ட்-பாயிண்ட்கள் நிறுவலின் உள்ளே தொடர்புடையவை, துவக்கம் /boot எடுத்துக்காட்டாக இருக்கும்." +msgstr "பகிர்வு மவுண்ட்-பாயிண்ட்கள் நிறுவலின் உள்ளே தொடர்புடையவை, துவக்கம் /boot எடுத்துக்காட்டாக இருக்கும்." msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." -msgstr "" +msgstr "மவுண்ட்பாயிண்ட் /boot அமைக்கப்பட்டால், பகிர்வு துவக்கக்கூடியதாகக் குறிக்கப்படும்." msgid "Mountpoint: " -msgstr "" +msgstr "மவுண்ட்பாயிண்ட்: " msgid "Current free sectors on device {}:" -msgstr "" +msgstr "{} சாதனத்தில் தற்போதைய இலவசப் பிரிவுகள்:" -#, fuzzy msgid "Total sectors: {}" -msgstr "சரியான கோப்பகம் இல்லை: {}" +msgstr "மொத்த பிரிவுகள்: {}" -#, fuzzy msgid "Enter the start sector (default: {}): " -msgstr "தொடக்கப் பிரிவை உள்ளிடவும் (சதவீதம் அல்லது தொகுதி எண், இயல்புநிலை: {}): " +msgstr "தொடக்கப் பிரிவை உள்ளிடவும் (இயல்புநிலை: {}): " -#, fuzzy msgid "Enter the end sector of the partition (percentage or block number, default: {}): " -msgstr "பகிர்வின் இறுதிப் பகுதியை உள்ளிடவும் (சதவீதம் அல்லது தொகுதி எண், எ.கா: {}): " +msgstr "பகிர்வின் இறுதிப் பகுதியை உள்ளிடவும் (சதவீதம் அல்லது தொகுதி எண், இயல்புநிலை: {}): " msgid "This will remove all newly added partitions, continue?" -msgstr "" +msgstr "இது புதிதாக சேர்க்கப்பட்ட அனைத்து பகிர்வுகளையும் அகற்றும், தொடரவா?" msgid "Partition management: {}" -msgstr "" +msgstr "பகிர்வு மேலாண்மை: {}" msgid "Total length: {}" -msgstr "" +msgstr "முழு நீளம்: {}" -#, fuzzy msgid "Encryption type" -msgstr "குறியாக்கம் கடவுச்சொல்" +msgstr "குறியாக்க வகை" msgid "Partitions" -msgstr "" +msgstr "பகிர்வுகள்" msgid "No HSM devices available" -msgstr "" +msgstr "HSM சாதனங்கள் எதுவும் இல்லை" -#, fuzzy msgid "Partitions to be encrypted" -msgstr "குறியாக்கம் செய்ய வேண்டிய பகிர்வுகளைத் தேர்ந்தெடுக்கவும்" +msgstr "குறியாக்கம் செய்யப்பட வேண்டிய பகிர்வுகள்" msgid "Select disk encryption option" -msgstr "" +msgstr "வட்டு குறியாக்க விருப்பத்தைத் தேர்ந்தெடுக்கவும்" msgid "Select a FIDO2 device to use for HSM" -msgstr "" +msgstr "HSM க்கு பயன்படுத்த FIDO2 சாதனத்தைத் தேர்ந்தெடுக்கவும்" -#, fuzzy msgid "Use a best-effort default partition layout" -msgstr "தேர்ந்தெடுக்கப்பட்ட அனைத்து இயக்கிகளையும் துடைத்து, சிறந்த முயற்சி இயல்புநிலை பகிர்வு அமைப்பைப் பயன்படுத்தவும்" +msgstr "சிறந்த முயற்சி இயல்புநிலை பகிர்வு தளவமைப்பைப் பயன்படுத்தவும்" -#, fuzzy msgid "Manual Partitioning" -msgstr "கைமுறை கட்டமைப்பு" +msgstr "கைமுறை பகிர்வு" -#, fuzzy msgid "Pre-mounted configuration" -msgstr "கட்டமைப்பு இல்லை" +msgstr "முன்-ஏற்றப்பட்ட கட்டமைப்பு" msgid "Unknown" -msgstr "" +msgstr "தெரியவில்லை/தெரியாதது" msgid "Partition encryption" -msgstr "" +msgstr "பகிர்வு குறியாக்கம்" msgid " ! Formatting {} in " -msgstr "" +msgstr " ! வடிவமைத்தல் {} உள்ள " msgid "← Back" -msgstr "" +msgstr "← பின்" msgid "Disk encryption" -msgstr "" +msgstr "வட்டு குறியாக்கம்" -#, fuzzy msgid "Configuration" -msgstr "கட்டமைப்பு இல்லை" +msgstr "கட்டமைப்பு" -#, fuzzy msgid "Password" -msgstr "ரூட் கடவுச்சொல்" +msgstr "கடவுச்சொல்" -#, fuzzy msgid "All settings will be reset, are you sure?" -msgstr "{} வரிசைப்படுத்தப்பட்ட பகிர்வுகளைக் கொண்டுள்ளது, இது அவற்றை அகற்றும், நீங்கள் உறுதியாக இருக்கிறீர்களா?" +msgstr "எல்லா அமைப்புகளும் மீட்டமைக்கப்படும், உறுதியாக இருக்கிறீர்களா?" msgid "Back" -msgstr "" +msgstr "திரும்பவும்" msgid "Please chose which greeter to install for the chosen profiles: {}" -msgstr "" +msgstr "தேர்ந்தெடுக்கப்பட்ட சுயவிவரங்களுக்கு எந்த வாழ்த்துரை நிறுவ வேண்டும் என்பதைத் தேர்வுசெய்யவும்: {}" msgid "Environment type: {}" -msgstr "" +msgstr "சுற்றுச்சூழல் வகை: {}" msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" -msgstr "" +msgstr "தனியுரிம Nvidia இயக்கி ஸ்வே ஆல் ஆதரிக்கப்படவில்லை. நீங்கள் சிக்கலில் சிக்க வாய்ப்புள்ளது, உனக்கு அது சரியா?" -#, fuzzy msgid "Installed packages" -msgstr "கூடுதல் தொகுப்புகள்" +msgstr "நிறுவப்பட்ட தொகுப்புகள்" -#, fuzzy msgid "Add profile" -msgstr "சுயவிவரம்" +msgstr "சுயவிவரத்தைச் சேர்க்கவும்" -#, fuzzy msgid "Edit profile" -msgstr "சுயவிவரம்" +msgstr "சுயவிவரத்தைத் திருத்தவும்" -#, fuzzy msgid "Delete profile" -msgstr "இடைமுகத்தை நீக்கு" +msgstr "சுயவிவரத்தை நீக்கு" -#, fuzzy msgid "Profile name: " -msgstr "சுயவிவரம்" +msgstr "சுயவிவரப் பெயர்: " -#, fuzzy msgid "The profile name you entered is already in use. Try again" -msgstr "நீங்கள் உள்ளிட்ட பயனர்பெயர் தவறானது. மீண்டும் முயற்சிக்கவும்" +msgstr "நீங்கள் உள்ளிட்ட சுயவிவரப் பெயர் ஏற்கனவே பயன்பாட்டில் உள்ளது. மீண்டும் முயற்சிக்கவும்" -#, fuzzy msgid "Packages to be install with this profile (space separated, leave blank to skip): " -msgstr "நிறுவ கூடுதல் தொகுப்புகளை எழுதவும் (இடம் பிரிக்கப்பட்டது, தவிர்க்க காலியாக விடவும்): " +msgstr "இந்த சுயவிவரத்துடன் நிறுவப்பட வேண்டிய தொகுப்புகள் (இடம் பிரிக்கப்பட்டது, தவிர்க்க காலியாக விடவும்): " -#, fuzzy msgid "Services to be enabled with this profile (space separated, leave blank to skip): " -msgstr "நிறுவ கூடுதல் தொகுப்புகளை எழுதவும் (இடம் பிரிக்கப்பட்டது, தவிர்க்க காலியாக விடவும்): " +msgstr "இந்தச் சுயவிவரத்துடன் இயக்கப்பட வேண்டிய சேவைகள் (இடம் பிரிக்கப்பட்டது, தவிர்க்க காலியாக விடவும்): " msgid "Should this profile be enabled for installation?" -msgstr "" +msgstr "நிறுவலுக்கு இந்த சுயவிவரம் இயக்கப்பட வேண்டுமா?" msgid "Create your own" -msgstr "" +msgstr "சொந்தமாக உருவாக்கவும்" -#, fuzzy msgid "" "\n" "Select a graphics driver or leave blank to install all open-source drivers" msgstr "" "\n" -"\n" -"கிராபிக்ஸ் இயக்கியைத் தேர்ந்தெடுக்கவும் அல்லது அனைத்து திறந்த மூல இயக்கிகளையும் நிறுவ காலியாக விடவும்" +"அனைத்து திறந்த மூல இயக்கிகளையும் நிறுவ வரைகலை இயக்கிகளை தேர்ந்தெடுக்கவும் அல்லது காலியாக விடவும்" msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" -msgstr "" +msgstr "ஸ்வேக்கு உங்கள் இருக்கைக்கான அணுகல் தேவை (வன்பொருள் சாதனங்களின் சேகரிப்பு அதாவது விசைப்பலகை, சுட்டி போன்றவை)" msgid "" "\n" "\n" "Choose an option to give Sway access to your hardware" msgstr "" +"\n" +"\n" +"உங்கள் வன்பொருளுக்கான அணுகலை வழங்குவதற்கான விருப்பத்தைத் தேர்வுசெய்யவும்" msgid "Graphics driver" -msgstr "" +msgstr "வரைகலை இயக்கி" msgid "Greeter" -msgstr "" +msgstr "வாழ்த்துபவர்" msgid "Please chose which greeter to install" -msgstr "" +msgstr "எந்த வாழ்த்துரை நிறுவ வேண்டும் என்பதைத் தேர்வுசெய்யவும்" msgid "This is a list of pre-programmed default_profiles" -msgstr "" +msgstr "இது முன்-திட்டமிடப்பட்ட default_profiles பட்டியல்" -#, fuzzy msgid "Disk configuration" -msgstr "கட்டமைப்பு இல்லை" +msgstr "வட்டு கட்டமைப்பு" -#, fuzzy msgid "Profiles" -msgstr "சுயவிவரம்" +msgstr "சுயவிவரங்கள்" msgid "Finding possible directories to save configuration files ..." -msgstr "" +msgstr "உள்ளமைவு கோப்புகளைச் சேமிக்க சாத்தியமான கோப்பகங்களைக் கண்டறிதல் ..." -#, fuzzy msgid "Select directory (or directories) for saving configuration files" -msgstr "பயன்படுத்த மற்றும் கட்டமைக்க ஒன்று அல்லது அதற்கு மேற்பட்ட ஹார்டு டிரைவ்களைத் தேர்ந்தெடுக்கவும்" +msgstr "உள்ளமைவு கோப்புகளை சேமிக்க கோப்பகத்தை (அல்லது கோப்பகங்களை) தேர்ந்தெடுக்கவும்" #, fuzzy msgid "Add a custom mirror" @@ -1175,3 +1139,10 @@ msgstr " - அதிகபட்ச மதிப்பு : {} ( {} இணை msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "தவறான உள்ளீடு! சரியான உள்ளீட்டில் [1 முதல் {} வரை அல்லது முடக்க 0 வரை] மீண்டும் முயற்சிக்கவும்" + +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "பயன்படுத்துங்கள் NetworkManager ஐப்(GNOME மற்றும் KDE இல் இணையத்தை வரைகலை முறையில் கட்டமைக்க அவசியம்)" -- cgit v1.2.3-70-g09d2 From aa1c6b014d3c53ab8350f684d32c10725cffa494 Mon Sep 17 00:00:00 2001 From: Wellinton Kricowski Date: Mon, 12 Jun 2023 10:25:46 -0300 Subject: PT BR translation improvements (#1760) --- archinstall/locales/pt_BR/LC_MESSAGES/base.po | 38 +++++++++++++-------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.po b/archinstall/locales/pt_BR/LC_MESSAGES/base.po index a4137edf..54419d23 100644 --- a/archinstall/locales/pt_BR/LC_MESSAGES/base.po +++ b/archinstall/locales/pt_BR/LC_MESSAGES/base.po @@ -905,7 +905,7 @@ msgid "Mark/Unmark as bootable" msgstr "" msgid "Change filesystem" -msgstr "" +msgstr "Mudar arquivo do sistema" #, fuzzy msgid "Mark/Unmark as compressed" @@ -920,7 +920,7 @@ msgid "Delete partition" msgstr "Deletar uma partição" msgid "Partition" -msgstr "" +msgstr "Partição: " msgid "This partition is currently encrypted, to format it a filesystem has to be specified" msgstr "" @@ -933,7 +933,7 @@ msgid "If mountpoint /boot is set, then the partition will also be marked as boo msgstr "" msgid "Mountpoint: " -msgstr "" +msgstr "Ponto de montagem: " msgid "Current free sectors on device {}:" msgstr "" @@ -957,7 +957,7 @@ msgid "Partition management: {}" msgstr "" msgid "Total length: {}" -msgstr "" +msgstr "Tamanho total: {}" #, fuzzy msgid "Encryption type" @@ -984,30 +984,30 @@ msgstr "Apagar todos os discos selecionados e usar um esquema de partições pad #, fuzzy msgid "Manual Partitioning" -msgstr "Configuração manual" +msgstr "Particionamento manual" #, fuzzy msgid "Pre-mounted configuration" msgstr "Nenhuma configuração" msgid "Unknown" -msgstr "" +msgstr "Desconhecido" msgid "Partition encryption" msgstr "Encriptação de partição" msgid " ! Formatting {} in " -msgstr "" +msgstr " ! Formatando {} em " msgid "← Back" -msgstr "" +msgstr "← Voltar" msgid "Disk encryption" msgstr "Encriptação de disco" #, fuzzy msgid "Configuration" -msgstr "Nenhuma configuração" +msgstr "Configuração" msgid "Password" msgstr "Senha" @@ -1017,7 +1017,7 @@ msgid "All settings will be reset, are you sure?" msgstr "{} contém partições em fila, isto irá removê-las, tem certeza?" msgid "Back" -msgstr "" +msgstr "Voltar" msgid "Please chose which greeter to install for the chosen profiles: {}" msgstr "" @@ -1034,23 +1034,23 @@ msgstr "Pacotes adicionais" #, fuzzy msgid "Add profile" -msgstr "Perfil" +msgstr "Adicionar perfil" #, fuzzy msgid "Edit profile" -msgstr "Perfil" +msgstr "Editar perfil" #, fuzzy msgid "Delete profile" -msgstr "Deletar interface" +msgstr "Deletar perfil" #, fuzzy msgid "Profile name: " -msgstr "Perfil" +msgstr "Nome do perfil: " #, fuzzy msgid "The profile name you entered is already in use. Try again" -msgstr "O nome de usuário que você digitou é inválido. Tente novamente" +msgstr "O nome de usuário que você digitou já esta em uso. Tente outro" #, fuzzy msgid "Packages to be install with this profile (space separated, leave blank to skip): " @@ -1064,7 +1064,7 @@ msgid "Should this profile be enabled for installation?" msgstr "" msgid "Create your own" -msgstr "" +msgstr "Crie o seu próprio" #, fuzzy msgid "" @@ -1085,7 +1085,7 @@ msgid "" msgstr "" msgid "Graphics driver" -msgstr "" +msgstr "Driver gráfico" msgid "Greeter" msgstr "" @@ -1098,14 +1098,14 @@ msgstr "" #, fuzzy msgid "Disk configuration" -msgstr "Nenhuma configuração" +msgstr "Configuração do disco" #, fuzzy msgid "Profiles" msgstr "Perfil" msgid "Finding possible directories to save configuration files ..." -msgstr "" +msgstr "Procurando possiveis diretórios para salvar os arquivos de configuração ..." #, fuzzy msgid "Select directory (or directories) for saving configuration files" -- cgit v1.2.3-70-g09d2 From 99fbc189c69817a2ba09b7328a560be0b4da0896 Mon Sep 17 00:00:00 2001 From: wc7086 <74053663+wc7086@users.noreply.github.com> Date: Mon, 12 Jun 2023 21:33:28 +0800 Subject: Update Simplified Chinese translation (#1757) Co-authored-by: wc7086 <190022474@qq.com> Co-authored-by: Anton Hvornum --- archinstall/locales/languages.json | 2 +- archinstall/locales/zh-CN/LC_MESSAGES/base.mo | Bin 23995 -> 32120 bytes archinstall/locales/zh-CN/LC_MESSAGES/base.po | 463 ++++++++++++-------------- 3 files changed, 217 insertions(+), 248 deletions(-) diff --git a/archinstall/locales/languages.json b/archinstall/locales/languages.json index 271fbb36..8575108c 100644 --- a/archinstall/locales/languages.json +++ b/archinstall/locales/languages.json @@ -181,6 +181,6 @@ {"abbr": "yi", "lang": "Yiddish"}, {"abbr": "yo", "lang": "Yoruba"}, {"abbr": "za", "lang": "Zhuang"}, + {"abbr": "zh-CN", "lang": "Simplified Chinese", "translated_lang": "简体中文"}, {"abbr": "zh-TW", "lang": "Traditional Chinese", "translated_lang": "繁體中文"}, - {"abbr": "zh-CN", "lang": "Simplified Chinese"}, {"abbr": "zu", "lang": "Zulu"}] diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.mo b/archinstall/locales/zh-CN/LC_MESSAGES/base.mo index 7410cb7d..a07e57f3 100644 Binary files a/archinstall/locales/zh-CN/LC_MESSAGES/base.mo and b/archinstall/locales/zh-CN/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.po b/archinstall/locales/zh-CN/LC_MESSAGES/base.po index 8bda3ef0..f981a257 100644 --- a/archinstall/locales/zh-CN/LC_MESSAGES/base.po +++ b/archinstall/locales/zh-CN/LC_MESSAGES/base.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" -"Last-Translator: Xiaotian Wu \n" +"Last-Translator: wc7086 <190022474@qq.com>\n" "Language-Team: \n" "Language: zh_CN\n" "MIME-Version: 1.0\n" @@ -12,31 +12,31 @@ msgstr "" "X-Generator: Poedit 3.1.1\n" msgid "[!] A log file has been created here: {} {}" -msgstr "[!] 已在此处创建日志文件:{} {}" +msgstr "[!] 日志文件已在此处创建:{} {}" msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" msgstr " 请将此问题(和文件)提交到 https://github.com/archlinux/archinstall/issues" msgid "Do you really want to abort?" -msgstr "你真的想中止吗?" +msgstr "您真的要中止吗?" msgid "And one more time for verification: " -msgstr "还有一次验证: " +msgstr "再输入一次以进行验证:" msgid "Would you like to use swap on zram?" -msgstr "你想在 zram 上使用交换吗?" +msgstr "您想在 zram 上使用交换分区(swap)吗?" msgid "Desired hostname for the installation: " -msgstr "安装所需的主机名: " +msgstr "请输入安装后期望使用的主机名(hostname):" msgid "Username for required superuser with sudo privileges: " -msgstr "具有 sudo 权限的所需超级用户的用户名: " +msgstr "请输入需要超级用户的用户名(sudo 权限):" msgid "Any additional users to install (leave blank for no users): " -msgstr "要新建的任何其他用户(留空表示没有用户): " +msgstr "请输入要安装的其他用户(留空表示不安装其他用户):" msgid "Should this user be a superuser (sudoer)?" -msgstr "这个用户应该是超级用户(sudoer)吗?" +msgstr "这个用户应该成为超级用户(sudoer)吗?" msgid "Select a timezone" msgstr "选择时区" @@ -48,62 +48,62 @@ msgid "Choose a bootloader" msgstr "选择引导加载程序" msgid "Choose an audio server" -msgstr "选择音频服务器" +msgstr "请选择一个音频服务器(audio server)" msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." -msgstr "仅安装 base、base-devel、linux、linux-firmware、efibootmgr 和可​​选配置文件包等软件包。" +msgstr "仅安装基本软件包(base),基本开发软件包(base-devel),Linux 内核(linux),Linux 固件(linux-firmware),efibootmgr 和可选的配置文件软件包(profile packages)。" msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." -msgstr "如果你想要一个 web 浏览器,例如 firefox 或 chromium,你可以在下面的提示中指定它。" +msgstr "如果您需要一个网络浏览器,例如 firefox 或 chromium,你可以在下面的提示中指定它。" msgid "Write additional packages to install (space separated, leave blank to skip): " -msgstr "编写要安装的附加软件包(空格分隔,留空跳过): " +msgstr "输入要安装的其他软件包(空格分隔,留空以跳过):" msgid "Copy ISO network configuration to installation" -msgstr "将 ISO 网络配置复制到安装" +msgstr "将 ISO 中的网络配置复制到安装中" msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "使用 NetworkManager(在 GNOME 和 KDE 中以图形方式配置 Internet 所必需的)" msgid "Select one network interface to configure" -msgstr "选择一个网络接口进行配置" +msgstr "选择要配置的网络接口" msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" -msgstr "选择要为“{}”配置的模式或跳过以使用默认模式“{}”" +msgstr "选择要为 \"{}\" 配置的模式或跳过以使用默认模式 \"{}\"" msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " -msgstr "输入 {} 的 IP 和子网(例如:192.168.0.5/24): " +msgstr "输入 {} 的 IP 和子网(例如:192.168.0.5/24):" msgid "Enter your gateway (router) IP address or leave blank for none: " -msgstr "输入您的网关(路由器)IP 地址或留空为无: " +msgstr "请输入您的网关(路由器)IP地址,如果没有请留空:" msgid "Enter your DNS servers (space separated, blank for none): " -msgstr "输入您的 DNS 服务器(空格分隔,空白表示无): " +msgstr "请输入您的 DNS 服务器地址(以空格分隔,如果没有请留空):" msgid "Select which filesystem your main partition should use" -msgstr "选择你的主分区应该使用哪个文件系统" +msgstr "选择您的主分区应使用的文件系统" msgid "Current partition layout" -msgstr "当前分区布局" +msgstr "当前的分区布局" msgid "" "Select what to do with\n" "{}" msgstr "" -"选择要做什么\n" +"选择要执行的操作\n" "{}" msgid "Enter a desired filesystem type for the partition" msgstr "为分区输入所需的文件系统类型" msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " -msgstr "" +msgstr "输入起始位置(单位:s,GB,% 等;默认值:{}):" msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " -msgstr "" +msgstr "输入结束位置(单位:s,GB,% 等;例如:{}):" msgid "{} contains queued partitions, this will remove those, are you sure?" -msgstr "{} 包含排队分区,这将删除这些分区,您确定吗?" +msgstr "{} 包含了已排队的分区,这将会删除这些分区,您确定吗?" msgid "" "{}\n" @@ -124,10 +124,10 @@ msgstr "" "按索引选择要挂载的分区" msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * 分区挂载点是相对于安装内部的,例如 boot 应该为 /boot。" +msgstr " * 分区挂载点是相对于安装目录内部的,例如引导分区(boot)的挂载点为 /boot。" msgid "Select where to mount partition (leave blank to remove mountpoint): " -msgstr "选择挂载分区的位置(留空以删除挂载点): " +msgstr "选择要挂载分区的位置(留空表示移除挂载点):" msgid "" "{}\n" @@ -136,7 +136,7 @@ msgid "" msgstr "" "{}\n" "\n" -"选择要标记的分区以进行格式化" +"选择要格式化的分区" msgid "" "{}\n" @@ -145,7 +145,7 @@ msgid "" msgstr "" "{}\n" "\n" -"选择要标记为加密的分区" +"选择要加密的分区" msgid "" "{}\n" @@ -163,40 +163,40 @@ msgid "" msgstr "" "{}\n" "\n" -"选择要在哪个分区上设置文件系统" +"选择要设置文件系统的分区" msgid "Enter a desired filesystem type for the partition: " -msgstr "为分区输入所需的文件系统类型: " +msgstr "为分区选择所需的文件系统类型:" msgid "Archinstall language" -msgstr "Archinstall 界面语言" +msgstr "Archinstall 语言" msgid "Wipe all selected drives and use a best-effort default partition layout" -msgstr "擦除所有选定的驱动器并使用最优的默认分区布局" +msgstr "擦除所有选定的驱动器并使用最佳的默认分区布局" msgid "Select what to do with each individual drive (followed by partition usage)" -msgstr "选择如何处理每个单独的驱动器(其次是分区使用情况)" +msgstr "选择对每个单独的驱动器执行的操作(后跟分区使用情况)" msgid "Select what you wish to do with the selected block devices" -msgstr "选择您希望对所选块设备执行的操作" +msgstr "选择要对所选硬盘驱动器执行的操作" msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments" -msgstr "这是预编程配置文件的列表,它们可能使安装桌面环境之类的东西变得更容易" +msgstr "这是预先编写的配置文件列表,它们可能会使安装桌面环境等内容变得更加容易" msgid "Select keyboard layout" msgstr "选择键盘布局" msgid "Select one of the regions to download packages from" -msgstr "选择要从中下载软件包的区域之一" +msgstr "选择要从中下载软件包的区域(可多选)" msgid "Select one or more hard drives to use and configure" -msgstr "选择一个或多个硬盘驱动器来使用和配置" +msgstr "选择要使用和配置的硬盘驱动器(可多选)" msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." -msgstr "为了与您的 AMD 硬件实现最佳兼容性,您可能需要使用所有开源或 AMD / ATI 选项。" +msgstr "为了与您的 AMD 硬件实现最佳兼容性,您可能需要使用所有开源显卡驱动程序或 AMD / ATI 选项。" msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" -msgstr "为了与您的 Intel 硬件实现最佳兼容性,您可能需要使用所有开源或 Intel 选项。\n" +msgstr "为了与您的 Intel 硬件实现最佳兼容性,您可能需要使用所有开源显卡驱动程序或 Intel 选项。\n" msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" msgstr "为了与您的 Nvidia 硬件实现最佳兼容性,您可能需要使用 Nvidia 专有驱动程序。\n" @@ -208,13 +208,13 @@ msgid "" msgstr "" "\n" "\n" -"选择图形驱动程序或留空以安装所有开源驱动程序" +"选择一个显卡驱动程序,或留空以安装所有开源驱动程序" msgid "All open-source (default)" -msgstr "全部开源(默认)" +msgstr "所有开源显卡驱动程序(默认)" msgid "Choose which kernels to use or leave blank for default \"{}\"" -msgstr "选择要使用的内核或将默认“{}”留空" +msgstr "选择要使用的内核或留空以使用默认值“{}”" msgid "Choose which locale language to use" msgstr "选择要使用的语言环境" @@ -223,19 +223,19 @@ msgid "Choose which locale encoding to use" msgstr "选择要使用的语言环境编码" msgid "Select one of the values shown below: " -msgstr "选择如下所示的值之一: " +msgstr "选择以下值之一:" msgid "Select one or more of the options below: " -msgstr "选择以下一个或多个选项: " +msgstr "选择以下选项之一或多个选项:" msgid "Adding partition...." -msgstr "添加分区...." +msgstr "正在添加分区……" msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." -msgstr "您需要输入有效的文件系统类型才能继续。有关有效的文件系统类型,请参阅 `man parted`。" +msgstr "您需要输入有效的文件系统类型才能继续。请参阅 `man parted` 以获取有效的文件系统类型。" msgid "Error: Listing profiles on URL \"{}\" resulted in:" -msgstr "错误:在 URL“{}”上列出配置文件导致:" +msgstr "错误:在 URL “{}” 上列出配置文件时出错:" msgid "Error: Could not decode \"{}\" result as JSON:" msgstr "错误:无法将“{}”结果解码为 JSON:" @@ -247,10 +247,10 @@ msgid "Mirror region" msgstr "镜像区域" msgid "Locale language" -msgstr "本地语言" +msgstr "语言环境" msgid "Locale encoding" -msgstr "本地编码" +msgstr "语言环境编码" msgid "Drive(s)" msgstr "驱动器" @@ -286,16 +286,16 @@ msgid "Kernels" msgstr "内核" msgid "Additional packages" -msgstr "附加包" +msgstr "附加软件包" msgid "Network configuration" msgstr "网络配置" msgid "Automatic time sync (NTP)" -msgstr "自动时间同步 (NTP)" +msgstr "自动时间同步(NTP)" msgid "Install ({} config(s) missing)" -msgstr "安装(缺少 {} 个配置)" +msgstr "安装({} 个配置缺失)" msgid "" "You decided to skip harddrive selection\n" @@ -303,16 +303,16 @@ msgid "" "WARNING: Archinstall won't check the suitability of this setup\n" "Do you wish to continue?" msgstr "" -"你决定跳过硬盘选择\n" -"并将使用安装在 {} 上的任何驱动器设置(实验性)\n" +"您决定跳过硬盘驱动器选择\n" +"并将使用挂载在 {}(实验性)上的任何驱动器设置\n" "警告:Archinstall 不会检查此设置的适用性\n" -"你想继续吗?" +"您是否要继续?" msgid "Re-using partition instance: {}" -msgstr "重用分区实例:{}" +msgstr "正在重新使用分区实例:{}" msgid "Create a new partition" -msgstr "创建新分区" +msgstr "创建一个新分区" msgid "Delete a partition" msgstr "删除一个分区" @@ -324,13 +324,13 @@ msgid "Assign mount-point for a partition" msgstr "为分区分配挂载点" msgid "Mark/Unmark a partition to be formatted (wipes data)" -msgstr "标记/取消标记要格式化的分区(擦除数据)" +msgstr "将分区标记/取消标记为格式化(擦除数据)" msgid "Mark/Unmark a partition as encrypted" msgstr "将分区标记/取消标记为加密" msgid "Mark/Unmark a partition as bootable (automatic for /boot)" -msgstr "将分区标记/取消标记为可引导(自动为 /boot)" +msgstr "将分区标记/取消标记为可引导(/boot 自动引导)" msgid "Set desired filesystem for a partition" msgstr "为分区设置所需的文件系统" @@ -348,7 +348,7 @@ msgid "Timezone" msgstr "时区" msgid "Set/Modify the below options" -msgstr "请设置/修改以下选项" +msgstr "设置/修改以下选项" msgid "Install" msgstr "安装" @@ -357,55 +357,55 @@ msgid "" "Use ESC to skip\n" "\n" msgstr "" -"按 ESC 键跳过\n" +"按 ESC 跳过\n" "\n" msgid "Suggest partition layout" -msgstr "建议分区布局" +msgstr "建议的分区布局" msgid "Enter a password: " -msgstr "输入密码: " +msgstr "输入密码:" msgid "Enter a encryption password for {}" -msgstr "输入 {} 的加密密码" +msgstr "为 {} 输入一个加密密码" msgid "Enter disk encryption password (leave blank for no encryption): " -msgstr "输入磁盘加密密码(留空表示不加密): " +msgstr "输入磁盘加密密码(留空则不加密):" msgid "Create a required super-user with sudo privileges: " -msgstr "创建具有 sudo 权限的必需超级用户: " +msgstr "创建具有 sudo 权限的必需超级用户:" msgid "Enter root password (leave blank to disable root): " -msgstr "输入 root 密码(留空以禁用 root): " +msgstr "输入 root 密码(留空以禁用 root):" msgid "Password for user \"{}\": " -msgstr "用户“{}”的密码: " +msgstr "用户“{}”的密码:" msgid "Verifying that additional packages exist (this might take a few seconds)" -msgstr "验证是否存在其他软件包(这可能需要几秒钟)" +msgstr "正在验证附加软件包是否存在(这可能需要几秒钟)" msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" -msgstr "是否要对默认时间服务器使用自动时间同步 (NTP)?\n" +msgstr "您是否希望使用默认时间服务器进行自动时间同步(NTP)?\n" msgid "" "Hardware time and other post-configuration steps might be required in order for NTP to work.\n" "For more information, please check the Arch wiki" msgstr "" -"为了使 NTP 工作,可能需要硬件时间和其他配置后步骤。\n" -"更多信息,请查看 Arch wiki" +"可能需要硬件时间和其他后配置步骤才能使 NTP 正常工作。\n" +"有关更多信息,请查看 Arch wiki" msgid "Enter a username to create an additional user (leave blank to skip): " -msgstr "输入用户名以创建其他用户(留空以跳过): " +msgstr "输入用户名以创建其他用户(留空以跳过):" msgid "Use ESC to skip\n" -msgstr "按 ESC 键跳过\n" +msgstr "按 ESC 跳过\n" msgid "" "\n" " Choose an object from the list, and select one of the available actions for it to execute" msgstr "" "\n" -"从列表中选择一个对象,然后为其选择一个可用的操作" +" 从列表中选择一个对象,并选择要执行的操作" msgid "Cancel" msgstr "取消" @@ -432,7 +432,7 @@ msgid "Copy to new key:" msgstr "复制到新密钥:" msgid "Unknown nic type: {}. Possible values are {}" -msgstr "未知网卡类型:{}。 可能的值为 {}" +msgstr "未知的网卡类型:{}。可能的值为 {}" msgid "" "\n" @@ -442,13 +442,13 @@ msgstr "" "这是您选择的配置:" msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." -msgstr "Pacman 已经在运行,最多等待 10 分钟以使其终止。" +msgstr "Pacman 已经在运行,最多等待 10 分钟或直到它终止。" msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." -msgstr "预先存在的 pacman 锁从未退出。 请在使用 archinstall 之前清理所有现有的 pacman 会话。" +msgstr "预先存在的 pacman 锁从未退出。请在使用 archinstall 之前清理任何现有的 pacman 会话。" msgid "Choose which optional additional repositories to enable" -msgstr "选择要启用的可选附加仓库" +msgstr "选择要启用的可选附加存储库" msgid "Add a user" msgstr "添加一个用户" @@ -467,16 +467,16 @@ msgid "" "Define a new user\n" msgstr "" "\n" -"定义新用户\n" +"定义一个新用户\n" msgid "User Name : " -msgstr "用户名: " +msgstr "用户名:" msgid "Should {} be a superuser (sudoer)?" -msgstr "{} 应该是超级用户 (sudoer) 吗?" +msgstr "是否将{}设置为超级用户(sudoer)?" msgid "Define users with sudo privilege: " -msgstr "定义具有 sudo 权限的用户: " +msgstr "定义具有 sudo 权限的用户:" msgid "No network configuration" msgstr "无网络配置" @@ -491,7 +491,7 @@ msgid "" msgstr "" "{}\n" "\n" -"选择要在哪个分区上设置子卷" +"选择要设置子卷的分区" msgid "Manage btrfs subvolumes for current partition" msgstr "管理当前分区的 btrfs 子卷" @@ -515,53 +515,53 @@ msgid "Choose which configuration to save" msgstr "选择要保存的配置" msgid "Enter a directory for the configuration(s) to be saved: " -msgstr "输入要保存配置的目录: " +msgstr "输入要保存配置的目录:" msgid "Not a valid directory: {}" msgstr "不是有效的目录:{}" msgid "The password you are using seems to be weak," -msgstr "您使用的密码似乎很弱," +msgstr "您正在使用的密码似乎很弱," msgid "are you sure you want to use it?" -msgstr "你确定要使用它吗?" +msgstr "您确定要使用它吗?" msgid "Optional repositories" -msgstr "可选仓库" +msgstr "可选存储库" msgid "Save configuration" msgstr "保存配置" msgid "Missing configurations:\n" -msgstr "缺少配置:\n" +msgstr "缺少配置:\n" msgid "Either root-password or at least 1 superuser must be specified" msgstr "必须指定 root 密码或至少 1 个超级用户" msgid "Manage superuser accounts: " -msgstr "管理超级用户帐户: " +msgstr "管理超级用户账户:" msgid "Manage ordinary user accounts: " -msgstr "管理普通用户账户: " +msgstr "管理普通用户账户:" msgid " Subvolume :{:16}" -msgstr " 子卷 :{:16}" +msgstr " 子卷:{:16}" msgid " mounted at {:16}" -msgstr " 安装在 {:16}" +msgstr " 挂载在 {:16}" msgid " with option {}" -msgstr " 带选项 {}" +msgstr " 与选项 {}" msgid "" "\n" " Fill the desired values for a new subvolume \n" msgstr "" "\n" -" 为新的子卷填充所需的值\n" +" 填写新子卷所需的值 \n" msgid "Subvolume name " -msgstr "子卷名 " +msgstr "子卷名称 " msgid "Subvolume mountpoint" msgstr "子卷挂载点" @@ -576,19 +576,19 @@ msgid "Subvolume name :" msgstr "子卷名称:" msgid "Select a mount point :" -msgstr "选择挂载点:" +msgstr "选择一个挂载点:" msgid "Select the desired subvolume options " msgstr "选择所需的子卷选项 " msgid "Define users with sudo privilege, by username: " -msgstr "通过用户名定义具有 sudo 权限的用户: " +msgstr "通过用户名定义具有 sudo 权限的用户:" msgid "[!] A log file has been created here: {}" msgstr "[!] 已在此处创建日志文件:{}" msgid "Would you like to use BTRFS subvolumes with a default structure?" -msgstr "您想使用具有默认结构的 BTRFS 子卷吗?" +msgstr "您想使用带有默认结构的 BTRFS 子卷吗?" msgid "Would you like to use BTRFS compression?" msgstr "您想使用 BTRFS 压缩吗?" @@ -618,13 +618,13 @@ msgid "set: {}" msgstr "设置:{}" msgid "Manual configuration setting must be a list" -msgstr "手动配置设置必须是列表" +msgstr "手动配置设置必须是一个列表" msgid "No iface specified for manual configuration" -msgstr "没有为手动配置指定 iface" +msgstr "没有为手动配置指定网卡接口" msgid "Manual nic configuration with no auto DHCP requires an IP address" -msgstr "没有自动 DHCP 的手动 nic 配置需要 IP 地址" +msgstr "没有自动 DHCP 的手动网卡配置需要 IP 地址" msgid "Add interface" msgstr "添加接口" @@ -645,43 +645,43 @@ msgid "Mark/Unmark a partition as compressed (btrfs only)" msgstr "将分区标记/取消标记为压缩(仅限 btrfs)" msgid "The password you are using seems to be weak, are you sure you want to use it?" -msgstr "您使用的密码似乎很弱,您确定要使用吗?" +msgstr "您正在使用的密码似乎很弱,您确定要使用它吗?" msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" -msgstr "提供一系列桌面环境和平铺窗口管理器,例如 gnome, kde, sway" +msgstr "提供一系列桌面环境和平铺窗口管理器的选择,例如 gnome、kde、sway" msgid "Select your desired desktop environment" -msgstr "选择您想要的桌面环境" +msgstr "选择您所需的桌面环境" msgid "A very basic installation that allows you to customize Arch Linux as you see fit." msgstr "一个非常基本的安装,允许您根据需要自定义 Arch Linux。" msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" -msgstr "提供一系列可供安装和启用的服务器包,例如 httpd、nginx、mariadb" +msgstr "提供一系列可安装和启用的各种服务器软件包的选择,例如 httpd、nginx、mariadb" msgid "Choose which servers to install, if none then a minimal installation will be done" -msgstr "选择要安装的服务器,如果没有,则进行最小安装" +msgstr "选择要安装的服务器,如果没有,则将执行最小安装" msgid "Installs a minimal system as well as xorg and graphics drivers." -msgstr "安装最小系统以及 xorg 和图形驱动程序。" +msgstr "安装一个最小系统以及 xorg 和显卡驱动程序。" msgid "Press Enter to continue." -msgstr "按回车键继续。" +msgstr "按 Enter 继续。" msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?" -msgstr "您想 chroot 进入新创建的安装并执行安装后配置吗?" +msgstr "您是否想要 chroot 到新创建的系统内以进行安装后的配置?" msgid "Are you sure you want to reset this setting?" msgstr "您确定要重置此设置吗?" msgid "Select one or more hard drives to use and configure\n" -msgstr "选择一个或多个硬盘驱动器来使用和配置\n" +msgstr "选择要使用和配置的硬盘驱动器(可多选)\n" msgid "Any modifications to the existing setting will reset the disk layout!" msgstr "对现有设置的任何修改都将重置磁盘布局!" msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" -msgstr "如果您重置硬盘驱动器选择,这也将重置当前磁盘布局。 你确定吗?" +msgstr "如果重置硬盘驱动器选择,则当前磁盘布局也将重置。你确定吗?" msgid "Save and exit" msgstr "保存并退出" @@ -691,7 +691,7 @@ msgid "" "contains queued partitions, this will remove those, are you sure?" msgstr "" "{}\n" -"包含排队的分区,这将删除那些,你确定吗?" +"包含排队的分区,这将删除这些分区,您确定吗?" msgid "No audio server" msgstr "没有音频服务器" @@ -700,7 +700,7 @@ msgid "(default)" msgstr "(默认)" msgid "Use ESC to skip" -msgstr "按 ESC 键跳过" +msgstr "按 ESC 跳过" msgid "" "Use CTRL+C to reset current selection\n" @@ -708,28 +708,28 @@ msgid "" msgstr "使用 CTRL+C 可重置当前选项\n" msgid "Copy to: " -msgstr "复制到: " +msgstr "复制到:" msgid "Edit: " -msgstr "编辑: " +msgstr "编辑:" msgid "Key: " -msgstr "键: " +msgstr "密钥:" msgid "Edit {}: " -msgstr "编辑 {}: " +msgstr "编辑 {}:" msgid "Add: " -msgstr "添加: " +msgstr "添加:" msgid "Value: " -msgstr "值: " +msgstr "值:" msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" -msgstr "您可以跳过选择驱动器和分区并使用安装在 /mnt 的任何驱动器设置(实验性)" +msgstr "您可以跳过选择驱动器和分区,并使用任何挂载在 /mnt 上的驱动器设置(实验性)" msgid "Select one of the disks or skip and use /mnt as default" -msgstr "选择其中一个磁盘或跳过并使用 /mnt 作为默认值" +msgstr "选择一个磁盘或跳过并使用 /mnt 作为默认值" msgid "Select which partitions to mark for formatting:" msgstr "选择要标记为格式化的分区:" @@ -738,7 +738,7 @@ msgid "Use HSM to unlock encrypted drive" msgstr "使用 HSM 解锁加密驱动器" msgid "Device" -msgstr "设备" +msgstr "驱动器" msgid "Size" msgstr "大小" @@ -753,17 +753,16 @@ msgid "Either root-password or at least 1 user with sudo privileges must be spec msgstr "必须指定 root 密码或至少 1 个具有 sudo 权限的用户" msgid "Enter username (leave blank to skip): " -msgstr "输入用户名(留空跳过): " +msgstr "输入用户名(留空跳过):" msgid "The username you entered is invalid. Try again" -msgstr "您输入的用户名无效。 再试一次" +msgstr "您输入的用户名无效。请重试" msgid "Should \"{}\" be a superuser (sudo)?" -msgstr "将 \"{}\" 设置为超级用户(sudo)吗?" +msgstr "是否将“{}”设置为超级用户(sudo)?" -#, fuzzy msgid "Select which partitions to encrypt" -msgstr "选择要加密的分区:" +msgstr "选择要加密的分区" msgid "very weak" msgstr "非常弱" @@ -787,10 +786,10 @@ msgid "Delete subvolume" msgstr "删除子卷" msgid "Configured {} interfaces" -msgstr "已配置的 {} 接口" +msgstr "已配置 {} 个接口" msgid "This option enables the number of parallel downloads that can occur during installation" -msgstr "此选项启用安装期间可能发生的并行下载次数" +msgstr "此选项启用安装期间可以发生的并行下载数" msgid "" "Enter the number of parallel downloads to be enabled.\n" @@ -805,10 +804,10 @@ msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloa msgstr " - 最大值:{}(允许 {} 次并行下载,一次允许 {} 次下载)" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" -msgstr " - 最小值:1(允许 1 次并行下载,一次允许 2 次下载)" +msgstr " - 最小值:1(允许 1 个并行下载,同时允许 2 个下载)" msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" -msgstr " - 禁用/默认:0(禁用并行下载,一次只允许 1 个下载)" +msgstr " - 禁用/默认值:0(禁用并行下载,同时只允许 1 个下载)" #, fuzzy, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" @@ -818,7 +817,7 @@ msgid "Parallel Downloads" msgstr "并行下载" msgid "ESC to skip" -msgstr "按 ESC 键跳过" +msgstr "按 ESC 跳过" msgid "CTRL+C to reset" msgstr "按 CTRL+C 重置" @@ -827,284 +826,247 @@ msgid "TAB to select" msgstr "按 TAB 选择" msgid "[Default value: 0] > " -msgstr "[默认值: 0] > " +msgstr "[默认值:0] > " msgid "To be able to use this translation, please install a font manually that supports the language." -msgstr "为了能够使用此翻译,请手动安装支持该语言的字体。" +msgstr "要使用此翻译,请手动安装支持该语言的字体。" msgid "The font should be stored as {}" msgstr "字体应存储为 {}" msgid "Archinstall requires root privileges to run. See --help for more." -msgstr "" +msgstr "Archinstall 需要 root 权限才能运行。有关更多信息,请参阅 --help。" -#, fuzzy msgid "Select an execution mode" -msgstr "为“{}”选择一个操作" +msgstr "选择执行模式" msgid "Unable to fetch profile from specified url: {}" -msgstr "" +msgstr "无法从指定的 URL 获取配置文件:{}" msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" -msgstr "" +msgstr "配置文件必须具有唯一的名称,但找到具有重复名称的配置文件定义:{}" -#, fuzzy msgid "Select one or more devices to use and configure" -msgstr "选择一个或多个硬盘驱动器来使用和配置" +msgstr "选择要使用和配置的驱动器(可多选)" -#, fuzzy msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" -msgstr "如果您重置硬盘驱动器选择,这也将重置当前磁盘布局。 你确定吗?" +msgstr "如果重置硬盘驱动器选择,则当前磁盘布局也将重置。你确定吗?" -#, fuzzy msgid "Existing Partitions" -msgstr "添加分区...." +msgstr "现有分区" -#, fuzzy msgid "Select a partitioning option" -msgstr "删除一个分区" +msgstr "选择分区选项" -#, fuzzy msgid "Enter the root directory of the mounted devices: " -msgstr "输入要保存配置的目录: " +msgstr "输入已挂载设备的根目录:" -#, fuzzy msgid "Minimum capacity for /home partition: {}GiB\n" -msgstr "/home 分区的最小容量:{}GB\n" +msgstr "/home 分区的最小容量为:{}GB\n" -#, fuzzy msgid "Minimum capacity for Arch Linux partition: {}GiB" -msgstr "Arch Linux 分区的最小容量:{}GB" +msgstr "Arch Linux 分区的最小容量为:{}GB" -#, fuzzy msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" -msgstr "这是预编程配置文件的列表,它们可能使安装桌面环境之类的东西变得更容易" +msgstr "这是预先编写的配置文件列表,它们可能会使安装桌面环境等内容更容易" -#, fuzzy msgid "Current profile selection" -msgstr "当前分区布局" +msgstr "当前配置文件选择" -#, fuzzy msgid "Remove all newly added partitions" -msgstr "创建新分区" +msgstr "删除所有新添加的分区" -#, fuzzy msgid "Assign mountpoint" -msgstr "为分区分配挂载点" +msgstr "分配挂载点" -#, fuzzy msgid "Mark/Unmark to be formatted (wipes data)" -msgstr "标记/取消标记要格式化的分区(擦除数据)" +msgstr "将分区标记/取消标记为格式化(清除数据)" msgid "Mark/Unmark as bootable" -msgstr "" +msgstr "将分区标记/取消标记为可引导" msgid "Change filesystem" -msgstr "" +msgstr "更改文件系统" -#, fuzzy msgid "Mark/Unmark as compressed" -msgstr "将分区标记/取消标记为压缩(仅限 btrfs)" +msgstr "将分区标记/取消标记为压缩" -#, fuzzy msgid "Set subvolumes" -msgstr "删除子卷" +msgstr "设置子卷" -#, fuzzy msgid "Delete partition" -msgstr "删除一个分区" +msgstr "删除分区" msgid "Partition" -msgstr "" +msgstr "分区" msgid "This partition is currently encrypted, to format it a filesystem has to be specified" -msgstr "" +msgstr "此分区当前已加密,要格式化它必须指定文件系统" -#, fuzzy msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * 分区挂载点是相对于安装内部的,例如 boot 应该为 /boot。" +msgstr " * 分区挂载点是相对于安装目录内部的,例如引导分区(boot)的挂载点为 /boot。" msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." -msgstr "" +msgstr "如果设置了挂载点 /boot,则该分区也将被标记为可引导。" msgid "Mountpoint: " -msgstr "" +msgstr "挂载点:" msgid "Current free sectors on device {}:" -msgstr "" +msgstr "驱动器 {} 上当前可用的扇区:" -#, fuzzy msgid "Total sectors: {}" -msgstr "不是有效的目录:{}" +msgstr "总扇区数:{}" -#, fuzzy msgid "Enter the start sector (default: {}): " msgstr "输入起始扇区(百分比或块号,默认:{}): " -#, fuzzy msgid "Enter the end sector of the partition (percentage or block number, default: {}): " -msgstr "输入分区的结束扇区(百分比或块号,例如:{}): " +msgstr "输入分区的结束扇区(百分比或块号,默认:{}):" msgid "This will remove all newly added partitions, continue?" -msgstr "" +msgstr "这将删除所有新添加的分区,是否继续?" msgid "Partition management: {}" -msgstr "" +msgstr "分区管理:{}" msgid "Total length: {}" -msgstr "" +msgstr "总长度:{}" -#, fuzzy msgid "Encryption type" -msgstr "加密密码" +msgstr "加密类型" msgid "Partitions" -msgstr "" +msgstr "分区" msgid "No HSM devices available" -msgstr "" +msgstr "没有可用的 HSM 设备" -#, fuzzy msgid "Partitions to be encrypted" -msgstr "选择要加密的分区:" +msgstr "要加密的分区" msgid "Select disk encryption option" -msgstr "" +msgstr "选择磁盘加密选项" msgid "Select a FIDO2 device to use for HSM" -msgstr "" +msgstr "选择要用于 HSM 的 FIDO2 设备" -#, fuzzy msgid "Use a best-effort default partition layout" -msgstr "擦除所有选定的驱动器并使用最优的默认分区布局" +msgstr "使用最佳的默认分区布局" -#, fuzzy msgid "Manual Partitioning" -msgstr "手动配置" +msgstr "手动分区" -#, fuzzy msgid "Pre-mounted configuration" -msgstr "无配置" +msgstr "预挂载配置" msgid "Unknown" -msgstr "" +msgstr "未知" msgid "Partition encryption" -msgstr "" +msgstr "分区加密" msgid " ! Formatting {} in " -msgstr "" +msgstr " ! 正在格式化 {} 为 " msgid "← Back" -msgstr "" +msgstr "← 返回" msgid "Disk encryption" -msgstr "" +msgstr "磁盘加密" -#, fuzzy msgid "Configuration" -msgstr "无配置" +msgstr "配置" -#, fuzzy msgid "Password" -msgstr "Root 密码" +msgstr "密码" -#, fuzzy msgid "All settings will be reset, are you sure?" -msgstr "{} 包含排队分区,这将删除这些分区,您确定吗?" +msgstr "所有设置将被重置,你确定吗?" msgid "Back" -msgstr "" +msgstr "返回" msgid "Please chose which greeter to install for the chosen profiles: {}" -msgstr "" +msgstr "请选择要为所选配置文件安装的登录管理器:{}" msgid "Environment type: {}" -msgstr "" +msgstr "环境类型:{}" msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" -msgstr "" +msgstr "Sway 不支持 Nvidia 的专有驱动。 您可能会遇到问题,您确定要继续吗?" -#, fuzzy msgid "Installed packages" -msgstr "附加包" +msgstr "已安装的软件包" -#, fuzzy msgid "Add profile" -msgstr "配置文件" +msgstr "添加配置文件" -#, fuzzy msgid "Edit profile" -msgstr "配置文件" +msgstr "编辑配置文件" -#, fuzzy msgid "Delete profile" -msgstr "删除接口" +msgstr "删除配置文件" -#, fuzzy msgid "Profile name: " -msgstr "配置文件" +msgstr "配置文件名称:" -#, fuzzy msgid "The profile name you entered is already in use. Try again" -msgstr "您输入的用户名无效。 再试一次" +msgstr "您输入的配置文件名称已被使用。 请重试" -#, fuzzy msgid "Packages to be install with this profile (space separated, leave blank to skip): " -msgstr "编写要安装的附加软件包(空格分隔,留空跳过): " +msgstr "编写要安装的附加软件包(空格分隔,留空跳过):" -#, fuzzy msgid "Services to be enabled with this profile (space separated, leave blank to skip): " -msgstr "编写要安装的附加软件包(空格分隔,留空跳过): " +msgstr "编写要启用的附加服务(空格分隔,留空跳过):" msgid "Should this profile be enabled for installation?" -msgstr "" +msgstr "是否启用此配置文件进行安装?" msgid "Create your own" -msgstr "" +msgstr "创建自己的" -#, fuzzy msgid "" "\n" "Select a graphics driver or leave blank to install all open-source drivers" msgstr "" "\n" "\n" -"选择图形驱动程序或留空以安装所有开源驱动程序" +"选择一个显卡驱动程序,或留空以安装所有开源驱动程序" msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" -msgstr "" +msgstr "Sway 需要访问您的用户环境(硬件设备的集合,例如键盘,鼠标等)" msgid "" "\n" "\n" "Choose an option to give Sway access to your hardware" msgstr "" +"\n" +"\n" +"选择一个选项来给 Sway 提供对您硬件的访问权限" msgid "Graphics driver" -msgstr "" +msgstr "显卡驱动程序" msgid "Greeter" -msgstr "" +msgstr "登录管理器" msgid "Please chose which greeter to install" -msgstr "" +msgstr "请选择要安装的登录管理器" msgid "This is a list of pre-programmed default_profiles" -msgstr "" +msgstr "这是预设的默认配置文件列表" -#, fuzzy msgid "Disk configuration" -msgstr "无配置" +msgstr "磁盘配置" -#, fuzzy msgid "Profiles" msgstr "配置文件" msgid "Finding possible directories to save configuration files ..." -msgstr "" +msgstr "正在查找可能用于保存配置文件的目录 ..." -#, fuzzy msgid "Select directory (or directories) for saving configuration files" msgstr "选择一个或多个硬盘驱动器来使用和配置" @@ -1174,3 +1136,10 @@ msgstr " - 最大值:{}(允许 {} 次并行下载,一次允许 {} 次下 msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "输入无效! 使用有效输入重试 [1 到 {},或 0 到禁用]" + +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "使用 NetworkManager(在 GNOME 和 KDE 中以图形方式配置 Internet 所必需的)" -- cgit v1.2.3-70-g09d2 From f5936966a43374f3bdaae896e6c0945f0049d913 Mon Sep 17 00:00:00 2001 From: Lena <126529524+acuteenvy@users.noreply.github.com> Date: Mon, 12 Jun 2023 15:44:04 +0200 Subject: Added changes only to PL language (#1750) Co-authored-by: Anton Hvornum --- archinstall/locales/pl/LC_MESSAGES/base.po | 236 +++++++++++++++-------------- 1 file changed, 125 insertions(+), 111 deletions(-) diff --git a/archinstall/locales/pl/LC_MESSAGES/base.po b/archinstall/locales/pl/LC_MESSAGES/base.po index f0fda19f..49603a57 100644 --- a/archinstall/locales/pl/LC_MESSAGES/base.po +++ b/archinstall/locales/pl/LC_MESSAGES/base.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" -"Last-Translator: MedzikUser \n" +"Last-Translator: acuteenvy\n" "Language-Team: \n" "Language: pl\n" "MIME-Version: 1.0\n" @@ -18,7 +18,7 @@ msgid " Please submit this issue (and file) to https://github.com/archlinux/a msgstr " Proszę zgłosić ten błąd (i dołączyć plik) pod adresem https://github.com/archlinux/archinstall/issues" msgid "Do you really want to abort?" -msgstr "Czy napewno chcesz przerwać proces?" +msgstr "Czy na pewno chcesz przerwać?" msgid "And one more time for verification: " msgstr "I jeszcze raz w celu weryfikacji: " @@ -27,7 +27,7 @@ msgid "Would you like to use swap on zram?" msgstr "Czy chcesz używać swap w zramie?" msgid "Desired hostname for the installation: " -msgstr "Nazwa hosta użyta do instalacji: " +msgstr "Nazwa hosta dla tej instalacji: " msgid "Username for required superuser with sudo privileges: " msgstr "Nazwa użytkownika dla wymaganego superusera z uprawnieniami sudo: " @@ -36,7 +36,7 @@ msgid "Any additional users to install (leave blank for no users): " msgstr "Ewentualni użytkownicy do instalacji (pozostaw puste jeśli nie chcesz tworzyć użytkowników): " msgid "Should this user be a superuser (sudoer)?" -msgstr "Czy użytkownik powinien być superuserem (sudo)?" +msgstr "Czy użytkownik powinien być superuserem (mieć uprawnienia sudo)?" msgid "Select a timezone" msgstr "Wybierz strefę czasową" @@ -54,13 +54,13 @@ msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr msgstr "Instalowane są tylko pakiety takie jak base, base-devel, linux, linux-firmware, efibootmgr i opcjonalne pakiety profili." msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." -msgstr "Jeśli wymagana jest przeglądarka internetowa, taka jak firefox lub chromium, można ją określić w następującym oknie dialogowym." +msgstr "Jeśli potrzebujesz przeglądarki internetowej, takiej jak firefox lub chromium, możesz ją określić w następującym oknie dialogowym." msgid "Write additional packages to install (space separated, leave blank to skip): " msgstr "Wpisz dodatkowe pakiety do zainstalowania (oddzielone spacjami, pozostaw puste aby pominąć): " msgid "Copy ISO network configuration to installation" -msgstr "Skopiuj ustawienia sieciowe ISO do instalacji" +msgstr "Skopiuj ustawienia sieciowe z ISO do instalacji" msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "Użyj programu NetworkManager (niezbędne do graficznej konfiguracji Internetu w środowiskach GNOME i KDE)" @@ -69,16 +69,16 @@ msgid "Select one network interface to configure" msgstr "Wybierz jeden interfejs sieciowy do skonfigurowania" msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" -msgstr "Wybierz tryb, który ma być skonfigurowany dla \"{}\" lub pomiń, aby użyć trybu domyślnego \"{}\"" +msgstr "Wybierz który tryb ma być skonfigurowany dla \"{}\" lub pomiń, aby użyć trybu domyślnego \"{}\"" msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " -msgstr "Wprowadź adres IP i podsieć dla {}. (przykład: 192.168.0.5/24): " +msgstr "Wprowadź adres IP i podsieć dla {} (przykład: 192.168.0.5/24): " msgid "Enter your gateway (router) IP address or leave blank for none: " -msgstr "Wprowadź adres IP bramy sieciowej (routera) (pozostaw puste pole w przypadku braku adresu): " +msgstr "Wprowadź adres IP bramy sieciowej (routera) lub pozostaw puste: " msgid "Enter your DNS servers (space separated, blank for none): " -msgstr "Wpisz swoje serwery DNS (oddzielone spacjami, pozostaw puste w przypadku braku): " +msgstr "Wpisz swoje serwery DNS (oddzielone spacjami, lub pozostaw puste): " msgid "Select which filesystem your main partition should use" msgstr "Wybierz, który system plików ma być używany na partycji głównej" @@ -90,20 +90,20 @@ msgid "" "Select what to do with\n" "{}" msgstr "" -"Wybierz, co ma być zrobione z\n" +"Wybierz, co zrobić z\n" "{}" msgid "Enter a desired filesystem type for the partition" -msgstr "Wprowadź typ systemu plików dla partycji" +msgstr "Wprowadź żądany typ systemu plików dla partycji" msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " -msgstr "" +msgstr "Wybierz lokację startową (w jednostkach parted: s, GB, %, itd. ; domyślna: {}" msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " -msgstr "" +msgstr "Wybierz lokację końcową (w jednostkach parted: s, GB, %, itd. ; przykład: {}" msgid "{} contains queued partitions, this will remove those, are you sure?" -msgstr "{} zawiera partycje oczekujące w kolejce, to spowoduje ich usunięcie, czy jesteś pewien?" +msgstr "{} zawiera partycje oczekujące w kolejce, to spowoduje ich usunięcie. Czy na pewno chcesz to zrobić?" msgid "" "{}\n" @@ -112,7 +112,7 @@ msgid "" msgstr "" "{}\n" "\n" -"Wybierz według indeksu, które partycje mają zostać usunięte" +"Wybierz według indeksu, które partycje usunąć" msgid "" "{}\n" @@ -121,7 +121,7 @@ msgid "" msgstr "" "{}\n" "\n" -"Wybierz według indeksu, które partycje mają zostać zamontowane" +"Wybierz według indeksu, które partycje zamontować i gdzie" msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." msgstr " * Punkty montowania partycji są względne w stosunku do wnętrza instalacji, np. partycja startowa to /boot." @@ -169,7 +169,7 @@ msgid "Enter a desired filesystem type for the partition: " msgstr "Wprowadź typ systemu plików dla partycji: " msgid "Archinstall language" -msgstr "Język Archinstall" +msgstr "Język archinstall-a" msgid "Wipe all selected drives and use a best-effort default partition layout" msgstr "Wymaż wszystkie wybrane dyski i użyj najlepszego domyślnego układu partycji" @@ -193,13 +193,13 @@ msgid "Select one or more hard drives to use and configure" msgstr "Wybierz jeden lub więcej dysków twardych do użycia i skonfiguruj je" msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." -msgstr "Aby uzyskać najlepszą kompatybilność ze sprzętem AMD, warto skorzystać z opcji całkowicie otawrto źródłowe (open-source) lub AMD / ATI." +msgstr "Aby uzyskać najlepszą kompatybilność ze sprzętem AMD, warto skorzystać z opcji całkowicie open-source lub AMD / ATI." msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" -msgstr "Aby uzyskać najlepszą kompatybilność ze sprzętem Intel, warto skorzystać z opcji całkowicie otawrto źródłowe (open-source) lub Intel.\n" +msgstr "Aby uzyskać najlepszą kompatybilność ze sprzętem Intel, warto skorzystać z opcji całkowicie open-source lub Intel.\n" msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" -msgstr "Aby uzyskać najlepszą kompatybilność ze sprzętem firmy Nvidia, warto skorzystać z firmowego sterownika firmy Nvidia.\n" +msgstr "Aby uzyskać najlepszą kompatybilność ze sprzętem firmy Nvidia, warto skorzystać z własnościowego sterownika firmy Nvidia.\n" msgid "" "\n" @@ -208,22 +208,22 @@ msgid "" msgstr "" "\n" "\n" -"Wybierz sterownik graficzny lub pozostaw puste pole, aby zainstalować wszystkie sterowniki typu open source" +"Wybierz sterownik graficzny lub pozostaw puste pole, aby zainstalować wszystkie sterowniki open-source" msgid "All open-source (default)" -msgstr "Wszystkie otwarto źródłowe (domyślnie)" +msgstr "Wszystkie open-source (domyślnie)" msgid "Choose which kernels to use or leave blank for default \"{}\"" -msgstr "Wybierz które jądra mają być używane, lub pozostaw puste dla ustawień domyślnych \"{}\"" +msgstr "Wybierz które jądra mają być używane, lub pozostaw puste, aby użyć ustawień domyślnych \"{}\"" msgid "Choose which locale language to use" -msgstr "Wybierz które locale języka mają zostać użyte" +msgstr "Wybierz języki, których chcesz używać" msgid "Choose which locale encoding to use" -msgstr "Wybierz które locale kodowania mają zostać użyte" +msgstr "Wybierz kodowania, których chcesz używać" msgid "Select one of the values shown below: " -msgstr "Wybierz jedną z wartości przedstawionych poniżej: " +msgstr "Wybierz jedną z poniższych wartości: " msgid "Select one or more of the options below: " msgstr "Wybierz jedną lub więcej z poniższych opcji: " @@ -232,25 +232,25 @@ msgid "Adding partition...." msgstr "Dodawanie partycji..." msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." -msgstr "Aby kontynuować, musisz podać poprawny typ fs. Zobacz `man parted`, aby poznać prawidłowe typy fs." +msgstr "Aby kontynuować, musisz podać poprawny fs-type. Zobacz `man parted`, aby poznać prawidłowe opcje." msgid "Error: Listing profiles on URL \"{}\" resulted in:" -msgstr "Błąd: Lista profili z URL \"{}\":" +msgstr "Błąd: Wyśwetlanie profili z URL \"{}\" spowodowało:" msgid "Error: Could not decode \"{}\" result as JSON:" -msgstr "Błąd: Nie można dekodować \"{}\" jako JSON:" +msgstr "Błąd: Nie można zdekodować \"{}\" jako JSON:" msgid "Keyboard layout" msgstr "Układ klawiatury" msgid "Mirror region" -msgstr "Region lustra" +msgstr "Region serwerów lustrzanych" msgid "Locale language" -msgstr "Locale języka" +msgstr "Język" msgid "Locale encoding" -msgstr "Locale kodowania" +msgstr "Kodowanie" msgid "Drive(s)" msgstr "Dyski twarde" @@ -268,7 +268,7 @@ msgid "Bootloader" msgstr "Program rozruchowy" msgid "Root password" -msgstr "Hasło użytkownika root" +msgstr "Hasło roota" msgid "Superuser account" msgstr "Konto superusera" @@ -292,7 +292,7 @@ msgid "Network configuration" msgstr "Konfiguracja sieci" msgid "Automatic time sync (NTP)" -msgstr "Automatycznej synchronizacji czasu (NTP)" +msgstr "Automatyczna synchronizacja czasu (NTP)" msgid "Install ({} config(s) missing)" msgstr "Zainstaluj ({} brakujących konfiguracji)" @@ -303,8 +303,8 @@ msgid "" "WARNING: Archinstall won't check the suitability of this setup\n" "Do you wish to continue?" msgstr "" -"Zdecydowałeś się pominąć wybór dysku twardego\n" -"i użyć konfiguracji dysku zamontowanego w {} (eksperymentalne)\n" +"Zdecydowano się na pominięcie wyboru dysku twardego\n" +"i użycie konfiguracji dysku zamontowanego w {} (eksperymentalne)\n" "OSTRZEŻENIE: Archinstall nie sprawdzi poprawności tej konfiguracji\n" "Czy chcesz kontynuować?" @@ -321,16 +321,16 @@ msgid "Clear/Delete all partitions" msgstr "Wyczyść/Usuń wszystkie partycje" msgid "Assign mount-point for a partition" -msgstr "Przydzielanie punktu montowania dla partycji" +msgstr "Przydziel punkt montowania dla partycji" msgid "Mark/Unmark a partition to be formatted (wipes data)" -msgstr "Zaznacz/odznacz partycję, która ma zostać sformatowana (wymazuje dane)" +msgstr "Oznacz/odznacz partycję, która ma zostać sformatowana (wymazuje dane)" msgid "Mark/Unmark a partition as encrypted" -msgstr "Oznaczanie/odznaczanie partycji jako zaszyfrowanej" +msgstr "Oznacz/odznacz partycję jako zaszyfrowaną" msgid "Mark/Unmark a partition as bootable (automatic for /boot)" -msgstr "Oznaczanie/odznaczanie partycji jako startowe (rozruchowe/bootowalne) (automatyczne dla /boot)" +msgstr "Oznacz/odznacz partycję jako startową (rozruchową/bootowalną) (automatyczne dla /boot)" msgid "Set desired filesystem for a partition" msgstr "Ustaw system plików dla partycji" @@ -348,7 +348,7 @@ msgid "Timezone" msgstr "Strefa czasowa" msgid "Set/Modify the below options" -msgstr "Ustaw/zmodyfikuj poniższe opcje" +msgstr "Ustaw/modyfikuj poniższe opcje" msgid "Install" msgstr "Zainstaluj" @@ -361,7 +361,7 @@ msgstr "" "\n" msgid "Suggest partition layout" -msgstr "Sugerowany układ partycji" +msgstr "Zasugeruj układ partycji" msgid "Enter a password: " msgstr "Wprowadź hasło: " @@ -382,7 +382,7 @@ msgid "Password for user \"{}\": " msgstr "Hasło użytkownika \"{}\": " msgid "Verifying that additional packages exist (this might take a few seconds)" -msgstr "Sprawdzenie, czy istnieją dodatkowe pakiety (może to potrwać kilka sekund)" +msgstr "Sprawdzanie, czy istnieją dodatkowe pakiety (może to potrwać kilka sekund)" msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" msgstr "Czy chcesz korzystać z automatycznej synchronizacji czasu (NTP) z domyślnymi serwerami czasu?\n" @@ -405,7 +405,7 @@ msgid "" " Choose an object from the list, and select one of the available actions for it to execute" msgstr "" "\n" -" Wybierz obiekt z listy, a następnie wybierz jedną z dostępnych akcji do wykonania" +" Wybierz obiekt z listy, a następnie wybierz jedno z dostępnych działań do wykonania" msgid "Cancel" msgstr "Anuluj" @@ -426,13 +426,13 @@ msgid "Delete" msgstr "Usuń" msgid "Select an action for '{}'" -msgstr "Wybierz akcję dla '{}'" +msgstr "Wybierz działanie dla '{}'" msgid "Copy to new key:" msgstr "Skopiuj do nowego klucza:" msgid "Unknown nic type: {}. Possible values are {}" -msgstr "Nieznany typ niszy: {}. Możliwe wartości to {}" +msgstr "Nieznany typ nic: {}. Możliwe wartości to {}" msgid "" "\n" @@ -442,13 +442,13 @@ msgstr "" "Wybrana konfiguracja:" msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." -msgstr "Pacman jest już uruchomiony i czeka maksymalnie 10 minut na zakończenie pracy." +msgstr "Pacman jest już uruchomiony, czekam maksymalnie 10 minut na zakończenie pracy." msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." -msgstr "Istniejąca wcześniej blokada programu pacman nie została zakończona. Proszę wyczyścić wszystkie istniejące sesje pacmana przed użyciem archinstall." +msgstr "Istniejąca wcześniej blokada pacmana nie została zakończona. Proszę wyczyścić wszystkie istniejące sesje pacmana przed użyciem archinstall-a." msgid "Choose which optional additional repositories to enable" -msgstr "Wybierz, które z opcjonalnych dodatkowych repozytoriów mają być włączone" +msgstr "Wybierz, które z opcjonalnych repozytoriów chcesz włączyć" msgid "Add a user" msgstr "Dodaj użytkownika" @@ -457,7 +457,7 @@ msgid "Change password" msgstr "Zmień hasło" msgid "Promote/Demote user" -msgstr "Promuj/usuń użytkownika" +msgstr "Promuj/degraduj użytkownika" msgid "Delete User" msgstr "Usuń użytkownika" @@ -473,10 +473,10 @@ msgid "User Name : " msgstr "Nazwa użytkownika : " msgid "Should {} be a superuser (sudoer)?" -msgstr "Czy {} powinien być superuserem (sudoer)?" +msgstr "Czy użytkownik {} powinien być superuserem (mieć uprawnienia sudo)?" msgid "Define users with sudo privilege: " -msgstr "Zdefiniuj użytkowników z przywilejem sudo: " +msgstr "Zdefiniuj użytkowników z uprawnieniami sudo: " msgid "No network configuration" msgstr "Brak konfiguracji sieciowej" @@ -497,7 +497,7 @@ msgid "No configuration" msgstr "Brak konfiguracji" msgid "Save user configuration" -msgstr "Zapisz konfiguracje użytkownika" +msgstr "Zapisz konfigurację użytkownika" msgid "Save user credentials" msgstr "Zapisz dane uwierzytelniające użytkownika" @@ -515,7 +515,7 @@ msgid "Enter a directory for the configuration(s) to be saved: " msgstr "Wprowadź katalog, w którym ma zostać zapisana konfiguracja: " msgid "Not a valid directory: {}" -msgstr "Nie jest to prawidłowy katalog: {}" +msgstr "Nieprawidłowy katalog: {}" msgid "The password you are using seems to be weak," msgstr "Używane przez Ciebie hasło wydaje się być słabe," @@ -524,19 +524,19 @@ msgid "are you sure you want to use it?" msgstr "czy na pewno chcesz go używać?" msgid "Optional repositories" -msgstr "Dodatkowe repozytoria" +msgstr "Opcjonalne repozytoria" msgid "Save configuration" -msgstr "Zapisz konfiguracje" +msgstr "Zapisz konfigurację" msgid "Missing configurations:\n" -msgstr "Brak konfiguracji:\n" +msgstr "Brakujące konfiguracje:\n" msgid "Either root-password or at least 1 superuser must be specified" -msgstr "Musi być podane albo hasło root, albo co najmniej jednego superusera" +msgstr "Należy podać hasło roota lub stworzyć co najmniej jednego superusera" msgid "Manage superuser accounts: " -msgstr "Zarządzaj kontami superusera: " +msgstr "Zarządzaj kontami superuserów: " msgid "Manage ordinary user accounts: " msgstr "Zarządzaj kontami zwykłych użytkowników: " @@ -555,28 +555,28 @@ msgid "" " Fill the desired values for a new subvolume \n" msgstr "" "\n" -" Wypełnij żądane wartości dla nowego subwolumenu \n" +" Wypełnij żądane wartości dla nowego subwoluminu \n" msgid "Subvolume name " -msgstr "Nazwa subwolumenu " +msgstr "Nazwa subwoluminu " msgid "Subvolume mountpoint" -msgstr "Punkt montowania subwolumenu" +msgstr "Punkt montowania subwoluminu" msgid "Subvolume options" -msgstr "Opcje subwolumenu" +msgstr "Opcje subwoluminu" msgid "Save" msgstr "Zapisz" msgid "Subvolume name :" -msgstr "Nazwa subwolumenu :" +msgstr "Nazwa subwoluminu :" msgid "Select a mount point :" msgstr "Wybierz punkt montowania :" msgid "Select the desired subvolume options " -msgstr "Wybierz opcje subwolumenu " +msgstr "Wybierz opcje subwoluminu " msgid "Define users with sudo privilege, by username: " msgstr "Określanie użytkowników z uprawnieniami sudo według nazwy użytkownika: " @@ -585,7 +585,7 @@ msgid "[!] A log file has been created here: {}" msgstr "[!] Plik dziennika został zapisany tutaj: {}" msgid "Would you like to use BTRFS subvolumes with a default structure?" -msgstr "Czy chcesz użyć subwolumenów BTRFS z domyślną strukturą?" +msgstr "Czy chcesz użyć subwoluminów BTRFS z domyślną strukturą?" msgid "Would you like to use BTRFS compression?" msgstr "Czy chcesz użyć kompresji BTRFS?" @@ -594,7 +594,7 @@ msgid "Would you like to create a separate partition for /home?" msgstr "Czy chcesz stworzyć oddzielną partycje dla /home?" msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" -msgstr "Wybrane dyski nie mają wymaganej pojemności dla automatycznej sugestii\n" +msgstr "Wybrane dyski nie mają minimalnej wymaganej pojemności dla automatycznej sugestii\n" msgid "Minimum capacity for /home partition: {}GB\n" msgstr "Maksymalna pojemność dla partycji /home: {}GB\n" @@ -612,13 +612,13 @@ msgid "no" msgstr "nie" msgid "set: {}" -msgstr "ustawienie: {}" +msgstr "ustawiono na: {}" msgid "Manual configuration setting must be a list" msgstr "Konfiguracja ustawiona manualnie musi być listą" msgid "No iface specified for manual configuration" -msgstr "Iface nie zostało ustawione w ręcznej konfiguracji" +msgstr "Nie określono interfejsu do ręcznej konfiguracji" msgid "Manual nic configuration with no auto DHCP requires an IP address" msgstr "Ręczna konfiguracja nic bez automatycznego DHCP wymaga podania adresu IP" @@ -633,7 +633,7 @@ msgid "Delete interface" msgstr "Usuń interfejs" msgid "Select interface to add" -msgstr "Wybierz interfejs sieciowy do skonfigurowania" +msgstr "Wybierz interfejs sieciowy do dodania" msgid "Manual configuration" msgstr "Ręczna konfiguracja" @@ -645,7 +645,7 @@ msgid "The password you are using seems to be weak, are you sure you want to use msgstr "Używane przez Ciebie hasło wydaje się być słabe, czy na pewno chcesz go użyć?" msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" -msgstr "Dostarcza wybór środowisk graficznych oraz menedżerów okien, np. gnome, kde, sway" +msgstr "Dostarcza wybór środowisk graficznych oraz kafelkowych menedżerów okien, np. gnome, kde, sway" msgid "Select your desired desktop environment" msgstr "Wybierz środowisko graficzne" @@ -657,16 +657,16 @@ msgid "Provides a selection of various server packages to install and enable, e. msgstr "Dostarcza wybór różnych pakietów serwerowych do zainstalowania i uruchomienia, np. httpd, nginx, mariadb" msgid "Choose which servers to install, if none then a minimal installation will be done" -msgstr "Wybierz jakie serwery zainstalować. Jeżeli żadne, wtedy będzie użyta minimalna instalacja" +msgstr "Wybierz jakie serwery zainstalować. Jeżeli żadne, wykonana będzie minimalna instalacja" msgid "Installs a minimal system as well as xorg and graphics drivers." -msgstr "Instaluje system podstawowy z xorg i sterownikami graficznymi." +msgstr "Instaluje system podstawowy, a także xorg-a i sterowniki graficzne." msgid "Press Enter to continue." msgstr "Naciśnij Enter, aby kontynuować." msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?" -msgstr "Czy chciałbyś zchrootować do nowej instalacji i przeprowadzić wstępną konfigurację?" +msgstr "Czy chcesz zchrootować do nowej instalacji i przeprowadzić dodatkową konfigurację?" msgid "Are you sure you want to reset this setting?" msgstr "Czy na pewno chcesz zresetować to ustawienie?" @@ -678,18 +678,17 @@ msgid "Any modifications to the existing setting will reset the disk layout!" msgstr "Każda zmiana istniejących ustawień zresetuje układ dysków!" msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" -msgstr "Jeżeli zresetujesz wybór dysków, zresetujesz także obecny układ dysków. Jesteś pewny, że chcesz to zrobić?" +msgstr "Jeżeli zresetujesz wybór dysków, zresetujesz także obecny układ dysków. Czy na pewno chcesz to zrobić?" msgid "Save and exit" msgstr "Zapisz i wyjdź" -#, fuzzy msgid "" "{}\n" "contains queued partitions, this will remove those, are you sure?" msgstr "" "{}\n" -"zawiera partycje oczekujące w kolejce, to spowoduje ich usunięcie, czy jesteś pewien?" +"zawiera partycje oczekujące w kolejce, to spowoduje ich usunięcie. Czy na pewno chcesz to zrobić?" msgid "No audio server" msgstr "Brak serwera dźwięku" @@ -710,7 +709,6 @@ msgstr "" msgid "Copy to: " msgstr "Kopiuj do: " -#, fuzzy msgid "Edit: " msgstr "Edytuj: " @@ -726,9 +724,8 @@ msgstr "Dodaj: " msgid "Value: " msgstr "Wartość: " -#, fuzzy msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" -msgstr "Możesz pominąć wybór dysków i partycji i użyć dowolnego zestawu dysku zamontowanego w /mnt (eksperymentalne)" +msgstr "Możesz pominąć wybór dysku i partycjonowanie i użyć konfiguracji dysku zamontowanego w /mnt (eksperymentalne)" msgid "Select one of the disks or skip and use /mnt as default" msgstr "Wybierz jeden z dysków lub pomiń i użyj /mnt jako domyślnego" @@ -752,7 +749,7 @@ msgid "Bus-type" msgstr "Typ magistrali" msgid "Either root-password or at least 1 user with sudo privileges must be specified" -msgstr "Musisz podać hasło root lub utworzyć co najmniej jednego superusera" +msgstr "Musisz podać hasło roota lub utworzyć co najmniej jednego superusera" msgid "Enter username (leave blank to skip): " msgstr "Wprowadź nazwę użytkownika (pozostaw puste, aby pominąć): " @@ -761,11 +758,10 @@ msgid "The username you entered is invalid. Try again" msgstr "Wprowadzona nazwa użytkownika jest nieprawidłowa. Spróbuj ponownie" msgid "Should \"{}\" be a superuser (sudo)?" -msgstr "Czy \"{}\" powinien być superuserem (sudo)?" +msgstr "Czy użytkownik \"{}\" powinien być superuserem (mieć uprawnienia sudo)?" -#, fuzzy msgid "Select which partitions to encrypt" -msgstr "Wybierz partycja która ma zostać zaszyfrowana" +msgstr "Wybierz, które partycje mają zostać zaszyfrowane" msgid "very weak" msgstr "bardzo słabe" @@ -780,19 +776,19 @@ msgid "strong" msgstr "mocne" msgid "Add subvolume" -msgstr "Dodaj podwolumen" +msgstr "Dodaj subwolumin" msgid "Edit subvolume" -msgstr "Edytuj podwolumen" +msgstr "Edytuj subwolumin" msgid "Delete subvolume" -msgstr "Usuń podwolumen" +msgstr "Usuń subwolumin" msgid "Configured {} interfaces" msgstr "Skonfigurowano {} interfejsów" msgid "This option enables the number of parallel downloads that can occur during installation" -msgstr "Ta opcja pozwala określić maksymalną liczbę pobieranych plików podczas instalacji" +msgstr "Ta opcja pozwala określić maksymalną liczbę pobieranych plików podczas instalacji" #, fuzzy msgid "" @@ -812,14 +808,14 @@ msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads a msgstr " - Minimalna wartość : 1 ( Zwiększa liczbę zadań o 1, co pozwala na pobieranie 2 plików jednocześnie )" msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" -msgstr " - Wyłącz/domyślnie : 0 ( Wyłącza pobieranie równoległe, więc tylko 1 plik może być pobierany w tym czasie )" +msgstr " - Wyłącz/Domyślne : 0 ( Wyłącza pobieranie wielu plików jednocześnie, więc tylko 1 plik może być pobierany w tym samym czasie )" -#, fuzzy, python-brace-format +#, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" msgstr "Nieprawidłowa wartość! Spróbuj wprowadzić wartość od 1 do {}, lub 0 aby wyłączyć." msgid "Parallel Downloads" -msgstr "Pobieranie równoległe" +msgstr "Pobieranie kilku plików jednocześnie" msgid "ESC to skip" msgstr "Naciśnij ESC, aby pominąć" @@ -827,18 +823,17 @@ msgstr "Naciśnij ESC, aby pominąć" msgid "CTRL+C to reset" msgstr "Naciśnij Ctrl+C, aby zresetować" -#, fuzzy msgid "TAB to select" msgstr "Naciśnij Tab, aby wybrać" msgid "[Default value: 0] > " -msgstr "" +msgstr "[Domyślna wartość: 0] > " msgid "To be able to use this translation, please install a font manually that supports the language." -msgstr "" +msgstr "Aby móc skorzystać z tego tłumaczenia, proszę ręcznie zainstalować czcionkę, która obsługuje ten język." msgid "The font should be stored as {}" -msgstr "" +msgstr "Czcionka powinna być przechowana jako {}" msgid "Archinstall requires root privileges to run. See --help for more." msgstr "" @@ -961,24 +956,25 @@ msgstr "" #, fuzzy msgid "Encryption type" -msgstr "Hasło szyfrujące" +msgstr "Typ szyfrowania" msgid "Partitions" -msgstr "" +msgstr "Partycje" msgid "No HSM devices available" -msgstr "" +msgstr "Brak dostępnych urządzeń HSM" -#, fuzzy msgid "Partitions to be encrypted" -msgstr "Wybierz partycja która ma zostać zaszyfrowana" +msgstr "Partycje do zaszyfrowania" -#, fuzzy msgid "Select disk encryption option" -msgstr "Wybierz układ dysku" +msgstr "Wybierz opcję szyfrowania dysku" msgid "Select a FIDO2 device to use for HSM" -msgstr "" +msgstr "Wybierz urządzenie FIDO2 do użycia z HSM" + +msgid "All settings will be reset, are you sure?" +msgstr "Wszystkie ustawienia zostaną zresetowane. Czy na pewno chcesz to zrobić?" #, fuzzy msgid "Use a best-effort default partition layout" @@ -1002,10 +998,10 @@ msgid " ! Formatting {} in " msgstr "" msgid "← Back" -msgstr "" +msgstr "← Wstecz" msgid "Disk encryption" -msgstr "" +msgstr "Szyfrowanie dysku" #, fuzzy msgid "Configuration" @@ -1013,14 +1009,32 @@ msgstr "Brak konfiguracji" #, fuzzy msgid "Password" -msgstr "Hasło użytkownika root" +msgstr "Hasło" -#, fuzzy -msgid "All settings will be reset, are you sure?" -msgstr "{} zawiera partycje oczekujące w kolejce, to spowoduje ich usunięcie, czy jesteś pewien?" +msgid "Partition encryption" +msgstr "Szyfrowanie partycji" + +msgid "When picking a directory to save configuration files to, by default we will ignore the following folders: " +msgstr "Podczas wybierania katalogu do zapisywania plików konfiguracyjnych, domyślnie ignorowane są następujące foldery: " + +msgid "Finding possible directories to save configuration files ..." +msgstr "Znajdywanie możliwych katalogów do zapisywania plików konfiguracyjnych ..." + +msgid "Select directory (or directories) for saving configuration files" +msgstr "Wybierz jeden lub więcej katalogów do zapisywania plików konfiguracyjnych" + +msgid "" +"Do you want to save {} configuration file(s) in the following locations?\n" +"\n" +"{}" -msgid "Back" msgstr "" +"Czy chcesz zapisać {} plików konfiguracyjnych do następujących lokalizacji?\n" +"\n" +"{}" + +msgid "Saving {} configuration files to {}" +msgstr "Zapisywanie {} plików konfiguracyjnych do {}" msgid "Please chose which greeter to install for the chosen profiles: {}" msgstr "" -- cgit v1.2.3-70-g09d2 From 768cb158437ff1056976cbb7842bebfebd959a46 Mon Sep 17 00:00:00 2001 From: Rafael Fontenelle Date: Mon, 12 Jun 2023 10:49:34 -0300 Subject: Update Brazilian Portuguese translation (#1736) Co-authored-by: Anton Hvornum --- archinstall/locales/pt_BR/LC_MESSAGES/base.mo | Bin 27026 -> 28310 bytes archinstall/locales/pt_BR/LC_MESSAGES/base.po | 19 ++++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo index fdd1a73e..6deacbcd 100644 Binary files a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo and b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.po b/archinstall/locales/pt_BR/LC_MESSAGES/base.po index 54419d23..3dd0a818 100644 --- a/archinstall/locales/pt_BR/LC_MESSAGES/base.po +++ b/archinstall/locales/pt_BR/LC_MESSAGES/base.po @@ -101,10 +101,10 @@ msgid "Enter a desired filesystem type for the partition" msgstr "Digite o tipo de sistema de arquivos desejado para a partição" msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " -msgstr "" +msgstr "Digite o local inicial (em unidades do parted: s, GB, %, etc. ; padrão: {}): " msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " -msgstr "" +msgstr "Digite o local final (em unidades do parted: s, GB, %, etc. ; padrão: {}): " msgid "{} contains queued partitions, this will remove those, are you sure?" msgstr "{} contém partições em fila, isto irá removê-las, tem certeza?" @@ -810,7 +810,7 @@ msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloa msgstr " - Valor máximo : {} ( Permite {} downloads paralelos, permite {} downloads por vez )" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" -msgstr " - Valor minimo : 1 ( Permite 1 download paralelo, permite 2 downloads por vez )" +msgstr " - Valor mínimo : 1 ( Permite 1 download paralelo, permite 2 downloads por vez )" msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" msgstr " - Desativar/Padrão : 0 ( Desativa os downloads paralelos, permite apenas 1 download por vez )" @@ -967,7 +967,7 @@ msgid "Partitions" msgstr "Partições" msgid "No HSM devices available" -msgstr "Nenhum dispositivo HSM disponivel" +msgstr "Nenhum dispositivo HSM disponível" msgid "Partitions to be encrypted" msgstr "Partições a serem encriptadas" @@ -996,6 +996,9 @@ msgstr "Desconhecido" msgid "Partition encryption" msgstr "Encriptação de partição" +msgid "When picking a directory to save configuration files to, by default we will ignore the following folders: " +msgstr "Ao selecionar um diretório para salvar arquivos de configuração, por padrão nós ignoramos as seguintes pastas: " + msgid " ! Formatting {} in " msgstr " ! Formatando {} em " @@ -1109,7 +1112,7 @@ msgstr "Procurando possiveis diretórios para salvar os arquivos de configuraç #, fuzzy msgid "Select directory (or directories) for saving configuration files" -msgstr "Selecione um ou mais discos rígidos para usar e configurar" +msgstr "Selecione um ou mais diretórios para salvar arquivos de configuração" #, fuzzy msgid "Add a custom mirror" @@ -1158,10 +1161,12 @@ msgid "" "\n" "{}" msgstr "" +"Você deseja salvar arquivo(s) de configuração de {} nos locais a seguir?\n" +"\n" +"{}" -#, fuzzy msgid "Saving {} configuration files to {}" -msgstr "Salvar configuração" +msgstr "Salvando arquivos de configuração de {} para {}" #, fuzzy msgid "Mirrors" -- cgit v1.2.3-70-g09d2 From 6e06d6d7c6d2202c4efdb127775b82d0803e5b21 Mon Sep 17 00:00:00 2001 From: Roxfr <52124613+roxfr@users.noreply.github.com> Date: Mon, 12 Jun 2023 16:52:31 +0200 Subject: Resolved merge conflict (#1730) Co-authored-by: Anton Hvornum --- archinstall/locales/fr/LC_MESSAGES/base.po | 289 +++++++++++++++++++++-------- 1 file changed, 210 insertions(+), 79 deletions(-) diff --git a/archinstall/locales/fr/LC_MESSAGES/base.po b/archinstall/locales/fr/LC_MESSAGES/base.po index 9f04a081..fde7d8ac 100644 --- a/archinstall/locales/fr/LC_MESSAGES/base.po +++ b/archinstall/locales/fr/LC_MESSAGES/base.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" -"Last-Translator: \n" +"Last-Translator: Roxfr \n" "Language-Team: \n" "Language: fr\n" "MIME-Version: 1.0\n" @@ -14,8 +14,12 @@ msgstr "" msgid "[!] A log file has been created here: {} {}" msgstr "[!] Un fichier journal a été créé ici : {} {}" -msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" -msgstr " Veuillez soumettre ce problème (et le fichier) à https://github.com/archlinux/archinstall/issues" +msgid "" +" Please submit this issue (and file) to https://github.com/archlinux/" +"archinstall/issues" +msgstr "" +" Veuillez soumettre ce problème (et le fichier) à https://github.com/" +"archlinux/archinstall/issues" msgid "Do you really want to abort?" msgstr "Voulez-vous vraiment abandonner ?" @@ -30,10 +34,13 @@ msgid "Desired hostname for the installation: " msgstr "Nom d'hôte souhaité pour l'installation : " msgid "Username for required superuser with sudo privileges: " -msgstr "Nom d'utilisateur pour le superutilisateur requis avec les privilèges sudo : " +msgstr "" +"Nom d'utilisateur pour le superutilisateur requis avec les privilèges sudo : " msgid "Any additional users to install (leave blank for no users): " -msgstr "Utilisateur supplémentaire à installer (laisser vide pour aucun utilisateur) : " +msgstr "" +"Utilisateur supplémentaire à installer (laisser vide pour aucun " +"utilisateur) : " msgid "Should this user be a superuser (sudoer)?" msgstr "Cet utilisateur doit-il être un superutilisateur (sudoer) ?" @@ -42,7 +49,9 @@ msgid "Select a timezone" msgstr "Sélectionner un fuseau horaire" msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?" -msgstr "Souhaitez-vous utiliser GRUB comme chargeur de démarrage au lieu de systemd-boot ?" +msgstr "" +"Souhaitez-vous utiliser GRUB comme chargeur de démarrage au lieu de systemd-" +"boot ?" msgid "Choose a bootloader" msgstr "Choisir un chargeur de démarrage" @@ -50,14 +59,25 @@ msgstr "Choisir un chargeur de démarrage" msgid "Choose an audio server" msgstr "Choisir un serveur audio" -msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." -msgstr "Seuls les packages tels que base, base-devel, linux, linux-firmware, efibootmgr et les packages de profil optionnels sont installés." +msgid "" +"Only packages such as base, base-devel, linux, linux-firmware, efibootmgr " +"and optional profile packages are installed." +msgstr "" +"Seuls les packages tels que base, base-devel, linux, linux-firmware, " +"efibootmgr et les packages de profil optionnels sont installés." -msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." -msgstr "Si vous désirez un navigateur Web, tel que firefox ou chrome, vous pouvez le spécifier dans l'invite suivante." +msgid "" +"If you desire a web browser, such as firefox or chromium, you may specify it " +"in the following prompt." +msgstr "" +"Si vous désirez un navigateur Web, tel que firefox ou chrome, vous pouvez le " +"spécifier dans l'invite suivante." -msgid "Write additional packages to install (space separated, leave blank to skip): " -msgstr "Écrire des packages supplémentaires à installer (espaces séparés, laisser vide pour ignorer) : " +msgid "" +"Write additional packages to install (space separated, leave blank to skip): " +msgstr "" +"Écrire des packages supplémentaires à installer (espaces séparés, laisser " +"vide pour ignorer) : " msgid "Copy ISO network configuration to installation" msgstr "Copier la configuration réseau ISO dans l'installation" @@ -68,20 +88,27 @@ msgstr "Utiliser NetworkManager (nécessaire pour configurer graphiquement Inter msgid "Select one network interface to configure" msgstr "Sélectionner une interface réseau à configurer" -msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" +msgid "" +"Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" msgstr "" +"Sélectionner le mode à configurer pour \"{}\" ou ignorer pour utiliser le " +"mode par défaut \"{}\"" msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " msgstr "Entrer l'IP et le sous-réseau pour {} (exemple : 192.168.0.5/24) : " msgid "Enter your gateway (router) IP address or leave blank for none: " -msgstr "Entrer l'adresse IP de votre passerelle (routeur) ou laisser vide pour aucune : " +msgstr "" +"Entrer l'adresse IP de votre passerelle (routeur) ou laisser vide pour " +"aucune : " msgid "Enter your DNS servers (space separated, blank for none): " msgstr "Entrer vos serveurs DNS (séparés par des espaces, vide pour aucun) : " msgid "Select which filesystem your main partition should use" -msgstr "Sélectionner le système de fichiers que votre partition principale doit utiliser" +msgstr "" +"Sélectionner le système de fichiers que votre partition principale doit " +"utiliser" msgid "Current partition layout" msgstr "Disposition actuelle des partitions" @@ -96,11 +123,16 @@ msgstr "" msgid "Enter a desired filesystem type for the partition" msgstr "Entrer un type de système de fichiers souhaité pour la partition" -msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " +msgid "" +"Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " msgstr "" +"Entrer l'emplacement de départ (en unités séparées : s, Go, %, etc. ; par " +"défaut : {}) : " msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " msgstr "" +"Entrer l'emplacement de fin (en unités séparées : s, Go, %, etc. ; ex : " +"{}) : " msgid "{} contains queued partitions, this will remove those, are you sure?" msgstr "{} contient des partitions en file d'attente, cela les supprimera, êtes-vous sûr ?" @@ -123,8 +155,12 @@ msgstr "" "\n" "Sélectionner par index où et quelle partition montée" -msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * Les points de montage de la partition sont relatifs à l'intérieur de l'installation, le démarrage serait /boot par exemple." +msgid "" +" * Partition mount-points are relative to inside the installation, the boot " +"would be /boot as an example." +msgstr "" +" * Les points de montage de la partition sont relatifs à l'intérieur de " +"l'installation, le démarrage serait /boot par exemple." msgid "Select where to mount partition (leave blank to remove mountpoint): " msgstr "Sélectionner où monter la partition (laisser vide pour supprimer le point de montage) : " @@ -172,34 +208,58 @@ msgid "Archinstall language" msgstr "Langue d'Archinstall" msgid "Wipe all selected drives and use a best-effort default partition layout" -msgstr "Effacer tous les lecteurs sélectionnés et utiliser une disposition de partition par défaut optimale" +msgstr "" +"Effacer tous les lecteurs sélectionnés et utiliser une disposition de " +"partition par défaut optimale" -msgid "Select what to do with each individual drive (followed by partition usage)" -msgstr "Sélectionner ce qu'il faut faire avec chaque lecteur individuel (suivi de l'utilisation de la partition)" +msgid "" +"Select what to do with each individual drive (followed by partition usage)" +msgstr "" +"Sélectionner ce qu'il faut faire avec chaque lecteur individuel (suivi de " +"l'utilisation de la partition)" msgid "Select what you wish to do with the selected block devices" -msgstr "Sélectionner ce que vous souhaitez faire avec les périphériques de bloc sélectionnés" +msgstr "" +"Sélectionner ce que vous souhaitez faire avec les périphériques de bloc " +"sélectionnés" -msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments" -msgstr "Ceci est une liste de profils préprogrammés, ils pourraient faciliter l'installation d'outils comme les environnements de bureau" +msgid "" +"This is a list of pre-programmed profiles, they might make it easier to " +"install things like desktop environments" +msgstr "" +"Ceci est une liste de profils préprogrammés, ils pourraient faciliter " +"l'installation d'outils comme les environnements de bureau" msgid "Select keyboard layout" msgstr "Sélectionner la disposition du clavier" msgid "Select one of the regions to download packages from" -msgstr "Sélectionner l'une des régions depuis lesquelles télécharger les packages" +msgstr "" +"Sélectionner l'une des régions depuis lesquelles télécharger les packages" msgid "Select one or more hard drives to use and configure" msgstr "Sélectionner un ou plusieurs disques durs à utiliser et à configurer" -msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." -msgstr "Pour une meilleure compatibilité avec votre matériel AMD, vous pouvez utiliser les options entièrement open source ou AMD / ATI." +msgid "" +"For the best compatibility with your AMD hardware, you may want to use " +"either the all open-source or AMD / ATI options." +msgstr "" +"Pour une meilleure compatibilité avec votre matériel AMD, vous pouvez " +"utiliser les options entièrement open source ou AMD / ATI." -msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" -msgstr "Pour une compatibilité optimale avec votre matériel Intel, vous pouvez utiliser les options entièrement open source ou Intel.\n" +msgid "" +"For the best compatibility with your Intel hardware, you may want to use " +"either the all open-source or Intel options.\n" +msgstr "" +"Pour une compatibilité optimale avec votre matériel Intel, vous pouvez " +"utiliser les options entièrement open source ou Intel.\n" -msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" -msgstr "Pour une meilleure compatibilité avec votre matériel Nvidia, vous pouvez utiliser le pilote propriétaire Nvidia.\n" +msgid "" +"For the best compatibility with your Nvidia hardware, you may want to use " +"the Nvidia proprietary driver.\n" +msgstr "" +"Pour une meilleure compatibilité avec votre matériel Nvidia, vous pouvez " +"utiliser le pilote propriétaire Nvidia.\n" msgid "" "\n" @@ -208,13 +268,14 @@ msgid "" msgstr "" "\n" "\n" -"Sélectionner un pilote graphique ou laisser vide pour installer tous les pilotes open-source" +"Sélectionner un pilote graphique ou laisser vide pour installer tous les " +"pilotes open-source" msgid "All open-source (default)" msgstr "Tout open-source (par défaut)" msgid "Choose which kernels to use or leave blank for default \"{}\"" -msgstr "" +msgstr "Choisir les noyaux à utiliser ou laisser vide pour \"{}\" par défaut" msgid "Choose which locale language to use" msgstr "Choisir la langue locale à utiliser" @@ -231,8 +292,12 @@ msgstr "Sélectionner une ou plusieurs des options ci-dessous : " msgid "Adding partition...." msgstr "Ajout de la partition...." -msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." -msgstr "Vous devez entrer un type de fs valide pour continuer. Voir `man parted` pour les types de fs valides." +msgid "" +"You need to enter a valid fs-type in order to continue. See `man parted` for " +"valid fs-type's." +msgstr "" +"Vous devez entrer un type de fs valide pour continuer. Voir `man parted` " +"pour les types de fs valides." msgid "Error: Listing profiles on URL \"{}\" resulted in:" msgstr "Erreur : la liste des profils sur l'URL \"{}\" a entraîné :" @@ -305,7 +370,8 @@ msgid "" msgstr "" "Vous avez décidé d'ignorer la sélection du disque dur\n" "et vous utiliserez la configuration de disque montée sur {} (expérimental)\n" -"ATTENTION : Archinstall ne vérifiera pas l'adéquation de cette configuration\n" +"ATTENTION : Archinstall ne vérifiera pas l'adéquation de cette " +"configuration\n" "Souhaitez-vous continuer ?" msgid "Re-using partition instance: {}" @@ -330,7 +396,8 @@ msgid "Mark/Unmark a partition as encrypted" msgstr "Marquer/Démarquer une partition comme chiffrée" msgid "Mark/Unmark a partition as bootable (automatic for /boot)" -msgstr "Marquer/Démarquer une partition comme amorçable (automatique pour /boot)" +msgstr "" +"Marquer/Démarquer une partition comme amorçable (automatique pour /boot)" msgid "Set desired filesystem for a partition" msgstr "Définir le système de fichiers souhaité pour une partition" @@ -388,10 +455,19 @@ msgid "Would you like to use automatic time synchronization (NTP) with the defau msgstr "Souhaitez-vous utiliser la synchronisation automatique de l'heure (NTP) avec les serveurs de temps par défaut ?\n" msgid "" -"Hardware time and other post-configuration steps might be required in order for NTP to work.\n" +"Would you like to use automatic time synchronization (NTP) with the default " +"time servers?\n" +msgstr "" +"Souhaitez-vous utiliser la synchronisation automatique de l'heure (NTP) avec " +"les serveurs de temps par défaut ?\n" + +msgid "" +"Hardware time and other post-configuration steps might be required in order " +"for NTP to work.\n" "For more information, please check the Arch wiki" msgstr "" -"Le temps matériel et d'autres étapes de post-configuration peuvent être nécessaires pour que NTP fonctionne.\n" +"Le temps matériel et d'autres étapes de post-configuration peuvent être " +"nécessaires pour que NTP fonctionne.\n" "Pour plus d'informations, veuillez consulter le wiki Arch" msgid "Enter a username to create an additional user (leave blank to skip): " @@ -402,10 +478,12 @@ msgstr "Utiliser ESC pour ignorer\n" msgid "" "\n" -" Choose an object from the list, and select one of the available actions for it to execute" +" Choose an object from the list, and select one of the available actions for " +"it to execute" msgstr "" "\n" -"Choisir un objet dans la liste et sélectionner l'une des actions disponibles pour qu'il s'exécute" +"Choisir un objet dans la liste et sélectionner l'une des actions disponibles " +"pour qu'il s'exécute" msgid "Cancel" msgstr "Annuler" @@ -441,11 +519,18 @@ msgstr "" "\n" "Voici la configuration choisie :" -msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." -msgstr "Pacman est déjà en cours d'exécution, attendez au maximum 10 minutes pour qu'il se termine." +msgid "" +"Pacman is already running, waiting maximum 10 minutes for it to terminate." +msgstr "" +"Pacman est déjà en cours d'exécution, attendez au maximum 10 minutes pour " +"qu'il se termine." -msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." -msgstr "Le verrou pacman préexistant n'a jamais été fermé. Veuillez nettoyer toutes les sessions pacman existantes avant d'utiliser archinstall." +msgid "" +"Pre-existing pacman lock never exited. Please clean up any existing pacman " +"sessions before using archinstall." +msgstr "" +"Le verrou pacman préexistant n'a jamais été fermé. Veuillez nettoyer toutes " +"les sessions pacman existantes avant d'utiliser archinstall." msgid "Choose which optional additional repositories to enable" msgstr "Choisir les référentiels supplémentaires en option à activer" @@ -582,7 +667,8 @@ msgid "Select the desired subvolume options " msgstr "Sélectionner les options de sous-volume souhaitées " msgid "Define users with sudo privilege, by username: " -msgstr "Définir les utilisateurs avec le privilège sudo, par nom d'utilisateur : " +msgstr "" +"Définir les utilisateurs avec le privilège sudo, par nom d'utilisateur : " msgid "[!] A log file has been created here: {}" msgstr "[!] Un fichier journal a été créé ici : {}" @@ -596,8 +682,12 @@ msgstr "Souhaitez-vous utiliser la compression BTRFS ?" msgid "Would you like to create a separate partition for /home?" msgstr "Souhaitez-vous créer une partition séparée pour /home ?" -msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" -msgstr "Les disques sélectionnés n'ont pas la capacité minimale requise pour une suggestion automatique\n" +msgid "" +"The selected drives do not have the minimum capacity required for an " +"automatic suggestion\n" +msgstr "" +"Les disques sélectionnés n'ont pas la capacité minimale requise pour une " +"suggestion automatique\n" msgid "Minimum capacity for /home partition: {}GB\n" msgstr "Capacité minimale pour la partition /home : {} Go\n" @@ -624,7 +714,9 @@ msgid "No iface specified for manual configuration" msgstr "Aucun iface spécifié pour la configuration manuelle" msgid "Manual nic configuration with no auto DHCP requires an IP address" -msgstr "La configuration manuelle de la carte réseau sans DHCP automatique nécessite une adresse IP" +msgstr "" +"La configuration manuelle de la carte réseau sans DHCP automatique nécessite " +"une adresse IP" msgid "Add interface" msgstr "Ajouter une interface" @@ -647,20 +739,36 @@ msgstr "Marquer/Démarquer une partition comme compressée (btrfs uniquement)" msgid "The password you are using seems to be weak, are you sure you want to use it?" msgstr "Le mot de passe que vous utilisez semble faible, êtes-vous sûr de vouloir l'utiliser ?" -msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" -msgstr "Fournit une sélection d'environnements de bureau et de gestionnaires de fenêtres en mosaïque, par ex. gnome, kde, sway" +msgid "" +"Provides a selection of desktop environments and tiling window managers, e." +"g. gnome, kde, sway" +msgstr "" +"Fournit une sélection d'environnements de bureau et de gestionnaires de " +"fenêtres en mosaïque, par ex. gnome, kde, sway" msgid "Select your desired desktop environment" msgstr "Sélectionner l'environnement de bureau souhaité" -msgid "A very basic installation that allows you to customize Arch Linux as you see fit." -msgstr "Une installation très basique qui vous permet de personnaliser Arch Linux comme bon vous semble." +msgid "" +"A very basic installation that allows you to customize Arch Linux as you see " +"fit." +msgstr "" +"Une installation très basique qui vous permet de personnaliser Arch Linux " +"comme bon vous semble." -msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" -msgstr "Fournit une sélection de divers paquets de serveur à installer et à activer, par ex. httpd, nginx, mariadb" +msgid "" +"Provides a selection of various server packages to install and enable, e.g. " +"httpd, nginx, mariadb" +msgstr "" +"Fournit une sélection de divers paquets de serveur à installer et à activer, " +"par ex. httpd, nginx, mariadb" -msgid "Choose which servers to install, if none then a minimal installation will be done" -msgstr "Choisir les serveurs à installer, s'il n'y en a pas, une installation minimale sera effectuée" +msgid "" +"Choose which servers to install, if none then a minimal installation will be " +"done" +msgstr "" +"Choisir les serveurs à installer, s'il n'y en a pas, une installation " +"minimale sera effectuée" msgid "Installs a minimal system as well as xorg and graphics drivers." msgstr "Installe un système minimal ainsi que les pilotes graphiques et xorg." @@ -680,8 +788,12 @@ msgstr "Sélectionner un ou plusieurs disques durs à utiliser et à configurer\ msgid "Any modifications to the existing setting will reset the disk layout!" msgstr "Toute modification du paramètre existant réinitialisera la disposition du disque !" -msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" -msgstr "Si vous réinitialisez la sélection du disque dur, cela réinitialisera également la disposition actuelle du disque. Êtes-vous sûr ?" +msgid "" +"If you reset the harddrive selection this will also reset the current disk " +"layout. Are you sure?" +msgstr "" +"Si vous réinitialisez la sélection du disque dur, cela réinitialisera " +"également la disposition actuelle du disque. Êtes-vous sûr ?" msgid "Save and exit" msgstr "Sauvegarder et quitter" @@ -727,8 +839,13 @@ msgstr "Ajouter: " msgid "Value: " msgstr "Valeur: " -msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" -msgstr "Vous pouvez ignorer la sélection d'un lecteur et le partitionnement et utiliser n'importe quelle configuration de lecteur montée sur /mnt (expérimental)" +msgid "" +"You can skip selecting a drive and partitioning and use whatever drive-setup " +"is mounted at /mnt (experimental)" +msgstr "" +"Vous pouvez ignorer la sélection d'un lecteur et le partitionnement et " +"utiliser n'importe quelle configuration de lecteur montée sur /mnt " +"(expérimental)" msgid "Select one of the disks or skip and use /mnt as default" msgstr "Sélectionner l'un des disques ou ignorer et utiliser /mnt par défaut" @@ -751,8 +868,12 @@ msgstr "Espace libre" msgid "Bus-type" msgstr "Type de bus" -msgid "Either root-password or at least 1 user with sudo privileges must be specified" -msgstr "Le mot de passe root ou au moins 1 utilisateur avec des privilèges sudo doit être spécifié" +msgid "" +"Either root-password or at least 1 user with sudo privileges must be " +"specified" +msgstr "" +"Le mot de passe root ou au moins 1 utilisateur avec des privilèges sudo doit " +"être spécifié" msgid "Enter username (leave blank to skip): " msgstr "Entrer le nom d'utilisateur (laisser vide pour passer) : " @@ -790,8 +911,12 @@ msgstr "Supprimer le sous-volume" msgid "Configured {} interfaces" msgstr "Interfaces {} configurées" -msgid "This option enables the number of parallel downloads that can occur during installation" -msgstr "Cette option active le nombre de téléchargements parallèles qui peuvent se produire pendant l'installation" +msgid "" +"This option enables the number of parallel downloads that can occur during " +"installation" +msgstr "" +"Cette option active le nombre de téléchargements parallèles qui peuvent se " +"produire pendant l'installation" msgid "" "Enter the number of parallel downloads to be enabled.\n" @@ -805,8 +930,12 @@ msgstr "" msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" msgstr " - Valeur maximale : {} (Autorise {} téléchargements parallèles, autorise {} téléchargements à la fois)" -msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" -msgstr " - Valeur minimale : 1 (Autorise 1 téléchargement parallèle, autorise 2 téléchargements à la fois)" +msgid "" +" - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a " +"time )" +msgstr "" +" - Valeur minimale : 1 (Autorise 1 téléchargement parallèle, autorise 2 " +"téléchargements à la fois)" msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" msgstr " - Désactiver/Défaut : 0 (Désactive le téléchargement parallèle, n'autorise qu'un seul téléchargement à la fois)" @@ -830,8 +959,12 @@ msgstr "TAB pour sélectionner" msgid "[Default value: 0] > " msgstr "[Valeur par défaut : 0] > " -msgid "To be able to use this translation, please install a font manually that supports the language." -msgstr "Pour pouvoir utiliser cette traduction, veuillez installer manuellement une police prenant en charge la langue." +msgid "" +"To be able to use this translation, please install a font manually that " +"supports the language." +msgstr "" +"Pour pouvoir utiliser cette traduction, veuillez installer manuellement une " +"police prenant en charge la langue." msgid "The font should be stored as {}" msgstr "La police doit être stockée sous {}" @@ -957,24 +1090,22 @@ msgstr "" #, fuzzy msgid "Encryption type" -msgstr "Mot de passe de chiffrement" +msgstr "Type de chiffrement" msgid "Partitions" -msgstr "" +msgstr "Partitions" msgid "No HSM devices available" -msgstr "" +msgstr "Aucun appareil HSM disponible" -#, fuzzy msgid "Partitions to be encrypted" -msgstr "Sélectionner les partitions à chiffrer" +msgstr "Partitions à chiffrer" -#, fuzzy msgid "Select disk encryption option" -msgstr "Sélectionner la disposition du disque" +msgstr "Sélectionner l'option de chiffrement du disque" msgid "Select a FIDO2 device to use for HSM" -msgstr "" +msgstr "Sélectionner un appareil FIDO2 à utiliser pour HSM" #, fuzzy msgid "Use a best-effort default partition layout" @@ -1001,7 +1132,7 @@ msgid "← Back" msgstr "" msgid "Disk encryption" -msgstr "" +msgstr "Cryptage du disque" #, fuzzy msgid "Configuration" @@ -1009,7 +1140,7 @@ msgstr "Aucune configuration" #, fuzzy msgid "Password" -msgstr "Mot de passe root" +msgstr "Mot de passe" #, fuzzy msgid "All settings will be reset, are you sure?" -- cgit v1.2.3-70-g09d2 From 9164c63f2a75e97459e47db02f8b4d4687713022 Mon Sep 17 00:00:00 2001 From: "K.B.Dharun Krishna" Date: Tue, 13 Jun 2023 01:50:38 +0530 Subject: po: update Tamil translation (#1867) --- archinstall/locales/ta/LC_MESSAGES/base.mo | Bin 62599 -> 66233 bytes archinstall/locales/ta/LC_MESSAGES/base.po | 57 +++++++++++++---------------- 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/archinstall/locales/ta/LC_MESSAGES/base.mo b/archinstall/locales/ta/LC_MESSAGES/base.mo index 8c163da6..1f4e6155 100644 Binary files a/archinstall/locales/ta/LC_MESSAGES/base.mo and b/archinstall/locales/ta/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ta/LC_MESSAGES/base.po b/archinstall/locales/ta/LC_MESSAGES/base.po index f6b7533a..e24a1800 100644 --- a/archinstall/locales/ta/LC_MESSAGES/base.po +++ b/archinstall/locales/ta/LC_MESSAGES/base.po @@ -9,7 +9,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.2.2\n" +"X-Generator: Poedit 3.3.1\n" msgid "[!] A log file has been created here: {} {}" msgstr "[!] ஒரு பதிவு கோப்பு இங்கே உருவாக்கப்பட்டது: {} {}" @@ -811,9 +811,9 @@ msgstr " - குறைந்தபட்ச மதிப்பு : 1 (1 இ msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" msgstr " - முடக்கு/இயல்புநிலை: 0 (இணை பதிவிறக்கத்தை முடக்குகிறது, ஒரு நேரத்தில் 1 பதிவிறக்கத்தை மட்டுமே அனுமதிக்கிறது )" -#, fuzzy, python-brace-format +#, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "தவறான உள்ளீடு! சரியான உள்ளீட்டில் [1 முதல் {} வரை அல்லது முடக்க 0 வரை] மீண்டும் முயற்சிக்கவும்" +msgstr "தவறான உள்ளீடு! சரியான உள்ளீட்டில் [1 முதல் {max_downloads} வரை அல்லது முடக்க 0 வரை] மீண்டும் முயற்சிக்கவும்" msgid "Parallel Downloads" msgstr "இணையான பதிவிறக்கங்கள்" @@ -1073,76 +1073,69 @@ msgstr "உள்ளமைவு கோப்புகளைச் சேமி msgid "Select directory (or directories) for saving configuration files" msgstr "உள்ளமைவு கோப்புகளை சேமிக்க கோப்பகத்தை (அல்லது கோப்பகங்களை) தேர்ந்தெடுக்கவும்" -#, fuzzy msgid "Add a custom mirror" -msgstr "ஒரு பயனரைச் சேர்க்கவும்" +msgstr "தனிப்பயன் கண்ணாடியைச் சேர்க்கவும்" msgid "Change custom mirror" -msgstr "" +msgstr "தனிப்பயன் கண்ணாடியை மாற்றவும்" msgid "Delete custom mirror" -msgstr "" +msgstr "தனிப்பயன் கண்ணாடியை நீக்கு" -#, fuzzy msgid "Enter name (leave blank to skip): " -msgstr "பயனர்பெயரை உள்ளிடவும் (தவிர்க்க காலியாக விடவும்): " +msgstr "பெயரை உள்ளிடவும் (தவிர்க்க காலியாக விடவும்): " -#, fuzzy msgid "Enter url (leave blank to skip): " -msgstr "பயனர்பெயரை உள்ளிடவும் (தவிர்க்க காலியாக விடவும்): " +msgstr "URL ஐ உள்ளிடவும் (தவிர்க்க காலியாக விடவும்): " -#, fuzzy msgid "Select signature check option" -msgstr "சேர்க்க இடைமுகத்தைத் தேர்ந்தெடுக்கவும்" +msgstr "கையொப்ப சரிபார்ப்பு விருப்பத்தைத் தேர்ந்தெடுக்கவும்" -#, fuzzy msgid "Select signature option" -msgstr "சேர்க்க இடைமுகத்தைத் தேர்ந்தெடுக்கவும்" +msgstr "கையெழுத்து விருப்பத்தைத் தேர்ந்தெடுக்கவும்" msgid "Custom mirrors" -msgstr "" +msgstr "தனிப்பயன் கண்ணாடிகள்" msgid "Defined" -msgstr "" +msgstr "வரையறுக்கப்பட்டது" -#, fuzzy msgid "Save user configuration (including disk layout)" -msgstr "பயனர் உள்ளமைவைச் சேமிக்கவும்" +msgstr "பயனர் உள்ளமைவைச் சேமிக்கவும் (வட்டு தளவமைப்பு உட்பட)" -#, fuzzy msgid "" "Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" "Save directory: " -msgstr "உள்ளமைவு(களை) சேமிக்க ஒரு கோப்பகத்தை உள்ளிடவும்: " +msgstr "" +"உள்ளமைவு(களை) சேமிக்கப்படுவதற்கான கோப்பகத்தை உள்ளிடவும் (தாவல் நிறைவு இயக்கப்பட்டது)\n" +"கோப்பகத்தை சேமி:" msgid "" "Do you want to save {} configuration file(s) in the following location?\n" "\n" "{}" msgstr "" +"பின்வரும் இடத்தில் {} உள்ளமைவுக் கோப்பை(களை) சேமிக்க விரும்புகிறீர்களா?\n" +"\n" +"{}" -#, fuzzy msgid "Saving {} configuration files to {}" -msgstr "உள்ளமைவைச் சேமிக்கவும்" +msgstr "{} உள்ளமைவு கோப்புகளை {} இல் சேமிக்கிறது" -#, fuzzy msgid "Mirrors" -msgstr "மிரர் பிராந்தியம்" +msgstr "கண்ணாடிகள்" -#, fuzzy msgid "Mirror regions" -msgstr "மிரர் பிராந்தியம்" +msgstr "கண்ணாடிப் பகுதிகள்" -#, fuzzy msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - அதிகபட்ச மதிப்பு : {} ( {} இணையான பதிவிறக்கங்களை அனுமதிக்கிறது, ஒரே நேரத்தில் {} பதிவிறக்கங்களை அனுமதிக்கிறது )" +msgstr "- அதிகபட்ச மதிப்பு: {} ( {} இணையான பதிவிறக்கங்களை அனுமதிக்கிறது, ஒரே நேரத்தில் {max download+1} பதிவிறக்க அனுமதிக்கிறது )" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "தவறான உள்ளீடு! சரியான உள்ளீட்டில் [1 முதல் {} வரை அல்லது முடக்க 0 வரை] மீண்டும் முயற்சிக்கவும்" msgid "Locales" -msgstr "" +msgstr "மொழி குறியீடுகள்" -#, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" -msgstr "பயன்படுத்துங்கள் NetworkManager ஐப்(GNOME மற்றும் KDE இல் இணையத்தை வரைகலை முறையில் கட்டமைக்க அவசியம்)" +msgstr "NetworkManager ஐப் பயன்படுத்தவும் (GNOME மற்றும் KDE இல் இணையத்தை வரைகலை முறையில் கட்டமைக்க அவசியம்)" -- cgit v1.2.3-70-g09d2 From c8bb8d7a0c056979d2a86cacf78f5cd82faf3817 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Mon, 12 Jun 2023 16:50:11 -0400 Subject: Use pathlib in `_add_systemd_bootloader()` (#1866) --- archinstall/lib/installer.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 3fcba39c..0680bf6e 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -804,20 +804,22 @@ class Installer: SysCommand(f'/usr/bin/arch-chroot {self.target} bootctl --no-variables --esp-path=/boot install') # Ensure that the /boot/loader directory exists before we try to create files in it - if not os.path.exists(f'{self.target}/boot/loader'): - os.makedirs(f'{self.target}/boot/loader') + loader_dir = self.target / 'boot/loader' + loader_dir.mkdir(parents=True, exist_ok=True) # Modify or create a loader.conf - if os.path.isfile(f'{self.target}/boot/loader/loader.conf'): - with open(f'{self.target}/boot/loader/loader.conf', 'r') as loader: + loader_conf = loader_dir / 'loader.conf' + + try: + with loader_conf.open() as loader: loader_data = loader.read().split('\n') - else: + except FileNotFoundError: loader_data = [ f"default {self.init_time}", "timeout 15" ] - with open(f'{self.target}/boot/loader/loader.conf', 'w') as loader: + with loader_conf.open('w') as loader: for line in loader_data: if line[:8] == 'default ': loader.write(f'default {self.init_time}_{self.kernels[0]}\n') @@ -828,13 +830,13 @@ class Installer: loader.write(f"{line}\n") # Ensure that the /boot/loader/entries directory exists before we try to create files in it - if not os.path.exists(f'{self.target}/boot/loader/entries'): - os.makedirs(f'{self.target}/boot/loader/entries') + entries_dir = loader_dir / 'entries' + entries_dir.mkdir(parents=True, exist_ok=True) for kernel in self.kernels: for variant in ("", "-fallback"): # Setup the loader entry - with open(f'{self.target}/boot/loader/entries/{self.init_time}_{kernel}{variant}.conf', 'w') as entry: + with open(entries_dir / f'{self.init_time}_{kernel}{variant}.conf', 'w') as entry: entry.write('# Created by: archinstall\n') entry.write(f'# Created on: {self.init_time}\n') entry.write(f'title Arch Linux ({kernel}{variant})\n') -- cgit v1.2.3-70-g09d2 From 92b0d0f8332f5006f9e242d33809cd2c7c2248ea Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Wed, 14 Jun 2023 08:26:15 +1000 Subject: Fixes 1861 (#1870) * Fix translation files * Add github action * Update * Update --------- Co-authored-by: Daniel Girtler --- .github/workflows/translation-check.yaml | 17 + archinstall/locales/ar/LC_MESSAGES/base.mo | Bin 3802 -> 3802 bytes archinstall/locales/ar/LC_MESSAGES/base.po | 7 + archinstall/locales/base.pot | 12 +- archinstall/locales/cs/LC_MESSAGES/base.mo | Bin 25984 -> 25984 bytes archinstall/locales/cs/LC_MESSAGES/base.po | 7 + archinstall/locales/de/LC_MESSAGES/base.mo | Bin 37644 -> 37609 bytes archinstall/locales/de/LC_MESSAGES/base.po | 144 +------- archinstall/locales/el/LC_MESSAGES/base.mo | Bin 35431 -> 35431 bytes archinstall/locales/el/LC_MESSAGES/base.po | 7 + archinstall/locales/en/LC_MESSAGES/base.po | 6 + archinstall/locales/es/LC_MESSAGES/base.mo | Bin 24396 -> 24396 bytes archinstall/locales/es/LC_MESSAGES/base.po | 7 + archinstall/locales/et/LC_MESSAGES/base.mo | Bin 35636 -> 35078 bytes archinstall/locales/et/LC_MESSAGES/base.po | 36 +- archinstall/locales/fr/LC_MESSAGES/base.mo | Bin 27431 -> 28587 bytes archinstall/locales/fr/LC_MESSAGES/base.po | 280 +++++---------- archinstall/locales/id/LC_MESSAGES/base.mo | Bin 26261 -> 26261 bytes archinstall/locales/id/LC_MESSAGES/base.po | 7 + archinstall/locales/it/LC_MESSAGES/base.mo | Bin 26544 -> 26544 bytes archinstall/locales/it/LC_MESSAGES/base.po | 7 + archinstall/locales/ka/LC_MESSAGES/base.mo | Bin 44826 -> 44826 bytes archinstall/locales/ka/LC_MESSAGES/base.po | 7 + archinstall/locales/ko/LC_MESSAGES/base.mo | Bin 27224 -> 27224 bytes archinstall/locales/ko/LC_MESSAGES/base.po | 7 + archinstall/locales/nl/LC_MESSAGES/base.mo | Bin 17691 -> 17691 bytes archinstall/locales/nl/LC_MESSAGES/base.po | 7 + archinstall/locales/pl/LC_MESSAGES/base.mo | Bin 25129 -> 27783 bytes archinstall/locales/pl/LC_MESSAGES/base.po | 70 ++-- archinstall/locales/pt/LC_MESSAGES/base.mo | Bin 16336 -> 16336 bytes archinstall/locales/pt/LC_MESSAGES/base.po | 7 + archinstall/locales/pt_BR/LC_MESSAGES/base.po | 13 +- archinstall/locales/ru/LC_MESSAGES/base.po | 467 +++++++------------------- archinstall/locales/sv/LC_MESSAGES/base.mo | Bin 22732 -> 22732 bytes archinstall/locales/sv/LC_MESSAGES/base.po | 7 + archinstall/locales/uk/LC_MESSAGES/base.mo | Bin 36027 -> 36027 bytes archinstall/locales/uk/LC_MESSAGES/base.po | 7 + archinstall/locales/ur/LC_MESSAGES/base.mo | Bin 20490 -> 20490 bytes archinstall/locales/ur/LC_MESSAGES/base.po | 7 + archinstall/locales/zh-TW/LC_MESSAGES/base.mo | Bin 32093 -> 31565 bytes archinstall/locales/zh-TW/LC_MESSAGES/base.po | 66 ++-- 41 files changed, 449 insertions(+), 753 deletions(-) create mode 100644 .github/workflows/translation-check.yaml diff --git a/.github/workflows/translation-check.yaml b/.github/workflows/translation-check.yaml new file mode 100644 index 00000000..18a3b5fc --- /dev/null +++ b/.github/workflows/translation-check.yaml @@ -0,0 +1,17 @@ +on: + [ push, pull_request ]: + paths: + - 'archinstall/locales/**' +name: translation file checks +jobs: + translation-check: + runs-on: ubuntu-latest + container: + image: archlinux:latest + steps: + - uses: actions/checkout@v3 + - run: pacman --noconfirm -Syu python python-pip + - run: python -m pip install --upgrade pip + - run: cd archinstall/locales + - run: bash locales_generator.sh + - run: git diff --name-only diff --git a/archinstall/locales/ar/LC_MESSAGES/base.mo b/archinstall/locales/ar/LC_MESSAGES/base.mo index d6e9ef26..33c12f10 100644 Binary files a/archinstall/locales/ar/LC_MESSAGES/base.mo and b/archinstall/locales/ar/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ar/LC_MESSAGES/base.po b/archinstall/locales/ar/LC_MESSAGES/base.po index 5633fbe6..68e1e968 100644 --- a/archinstall/locales/ar/LC_MESSAGES/base.po +++ b/archinstall/locales/ar/LC_MESSAGES/base.po @@ -1083,3 +1083,10 @@ msgstr "" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "" + +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "استخدم مُدير الشبكة (ضروري لإعداد الإنترنت باستخدام واجهة رسومية في جنوم و كيدي)" diff --git a/archinstall/locales/base.pot b/archinstall/locales/base.pot index fd521883..5bc4ae03 100644 --- a/archinstall/locales/base.pot +++ b/archinstall/locales/base.pot @@ -63,8 +63,8 @@ msgid "Copy ISO network configuration to installation" msgstr "" msgid "" -"Use NetworkManager (necessary for configuring internet graphically in GNOME and " -"KDE)" +"Use NetworkManager (necessary for configuring internet graphically in GNOME " +"and KDE)" msgstr "" msgid "Select one network interface to configure" @@ -1153,3 +1153,11 @@ msgstr "" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "" + +msgid "Locales" +msgstr "" + +msgid "" +"Use NetworkManager (necessary to configure internet graphically in GNOME and " +"KDE)" +msgstr "" diff --git a/archinstall/locales/cs/LC_MESSAGES/base.mo b/archinstall/locales/cs/LC_MESSAGES/base.mo index d668d464..83b8b94c 100644 Binary files a/archinstall/locales/cs/LC_MESSAGES/base.mo and b/archinstall/locales/cs/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/cs/LC_MESSAGES/base.po b/archinstall/locales/cs/LC_MESSAGES/base.po index 0a3aa43c..a7eb10ec 100644 --- a/archinstall/locales/cs/LC_MESSAGES/base.po +++ b/archinstall/locales/cs/LC_MESSAGES/base.po @@ -1169,3 +1169,10 @@ msgstr " - Maximální hodnota : {} (Povolí {} paralelních stahování, povol msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "Neplatný vstup! Zkuste to, prosím, znovu s platným vstupem [1 až {}, nebo 0 pro vypnutí]" + +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Použít NetworkManager (potřebné pro grafickou konfiguraci v GNOME a KDE)" diff --git a/archinstall/locales/de/LC_MESSAGES/base.mo b/archinstall/locales/de/LC_MESSAGES/base.mo index 05cb8081..1e779cb7 100644 Binary files a/archinstall/locales/de/LC_MESSAGES/base.mo and b/archinstall/locales/de/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/de/LC_MESSAGES/base.po b/archinstall/locales/de/LC_MESSAGES/base.po index 873e740a..3ac24e3a 100644 --- a/archinstall/locales/de/LC_MESSAGES/base.po +++ b/archinstall/locales/de/LC_MESSAGES/base.po @@ -1072,61 +1072,6 @@ msgstr "Finde mögliche Pfade um Konfigurationsdateien zu speichern..." msgid "Select directory (or directories) for saving configuration files" msgstr "Ordner um Konfigurationsdateien zu erstellen auswählen" -msgid "Add a custom mirror" -msgstr "Eigenen Spiegelserver hinzufügen" - -msgid "Change custom mirror" -msgstr "Eigenen Spiegelserver ändern" - -msgid "Delete custom mirror" -msgstr "Eigenen Spiegelserver löschen" - -msgid "Enter name (leave blank to skip): " -msgstr "Namen eingeben (leer lassen zum überspringen): " - -msgid "Enter url (leave blank to skip): " -msgstr "URL eingeben (leer lassen zum überspringen): " - -msgid "Select signature check option" -msgstr "Signatursüberprüfungsoptions auswählen" - -msgid "Select signature option" -msgstr "Signatursoption auswählen" - -msgid "Custom mirrors" -msgstr "Eigener Spiegelserver" - -msgid "Defined" -msgstr "Definiert" - -msgid "Save user configuration (including disk layout)" -msgstr "Benutzerkonfiguration (mit Laufwerkslayout) speichern" - -msgid "" -"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" -"Save directory: " -msgstr "" -"Geben Sie einen Ordner an, in dem die Konfiguration(en) gespeichert werden sollen (TAB zum vervollständigen)\n" -"Ordner: " - -msgid "" -"Do you want to save {} configuration file(s) in the following location?\n" -"\n" -"{}" -msgstr "" -"Möchten Sie {} Konfigurationsdatei(en) in folgendem Ordner speichern?\n" -"\n" -"{}" - -msgid "Saving {} configuration files to {}" -msgstr "{} Konfigurationsdateien in {} speichern" - -msgid "Mirrors" -msgstr "Spiegelserver" - -msgid "Mirror regions" -msgstr "Spiegelserverregionen" - #, fuzzy msgid "Add a custom mirror" msgstr "Benutzerkonto hinzufügen" @@ -1149,9 +1094,8 @@ msgstr "Geben sie einen weiteren Benutzernamen an der angelegt werden soll (leer msgid "Select signature check option" msgstr "Laufwerke-layout auswählen" -#, fuzzy msgid "Select signature option" -msgstr "Laufwerke-layout auswählen" +msgstr "Signatursoption auswählen" msgid "Custom mirrors" msgstr "Benutzerdefinierte Spiegelserver" @@ -1159,96 +1103,33 @@ msgstr "Benutzerdefinierte Spiegelserver" msgid "Defined" msgstr "Definiert" -#, fuzzy -msgid "Mirrors" -msgstr "Mirror-region" - -#, fuzzy -msgid "Mirror regions" -msgstr "Mirror-region" - -#, fuzzy msgid "Save user configuration (including disk layout)" -msgstr "Benutzerkonfiguration speichern" +msgstr "Benutzerkonfiguration (mit Laufwerkslayout) speichern" -#, fuzzy msgid "" "Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" "Save directory: " -msgstr "Geben sie eine Ordner an wo die Konfigurationen gespeichert werden sollen: " - -msgid "" -"Do you want to save {} configuration file(s) in the following location?\n" -"\n" -"{}" msgstr "" - -#, fuzzy -msgid "Saving {} configuration files to {}" -msgstr "Konfiguration speichern" - -#, fuzzy -msgid "Add a custom mirror" -msgstr "Benutzerkonto hinzufügen" - -msgid "Change custom mirror" -msgstr "Benutzerdefinierte Spiegelserver bearbeiten" - -msgid "Delete custom mirror" -msgstr "Benutzerdefinierte Spiegelserver bearbeiten" - -#, fuzzy -msgid "Enter name (leave blank to skip): " -msgstr "Geben sie einen weiteren Benutzernamen an der angelegt werden soll (leer lassen um zu Überspringen): " - -#, fuzzy -msgid "Enter url (leave blank to skip): " -msgstr "Geben sie einen weiteren Benutzernamen an der angelegt werden soll (leer lassen um zu Überspringen): " - -#, fuzzy -msgid "Select signature check option" -msgstr "Laufwerke-layout auswählen" - -#, fuzzy -msgid "Select signature option" -msgstr "Laufwerke-layout auswählen" - -msgid "Custom mirrors" -msgstr "Benutzerdefinierte Spiegel" - -msgid "Defined" -msgstr "Definiert" - -#, fuzzy -msgid "Save user configuration (including disk layout)" -msgstr "Benutzerkonfiguration speichern" - -#, fuzzy -msgid "" -"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" -"Save directory: " -msgstr "Geben sie eine Ordner an wo die Konfigurationen gespeichert werden sollen: " +"Geben Sie einen Ordner an, in dem die Konfiguration(en) gespeichert werden sollen (TAB zum vervollständigen)\n" +"Ordner: " msgid "" "Do you want to save {} configuration file(s) in the following location?\n" "\n" "{}" msgstr "" -"Sollen {} Konfigurationsdateie(n) an der folgenden Stelle gespeichert werden?" +"Möchten Sie {} Konfigurationsdatei(en) in folgendem Ordner speichern?\n" "\n" "{}" -#, fuzzy msgid "Saving {} configuration files to {}" -msgstr "Konfiguration speichern" +msgstr "{} Konfigurationsdateien in {} speichern" -#, fuzzy msgid "Mirrors" -msgstr "Mirror-region" +msgstr "Spiegelserver" -#, fuzzy msgid "Mirror regions" -msgstr "Mirror-region" +msgstr "Spiegelserverregionen" msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" msgstr " - Maximalwert : {} ( Erlaubt {} parallele Downloads, erlaubt {max_downloads+1} Downloads gleichzeitig)" @@ -1256,8 +1137,9 @@ msgstr " - Maximalwert : {} ( Erlaubt {} parallele Downloads, erlaubt {max_do msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "Ungültige Eingabe! Erneut mit gültiger Eingabe versuchen [1 bis {}, oder 0 zum deaktivieren]" -#~ msgid "Add :" -#~ msgstr "Hinzufügen :" +msgid "Locales" +msgstr "" -#~ msgid "Value :" -#~ msgstr "Wert :" +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "NetworkManager benutzen (notwendig um Internet auf grafische Weise in GNOME und KDE einzustellen)" diff --git a/archinstall/locales/el/LC_MESSAGES/base.mo b/archinstall/locales/el/LC_MESSAGES/base.mo index 40a9c663..7a3ec8a7 100644 Binary files a/archinstall/locales/el/LC_MESSAGES/base.mo and b/archinstall/locales/el/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/el/LC_MESSAGES/base.po b/archinstall/locales/el/LC_MESSAGES/base.po index 7c04c2ca..802356ac 100644 --- a/archinstall/locales/el/LC_MESSAGES/base.po +++ b/archinstall/locales/el/LC_MESSAGES/base.po @@ -1176,3 +1176,10 @@ msgstr " - Μέγιστη τιμή : {} ( Επιτρέπει {} παράλλη msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "Μη έγκυρη είσοδος! Προσπαθήστε ξανά με μία έγκυρη είσοδο [1 μέχρι {}, ή 0 για απενεργοποίηση]" + +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Χρήση NetworkManager (απαραίτητος για τη διαμόρφωση του δικτύου γραφικά σε GNOME και KDE)" diff --git a/archinstall/locales/en/LC_MESSAGES/base.po b/archinstall/locales/en/LC_MESSAGES/base.po index ae7f080b..336a419f 100644 --- a/archinstall/locales/en/LC_MESSAGES/base.po +++ b/archinstall/locales/en/LC_MESSAGES/base.po @@ -1072,3 +1072,9 @@ msgstr "" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "" + +msgid "Locales" +msgstr "" + +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "" diff --git a/archinstall/locales/es/LC_MESSAGES/base.mo b/archinstall/locales/es/LC_MESSAGES/base.mo index 08511f1d..28b9dbe1 100644 Binary files a/archinstall/locales/es/LC_MESSAGES/base.mo and b/archinstall/locales/es/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/es/LC_MESSAGES/base.po b/archinstall/locales/es/LC_MESSAGES/base.po index f507683b..e00e0e8c 100644 --- a/archinstall/locales/es/LC_MESSAGES/base.po +++ b/archinstall/locales/es/LC_MESSAGES/base.po @@ -1175,6 +1175,13 @@ msgstr "" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "" +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Usar NetworkManager (necesario para configurar internet gráficamente en GNOME y KDE)" + #~ msgid "Add :" #~ msgstr "Añadir :" diff --git a/archinstall/locales/et/LC_MESSAGES/base.mo b/archinstall/locales/et/LC_MESSAGES/base.mo index d4a59283..24e91a77 100644 Binary files a/archinstall/locales/et/LC_MESSAGES/base.mo and b/archinstall/locales/et/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/et/LC_MESSAGES/base.po b/archinstall/locales/et/LC_MESSAGES/base.po index ca97f57c..6a1b9238 100644 --- a/archinstall/locales/et/LC_MESSAGES/base.po +++ b/archinstall/locales/et/LC_MESSAGES/base.po @@ -806,18 +806,19 @@ msgstr "Konfigureeritud {} liidesed" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "See valik võimaldab installimise ajal valida paralleelsete allalaadimiste arvu" -#, python-brace-format +#, fuzzy msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" "Sisestage lubatavate paralleelsete allalaadimiste arv.\n" " (Sisestage väärtus vahemikus 1 kuni {max_downloads})\n" "note:" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - Maksimaalne väärtus : {max_downloads} ( Võimaldab {max_downloads} paralleelset allalaadimist, lubab {max_downloads+1} allalaadimist korraga )" +#, fuzzy +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - Minimaalne väärtus : 1 ( Võimaldab 1 paralleelset allalaadimist, võimaldab 2 allalaadimist korraga )" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" msgstr " - Minimaalne väärtus : 1 ( Võimaldab 1 paralleelset allalaadimist, võimaldab 2 allalaadimist korraga )" @@ -1112,12 +1113,6 @@ msgstr "Kohandatud peegel" msgid "Defined" msgstr "Defineeritud" -msgid "Mirrors" -msgstr "Peeglid" - -msgid "Mirror regions" -msgstr "Peegel regioonid" - msgid "Save user configuration (including disk layout)" msgstr "Salvesta kasutaja konfiguratsioon (kaasa arvatud plaadi paigutus)" @@ -1139,3 +1134,24 @@ msgstr "" msgid "Saving {} configuration files to {}" msgstr "{} konfiguratsioonifailide salvestamine {}" + +msgid "Mirrors" +msgstr "Peeglid" + +msgid "Mirror regions" +msgstr "Peegel regioonid" + +#, fuzzy +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - Maksimaalne väärtus : {max_downloads} ( Võimaldab {max_downloads} paralleelset allalaadimist, lubab {max_downloads+1} allalaadimist korraga )" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "Vale sisestus! Proovige uuesti kehtiva sisendiga [1 {max_downloads} või 0 keelamiseks]." + +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Kasutage NetworkManagerit (vajalik interneti graafiliseks konfigureerimiseks GNOME-s ja KDE-s)." diff --git a/archinstall/locales/fr/LC_MESSAGES/base.mo b/archinstall/locales/fr/LC_MESSAGES/base.mo index b34ab4d1..a19c62cb 100644 Binary files a/archinstall/locales/fr/LC_MESSAGES/base.mo and b/archinstall/locales/fr/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/fr/LC_MESSAGES/base.po b/archinstall/locales/fr/LC_MESSAGES/base.po index fde7d8ac..d86a4670 100644 --- a/archinstall/locales/fr/LC_MESSAGES/base.po +++ b/archinstall/locales/fr/LC_MESSAGES/base.po @@ -14,12 +14,8 @@ msgstr "" msgid "[!] A log file has been created here: {} {}" msgstr "[!] Un fichier journal a été créé ici : {} {}" -msgid "" -" Please submit this issue (and file) to https://github.com/archlinux/" -"archinstall/issues" -msgstr "" -" Veuillez soumettre ce problème (et le fichier) à https://github.com/" -"archlinux/archinstall/issues" +msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" +msgstr " Veuillez soumettre ce problème (et le fichier) à https://github.com/archlinux/archinstall/issues" msgid "Do you really want to abort?" msgstr "Voulez-vous vraiment abandonner ?" @@ -34,13 +30,10 @@ msgid "Desired hostname for the installation: " msgstr "Nom d'hôte souhaité pour l'installation : " msgid "Username for required superuser with sudo privileges: " -msgstr "" -"Nom d'utilisateur pour le superutilisateur requis avec les privilèges sudo : " +msgstr "Nom d'utilisateur pour le superutilisateur requis avec les privilèges sudo : " msgid "Any additional users to install (leave blank for no users): " -msgstr "" -"Utilisateur supplémentaire à installer (laisser vide pour aucun " -"utilisateur) : " +msgstr "Utilisateur supplémentaire à installer (laisser vide pour aucun utilisateur) : " msgid "Should this user be a superuser (sudoer)?" msgstr "Cet utilisateur doit-il être un superutilisateur (sudoer) ?" @@ -49,9 +42,7 @@ msgid "Select a timezone" msgstr "Sélectionner un fuseau horaire" msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?" -msgstr "" -"Souhaitez-vous utiliser GRUB comme chargeur de démarrage au lieu de systemd-" -"boot ?" +msgstr "Souhaitez-vous utiliser GRUB comme chargeur de démarrage au lieu de systemd-boot ?" msgid "Choose a bootloader" msgstr "Choisir un chargeur de démarrage" @@ -59,25 +50,14 @@ msgstr "Choisir un chargeur de démarrage" msgid "Choose an audio server" msgstr "Choisir un serveur audio" -msgid "" -"Only packages such as base, base-devel, linux, linux-firmware, efibootmgr " -"and optional profile packages are installed." -msgstr "" -"Seuls les packages tels que base, base-devel, linux, linux-firmware, " -"efibootmgr et les packages de profil optionnels sont installés." +msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." +msgstr "Seuls les packages tels que base, base-devel, linux, linux-firmware, efibootmgr et les packages de profil optionnels sont installés." -msgid "" -"If you desire a web browser, such as firefox or chromium, you may specify it " -"in the following prompt." -msgstr "" -"Si vous désirez un navigateur Web, tel que firefox ou chrome, vous pouvez le " -"spécifier dans l'invite suivante." +msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." +msgstr "Si vous désirez un navigateur Web, tel que firefox ou chrome, vous pouvez le spécifier dans l'invite suivante." -msgid "" -"Write additional packages to install (space separated, leave blank to skip): " -msgstr "" -"Écrire des packages supplémentaires à installer (espaces séparés, laisser " -"vide pour ignorer) : " +msgid "Write additional packages to install (space separated, leave blank to skip): " +msgstr "Écrire des packages supplémentaires à installer (espaces séparés, laisser vide pour ignorer) : " msgid "Copy ISO network configuration to installation" msgstr "Copier la configuration réseau ISO dans l'installation" @@ -88,27 +68,20 @@ msgstr "Utiliser NetworkManager (nécessaire pour configurer graphiquement Inter msgid "Select one network interface to configure" msgstr "Sélectionner une interface réseau à configurer" -msgid "" -"Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" -msgstr "" -"Sélectionner le mode à configurer pour \"{}\" ou ignorer pour utiliser le " -"mode par défaut \"{}\"" +msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" +msgstr "Sélectionner le mode à configurer pour \"{}\" ou ignorer pour utiliser le mode par défaut \"{}\"" msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " msgstr "Entrer l'IP et le sous-réseau pour {} (exemple : 192.168.0.5/24) : " msgid "Enter your gateway (router) IP address or leave blank for none: " -msgstr "" -"Entrer l'adresse IP de votre passerelle (routeur) ou laisser vide pour " -"aucune : " +msgstr "Entrer l'adresse IP de votre passerelle (routeur) ou laisser vide pour aucune : " msgid "Enter your DNS servers (space separated, blank for none): " msgstr "Entrer vos serveurs DNS (séparés par des espaces, vide pour aucun) : " msgid "Select which filesystem your main partition should use" -msgstr "" -"Sélectionner le système de fichiers que votre partition principale doit " -"utiliser" +msgstr "Sélectionner le système de fichiers que votre partition principale doit utiliser" msgid "Current partition layout" msgstr "Disposition actuelle des partitions" @@ -123,16 +96,11 @@ msgstr "" msgid "Enter a desired filesystem type for the partition" msgstr "Entrer un type de système de fichiers souhaité pour la partition" -msgid "" -"Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " -msgstr "" -"Entrer l'emplacement de départ (en unités séparées : s, Go, %, etc. ; par " -"défaut : {}) : " +msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " +msgstr "Entrer l'emplacement de départ (en unités séparées : s, Go, %, etc. ; par défaut : {}) : " msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " -msgstr "" -"Entrer l'emplacement de fin (en unités séparées : s, Go, %, etc. ; ex : " -"{}) : " +msgstr "Entrer l'emplacement de fin (en unités séparées : s, Go, %, etc. ; ex : {}) : " msgid "{} contains queued partitions, this will remove those, are you sure?" msgstr "{} contient des partitions en file d'attente, cela les supprimera, êtes-vous sûr ?" @@ -155,12 +123,8 @@ msgstr "" "\n" "Sélectionner par index où et quelle partition montée" -msgid "" -" * Partition mount-points are relative to inside the installation, the boot " -"would be /boot as an example." -msgstr "" -" * Les points de montage de la partition sont relatifs à l'intérieur de " -"l'installation, le démarrage serait /boot par exemple." +msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Les points de montage de la partition sont relatifs à l'intérieur de l'installation, le démarrage serait /boot par exemple." msgid "Select where to mount partition (leave blank to remove mountpoint): " msgstr "Sélectionner où monter la partition (laisser vide pour supprimer le point de montage) : " @@ -208,58 +172,34 @@ msgid "Archinstall language" msgstr "Langue d'Archinstall" msgid "Wipe all selected drives and use a best-effort default partition layout" -msgstr "" -"Effacer tous les lecteurs sélectionnés et utiliser une disposition de " -"partition par défaut optimale" +msgstr "Effacer tous les lecteurs sélectionnés et utiliser une disposition de partition par défaut optimale" -msgid "" -"Select what to do with each individual drive (followed by partition usage)" -msgstr "" -"Sélectionner ce qu'il faut faire avec chaque lecteur individuel (suivi de " -"l'utilisation de la partition)" +msgid "Select what to do with each individual drive (followed by partition usage)" +msgstr "Sélectionner ce qu'il faut faire avec chaque lecteur individuel (suivi de l'utilisation de la partition)" msgid "Select what you wish to do with the selected block devices" -msgstr "" -"Sélectionner ce que vous souhaitez faire avec les périphériques de bloc " -"sélectionnés" +msgstr "Sélectionner ce que vous souhaitez faire avec les périphériques de bloc sélectionnés" -msgid "" -"This is a list of pre-programmed profiles, they might make it easier to " -"install things like desktop environments" -msgstr "" -"Ceci est une liste de profils préprogrammés, ils pourraient faciliter " -"l'installation d'outils comme les environnements de bureau" +msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments" +msgstr "Ceci est une liste de profils préprogrammés, ils pourraient faciliter l'installation d'outils comme les environnements de bureau" msgid "Select keyboard layout" msgstr "Sélectionner la disposition du clavier" msgid "Select one of the regions to download packages from" -msgstr "" -"Sélectionner l'une des régions depuis lesquelles télécharger les packages" +msgstr "Sélectionner l'une des régions depuis lesquelles télécharger les packages" msgid "Select one or more hard drives to use and configure" msgstr "Sélectionner un ou plusieurs disques durs à utiliser et à configurer" -msgid "" -"For the best compatibility with your AMD hardware, you may want to use " -"either the all open-source or AMD / ATI options." -msgstr "" -"Pour une meilleure compatibilité avec votre matériel AMD, vous pouvez " -"utiliser les options entièrement open source ou AMD / ATI." +msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." +msgstr "Pour une meilleure compatibilité avec votre matériel AMD, vous pouvez utiliser les options entièrement open source ou AMD / ATI." -msgid "" -"For the best compatibility with your Intel hardware, you may want to use " -"either the all open-source or Intel options.\n" -msgstr "" -"Pour une compatibilité optimale avec votre matériel Intel, vous pouvez " -"utiliser les options entièrement open source ou Intel.\n" +msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" +msgstr "Pour une compatibilité optimale avec votre matériel Intel, vous pouvez utiliser les options entièrement open source ou Intel.\n" -msgid "" -"For the best compatibility with your Nvidia hardware, you may want to use " -"the Nvidia proprietary driver.\n" -msgstr "" -"Pour une meilleure compatibilité avec votre matériel Nvidia, vous pouvez " -"utiliser le pilote propriétaire Nvidia.\n" +msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" +msgstr "Pour une meilleure compatibilité avec votre matériel Nvidia, vous pouvez utiliser le pilote propriétaire Nvidia.\n" msgid "" "\n" @@ -268,8 +208,7 @@ msgid "" msgstr "" "\n" "\n" -"Sélectionner un pilote graphique ou laisser vide pour installer tous les " -"pilotes open-source" +"Sélectionner un pilote graphique ou laisser vide pour installer tous les pilotes open-source" msgid "All open-source (default)" msgstr "Tout open-source (par défaut)" @@ -292,12 +231,8 @@ msgstr "Sélectionner une ou plusieurs des options ci-dessous : " msgid "Adding partition...." msgstr "Ajout de la partition...." -msgid "" -"You need to enter a valid fs-type in order to continue. See `man parted` for " -"valid fs-type's." -msgstr "" -"Vous devez entrer un type de fs valide pour continuer. Voir `man parted` " -"pour les types de fs valides." +msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." +msgstr "Vous devez entrer un type de fs valide pour continuer. Voir `man parted` pour les types de fs valides." msgid "Error: Listing profiles on URL \"{}\" resulted in:" msgstr "Erreur : la liste des profils sur l'URL \"{}\" a entraîné :" @@ -370,8 +305,7 @@ msgid "" msgstr "" "Vous avez décidé d'ignorer la sélection du disque dur\n" "et vous utiliserez la configuration de disque montée sur {} (expérimental)\n" -"ATTENTION : Archinstall ne vérifiera pas l'adéquation de cette " -"configuration\n" +"ATTENTION : Archinstall ne vérifiera pas l'adéquation de cette configuration\n" "Souhaitez-vous continuer ?" msgid "Re-using partition instance: {}" @@ -396,8 +330,7 @@ msgid "Mark/Unmark a partition as encrypted" msgstr "Marquer/Démarquer une partition comme chiffrée" msgid "Mark/Unmark a partition as bootable (automatic for /boot)" -msgstr "" -"Marquer/Démarquer une partition comme amorçable (automatique pour /boot)" +msgstr "Marquer/Démarquer une partition comme amorçable (automatique pour /boot)" msgid "Set desired filesystem for a partition" msgstr "Définir le système de fichiers souhaité pour une partition" @@ -455,19 +388,10 @@ msgid "Would you like to use automatic time synchronization (NTP) with the defau msgstr "Souhaitez-vous utiliser la synchronisation automatique de l'heure (NTP) avec les serveurs de temps par défaut ?\n" msgid "" -"Would you like to use automatic time synchronization (NTP) with the default " -"time servers?\n" -msgstr "" -"Souhaitez-vous utiliser la synchronisation automatique de l'heure (NTP) avec " -"les serveurs de temps par défaut ?\n" - -msgid "" -"Hardware time and other post-configuration steps might be required in order " -"for NTP to work.\n" +"Hardware time and other post-configuration steps might be required in order for NTP to work.\n" "For more information, please check the Arch wiki" msgstr "" -"Le temps matériel et d'autres étapes de post-configuration peuvent être " -"nécessaires pour que NTP fonctionne.\n" +"Le temps matériel et d'autres étapes de post-configuration peuvent être nécessaires pour que NTP fonctionne.\n" "Pour plus d'informations, veuillez consulter le wiki Arch" msgid "Enter a username to create an additional user (leave blank to skip): " @@ -478,12 +402,10 @@ msgstr "Utiliser ESC pour ignorer\n" msgid "" "\n" -" Choose an object from the list, and select one of the available actions for " -"it to execute" +" Choose an object from the list, and select one of the available actions for it to execute" msgstr "" "\n" -"Choisir un objet dans la liste et sélectionner l'une des actions disponibles " -"pour qu'il s'exécute" +"Choisir un objet dans la liste et sélectionner l'une des actions disponibles pour qu'il s'exécute" msgid "Cancel" msgstr "Annuler" @@ -519,18 +441,11 @@ msgstr "" "\n" "Voici la configuration choisie :" -msgid "" -"Pacman is already running, waiting maximum 10 minutes for it to terminate." -msgstr "" -"Pacman est déjà en cours d'exécution, attendez au maximum 10 minutes pour " -"qu'il se termine." +msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." +msgstr "Pacman est déjà en cours d'exécution, attendez au maximum 10 minutes pour qu'il se termine." -msgid "" -"Pre-existing pacman lock never exited. Please clean up any existing pacman " -"sessions before using archinstall." -msgstr "" -"Le verrou pacman préexistant n'a jamais été fermé. Veuillez nettoyer toutes " -"les sessions pacman existantes avant d'utiliser archinstall." +msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." +msgstr "Le verrou pacman préexistant n'a jamais été fermé. Veuillez nettoyer toutes les sessions pacman existantes avant d'utiliser archinstall." msgid "Choose which optional additional repositories to enable" msgstr "Choisir les référentiels supplémentaires en option à activer" @@ -667,8 +582,7 @@ msgid "Select the desired subvolume options " msgstr "Sélectionner les options de sous-volume souhaitées " msgid "Define users with sudo privilege, by username: " -msgstr "" -"Définir les utilisateurs avec le privilège sudo, par nom d'utilisateur : " +msgstr "Définir les utilisateurs avec le privilège sudo, par nom d'utilisateur : " msgid "[!] A log file has been created here: {}" msgstr "[!] Un fichier journal a été créé ici : {}" @@ -682,12 +596,8 @@ msgstr "Souhaitez-vous utiliser la compression BTRFS ?" msgid "Would you like to create a separate partition for /home?" msgstr "Souhaitez-vous créer une partition séparée pour /home ?" -msgid "" -"The selected drives do not have the minimum capacity required for an " -"automatic suggestion\n" -msgstr "" -"Les disques sélectionnés n'ont pas la capacité minimale requise pour une " -"suggestion automatique\n" +msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" +msgstr "Les disques sélectionnés n'ont pas la capacité minimale requise pour une suggestion automatique\n" msgid "Minimum capacity for /home partition: {}GB\n" msgstr "Capacité minimale pour la partition /home : {} Go\n" @@ -714,9 +624,7 @@ msgid "No iface specified for manual configuration" msgstr "Aucun iface spécifié pour la configuration manuelle" msgid "Manual nic configuration with no auto DHCP requires an IP address" -msgstr "" -"La configuration manuelle de la carte réseau sans DHCP automatique nécessite " -"une adresse IP" +msgstr "La configuration manuelle de la carte réseau sans DHCP automatique nécessite une adresse IP" msgid "Add interface" msgstr "Ajouter une interface" @@ -739,36 +647,20 @@ msgstr "Marquer/Démarquer une partition comme compressée (btrfs uniquement)" msgid "The password you are using seems to be weak, are you sure you want to use it?" msgstr "Le mot de passe que vous utilisez semble faible, êtes-vous sûr de vouloir l'utiliser ?" -msgid "" -"Provides a selection of desktop environments and tiling window managers, e." -"g. gnome, kde, sway" -msgstr "" -"Fournit une sélection d'environnements de bureau et de gestionnaires de " -"fenêtres en mosaïque, par ex. gnome, kde, sway" +msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" +msgstr "Fournit une sélection d'environnements de bureau et de gestionnaires de fenêtres en mosaïque, par ex. gnome, kde, sway" msgid "Select your desired desktop environment" msgstr "Sélectionner l'environnement de bureau souhaité" -msgid "" -"A very basic installation that allows you to customize Arch Linux as you see " -"fit." -msgstr "" -"Une installation très basique qui vous permet de personnaliser Arch Linux " -"comme bon vous semble." +msgid "A very basic installation that allows you to customize Arch Linux as you see fit." +msgstr "Une installation très basique qui vous permet de personnaliser Arch Linux comme bon vous semble." -msgid "" -"Provides a selection of various server packages to install and enable, e.g. " -"httpd, nginx, mariadb" -msgstr "" -"Fournit une sélection de divers paquets de serveur à installer et à activer, " -"par ex. httpd, nginx, mariadb" +msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" +msgstr "Fournit une sélection de divers paquets de serveur à installer et à activer, par ex. httpd, nginx, mariadb" -msgid "" -"Choose which servers to install, if none then a minimal installation will be " -"done" -msgstr "" -"Choisir les serveurs à installer, s'il n'y en a pas, une installation " -"minimale sera effectuée" +msgid "Choose which servers to install, if none then a minimal installation will be done" +msgstr "Choisir les serveurs à installer, s'il n'y en a pas, une installation minimale sera effectuée" msgid "Installs a minimal system as well as xorg and graphics drivers." msgstr "Installe un système minimal ainsi que les pilotes graphiques et xorg." @@ -788,12 +680,8 @@ msgstr "Sélectionner un ou plusieurs disques durs à utiliser et à configurer\ msgid "Any modifications to the existing setting will reset the disk layout!" msgstr "Toute modification du paramètre existant réinitialisera la disposition du disque !" -msgid "" -"If you reset the harddrive selection this will also reset the current disk " -"layout. Are you sure?" -msgstr "" -"Si vous réinitialisez la sélection du disque dur, cela réinitialisera " -"également la disposition actuelle du disque. Êtes-vous sûr ?" +msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" +msgstr "Si vous réinitialisez la sélection du disque dur, cela réinitialisera également la disposition actuelle du disque. Êtes-vous sûr ?" msgid "Save and exit" msgstr "Sauvegarder et quitter" @@ -839,13 +727,8 @@ msgstr "Ajouter: " msgid "Value: " msgstr "Valeur: " -msgid "" -"You can skip selecting a drive and partitioning and use whatever drive-setup " -"is mounted at /mnt (experimental)" -msgstr "" -"Vous pouvez ignorer la sélection d'un lecteur et le partitionnement et " -"utiliser n'importe quelle configuration de lecteur montée sur /mnt " -"(expérimental)" +msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" +msgstr "Vous pouvez ignorer la sélection d'un lecteur et le partitionnement et utiliser n'importe quelle configuration de lecteur montée sur /mnt (expérimental)" msgid "Select one of the disks or skip and use /mnt as default" msgstr "Sélectionner l'un des disques ou ignorer et utiliser /mnt par défaut" @@ -868,12 +751,8 @@ msgstr "Espace libre" msgid "Bus-type" msgstr "Type de bus" -msgid "" -"Either root-password or at least 1 user with sudo privileges must be " -"specified" -msgstr "" -"Le mot de passe root ou au moins 1 utilisateur avec des privilèges sudo doit " -"être spécifié" +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "Le mot de passe root ou au moins 1 utilisateur avec des privilèges sudo doit être spécifié" msgid "Enter username (leave blank to skip): " msgstr "Entrer le nom d'utilisateur (laisser vide pour passer) : " @@ -911,12 +790,8 @@ msgstr "Supprimer le sous-volume" msgid "Configured {} interfaces" msgstr "Interfaces {} configurées" -msgid "" -"This option enables the number of parallel downloads that can occur during " -"installation" -msgstr "" -"Cette option active le nombre de téléchargements parallèles qui peuvent se " -"produire pendant l'installation" +msgid "This option enables the number of parallel downloads that can occur during installation" +msgstr "Cette option active le nombre de téléchargements parallèles qui peuvent se produire pendant l'installation" msgid "" "Enter the number of parallel downloads to be enabled.\n" @@ -930,12 +805,8 @@ msgstr "" msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" msgstr " - Valeur maximale : {} (Autorise {} téléchargements parallèles, autorise {} téléchargements à la fois)" -msgid "" -" - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a " -"time )" -msgstr "" -" - Valeur minimale : 1 (Autorise 1 téléchargement parallèle, autorise 2 " -"téléchargements à la fois)" +msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" +msgstr " - Valeur minimale : 1 (Autorise 1 téléchargement parallèle, autorise 2 téléchargements à la fois)" msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" msgstr " - Désactiver/Défaut : 0 (Désactive le téléchargement parallèle, n'autorise qu'un seul téléchargement à la fois)" @@ -959,12 +830,8 @@ msgstr "TAB pour sélectionner" msgid "[Default value: 0] > " msgstr "[Valeur par défaut : 0] > " -msgid "" -"To be able to use this translation, please install a font manually that " -"supports the language." -msgstr "" -"Pour pouvoir utiliser cette traduction, veuillez installer manuellement une " -"police prenant en charge la langue." +msgid "To be able to use this translation, please install a font manually that supports the language." +msgstr "Pour pouvoir utiliser cette traduction, veuillez installer manuellement une police prenant en charge la langue." msgid "The font should be stored as {}" msgstr "La police doit être stockée sous {}" @@ -1308,6 +1175,13 @@ msgstr " - Valeur maximale : {} (Autorise {} téléchargements parallèles, auto msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "Entrée invalide ! Réessayer avec une entrée valide [1 pour {}, ou 0 pour désactiver]" +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Utiliser NetworkManager (nécessaire pour configurer graphiquement Internet dans GNOME et KDE)" + #, python-brace-format #~ msgid "Edit {origkey} :" #~ msgstr "Modifier {origkey} :" diff --git a/archinstall/locales/id/LC_MESSAGES/base.mo b/archinstall/locales/id/LC_MESSAGES/base.mo index 1d19f536..b4a35025 100644 Binary files a/archinstall/locales/id/LC_MESSAGES/base.mo and b/archinstall/locales/id/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/id/LC_MESSAGES/base.po b/archinstall/locales/id/LC_MESSAGES/base.po index 39b50564..1d203264 100644 --- a/archinstall/locales/id/LC_MESSAGES/base.po +++ b/archinstall/locales/id/LC_MESSAGES/base.po @@ -1175,3 +1175,10 @@ msgstr " - Nilai maksimum : {} ( Memungkinkan {} unduhan paralel, memungkinkan msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "Input tidak valid! Coba lagi dengan input yang valid [1 untuk {}, atau 0 untuk menonaktifkan]" + +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Gunakan NetworkManager (diperlukan untuk mengkonfigurasi internet secara grafis di GNOME dan KDE)" diff --git a/archinstall/locales/it/LC_MESSAGES/base.mo b/archinstall/locales/it/LC_MESSAGES/base.mo index 1fde5011..ccf23c39 100644 Binary files a/archinstall/locales/it/LC_MESSAGES/base.mo and b/archinstall/locales/it/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/it/LC_MESSAGES/base.po b/archinstall/locales/it/LC_MESSAGES/base.po index acb2ef11..d9aa0ad6 100644 --- a/archinstall/locales/it/LC_MESSAGES/base.po +++ b/archinstall/locales/it/LC_MESSAGES/base.po @@ -1175,3 +1175,10 @@ msgstr " - Valore massimo : {} ( Consente {} download parallelo, consente {} d msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "Input non valido! Riprova con un input valido [da 1 a {}, o 0 per disabilitare]." + +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Usa NetworkManager (necessario per configurare graficamente Internet in GNOME e KDE)" diff --git a/archinstall/locales/ka/LC_MESSAGES/base.mo b/archinstall/locales/ka/LC_MESSAGES/base.mo index dfb1dc92..04c94cd0 100644 Binary files a/archinstall/locales/ka/LC_MESSAGES/base.mo and b/archinstall/locales/ka/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ka/LC_MESSAGES/base.po b/archinstall/locales/ka/LC_MESSAGES/base.po index 727730c3..9813aa96 100644 --- a/archinstall/locales/ka/LC_MESSAGES/base.po +++ b/archinstall/locales/ka/LC_MESSAGES/base.po @@ -1174,3 +1174,10 @@ msgstr " - მინიმალური მნიშვნელობა : msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "შეყვანილი რიცხვი არასწორია! თავიდან სცადეთ [1-დან {}-მდე, ან 0, გასათიშად]" + +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "NetworkManager-ის გამოყენება (აუცილებელია ინტერნეტის GNOME/KDE-დან მოსარგებად)" diff --git a/archinstall/locales/ko/LC_MESSAGES/base.mo b/archinstall/locales/ko/LC_MESSAGES/base.mo index dc3fa713..098713fa 100644 Binary files a/archinstall/locales/ko/LC_MESSAGES/base.mo and b/archinstall/locales/ko/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ko/LC_MESSAGES/base.po b/archinstall/locales/ko/LC_MESSAGES/base.po index 0a26aeed..e77fb2c9 100644 --- a/archinstall/locales/ko/LC_MESSAGES/base.po +++ b/archinstall/locales/ko/LC_MESSAGES/base.po @@ -1176,3 +1176,10 @@ msgstr " - 최댓값 : {} ( {} 개의 병렬 다운로드 허용, 한 번에 { msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "잘못된 값입니다! 유효한 값으로 다시 시도해주세요 [1 부터 {} 까지, 비활성화 하려면 0]" + +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "NetworkManager 사용 (GNOME 이나 KDE 에서 그래픽으로 인터넷을 구성하는 데 필요)" diff --git a/archinstall/locales/nl/LC_MESSAGES/base.mo b/archinstall/locales/nl/LC_MESSAGES/base.mo index 4edd6bc9..6f33f8ba 100644 Binary files a/archinstall/locales/nl/LC_MESSAGES/base.mo and b/archinstall/locales/nl/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/nl/LC_MESSAGES/base.po b/archinstall/locales/nl/LC_MESSAGES/base.po index f9753866..0a79fd85 100644 --- a/archinstall/locales/nl/LC_MESSAGES/base.po +++ b/archinstall/locales/nl/LC_MESSAGES/base.po @@ -1200,6 +1200,13 @@ msgstr "" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "" +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "NetworkManager gebruiken (benodigd om internetinstellingen grafisch in te stellen in GNOME en KDE)" + #~ msgid "Add :" #~ msgstr "Toevoegen:" diff --git a/archinstall/locales/pl/LC_MESSAGES/base.mo b/archinstall/locales/pl/LC_MESSAGES/base.mo index ed3e216d..137b7c06 100644 Binary files a/archinstall/locales/pl/LC_MESSAGES/base.mo and b/archinstall/locales/pl/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pl/LC_MESSAGES/base.po b/archinstall/locales/pl/LC_MESSAGES/base.po index 49603a57..f5a99b23 100644 --- a/archinstall/locales/pl/LC_MESSAGES/base.po +++ b/archinstall/locales/pl/LC_MESSAGES/base.po @@ -973,9 +973,6 @@ msgstr "Wybierz opcję szyfrowania dysku" msgid "Select a FIDO2 device to use for HSM" msgstr "Wybierz urządzenie FIDO2 do użycia z HSM" -msgid "All settings will be reset, are you sure?" -msgstr "Wszystkie ustawienia zostaną zresetowane. Czy na pewno chcesz to zrobić?" - #, fuzzy msgid "Use a best-effort default partition layout" msgstr "Wymaż wszystkie wybrane dyski i użyj najlepszego domyślnego układu partycji" @@ -992,7 +989,7 @@ msgid "Unknown" msgstr "" msgid "Partition encryption" -msgstr "" +msgstr "Szyfrowanie partycji" msgid " ! Formatting {} in " msgstr "" @@ -1011,30 +1008,12 @@ msgstr "Brak konfiguracji" msgid "Password" msgstr "Hasło" -msgid "Partition encryption" -msgstr "Szyfrowanie partycji" - -msgid "When picking a directory to save configuration files to, by default we will ignore the following folders: " -msgstr "Podczas wybierania katalogu do zapisywania plików konfiguracyjnych, domyślnie ignorowane są następujące foldery: " - -msgid "Finding possible directories to save configuration files ..." -msgstr "Znajdywanie możliwych katalogów do zapisywania plików konfiguracyjnych ..." - -msgid "Select directory (or directories) for saving configuration files" -msgstr "Wybierz jeden lub więcej katalogów do zapisywania plików konfiguracyjnych" - -msgid "" -"Do you want to save {} configuration file(s) in the following locations?\n" -"\n" -"{}" - -msgstr "" -"Czy chcesz zapisać {} plików konfiguracyjnych do następujących lokalizacji?\n" -"\n" -"{}" +msgid "All settings will be reset, are you sure?" +msgstr "Wszystkie ustawienia zostaną zresetowane. Czy na pewno chcesz to zrobić?" -msgid "Saving {} configuration files to {}" -msgstr "Zapisywanie {} plików konfiguracyjnych do {}" +#, fuzzy +msgid "Back" +msgstr "← Wstecz" msgid "Please chose which greeter to install for the chosen profiles: {}" msgstr "" @@ -1122,11 +1101,10 @@ msgid "Profiles" msgstr "Profil" msgid "Finding possible directories to save configuration files ..." -msgstr "" +msgstr "Znajdywanie możliwych katalogów do zapisywania plików konfiguracyjnych ..." -#, fuzzy msgid "Select directory (or directories) for saving configuration files" -msgstr "Wybierz jeden lub więcej dysków twardych do użycia i skonfiguruj je" +msgstr "Wybierz jeden lub więcej katalogów do zapisywania plików konfiguracyjnych" #, fuzzy msgid "Add a custom mirror" @@ -1160,14 +1138,6 @@ msgstr "" msgid "Defined" msgstr "" -#, fuzzy -msgid "Mirrors" -msgstr "Region lustra" - -#, fuzzy -msgid "Mirror regions" -msgstr "Region lustra" - #, fuzzy msgid "Save user configuration (including disk layout)" msgstr "Zapisz konfiguracje użytkownika" @@ -1184,9 +1154,8 @@ msgid "" "{}" msgstr "" -#, fuzzy msgid "Saving {} configuration files to {}" -msgstr "Zapisz konfiguracje" +msgstr "Zapisywanie {} plików konfiguracyjnych do {}" #, fuzzy msgid "Mirrors" @@ -1194,7 +1163,7 @@ msgstr "Region lustra" #, fuzzy msgid "Mirror regions" -msgstr "Region lustra" +msgstr "Region serwerów lustrzanych" #, fuzzy msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" @@ -1204,6 +1173,25 @@ msgstr " - Maksymalna wartość : {} ( Zwiększa liczbę zadań o {}, co pozwa msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "Nieprawidłowa wartość! Spróbuj wprowadzić wartość od 1 do {}, lub 0 aby wyłączyć." +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Użyj programu NetworkManager (niezbędne do graficznej konfiguracji Internetu w środowiskach GNOME i KDE)" + +#~ msgid "When picking a directory to save configuration files to, by default we will ignore the following folders: " +#~ msgstr "Podczas wybierania katalogu do zapisywania plików konfiguracyjnych, domyślnie ignorowane są następujące foldery: " + +#~ msgid "" +#~ "Do you want to save {} configuration file(s) in the following locations?\n" +#~ "\n" +#~ "{}" +#~ msgstr "" +#~ "Czy chcesz zapisać {} plików konfiguracyjnych do następujących lokalizacji?\n" +#~ "\n" +#~ "{}" + #~ msgid "Add :" #~ msgstr "Dodaj :" diff --git a/archinstall/locales/pt/LC_MESSAGES/base.mo b/archinstall/locales/pt/LC_MESSAGES/base.mo index fe43da98..67a5f03b 100644 Binary files a/archinstall/locales/pt/LC_MESSAGES/base.mo and b/archinstall/locales/pt/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pt/LC_MESSAGES/base.po b/archinstall/locales/pt/LC_MESSAGES/base.po index 6c4e71f6..843e9b7c 100644 --- a/archinstall/locales/pt/LC_MESSAGES/base.po +++ b/archinstall/locales/pt/LC_MESSAGES/base.po @@ -1222,6 +1222,13 @@ msgstr "" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "" +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Usar NetworkManager (necessário para configurar internet graficamente em GNOME e KDE)" + #~ msgid "Add :" #~ msgstr "Adicionar :" diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.po b/archinstall/locales/pt_BR/LC_MESSAGES/base.po index 3dd0a818..8daeb76c 100644 --- a/archinstall/locales/pt_BR/LC_MESSAGES/base.po +++ b/archinstall/locales/pt_BR/LC_MESSAGES/base.po @@ -996,9 +996,6 @@ msgstr "Desconhecido" msgid "Partition encryption" msgstr "Encriptação de partição" -msgid "When picking a directory to save configuration files to, by default we will ignore the following folders: " -msgstr "Ao selecionar um diretório para salvar arquivos de configuração, por padrão nós ignoramos as seguintes pastas: " - msgid " ! Formatting {} in " msgstr " ! Formatando {} em " @@ -1182,3 +1179,13 @@ msgstr " - Valor máximo : {} ( Permite {} downloads paralelos, permite {} dow msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {}, ou 0 para desativar]" + +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Usar NetworkManager (necessário para configurar internet graficamente no GNOME e KDE)" + +#~ msgid "When picking a directory to save configuration files to, by default we will ignore the following folders: " +#~ msgstr "Ao selecionar um diretório para salvar arquivos de configuração, por padrão nós ignoramos as seguintes pastas: " diff --git a/archinstall/locales/ru/LC_MESSAGES/base.po b/archinstall/locales/ru/LC_MESSAGES/base.po index b2743719..5f1fe09e 100644 --- a/archinstall/locales/ru/LC_MESSAGES/base.po +++ b/archinstall/locales/ru/LC_MESSAGES/base.po @@ -9,19 +9,14 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " -"n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" "X-Generator: Poedit 3.3.1\n" msgid "[!] A log file has been created here: {} {}" msgstr "[!] Здесь был создан файл журнала: {} {}" -msgid "" -" Please submit this issue (and file) to https://github.com/archlinux/" -"archinstall/issues" -msgstr "" -" Пожалуйста, отправьте эту проблему (и файл) по адресу https://github.com/" -"archlinux/archinstall/issues" +msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" +msgstr " Пожалуйста, отправьте эту проблему (и файл) по адресу https://github.com/archlinux/archinstall/issues" msgid "Do you really want to abort?" msgstr "Вы действительно хотите прекратить?" @@ -36,13 +31,10 @@ msgid "Desired hostname for the installation: " msgstr "Желаемое имя хоста для установки: " msgid "Username for required superuser with sudo privileges: " -msgstr "" -"Имя пользователя для требуемого суперпользователя с привилегиями sudo: " +msgstr "Имя пользователя для требуемого суперпользователя с привилегиями sudo: " msgid "Any additional users to install (leave blank for no users): " -msgstr "" -"Любые дополнительные пользователи для установки (оставьте пустым, если " -"пользователей нет): " +msgstr "Любые дополнительные пользователи для установки (оставьте пустым, если пользователей нет): " msgid "Should this user be a superuser (sudoer)?" msgstr "Должен ли этот пользователь быть суперпользователем (sudoer)?" @@ -59,59 +51,38 @@ msgstr "Выберите загрузчик" msgid "Choose an audio server" msgstr "Выберите звуковой сервер" -msgid "" -"Only packages such as base, base-devel, linux, linux-firmware, efibootmgr " -"and optional profile packages are installed." -msgstr "" -"Устанавливаются только такие пакеты, как base, base-devel, linux, linux-" -"firmware, efibootmgr и дополнительные пакеты профиля." +msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." +msgstr "Устанавливаются только такие пакеты, как base, base-devel, linux, linux-firmware, efibootmgr и дополнительные пакеты профиля." -msgid "" -"If you desire a web browser, such as firefox or chromium, you may specify it " -"in the following prompt." -msgstr "" -"Если вы хотите использовать веб-браузер, например, firefox или chromium, вы " -"можете указать его в следующем запросе." +msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." +msgstr "Если вы хотите использовать веб-браузер, например, firefox или chromium, вы можете указать его в следующем запросе." -msgid "" -"Write additional packages to install (space separated, leave blank to skip): " -msgstr "" -"Напишите дополнительные пакеты для установки (разделите пробелами, оставьте " -"пустым, чтобы пропустить): " +msgid "Write additional packages to install (space separated, leave blank to skip): " +msgstr "Напишите дополнительные пакеты для установки (разделите пробелами, оставьте пустым, чтобы пропустить): " msgid "Copy ISO network configuration to installation" msgstr "Копировать сетевую конфигурацию ISO в установку" -msgid "" -"Use NetworkManager (necessary for configuring internet graphically in GNOME " -"and KDE)" -msgstr "" -"Использовать NetworkManager (необходим для графической настройки интернета в " -"GNOME и KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" +msgstr "Использовать NetworkManager (необходим для графической настройки интернета в GNOME и KDE)" msgid "Select one network interface to configure" msgstr "Выберите один сетевой интерфейс для настройки" -msgid "" -"Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" -msgstr "" -"Выберите режим для конфигурации \"{}\" или пропустите, чтобы использовать " -"режим по умолчанию \"{}\"." +msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" +msgstr "Выберите режим для конфигурации \"{}\" или пропустите, чтобы использовать режим по умолчанию \"{}\"." msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " msgstr "Введите IP-адрес и подсеть для {} (пример: 192.168.0.5/24): " msgid "Enter your gateway (router) IP address or leave blank for none: " -msgstr "" -"Введите IP-адрес вашего шлюза (маршрутизатора) или оставьте пустым, если его " -"нет: " +msgstr "Введите IP-адрес вашего шлюза (маршрутизатора) или оставьте пустым, если его нет: " msgid "Enter your DNS servers (space separated, blank for none): " msgstr "Введите ваши DNS-серверы (через пробел, пустой - нет): " msgid "Select which filesystem your main partition should use" -msgstr "" -"Выберите, какую файловую систему должен использовать ваш основной раздел" +msgstr "Выберите, какую файловую систему должен использовать ваш основной раздел" msgid "Current partition layout" msgstr "Текущая разметка разделов" @@ -126,16 +97,11 @@ msgstr "" msgid "Enter a desired filesystem type for the partition" msgstr "Введите желаемый тип файловой системы для раздела" -msgid "" -"Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " -msgstr "" -"Введите начальное значение (в раздельных блоках: с, ГБ, % и т.д.; по " -"умолчанию: {}): " +msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " +msgstr "Введите начальное значение (в раздельных блоках: с, ГБ, % и т.д.; по умолчанию: {}): " msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " -msgstr "" -"Введите конечное значение (в раздельных блоках: с, Гб, % и т.д.; например: " -"{}): " +msgstr "Введите конечное значение (в раздельных блоках: с, Гб, % и т.д.; например: {}): " msgid "{} contains queued partitions, this will remove those, are you sure?" msgstr "{} содержит разделы в очереди, это удалит их, вы уверены?" @@ -158,17 +124,11 @@ msgstr "" "\n" "Выберите по индексу, какой раздел куда монтировать" -msgid "" -" * Partition mount-points are relative to inside the installation, the boot " -"would be /boot as an example." -msgstr "" -" * Точки монтирования разделов являются относительными внутри установки, " -"например, загрузочный будет /boot." +msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Точки монтирования разделов являются относительными внутри установки, например, загрузочный будет /boot." msgid "Select where to mount partition (leave blank to remove mountpoint): " -msgstr "" -"Выберите куда монтировать раздел (оставьте пустым, чтобы удалить точку " -"монтирования): " +msgstr "Выберите куда монтировать раздел (оставьте пустым, чтобы удалить точку монтирования): " msgid "" "{}\n" @@ -213,25 +173,16 @@ msgid "Archinstall language" msgstr "Язык Archinstall" msgid "Wipe all selected drives and use a best-effort default partition layout" -msgstr "" -"Стереть все выбранные диски и использовать оптимальную схему разделов по " -"умолчанию" +msgstr "Стереть все выбранные диски и использовать оптимальную схему разделов по умолчанию" -msgid "" -"Select what to do with each individual drive (followed by partition usage)" -msgstr "" -"Выберите, что делать с каждым отдельным диском (с последующим использованием " -"разделов)" +msgid "Select what to do with each individual drive (followed by partition usage)" +msgstr "Выберите, что делать с каждым отдельным диском (с последующим использованием разделов)" msgid "Select what you wish to do with the selected block devices" msgstr "Выберите, что вы хотите сделать с выбранными блочными устройствами" -msgid "" -"This is a list of pre-programmed profiles, they might make it easier to " -"install things like desktop environments" -msgstr "" -"Это список предварительно запрограммированных профилей, они могут облегчить " -"установку таких вещей, как окружения рабочего стола" +msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments" +msgstr "Это список предварительно запрограммированных профилей, они могут облегчить установку таких вещей, как окружения рабочего стола" msgid "Select keyboard layout" msgstr "Выберите раскладку клавиатуры" @@ -240,29 +191,16 @@ msgid "Select one of the regions to download packages from" msgstr "Выберите один из регионов для загрузки пакетов" msgid "Select one or more hard drives to use and configure" -msgstr "" -"Выберите один или несколько жестких дисков для использования и настройте их" +msgstr "Выберите один или несколько жестких дисков для использования и настройте их" -msgid "" -"For the best compatibility with your AMD hardware, you may want to use " -"either the all open-source or AMD / ATI options." -msgstr "" -"Для наилучшей совместимости с оборудованием AMD вы можете использовать либо " -"все варианты с открытым исходным кодом, либо AMD / ATI." +msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." +msgstr "Для наилучшей совместимости с оборудованием AMD вы можете использовать либо все варианты с открытым исходным кодом, либо AMD / ATI." -msgid "" -"For the best compatibility with your Intel hardware, you may want to use " -"either the all open-source or Intel options.\n" -msgstr "" -"Для лучшей совместимости с оборудованием Intel вы можете использовать либо " -"все варианты с открытым исходным кодом, либо Intel.\n" +msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" +msgstr "Для лучшей совместимости с оборудованием Intel вы можете использовать либо все варианты с открытым исходным кодом, либо Intel.\n" -msgid "" -"For the best compatibility with your Nvidia hardware, you may want to use " -"the Nvidia proprietary driver.\n" -msgstr "" -"Для наилучшей совместимости с оборудованием Nvidia вы можете использовать " -"проприетарный драйвер Nvidia.\n" +msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" +msgstr "Для наилучшей совместимости с оборудованием Nvidia вы можете использовать проприетарный драйвер Nvidia.\n" msgid "" "\n" @@ -271,15 +209,13 @@ msgid "" msgstr "" "\n" "\n" -"Выберите графический драйвер или оставьте пустым, чтобы установить все " -"драйверы с открытым исходным кодом" +"Выберите графический драйвер или оставьте пустым, чтобы установить все драйверы с открытым исходным кодом" msgid "All open-source (default)" msgstr "Все с открытым исходным кодом (по умолчанию)" msgid "Choose which kernels to use or leave blank for default \"{}\"" -msgstr "" -"Выберите, какие ядра использовать, или оставьте пустым по умолчанию \"{}\"." +msgstr "Выберите, какие ядра использовать, или оставьте пустым по умолчанию \"{}\"." msgid "Choose which locale language to use" msgstr "Выберите, какой язык локали использовать" @@ -296,12 +232,8 @@ msgstr "Выберите один или несколько из приведе msgid "Adding partition...." msgstr "Добавление раздела...." -msgid "" -"You need to enter a valid fs-type in order to continue. See `man parted` for " -"valid fs-type's." -msgstr "" -"Чтобы продолжить, вам нужно ввести действительный fs-тип. Смотрите `man " -"parted` для правильных fs-типов." +msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." +msgstr "Чтобы продолжить, вам нужно ввести действительный fs-тип. Смотрите `man parted` для правильных fs-типов." msgid "Error: Listing profiles on URL \"{}\" resulted in:" msgstr "Ошибка: Перечисление профилей по URL \"{}\" привело к:" @@ -373,8 +305,7 @@ msgid "" "Do you wish to continue?" msgstr "" "Вы решили пропустить выбор жесткого диска\n" -"и будете использовать любой диск, смонтированный по адресу {} " -"(экспериментально)\n" +"и будете использовать любой диск, смонтированный по адресу {} (экспериментально)\n" "ПРЕДУПРЕЖДЕНИЕ: Archinstall не будет проверять пригодность этой установки.\n" "Вы хотите продолжить?" @@ -394,16 +325,13 @@ msgid "Assign mount-point for a partition" msgstr "Назначить точку монтирования для раздела" msgid "Mark/Unmark a partition to be formatted (wipes data)" -msgstr "" -"Пометить/снять отметку с раздела, который будет отформатирован (стирание " -"данных)" +msgstr "Пометить/снять отметку с раздела, который будет отформатирован (стирание данных)" msgid "Mark/Unmark a partition as encrypted" msgstr "Пометить/снять отметку с раздела как зашифрованный" msgid "Mark/Unmark a partition as bootable (automatic for /boot)" -msgstr "" -"Пометить/снять отметку с раздела как загрузочный (автоматически для /boot)" +msgstr "Пометить/снять отметку с раздела как загрузочный (автоматически для /boot)" msgid "Set desired filesystem for a partition" msgstr "Установите желаемую файловую систему для раздела" @@ -443,8 +371,7 @@ msgid "Enter a encryption password for {}" msgstr "Введите пароль шифрования для {}" msgid "Enter disk encryption password (leave blank for no encryption): " -msgstr "" -"Введите пароль шифрования диска (оставьте пустым для отсутствия шифрования): " +msgstr "Введите пароль шифрования диска (оставьте пустым для отсутствия шифрования): " msgid "Create a required super-user with sudo privileges: " msgstr "Создайте необходимого суперпользователя с привилегиями sudo: " @@ -455,43 +382,31 @@ msgstr "Введите пароль root (оставьте пустым, что msgid "Password for user \"{}\": " msgstr "Пароль для пользователя \"{}\": " -msgid "" -"Verifying that additional packages exist (this might take a few seconds)" -msgstr "" -"Проверка наличия дополнительных пакетов (это может занять несколько секунд)" +msgid "Verifying that additional packages exist (this might take a few seconds)" +msgstr "Проверка наличия дополнительных пакетов (это может занять несколько секунд)" -msgid "" -"Would you like to use automatic time synchronization (NTP) with the default " -"time servers?\n" -msgstr "" -"Вы хотите использовать автоматическую синхронизацию времени (NTP) с " -"серверами времени по умолчанию?\n" +msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" +msgstr "Вы хотите использовать автоматическую синхронизацию времени (NTP) с серверами времени по умолчанию?\n" msgid "" -"Hardware time and other post-configuration steps might be required in order " -"for NTP to work.\n" +"Hardware time and other post-configuration steps might be required in order for NTP to work.\n" "For more information, please check the Arch wiki" msgstr "" -"Для работы NTP может потребоваться аппаратное время и другие шаги после " -"конфигурации.\n" +"Для работы NTP может потребоваться аппаратное время и другие шаги после конфигурации.\n" "Для получения дополнительной информации, пожалуйста, ознакомьтесь с ArchWiki" msgid "Enter a username to create an additional user (leave blank to skip): " -msgstr "" -"Введите имя пользователя для создания дополнительного пользователя (оставьте " -"пустым, чтобы пропустить): " +msgstr "Введите имя пользователя для создания дополнительного пользователя (оставьте пустым, чтобы пропустить): " msgid "Use ESC to skip\n" msgstr "Используйте ESC, чтобы пропустить\n" msgid "" "\n" -" Choose an object from the list, and select one of the available actions for " -"it to execute" +" Choose an object from the list, and select one of the available actions for it to execute" msgstr "" "\n" -" Выберите объект из списка и выберите одно из доступных действий для его " -"выполнения" +" Выберите объект из списка и выберите одно из доступных действий для его выполнения" msgid "Cancel" msgstr "Отменить" @@ -527,17 +442,11 @@ msgstr "" "\n" "Это выбранная вами конфигурация:" -msgid "" -"Pacman is already running, waiting maximum 10 minutes for it to terminate." -msgstr "" -"Pacman уже запущен, ожидание его завершения составляет максимум 10 минут." +msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." +msgstr "Pacman уже запущен, ожидание его завершения составляет максимум 10 минут." -msgid "" -"Pre-existing pacman lock never exited. Please clean up any existing pacman " -"sessions before using archinstall." -msgstr "" -"Существовавшая ранее блокировка pacman не завершилась. Пожалуйста, очистите " -"все существующие сессии pacman перед использованием archinstall." +msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." +msgstr "Существовавшая ранее блокировка pacman не завершилась. Пожалуйста, очистите все существующие сессии pacman перед использованием archinstall." msgid "Choose which optional additional repositories to enable" msgstr "Выберите, какие дополнительные репозитории следует включить" @@ -628,8 +537,7 @@ msgid "Missing configurations:\n" msgstr "Отсутствующие конфигурации:\n" msgid "Either root-password or at least 1 superuser must be specified" -msgstr "" -"Должен быть указан либо пароль root, либо как минимум 1 суперпользователь" +msgstr "Должен быть указан либо пароль root, либо как минимум 1 суперпользователь" msgid "Manage superuser accounts: " msgstr "Управление учетными записями суперпользователей: " @@ -689,12 +597,8 @@ msgstr "Хотите ли вы использовать сжатие BTRFS?" msgid "Would you like to create a separate partition for /home?" msgstr "Хотите ли вы создать отдельный раздел для /home?" -msgid "" -"The selected drives do not have the minimum capacity required for an " -"automatic suggestion\n" -msgstr "" -"Выбранные диски не имеют минимальной емкости, необходимой для " -"автоматического предложения\n" +msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" +msgstr "Выбранные диски не имеют минимальной емкости, необходимой для автоматического предложения\n" msgid "Minimum capacity for /home partition: {}GB\n" msgstr "Минимальный размер раздела /home: {}GB\n" @@ -721,9 +625,7 @@ msgid "No iface specified for manual configuration" msgstr "Не указан iface для ручной настройки" msgid "Manual nic configuration with no auto DHCP requires an IP address" -msgstr "" -"Ручная конфигурация сетевого адаптера без автоматического DHCP требует IP-" -"адреса" +msgstr "Ручная конфигурация сетевого адаптера без автоматического DHCP требует IP-адреса" msgid "Add interface" msgstr "Добавить интерфейс" @@ -743,74 +645,44 @@ msgstr "Ручная конфигурация" msgid "Mark/Unmark a partition as compressed (btrfs only)" msgstr "Пометить/снять отметку с раздела как сжатый (только для btrfs)" -msgid "" -"The password you are using seems to be weak, are you sure you want to use it?" -msgstr "" -"Пароль, который вы используете, кажется слабым, вы уверены, что хотите его " -"использовать?" +msgid "The password you are using seems to be weak, are you sure you want to use it?" +msgstr "Пароль, который вы используете, кажется слабым, вы уверены, что хотите его использовать?" -msgid "" -"Provides a selection of desktop environments and tiling window managers, e." -"g. gnome, kde, sway" -msgstr "" -"Предоставляет выбор окружений рабочего стола и тайловых оконных менеджеров, " -"например, gnome, kde, sway" +msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" +msgstr "Предоставляет выбор окружений рабочего стола и тайловых оконных менеджеров, например, gnome, kde, sway" msgid "Select your desired desktop environment" msgstr "Выберите желаемое окружение рабочего стола" -msgid "" -"A very basic installation that allows you to customize Arch Linux as you see " -"fit." -msgstr "" -"Очень базовая установка, позволяющая настроить Arch Linux по своему " -"усмотрению." +msgid "A very basic installation that allows you to customize Arch Linux as you see fit." +msgstr "Очень базовая установка, позволяющая настроить Arch Linux по своему усмотрению." -msgid "" -"Provides a selection of various server packages to install and enable, e.g. " -"httpd, nginx, mariadb" -msgstr "" -"Предоставляет выбор различных пакетов сервера для установки и включения, " -"например, httpd, nginx, mariadb" +msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" +msgstr "Предоставляет выбор различных пакетов сервера для установки и включения, например, httpd, nginx, mariadb" -msgid "" -"Choose which servers to install, if none then a minimal installation will be " -"done" -msgstr "" -"Выберите серверы для установки, если их нет, то будет выполнена минимальная " -"установка" +msgid "Choose which servers to install, if none then a minimal installation will be done" +msgstr "Выберите серверы для установки, если их нет, то будет выполнена минимальная установка" msgid "Installs a minimal system as well as xorg and graphics drivers." -msgstr "" -"Устанавливает минимальную систему, а также xorg и графические драйверы." +msgstr "Устанавливает минимальную систему, а также xorg и графические драйверы." msgid "Press Enter to continue." msgstr "Нажмите Enter, чтобы продолжить." -msgid "" -"Would you like to chroot into the newly created installation and perform " -"post-installation configuration?" -msgstr "" -"Хотите ли вы использовать chroot в новой созданной установке и выполнить " -"настройку после установки?" +msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?" +msgstr "Хотите ли вы использовать chroot в новой созданной установке и выполнить настройку после установки?" msgid "Are you sure you want to reset this setting?" msgstr "Вы уверены, что хотите сбросить эту настройку?" msgid "Select one or more hard drives to use and configure\n" -msgstr "" -"Выберите один или несколько жестких дисков для использования и настройки\n" +msgstr "Выберите один или несколько жестких дисков для использования и настройки\n" msgid "Any modifications to the existing setting will reset the disk layout!" -msgstr "" -"Любые изменения существующей настройки приведут к сбросу разметки диска!" +msgstr "Любые изменения существующей настройки приведут к сбросу разметки диска!" -msgid "" -"If you reset the harddrive selection this will also reset the current disk " -"layout. Are you sure?" -msgstr "" -"Если вы сбросите выбор жесткого диска, это также сбросит текущую разметку " -"диска. Вы уверены?" +msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" +msgstr "Если вы сбросите выбор жесткого диска, это также сбросит текущую разметку диска. Вы уверены?" msgid "Save and exit" msgstr "Сохранить и выйти" @@ -856,12 +728,8 @@ msgstr "Добавить: " msgid "Value: " msgstr "Значение: " -msgid "" -"You can skip selecting a drive and partitioning and use whatever drive-setup " -"is mounted at /mnt (experimental)" -msgstr "" -"Вы можете не выбирать диск и разметку и использовать любой диск, " -"смонтированный в /mnt (экспериментально)" +msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" +msgstr "Вы можете не выбирать диск и разметку и использовать любой диск, смонтированный в /mnt (экспериментально)" msgid "Select one of the disks or skip and use /mnt as default" msgstr "Выберите один из дисков или пропустите и используйте /mnt по умолчанию" @@ -884,12 +752,8 @@ msgstr "Свободное место" msgid "Bus-type" msgstr "Тип шины" -msgid "" -"Either root-password or at least 1 user with sudo privileges must be " -"specified" -msgstr "" -"Должен быть указан либо пароль root, либо хотя бы 1 пользователь с " -"привилегиями sudo" +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "Должен быть указан либо пароль root, либо хотя бы 1 пользователь с привилегиями sudo" msgid "Enter username (leave blank to skip): " msgstr "Введите имя пользователя (оставьте пустым, чтобы пропустить): " @@ -927,12 +791,8 @@ msgstr "Удалить подтом" msgid "Configured {} interfaces" msgstr "Настроено интерфейсов: {}" -msgid "" -"This option enables the number of parallel downloads that can occur during " -"installation" -msgstr "" -"Этот параметр определяет количество параллельных загрузок, которые могут " -"происходить во время установки" +msgid "This option enables the number of parallel downloads that can occur during installation" +msgstr "Этот параметр определяет количество параллельных загрузок, которые могут происходить во время установки" msgid "" "Enter the number of parallel downloads to be enabled.\n" @@ -943,34 +803,18 @@ msgstr "" " (Введите значение от 1 до {})\n" "Примечание:" -msgid "" -" - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads " -"at a time )" -msgstr "" -" - Минимальное значение: {} ( Позволяет {} параллельную загрузку, позволяет " -"{} загрузки одновременно )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - Минимальное значение: {} ( Позволяет {} параллельную загрузку, позволяет {} загрузки одновременно )" -msgid "" -" - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a " -"time )" -msgstr "" -" - Минимальное значение: 1 ( Позволяет 1 параллельную загрузку, позволяет 2 " -"загрузки одновременно )" +msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" +msgstr " - Минимальное значение: 1 ( Позволяет 1 параллельную загрузку, позволяет 2 загрузки одновременно )" -msgid "" -" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 " -"download at a time )" -msgstr "" -" - Отключить/по умолчанию: 0 ( Отключает параллельную загрузку, позволяет " -"только 1 загрузку за один раз )" +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" +msgstr " - Отключить/по умолчанию: 0 ( Отключает параллельную загрузку, позволяет только 1 загрузку за один раз )" #, python-brace-format -msgid "" -"Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to " -"disable]" -msgstr "" -"Неверный ввод! Повторите попытку с правильным вводом [1 - {max_downloads}, " -"или 0 - отключить]" +msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" +msgstr "Неверный ввод! Повторите попытку с правильным вводом [1 - {max_downloads}, или 0 - отключить]" msgid "Parallel Downloads" msgstr "Параллельные загрузки" @@ -987,20 +831,14 @@ msgstr "TAB, чтобы выбрать" msgid "[Default value: 0] > " msgstr "[Значение по умолчанию: 0] > " -msgid "" -"To be able to use this translation, please install a font manually that " -"supports the language." -msgstr "" -"Чтобы иметь возможность использовать этот перевод, пожалуйста, установите " -"вручную шрифт, поддерживающий данный язык." +msgid "To be able to use this translation, please install a font manually that supports the language." +msgstr "Чтобы иметь возможность использовать этот перевод, пожалуйста, установите вручную шрифт, поддерживающий данный язык." msgid "The font should be stored as {}" msgstr "Шрифт должен быть сохранен как {}" msgid "Archinstall requires root privileges to run. See --help for more." -msgstr "" -"Для запуска Archinstall требуются привилегии root. Для получения " -"дополнительной информации смотрите --help." +msgstr "Для запуска Archinstall требуются привилегии root. Для получения дополнительной информации смотрите --help." msgid "Select an execution mode" msgstr "Выберите режим выполнения" @@ -1008,22 +846,14 @@ msgstr "Выберите режим выполнения" msgid "Unable to fetch profile from specified url: {}" msgstr "Невозможно получить профиль из указанного url: {}" -msgid "" -"Profiles must have unique name, but profile definitions with duplicate name " -"found: {}" -msgstr "" -"Профили должны иметь уникальное имя, но найдены определения профиля с " -"дублирующимся именем: {}" +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "Профили должны иметь уникальное имя, но найдены определения профиля с дублирующимся именем: {}" msgid "Select one or more devices to use and configure" msgstr "Выберите одно или несколько устройств для использования и настройки" -msgid "" -"If you reset the device selection this will also reset the current disk " -"layout. Are you sure?" -msgstr "" -"Если вы сбросите выбор устройства, это также сбросит текущую разметку " -"дисков. Вы уверены?" +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "Если вы сбросите выбор устройства, это также сбросит текущую разметку дисков. Вы уверены?" msgid "Existing Partitions" msgstr "Существующие разделы" @@ -1040,12 +870,8 @@ msgstr "Минимальный размер раздела /home: {}GiB\n" msgid "Minimum capacity for Arch Linux partition: {}GiB" msgstr "Минимальный размер раздела Arch Linux: {}GiB" -msgid "" -"This is a list of pre-programmed profiles_bck, they might make it easier to " -"install things like desktop environments" -msgstr "" -"Это список предварительно запрограммированных профилей, они могут облегчить " -"установку таких вещей, как окружения рабочего стола" +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "Это список предварительно запрограммированных профилей, они могут облегчить установку таких вещей, как окружения рабочего стола" msgid "Current profile selection" msgstr "Текущий выбор профиля" @@ -1077,26 +903,14 @@ msgstr "Удалить раздел" msgid "Partition" msgstr "Раздел" -msgid "" -"This partition is currently encrypted, to format it a filesystem has to be " -"specified" -msgstr "" -"Этот раздел в настоящее время зашифрован, для его форматирования необходимо " -"указать файловую систему" +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "Этот раздел в настоящее время зашифрован, для его форматирования необходимо указать файловую систему" -msgid "" -"Partition mount-points are relative to inside the installation, the boot " -"would be /boot as an example." -msgstr "" -"Точки монтирования разделов являются относительными внутри установки, " -"например, загрузочный раздел будет /boot." +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr "Точки монтирования разделов являются относительными внутри установки, например, загрузочный раздел будет /boot." -msgid "" -"If mountpoint /boot is set, then the partition will also be marked as " -"bootable." -msgstr "" -"Если установлена точка монтирования /boot, то раздел также будет помечен как " -"загрузочный." +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "Если установлена точка монтирования /boot, то раздел также будет помечен как загрузочный." msgid "Mountpoint: " msgstr "Точка монтирования: " @@ -1110,11 +924,8 @@ msgstr "Всего секторов: {}" msgid "Enter the start sector (default: {}): " msgstr "Введите начальный сектор (по умолчанию: {}): " -msgid "" -"Enter the end sector of the partition (percentage or block number, default: " -"{}): " -msgstr "" -"Введите конечный сектор раздела (процент или номер блока, по умолчанию: {}): " +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Введите конечный сектор раздела (процент или номер блока, по умолчанию: {}): " msgid "This will remove all newly added partitions, continue?" msgstr "Это приведет к удалению всех вновь добавленных разделов, продолжить?" @@ -1180,19 +991,13 @@ msgid "Back" msgstr "Назад" msgid "Please chose which greeter to install for the chosen profiles: {}" -msgstr "" -"Пожалуйста, выберите, какой экран приветствия установить для выбранных " -"профилей: {}" +msgstr "Пожалуйста, выберите, какой экран приветствия установить для выбранных профилей: {}" msgid "Environment type: {}" msgstr "Тип окружения рабочего стола: {}" -msgid "" -"The proprietary Nvidia driver is not supported by Sway. It is likely that " -"you will run into issues, are you okay with that?" -msgstr "" -"Проприетарный драйвер Nvidia не поддерживается Sway. Вполне вероятно, что вы " -"столкнетесь с проблемами, вы согласны с этим?" +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "Проприетарный драйвер Nvidia не поддерживается Sway. Вполне вероятно, что вы столкнетесь с проблемами, вы согласны с этим?" msgid "Installed packages" msgstr "Устанавливаемые пакеты" @@ -1212,19 +1017,11 @@ msgstr "Имя профиля: " msgid "The profile name you entered is already in use. Try again" msgstr "Введенное вами имя профиля уже используется. Попробуйте еще раз" -msgid "" -"Packages to be install with this profile (space separated, leave blank to " -"skip): " -msgstr "" -"Пакеты, которые будут установлены с этим профилем (разделенные пробелами, " -"оставьте пустым, чтобы пропустить): " +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Пакеты, которые будут установлены с этим профилем (разделенные пробелами, оставьте пустым, чтобы пропустить): " -msgid "" -"Services to be enabled with this profile (space separated, leave blank to " -"skip): " -msgstr "" -"Службы, которые должны быть включены с помощью этого профиля (разделенные " -"пробелами, оставьте пустым, чтобы пропустить): " +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Службы, которые должны быть включены с помощью этого профиля (разделенные пробелами, оставьте пустым, чтобы пропустить): " msgid "Should this profile be enabled for installation?" msgstr "Должен ли этот профиль быть включен для установки?" @@ -1237,15 +1034,10 @@ msgid "" "Select a graphics driver or leave blank to install all open-source drivers" msgstr "" "\n" -"Выберите графический драйвер или оставьте пустым, чтобы установить все " -"драйверы с открытым исходным кодом" +"Выберите графический драйвер или оставьте пустым, чтобы установить все драйверы с открытым исходным кодом" -msgid "" -"Sway needs access to your seat (collection of hardware devices i.e. " -"keyboard, mouse, etc)" -msgstr "" -"Sway необходим доступ к вашему компьютеру (набор аппаратных устройств, т.е. " -"клавиатура, мышь и т.д.)" +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "Sway необходим доступ к вашему компьютеру (набор аппаратных устройств, т.е. клавиатура, мышь и т.д.)" msgid "" "\n" @@ -1311,12 +1103,10 @@ msgid "Save user configuration (including disk layout)" msgstr "Сохранить конфигурацию пользователя (включая разметку диска)" msgid "" -"Enter a directory for the configuration(s) to be saved (tab completion " -"enabled)\n" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" "Save directory: " msgstr "" -"Введите каталог для сохранения конфигурации (-ций) (включено заполнение " -"вкладок)\n" +"Введите каталог для сохранения конфигурации (-ций) (включено заполнение вкладок)\n" "Каталог сохранения: " msgid "" @@ -1337,17 +1127,18 @@ msgstr "Зеркала" msgid "Mirror regions" msgstr "Регионы зеркала" -msgid "" -" - Maximum value : {} ( Allows {} parallel downloads, allows " -"{max_downloads+1} downloads at a time )" -msgstr "" -" - Максимальное значение: {} ( Позволяет {} параллельных загрузок, позволяет " -"{max_downloads+1} загрузок одновременно )" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - Максимальное значение: {} ( Позволяет {} параллельных загрузок, позволяет {max_downloads+1} загрузок одновременно )" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "Неверный ввод! Повторите попытку с правильным вводом [1 - {}, или 0 - отключить]" + +msgid "Locales" msgstr "" -"Неверный ввод! Повторите попытку с правильным вводом [1 - {}, или 0 - " -"отключить]" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Использовать NetworkManager (необходим для графической настройки интернета в GNOME и KDE)" #, python-brace-format #~ msgid "Edit {origkey} :" diff --git a/archinstall/locales/sv/LC_MESSAGES/base.mo b/archinstall/locales/sv/LC_MESSAGES/base.mo index 906c9de5..11d757b0 100644 Binary files a/archinstall/locales/sv/LC_MESSAGES/base.mo and b/archinstall/locales/sv/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/sv/LC_MESSAGES/base.po b/archinstall/locales/sv/LC_MESSAGES/base.po index fa54b97e..3566585b 100644 --- a/archinstall/locales/sv/LC_MESSAGES/base.po +++ b/archinstall/locales/sv/LC_MESSAGES/base.po @@ -1183,3 +1183,10 @@ msgstr "" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "" + +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Använd NetworkManager (nödvändig för konfigurera internet i grafiska miljöerna GNOME och KDE)" diff --git a/archinstall/locales/uk/LC_MESSAGES/base.mo b/archinstall/locales/uk/LC_MESSAGES/base.mo index 45bffd7e..8c8cfcd3 100644 Binary files a/archinstall/locales/uk/LC_MESSAGES/base.mo and b/archinstall/locales/uk/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/uk/LC_MESSAGES/base.po b/archinstall/locales/uk/LC_MESSAGES/base.po index f2b84a49..d8f7c70c 100644 --- a/archinstall/locales/uk/LC_MESSAGES/base.po +++ b/archinstall/locales/uk/LC_MESSAGES/base.po @@ -1173,3 +1173,10 @@ msgstr " - Максимальне значення : {} ( Дозволяє {} msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "Некоректне введення! Повторіть спробу з валідним введенням [від 1 до {} або 0 для вимкнення]" + +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Використовувати NetworkManager (необхідний для графічного налаштування Інтернету в GNOME та KDE)" diff --git a/archinstall/locales/ur/LC_MESSAGES/base.mo b/archinstall/locales/ur/LC_MESSAGES/base.mo index 67ce2f1f..887c5126 100644 Binary files a/archinstall/locales/ur/LC_MESSAGES/base.mo and b/archinstall/locales/ur/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ur/LC_MESSAGES/base.po b/archinstall/locales/ur/LC_MESSAGES/base.po index 72555aec..551d53f6 100644 --- a/archinstall/locales/ur/LC_MESSAGES/base.po +++ b/archinstall/locales/ur/LC_MESSAGES/base.po @@ -1203,6 +1203,13 @@ msgstr "" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "" +msgid "Locales" +msgstr "" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "نیٹ ورک مینجر کا استعمال کریں (GNOME اور KDE میں انٹرنیٹ کو گرافیکلی ترتیب دینے کے لیے ضروری ہے)" + #~ msgid "Add :" #~ msgstr "شامل:" diff --git a/archinstall/locales/zh-TW/LC_MESSAGES/base.mo b/archinstall/locales/zh-TW/LC_MESSAGES/base.mo index 4a258a33..15491b88 100644 Binary files a/archinstall/locales/zh-TW/LC_MESSAGES/base.mo and b/archinstall/locales/zh-TW/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/zh-TW/LC_MESSAGES/base.po b/archinstall/locales/zh-TW/LC_MESSAGES/base.po index 0c3226ec..f1aa86ae 100644 --- a/archinstall/locales/zh-TW/LC_MESSAGES/base.po +++ b/archinstall/locales/zh-TW/LC_MESSAGES/base.po @@ -791,18 +791,19 @@ msgstr "已配置的 {} 接口" msgid "This option enables the number of parallel downloads that can occur during installation" msgstr "此選項啟用安裝期間可能發生的並行下載數" -#, python-brace-format +#, fuzzy msgid "" "Enter the number of parallel downloads to be enabled.\n" -" (Enter a value between 1 to {max_downloads})\n" +" (Enter a value between 1 to {})\n" "Note:" msgstr "" "輸入要啟用的並行下載數。\n" " (輸入一個 1 到 {max_downloads} 之間的值)\n" "提示:" -msgid " - Maximum value : {max_downloads} ( Allows {max_downloads} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - 最大值:{max_downloads}(允許 {max_downloads} 个並行下載,同時允許 {max_downloads+1} 个下載)" +#, fuzzy +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - 最小值:1(允許 1 个並行下載,同時允許 2 个下載)" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" msgstr " - 最小值:1(允許 1 个並行下載,同時允許 2 个下載)" @@ -1072,19 +1073,37 @@ msgid "Select directory (or directories) for saving configuration files" msgstr "選擇用於保存配置文件的目錄(或多個目錄)" #, fuzzy -msgid "Mirrors" -msgstr "鏡像區域" +msgid "Add a custom mirror" +msgstr "新增一個使用者" -msgid "Defined" +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" msgstr "" #, fuzzy -msgid "Mirror regions" -msgstr "鏡像區域" +msgid "Enter name (leave blank to skip): " +msgstr "輸入用戶名(留空以跳過):" + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "輸入用戶名(留空以跳過):" + +#, fuzzy +msgid "Select signature check option" +msgstr "選擇硬盤加密選項" + +#, fuzzy +msgid "Select signature option" +msgstr "選擇硬盤加密選項" msgid "Custom mirrors" msgstr "" +msgid "Defined" +msgstr "" + #, fuzzy msgid "Save user configuration (including disk layout)" msgstr "保存使用者配置" @@ -1106,27 +1125,24 @@ msgid "Saving {} configuration files to {}" msgstr "保存配置" #, fuzzy -msgid "Add a custom mirror" -msgstr "新增一個使用者" - -msgid "Change custom mirror" -msgstr "" - -msgid "Delete custom mirror" -msgstr "" +msgid "Mirrors" +msgstr "鏡像區域" #, fuzzy -msgid "Enter name (leave blank to skip): " -msgstr "輸入用戶名(留空以跳過):" +msgid "Mirror regions" +msgstr "鏡像區域" #, fuzzy -msgid "Enter url (leave blank to skip): " -msgstr "輸入用戶名(留空以跳過):" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - 最大值:{max_downloads}(允許 {max_downloads} 个並行下載,同時允許 {max_downloads+1} 个下載)" #, fuzzy -msgid "Select signature check option" -msgstr "選擇硬盤加密選項" +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "輸入無效! 請嘗試使用有效輸入重試 [1 到 {max_downloads},或 0 到禁用]" + +msgid "Locales" +msgstr "" #, fuzzy -msgid "Select signature option" -msgstr "選擇硬盤加密選項" +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "使用 NetworkManager(在 GNOME 和 KDE 透過圖形界面設置網際網路連線所需)" -- cgit v1.2.3-70-g09d2 From 03d228fee8f850bb58249d23fd7c17f742a7701b Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Wed, 21 Jun 2023 17:45:17 +1000 Subject: Update contributing doc (#1873) Co-authored-by: Daniel Girtler --- CONTRIBUTING.md | 13 +++++++++++-- README.md | 5 +++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5ed5142d..3faa8ae8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,8 +27,7 @@ The exceptions to PEP8 are: * Archinstall uses [tabs instead of spaces](https://www.python.org/dev/peps/pep-0008/#tabs-or-spaces) simply to make it easier for non-IDE developers to navigate the code *(Tab display-width should be equal to 4 spaces)*. Exception to the rule are comments that need fine-tuned indentation for documentation purposes. -* [Line length](https://www.python.org/dev/peps/pep-0008/#maximum-line-length) should aim for no more than 100 - characters, but not strictly enforced. +* [Line length](https://www.python.org/dev/peps/pep-0008/#maximum-line-length) a maximum line length is enforced via flake8 with 236 characters * [Line breaks before/after binary operator](https://www.python.org/dev/peps/pep-0008/#should-a-line-break-before-or-after-a-binary-operator) is not enforced, as long as the style of line breaks is consistent within the same code block. * Archinstall should always be saved with **Unix-formatted line endings** and no other platform-specific formats. @@ -39,6 +38,16 @@ The exceptions to PEP8 are: Most of these style guidelines have been put into place after the fact *(in an attempt to clean up the code)*.
There might therefore be older code which does not follow the coding convention and the code is subject to change. +## Git hooks + +`archinstall` ships pre-commit hooks that make it easier to run check such as `mypy` and `flake8` locally. +The checks are listed in `.pre-commit-config.yaml` and can be installed via +``` +pre-commit install +``` + +This will install the pre-commit hook and run it every time a `git commit` is executed. + ## Documentation If you'd like to contribute to the documentation, refer to [this guide](docs/README.md) on how to build the documentation locally. diff --git a/README.md b/README.md index 9893ddc3..15afdda4 100644 --- a/README.md +++ b/README.md @@ -229,3 +229,8 @@ This will create a *20 GB* `testimage.img` and create a loop device which we can There's also a [Building and Testing](https://github.com/archlinux/archinstall/wiki/Building-and-Testing) guide.
It will go through everything from packaging, building and running *(with qemu)* the installer against a dev branch. + + +# Contributing + +Please see [CONTRIBUTING.md](https://github.com/archlinux/archinstall/blob/master/CONTRIBUTING.md) -- cgit v1.2.3-70-g09d2 From 1b5ecb65aabe546cea51da1de7fad61ff1d32525 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Wed, 21 Jun 2023 17:47:30 +1000 Subject: Always verify permissions and check for log dir (#1874) Co-authored-by: Daniel Girtler --- archinstall/__init__.py | 7 +------ archinstall/lib/output.py | 6 +++++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/archinstall/__init__.py b/archinstall/__init__.py index ce58e255..305d4096 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -20,10 +20,7 @@ from . import default_profiles from .lib.hardware import SysInfo, AVAILABLE_GFX_DRIVERS from .lib.installer import Installer, accessibility_tools_in_use -from .lib.output import ( - FormattedOutput, log, error, - check_log_permissions, debug, warn, info -) +from .lib.output import FormattedOutput, log, error, debug, warn, info from .lib.storage import storage from .lib.global_menu import GlobalMenu from .lib.boot import Boot @@ -50,8 +47,6 @@ storage['__version__'] = __version__ # project to mark strings as translatable with _('translate me') DeferredTranslation.install() -check_log_permissions() - # Log various information about hardware before starting the installation. This might assist in troubleshooting debug(f"Hardware model detected: {SysInfo.sys_vendor()} {SysInfo.product_name()}; UEFI mode: {SysInfo.has_uefi()}") debug(f"Processor model detected: {SysInfo.cpu_model()}") diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py index b406d624..d266afa8 100644 --- a/archinstall/lib/output.py +++ b/archinstall/lib/output.py @@ -135,7 +135,7 @@ class Journald: log_adapter.log(level, message) -def check_log_permissions(): +def _check_log_permissions(): filename = storage.get('LOG_FILE', None) log_dir = storage.get('LOG_PATH', Path('./')) @@ -292,6 +292,10 @@ def log( reset: bool = False, font: List[Font] = [] ): + # leave this check here as we need to setup the logging + # right from the beginning when the modules are loaded + _check_log_permissions() + text = orig_string = ' '.join([str(x) for x in msgs]) # Attempt to colorize the output if supported -- cgit v1.2.3-70-g09d2 From 64a0d097f047b8923881daa1d4c2516aa6538dae Mon Sep 17 00:00:00 2001 From: Nikita Triblya <69369034+Tr3bleee@users.noreply.github.com> Date: Wed, 21 Jun 2023 09:48:48 +0200 Subject: Update budgie.py (#1877) Removed the comma after the "papirus-icon-theme" package, since it is the final package --- archinstall/default_profiles/desktops/budgie.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archinstall/default_profiles/desktops/budgie.py b/archinstall/default_profiles/desktops/budgie.py index 32bd718d..93be320f 100644 --- a/archinstall/default_profiles/desktops/budgie.py +++ b/archinstall/default_profiles/desktops/budgie.py @@ -18,7 +18,7 @@ class BudgieProfile(XorgProfile): "budgie", "mate-terminal", "nemo", - "papirus-icon-theme", + "papirus-icon-theme" ] @property -- cgit v1.2.3-70-g09d2 From 96cb6cc943758664ca4b91ea8adc4c4e29f124d2 Mon Sep 17 00:00:00 2001 From: Nikita Triblya <69369034+Tr3bleee@users.noreply.github.com> Date: Wed, 21 Jun 2023 09:49:06 +0200 Subject: Update lxqt.py (#1878) Removed the comma after the "slock" package, since it is the final package --- archinstall/default_profiles/desktops/lxqt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archinstall/default_profiles/desktops/lxqt.py b/archinstall/default_profiles/desktops/lxqt.py index 146e168e..60bf47f2 100644 --- a/archinstall/default_profiles/desktops/lxqt.py +++ b/archinstall/default_profiles/desktops/lxqt.py @@ -23,7 +23,7 @@ class LxqtProfile(XorgProfile): "xdg-utils", "ttf-freefont", "leafpad", - "slock", + "slock" ] @property -- cgit v1.2.3-70-g09d2 From 21bf87f234a9f8782a13492d815ed3c9aff9d0ed Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Wed, 21 Jun 2023 17:50:28 +1000 Subject: Fix workflows and checks (#1872) * Update translation workflow * Fix broken checks --------- Co-authored-by: Daniel Girtler --- .github/workflows/flake8.yaml | 4 ++-- .github/workflows/mypy.yaml | 4 ++-- .github/workflows/pytest.yaml | 4 ++-- .github/workflows/translation-check.yaml | 12 ++++++++---- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/.github/workflows/flake8.yaml b/.github/workflows/flake8.yaml index d9db80e9..a33fb07c 100644 --- a/.github/workflows/flake8.yaml +++ b/.github/workflows/flake8.yaml @@ -8,7 +8,7 @@ jobs: steps: - uses: actions/checkout@v3 - run: pacman --noconfirm -Syu python python-pip - - run: python -m pip install --upgrade pip - - run: pip install flake8 + - run: pip install --break-system-packages --upgrade pip + - run: pip install --break-system-packages flake8 - name: Lint with flake8 run: flake8 diff --git a/.github/workflows/mypy.yaml b/.github/workflows/mypy.yaml index e0db6f06..ccca2b9d 100644 --- a/.github/workflows/mypy.yaml +++ b/.github/workflows/mypy.yaml @@ -8,8 +8,8 @@ jobs: steps: - uses: actions/checkout@v3 - run: pacman --noconfirm -Syu python mypy python-pip - - run: python -m pip install --upgrade pip - - run: pip install fastapi pydantic + - run: pip install --break-system-packages --upgrade pip + - run: pip install --break-system-packages fastapi pydantic - run: python --version - run: mypy --version # one day this will be enabled diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index 568a6e6c..83d2e177 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -9,7 +9,7 @@ jobs: steps: - uses: actions/checkout@v3 - run: pacman --noconfirm -Syu python python-pip qemu gcc - - run: python -m pip install --upgrade pip - - run: pip install pytest + - run: python -m pip install --break-system-packages --upgrade pip + - run: pip install --break-system-packages pytest - name: Test with pytest run: python -m pytest || exit 0 diff --git a/.github/workflows/translation-check.yaml b/.github/workflows/translation-check.yaml index 18a3b5fc..1d9a5de6 100644 --- a/.github/workflows/translation-check.yaml +++ b/.github/workflows/translation-check.yaml @@ -1,7 +1,10 @@ on: - [ push, pull_request ]: - paths: - - 'archinstall/locales/**' + push: + paths: + - 'archinstall/locales/**' + pull_request: + paths: + - 'archinstall/locales/**' name: translation file checks jobs: translation-check: @@ -11,7 +14,8 @@ jobs: steps: - uses: actions/checkout@v3 - run: pacman --noconfirm -Syu python python-pip - - run: python -m pip install --upgrade pip + - run: pip install --break-system-packages --upgrade pip - run: cd archinstall/locales - run: bash locales_generator.sh - run: git diff --name-only + - run: ls -- cgit v1.2.3-70-g09d2 From 4435dc3e2daeb370eed4035b0f997d3cba6fdfbf Mon Sep 17 00:00:00 2001 From: Schwarzeisc00l <78359804+Schwarzeisc00l@users.noreply.github.com> Date: Wed, 21 Jun 2023 07:51:08 +0000 Subject: Update base.po (#1883) --- archinstall/locales/tr/LC_MESSAGES/base.po | 74 +++++++++++++++--------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/archinstall/locales/tr/LC_MESSAGES/base.po b/archinstall/locales/tr/LC_MESSAGES/base.po index 1b355cfb..8d82f48a 100644 --- a/archinstall/locales/tr/LC_MESSAGES/base.po +++ b/archinstall/locales/tr/LC_MESSAGES/base.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: 21.05.2022\n" "PO-Revision-Date: 2022-05-25 17:42+0300\n" -"Last-Translator: Eren İnce @ernince \n" +"Last-Translator: Schwarze \n" "Language-Team: \n" "Language: tr\n" "MIME-Version: 1.0\n" @@ -460,10 +460,10 @@ msgid "Add a user" msgstr "Kullanıcı ekle" msgid "Change password" -msgstr "Şifre değiştir" +msgstr "Şifren değiştir" msgid "Promote/Demote user" -msgstr "Kullanıcı terfi ettir/indirge" +msgstr "Kullanıcı terfi et/indirge" msgid "Delete User" msgstr "Kullanıcı Sil" @@ -479,7 +479,7 @@ msgid "User Name : " msgstr "Kullanıcı Adı : " msgid "Should {} be a superuser (sudoer)?" -msgstr "{} bir süper kullanıcı (sudoer) olmalı mı?" +msgstr "{} bir süper kullanıcı (sudoer) mı olmalı?" msgid "Define users with sudo privilege: " msgstr "Sudo yetkilerine sahip kullanıcıları tanımlayın: " @@ -530,19 +530,19 @@ msgid "The password you are using seems to be weak," msgstr "Kullandığınız şifre zayıf gözüküyor," msgid "are you sure you want to use it?" -msgstr "kullanmak istediğinize emin misiniz?" +msgstr "Kullanmak istediğinize emin misiniz?" msgid "Optional repositories" -msgstr "Tercihi \"repository\"ler (depolar)" +msgstr "Tercihi depolar" msgid "Save configuration" -msgstr "Yapılandırmayı kaydet" +msgstr "Konfigürasyonu kaydet" msgid "Missing configurations:\n" -msgstr "Eksik yapılandırmalar:\n" +msgstr "Eksik konfigürasyon:\n" msgid "Either root-password or at least 1 superuser must be specified" -msgstr "Root (kök) şifresi ya da en azından 1 adet süper-kullanıcı belirtilmek zorunda" +msgstr "Kök (yönetici) şifresi ya da en azından 1 adet süper-kullanıcı belirtilmek zorunda" msgid "Manage superuser accounts: " msgstr "Süper-kullanıcı hesaplarını yönet: " @@ -554,7 +554,7 @@ msgid " Subvolume :{:16}" msgstr " alt disk bölümü :{:16}" msgid " mounted at {:16}" -msgstr " {:16}'da mount (monte) edildi" +msgstr " {:16}'da monte edildi" msgid " with option {}" msgstr " {} seçeneğiyle" @@ -570,7 +570,7 @@ msgid "Subvolume name " msgstr "Alt disk bölümü ismi " msgid "Subvolume mountpoint" -msgstr "Alt disk bölümü mount (monte) noktası" +msgstr "Alt disk bölümü monte noktası" msgid "Subvolume options" msgstr "Alt disk bölümü seçenekleri" @@ -582,16 +582,16 @@ msgid "Subvolume name :" msgstr "Alt disk bölümü ismi :" msgid "Select a mount point :" -msgstr "Bir mount (monte) noktası seçin :" +msgstr "Bir monte noktası seçin :" msgid "Select the desired subvolume options " msgstr "İstenilen alt disk bölümü ayarlarını seçin " msgid "Define users with sudo privilege, by username: " -msgstr "Sudo yetkilerine sahip kullanıcıları tanımlayın, kullanıcı adı ile: " +msgstr "Süper-kullanıcı yetkilerine sahip kullanıcıları tanımlayın, kullanıcı adı ile: " msgid "[!] A log file has been created here: {}" -msgstr "[!] Burada bir günlük (log) dosyası oluşturuldu: {}" +msgstr "[!] Burada bir günlük dosyası oluşturuldu: {}" msgid "Would you like to use BTRFS subvolumes with a default structure?" msgstr "BTRFS alt disk bölümlerini varsayılan yapıyla kullanmak ister misiniz?" @@ -760,7 +760,7 @@ msgstr "Bus(veri yolu)-tipi" #, fuzzy msgid "Either root-password or at least 1 user with sudo privileges must be specified" -msgstr "Root (kök) şifresi ya da en azından 1 adet süper-kullanıcı belirtilmek zorunda" +msgstr "RKök şifresi ya da en azından 1 adet süper-kullanıcı belirtilmek zorunda" #, fuzzy msgid "Enter username (leave blank to skip): " @@ -842,7 +842,7 @@ msgid "TAB to select" msgstr "Seçmek için TAB" msgid "[Default value: 0] > " -msgstr "" +msgstr "[Varsayılan değer: 0] > " msgid "To be able to use this translation, please install a font manually that supports the language." msgstr "Bu çeviriyi kullanabilmek için lütfen dili destekleyen bir yazı tipini manuel olarak yükleyin." @@ -885,11 +885,11 @@ msgstr "Yapılandırma(lar)ın kaydedilmesi için bir dizin girin: " #, fuzzy msgid "Minimum capacity for /home partition: {}GiB\n" -msgstr "/home disk bölümü için minimum kapasite: {}GB\n" +msgstr "/home disk bölümü için minimum kapasite: {}GiB\n" #, fuzzy msgid "Minimum capacity for Arch Linux partition: {}GiB" -msgstr "Arch Linux disk bölümü için minimum kapasite: {}GB" +msgstr "Arch Linux disk bölümü için minimum kapasite: {}GiB" #, fuzzy msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" @@ -905,11 +905,11 @@ msgstr "Yeni eklenen tüm disk bölümleri kaldırın" #, fuzzy msgid "Assign mountpoint" -msgstr "Bir disk bölümü için mount (monte) noktası ata" +msgstr "Bir disk bölümü için monte noktası ata" #, fuzzy msgid "Mark/Unmark to be formatted (wipes data)" -msgstr "Bir disk bölümünü biçimlendirilmek üzere işaretle/işareti kaldır (veriyi temizler)" +msgstr "Bir disk bölümünü biçimlendirilmek üzere işaretle/işareti kaldır (verileri siler)" msgid "Mark/Unmark as bootable" msgstr "Önyüklenebilir olarak İşaretle/İşareti kaldır" @@ -943,7 +943,7 @@ msgid "If mountpoint /boot is set, then the partition will also be marked as boo msgstr "mountpoint /boot ayarlanmışsa, bölüm de önyüklenebilir olarak işaretlenecektir." msgid "Mountpoint: " -msgstr "Bağlama noktası: " +msgstr "Monte etme noktası: " msgid "Current free sectors on device {}:" msgstr "{} cihazındaki mevcut boş sektörler:" @@ -1018,15 +1018,15 @@ msgstr "Disk şifrelemesi" #, fuzzy msgid "Configuration" -msgstr "Yapılandırma yok" +msgstr "Konfigürasyon" #, fuzzy msgid "Password" -msgstr "Root (kök) şifresi" +msgstr "Şifre" #, fuzzy msgid "All settings will be reset, are you sure?" -msgstr "{} işlem sırasında bekleyen disk bölümleri bulunduruyor, bu onları kaldıracak, emin misiniz?" +msgstr "Bütün ayarlar sıfırlanacak, emin misiniz?" msgid "Back" msgstr "Geri" @@ -1137,62 +1137,62 @@ msgstr "Özel indirme sunucusunu silin" #, fuzzy msgid "Enter name (leave blank to skip): " -msgstr "Ek kullanıcı oluşturmak için bir kullanıcı adı girin (geçmek için boş bırakın): " +msgstr "İsim girin (geçmek için boş bırakın): " #, fuzzy msgid "Enter url (leave blank to skip): " -msgstr "Ek kullanıcı oluşturmak için bir kullanıcı adı girin (geçmek için boş bırakın): " +msgstr "url girin (geçmek için boş bırakın): " #, fuzzy msgid "Select signature check option" -msgstr "Eklemek için arayüz seçin" +msgstr "İmzalama kontrol opsiyonu seçin" #, fuzzy msgid "Select signature option" msgstr "İmza seçeneğini seçin" msgid "Custom mirrors" -msgstr "Özel indirme sunucusu bölgesi" +msgstr "Kişisel depo aynası" msgid "Defined" msgstr "Tanımlı" #, fuzzy msgid "Save user configuration (including disk layout)" -msgstr "Kullanıcı yapılandırmasını kaydet" +msgstr "Kullanıcı konfigürasyonunu kaydet (Disk düzeni ile birlikte)" #, fuzzy msgid "" "Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" "Save directory: " -msgstr "Yapılandırma(lar)ın kaydedilmesi için bir dizin girin: " +msgstr "Yapılandırma(lar)ın kaydedilmesi için bir dizin girin (tab ile tamamlama etkin)\n " msgid "" "Do you want to save {} configuration file(s) in the following location?\n" "\n" "{}" -msgstr "" +msgstr "{} konfigürasyon dosya(lar)ını gösterilen konuma kaydetmek ister miydiniz?" #, fuzzy msgid "Saving {} configuration files to {}" -msgstr "Yapılandırmayı kaydet" +msgstr "{} konfigürasyon dosyaları {} konumuna kaydediliyor" #, fuzzy msgid "Mirrors" -msgstr "İndirme sunucusu bölgesi" +msgstr "Depo sunucusu" #, fuzzy msgid "Mirror regions" -msgstr "İndirme sunucusu bölgesi" +msgstr "Deop sunucusu bölgesi" msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr "" +msgstr "Maximum değer : {} ( {} paralel indirmelerine izin verir, Tek seferde {max_download+1} indirmeye izin verir.)" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" -msgstr "Geçersiz girdi! Geçerli bir girişle tekrar deneyin [1 ila {} veya devre dışı bırakmak için 0]" +msgstr "Geçersiz girdi! Geçerli bir girdiyle tekrar deneyin [1 ila {} veya devre dışı bırakmak için 0]" msgid "Locales" -msgstr "" +msgstr "Yerel Ayarlar" #, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" -- cgit v1.2.3-70-g09d2 From 16132e6fc9d54f237f260227f99dad5b639891db Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Wed, 21 Jun 2023 17:52:06 +1000 Subject: Fix 1862 (#1884) * Fix 1862 * Update --------- Co-authored-by: Daniel Girtler --- archinstall/lib/disk/device_model.py | 41 +++++++-- archinstall/lib/disk/partitioning_menu.py | 117 ++++++++++++++---------- archinstall/lib/mirrors.py | 2 +- archinstall/lib/models/network_configuration.py | 2 +- archinstall/lib/output.py | 20 ++-- 5 files changed, 116 insertions(+), 66 deletions(-) diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index 36dd0c4f..8e72390c 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -137,6 +137,10 @@ class Unit(Enum): Percent = '%' # size in percentile + @staticmethod + def get_all_units() -> List[str]: + return [u.name for u in Unit] + @dataclass class Size: @@ -214,16 +218,25 @@ class Size: value = int(self._normalize() / target_unit.value) # type: ignore return Size(value, target_unit) + def as_text(self) -> str: + return self.format_size( + self.unit, + self.sector_size + ) + def format_size( self, target_unit: Unit, - sector_size: Optional[Size] = None + sector_size: Optional[Size] = None, + include_unit: bool = True ) -> str: if self.unit == Unit.Percent: return f'{self.value}%' else: target_size = self.convert(target_unit, sector_size) - return f'{target_size.value} {target_unit.name}' + if include_unit: + return f'{target_size.value} {target_unit.name}' + return f'{target_size.value}' def _normalize(self) -> int: """ @@ -280,7 +293,7 @@ class _PartitionInfo: mountpoints: List[Path] btrfs_subvol_infos: List[_BtrfsSubvolumeInfo] = field(default_factory=list) - def as_json(self) -> Dict[str, Any]: + def table_data(self) -> Dict[str, Any]: part_info = { 'Name': self.name, 'Type': self.type.value, @@ -343,7 +356,7 @@ class _DeviceInfo: read_only: bool dirty: bool - def as_json(self) -> Dict[str, Any]: + def table_data(self) -> Dict[str, Any]: total_free_space = sum([region.get_length(unit=Unit.MiB) for region in self.free_space_regions]) return { 'Model': self.model, @@ -440,7 +453,7 @@ class SubvolumeModification: 'nodatacow': self.nodatacow } - def as_json(self) -> Dict[str, Any]: + def table_data(self) -> Dict[str, Any]: return { 'name': str(self.name), 'mountpoint': str(self.mountpoint), @@ -465,12 +478,20 @@ class DeviceGeometry: def get_length(self, unit: Unit = Unit.sectors) -> int: return self._geometry.getLength(unit.name) - def as_json(self) -> Dict[str, Any]: + def table_data(self) -> Dict[str, Any]: + start = Size(self._geometry.start, Unit.sectors, self._sector_size) + end = Size(self._geometry.end, Unit.sectors, self._sector_size) + length = Size(self._geometry.getLength(), Unit.sectors, self._sector_size) + + start_str = f'{self._geometry.start} / {start.format_size(Unit.B, include_unit=False)}' + end_str = f'{self._geometry.end} / {end.format_size(Unit.B, include_unit=False)}' + length_str = f'{self._geometry.getLength()} / {length.format_size(Unit.B, include_unit=False)}' + return { 'Sector size': self._sector_size.value, - 'Start sector': self._geometry.start, - 'End sector': self._geometry.end, - 'Length': self._geometry.getLength() + 'Start (sector/B)': start_str, + 'End (sector/B)': end_str, + 'Length (sectors/B)': length_str } @@ -700,7 +721,7 @@ class PartitionModification: 'btrfs': [vol.__dump__() for vol in self.btrfs_subvols] } - def as_json(self) -> Dict[str, Any]: + def table_data(self) -> Dict[str, Any]: """ Called for displaying data in table format """ diff --git a/archinstall/lib/disk/partitioning_menu.py b/archinstall/lib/disk/partitioning_menu.py index 89cf6293..4acb4e85 100644 --- a/archinstall/lib/disk/partitioning_menu.py +++ b/archinstall/lib/disk/partitioning_menu.py @@ -1,10 +1,11 @@ from __future__ import annotations +import re from pathlib import Path from typing import Any, Dict, TYPE_CHECKING, List, Optional, Tuple from .device_model import PartitionModification, FilesystemType, BDevice, Size, Unit, PartitionType, PartitionFlag, \ - ModificationStatus + ModificationStatus, DeviceGeometry from ..menu import Menu, ListManager, MenuSelection, TextInput from ..output import FormattedOutput, warn from .subvolume_menu import SubvolumeMenu @@ -192,22 +193,51 @@ class PartitioningList(ListManager): choice = Menu(prompt, options, sort=False, skip=False).run() return options[choice.single_value] - def _validate_sector(self, start_sector: str, end_sector: Optional[str] = None) -> bool: - if not start_sector.isdigit(): - return False + def _validate_value( + self, + sector_size: Size, + total_size: Size, + value: str + ) -> Optional[Size]: + match = re.match(r'([0-9]+)([a-zA-Z|%]*)', value, re.I) + + if match: + value, unit = match.groups() + + if unit == '%': + unit = Unit.Percent.name + + if unit and unit not in Unit.get_all_units(): + return None + + unit = Unit[unit] if unit else Unit.sectors + return Size(int(value), unit, sector_size, total_size) + + return None + + def _enter_size( + self, + sector_size: Size, + total_size: Size, + prompt: str, + default: Size + ) -> Size: + while True: + value = TextInput(prompt).run().strip() + + size: Optional[Size] = None - if end_sector: - if end_sector.endswith('%'): - if not end_sector[:-1].isdigit(): - return False - elif not end_sector.isdigit(): - return False - elif int(start_sector) > int(end_sector): - return False + if not value: + size = default + else: + size = self._validate_value(sector_size, total_size, value) - return True + if size: + return size - def _prompt_sectors(self) -> Tuple[Size, Size]: + warn(f'Invalid value: {value}') + + def _prompt_size(self) -> Tuple[Size, Size]: device_info = self._device.device_info text = str(_('Current free sectors on device {}:')).format(device_info.path) + '\n\n' @@ -215,54 +245,45 @@ class PartitioningList(ListManager): prompt = text + free_space_table + '\n' total_sectors = device_info.total_size.format_size(Unit.sectors, device_info.sector_size) - prompt += str(_('Total sectors: {}')).format(total_sectors) + '\n' + total_bytes = device_info.total_size.format_size(Unit.B) + + prompt += str(_('Total: {} / {}')).format(total_sectors, total_bytes) + '\n\n' + prompt += str(_('All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB...')) + '\n' + prompt += str(_('If no unit is provided, the value is interpreted as sectors')) + '\n' print(prompt) - largest_free_area = max(device_info.free_space_regions, key=lambda r: r.get_length()) + largest_free_area: DeviceGeometry = max(device_info.free_space_regions, key=lambda r: r.get_length()) # prompt until a valid start sector was entered - while True: - start_prompt = str(_('Enter the start sector (default: {}): ')).format(largest_free_area.start) - start_sector = TextInput(start_prompt).run().strip() - - if not start_sector or self._validate_sector(start_sector): - break - - warn(f'Invalid start sector entered: {start_sector}') + default_start = Size(largest_free_area.start, Unit.sectors, device_info.sector_size) + start_prompt = str(_('Enter start (default: sector {}): ')).format(largest_free_area.start) + start_size = self._enter_size( + device_info.sector_size, + device_info.total_size, + start_prompt, + default_start + ) - if not start_sector: - start_sector = str(largest_free_area.start) - end_sector = str(largest_free_area.end) + if start_size.value == largest_free_area.start: + end_size = Size(largest_free_area.end, Unit.sectors, device_info.sector_size) else: - end_sector = '100%' + end_size = Size(100, Unit.Percent, total_size=device_info.total_size) # prompt until valid end sector was entered - while True: - end_prompt = str(_('Enter the end sector of the partition (percentage or block number, default: {}): ')).format(end_sector) - end_value = TextInput(end_prompt).run().strip() - - if not end_value or self._validate_sector(start_sector, end_value): - break - - warn(f'Invalid end sector entered: {start_sector}') - - # override the default value with the user value - if end_value: - end_sector = end_value - - start_size = Size(int(start_sector), Unit.sectors, device_info.sector_size) - - if end_sector.endswith('%'): - end_size = Size(int(end_sector[:-1]), Unit.Percent, device_info.sector_size, device_info.total_size) - else: - end_size = Size(int(end_sector), Unit.sectors, device_info.sector_size) + end_prompt = str(_('Enter end (default: {}): ')).format(end_size.as_text()) + end_size = self._enter_size( + device_info.sector_size, + device_info.total_size, + end_prompt, + end_size + ) return start_size, end_size def _create_new_partition(self) -> PartitionModification: fs_type = self._prompt_partition_fs_type() - start_size, end_size = self._prompt_sectors() + start_size, end_size = self._prompt_size() length = end_size - start_size # new line for the next prompt diff --git a/archinstall/lib/mirrors.py b/archinstall/lib/mirrors.py index 521a8e5b..74cdd0aa 100644 --- a/archinstall/lib/mirrors.py +++ b/archinstall/lib/mirrors.py @@ -30,7 +30,7 @@ class CustomMirror: sign_check: SignCheck sign_option: SignOption - def as_json(self) -> Dict[str, str]: + def table_data(self) -> Dict[str, str]: return { 'Name': self.name, 'Url': self.url, diff --git a/archinstall/lib/models/network_configuration.py b/archinstall/lib/models/network_configuration.py index 93dd1c44..e564b97b 100644 --- a/archinstall/lib/models/network_configuration.py +++ b/archinstall/lib/models/network_configuration.py @@ -39,7 +39,7 @@ class NetworkConfiguration: else: return 'Unknown type' - def as_json(self) -> Dict: + def table_data(self) -> Dict[str, Any]: exclude_fields = ['type'] data = {} for k, v in self.__dict__.items(): diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py index d266afa8..d1c95ec5 100644 --- a/archinstall/lib/output.py +++ b/archinstall/lib/output.py @@ -11,14 +11,16 @@ from .storage import storage class FormattedOutput: + @classmethod - def values( + def _get_values( cls, o: Any, class_formatter: Optional[Union[str, Callable]] = None, filter_list: List[str] = [] ) -> Dict[str, Any]: - """ the original values returned a dataclass as dict thru the call to some specific methods + """ + the original values returned a dataclass as dict thru the call to some specific methods this version allows thru the parameter class_formatter to call a dynamicly selected formatting method. Can transmit a filter list to the class_formatter, """ @@ -33,8 +35,8 @@ class FormattedOutput: return func(filter_list) raise ValueError('Unsupported formatting call') - elif hasattr(o, 'as_json'): - return o.as_json() + elif hasattr(o, 'table_data'): + return o.table_data() elif hasattr(o, 'json'): return o.json() elif is_dataclass(o): @@ -58,7 +60,7 @@ class FormattedOutput: is for compatibility with a print statement As_table_filter can be a drop in replacement for as_table """ - raw_data = [cls.values(o, class_formatter, filter_list) for o in obj] + raw_data = [cls._get_values(o, class_formatter, filter_list) for o in obj] # determine the maximum column size column_width: Dict[str, int] = {} @@ -92,18 +94,24 @@ class FormattedOutput: for key in filter_list: width = column_width.get(key, len(key)) value = record.get(key, '') + if '!' in key: value = '*' * width - if isinstance(value,(int, float)) or (isinstance(value, str) and value.isnumeric()): + + if isinstance(value, (int, float)) or (isinstance(value, str) and value.isnumeric()): obj_data.append(str(value).rjust(width)) else: obj_data.append(str(value).ljust(width)) + output += ' | '.join(obj_data) + '\n' return output @classmethod def as_columns(cls, entries: List[str], cols: int) -> str: + """ + Will format a list into a given number of columns + """ chunks = [] output = '' -- cgit v1.2.3-70-g09d2 From c7c34c9e704b880ba0ad26696946b6561d2ee784 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Wed, 21 Jun 2023 17:52:48 +1000 Subject: Make Gfx driver handling saver (#1885) Co-authored-by: Daniel Girtler --- archinstall/__init__.py | 2 +- archinstall/lib/global_menu.py | 2 +- archinstall/lib/hardware.py | 117 +++++++++++++++++++--------- archinstall/lib/interactions/system_conf.py | 24 +++--- archinstall/lib/profile/profile_menu.py | 15 ++-- archinstall/lib/profile/profile_model.py | 8 +- archinstall/lib/profile/profiles_handler.py | 11 +-- 7 files changed, 113 insertions(+), 66 deletions(-) diff --git a/archinstall/__init__.py b/archinstall/__init__.py index 305d4096..ddf81824 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -18,7 +18,7 @@ from .lib import profile from .lib import interactions from . import default_profiles -from .lib.hardware import SysInfo, AVAILABLE_GFX_DRIVERS +from .lib.hardware import SysInfo, GfxDriver from .lib.installer import Installer, accessibility_tools_in_use from .lib.output import FormattedOutput, log, error, debug, warn, info from .lib.storage import storage diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 91ebc6a0..54b30240 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -349,7 +349,7 @@ class GlobalMenu(AbstractMenu): output += profile_config.profile.name + '\n' if profile_config.gfx_driver: - output += str(_('Graphics driver')) + ': ' + profile_config.gfx_driver + '\n' + output += str(_('Graphics driver')) + ': ' + profile_config.gfx_driver.value + '\n' if profile_config.greeter: output += str(_('Greeter')) + ': ' + profile_config.greeter.value + '\n' diff --git a/archinstall/lib/hardware.py b/archinstall/lib/hardware.py index bd153a63..85f903e1 100644 --- a/archinstall/lib/hardware.py +++ b/archinstall/lib/hardware.py @@ -1,4 +1,5 @@ import os +from enum import Enum from functools import cached_property from pathlib import Path from typing import Optional, Dict, List @@ -8,43 +9,85 @@ from .general import SysCommand from .networking import list_interfaces, enrich_iface_types from .output import debug -AVAILABLE_GFX_DRIVERS = { - # Sub-dicts are layer-2 options to be selected - # and lists are a list of packages to be installed - "All open-source (default)": [ - "mesa", - "xf86-video-amdgpu", - "xf86-video-ati", - "xf86-video-nouveau", - "xf86-video-vmware", - "libva-mesa-driver", - "libva-intel-driver", - "intel-media-driver", - "vulkan-radeon", - "vulkan-intel", - ], - "AMD / ATI (open-source)": [ - "mesa", - "xf86-video-amdgpu", - "xf86-video-ati", - "libva-mesa-driver", - "vulkan-radeon", - ], - "Intel (open-source)": [ - "mesa", - "libva-intel-driver", - "intel-media-driver", - "vulkan-intel", - ], - "Nvidia (open kernel module for newer GPUs, Turing+)": ["nvidia-open"], - "Nvidia (open-source nouveau driver)": [ - "mesa", - "xf86-video-nouveau", - "libva-mesa-driver" - ], - "Nvidia (proprietary)": ["nvidia"], - "VMware / VirtualBox (open-source)": ["mesa", "xf86-video-vmware"], -} + +class GfxPackage(Enum): + IntelMediaDriver = 'intel-media-driver' + LibvaIntelDriver = 'libva-intel-driver' + LibvaMesaDriver = 'libva-mesa-driver' + Mesa = "mesa" + Nvidia = 'nvidia' + NvidiaOpen = 'nvidia-open' + VulkanIntel = 'vulkan-intel' + VulkanRadeon = 'vulkan-radeon' + Xf86VideoAmdgpu = "xf86-video-amdgpu" + Xf86VideoAti = "xf86-video-ati" + Xf86VideoNouveau = 'xf86-video-nouveau' + Xf86VideoVmware = 'xf86-video-vmware' + + +class GfxDriver(Enum): + AllOpenSource = 'All open-source' + AmdOpenSource = 'AMD / ATI (open-source)' + IntelOpenSource = 'Intel (open-source)' + NvidiaOpenKernel = 'Nvidia (open kernel module for newer GPUs, Turing+)' + NvidiaOpenSource = 'Nvidia (open-source nouveau driver)' + NvidiaProprietary = 'Nvidia (proprietary)' + VMOpenSource = 'VMware / VirtualBox (open-source)' + + def is_nvidia(self) -> bool: + match self: + case GfxDriver.NvidiaProprietary | \ + GfxDriver.NvidiaOpenSource | \ + GfxDriver.NvidiaOpenKernel: + return True + case _: + return False + + def packages(self) -> List[GfxPackage]: + match self: + case GfxDriver.AllOpenSource: + return [ + GfxPackage.Mesa, + GfxPackage.Xf86VideoAmdgpu, + GfxPackage.Xf86VideoAti, + GfxPackage.Xf86VideoNouveau, + GfxPackage.Xf86VideoVmware, + GfxPackage.LibvaMesaDriver, + GfxPackage.LibvaIntelDriver, + GfxPackage.IntelMediaDriver, + GfxPackage.VulkanRadeon, + GfxPackage.VulkanIntel + ] + case GfxDriver.AmdOpenSource: + return [ + GfxPackage.Mesa, + GfxPackage.Xf86VideoAmdgpu, + GfxPackage.Xf86VideoAti, + GfxPackage.LibvaMesaDriver, + GfxPackage.VulkanRadeon + ] + case GfxDriver.IntelOpenSource: + return [ + GfxPackage.Mesa, + GfxPackage.LibvaIntelDriver, + GfxPackage.IntelMediaDriver, + GfxPackage.VulkanIntel + ] + case GfxDriver.NvidiaOpenKernel: + return [GfxPackage.NvidiaOpen] + case GfxDriver.NvidiaOpenSource: + return [ + GfxPackage.Mesa, + GfxPackage.Xf86VideoNouveau, + GfxPackage.LibvaMesaDriver + ] + case GfxDriver.NvidiaProprietary: + return [GfxPackage.Nvidia] + case GfxDriver.VMOpenSource: + return [ + GfxPackage.Mesa, + GfxPackage.Xf86VideoVmware + ] class _SysInfo: diff --git a/archinstall/lib/interactions/system_conf.py b/archinstall/lib/interactions/system_conf.py index ea7e5989..5b1bc456 100644 --- a/archinstall/lib/interactions/system_conf.py +++ b/archinstall/lib/interactions/system_conf.py @@ -1,8 +1,8 @@ from __future__ import annotations -from typing import List, Any, Dict, TYPE_CHECKING, Optional +from typing import List, Any, TYPE_CHECKING, Optional -from ..hardware import AVAILABLE_GFX_DRIVERS, SysInfo +from ..hardware import SysInfo, GfxDriver from ..menu import MenuSelectionType, Menu from ..models.bootloader import Bootloader @@ -65,7 +65,7 @@ def ask_for_bootloader(preset: Bootloader) -> Bootloader: return preset -def select_driver(options: Dict[str, Any] = {}, current_value: Optional[str] = None) -> Optional[str]: +def select_driver(options: List[GfxDriver] = [], current_value: Optional[GfxDriver] = None) -> Optional[GfxDriver]: """ Some what convoluted function, whose job is simple. Select a graphics driver from a pre-defined set of popular options. @@ -73,11 +73,10 @@ def select_driver(options: Dict[str, Any] = {}, current_value: Optional[str] = N (The template xorg is for beginner users, not advanced, and should there for appeal to the general public first and edge cases later) """ - if not options: - options = AVAILABLE_GFX_DRIVERS + options = [driver for driver in GfxDriver] - drivers = sorted(list(options.keys())) + drivers = sorted([o.value for o in options]) if drivers: title = '' @@ -90,13 +89,18 @@ def select_driver(options: Dict[str, Any] = {}, current_value: Optional[str] = N title += str(_('\nSelect a graphics driver or leave blank to install all open-source drivers')) - preset = current_value if current_value else None - choice = Menu(title, drivers, preset_values=preset).run() + preset = current_value.value if current_value else None + choice = Menu( + title, + drivers, + preset_values=preset, + default_option=GfxDriver.AllOpenSource.value + ).run() if choice.type_ != MenuSelectionType.Selection: - return None + return current_value - return choice.value # type: ignore + return GfxDriver(choice.single_value) return current_value diff --git a/archinstall/lib/profile/profile_menu.py b/archinstall/lib/profile/profile_menu.py index 213466a6..079a9817 100644 --- a/archinstall/lib/profile/profile_menu.py +++ b/archinstall/lib/profile/profile_menu.py @@ -4,9 +4,9 @@ from typing import TYPE_CHECKING, Any, Optional, Dict from archinstall.default_profiles.profile import Profile, GreeterType from .profile_model import ProfileConfiguration -from ..hardware import AVAILABLE_GFX_DRIVERS from ..menu import Menu, MenuSelectionType, AbstractSubMenu, Selector from ..interactions.system_conf import select_driver +from ..hardware import GfxDriver if TYPE_CHECKING: _: Any @@ -38,7 +38,7 @@ class ProfileMenu(AbstractSubMenu): self._menu_options['gfx_driver'] = Selector( _('Graphics driver'), lambda preset: self._select_gfx_driver(preset), - display_func=lambda x: x if x else None, + display_func=lambda x: x.value if x else None, dependencies=['profile'], default=self._preset.gfx_driver if self._preset.profile and self._preset.profile.is_graphic_driver_supported() else None, enabled=self._preset.profile.is_graphic_driver_supported() if self._preset.profile else False @@ -73,7 +73,7 @@ class ProfileMenu(AbstractSubMenu): self._menu_options['gfx_driver'].set_current_selection(None) else: self._menu_options['gfx_driver'].set_enabled(True) - self._menu_options['gfx_driver'].set_current_selection('All open-source (default)') + self._menu_options['gfx_driver'].set_current_selection(GfxDriver.AllOpenSource) if not profile.is_greeter_supported(): self._menu_options['greeter'].set_enabled(False) @@ -87,7 +87,7 @@ class ProfileMenu(AbstractSubMenu): return profile - def _select_gfx_driver(self, preset: Optional[str] = None) -> Optional[str]: + def _select_gfx_driver(self, preset: Optional[GfxDriver] = None) -> Optional[GfxDriver]: driver = preset profile: Optional[Profile] = self._menu_options['profile'].current_selection @@ -96,11 +96,8 @@ class ProfileMenu(AbstractSubMenu): driver = select_driver(current_value=preset) if driver and 'Sway' in profile.current_selection_names(): - packages = AVAILABLE_GFX_DRIVERS[driver] - - if packages and "nvidia" in packages: - prompt = str( - _('The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?')) + if driver.is_nvidia(): + prompt = str(_('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(), skip=False).run() if choice.value == Menu.no(): diff --git a/archinstall/lib/profile/profile_model.py b/archinstall/lib/profile/profile_model.py index ad3015ae..2b52073a 100644 --- a/archinstall/lib/profile/profile_model.py +++ b/archinstall/lib/profile/profile_model.py @@ -3,6 +3,7 @@ from __future__ import annotations from dataclasses import dataclass from typing import TYPE_CHECKING, Any, Optional, Dict +from ..hardware import GfxDriver from archinstall.default_profiles.profile import Profile, GreeterType if TYPE_CHECKING: @@ -12,14 +13,14 @@ if TYPE_CHECKING: @dataclass class ProfileConfiguration: profile: Optional[Profile] = None - gfx_driver: Optional[str] = None + gfx_driver: Optional[GfxDriver] = None greeter: Optional[GreeterType] = None def json(self) -> Dict[str, Any]: from .profiles_handler import profile_handler return { 'profile': profile_handler.to_json(self.profile), - 'gfx_driver': self.gfx_driver, + 'gfx_driver': self.gfx_driver.value if self.gfx_driver else None, 'greeter': self.greeter.value if self.greeter else None } @@ -27,9 +28,10 @@ class ProfileConfiguration: def parse_arg(cls, arg: Dict[str, Any]) -> 'ProfileConfiguration': from .profiles_handler import profile_handler greeter = arg.get('greeter', None) + gfx_driver = arg.get('gfx_driver', None) return ProfileConfiguration( profile_handler.parse_profile_config(arg['profile']), - arg.get('gfx_driver', None), + GfxDriver(gfx_driver) if gfx_driver else None, GreeterType(greeter) if greeter else None ) diff --git a/archinstall/lib/profile/profiles_handler.py b/archinstall/lib/profile/profiles_handler.py index 2cc15d8e..4e7c3d2b 100644 --- a/archinstall/lib/profile/profiles_handler.py +++ b/archinstall/lib/profile/profiles_handler.py @@ -11,7 +11,7 @@ from typing import List, TYPE_CHECKING, Any, Optional, Dict, Union from archinstall.default_profiles.profile import Profile, TProfile, GreeterType from .profile_model import ProfileConfiguration -from ..hardware import AVAILABLE_GFX_DRIVERS +from ..hardware import GfxDriver, GfxPackage from ..menu import MenuSelectionType, Menu, MenuSelection from ..networking import list_interfaces, fetch_data_from_url from ..output import error, debug, info, warn @@ -188,17 +188,18 @@ class ProfileHandler: if service: install_session.enable_service(service) - def install_gfx_driver(self, install_session: 'Installer', driver: str): + def install_gfx_driver(self, install_session: 'Installer', driver: Optional[GfxDriver]): try: - driver_pkgs = AVAILABLE_GFX_DRIVERS[driver] if driver else [] - additional_pkg = ' '.join(['xorg-server', 'xorg-xinit'] + driver_pkgs) + driver_pkgs = driver.packages() if driver else [] + pkg_names = [p.value for p in driver_pkgs] + additional_pkg = ' '.join(['xorg-server', 'xorg-xinit'] + pkg_names) if driver is not None: # Find the intersection between the set of known nvidia drivers # and the selected driver packages. Since valid intesections can # only have one element or none, we iterate and try to take the # first element. - if driver_pkg := next(iter({'nvidia','nvidia-open'} & set(driver_pkgs)), None): + if driver_pkg := next(iter({GfxPackage.Nvidia, GfxPackage.NvidiaOpen} & set(driver_pkgs)), None): if any(kernel in install_session.base_packages for kernel in ("linux-lts", "linux-zen")): for kernel in install_session.kernels: # Fixes https://github.com/archlinux/archinstall/issues/585 -- cgit v1.2.3-70-g09d2 From 4eae8f656f072c2f71e8825519f3eed800e9db37 Mon Sep 17 00:00:00 2001 From: Diogo Silva Date: Wed, 21 Jun 2023 04:53:29 -0300 Subject: chore: update translation for pt_BR (#1886) --- archinstall/locales/pt_BR/LC_MESSAGES/base.mo | Bin 28310 -> 31068 bytes archinstall/locales/pt_BR/LC_MESSAGES/base.po | 47 ++++++++++++++------------ 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo index 6deacbcd..c990ab44 100644 Binary files a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo and b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.po b/archinstall/locales/pt_BR/LC_MESSAGES/base.po index 8daeb76c..064daed2 100644 --- a/archinstall/locales/pt_BR/LC_MESSAGES/base.po +++ b/archinstall/locales/pt_BR/LC_MESSAGES/base.po @@ -2,12 +2,14 @@ # @Cain-dev (cain-dev.github.io) # Rafael Fontenelle # Jefferson Michael +# Diogo Silva + msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" -"Last-Translator: Jefferson Michael \n" +"Last-Translator: Diogo Silva \n" "Language-Team: \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" @@ -841,17 +843,17 @@ msgid "The font should be stored as {}" msgstr "A fonte deve ser armazenada como {}" msgid "Archinstall requires root privileges to run. See --help for more." -msgstr "" +msgstr "O Archinstall requer privilégios de root para ser executado. Consulte --help para mais informações." #, fuzzy msgid "Select an execution mode" msgstr "Selecione uma ação para '{}'" msgid "Unable to fetch profile from specified url: {}" -msgstr "" +msgstr "Não foi possível obter o perfil a partir da URL especificada: {}" msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" -msgstr "" +msgstr "Os perfis devem ter nomes únicos, mas foram encontradas definições de perfil com nomes duplicados: {}" #, fuzzy msgid "Select one or more devices to use and configure" @@ -923,20 +925,20 @@ msgid "Partition" msgstr "Partição: " msgid "This partition is currently encrypted, to format it a filesystem has to be specified" -msgstr "" +msgstr "Esta partição está criptografada. Para formatá-la, um sistema de arquivos deve ser especificado" #, fuzzy msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." msgstr " * Os pontos de montagem das partições são relativos aos de dentro da instalação, boot por exemplo seria /boot." msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." -msgstr "" +msgstr "Se o ponto de montagem /boot for definido, a partição também será marcada como inicializável." msgid "Mountpoint: " msgstr "Ponto de montagem: " msgid "Current free sectors on device {}:" -msgstr "" +msgstr "Setores livres no dispositivo {}:" #, fuzzy msgid "Total sectors: {}" @@ -951,10 +953,10 @@ msgid "Enter the end sector of the partition (percentage or block number, defaul msgstr "Digite o setor final da partição (porcentagem ou número de bloco, ex.: {}): " msgid "This will remove all newly added partitions, continue?" -msgstr "" +msgstr "Isso irá remover todas as partições recém-adicionadas, continuar?" msgid "Partition management: {}" -msgstr "" +msgstr "Gerenciamento de partições: {}" msgid "Total length: {}" msgstr "Tamanho total: {}" @@ -1020,13 +1022,13 @@ msgid "Back" msgstr "Voltar" msgid "Please chose which greeter to install for the chosen profiles: {}" -msgstr "" +msgstr "Por favor, escolha qual greeter instalar para os perfis escolhidos: {}" msgid "Environment type: {}" -msgstr "" +msgstr "Tipo de ambiente: {}" msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" -msgstr "" +msgstr "O driver proprietário Nvidia não é suportado pelo Sway. É provável que você encontre problemas. Você está de acordo com isso?" #, fuzzy msgid "Installed packages" @@ -1061,7 +1063,7 @@ msgid "Services to be enabled with this profile (space separated, leave blank to msgstr "Digite pacotes adicionais para instalar (separados por espaço, deixe em branco para pular): " msgid "Should this profile be enabled for installation?" -msgstr "" +msgstr "Este perfil deve ser ativado para instalação?" msgid "Create your own" msgstr "Crie o seu próprio" @@ -1076,13 +1078,16 @@ msgstr "" "Selecione um driver de vídeo ou deixe em branco para instalar os drivers completamente open-source" msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" -msgstr "" +msgstr "O Sway precisa de acesso ao seu seat (conjunto de dispositivos de hardware, como teclado, mouse, etc)" msgid "" "\n" "\n" "Choose an option to give Sway access to your hardware" msgstr "" +"\n" +"\n" +"Selecione uma opção para permitir o acesso do Sway ao seu hardware." msgid "Graphics driver" msgstr "Driver gráfico" @@ -1091,10 +1096,10 @@ msgid "Greeter" msgstr "" msgid "Please chose which greeter to install" -msgstr "" +msgstr "Por favor, escolha qual greeter instalar" msgid "This is a list of pre-programmed default_profiles" -msgstr "" +msgstr "Esta é uma lista de perfis pré-programados (default_profiles)" #, fuzzy msgid "Disk configuration" @@ -1116,10 +1121,10 @@ msgid "Add a custom mirror" msgstr "Adicionar usuário" msgid "Change custom mirror" -msgstr "" +msgstr "Alterar mirror personalizado" msgid "Delete custom mirror" -msgstr "" +msgstr "Excluir mirror personalizado" #, fuzzy msgid "Enter name (leave blank to skip): " @@ -1138,10 +1143,10 @@ msgid "Select signature option" msgstr "Selecione a opção de encriptação de disco" msgid "Custom mirrors" -msgstr "" +msgstr "Mirrors personalizados" msgid "Defined" -msgstr "" +msgstr "Definido" #, fuzzy msgid "Save user configuration (including disk layout)" @@ -1181,7 +1186,7 @@ msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {}, ou 0 para desativar]" msgid "Locales" -msgstr "" +msgstr "Localidades" #, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" -- cgit v1.2.3-70-g09d2 From 885f89c3a15ef9d5ed8711cdb270ed5aea189705 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Wed, 21 Jun 2023 17:54:42 +1000 Subject: Rename encryption method (#1888) * Rename encryption method * Update --------- Co-authored-by: Daniel Girtler --- archinstall/lib/disk/device_model.py | 7 +++---- archinstall/lib/disk/encryption_menu.py | 2 +- archinstall/scripts/minimal.py | 2 +- examples/full_automated_installation.py | 2 +- examples/minimal_installation.py | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index 8e72390c..35fbd40c 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -777,13 +777,12 @@ class DeviceModification: class EncryptionType(Enum): NoEncryption = "no_encryption" - Partition = "partition" + Luks = "luks" @classmethod def _encryption_type_mapper(cls) -> Dict[str, 'EncryptionType']: return { - # str(_('Full disk encryption')): EncryptionType.FullDiskEncryption, - str(_('Partition encryption')): EncryptionType.Partition + 'Luks': EncryptionType.Luks } @classmethod @@ -800,7 +799,7 @@ class EncryptionType(Enum): @dataclass class DiskEncryption: - encryption_type: EncryptionType = EncryptionType.Partition + encryption_type: EncryptionType = EncryptionType.Luks encryption_password: str = '' partitions: List[PartitionModification] = field(default_factory=list) hsm_device: Optional[Fido2Device] = None diff --git a/archinstall/lib/disk/encryption_menu.py b/archinstall/lib/disk/encryption_menu.py index 8c64e65e..89eade2b 100644 --- a/archinstall/lib/disk/encryption_menu.py +++ b/archinstall/lib/disk/encryption_menu.py @@ -110,7 +110,7 @@ class DiskEncryptionMenu(AbstractSubMenu): def select_encryption_type(preset: EncryptionType) -> Optional[EncryptionType]: title = str(_('Select disk encryption option')) options = [ - EncryptionType.type_to_text(EncryptionType.Partition) + EncryptionType.type_to_text(EncryptionType.Luks) ] preset_value = EncryptionType.type_to_text(preset) diff --git a/archinstall/scripts/minimal.py b/archinstall/scripts/minimal.py index 704759fc..f6650ff8 100644 --- a/archinstall/scripts/minimal.py +++ b/archinstall/scripts/minimal.py @@ -82,7 +82,7 @@ def parse_disk_encryption(): partitions += list(filter(lambda x: x.mountpoint != Path('/boot'), mod.partitions)) archinstall.arguments['disk_encryption'] = disk.DiskEncryption( - encryption_type=disk.EncryptionType.Partition, + encryption_type=disk.EncryptionType.Luks, encryption_password=enc_password, partitions=partitions ) diff --git a/examples/full_automated_installation.py b/examples/full_automated_installation.py index dcef731a..79e85348 100644 --- a/examples/full_automated_installation.py +++ b/examples/full_automated_installation.py @@ -63,7 +63,7 @@ disk_config = disk.DiskLayoutConfiguration( # disk encryption configuration (Optional) disk_encryption = disk.DiskEncryption( encryption_password="enc_password", - encryption_type=disk.EncryptionType.Partition, + encryption_type=disk.EncryptionType.Luks, partitions=[home_partition], hsm_device=None ) diff --git a/examples/minimal_installation.py b/examples/minimal_installation.py index e31adea4..c91a5d46 100644 --- a/examples/minimal_installation.py +++ b/examples/minimal_installation.py @@ -66,7 +66,7 @@ def parse_disk_encryption(): partitions += list(filter(lambda x: x.mountpoint != Path('/boot'), mod.partitions)) archinstall.arguments['disk_encryption'] = disk.DiskEncryption( - encryption_type=disk.EncryptionType.Partition, + encryption_type=disk.EncryptionType.Luks, encryption_password=enc_password, partitions=partitions ) -- cgit v1.2.3-70-g09d2 From 23f98082cc5bb2703f4c31d5c42c299af18598ea Mon Sep 17 00:00:00 2001 From: mosfet80 Date: Wed, 21 Jun 2023 09:55:17 +0200 Subject: Update autoflake (#1889) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 002defbb..32cc9eec 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ default_stages: ['commit'] repos: - repo: https://github.com/pycqa/autoflake - rev: v2.0.2 + rev: v2.1.1 hooks: - id: autoflake args: [ -- cgit v1.2.3-70-g09d2 From 72661dbf9bfbd12e42425066f9d5ce7c887f40c7 Mon Sep 17 00:00:00 2001 From: Himadri Bhattacharjee <107522312+lavafroth@users.noreply.github.com> Date: Thu, 22 Jun 2023 12:09:14 +0000 Subject: Simplify object serialization before JSON encoding (#1871) * fix: check for helper functions for unsafe encode before falling back to safe encoding * feat: merge _encode and _unsafe_encode into simple serialization function to avoid immediate json.loads after json.dumps * fix: use function instead of a serializing class without trying to serialize keys that are unhashable or unsupported * feat: lazily evaluate serialized value based on key validity * feat: use dictionary comprehension and predefined compatible types * fix: handle enum types immediately after dicts * fix: return stringified object as a default * doc: update function docstring for serialize_to_dict * fix: rename serialize_to_dict to jsonify as it serializes to other primitive types as well --- archinstall/__init__.py | 2 +- archinstall/lib/general.py | 105 ++++++++++++++++----------------------------- 2 files changed, 37 insertions(+), 70 deletions(-) diff --git a/archinstall/__init__.py b/archinstall/__init__.py index ddf81824..e5ec462a 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -30,7 +30,7 @@ from .lib.configuration import ConfigurationOutput from .lib.general import ( generate_password, locate_binary, clear_vt100_escape_codes, - JsonEncoder, JSON, UNSAFE_JSON, SysCommandWorker, SysCommand, + JSON, UNSAFE_JSON, SysCommandWorker, SysCommand, run_custom_user_commands, json_stream_to_structure, secret ) diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py index 777ee90e..f43d4f57 100644 --- a/archinstall/lib/general.py +++ b/archinstall/lib/general.py @@ -15,6 +15,7 @@ import urllib.request import urllib.error import pathlib from datetime import datetime, date +from enum import Enum from typing import Callable, Optional, Dict, Any, List, Union, Iterator, TYPE_CHECKING from select import epoll, EPOLLIN, EPOLLHUP @@ -57,89 +58,55 @@ def clear_vt100_escape_codes(data :Union[bytes, str]) -> Union[bytes, str]: return data -class JsonEncoder: - @staticmethod - def _encode(obj :Any) -> Any: - """ - This JSON encoder function will try it's best to convert - any archinstall data structures, instances or variables into - something that's understandable by the json.parse()/json.loads() lib. - - _encode() will skip any dictionary key starting with an exclamation mark (!) - """ - if isinstance(obj, dict): - # We'll need to iterate not just the value that default() usually gets passed - # But also iterate manually over each key: value pair in order to trap the keys. - - copy = {} - for key, val in list(obj.items()): - if isinstance(val, dict): - # This, is a EXTREMELY ugly hack.. but it's the only quick way I can think of to trigger a encoding of sub-dictionaries. - val = json.loads(json.dumps(val, cls=JSON)) - else: - val = JsonEncoder._encode(val) - - if type(key) == str and key[0] == '!': - pass - else: - copy[JsonEncoder._encode(key)] = val - return copy - elif hasattr(obj, 'json'): - # json() is a friendly name for json-helper, it should return - # a dictionary representation of the object so that it can be - # processed by the json library. - return json.loads(json.dumps(obj.json(), cls=JSON)) - elif hasattr(obj, '__dump__'): - return obj.__dump__() - elif isinstance(obj, (datetime, date)): - return obj.isoformat() - elif isinstance(obj, (list, set, tuple)): - return [json.loads(json.dumps(item, cls=JSON)) for item in obj] - elif isinstance(obj, pathlib.Path): - return str(obj) - else: - return obj - - @staticmethod - def _unsafe_encode(obj :Any) -> Any: - """ - Same as _encode() but it keeps dictionary keys starting with ! - """ - if isinstance(obj, dict): - copy = {} - for key, val in list(obj.items()): - if isinstance(val, dict): - # This, is a EXTREMELY ugly hack.. but it's the only quick way I can think of to trigger a encoding of sub-dictionaries. - val = json.loads(json.dumps(val, cls=UNSAFE_JSON)) - else: - val = JsonEncoder._unsafe_encode(val) - - copy[JsonEncoder._unsafe_encode(key)] = val - return copy - else: - return JsonEncoder._encode(obj) +def jsonify(obj: Any, safe: bool = True) -> Any: + """ + Converts objects into json.dumps() compatible nested dictionaries. + Setting safe to True skips dictionary keys starting with a bang (!) + """ + compatible_types = str, int, float, bool + if isinstance(obj, dict): + return { + key: jsonify(value, safe) + for key, value in obj.items() + if isinstance(key, compatible_types) + and not (isinstance(key, str) and key.startswith("!") and safe) + } + if isinstance(obj, Enum): + return obj.value + if hasattr(obj, 'json'): + # json() is a friendly name for json-helper, it should return + # a dictionary representation of the object so that it can be + # processed by the json library. + return jsonify(obj.json(), safe) + if hasattr(obj, '__dump__'): + return obj.__dump__() + if isinstance(obj, (datetime, date)): + return obj.isoformat() + if isinstance(obj, (list, set, tuple)): + return [jsonify(item, safe) for item in obj] + if isinstance(obj, pathlib.Path): + return str(obj) + if hasattr(obj, "__dict__"): + return vars(obj) + return str(obj) class JSON(json.JSONEncoder, json.JSONDecoder): """ A safe JSON encoder that will omit private information in dicts (starting with !) """ - def _encode(self, obj :Any) -> Any: - return JsonEncoder._encode(obj) - def encode(self, obj :Any) -> Any: - return super(JSON, self).encode(self._encode(obj)) + def encode(self, obj: Any) -> str: + return super().encode(jsonify(obj)) class UNSAFE_JSON(json.JSONEncoder, json.JSONDecoder): """ UNSAFE_JSON will call/encode and keep private information in dicts (starting with !) """ - def _encode(self, obj :Any) -> Any: - return JsonEncoder._unsafe_encode(obj) - def encode(self, obj :Any) -> Any: - return super(UNSAFE_JSON, self).encode(self._encode(obj)) + def encode(self, obj: Any) -> str: + return super().encode(jsonify(obj, safe=False)) class SysCommandWorker: -- cgit v1.2.3-70-g09d2 From 748f03cdb1fbd28becf2d455d7137b21934f84fe Mon Sep 17 00:00:00 2001 From: ArtikusHG <24320212+ArtikusHG@users.noreply.github.com> Date: Thu, 22 Jun 2023 20:49:08 +0000 Subject: Fix crash when libfido2 is not installed (#1893) Co-authored-by: ArtikusHG --- archinstall/lib/disk/fido.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/archinstall/lib/disk/fido.py b/archinstall/lib/disk/fido.py index 97c38d84..83b85d08 100644 --- a/archinstall/lib/disk/fido.py +++ b/archinstall/lib/disk/fido.py @@ -36,7 +36,11 @@ class Fido2: # to prevent continous reloading which will slow # down moving the cursor in the menu if not cls._loaded or reload: - ret: Optional[str] = SysCommand(f"systemd-cryptenroll --fido2-device=list").decode('UTF-8') + ret = "" + try: + ret = Optional[str] = SysCommand(f"systemd-cryptenroll --fido2-device=list").decode('UTF-8') + except: + error('fido2 support is most likely not installed') if not ret: error('Unable to retrieve fido2 devices') return [] -- cgit v1.2.3-70-g09d2 From 27a474ed4a2ac8c2cc001f7bb969fee494636af4 Mon Sep 17 00:00:00 2001 From: Himadri Bhattacharjee <107522312+lavafroth@users.noreply.github.com> Date: Fri, 23 Jun 2023 07:46:30 +0000 Subject: fix: define ret as Optional[str] before reassignment in get_fido2_devices (#1894) --- archinstall/lib/disk/fido.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/archinstall/lib/disk/fido.py b/archinstall/lib/disk/fido.py index 83b85d08..d8c8fd3c 100644 --- a/archinstall/lib/disk/fido.py +++ b/archinstall/lib/disk/fido.py @@ -36,9 +36,9 @@ class Fido2: # to prevent continous reloading which will slow # down moving the cursor in the menu if not cls._loaded or reload: - ret = "" + ret: Optional[str] = None try: - ret = Optional[str] = SysCommand(f"systemd-cryptenroll --fido2-device=list").decode('UTF-8') + ret = SysCommand("systemd-cryptenroll --fido2-device=list").decode('UTF-8') except: error('fido2 support is most likely not installed') if not ret: -- cgit v1.2.3-70-g09d2 From 82a53207647bca3d8aa797619b22b0a2dd68897b Mon Sep 17 00:00:00 2001 From: Rodrigo Broggi Date: Sun, 25 Jun 2023 06:06:52 -0300 Subject: Document new pip install flag (#1897) See [this thead](https://github.com/archlinux/archinstall/issues/1734#issuecomment-1605709792) Including `--break-operating-system` flag in `pip install` instructions in doc. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 15afdda4..9d6fecd9 100644 --- a/README.md +++ b/README.md @@ -207,7 +207,7 @@ you can replace the version of archinstall with a new version and run that with 4. Now clone the latest repository with `git clone https://github.com/archlinux/archinstall` 5. Enter the repository with `cd archinstall` *At this stage, you can choose to check out a feature branch for instance with `git checkout v2.3.1-rc1`* -6. Build the project and install it using `pip install` +6. Build the project and install it using `pip install --break-operating-system .` After this, running archinstall with `python -m archinstall` will run against whatever branch you chose in step 5. -- cgit v1.2.3-70-g09d2 From 57ebc42ffd64babb121c940caa3c5ff415062162 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Wed, 28 Jun 2023 21:34:54 +1000 Subject: Fix 1875 (#1880) Co-authored-by: Daniel Girtler --- archinstall/lib/disk/device_handler.py | 41 +++++++++++++---------- archinstall/lib/disk/device_model.py | 19 ++++++++--- archinstall/lib/installer.py | 61 +++++++++++++++++----------------- pyproject.toml | 1 + 4 files changed, 69 insertions(+), 53 deletions(-) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index 4341c53c..2c88e382 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -238,41 +238,46 @@ class DeviceHandler(object): info(f'luks2 locking device: {dev_path}') luks_handler.lock() + def _validate(self, device_mod: DeviceModification): + checks = { + # verify that all partitions have a path set (which implies that they have been created) + lambda x: x.dev_path is None: ValueError('When formatting, all partitions must have a path set'), + # crypto luks is not a valid file system type + lambda x: x.fs_type is FilesystemType.Crypto_luks: ValueError('Crypto luks cannot be set as a filesystem type'), + # file system type must be set + lambda x: x.fs_type is None: ValueError('File system type must be set for modification') + } + + for check, exc in checks.items(): + found = next(filter(check, device_mod.partitions), None) + if found is not None: + raise exc + def format( self, - modification: DeviceModification, + device_mod: DeviceModification, enc_conf: Optional['DiskEncryption'] = None ): """ Format can be given an overriding path, for instance /dev/null to test the formatting functionality and in essence the support for the given filesystem. """ - - # verify that all partitions have a path set (which implies that they have been created) - missing_path = next(filter(lambda x: x.dev_path is None, modification.partitions), None) - if missing_path is not None: - raise ValueError('When formatting, all partitions must have a path set') - - # crypto luks is not known to parted and can therefore not - # be used as a filesystem type in that sense; - invalid_fs_type = next(filter(lambda x: x.fs_type is FilesystemType.Crypto_luks, modification.partitions), None) - if invalid_fs_type is not None: - raise ValueError('Crypto luks cannot be set as a filesystem type') + self._validate(device_mod) # make sure all devices are unmounted - self._umount_all_existing(modification) + self._umount_all_existing(device_mod) - for part_mod in modification.partitions: + for part_mod in device_mod.partitions: # partition will be encrypted if enc_conf is not None and part_mod in enc_conf.partitions: self._perform_enc_formatting( part_mod.safe_dev_path, part_mod.mapper_name, - part_mod.fs_type, + part_mod.safe_fs_type, enc_conf ) else: - self._perform_formatting(part_mod.fs_type, part_mod.safe_dev_path) + self._perform_formatting(part_mod.safe_fs_type, part_mod.safe_dev_path) def _perform_partitioning( self, @@ -312,7 +317,7 @@ class DeviceHandler(object): length=length_sector.value ) - filesystem = FileSystem(type=part_mod.fs_type.value, geometry=geometry) + filesystem = FileSystem(type=part_mod.safe_fs_type.value, geometry=geometry) partition = Partition( disk=disk, @@ -325,7 +330,7 @@ class DeviceHandler(object): partition.setFlag(flag.value) debug(f'\tType: {part_mod.type.value}') - debug(f'\tFilesystem: {part_mod.fs_type.value}') + debug(f'\tFilesystem: {part_mod.safe_fs_type.value}') debug(f'\tGeometry: {start_sector.value} start sector, {length_sector.value} length') try: diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index 35fbd40c..97623772 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -283,7 +283,7 @@ class _PartitionInfo: partition: Partition name: str type: PartitionType - fs_type: FilesystemType + fs_type: Optional[FilesystemType] path: Path start: Size length: Size @@ -313,7 +313,7 @@ class _PartitionInfo: def from_partition( cls, partition: Partition, - fs_type: FilesystemType, + fs_type: Optional[FilesystemType], partuuid: str, mountpoints: List[Path], btrfs_subvol_infos: List[_BtrfsSubvolumeInfo] = [] @@ -594,7 +594,7 @@ class PartitionModification: type: PartitionType start: Size length: Size - fs_type: FilesystemType + fs_type: Optional[FilesystemType] mountpoint: Optional[Path] = None mount_options: List[str] = field(default_factory=list) flags: List[PartitionFlag] = field(default_factory=list) @@ -613,6 +613,9 @@ class PartitionModification: if self.is_exists_or_modify() and not self.dev_path: raise ValueError('If partition marked as existing a path must be set') + if self.fs_type is None and self.status == ModificationStatus.Modify: + raise ValueError('FS type must not be empty on modifications with status type modify') + def __hash__(self): return hash(self._obj_id) @@ -628,6 +631,12 @@ class PartitionModification: raise ValueError('Device path was not set') return self.dev_path + @property + def safe_fs_type(self) -> FilesystemType: + if self.fs_type is None: + raise ValueError('File system type is not set') + return self.fs_type + @classmethod def from_existing_partition(cls, partition_info: _PartitionInfo) -> PartitionModification: if partition_info.btrfs_subvol_infos: @@ -714,7 +723,7 @@ class PartitionModification: 'type': self.type.value, 'start': self.start.__dump__(), 'length': self.length.__dump__(), - 'fs_type': self.fs_type.value, + 'fs_type': self.fs_type.value if self.fs_type else '', 'mountpoint': str(self.mountpoint) if self.mountpoint else None, 'mount_options': self.mount_options, 'flags': [f.name for f in self.flags], @@ -731,7 +740,7 @@ class PartitionModification: 'Type': self.type.value, 'Start': self.start.format_size(Unit.MiB), 'Length': self.length.format_size(Unit.MiB), - 'FS type': self.fs_type.value, + 'FS type': self.fs_type.value if self.fs_type else 'Unknown', 'Mountpoint': self.mountpoint if self.mountpoint else '', 'Mount options': ', '.join(self.mount_options), 'Flags': ', '.join([f.name for f in self.flags]), diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 0680bf6e..f1a7f71a 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -667,28 +667,29 @@ class Installer: ): for mod in self._disk_config.device_modifications: for part in mod.partitions: - if (pkg := part.fs_type.installation_pkg) is not None: - self.base_packages.append(pkg) - if (module := part.fs_type.installation_module) is not None: - self.modules.append(module) - if (binary := part.fs_type.installation_binary) is not None: - self._binaries.append(binary) - - # There is not yet an fsck tool for NTFS. If it's being used for the root filesystem, the hook should be removed. - if part.fs_type.fs_type_mount == 'ntfs3' and part.mountpoint == self.target: - if 'fsck' in self._hooks: - self._hooks.remove('fsck') - - if part in self._disk_encryption.partitions: - if self._disk_encryption.hsm_device: - # Required bby mkinitcpio to add support for fido2-device options - self._pacstrap('libfido2') - - if 'sd-encrypt' not in self._hooks: - self._hooks.insert(self._hooks.index('filesystems'), 'sd-encrypt') - else: - if 'encrypt' not in self._hooks: - self._hooks.insert(self._hooks.index('filesystems'), 'encrypt') + if part.fs_type is not None: + if (pkg := part.fs_type.installation_pkg) is not None: + self.base_packages.append(pkg) + if (module := part.fs_type.installation_module) is not None: + self.modules.append(module) + if (binary := part.fs_type.installation_binary) is not None: + self._binaries.append(binary) + + # There is not yet an fsck tool for NTFS. If it's being used for the root filesystem, the hook should be removed. + if part.fs_type.fs_type_mount == 'ntfs3' and part.mountpoint == self.target: + if 'fsck' in self._hooks: + self._hooks.remove('fsck') + + if part in self._disk_encryption.partitions: + if self._disk_encryption.hsm_device: + # Required bby mkinitcpio to add support for fido2-device options + self._pacstrap('libfido2') + + if 'sd-encrypt' not in self._hooks: + self._hooks.insert(self._hooks.index('filesystems'), 'sd-encrypt') + else: + if 'encrypt' not in self._hooks: + self._hooks.insert(self._hooks.index('filesystems'), 'encrypt') if not SysInfo.has_uefi(): self.base_packages.append('grub') @@ -857,7 +858,7 @@ class Installer: # blkid doesn't trigger on loopback devices really well, # so we'll use the old manual method until we get that sorted out. - options_entry = f'rw rootfstype={root_partition.fs_type.fs_type_mount} {" ".join(self._kernel_params)}\n' + options_entry = f'rw rootfstype={root_partition.safe_fs_type.fs_type_mount} {" ".join(self._kernel_params)}\n' for sub_vol in root_partition.btrfs_subvols: if sub_vol.is_root(): @@ -868,7 +869,7 @@ class Installer: if self._zram_enabled: options_entry = "zswap.enabled=0 " + options_entry - if root_partition.fs_type.is_crypto(): + if root_partition.safe_fs_type.is_crypto(): # TODO: We need to detect if the encrypted device is a whole disk encryption, # or simply a partition encryption. Right now we assume it's a partition (and we always have) debug('Root partition is an encrypted device, identifying by PARTUUID: {root_partition.partuuid}') @@ -900,15 +901,15 @@ class Installer: _file = "/etc/default/grub" - if root_partition.fs_type.is_crypto(): + if root_partition.safe_fs_type.is_crypto(): debug(f"Using UUID {root_partition.uuid} as encrypted root identifier") - cmd_line_linux = f"sed -i 's/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"cryptdevice=UUID={root_partition.uuid}:cryptlvm rootfstype={root_partition.fs_type.value}\"/'" + cmd_line_linux = f"sed -i 's/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"cryptdevice=UUID={root_partition.uuid}:cryptlvm rootfstype={root_partition.safe_fs_type.value}\"/'" enable_cryptdisk = "sed -i 's/#GRUB_ENABLE_CRYPTODISK=y/GRUB_ENABLE_CRYPTODISK=y/'" SysCommand(f"/usr/bin/arch-chroot {self.target} {enable_cryptdisk} {_file}") else: - cmd_line_linux = f"sed -i 's/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"rootfstype={root_partition.fs_type.value}\"/'" + cmd_line_linux = f"sed -i 's/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"rootfstype={root_partition.safe_fs_type.value}\"/'" SysCommand(f"/usr/bin/arch-chroot {self.target} {cmd_line_linux} {_file}") @@ -984,14 +985,14 @@ class Installer: # blkid doesn't trigger on loopback devices really well, # so we'll use the old manual method until we get that sorted out. - if root_partition.fs_type.is_crypto(): + if root_partition.safe_fs_type.is_crypto(): # TODO: We need to detect if the encrypted device is a whole disk encryption, # or simply a partition encryption. Right now we assume it's a partition (and we always have) debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}') - kernel_parameters.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:luksdev root=/dev/mapper/luksdev rw rootfstype={root_partition.fs_type.value} {" ".join(self._kernel_params)}') + kernel_parameters.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:luksdev root=/dev/mapper/luksdev rw rootfstype={root_partition.safe_fs_type.value} {" ".join(self._kernel_params)}') else: debug(f'Root partition is an encrypted device identifying by PARTUUID: {root_partition.partuuid}') - kernel_parameters.append(f'root=PARTUUID={root_partition.partuuid} rw rootfstype={root_partition.fs_type.value} {" ".join(self._kernel_params)}') + kernel_parameters.append(f'root=PARTUUID={root_partition.partuuid} rw rootfstype={root_partition.safe_fs_type.value} {" ".join(self._kernel_params)}') device = disk.device_handler.get_device_by_partition_path(boot_partition.safe_dev_path) diff --git a/pyproject.toml b/pyproject.toml index 8b6ae4c7..f67f1eca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,6 +63,7 @@ packages = ["archinstall"] python_version = "3.10" files = "archinstall/" exclude = "tests" +#check_untyped_defs=true [tool.bandit] targets = ["archinstall"] -- cgit v1.2.3-70-g09d2 From 1ae1f2ff1144d502830834ba5a64262f7d195e91 Mon Sep 17 00:00:00 2001 From: Himadri Bhattacharjee <107522312+lavafroth@users.noreply.github.com> Date: Wed, 28 Jun 2023 11:42:53 +0000 Subject: Refactor installer and general design patterns (#1895) * fix: refactor clear_vt100_escape_codes * fix: check for structure being a dict after handling potential parsing errors * refactor: use short circuit logic than if-elif-else chains * fix: use or for nullish moutpoint attribute * fix: better error handling for JSON from urls and paths * chore: json_stream_to_structure documentation * refactor: dry up relative and chroot path for custom command scripts * refactor: use write_text for pathlib.Path object * refactor: use sets to find intersection instead of filter and list * refactor: replace loop with dictionary comprehension in preparing luks partition * refactor: use walrus operator to check if luks_handler exists * refactor: use read_text and splitlines for potential Path object * fix: use keepends in splitlines for compatibility * fix: use keepends in splitlines for compatibility * feat: set pacman_conf Path as an attribute of installer * fix: empty string is a part of any string, avoid tuples * refactor: use iterator patterns to uncomment multilib and testing blocks * fix: don't json.loads an already loaded structure * fix: use fstab_path uniformly in genfstab * fix: remove unused variable matched * refactor: create separate class to modify pacman.conf in a single pass * fix: remove unused attribute pacman_conf from installer * fix: remove unused attribute pacman_conf from installer * feat: add persist method for pacman.conf, rewrite only when needed * fix: use path.write_text for locale.conf * use `or` operator for nullish new_conf * refactor: Installer.target is always a pathlib.Path object, do not check for string type * fix: use Optional[str] in function type definition instead of sumtype of str and None * fix: mypy type annotation * fix: make flake8 happy * chore: move pacman config and repo into pacman module * refactor: use Pacman object instead of Installer's pacstrap method * fix: break after first sync * fix: keep old build script for now * use nullish operator for base_packages and disk_encryption of Installer * feat: use shutil.which instead of rolling our own implementation * fix: check for binary only if list is not empty * fix: import Enum and fix mypy errors * refactor: use nullish operator for default values * refactor: linear search for key in Installer._trace_log only once * fix: use logs instead of the entirety of self._trace_log when searching for key * refactor: do not copy slice of bytes for search * refactor: use rfind only once to iterate over logs, do not raise ValueError in clear_vt100_escape_codes since TYPE_CHECKING will take care of it. * refactor: try decoding trace log before falling back to strigification * refactor: use an empty dict as default for callbacks in SysCommand.__init__ * refactor: use nullish or operator for slice start and end when not specified * refactor: use nullish or operator for SysCommand session * refactor: use pre-existing decode method in __repr__ for SysCommand * fix: overindentation * fix: use shallow copy of callbacks to prevent mutating the key-value relationships of the argument dict * refactor: use truthy value of self.session is not None for json encoding SysCommand * refactor: directly assign to SysCommand.session in create_session since it short circuits to True if already present * refactor: use dict.items() instead of manually retrieving the value using the key * refactor: user_config_to_json method sounds pretty self explanatory * refactor: store path validity as boolean for return * refactor: use pathlib.Path.write_text to write configs to destinations * fix: cannot use assignment expressions with expression * fix: use config_output.save for saving both config and creds * refactor: switch dictionary keys and values for options to avoid redundancy * refactor: use itertools.takewhile to collect locale.gen entries until the empty line * refactor: use iterative approach for nvidia driver fix * refactor: install packages if not nvidia * refactor: return early if no profile is selected * refactor: use strip to remove commented lines * fix: install additional packages only when we have a driver * fix: path with one command is matched as relative to '.' * fix: remove translation for debug log --------- Co-authored-by: Anton Hvornum --- archinstall/lib/configuration.py | 101 ++++++-------- archinstall/lib/general.py | 184 +++++++++++-------------- archinstall/lib/installer.py | 205 +++++++--------------------- archinstall/lib/locale.py | 23 ++-- archinstall/lib/networking.py | 6 +- archinstall/lib/packages/packages.py | 4 +- archinstall/lib/pacman.py | 31 ----- archinstall/lib/pacman/__init__.py | 88 ++++++++++++ archinstall/lib/pacman/config.py | 33 +++++ archinstall/lib/pacman/repo.py | 6 + archinstall/lib/profile/profiles_handler.py | 66 +++++---- 11 files changed, 348 insertions(+), 399 deletions(-) delete mode 100644 archinstall/lib/pacman.py create mode 100644 archinstall/lib/pacman/__init__.py create mode 100644 archinstall/lib/pacman/config.py create mode 100644 archinstall/lib/pacman/repo.py diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py index 8c7b11fa..aeeddbb8 100644 --- a/archinstall/lib/configuration.py +++ b/archinstall/lib/configuration.py @@ -26,7 +26,7 @@ class ConfigurationOutput: self._config = config self._user_credentials: Dict[str, Any] = {} self._user_config: Dict[str, Any] = {} - self._default_save_path = Path(storage.get('LOG_PATH', '.')) + self._default_save_path = storage.get('LOG_PATH', Path('.')) self._user_config_file = 'user_configuration.json' self._user_creds_file = "user_credentials.json" @@ -44,17 +44,17 @@ class ConfigurationOutput: return self._user_config_file def _process_config(self): - for key in self._config: + for key, value in self._config.items(): if key in self._sensitive: - self._user_credentials[key] = self._config[key] + self._user_credentials[key] = value elif key in self._ignore: pass else: - self._user_config[key] = self._config[key] + self._user_config[key] = value # special handling for encryption password - if key == 'disk_encryption' and self._config[key] is not None: - self._user_credentials['encryption_password'] = self._config[key].encryption_password + if key == 'disk_encryption' and value: + self._user_credentials['encryption_password'] = value.encryption_password def user_config_to_json(self) -> str: return json.dumps({ @@ -72,42 +72,33 @@ class ConfigurationOutput: print(_('\nThis is your chosen configuration:')) debug(" -- Chosen configuration --") - user_conig = self.user_config_to_json() - info(user_conig) - + info(self.user_config_to_json()) print() def _is_valid_path(self, dest_path: Path) -> bool: - if (not dest_path.exists()) or not (dest_path.is_dir()): + dest_path_ok = dest_path.exists() and dest_path.is_dir() + if not dest_path_ok: warn( f'Destination directory {dest_path.resolve()} does not exist or is not a directory\n.', 'Configuration files can not be saved' ) - return False - return True + return dest_path_ok def save_user_config(self, dest_path: Path): if self._is_valid_path(dest_path): target = dest_path / self._user_config_file - - with open(target, 'w') as config_file: - config_file.write(self.user_config_to_json()) - - os.chmod(str(dest_path / self._user_config_file), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) + target.write_text(self.user_config_to_json()) + os.chmod(target, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) def save_user_creds(self, dest_path: Path): if self._is_valid_path(dest_path): if user_creds := self.user_credentials_to_json(): target = dest_path / self._user_creds_file - - with open(target, 'w') as config_file: - config_file.write(user_creds) - - os.chmod(str(target), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) + target.write_text(user_creds) + os.chmod(target, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) def save(self, dest_path: Optional[Path] = None): - if not dest_path: - dest_path = self._default_save_path + dest_path = dest_path or self._default_save_path if self._is_valid_path(dest_path): self.save_user_config(dest_path) @@ -116,33 +107,33 @@ class ConfigurationOutput: def save_config(config: Dict): def preview(selection: str): - if options["user_config"] == selection: - serialized = config_output.user_config_to_json() - return f"{config_output.user_configuration_file}\n{serialized}" - elif options["user_creds"] == selection: - if maybe_serial := config_output.user_credentials_to_json(): - return f"{config_output.user_credentials_file}\n{maybe_serial}" - else: + match options[selection]: + case "user_config": + serialized = config_output.user_config_to_json() + return f"{config_output.user_configuration_file}\n{serialized}" + case "user_creds": + if maybe_serial := config_output.user_credentials_to_json(): + return f"{config_output.user_credentials_file}\n{maybe_serial}" return str(_("No configuration")) - elif options["all"] == selection: - output = f"{config_output.user_configuration_file}\n" - if config_output.user_credentials_to_json(): - output += f"{config_output.user_credentials_file}\n" - return output[:-1] + case "all": + output = [config_output.user_configuration_file] + if config_output.user_credentials_to_json(): + output.append(config_output.user_credentials_file) + return '\n'.join(output) return None try: config_output = ConfigurationOutput(config) options = { - "user_config": str(_("Save user configuration (including disk layout)")), - "user_creds": str(_("Save user credentials")), - "all": str(_("Save all")), + str(_("Save user configuration (including disk layout)")): "user_config", + str(_("Save user credentials")): "user_creds", + str(_("Save all")): "all", } save_choice = Menu( _("Choose which configuration to save"), - list(options.values()), + list(options), sort=False, skip=True, preview_size=0.75, @@ -170,27 +161,21 @@ def save_config(config: Dict): prompt = _( "Do you want to save {} configuration file(s) in the following location?\n\n{}" - ).format( - list(options.keys())[list(options.values()).index(str(save_choice.value))], - dest_path.absolute(), - ) + ).format(options[str(save_choice.value)], dest_path.absolute()) + save_confirmation = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run() if save_confirmation == Menu.no(): return - debug( - _("Saving {} configuration files to {}").format( - list(options.keys())[list(options.values()).index(str(save_choice.value))], - dest_path.absolute(), - ) - ) - - if options["user_config"] == save_choice.value: - config_output.save_user_config(dest_path) - elif options["user_creds"] == save_choice.value: - config_output.save_user_creds(dest_path) - elif options["all"] == save_choice.value: - config_output.save_user_config(dest_path) - config_output.save_user_creds(dest_path) + debug("Saving {} configuration files to {}".format(options[str(save_choice.value)], dest_path.absolute())) + + match options[str(save_choice.value)]: + case "user_config": + config_output.save_user_config(dest_path) + case "user_creds": + config_output.save_user_creds(dest_path) + case "all": + config_output.save(dest_path) + except KeyboardInterrupt: return diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py index f43d4f57..c85208ec 100644 --- a/archinstall/lib/general.py +++ b/archinstall/lib/general.py @@ -11,13 +11,14 @@ import sys import time import re import urllib.parse -import urllib.request +from urllib.request import Request, urlopen import urllib.error import pathlib from datetime import datetime, date from enum import Enum from typing import Callable, Optional, Dict, Any, List, Union, Iterator, TYPE_CHECKING from select import epoll, EPOLLIN, EPOLLHUP +from shutil import which from .exceptions import RequirementError, SysCallError from .output import debug, error, info @@ -34,28 +35,17 @@ def generate_password(length :int = 64) -> str: def locate_binary(name :str) -> str: - for PATH in os.environ['PATH'].split(':'): - for root, folders, files in os.walk(PATH): - for file in files: - if file == name: - return os.path.join(root, file) - break # Don't recurse - + if path := which(name): + return path raise RequirementError(f"Binary {name} does not exist.") def clear_vt100_escape_codes(data :Union[bytes, str]) -> Union[bytes, str]: # https://stackoverflow.com/a/43627833/929999 - if type(data) == bytes: - byte_vt100_escape_regex = bytes(r'\x1B\[[?0-9;]*[a-zA-Z]', 'UTF-8') - data = re.sub(byte_vt100_escape_regex, b'', data) - elif type(data) == str: - vt100_escape_regex = r'\x1B\[[?0-9;]*[a-zA-Z]' - data = re.sub(vt100_escape_regex, '', data) - else: - raise ValueError(f'Unsupported data type: {type(data)}') - - return data + vt100_escape_regex = r'\x1B\[[?0-9;]*[a-zA-Z]' + if isinstance(data, bytes): + return re.sub(vt100_escape_regex.encode(), b'', data) + return re.sub(vt100_escape_regex, '', data) def jsonify(obj: Any, safe: bool = True) -> Any: @@ -120,21 +110,15 @@ class SysCommandWorker: working_directory :Optional[str] = './', remove_vt100_escape_codes_from_lines :bool = True ): - if not callbacks: - callbacks = {} + callbacks = callbacks or {} + environment_vars = environment_vars or {} - if not environment_vars: - environment_vars = {} - - if type(cmd) is str: + if isinstance(cmd, str): cmd = shlex.split(cmd) - cmd = list(cmd) # This is to please mypy - if cmd[0][0] != '/' and cmd[0][:2] != './': - # "which" doesn't work as it's a builtin to bash. - # It used to work, but for whatever reason it doesn't anymore. - # We there for fall back on manual lookup in os.PATH - cmd[0] = locate_binary(cmd[0]) + if cmd: + if cmd[0][0] != '/' and cmd[0][:2] != './': # pathlib.Path does not work well + cmd[0] = locate_binary(cmd[0]) self.cmd = cmd self.callbacks = callbacks @@ -158,29 +142,36 @@ class SysCommandWorker: Contains will also move the current buffert position forward. This is to avoid re-checking the same data when looking for output. """ - assert type(key) == bytes + assert isinstance(key, bytes) - if (contains := key in self._trace_log[self._trace_log_pos:]): - self._trace_log_pos += self._trace_log[self._trace_log_pos:].find(key) + len(key) + index = self._trace_log.find(key, self._trace_log_pos) + if index >= 0: + self._trace_log_pos += index + len(key) + return True - return contains + return False def __iter__(self, *args :str, **kwargs :Dict[str, Any]) -> Iterator[bytes]: - for line in self._trace_log[self._trace_log_pos:self._trace_log.rfind(b'\n')].split(b'\n'): - if line: - escaped_line: bytes = line - - if self.remove_vt100_escape_codes_from_lines: - escaped_line = clear_vt100_escape_codes(line) # type: ignore + last_line = self._trace_log.rfind(b'\n') + lines = filter(None, self._trace_log[self._trace_log_pos:last_line].splitlines()) + for line in lines: + if self.remove_vt100_escape_codes_from_lines: + line = clear_vt100_escape_codes(line) # type: ignore - yield escaped_line + b'\n' + yield line + b'\n' - self._trace_log_pos = self._trace_log.rfind(b'\n') + self._trace_log_pos = last_line def __repr__(self) -> str: self.make_sure_we_are_executing() return str(self._trace_log) + def __str__(self) -> str: + try: + return self._trace_log.decode('utf-8') + except UnicodeDecodeError: + return str(self._trace_log) + def __enter__(self) -> 'SysCommandWorker': return self @@ -205,7 +196,7 @@ class SysCommandWorker: if self.exit_code != 0: raise SysCallError( - f"{self.cmd} exited with abnormal exit code [{self.exit_code}]: {str(self._trace_log[-500:])}", + f"{self.cmd} exited with abnormal exit code [{self.exit_code}]: {str(self)[-500:]}", self.exit_code, worker=self ) @@ -244,7 +235,7 @@ class SysCommandWorker: def peak(self, output: Union[str, bytes]) -> bool: if self.peek_output: - if type(output) == bytes: + if isinstance(output, bytes): try: output = output.decode('UTF-8') except UnicodeDecodeError: @@ -282,7 +273,7 @@ class SysCommandWorker: self.ended = time.time() break - if self.ended or (got_output is False and _pid_exists(self.pid) is False): + if self.ended or (not got_output and not _pid_exists(self.pid)): self.ended = time.time() try: wait_status = os.waitpid(self.pid, 0)[1] @@ -321,10 +312,8 @@ class SysCommandWorker: if change_perm: os.chmod(str(history_logfile), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) - except PermissionError: - pass + except (PermissionError, FileNotFoundError): # If history_logfile does not exist, ignore the error - except FileNotFoundError: pass except Exception as e: exception_type = type(e).__name__ @@ -355,22 +344,18 @@ class SysCommandWorker: class SysCommand: def __init__(self, cmd :Union[str, List[str]], - callbacks :Optional[Dict[str, Callable[[Any], Any]]] = None, + callbacks :Dict[str, Callable[[Any], Any]] = {}, start_callback :Optional[Callable[[Any], Any]] = None, peek_output :Optional[bool] = False, environment_vars :Optional[Dict[str, Any]] = None, working_directory :Optional[str] = './', remove_vt100_escape_codes_from_lines :bool = True): - _callbacks = {} - if callbacks: - for hook, func in callbacks.items(): - _callbacks[hook] = func + self._callbacks = callbacks.copy() if start_callback: - _callbacks['on_start'] = start_callback + self._callbacks['on_start'] = start_callback self.cmd = cmd - self._callbacks = _callbacks self.peek_output = peek_output self.environment_vars = environment_vars self.working_directory = working_directory @@ -398,17 +383,15 @@ class SysCommand: if not self.session: raise KeyError(f"SysCommand() does not have an active session.") elif type(key) is slice: - start = key.start if key.start else 0 - end = key.stop if key.stop else len(self.session._trace_log) + start = key.start or 0 + end = key.stop or len(self.session._trace_log) return self.session._trace_log[start:end] else: raise ValueError("SysCommand() doesn't have key & value pairs, only slices, SysCommand('ls')[:10] as an example.") def __repr__(self, *args :List[Any], **kwargs :Dict[str, Any]) -> str: - if self.session: - return self.session._trace_log.decode('UTF-8', errors='backslashreplace') - return '' + return self.decode('UTF-8', errors='backslashreplace') or '' def __json__(self) -> Dict[str, Union[str, bool, List[str], Dict[str, Any], Optional[bool], Optional[Dict[str, Any]]]]: return { @@ -416,7 +399,7 @@ class SysCommand: 'callbacks': self._callbacks, 'peak': self.peek_output, 'environment_vars': self.environment_vars, - 'session': True if self.session else False + 'session': self.session is not None } def create_session(self) -> bool: @@ -436,10 +419,9 @@ class SysCommand: remove_vt100_escape_codes_from_lines=self.remove_vt100_escape_codes_from_lines, working_directory=self.working_directory) as session: - if not self.session: - self.session = session + self.session = session - while self.session.ended is None: + while not self.session.ended: self.session.poll() if self.peek_output: @@ -448,9 +430,9 @@ class SysCommand: return True - def decode(self, fmt :str = 'UTF-8') -> Optional[str]: + def decode(self, *args, **kwargs) -> Optional[str]: if self.session: - return self.session._trace_log.decode(fmt) + return self.session._trace_log.decode(*args, **kwargs) return None @property @@ -476,54 +458,52 @@ def _pid_exists(pid: int) -> bool: def run_custom_user_commands(commands :List[str], installation :Installer) -> None: for index, command in enumerate(commands): + script_path = f"/var/tmp/user-command.{index}.sh" + chroot_path = installation.target / script_path + info(f'Executing custom command "{command}" ...') - - with open(f"{installation.target}/var/tmp/user-command.{index}.sh", "w") as temp_script: - temp_script.write(command) - - SysCommand(f"arch-chroot {installation.target} bash /var/tmp/user-command.{index}.sh") - - os.unlink(f"{installation.target}/var/tmp/user-command.{index}.sh") + chroot_path.write_text(command) + SysCommand(f"arch-chroot {installation.target} bash {script_path}") + + os.unlink(chroot_path) def json_stream_to_structure(configuration_identifier : str, stream :str, target :dict) -> bool : """ - Function to load a stream (file (as name) or valid JSON string into an existing dictionary - Returns true if it could be done - Return false if operation could not be executed + Load a JSON encoded dictionary from a stream and merge it into an existing dictionary. + A stream can be a filepath, a URL or a raw JSON string. + Returns True if the operation succeeded, False otherwise. +configuration_identifier is just a parameter to get meaningful, but not so long messages """ - parsed_url = urllib.parse.urlparse(stream) - - if parsed_url.scheme: # The stream is in fact a URL that should be grabbed + raw: Optional[str] = None + # Try using the stream as a URL that should be grabbed + if urllib.parse.urlparse(stream).scheme: try: - with urllib.request.urlopen(urllib.request.Request(stream, headers={'User-Agent': 'ArchInstall'})) as response: - target.update(json.loads(response.read())) + with urlopen(Request(stream, headers={'User-Agent': 'ArchInstall'})) as response: + raw = response.read() except urllib.error.HTTPError as err: - error(f"Could not load {configuration_identifier} via {parsed_url} due to: {err}") + error(f"Could not fetch JSON from {stream} as {configuration_identifier}: {err}") return False - else: - if pathlib.Path(stream).exists(): - try: - with pathlib.Path(stream).open() as fh: - target.update(json.load(fh)) - except Exception as err: - error(f"{configuration_identifier} = {stream} does not contain a valid JSON format: {err}") - return False - else: - # NOTE: This is a rudimentary check if what we're trying parse is a dict structure. - # Which is the only structure we tolerate anyway. - if stream.strip().startswith('{') and stream.strip().endswith('}'): - try: - target.update(json.loads(stream)) - except Exception as e: - error(f"{configuration_identifier} Contains an invalid JSON format: {e}") - return False - else: - error(f"{configuration_identifier} is neither a file nor is a JSON string") - return False + # Try using the stream as a filepath that should be read + if raw is None and (path := pathlib.Path(stream)).exists(): + try: + raw = path.read_text() + except Exception as err: + error(f"Could not read file {stream} as {configuration_identifier}: {err}") + return False + + try: + # We use `or` to try the stream as raw JSON to be parsed + structure = json.loads(raw or stream) + except Exception as err: + error(f"{configuration_identifier} contains an invalid JSON format: {err}") + return False + if not isinstance(structure, dict): + error(f"{stream} passed as {configuration_identifier} is not a JSON encoded dictionary") + return False + target.update(structure) return True diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index f1a7f71a..ee546993 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -20,7 +20,8 @@ from .models.bootloader import Bootloader from .models.network_configuration import NetworkConfiguration from .models.users import User from .output import log, error, info, warn, debug -from .pacman import run_pacman +from . import pacman +from .pacman import Pacman from .plugins import plugins from .storage import storage @@ -52,27 +53,16 @@ class Installer: `Installer()` is the wrapper for most basic installation steps. It also wraps :py:func:`~archinstall.Installer.pacstrap` among other things. """ - if not base_packages: - base_packages = __packages__[:3] - - if kernels is None: - self.kernels = ['linux'] - else: - self.kernels = kernels - + self.base_packages = base_packages or __packages__[:3] + self.kernels = kernels or ['linux'] self._disk_config = disk_config - if disk_encryption is None: - self._disk_encryption = disk.DiskEncryption(disk.EncryptionType.NoEncryption) - else: - self._disk_encryption = disk_encryption - + self._disk_encryption = disk_encryption or disk.DiskEncryption(disk.EncryptionType.NoEncryption) self.target: Path = target self.init_time = time.strftime('%Y-%m-%d_%H-%M-%S') self.milliseconds = int(str(time.time()).split('.')[1]) self.helper_flags: Dict[str, Any] = {'base': False, 'bootloader': None} - self.base_packages = base_packages for kernel in self.kernels: self.base_packages.append(kernel) @@ -101,6 +91,7 @@ class Installer: self._fstab_entries: List[str] = [] self._zram_enabled = False + self.pacman = Pacman(self.target, storage['arguments'].get('silent', False)) def __enter__(self) -> 'Installer': return self @@ -189,35 +180,33 @@ class Installer: # partitions have to mounted in the right order on btrfs the mountpoint will # be empty as the actual subvolumes are getting mounted instead so we'll use # '/' just for sorting - sorted_part_mods = sorted(mod.partitions, key=lambda x: x.mountpoint if x.mountpoint else Path('/')) + sorted_part_mods = sorted(mod.partitions, key=lambda x: x.mountpoint or Path('/')) + enc_partitions = [] if self._disk_encryption.encryption_type is not disk.EncryptionType.NoEncryption: - enc_partitions = list(filter(lambda x: x in self._disk_encryption.partitions, sorted_part_mods)) - else: - enc_partitions = [] + enc_partitions = list(set(sorted_part_mods) & set(self._disk_encryption.partitions)) # attempt to decrypt all luks partitions luks_handlers = self._prepare_luks_partitions(enc_partitions) for part_mod in sorted_part_mods: - if part_mod not in luks_handlers: # partition is not encrypted + if luks_handler := luks_handlers.get(part_mod): + # mount encrypted partition + self._mount_luks_partiton(part_mod, luks_handler) + else: + # partition is not encrypted self._mount_partition(part_mod) - else: # mount encrypted partition - self._mount_luks_partiton(part_mod, luks_handlers[part_mod]) def _prepare_luks_partitions(self, partitions: List[disk.PartitionModification]) -> Dict[disk.PartitionModification, Luks2]: - luks_handlers = {} - - for part_mod in partitions: - if part_mod.mapper_name and part_mod.dev_path: - luks_handler = disk.device_handler.unlock_luks2_dev( - part_mod.dev_path, - part_mod.mapper_name, - self._disk_encryption.encryption_password - ) - luks_handlers[part_mod] = luks_handler - - return luks_handlers + return { + part_mod: disk.device_handler.unlock_luks2_dev( + part_mod.dev_path, + part_mod.mapper_name, + self._disk_encryption.encryption_password + ) + for part_mod in partitions + if part_mod.mapper_name and part_mod.dev_path + } def _mount_partition(self, part_mod: disk.PartitionModification): # it would be none if it's btrfs as the subvolumes will have the mountpoints defined @@ -302,93 +291,6 @@ class Installer: def post_install_check(self, *args :str, **kwargs :str) -> List[str]: return [step for step, flag in self.helper_flags.items() if flag is False] - def enable_multilib_repository(self): - # Set up a regular expression pattern of a commented line containing 'multilib' within [] - pattern = re.compile(r"^#\s*\[multilib\]$") - - # This is used to track if the previous line is a match, so we end up uncommenting the line after the block. - matched = False - - # Read in the lines from the original file - with open("/etc/pacman.conf", "r") as pacman_conf: - lines = pacman_conf.readlines() - - # Open the file again in write mode, to replace the contents - with open("/etc/pacman.conf", "w") as pacman_conf: - for line in lines: - if pattern.match(line): - # If this is the [] block containing 'multilib', uncomment it and set the matched tracking boolean. - pacman_conf.write(line.lstrip('#')) - matched = True - elif matched: - # The previous line was a match for [.*multilib.*]. - # This means we're on a line that looks like '#Include = /etc/pacman.d/mirrorlist' - pacman_conf.write(line.lstrip('#')) - matched = False # Reset the state of matched to False. - else: - pacman_conf.write(line) - - def enable_testing_repositories(self, enable_multilib_testing=False): - # Set up a regular expression pattern of a commented line containing 'testing' within [] - pattern = re.compile("^#\\[.*testing.*\\]$") - - # This is used to track if the previous line is a match, so we end up uncommenting the line after the block. - matched = False - - # Read in the lines from the original file - with open("/etc/pacman.conf", "r") as pacman_conf: - lines = pacman_conf.readlines() - - # Open the file again in write mode, to replace the contents - with open("/etc/pacman.conf", "w") as pacman_conf: - for line in lines: - if pattern.match(line) and (enable_multilib_testing or 'multilib' not in line): - # If this is the [] block containing 'testing', uncomment it and set the matched tracking boolean. - pacman_conf.write(line.lstrip('#')) - matched = True - elif matched: - # The previous line was a match for [.*testing.*]. - # This means we're on a line that looks like '#Include = /etc/pacman.d/mirrorlist' - pacman_conf.write(line.lstrip('#')) - matched = False # Reset the state of matched to False. - else: - pacman_conf.write(line) - - def _pacstrap(self, packages: Union[str, List[str]]) -> bool: - if isinstance(packages, str): - packages = [packages] - - for plugin in plugins.values(): - if hasattr(plugin, 'on_pacstrap'): - if (result := plugin.on_pacstrap(packages)): - packages = result - - info(f'Installing packages: {packages}') - - # TODO: We technically only need to run the -Syy once. - try: - run_pacman('-Syy', default_cmd='/usr/bin/pacman') - except SysCallError as err: - error(f'Could not sync a new package database: {err}') - - 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) - - raise RequirementError(f'Could not sync mirrors: {err}') - - try: - SysCommand(f'/usr/bin/pacstrap -C /etc/pacman.conf -K {self.target} {" ".join(packages)} --noconfirm', peek_output=True) - return True - except SysCallError as err: - error(f'Could not strap in packages: {err}') - - 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) - - raise RequirementError("Pacstrap failed. See /var/log/archinstall/install.log or above message for error details.") - def set_mirrors(self, mirror_config: MirrorConfiguration): for plugin in plugins.values(): if hasattr(plugin, 'on_mirrors'): @@ -402,7 +304,8 @@ class Installer: add_custom_mirrors(mirror_config.custom_mirrors) def genfstab(self, flags :str = '-pU'): - info(f"Updating {self.target}/etc/fstab") + fstab_path = self.target / "etc" / "fstab" + info(f"Updating {fstab_path}") try: gen_fstab = SysCommand(f'/usr/bin/genfstab {flags} {self.target}').decode() @@ -412,10 +315,10 @@ class Installer: if not gen_fstab: raise RequirementError(f'Genrating fstab returned empty value') - with open(f"{self.target}/etc/fstab", 'a') as fp: + with open(fstab_path, 'a') as fp: fp.write(gen_fstab) - if not os.path.isfile(f'{self.target}/etc/fstab'): + if not fstab_path.is_file(): raise RequirementError(f'Could not create fstab file') for plugin in plugins.values(): @@ -423,7 +326,7 @@ class Installer: if plugin.on_genfstab(self) is True: break - with open(f"{self.target}/etc/fstab", 'a') as fp: + with open(fstab_path, 'a') as fp: for entry in self._fstab_entries: fp.write(f'{entry}\n') @@ -432,9 +335,7 @@ class Installer: if part_mod.fs_type != disk.FilesystemType.Btrfs: continue - fstab_file = Path(f'{self.target}/etc/fstab') - - with fstab_file.open('r') as fp: + with fstab_path.open('r') as fp: fstab = fp.readlines() # Replace the {installation}/etc/fstab with entries @@ -456,7 +357,7 @@ class Installer: fstab[index] = line.replace(subvoldef[0], f',compress=zstd{subvoldef[0]}') break - with fstab_file.open('w') as fp: + with fstab_path.open('w') as fp: fp.writelines(fstab) def set_hostname(self, hostname: str, *args :str, **kwargs :str) -> None: @@ -486,8 +387,7 @@ class Installer: with open(f'{self.target}/etc/locale.gen', 'a') as fh: fh.write(f'{lang}.{encoding}{modifier} {encoding}\n') - with open(f'{self.target}/etc/locale.conf', 'w') as fh: - fh.write(f'LANG={lang}.{encoding}{modifier}\n') + (self.target / "etc" / "locale.conf").write_text(f'LANG={lang}.{encoding}{modifier}\n') try: SysCommand(f'/usr/bin/arch-chroot {self.target} locale-gen') @@ -561,16 +461,13 @@ class Installer: for plugin in plugins.values(): if hasattr(plugin, 'on_configure_nic'): - new_conf = plugin.on_configure_nic( + conf = plugin.on_configure_nic( network_config.iface, network_config.dhcp, network_config.ip, network_config.gateway, network_config.dns - ) - - if new_conf: - conf = new_conf + ) or conf with open(f"{self.target}/etc/systemd/network/10-{network_config.iface}.network", "a") as netconf: netconf.write(str(conf)) @@ -597,7 +494,7 @@ class Installer: # Otherwise, we can go ahead and add the required package # and enable it's service: else: - self._pacstrap('iwd') + self.pacman.strap('iwd') self.enable_service('iwd') for psk in psk_files: @@ -683,7 +580,7 @@ class Installer: if part in self._disk_encryption.partitions: if self._disk_encryption.hsm_device: # Required bby mkinitcpio to add support for fido2-device options - self._pacstrap('libfido2') + self.pacman.strap('libfido2') if 'sd-encrypt' not in self._hooks: self._hooks.insert(self._hooks.index('filesystems'), 'sd-encrypt') @@ -709,24 +606,27 @@ class Installer: # Determine whether to enable multilib/testing repositories before running pacstrap if testing flag is set. # This action takes place on the host system as pacstrap copies over package repository lists. + pacman_conf = pacman.Config(self.target) if multilib: info("The multilib flag is set. This system will be installed with the multilib repository enabled.") - self.enable_multilib_repository() + pacman_conf.enable(pacman.Repo.Multilib) else: info("The multilib flag is not set. This system will be installed without multilib repositories enabled.") if testing: info("The testing flag is set. This system will be installed with testing repositories enabled.") - self.enable_testing_repositories(multilib) + pacman_conf.enable(pacman.Repo.Testing) + if multilib: + pacman_conf.enable(pacman.Repo.MultilibTesting) else: info("The testing flag is not set. This system will be installed without testing repositories enabled.") - self._pacstrap(self.base_packages) + pacman_conf.apply() + + self.pacman.strap(self.base_packages) self.helper_flags['base-strapped'] = True - # This handles making sure that the repositories we enabled persist on the installed system - if multilib or testing: - shutil.copy2("/etc/pacman.conf", f"{self.target}/etc/pacman.conf") + pacman_conf.persist() # Periodic TRIM may improve the performance and longevity of SSDs whilst # having no adverse effect on other devices. Most distributions enable @@ -761,7 +661,7 @@ class Installer: def setup_swap(self, kind :str = 'zram'): if kind == 'zram': info(f"Setting up swap on zram") - self._pacstrap('zram-generator') + self.pacman.strap('zram-generator') # We could use the default example below, but maybe not the best idea: https://github.com/archlinux/archinstall/pull/678#issuecomment-962124813 # zram_example_location = '/usr/share/doc/zram-generator/zram-generator.conf.example' @@ -788,7 +688,7 @@ class Installer: return None def _add_systemd_bootloader(self, root_partition: disk.PartitionModification): - self._pacstrap('efibootmgr') + self.pacman.strap('efibootmgr') if not SysInfo.has_uefi(): raise HardwareIncompatibilityError @@ -897,7 +797,7 @@ class Installer: boot_partition: disk.PartitionModification, root_partition: disk.PartitionModification ): - self._pacstrap('grub') # no need? + self.pacman.strap('grub') # no need? _file = "/etc/default/grub" @@ -916,7 +816,7 @@ class Installer: info(f"GRUB boot partition: {boot_partition.dev_path}") if SysInfo.has_uefi(): - self._pacstrap('efibootmgr') # TODO: Do we need? Yes, but remove from minimal_installation() instead? + self.pacman.strap('efibootmgr') # TODO: Do we need? Yes, but remove from minimal_installation() instead? try: SysCommand(f'/usr/bin/arch-chroot {self.target} grub-install --debug --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB --removable', peek_output=True) @@ -955,7 +855,7 @@ class Installer: boot_partition: disk.PartitionModification, root_partition: disk.PartitionModification ): - self._pacstrap('efibootmgr') + self.pacman.strap('efibootmgr') if not SysInfo.has_uefi(): raise HardwareIncompatibilityError @@ -1030,9 +930,6 @@ class Installer: if plugin.on_add_bootloader(self): return True - if type(self.target) == str: - self.target = Path(self.target) - boot_partition = self._get_boot_partition() root_partition = self._get_root_partition() @@ -1053,7 +950,7 @@ class Installer: self._add_efistub_bootloader(boot_partition, root_partition) def add_additional_packages(self, packages: Union[str, List[str]]) -> bool: - return self._pacstrap(packages) + return self.pacman.strap(packages) def _enable_users(self, service: str, users: List[User]): for user in users: @@ -1214,7 +1111,7 @@ class Installer: return True - def _service_started(self, service_name: str) -> str | None: + def _service_started(self, service_name: str) -> Optional[str]: if os.path.splitext(service_name)[1] not in ('.service', '.target', '.timer'): service_name += '.service' # Just to be safe diff --git a/archinstall/lib/locale.py b/archinstall/lib/locale.py index 0a36c072..ab158984 100644 --- a/archinstall/lib/locale.py +++ b/archinstall/lib/locale.py @@ -1,3 +1,5 @@ +from itertools import takewhile +from pathlib import Path from typing import Iterator, List from .exceptions import ServiceException, SysCallError @@ -11,21 +13,12 @@ def list_keyboard_languages() -> Iterator[str]: def list_locales() -> List[str]: - with open('/etc/locale.gen', 'r') as fp: - locales = [] - # before the list of locales begins there's an empty line with a '#' in front - # so we'll collect the localels from bottom up and halt when we're donw - entries = fp.readlines() - entries.reverse() - - for entry in entries: - text = entry.replace('#', '').strip() - if text == '': - break - locales.append(text) - - locales.reverse() - return locales + entries = Path('/etc/locale.gen').read_text().splitlines() + # Before the list of locales begins there's an empty line with a '#' in front + # so we'll collect the locales from bottom up and halt when we're done. + locales = list(takewhile(bool, map(lambda entry: entry.strip('\n\t #'), reversed(entries)))) + locales.reverse() + return locales def list_x11_keyboard_languages() -> Iterator[str]: diff --git a/archinstall/lib/networking.py b/archinstall/lib/networking.py index 6906c320..2a086f39 100644 --- a/archinstall/lib/networking.py +++ b/archinstall/lib/networking.py @@ -9,7 +9,7 @@ from urllib.request import urlopen from .exceptions import SysCallError from .output import error, info, debug -from .pacman import run_pacman +from .pacman import Pacman def get_hw_addr(ifname :str) -> str: @@ -35,7 +35,7 @@ def list_interfaces(skip_loopback :bool = True) -> Dict[str, str]: def check_mirror_reachable() -> bool: info("Testing connectivity to the Arch Linux mirrors...") try: - run_pacman("-Sy") + Pacman.run("-Sy") return True except SysCallError as err: if os.geteuid() != 0: @@ -48,7 +48,7 @@ def check_mirror_reachable() -> bool: def update_keyring() -> bool: info("Updating archlinux-keyring ...") try: - run_pacman("-Sy --noconfirm archlinux-keyring") + Pacman.run("-Sy --noconfirm archlinux-keyring") return True except SysCallError: if os.geteuid() != 0: diff --git a/archinstall/lib/packages/packages.py b/archinstall/lib/packages/packages.py index 71818ca5..b71b0ce8 100644 --- a/archinstall/lib/packages/packages.py +++ b/archinstall/lib/packages/packages.py @@ -8,7 +8,7 @@ from urllib.request import urlopen from ..exceptions import PackageError, SysCallError from ..models.gen import PackageSearch, PackageSearchResult, LocalPackage -from ..pacman import run_pacman +from ..pacman import Pacman BASE_URL_PKG_SEARCH = 'https://archlinux.org/packages/search/json/' # BASE_URL_PKG_CONTENT = 'https://archlinux.org/packages/search/json/' @@ -106,7 +106,7 @@ def validate_package_list(packages :list) -> Tuple[list, list]: def installed_package(package :str) -> LocalPackage: package_info = {} try: - for line in run_pacman(f"-Q --info {package}"): + for line in Pacman.run(f"-Q --info {package}"): if b':' in line: key, value = line.decode().split(':', 1) package_info[key.strip().lower().replace(' ', '_')] = value.strip() diff --git a/archinstall/lib/pacman.py b/archinstall/lib/pacman.py deleted file mode 100644 index f5514f05..00000000 --- a/archinstall/lib/pacman.py +++ /dev/null @@ -1,31 +0,0 @@ -import pathlib -import time -from typing import TYPE_CHECKING, Any - -from .general import SysCommand -from .output import warn, error - -if TYPE_CHECKING: - _: Any - - -def run_pacman(args :str, default_cmd :str = 'pacman') -> SysCommand: - """ - A centralized function to call `pacman` from. - It also protects us from colliding with other running pacman sessions (if used locally). - The grace period is set to 10 minutes before exiting hard if another pacman instance is running. - """ - pacman_db_lock = pathlib.Path('/var/lib/pacman/db.lck') - - if pacman_db_lock.exists(): - warn(_('Pacman is already running, waiting maximum 10 minutes for it to terminate.')) - - started = time.time() - while pacman_db_lock.exists(): - time.sleep(0.25) - - if time.time() - started > (60 * 10): - error(_('Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall.')) - exit(1) - - return SysCommand(f'{default_cmd} {args}') diff --git a/archinstall/lib/pacman/__init__.py b/archinstall/lib/pacman/__init__.py new file mode 100644 index 00000000..6478f0cc --- /dev/null +++ b/archinstall/lib/pacman/__init__.py @@ -0,0 +1,88 @@ +from pathlib import Path +import time +import re +from typing import TYPE_CHECKING, Any, List, Callable, Union +from shutil import copy2 + +from ..general import SysCommand +from ..output import warn, error, info +from .repo import Repo +from .config import Config +from ..exceptions import RequirementError +from ..plugins import plugins + +if TYPE_CHECKING: + _: Any + + +class Pacman: + + def __init__(self, target: Path, silent: bool = False): + self.synced = False + self.silent = silent + self.target = target + + @staticmethod + def run(args :str, default_cmd :str = 'pacman') -> SysCommand: + """ + A centralized function to call `pacman` from. + It also protects us from colliding with other running pacman sessions (if used locally). + The grace period is set to 10 minutes before exiting hard if another pacman instance is running. + """ + pacman_db_lock = Path('/var/lib/pacman/db.lck') + + if pacman_db_lock.exists(): + warn(_('Pacman is already running, waiting maximum 10 minutes for it to terminate.')) + + started = time.time() + while pacman_db_lock.exists(): + time.sleep(0.25) + + if time.time() - started > (60 * 10): + error(_('Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall.')) + exit(1) + + return SysCommand(f'{default_cmd} {args}') + + def ask(self, error_message: str, bail_message: str, func: Callable, *args, **kwargs): + while True: + try: + func(*args, **kwargs) + break + except Exception as err: + error(f'{error_message}: {err}') + if not self.silent and input('Would you like to re-try this download? (Y/n): ').lower().strip() in 'y': + continue + raise RequirementError(f'{bail_message}: {err}') + + def sync(self): + if self.synced: + return + self.ask( + 'Could not sync a new package database', + 'Could not sync mirrors', + self.run, + '-Syy', + default_cmd='/usr/bin/pacman' + ) + self.synced = True + + def strap(self, packages: Union[str, List[str]]): + self.sync() + if isinstance(packages, str): + packages = [packages] + + for plugin in plugins.values(): + if hasattr(plugin, 'on_pacstrap'): + if (result := plugin.on_pacstrap(packages)): + packages = result + + info(f'Installing packages: {packages}') + + self.ask( + 'Could not strap in packages', + 'Pacstrap failed. See /var/log/archinstall/install.log or above message for error details', + SysCommand, + f'/usr/bin/pacstrap -C /etc/pacman.conf -K {self.target} {" ".join(packages)} --noconfirm', + peek_output=True + ) diff --git a/archinstall/lib/pacman/config.py b/archinstall/lib/pacman/config.py new file mode 100644 index 00000000..60d202bc --- /dev/null +++ b/archinstall/lib/pacman/config.py @@ -0,0 +1,33 @@ +import re +from pathlib import Path +from shutil import copy2 +from typing import List + +from .repo import Repo + + +class Config: + def __init__(self, target: Path): + self.path = Path("/etc") / "pacman.conf" + self.chroot_path = target / "etc" / "pacman.conf" + self.patterns: List[re.Pattern] = [] + + def enable(self, repo: Repo): + self.patterns.append(re.compile(r"^#\s*\[{}\]$".format(repo.value))) + + def apply(self): + if not self.patterns: + return + lines = iter(self.path.read_text().splitlines(keepends=True)) + with open(self.path, 'w') as f: + for line in lines: + if any(pattern.match(line) for pattern in self.patterns): + # Uncomment this line and the next. + f.write(line.lstrip('#')) + f.write(next(lines).lstrip('#')) + else: + f.write(line) + + def persist(self): + if self.patterns: + copy2(self.path, self.chroot_path) diff --git a/archinstall/lib/pacman/repo.py b/archinstall/lib/pacman/repo.py new file mode 100644 index 00000000..b4106f97 --- /dev/null +++ b/archinstall/lib/pacman/repo.py @@ -0,0 +1,6 @@ +from enum import Enum + +class Repo(Enum): + Multilib = "multilib" + Testing = "testing" + MultilibTesting = "multilib-testing" diff --git a/archinstall/lib/profile/profiles_handler.py b/archinstall/lib/profile/profiles_handler.py index 4e7c3d2b..74c21824 100644 --- a/archinstall/lib/profile/profiles_handler.py +++ b/archinstall/lib/profile/profiles_handler.py @@ -98,14 +98,19 @@ class ProfileHandler: profile = self.get_profile_by_name(main) if main else None valid: List[Profile] = [] + details: List[str] = profile_config.get('details', []) + if details: + valid = [] + invalid = [] - if details := profile_config.get('details', []): - resolved = {detail: self.get_profile_by_name(detail) for detail in details if detail} - valid = [p for p in resolved.values() if p is not None] - invalid = ', '.join([k for k, v in resolved.items() if v is None]) + for detail in filter(None, details): + if profile := self.get_profile_by_name(detail): + valid.append(profile) + else: + invalid.append(detail) if invalid: - info(f'No profile definition found: {invalid}') + info('No profile definition found: {}'.format(', '.join(invalid))) custom_settings = profile_config.get('custom_settings', {}) for profile in valid: @@ -123,14 +128,12 @@ class ProfileHandler: """ List of all available default_profiles """ - if self._profiles is None: - self._profiles = self._find_available_profiles() + self._profiles = self._profiles or self._find_available_profiles() return self._profiles @cached_property def _local_mac_addresses(self) -> List[str]: - ifaces = list_interfaces() - return list(ifaces.keys()) + return list(list_interfaces()) def add_custom_profiles(self, profiles: Union[TProfile, List[TProfile]]): if not isinstance(profiles, list): @@ -190,25 +193,20 @@ class ProfileHandler: def install_gfx_driver(self, install_session: 'Installer', driver: Optional[GfxDriver]): try: - driver_pkgs = driver.packages() if driver else [] - pkg_names = [p.value for p in driver_pkgs] - additional_pkg = ' '.join(['xorg-server', 'xorg-xinit'] + pkg_names) if driver is not None: - # Find the intersection between the set of known nvidia drivers - # and the selected driver packages. Since valid intesections can - # only have one element or none, we iterate and try to take the - # first element. - if driver_pkg := next(iter({GfxPackage.Nvidia, GfxPackage.NvidiaOpen} & set(driver_pkgs)), None): - if any(kernel in install_session.base_packages for kernel in ("linux-lts", "linux-zen")): - for kernel in install_session.kernels: - # Fixes https://github.com/archlinux/archinstall/issues/585 - install_session.add_additional_packages(f"{kernel}-headers") + driver_pkgs = driver.packages() + pkg_names = [p.value for p in driver_pkgs] + for driver_pkg in {GfxPackage.Nvidia, GfxPackage.NvidiaOpen} & set(driver_pkgs): + for kernel in {"linux-lts", "linux-zen"} & set(install_session.kernels): + # Fixes https://github.com/archlinux/archinstall/issues/585 + install_session.add_additional_packages(f"{kernel}-headers") # I've had kernel regen fail if it wasn't installed before nvidia-dkms - install_session.add_additional_packages(['dkms', 'xorg-server', 'xorg-xinit', f'{driver_pkg}-dkms']) - return - elif 'amdgpu' in driver_pkgs: + install_session.add_additional_packages(['dkms', 'xorg-server', 'xorg-xinit', f'{driver_pkg.value}-dkms']) + # Return after first driver match, since it is impossible to use both simultaneously. + return + if 'amdgpu' in driver_pkgs: # The order of these two are important if amdgpu is installed #808 if 'amdgpu' in install_session.modules: install_session.modules.remove('amdgpu') @@ -218,23 +216,24 @@ class ProfileHandler: install_session.modules.remove('radeon') install_session.modules.append('radeon') - install_session.add_additional_packages(additional_pkg) + install_session.add_additional_packages(pkg_names) except Exception as err: warn(f"Could not handle nvidia and linuz-zen specific situations during xorg installation: {err}") # Prep didn't run, so there's no driver to install - install_session.add_additional_packages(['xorg-server', 'xorg-xinit']) + install_session.add_additional_packages(['xorg-server', 'xorg-xinit']) def install_profile_config(self, install_session: 'Installer', profile_config: ProfileConfiguration): profile = profile_config.profile - if profile: - profile.install(install_session) + if not profile: + return - if profile and profile_config.gfx_driver: - if profile.is_xorg_type_profile() or profile.is_desktop_type_profile(): - self.install_gfx_driver(install_session, profile_config.gfx_driver) + profile.install(install_session) - if profile and profile_config.greeter: + if profile_config.gfx_driver and (profile.is_xorg_type_profile() or profile.is_desktop_type_profile()): + self.install_gfx_driver(install_session, profile_config.gfx_driver) + + if profile_config.greeter: self.install_greeter(install_session, profile_config.greeter) def _import_profile_from_url(self, url: str): @@ -312,8 +311,7 @@ class ProfileHandler: debug(f'Importing profile: {file}') try: - spec = importlib.util.spec_from_file_location(name, file) - if spec is not None: + if spec := importlib.util.spec_from_file_location(name, file): imported = importlib.util.module_from_spec(spec) if spec.loader is not None: spec.loader.exec_module(imported) -- cgit v1.2.3-70-g09d2 From 5b102b0228e8ffce81341bb2bf771a082d8263c4 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Wed, 28 Jun 2023 14:22:07 +0200 Subject: Adding python-simple-term-menu to the dependency list (#1901) * Adding python-simple-term-menu to the dependency list * Added dependencies to all binaries we call, such as 'ps' and 'mkfs' etc * Sorted the depends list - just for peace of mind * Bumped version in prep for release of rc1, also updated README a bit * Removed older python versions from classifiers --- PKGBUILD | 14 ++++++++++++-- README.md | 11 ++++++++--- archinstall/__init__.py | 2 +- pyproject.toml | 8 +++----- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index 03160a1c..315256e3 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -4,16 +4,26 @@ # Contributor: demostanis worlds pkgname=archinstall -pkgver=2.5.6 +pkgver=2.6.0 pkgrel=1 pkgdesc="Just another guided/automated Arch Linux installer with a twist" arch=(any) url="https://github.com/archlinux/archinstall" license=(GPL3) depends=( + 'arch-install-scripts' + 'btrfs-progs' + 'coreutils' + 'cryptsetup' + 'e2fsprogs' + 'kbd' + 'pciutils' + 'procps-ng' 'python' - 'systemd' 'python-pyparted' + 'python-simple-term-menu' + 'systemd' + 'util-linux' ) makedepends=( 'python-setuptools' diff --git a/README.md b/README.md index 9d6fecd9..9ec40d17 100644 --- a/README.md +++ b/README.md @@ -17,15 +17,20 @@ The installer also doubles as a python library to install Arch Linux and manage $ sudo pacman -S archinstall -Or simply `git clone` the repo as it has no external dependencies *(but there are optional ones)*.
-Or use `pip install --upgrade archinstall` to use as a library. +Alternative ways to install are `git clone` the repository or `pip install --upgrade archinstall`. ## Running the [guided](https://github.com/archlinux/archinstall/blob/master/archinstall/scripts/guided.py) installer -Assuming you are on an Arch Linux live-ISO: +Assuming you are on an Arch Linux live-ISO or installed via `pip`: # archinstall +## Running the [guided](https://github.com/archlinux/archinstall/blob/master/archinstall/scripts/guided.py) installer using `git` + + # cd archinstall-git + # cp archinstall/scripts/guided.py + # python guided.py + #### Advanced Some additional options that are not needed by most users are hidden behind the `--advanced` flag. diff --git a/archinstall/__init__.py b/archinstall/__init__.py index e5ec462a..af811465 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -39,7 +39,7 @@ if TYPE_CHECKING: _: Any -__version__ = "2.5.6" +__version__ = "2.6.0rc1" storage['__version__'] = __version__ diff --git a/pyproject.toml b/pyproject.toml index f67f1eca..83c68e1d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,12 +11,10 @@ authors = [ ] license = {text = "GPL-3.0-only"} readme = "README.md" -requires-python = ">=3.10" +requires-python = ">=3.11" keywords = ["linux", "arch", "archinstall", "installer"] classifiers = [ - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Operating System :: POSIX :: Linux", ] dependencies = [ @@ -60,7 +58,7 @@ packages = ["archinstall"] # where = ["archinstall"] [tool.mypy] -python_version = "3.10" +python_version = "3.11" files = "archinstall/" exclude = "tests" #check_untyped_defs=true -- cgit v1.2.3-70-g09d2 From 0785b35eb4fb5ce9a1182f3579ed673aa96b8f9f Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Wed, 28 Jun 2023 08:51:07 -0400 Subject: Bump Python in workflow (#1863) * Bump Python in workflow * Attempting fix to build runner failing * Appended --break-system-packages since the new changes was introduced in Arch --------- Co-authored-by: Anton Hvornum --- .github/workflows/python-build.yml | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/.github/workflows/python-build.yml b/.github/workflows/python-build.yml index 950ff8f4..f51ff887 100644 --- a/.github/workflows/python-build.yml +++ b/.github/workflows/python-build.yml @@ -12,23 +12,16 @@ jobs: options: --privileged steps: - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.10' - name: Prepare arch run: | pacman-key --init pacman --noconfirm -Sy archlinux-keyring - pacman --noconfirm -Sy python-pyparted pkgconfig gcc + pacman --noconfirm -Sy python-pip python-pyparted python-simple-term-menu pkgconfig gcc - name: Install build dependencies run: | - python -m pip install --upgrade pip - pip install --upgrade build twine wheel setuptools installer - pip uninstall archinstall -y - - name: Install package dependencies - run: | - pip install --upgrade simple-term-menu pyparted + python -m pip install --break-system-packages --upgrade pip + pip install --break-system-packages --upgrade build twine wheel setuptools installer + pip uninstall archinstall -y --break-system-packages - name: Build archinstall run: python -m build --wheel --no-isolation - name: Install archinstall -- cgit v1.2.3-70-g09d2 From 2450c7bdff093aafba4a227156234ceed1887c31 Mon Sep 17 00:00:00 2001 From: Sumit Patel <89197795+Dark-Kernel@users.noreply.github.com> Date: Wed, 28 Jun 2023 23:34:32 +0530 Subject: Created dwm.py (#1902) Created dwm.py for suckless users. --- archinstall/default_profiles/desktops/dwm.py | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 archinstall/default_profiles/desktops/dwm.py diff --git a/archinstall/default_profiles/desktops/dwm.py b/archinstall/default_profiles/desktops/dwm.py new file mode 100644 index 00000000..3a7c0348 --- /dev/null +++ b/archinstall/default_profiles/desktops/dwm.py @@ -0,0 +1,35 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class BspwmProfile(XorgProfile): + def __init__(self): + super().__init__('Bspwm', ProfileType.WindowMgr, description='') + + @property + def packages(self) -> List[str]: + return [ + 'dwm', + 'st', + 'dmenu', + 'picom', + 'xorg-xsetroot', + 'xorg-xset', + 'pywal', + 'xdo', + 'dunst', + 'slock' + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm + + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + return text + '\n' + self.packages_text() -- cgit v1.2.3-70-g09d2 From 025ee8f064f6a142aa3de702c42c52101e8da9f6 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Wed, 28 Jun 2023 23:07:25 +0200 Subject: Fixed indentation issues from #1902 (#1904) --- archinstall/default_profiles/desktops/dwm.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/archinstall/default_profiles/desktops/dwm.py b/archinstall/default_profiles/desktops/dwm.py index 3a7c0348..6b9ca6bc 100644 --- a/archinstall/default_profiles/desktops/dwm.py +++ b/archinstall/default_profiles/desktops/dwm.py @@ -19,11 +19,11 @@ class BspwmProfile(XorgProfile): 'dmenu', 'picom', 'xorg-xsetroot', - 'xorg-xset', - 'pywal', - 'xdo', - 'dunst', - 'slock' + 'xorg-xset', + 'pywal', + 'xdo', + 'dunst', + 'slock' ] @property -- cgit v1.2.3-70-g09d2 From 58bccccd5d2048dd002ec0f2efeb185e480a72bd Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Thu, 29 Jun 2023 09:49:22 +0200 Subject: Revert "Created dwm.py (#1902)" - This reverts commit 2450c7bdff093aafba4a227156234ceed1887c31. (#1905) --- archinstall/default_profiles/desktops/dwm.py | 35 ---------------------------- 1 file changed, 35 deletions(-) delete mode 100644 archinstall/default_profiles/desktops/dwm.py diff --git a/archinstall/default_profiles/desktops/dwm.py b/archinstall/default_profiles/desktops/dwm.py deleted file mode 100644 index 6b9ca6bc..00000000 --- a/archinstall/default_profiles/desktops/dwm.py +++ /dev/null @@ -1,35 +0,0 @@ -from typing import List, Optional, Any, TYPE_CHECKING - -from archinstall.default_profiles.profile import ProfileType, GreeterType -from archinstall.default_profiles.xorg import XorgProfile - -if TYPE_CHECKING: - _: Any - - -class BspwmProfile(XorgProfile): - def __init__(self): - super().__init__('Bspwm', ProfileType.WindowMgr, description='') - - @property - def packages(self) -> List[str]: - return [ - 'dwm', - 'st', - 'dmenu', - 'picom', - 'xorg-xsetroot', - 'xorg-xset', - 'pywal', - 'xdo', - 'dunst', - 'slock' - ] - - @property - def default_greeter_type(self) -> Optional[GreeterType]: - return GreeterType.Lightdm - - def preview_text(self) -> Optional[str]: - text = str(_('Environment type: {}')).format(self.profile_type.value) - return text + '\n' + self.packages_text() -- cgit v1.2.3-70-g09d2 From ffb9366280803578bd47f2d7102a5772fc44caab Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Thu, 29 Jun 2023 09:33:14 -0400 Subject: Skip rom devices (#1906) --- archinstall/lib/disk/device_handler.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index 2c88e382..9acf0999 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -45,6 +45,9 @@ class DeviceHandler(object): block_devices = {} for device in getAllDevices(): + if get_lsblk_info(device.path).type == 'rom': + continue + try: disk = Disk(device) except DiskLabelException as err: -- cgit v1.2.3-70-g09d2 From a0e4e6ee7604419d58d80f22b0348df6e745d8c8 Mon Sep 17 00:00:00 2001 From: Anhad Singh <62820092+Andy-Python-Programmer@users.noreply.github.com> Date: Fri, 30 Jun 2023 17:53:53 +1000 Subject: installer: add Limine bootloader (#1815) * installer: add Limine bootloader Limine is a modern, advanced, portable, multiprotocol bootloader. [Limine GitHub](https://github.com/limine-bootloader/limine) [Limine Arch Wiki](https://wiki.archlinux.org/title/Limine) Signed-off-by: Anhad Singh * limine: add UEFI support Signed-off-by: Anhad Singh * global_menu: check filesystem and bootloader compatibility Before on install, only missing configurations were checked. This commit introduces bootloader validatity checks on install which verify if the selected filesystem is compatiable with the selected bootloader (for example, it is not possible to boot limine from BTRFS). Signed-off-by: Anhad Singh * misc: fix the return value of `_validate_bootloader` Signed-off-by: Anhad Singh * global_menu: make `mypy` happy Signed-off-by: Anhad Singh * misc: make `flake8` happy Signed-off-by: Anhad Singh * limine: upgrade to v5 Signed-off-by: Anhad Singh * limine: install packman hooks Create the BIOS and UEFI pacman hooks so limine gets auto deployed on update. Signed-off-by: Anhad Singh * installer::limine: fix broken root UUID Signed-off-by: Anhad Singh * docs: add a note saying its in beta Signed-off-by: Anhad Singh * install_limine: use `safe_fs_type` Signed-off-by: Anhad Singh --------- Signed-off-by: Anhad Singh --- archinstall/lib/global_menu.py | 44 ++++++++++- archinstall/lib/installer.py | 112 ++++++++++++++++++++++++++++ archinstall/lib/interactions/system_conf.py | 4 +- archinstall/lib/models/bootloader.py | 1 + 4 files changed, 156 insertions(+), 5 deletions(-) diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 54b30240..5a431010 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -169,8 +169,8 @@ class GlobalMenu(AbstractMenu): self._menu_options['install'] = \ Selector( self._install_text(), - exec_func=lambda n, v: True if len(self._missing_configs()) == 0 else False, - preview_func=self._prev_install_missing_config, + exec_func=lambda n, v: self._is_config_valid(), + preview_func=self._prev_install_invalid_config, no_store=True) self._menu_options['abort'] = Selector(_('Abort'), exec_func=lambda n,v:exit(1)) @@ -200,6 +200,14 @@ class GlobalMenu(AbstractMenu): return list(missing) + def _is_config_valid(self) -> bool: + """ + Checks the validity of the current configuration. + """ + if len(self._missing_configs()) != 0: + return False + return self._validate_bootloader() is None + def _update_install_text(self, name: str, value: str): text = self._install_text() self._menu_options['install'].update_description(text) @@ -321,12 +329,42 @@ class GlobalMenu(AbstractMenu): return disk.EncryptionType.type_to_text(current_value.encryption_type) return '' - def _prev_install_missing_config(self) -> Optional[str]: + def _validate_bootloader(self) -> Optional[str]: + """ + Checks the selected bootloader is valid for the selected filesystem + type of the boot partition. + + Returns [`None`] if the bootloader is valid, otherwise returns a + string with the error message. + """ + bootloader = self._menu_options['bootloader'].current_selection + boot_partition: Optional[disk.PartitionModification] = None + + if disk_config := self._menu_options['disk_config'].current_selection: + for layout in disk_config.device_modifications: + if boot_partition := layout.get_boot_partition(): + break + else: + return "No disk layout selected" + + if boot_partition is None: + return "Boot partition not found" + + if bootloader == Bootloader.Limine and boot_partition.fs_type == disk.FilesystemType.Btrfs: + return "Limine bootloader does not support booting from BTRFS filesystem" + + return None + + def _prev_install_invalid_config(self) -> Optional[str]: if missing := self._missing_configs(): text = str(_('Missing configurations:\n')) for m in missing: text += f'- {m}\n' return text[:-1] # remove last new line + + if error := self._validate_bootloader(): + return f"Invalid configuration: {error}" + return None def _prev_users(self) -> Optional[str]: diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index ee546993..0d43b2fe 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -8,6 +8,8 @@ import time from pathlib import Path from typing import Any, List, Optional, TYPE_CHECKING, Union, Dict, Callable +from ..lib.disk.device_model import get_lsblk_info + from . import disk from .exceptions import DiskError, ServiceException, RequirementError, HardwareIncompatibilityError, SysCallError from .general import SysCommand @@ -850,6 +852,113 @@ class Installer: self.helper_flags['bootloader'] = "grub" + def _add_limine_bootloader( + self, + boot_partition: disk.PartitionModification, + root_partition: disk.PartitionModification + ): + self.pacman.strap('limine') + info(f"Limine boot partition: {boot_partition.dev_path}") + + # XXX: We cannot use `root_partition.uuid` since corresponds to the UUID of the root + # partition before the format. + root_uuid = get_lsblk_info(root_partition.safe_dev_path).uuid + + device = disk.device_handler.get_device_by_partition_path(boot_partition.safe_dev_path) + if not device: + raise ValueError(f'Can not find block device: {boot_partition.safe_dev_path}') + + def create_pacman_hook(contents: str): + HOOK_DIR = "/etc/pacman.d/hooks" + SysCommand(f"/usr/bin/arch-chroot {self.target} mkdir -p {HOOK_DIR}") + SysCommand(f"/usr/bin/arch-chroot {self.target} sh -c \"echo '{contents}' > {HOOK_DIR}/liminedeploy.hook\"") + + if SysInfo.has_uefi(): + try: + # The `limine.sys` file, contains stage 3 code. + cmd = f'/usr/bin/arch-chroot' \ + f' {self.target}' \ + f' cp' \ + f' /usr/share/limine/BOOTX64.EFI' \ + f' /boot/EFI/BOOT/' + except SysCallError as err: + raise DiskError(f"Failed to install Limine BOOTX64.EFI on {boot_partition.dev_path}: {err}") + + # Create the EFI limine pacman hook. + create_pacman_hook(""" +[Trigger] +Operation = Install +Operation = Upgrade +Type = Package +Target = limine + +[Action] +Description = Deploying Limine after upgrade... +When = PostTransaction +Exec = /usr/bin/cp /usr/share/limine/BOOTX64.EFI /boot/EFI/BOOT/ + """) + else: + try: + # The `limine.sys` file, contains stage 3 code. + cmd = f'/usr/bin/arch-chroot' \ + f' {self.target}' \ + f' cp' \ + f' /usr/share/limine/limine-bios.sys' \ + f' /boot/limine-bios.sys' + + SysCommand(cmd, peek_output=True) + + # `limine bios-install` deploys the stage 1 and 2 to the disk. + cmd = f'/usr/bin/arch-chroot' \ + f' {self.target}' \ + f' limine' \ + f' bios-install' \ + f' {device.device_info.path}' + + SysCommand(cmd, peek_output=True) + except SysCallError as err: + raise DiskError(f"Failed to install Limine on {boot_partition.dev_path}: {err}") + + create_pacman_hook(f""" +[Trigger] +Operation = Install +Operation = Upgrade +Type = Package +Target = limine + +[Action] +Description = Deploying Limine after upgrade... +When = PostTransaction +# XXX: Kernel name descriptors cannot be used since they are not persistent and +# can change after each boot. +Exec = /bin/sh -c \\"/usr/bin/limine bios-install /dev/disk/by-uuid/{root_uuid} && /usr/bin/cp /usr/share/limine/limine-bios.sys /boot/\\" + """) + + # Limine does not ship with a default configuation file. We are going to + # create a basic one that is similar to the one GRUB generates. + try: + config = f""" +TIMEOUT=5 + +:Arch Linux + PROTOCOL=linux + KERNEL_PATH=boot:///vmlinuz-linux + CMDLINE=root=UUID={root_uuid} rw rootfstype={root_partition.safe_fs_type.value} loglevel=3 + MODULE_PATH=boot:///initramfs-linux.img + +:Arch Linux (fallback) + PROTOCOL=linux + KERNEL_PATH=boot:///vmlinuz-linux + CMDLINE=root=UUID={root_uuid} rw rootfstype={root_partition.safe_fs_type.value} loglevel=3 + MODULE_PATH=boot:///initramfs-linux-fallback.img + """ + + SysCommand(f"/usr/bin/arch-chroot {self.target} sh -c \"echo '{config}' > /boot/limine.cfg\"") + except SysCallError as err: + raise DiskError(f"Could not configure Limine: {err}") + + self.helper_flags['bootloader'] = "limine" + def _add_efistub_bootloader( self, boot_partition: disk.PartitionModification, @@ -918,6 +1027,7 @@ class Installer: Archinstall supports one of three types: * systemd-bootctl * grub + * limine (beta) * efistub (beta) :param bootloader: Type of bootloader to be added @@ -948,6 +1058,8 @@ class Installer: self._add_grub_bootloader(boot_partition, root_partition) case Bootloader.Efistub: self._add_efistub_bootloader(boot_partition, root_partition) + case Bootloader.Limine: + self._add_limine_bootloader(boot_partition, root_partition) def add_additional_packages(self, packages: Union[str, List[str]]) -> bool: return self.pacman.strap(packages) diff --git a/archinstall/lib/interactions/system_conf.py b/archinstall/lib/interactions/system_conf.py index 5b1bc456..0e5e0f1e 100644 --- a/archinstall/lib/interactions/system_conf.py +++ b/archinstall/lib/interactions/system_conf.py @@ -40,9 +40,9 @@ def select_kernel(preset: List[str] = []) -> List[str]: def ask_for_bootloader(preset: Bootloader) -> Bootloader: - # when the system only supports grub + # Systemd is UEFI only if not SysInfo.has_uefi(): - options = [Bootloader.Grub.value] + options = [Bootloader.Grub.value, Bootloader.Limine.value] default = Bootloader.Grub.value else: options = Bootloader.values() diff --git a/archinstall/lib/models/bootloader.py b/archinstall/lib/models/bootloader.py index e21cda33..be9812a0 100644 --- a/archinstall/lib/models/bootloader.py +++ b/archinstall/lib/models/bootloader.py @@ -12,6 +12,7 @@ class Bootloader(Enum): Systemd = 'Systemd-boot' Grub = 'Grub' Efistub = 'Efistub' + Limine = 'Limine' def json(self): return self.value -- cgit v1.2.3-70-g09d2 From a04e68495bdf108ec48f5dc680de0b50df6e6dd1 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Fri, 30 Jun 2023 08:36:38 -0400 Subject: Refactor `_add_systemd_bootloader()` entries creation (#1907) --- archinstall/lib/installer.py | 111 ++++++++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 50 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 0d43b2fe..5d932981 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -736,61 +736,72 @@ class Installer: entries_dir = loader_dir / 'entries' entries_dir.mkdir(parents=True, exist_ok=True) + comments = ( + '# Created by: archinstall\n', + f'# Created on: {self.init_time}\n' + ) + + microcode = [] + + if not SysInfo.is_vm(): + vendor = SysInfo.cpu_vendor() + if vendor == "AuthenticAMD": + microcode.append('initrd /amd-ucode.img\n') + elif vendor == "GenuineIntel": + microcode.append('initrd /intel-ucode.img\n') + else: + debug( + f"Unknown CPU vendor '{vendor}' detected.", + "Archinstall won't add any ucode to systemd-boot config.", + ) + + # blkid doesn't trigger on loopback devices really well, + # so we'll use the old manual method until we get that sorted out. + + options_entry = f'rw rootfstype={root_partition.safe_fs_type.fs_type_mount} {" ".join(self._kernel_params)}' + + for sub_vol in root_partition.btrfs_subvols: + if sub_vol.is_root(): + options_entry = f"rootflags=subvol={sub_vol.name} " + options_entry + + # Zswap should be disabled when using zram. + # https://github.com/archlinux/archinstall/issues/881 + if self._zram_enabled: + options_entry = "zswap.enabled=0 " + options_entry + + if root_partition.safe_fs_type.is_crypto(): + # TODO: We need to detect if the encrypted device is a whole disk encryption, + # or simply a partition encryption. Right now we assume it's a partition (and we always have) + debug('Root partition is an encrypted device, identifying by PARTUUID: {root_partition.partuuid}') + + if self._disk_encryption and self._disk_encryption.hsm_device: + # Note: lsblk UUID must be used, not PARTUUID for sd-encrypt to work + kernel_options = f'rd.luks.name={root_partition.uuid}=luksdev' + # Note: tpm2-device and fido2-device don't play along very well: + # https://github.com/archlinux/archinstall/pull/1196#issuecomment-1129715645 + kernel_options += ' rd.luks.options=fido2-device=auto,password-echo=no' + else: + kernel_options = f'cryptdevice=PARTUUID={root_partition.partuuid}:luksdev' + + cmdline = f'{kernel_options} root=/dev/mapper/luksdev {options_entry}' + else: + debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}') + cmdline = f'root=PARTUUID={root_partition.partuuid} {options_entry}' + for kernel in self.kernels: for variant in ("", "-fallback"): # Setup the loader entry with open(entries_dir / f'{self.init_time}_{kernel}{variant}.conf', 'w') as entry: - entry.write('# Created by: archinstall\n') - entry.write(f'# Created on: {self.init_time}\n') - entry.write(f'title Arch Linux ({kernel}{variant})\n') - entry.write(f"linux /vmlinuz-{kernel}\n") - if not SysInfo.is_vm(): - vendor = SysInfo.cpu_vendor() - if vendor == "AuthenticAMD": - entry.write("initrd /amd-ucode.img\n") - elif vendor == "GenuineIntel": - entry.write("initrd /intel-ucode.img\n") - else: - debug( - f"Unknown CPU vendor '{vendor}' detected.", - "Archinstall won't add any ucode to systemd-boot config.", - ) - - entry.write(f"initrd /initramfs-{kernel}{variant}.img\n") - # blkid doesn't trigger on loopback devices really well, - # so we'll use the old manual method until we get that sorted out. - - options_entry = f'rw rootfstype={root_partition.safe_fs_type.fs_type_mount} {" ".join(self._kernel_params)}\n' - - for sub_vol in root_partition.btrfs_subvols: - if sub_vol.is_root(): - options_entry = f"rootflags=subvol={sub_vol.name} " + options_entry - - # Zswap should be disabled when using zram. - # https://github.com/archlinux/archinstall/issues/881 - if self._zram_enabled: - options_entry = "zswap.enabled=0 " + options_entry - - if root_partition.safe_fs_type.is_crypto(): - # TODO: We need to detect if the encrypted device is a whole disk encryption, - # or simply a partition encryption. Right now we assume it's a partition (and we always have) - debug('Root partition is an encrypted device, identifying by PARTUUID: {root_partition.partuuid}') - - kernel_options = f"options" - - if self._disk_encryption and self._disk_encryption.hsm_device: - # Note: lsblk UUID must be used, not PARTUUID for sd-encrypt to work - kernel_options += f' rd.luks.name={root_partition.uuid}=luksdev' - # Note: tpm2-device and fido2-device don't play along very well: - # https://github.com/archlinux/archinstall/pull/1196#issuecomment-1129715645 - kernel_options += f' rd.luks.options=fido2-device=auto,password-echo=no' - else: - kernel_options += f' cryptdevice=PARTUUID={root_partition.partuuid}:luksdev' + entry_lines: List[str] = [] - entry.write(f'{kernel_options} root=/dev/mapper/luksdev {options_entry}') - else: - debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}') - entry.write(f'options root=PARTUUID={root_partition.partuuid} {options_entry}') + entry_lines.extend(comments) + entry_lines.append(f'title Arch Linux ({kernel}{variant})\n') + entry_lines.append(f'linux /vmlinuz-{kernel}\n') + entry_lines.extend(microcode) + entry_lines.append(f'initrd /initramfs-{kernel}{variant}.img\n') + entry_lines.append(f'options {cmdline}\n') + + entry.writelines(entry_lines) self.helper_flags['bootloader'] = 'systemd' -- cgit v1.2.3-70-g09d2 From 10fc344097c392685d286e585e91493556c96847 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Fri, 30 Jun 2023 14:27:49 -0400 Subject: Refactor `_add_systemd_bootloader()` entries options (#1909) * Refactor `_add_systemd_bootloader()` entries options * Added suggested tweak https://github.com/archlinux/archinstall/pull/1907#discussion_r1247558250 --------- Co-authored-by: Anton Hvornum --- archinstall/lib/installer.py | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 5d932981..083bd7c9 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -755,19 +755,7 @@ class Installer: "Archinstall won't add any ucode to systemd-boot config.", ) - # blkid doesn't trigger on loopback devices really well, - # so we'll use the old manual method until we get that sorted out. - - options_entry = f'rw rootfstype={root_partition.safe_fs_type.fs_type_mount} {" ".join(self._kernel_params)}' - - for sub_vol in root_partition.btrfs_subvols: - if sub_vol.is_root(): - options_entry = f"rootflags=subvol={sub_vol.name} " + options_entry - - # Zswap should be disabled when using zram. - # https://github.com/archlinux/archinstall/issues/881 - if self._zram_enabled: - options_entry = "zswap.enabled=0 " + options_entry + options_entry = [] if root_partition.safe_fs_type.is_crypto(): # TODO: We need to detect if the encrypted device is a whole disk encryption, @@ -776,17 +764,33 @@ class Installer: if self._disk_encryption and self._disk_encryption.hsm_device: # Note: lsblk UUID must be used, not PARTUUID for sd-encrypt to work - kernel_options = f'rd.luks.name={root_partition.uuid}=luksdev' + options_entry.append(f'rd.luks.name={root_partition.uuid}=luksdev') # Note: tpm2-device and fido2-device don't play along very well: # https://github.com/archlinux/archinstall/pull/1196#issuecomment-1129715645 - kernel_options += ' rd.luks.options=fido2-device=auto,password-echo=no' + options_entry.append('rd.luks.options=fido2-device=auto,password-echo=no') else: - kernel_options = f'cryptdevice=PARTUUID={root_partition.partuuid}:luksdev' + options_entry.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:luksdev') - cmdline = f'{kernel_options} root=/dev/mapper/luksdev {options_entry}' + options_entry.append('root=/dev/mapper/luksdev') else: debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}') - cmdline = f'root=PARTUUID={root_partition.partuuid} {options_entry}' + options_entry.append(f'root=PARTUUID={root_partition.partuuid}') + + # Zswap should be disabled when using zram. + # https://github.com/archlinux/archinstall/issues/881 + if self._zram_enabled: + options_entry.append('zswap.enabled=0') + + for sub_vol in root_partition.btrfs_subvols: + if sub_vol.is_root(): + options_entry.append(f'rootflags=subvol={sub_vol.name}') + break + + options_entry.append('rw') + options_entry.append(f'rootfstype={root_partition.safe_fs_type.fs_type_mount}') + options_entry.extend(self._kernel_params) + + options = 'options ' + ' '.join(options_entry) + '\n' for kernel in self.kernels: for variant in ("", "-fallback"): @@ -799,7 +803,7 @@ class Installer: entry_lines.append(f'linux /vmlinuz-{kernel}\n') entry_lines.extend(microcode) entry_lines.append(f'initrd /initramfs-{kernel}{variant}.img\n') - entry_lines.append(f'options {cmdline}\n') + entry_lines.append(options) entry.writelines(entry_lines) -- cgit v1.2.3-70-g09d2 From ba9f2eea5952690ef1a6bf96b3e3055528c71d2f Mon Sep 17 00:00:00 2001 From: Anhad Singh <62820092+Andy-Python-Programmer@users.noreply.github.com> Date: Sat, 1 Jul 2023 04:29:18 +1000 Subject: global_menu: wrap invalid config error message with the translation shim (#1908) * global_menu: wrap invalid config error message with the translation shim Signed-off-by: Anhad Singh * validate_bootloader: add a caller note XXX: The caller is responsible for wrapping the string with the translation shim if necessary. Signed-off-by: Anhad Singh --------- Signed-off-by: Anhad Singh --- archinstall/lib/global_menu.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 5a431010..02b1b0b6 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -336,6 +336,9 @@ class GlobalMenu(AbstractMenu): Returns [`None`] if the bootloader is valid, otherwise returns a string with the error message. + + XXX: The caller is responsible for wrapping the string with the translation + shim if necessary. """ bootloader = self._menu_options['bootloader'].current_selection boot_partition: Optional[disk.PartitionModification] = None @@ -363,7 +366,7 @@ class GlobalMenu(AbstractMenu): return text[:-1] # remove last new line if error := self._validate_bootloader(): - return f"Invalid configuration: {error}" + return str(_(f"Invalid configuration: {error}")) return None -- cgit v1.2.3-70-g09d2 From 64fc585fa7624060d406b9fcb877baf968ff0f24 Mon Sep 17 00:00:00 2001 From: uranderu <71091366+uranderu@users.noreply.github.com> Date: Fri, 30 Jun 2023 20:37:39 +0200 Subject: Update README.md (#1755) Co-authored-by: Anton Hvornum --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9ec40d17..4bc3104e 100644 --- a/README.md +++ b/README.md @@ -206,7 +206,7 @@ If you want to test a commit, branch or bleeding edge release from the repositor you can replace the version of archinstall with a new version and run that with the steps described below: 1. You need a working network connection -2. Install the build requirements with `pacman -Sy; pacman -S git python-pip` +2. Install the build requirements with `pacman -Sy; pacman -S git python-pip gcc pkgconf` *(note that this may or may not work depending on your RAM and current state of the squashfs maximum filesystem free space)* 3. Uninstall the previous version of archinstall with `pip uninstall archinstall` 4. Now clone the latest repository with `git clone https://github.com/archlinux/archinstall` -- cgit v1.2.3-70-g09d2 From 21735c57ca4ace638b3c8faecc503720008fa82b Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Wed, 5 Jul 2023 14:17:39 +0200 Subject: Added missing python-simple-term-menu (#1919) --- build_iso.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/build_iso.sh b/build_iso.sh index 80bd8bb0..e18b9b6b 100755 --- a/build_iso.sh +++ b/build_iso.sh @@ -12,6 +12,7 @@ packages=( python-build python-setuptools python-wheel + python-simple-term-menu python-pyparted ) -- cgit v1.2.3-70-g09d2 From afaf42e6469206e181f6373c1ded209217539e71 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Mon, 17 Jul 2023 00:14:44 +0200 Subject: Enable separate /boot and /boot/esp via XBOOTLDR in systemd-boot (#1859) * Disabled /boot check for now * Making '/boot' more dynamic * str() on boot_partition didn't work * _pacstrap -> pacman.strap() * Added 'finding' the EFI partition logic * f-string qotations * Locked down so get_boot_partition() looks for /boot and get_efi_partition() looks for /boot/efi - essentially hardcoding it for now, as there's no easy way to distinguish between the EFI partition or BOOT partition if they are both FAT32 for some reason. * Added some debugging output * Fixed some mypy complaints * Fixed PosixPath() vs str comparison * Changed FAT32 comparitor, should be FilesystemType.Fat32 now * Fixed PosixPath() vs str comparison * Re-ordered _add_systemd_bootloader() argument order, to match the other functions. This will cause the function to break on scripts that call this explicitly. * is_boot() now returns True if any type of valid boot flags are set, not just the 'Boot' flag. This allows us to check for XBOOTLDR flag as well. * Converted static INT to _ped.PARTITION_ definition. This matches the way pyparted checks for flags on partitions. * /boot/efi -> /boot/EFI (while the recommendation from bootctl is to mount it to /efi, I want to test it with custom paths first) * Removed _ped from mypy checks * flake8 fix * Added ESP flag to partitions * Added more docs in the docstring * Renamed *efi_partition to *xbootldr_partition within this PR changes * Naming collision, PartitionType -> PartitionGUIDs to avoid overwriting existing PartitionType * Check for XBOOTLDR instead of fixed EFI mountpoint in get_xbootldr_partition() * Mixed up XBOOTLDR and EFI partitions a bit, brought back get_efi_partition() which now filters out XBOOTLDR partitions and only returns a partition when there is a boot partition found by get_boot_partition() * Fixed symbiosis between get_boot() and get_efi() so that they don't report the same potential partition * Removed debugging code * Improved comments surrounding why /loader/ rather than /loader/ - this may change --- archinstall/lib/disk/device_model.py | 45 ++++++++++++++++++++++++++++++++---- archinstall/lib/installer.py | 43 +++++++++++++++++++++++++--------- 2 files changed, 73 insertions(+), 15 deletions(-) diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index 97623772..ad3426b6 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -13,6 +13,7 @@ from typing import Optional, List, Dict, TYPE_CHECKING, Any from typing import Union import parted # type: ignore +import _ped # type: ignore from parted import Disk, Geometry, Partition from ..exceptions import DiskError, SysCallError @@ -525,7 +526,21 @@ class PartitionType(Enum): class PartitionFlag(Enum): - Boot = 1 + """ + Flags are taken from _ped because pyparted uses this to look + up their flag definitions: https://github.com/dcantrell/pyparted/blob/c4e0186dad45c8efbe67c52b02c8c4319df8aa9b/src/parted/__init__.py#L200-L202 + Which is the way libparted checks for its flags: https://git.savannah.gnu.org/gitweb/?p=parted.git;a=blob;f=libparted/labels/gpt.c;hb=4a0e468ed63fff85a1f9b923189f20945b32f4f1#l183 + """ + Boot = _ped.PARTITION_BOOT + XBOOTLDR = _ped.PARTITION_BLS_BOOT # Note: parted calls this bls_boot + ESP = _ped.PARTITION_ESP + + +# class PartitionGUIDs(Enum): +# """ +# A list of Partition type GUIDs (lsblk -o+PARTTYPE) can be found here: https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs +# """ +# XBOOTLDR = 'bc13c2ff-59e6-4262-a352-b275fd6f7172' class FilesystemType(Enum): @@ -605,6 +620,8 @@ class PartitionModification: partuuid: Optional[str] = None uuid: Optional[str] = None + _boot_indicator_flags = [PartitionFlag.Boot, PartitionFlag.XBOOTLDR] + def __post_init__(self): # needed to use the object as a dictionary key due to hash func if not hasattr(self, '_obj_id'): @@ -674,7 +691,10 @@ class PartitionModification: raise ValueError('Mountpoint is not specified') def is_boot(self) -> bool: - return PartitionFlag.Boot in self.flags + """ + Returns True if any of the boot indicator flags are found in self.flags + """ + return any(set(self.flags) & set(self._boot_indicator_flags)) def is_root(self, relative_mountpoint: Optional[Path] = None) -> bool: if relative_mountpoint is not None and self.mountpoint is not None: @@ -765,9 +785,24 @@ class DeviceModification: def add_partition(self, partition: PartitionModification): self.partitions.append(partition) + def get_efi_partition(self) -> Optional[PartitionModification]: + """ + Similar to get_boot_partition() but excludes XBOOTLDR partitions from it's candidates. + """ + fliltered = filter(lambda x: x.is_boot() and x.fs_type == FilesystemType.Fat32 and PartitionFlag.XBOOTLDR not in x.flags, self.partitions) + return next(fliltered, None) + def get_boot_partition(self) -> Optional[PartitionModification]: - liltered = filter(lambda x: x.is_boot(), self.partitions) - return next(liltered, None) + """ + Returns the first partition marked as XBOOTLDR (PARTTYPE id of bc13c2ff-...) or Boot and has a mountpoint. + Only returns XBOOTLDR if separate EFI is detected using self.get_efi_partition() + """ + if efi_partition := self.get_efi_partition(): + fliltered = filter(lambda x: x.is_boot() and x != efi_partition and x.mountpoint, self.partitions) + else: + fliltered = filter(lambda x: x.is_boot() and x.mountpoint, self.partitions) + + return next(fliltered, None) def get_root_partition(self, relative_path: Optional[Path]) -> Optional[PartitionModification]: filtered = filter(lambda x: x.is_root(relative_path), self.partitions) @@ -886,6 +921,7 @@ class LsblkInfo: rota: bool = False tran: Optional[str] = None partuuid: Optional[str] = None + parttype :Optional[str] = None uuid: Optional[str] = None fstype: Optional[str] = None fsver: Optional[str] = None @@ -909,6 +945,7 @@ class LsblkInfo: 'rota': self.rota, 'tran': self.tran, 'partuuid': self.partuuid, + 'parttype' : self.parttype, 'uuid': self.uuid, 'fstype': self.fstype, 'fsver': self.fsver, diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 083bd7c9..9582df77 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -172,7 +172,7 @@ class Installer: ) def sanity_check(self): - self._verify_boot_part() + # self._verify_boot_part() self._verify_service_stop() def mount_ordered_layout(self): @@ -677,6 +677,12 @@ class Installer: else: raise ValueError(f"Archinstall currently only supports setting up swap on zram") + def _get_efi_partition(self) -> Optional[disk.PartitionModification]: + for layout in self._disk_config.device_modifications: + if partition := layout.get_efi_partition(): + return partition + return None + def _get_boot_partition(self) -> Optional[disk.PartitionModification]: for layout in self._disk_config.device_modifications: if boot := layout.get_boot_partition(): @@ -689,7 +695,12 @@ class Installer: return root return None - def _add_systemd_bootloader(self, root_partition: disk.PartitionModification): + def _add_systemd_bootloader( + self, + boot_partition: disk.PartitionModification, + root_partition: disk.PartitionModification, + efi_partition: Optional[disk.PartitionModification] + ): self.pacman.strap('efibootmgr') if not SysInfo.has_uefi(): @@ -698,15 +709,24 @@ class Installer: # TODO: Ideally we would want to check if another config # points towards the same disk and/or partition. # And in which case we should do some clean up. + bootctl_options = [ + f'--esp-path={efi_partition.mountpoint}' if efi_partition else '', + f'--boot-path={boot_partition.mountpoint}' if boot_partition else '' + ] # Install the boot loader try: - SysCommand(f'/usr/bin/arch-chroot {self.target} bootctl --esp-path=/boot install') + SysCommand(f"/usr/bin/arch-chroot {self.target} bootctl {' '.join(bootctl_options)} install") except SysCallError: # Fallback, try creating the boot loader without touching the EFI variables - SysCommand(f'/usr/bin/arch-chroot {self.target} bootctl --no-variables --esp-path=/boot install') + SysCommand(f"/usr/bin/arch-chroot {self.target} bootctl --no-variables {' '.join(bootctl_options)} install") - # Ensure that the /boot/loader directory exists before we try to create files in it + # Ensure that the $BOOT/loader/ directory exists before we try to create files in it. + # + # As mentioned in https://github.com/archlinux/archinstall/pull/1859 - we store the + # loader entries in $BOOT/loader/ rather than $ESP/loader/ + # The current reasoning being that $BOOT works in both use cases as well + # as being tied to the current installation. This may change. loader_dir = self.target / 'boot/loader' loader_dir.mkdir(parents=True, exist_ok=True) @@ -732,7 +752,7 @@ class Installer: else: loader.write(f"{line}\n") - # Ensure that the /boot/loader/entries directory exists before we try to create files in it + # Ensure that the $BOOT/loader/entries/ directory exists before we try to create files in it entries_dir = loader_dir / 'entries' entries_dir.mkdir(parents=True, exist_ok=True) @@ -836,12 +856,12 @@ class Installer: self.pacman.strap('efibootmgr') # TODO: Do we need? Yes, but remove from minimal_installation() instead? try: - SysCommand(f'/usr/bin/arch-chroot {self.target} grub-install --debug --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB --removable', peek_output=True) + SysCommand(f'/usr/bin/arch-chroot {self.target} grub-install --debug --target=x86_64-efi --efi-directory={boot_partition.mountpoint} --bootloader-id=GRUB --removable', peek_output=True) except SysCallError: try: - SysCommand(f'/usr/bin/arch-chroot {self.target} grub-install --debug --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB --removable', peek_output=True) + SysCommand(f'/usr/bin/arch-chroot {self.target} grub-install --debug --target=x86_64-efi --efi-directory={boot_partition.mountpoint} --bootloader-id=GRUB --removable', peek_output=True) except SysCallError as err: - raise DiskError(f"Could not install GRUB to {self.target}/boot: {err}") + raise DiskError(f"Could not install GRUB to {self.target}{boot_partition.mountpoint}: {err}") else: device = disk.device_handler.get_device_by_partition_path(boot_partition.safe_dev_path) @@ -861,7 +881,7 @@ class Installer: raise DiskError(f"Failed to install GRUB boot on {boot_partition.dev_path}: {err}") try: - SysCommand(f'/usr/bin/arch-chroot {self.target} grub-mkconfig -o /boot/grub/grub.cfg') + SysCommand(f'/usr/bin/arch-chroot {self.target} grub-mkconfig -o {boot_partition.mountpoint}/grub/grub.cfg') except SysCallError as err: raise DiskError(f"Could not configure GRUB: {err}") @@ -1055,6 +1075,7 @@ TIMEOUT=5 if plugin.on_add_bootloader(self): return True + efi_partition = self._get_efi_partition() boot_partition = self._get_boot_partition() root_partition = self._get_root_partition() @@ -1068,7 +1089,7 @@ TIMEOUT=5 match bootloader: case Bootloader.Systemd: - self._add_systemd_bootloader(root_partition) + self._add_systemd_bootloader(boot_partition, root_partition, efi_partition) case Bootloader.Grub: self._add_grub_bootloader(boot_partition, root_partition) case Bootloader.Efistub: -- cgit v1.2.3-70-g09d2 From c67bb0b549b35ce335941c9c1cbe22f99c28f7fe Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Sun, 16 Jul 2023 18:17:21 -0400 Subject: Refactor `_add_systemd_bootloader()` loader configuration (#1911) --- archinstall/lib/installer.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 9582df77..02d48768 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -733,24 +733,26 @@ class Installer: # Modify or create a loader.conf loader_conf = loader_dir / 'loader.conf' + default = f'default {self.init_time}_{self.kernels[0]}.conf\n' + try: with loader_conf.open() as loader: - loader_data = loader.read().split('\n') + loader_data = loader.readlines() except FileNotFoundError: loader_data = [ - f"default {self.init_time}", - "timeout 15" + default, + 'timeout 15\n' ] + else: + for index, line in enumerate(loader_data): + if line.startswith('default'): + loader_data[index] = default + elif line.startswith('#timeout'): + # We add in the default timeout to support dual-boot + loader_data[index] = line.removeprefix('#') with loader_conf.open('w') as loader: - for line in loader_data: - if line[:8] == 'default ': - loader.write(f'default {self.init_time}_{self.kernels[0]}\n') - elif line[:8] == '#timeout' and 'timeout 15' not in loader_data: - # We add in the default timeout to support dual-boot - loader.write(f"{line[1:]}\n") - else: - loader.write(f"{line}\n") + loader.writelines(loader_data) # Ensure that the $BOOT/loader/entries/ directory exists before we try to create files in it entries_dir = loader_dir / 'entries' -- cgit v1.2.3-70-g09d2 From 2f273868d416c3309191db8c616aae683d78370a Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Mon, 17 Jul 2023 17:27:21 +1000 Subject: Fix network settings loading from config file (#1921) * Fix network config error and simplify code * Update schema and exmaple --------- Co-authored-by: Daniel Girtler --- archinstall/__init__.py | 7 +- archinstall/lib/global_menu.py | 27 ++-- archinstall/lib/installer.py | 18 +-- archinstall/lib/interactions/__init__.py | 2 +- archinstall/lib/interactions/network_conf.py | 172 -------------------- archinstall/lib/interactions/network_menu.py | 159 +++++++++++++++++++ archinstall/lib/models/__init__.py | 6 +- archinstall/lib/models/network_configuration.py | 200 ++++++++++-------------- archinstall/scripts/guided.py | 11 +- archinstall/scripts/swiss.py | 9 +- docs/installing/guided.rst | 8 +- examples/config-sample.json | 20 ++- examples/custom-command-sample.json | 4 +- examples/interactive_installation.py | 7 +- schema.json | 23 +-- 15 files changed, 313 insertions(+), 360 deletions(-) delete mode 100644 archinstall/lib/interactions/network_conf.py create mode 100644 archinstall/lib/interactions/network_menu.py diff --git a/archinstall/__init__.py b/archinstall/__init__.py index af811465..c4b64912 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -225,10 +225,9 @@ def load_config(): if arguments.get('servers', None) is not None: storage['_selected_servers'] = arguments.get('servers', None) - if arguments.get('nic', None) is not None: - handler = models.NetworkConfigurationHandler() - handler.parse_arguments(arguments.get('nic')) - arguments['nic'] = handler.configuration + if arguments.get('network_config', None) is not None: + config = NetworkConfiguration.parse_arg(arguments.get('network_config')) + arguments['network_config'] = config if arguments.get('!users', None) is not None or arguments.get('!superusers', None) is not None: users = arguments.get('!users', None) diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 02b1b0b6..5503d9ce 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -7,7 +7,7 @@ from .general import secret from .locale.locale_menu import LocaleConfiguration, LocaleMenu from .menu import Selector, AbstractMenu from .mirrors import MirrorConfiguration, MirrorMenu -from .models import NetworkConfiguration +from .models import NetworkConfiguration, NicType from .models.bootloader import Bootloader from .models.users import User from .output import FormattedOutput @@ -142,7 +142,7 @@ class GlobalMenu(AbstractMenu): lambda preset: select_additional_repositories(preset), display_func=lambda x: ', '.join(x) if x else None, default=[]) - self._menu_options['nic'] = \ + self._menu_options['network_config'] = \ Selector( _('Network configuration'), lambda preset: ask_to_configure_network(preset), @@ -221,14 +221,11 @@ class GlobalMenu(AbstractMenu): return _('Install ({} config(s) missing)').format(missing) return _('Install') - def _display_network_conf(self, cur_value: Union[NetworkConfiguration, List[NetworkConfiguration]]) -> str: - if not cur_value: - return _('Not configured, unavailable unless setup manually') - else: - if isinstance(cur_value, list): - return str(_('Configured {} interfaces')).format(len(cur_value)) - else: - return str(cur_value) + def _display_network_conf(self, config: Optional[NetworkConfiguration]) -> str: + if not config: + return str(_('Not configured, unavailable unless setup manually')) + + return config.type.display_msg() def _disk_encryption(self, preset: Optional[disk.DiskEncryption]) -> Optional[disk.DiskEncryption]: mods: Optional[List[disk.DeviceModification]] = self._menu_options['disk_config'].current_selection @@ -257,11 +254,11 @@ class GlobalMenu(AbstractMenu): return None def _prev_network_config(self) -> Optional[str]: - selector = self._menu_options['nic'] - if selector.has_selection(): - ifaces = selector.current_selection - if isinstance(ifaces, list): - return FormattedOutput.as_table(ifaces) + selector: Optional[NetworkConfiguration] = self._menu_options['network_config'].current_selection + if selector: + if selector.type == NicType.MANUAL: + output = FormattedOutput.as_table(selector.nics) + return output return None def _prev_additional_pkgs(self): diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 02d48768..8c5e7648 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -19,7 +19,7 @@ from .locale import verify_keyboard_layout, verify_x11_keyboard_layout from .luks import Luks2 from .mirrors import use_mirrors, MirrorConfiguration, add_custom_mirrors from .models.bootloader import Bootloader -from .models.network_configuration import NetworkConfiguration +from .models.network_configuration import Nic from .models.users import User from .output import log, error, info, warn, debug from . import pacman @@ -458,20 +458,20 @@ class Installer: def drop_to_shell(self) -> None: subprocess.check_call(f"/usr/bin/arch-chroot {self.target}", shell=True) - def configure_nic(self, network_config: NetworkConfiguration) -> None: - conf = network_config.as_systemd_config() + def configure_nic(self, nic: Nic): + conf = nic.as_systemd_config() for plugin in plugins.values(): if hasattr(plugin, 'on_configure_nic'): conf = plugin.on_configure_nic( - network_config.iface, - network_config.dhcp, - network_config.ip, - network_config.gateway, - network_config.dns + nic.iface, + nic.dhcp, + nic.ip, + nic.gateway, + nic.dns ) or conf - with open(f"{self.target}/etc/systemd/network/10-{network_config.iface}.network", "a") as netconf: + with open(f"{self.target}/etc/systemd/network/10-{nic.iface}.network", "a") as netconf: netconf.write(str(conf)) def copy_iso_network_config(self, enable_services :bool = False) -> bool: diff --git a/archinstall/lib/interactions/__init__.py b/archinstall/lib/interactions/__init__.py index 466cfa0b..53be8e7a 100644 --- a/archinstall/lib/interactions/__init__.py +++ b/archinstall/lib/interactions/__init__.py @@ -1,5 +1,5 @@ from .manage_users_conf import UserList, ask_for_additional_users -from .network_conf import ManualNetworkConfig, ask_to_configure_network +from .network_menu import ManualNetworkConfig, ask_to_configure_network from .utils import get_password from .disk_conf import ( diff --git a/archinstall/lib/interactions/network_conf.py b/archinstall/lib/interactions/network_conf.py deleted file mode 100644 index 18a834a1..00000000 --- a/archinstall/lib/interactions/network_conf.py +++ /dev/null @@ -1,172 +0,0 @@ -from __future__ import annotations - -import ipaddress -from typing import Any, Optional, TYPE_CHECKING, List, Union, Dict - -from ..menu import MenuSelectionType, TextInput -from ..models.network_configuration import NetworkConfiguration, NicType - -from ..networking import list_interfaces -from ..output import FormattedOutput, warn -from ..menu import ListManager, Menu - -if TYPE_CHECKING: - _: Any - - -class ManualNetworkConfig(ListManager): - """ - subclass of ListManager for the managing of network configurations - """ - - def __init__(self, prompt: str, ifaces: List[NetworkConfiguration]): - self._actions = [ - str(_('Add interface')), - str(_('Edit interface')), - str(_('Delete interface')) - ] - - super().__init__(prompt, ifaces, [self._actions[0]], self._actions[1:]) - - def reformat(self, data: List[NetworkConfiguration]) -> Dict[str, Optional[NetworkConfiguration]]: - table = FormattedOutput.as_table(data) - rows = table.split('\n') - - # these are the header rows of the table and do not map to any User obviously - # we're adding 2 spaces as prefix because the menu selector '> ' will be put before - # the selectable rows so the header has to be aligned - display_data: Dict[str, Optional[NetworkConfiguration]] = {f' {rows[0]}': None, f' {rows[1]}': None} - - for row, iface in zip(rows[2:], data): - row = row.replace('|', '\\|') - display_data[row] = iface - - return display_data - - def selected_action_display(self, iface: NetworkConfiguration) -> str: - return iface.iface if iface.iface else '' - - def handle_action(self, action: str, entry: Optional[NetworkConfiguration], data: List[NetworkConfiguration]): - if action == self._actions[0]: # add - iface_name = self._select_iface(data) - if iface_name: - iface = NetworkConfiguration(NicType.MANUAL, iface=iface_name) - iface = self._edit_iface(iface) - data += [iface] - elif entry: - if action == self._actions[1]: # edit interface - data = [d for d in data if d.iface != entry.iface] - data.append(self._edit_iface(entry)) - elif action == self._actions[2]: # delete - data = [d for d in data if d != entry] - - return data - - def _select_iface(self, data: List[NetworkConfiguration]) -> Optional[Any]: - all_ifaces = list_interfaces().values() - existing_ifaces = [d.iface for d in data] - available = set(all_ifaces) - set(existing_ifaces) - choice = Menu(str(_('Select interface to add')), list(available), skip=True).run() - - if choice.type_ == MenuSelectionType.Skip: - return None - - return choice.value - - def _edit_iface(self, edit_iface: NetworkConfiguration): - iface_name = edit_iface.iface - modes = ['DHCP (auto detect)', 'IP (static)'] - 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, skip=False).run() - - 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() - # Implemented new check for correct IP/subnet input - try: - ipaddress.ip_interface(ip) - break - except ValueError: - warn("You need to enter a valid IP in IP-config mode") - - # Implemented new check for correct gateway IP address - gateway = None - - while 1: - gateway = TextInput( - _('Enter your gateway (router) IP address or leave blank for none: '), - edit_iface.gateway - ).run().strip() - try: - if len(gateway) > 0: - ipaddress.ip_address(gateway) - break - except ValueError: - warn("You need to enter a valid gateway (router) IP address") - - if edit_iface.dns: - display_dns = ' '.join(edit_iface.dns) - else: - 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(' ') - - return NetworkConfiguration(NicType.MANUAL, iface=iface_name, ip=ip, gateway=gateway, dns=dns, dhcp=False) - else: - # this will contain network iface names - return NetworkConfiguration(NicType.MANUAL, iface=iface_name) - - -def ask_to_configure_network( - preset: Union[NetworkConfiguration, List[NetworkConfiguration]] -) -> Optional[NetworkConfiguration | List[NetworkConfiguration]]: - """ - Configure the network on the newly installed system - """ - network_options = { - 'none': str(_('No network configuration')), - 'iso_config': str(_('Copy ISO network configuration to installation')), - 'network_manager': str(_('Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)')), - 'manual': str(_('Manual configuration')) - } - # for this routine it's easier to set the cursor position rather than a preset value - cursor_idx = None - - if preset and not isinstance(preset, list): - if preset.type == 'iso_config': - cursor_idx = 0 - elif preset.type == 'network_manager': - cursor_idx = 1 - - 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, - allow_reset=True, - allow_reset_warning_msg=warning - ).run() - - match choice.type_: - case MenuSelectionType.Skip: return preset - case MenuSelectionType.Reset: return None - - if choice.value == network_options['none']: - return None - elif choice.value == network_options['iso_config']: - return NetworkConfiguration(NicType.ISO) - elif choice.value == network_options['network_manager']: - return NetworkConfiguration(NicType.NM) - elif choice.value == network_options['manual']: - preset_ifaces = preset if isinstance(preset, list) else [] - return ManualNetworkConfig('Configure interfaces', preset_ifaces).run() - - return preset diff --git a/archinstall/lib/interactions/network_menu.py b/archinstall/lib/interactions/network_menu.py new file mode 100644 index 00000000..14fc5785 --- /dev/null +++ b/archinstall/lib/interactions/network_menu.py @@ -0,0 +1,159 @@ +from __future__ import annotations + +import ipaddress +from typing import Any, Optional, TYPE_CHECKING, List, Dict + +from ..menu import MenuSelectionType, TextInput +from ..models.network_configuration import NetworkConfiguration, NicType, Nic + +from ..networking import list_interfaces +from ..output import FormattedOutput, warn +from ..menu import ListManager, Menu + +if TYPE_CHECKING: + _: Any + + +class ManualNetworkConfig(ListManager): + """ + subclass of ListManager for the managing of network configurations + """ + + def __init__(self, prompt: str, preset: List[Nic]): + self._actions = [ + str(_('Add interface')), + str(_('Edit interface')), + str(_('Delete interface')) + ] + super().__init__(prompt, preset, [self._actions[0]], self._actions[1:]) + + def reformat(self, data: List[Nic]) -> Dict[str, Optional[Nic]]: + table = FormattedOutput.as_table(data) + rows = table.split('\n') + + # these are the header rows of the table and do not map to any User obviously + # we're adding 2 spaces as prefix because the menu selector '> ' will be put before + # the selectable rows so the header has to be aligned + display_data: Dict[str, Optional[Nic]] = {f' {rows[0]}': None, f' {rows[1]}': None} + + for row, iface in zip(rows[2:], data): + row = row.replace('|', '\\|') + display_data[row] = iface + + return display_data + + def selected_action_display(self, nic: Nic) -> str: + return nic.iface if nic.iface else '' + + def handle_action(self, action: str, entry: Optional[Nic], data: List[Nic]): + if action == self._actions[0]: # add + iface = self._select_iface(data) + if iface: + nic = Nic(iface=iface) + nic = self._edit_iface(nic) + data += [nic] + elif entry: + if action == self._actions[1]: # edit interface + data = [d for d in data if d.iface != entry.iface] + data.append(self._edit_iface(entry)) + elif action == self._actions[2]: # delete + data = [d for d in data if d != entry] + + return data + + def _select_iface(self, data: List[Nic]) -> Optional[str]: + all_ifaces = list_interfaces().values() + existing_ifaces = [d.iface for d in data] + available = set(all_ifaces) - set(existing_ifaces) + choice = Menu(str(_('Select interface to add')), list(available), skip=True).run() + + if choice.type_ == MenuSelectionType.Skip: + return None + + return choice.single_value + + def _edit_iface(self, edit_nic: Nic) -> Nic: + iface_name = edit_nic.iface + modes = ['DHCP (auto detect)', 'IP (static)'] + 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, skip=False).run() + + 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_nic.ip).run().strip() + # Implemented new check for correct IP/subnet input + try: + ipaddress.ip_interface(ip) + break + except ValueError: + warn("You need to enter a valid IP in IP-config mode") + + # Implemented new check for correct gateway IP address + gateway = None + + while 1: + gateway = TextInput( + _('Enter your gateway (router) IP address or leave blank for none: '), + edit_nic.gateway + ).run().strip() + try: + if len(gateway) > 0: + ipaddress.ip_address(gateway) + break + except ValueError: + warn("You need to enter a valid gateway (router) IP address") + + if edit_nic.dns: + display_dns = ' '.join(edit_nic.dns) + else: + 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(' ') + + return Nic(iface=iface_name, ip=ip, gateway=gateway, dns=dns, dhcp=False) + else: + # this will contain network iface names + return Nic(iface=iface_name) + + +def ask_to_configure_network(preset: Optional[NetworkConfiguration]) -> Optional[NetworkConfiguration]: + """ + Configure the network on the newly installed system + """ + options = {n.display_msg(): n for n in NicType} + preset_val = preset.type.display_msg() if preset else None + warning = str(_('Are you sure you want to reset this setting?')) + + choice = Menu( + _('Select one network interface to configure'), + list(options.keys()), + preset_values=preset_val, + sort=False, + allow_reset=True, + allow_reset_warning_msg=warning + ).run() + + match choice.type_: + case MenuSelectionType.Skip: return preset + case MenuSelectionType.Reset: return None + case MenuSelectionType.Selection: + nic_type = options[choice.single_value] + + match nic_type: + case NicType.ISO: + return NetworkConfiguration(NicType.ISO) + case NicType.NM: + return NetworkConfiguration(NicType.NM) + case NicType.MANUAL: + preset_nics = preset.nics if preset else [] + nics = ManualNetworkConfig('Configure interfaces', preset_nics).run() + if nics: + return NetworkConfiguration(NicType.MANUAL, nics) + + return preset diff --git a/archinstall/lib/models/__init__.py b/archinstall/lib/models/__init__.py index 8cc49ea0..7415f63f 100644 --- a/archinstall/lib/models/__init__.py +++ b/archinstall/lib/models/__init__.py @@ -1,4 +1,8 @@ -from .network_configuration import NetworkConfiguration, NicType, NetworkConfigurationHandler +from .network_configuration import ( + NetworkConfiguration, + NicType, + Nic +) from .bootloader import Bootloader from .gen import VersionDef, PackageSearchResult, PackageSearch, LocalPackage from .users import PasswordStrength, User diff --git a/archinstall/lib/models/network_configuration.py b/archinstall/lib/models/network_configuration.py index e564b97b..fac7bbef 100644 --- a/archinstall/lib/models/network_configuration.py +++ b/archinstall/lib/models/network_configuration.py @@ -2,56 +2,64 @@ from __future__ import annotations from dataclasses import dataclass, field from enum import Enum -from typing import List, Optional, Dict, Union, Any, TYPE_CHECKING, Tuple +from typing import List, Optional, Dict, Any, TYPE_CHECKING, Tuple -from ..output import debug from ..profile import ProfileConfiguration if TYPE_CHECKING: _: Any -class NicType(str, Enum): +class NicType(Enum): ISO = "iso" NM = "nm" MANUAL = "manual" + def display_msg(self) -> str: + match self: + case NicType.ISO: + return str(_('Copy ISO network configuration to installation')) + case NicType.NM: + return str(_('Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)')) + case NicType.MANUAL: + return str(_('Manual configuration')) + @dataclass -class NetworkConfiguration: - type: NicType +class Nic: iface: Optional[str] = None ip: Optional[str] = None dhcp: bool = True gateway: Optional[str] = None dns: List[str] = field(default_factory=list) - def __str__(self): - if self.is_iso(): - return "Copy ISO configuration" - elif self.is_network_manager(): - return "Use NetworkManager" - elif self.is_manual(): - if self.dhcp: - return f'iface={self.iface}, dhcp=auto' - else: - return f'iface={self.iface}, ip={self.ip}, dhcp=staticIp, gateway={self.gateway}, dns={self.dns}' - else: - return 'Unknown type' - def table_data(self) -> Dict[str, Any]: - exclude_fields = ['type'] - data = {} - for k, v in self.__dict__.items(): - if k not in exclude_fields: - if isinstance(v, list) and len(v) == 0: - v = '' - elif v is None: - v = '' - - data[k] = v - - return data + return { + 'iface': self.iface if self.iface else '', + 'ip': self.ip if self.ip else '', + 'dhcp': self.dhcp, + 'gateway': self.gateway if self.gateway else '', + 'dns': self.dns + } + + def __dump__(self) -> Dict[str, Any]: + return { + 'iface': self.iface, + 'ip': self.ip, + 'dhcp': self.dhcp, + 'gateway': self.gateway, + 'dns': self.dns + } + + @staticmethod + def parse_arg(arg: Dict[str, Any]) -> Nic: + return Nic( + iface=arg.get('iface', None), + ip=arg.get('ip', None), + dhcp=arg.get('dhcp', True), + gateway=arg.get('gateway', None), + dns=arg.get('dns', []), + ) def as_systemd_config(self) -> str: match: List[Tuple[str, str]] = [] @@ -80,107 +88,57 @@ class NetworkConfiguration: return config_str - def json(self) -> Dict: - # for json serialization when calling json.dumps(...) on this class - return self.__dict__ - - def is_iso(self) -> bool: - return self.type == NicType.ISO - - def is_network_manager(self) -> bool: - return self.type == NicType.NM - - def is_manual(self) -> bool: - return self.type == NicType.MANUAL - - -class NetworkConfigurationHandler: - def __init__(self, config: Union[None, NetworkConfiguration, List[NetworkConfiguration]] = None): - self._configuration = config - - @property - def configuration(self): - return self._configuration - def config_installer( +@dataclass +class NetworkConfiguration: + type: NicType + nics: List[Nic] = field(default_factory=list) + + def __dump__(self) -> Dict[str, Any]: + config: Dict[str, Any] = {'type': self.type.value} + if self.nics: + config['nics'] = [n.__dump__() for n in self.nics] + + return config + + @staticmethod + def parse_arg(config: Dict[str, Any]) -> Optional[NetworkConfiguration]: + nic_type = config.get('type', None) + if not nic_type: + return None + + match NicType(nic_type): + case NicType.ISO: + return NetworkConfiguration(NicType.ISO) + case NicType.NM: + return NetworkConfiguration(NicType.NM) + case NicType.MANUAL: + nics_arg = config.get('nics', []) + if nics_arg: + nics = [Nic.parse_arg(n) for n in nics_arg] + return NetworkConfiguration(NicType.MANUAL, nics) + + return None + + def install_network_config( self, installation: Any, profile_config: Optional[ProfileConfiguration] = None ): - if self._configuration is None: - return - - if isinstance(self._configuration, list): - for config in self._configuration: - installation.configure_nic(config) - - installation.enable_service('systemd-networkd') - installation.enable_service('systemd-resolved') - else: - # If user selected to copy the current ISO network configuration - # Perform a copy of the config - if self._configuration.is_iso(): + match self.type: + case NicType.ISO: installation.copy_iso_network_config( - enable_services=True # Sources the ISO network configuration to the install medium. + enable_services=True # Sources the ISO network configuration to the install medium. ) - elif self._configuration.is_network_manager(): + case NicType.NM: installation.add_additional_packages(["networkmanager"]) if profile_config and profile_config.profile: if profile_config.profile.is_desktop_type_profile(): installation.add_additional_packages(["network-manager-applet"]) installation.enable_service('NetworkManager.service') + case NicType.MANUAL: + for nic in self.nics: + installation.configure_nic(nic) - def _parse_manual_config(self, configs: List[Dict[str, Any]]) -> Optional[List[NetworkConfiguration]]: - configurations = [] - - for manual_config in configs: - iface = manual_config.get('iface', None) - - if iface is None: - raise ValueError('No iface specified for manual configuration') - - if manual_config.get('dhcp', False) or not any([manual_config.get(v, '') for v in ['ip', 'gateway', 'dns']]): - configurations.append( - NetworkConfiguration(NicType.MANUAL, iface=iface) - ) - else: - ip = manual_config.get('ip', '') - if not ip: - raise ValueError('Manual nic configuration with no auto DHCP requires an IP address') - - dns = manual_config.get('dns', []) - if not isinstance(dns, list): - dns = [dns] - - configurations.append( - NetworkConfiguration( - NicType.MANUAL, - iface=iface, - ip=ip, - gateway=manual_config.get('gateway', ''), - dns=dns, - dhcp=False - ) - ) - - return configurations - - def _parse_nic_type(self, nic_type: str) -> NicType: - try: - return NicType(nic_type) - except ValueError: - options = [e.value for e in NicType] - raise ValueError(f'Unknown nic type: {nic_type}. Possible values are {options}') - - def parse_arguments(self, config: Any): - if isinstance(config, list): # new data format - self._configuration = self._parse_manual_config(config) - elif nic_type := config.get('type', None): # new data format - type_ = self._parse_nic_type(nic_type) - - if type_ != NicType.MANUAL: - self._configuration = NetworkConfiguration(type_) - else: # manual configuration settings - self._configuration = self._parse_manual_config([config]) - else: - debug(f'Unable to parse network configuration: {config}') + installation.enable_service('systemd-networkd') + installation.enable_service('systemd-resolved') diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 7f9b9fd6..c8df590d 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -1,6 +1,6 @@ import os from pathlib import Path -from typing import Any, TYPE_CHECKING +from typing import Any, TYPE_CHECKING, Optional import archinstall from archinstall import info, debug @@ -14,7 +14,7 @@ from archinstall.lib.installer import Installer from archinstall.lib.menu import Menu from archinstall.lib.mirrors import use_mirrors, add_custom_mirrors from archinstall.lib.models.bootloader import Bootloader -from archinstall.lib.models.network_configuration import NetworkConfigurationHandler +from archinstall.lib.models.network_configuration import NetworkConfiguration from archinstall.lib.networking import check_mirror_reachable from archinstall.lib.profile.profiles_handler import profile_handler @@ -82,7 +82,7 @@ def ask_user_questions(): global_menu.enable('parallel downloads') # Ask or Call the helper function that asks the user to optionally configure a network. - global_menu.enable('nic') + global_menu.enable('network_config') global_menu.enable('timezone') @@ -158,11 +158,10 @@ def perform_installation(mountpoint: Path): # If user selected to copy the current ISO network configuration # Perform a copy of the config - network_config = archinstall.arguments.get('nic', None) + network_config: Optional[NetworkConfiguration] = archinstall.arguments.get('network_config', None) if network_config: - handler = NetworkConfigurationHandler(network_config) - handler.config_installer( + network_config.install_network_config( installation, archinstall.arguments.get('profile_config', None) ) diff --git a/archinstall/scripts/swiss.py b/archinstall/scripts/swiss.py index 375458a1..a2ab0549 100644 --- a/archinstall/scripts/swiss.py +++ b/archinstall/scripts/swiss.py @@ -95,7 +95,7 @@ class SwissMainMenu(GlobalMenu): options_list = [ 'mirror_config', 'disk_config', 'disk_encryption', 'swap', 'bootloader', 'hostname', '!root-password', - '!users', 'profile_config', 'audio', 'kernels', 'packages', 'additional-repositories', 'nic', + '!users', 'profile_config', 'audio', 'kernels', 'packages', 'additional-repositories', 'network_config', 'timezone', 'ntp' ] @@ -110,7 +110,7 @@ class SwissMainMenu(GlobalMenu): options_list = [ 'mirror_config','bootloader', 'hostname', '!root-password', '!users', 'profile_config', 'audio', 'kernels', - 'packages', 'additional-repositories', 'nic', 'timezone', 'ntp' + 'packages', 'additional-repositories', 'network_config', 'timezone', 'ntp' ] mandatory_list = ['hostname'] @@ -222,11 +222,10 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): # If user selected to copy the current ISO network configuration # Perform a copy of the config - network_config = archinstall.arguments.get('nic', None) + network_config = archinstall.arguments.get('network_config', None) if network_config: - handler = models.NetworkConfigurationHandler(network_config) - handler.config_installer( + network_config.install_network_config( installation, archinstall.arguments.get('profile_config', None) ) diff --git a/docs/installing/guided.rst b/docs/installing/guided.rst index 4cb07ae1..0a075282 100644 --- a/docs/installing/guided.rst +++ b/docs/installing/guided.rst @@ -29,7 +29,7 @@ To start the installer, run the following in the latest Arch Linux ISO: .. code-block:: sh archinstall --script guided - + | The ``--script guided`` argument is optional as it's the default behavior. | But this will use our most guided installation and if you skip all the option steps it will install a minimal Arch Linux experience. @@ -49,7 +49,7 @@ There are three different configuration files, all of which are optional. .. note:: You can always get the latest options with ``archinstall --dry-run``, but edit the following json according to your needs. Save the configuration as a ``.json`` file. Archinstall can source it via a local or remote path (URL) - + .. code-block:: json { @@ -72,8 +72,8 @@ There are three different configuration files, all of which are optional. ], "keyboard-language": "us", "mirror-region": "Worldwide", - "nic": { - "type": "NM" + "network_config": { + "type": "nm" }, "ntp": true, "packages": ["docker", "git", "wget", "zsh"], diff --git a/examples/config-sample.json b/examples/config-sample.json index a7c5d537..38415b2c 100644 --- a/examples/config-sample.json +++ b/examples/config-sample.json @@ -99,13 +99,19 @@ "http://archlinux.mirror.digitalpacific.com.au/$repo/os/$arch": true, } }, - "nic": { - "dhcp": true, - "dns": null, - "gateway": null, - "iface": null, - "ip": null, - "type": "nm" + "network_config": { + "nics": [ + { + "dhcp": false, + "dns": [ + "3.3.3.3" + ], + "gateway": "2.2.2.2", + "iface": "enp0s31f6", + "ip": "1.1.1.1" + } + ], + "type": "manual" }, "no_pkg_lookups": false, "ntp": true, diff --git a/examples/custom-command-sample.json b/examples/custom-command-sample.json index 8d8d611d..b2250e2c 100644 --- a/examples/custom-command-sample.json +++ b/examples/custom-command-sample.json @@ -12,8 +12,8 @@ ], "keyboard-layout": "us", "mirror-region": "Worldwide", - "nic": { - "type": "NM" + "network_config": { + "type": "nm" }, "ntp": true, "packages": ["docker", "git", "wget", "zsh"], diff --git a/examples/interactive_installation.py b/examples/interactive_installation.py index ce1a80ec..8e82ca7e 100644 --- a/examples/interactive_installation.py +++ b/examples/interactive_installation.py @@ -61,7 +61,7 @@ def ask_user_questions(): global_menu.enable('parallel downloads') # Ask or Call the helper function that asks the user to optionally configure a network. - global_menu.enable('nic') + global_menu.enable('network_config') global_menu.enable('timezone') @@ -137,11 +137,10 @@ def perform_installation(mountpoint: Path): # If user selected to copy the current ISO network configuration # Perform a copy of the config - network_config = archinstall.arguments.get('nic', None) + network_config = archinstall.arguments.get('network_config', None) if network_config: - handler = models.NetworkConfigurationHandler(network_config) - handler.config_installer( + network_config.install_network_config( installation, archinstall.arguments.get('profile_config', None) ) diff --git a/schema.json b/schema.json index 0a41ebf0..b74588a1 100644 --- a/schema.json +++ b/schema.json @@ -69,21 +69,26 @@ "description": "By default, it will autodetect the best region. Enter a region or a dictionary of regions and mirrors to use specific ones", "type": "object" }, - "nic": { + "network_config": { "description": "Choose between NetworkManager, manual configuration, use systemd-networkd from the ISO or no configuration", "type": "object", "properties": { "type": "string", - "iface": "string", - "dhcp": "boolean", - "ip": "string", - "gateway": "string", - "dns": { - "description": "List of DNS servers", + "nics": [ "type": "array", "items": { - "type": "string" - } + "iface": "string", + "dhcp": "boolean", + "ip": "string", + "gateway": "string", + "dns": { + "description": "List of DNS servers", + "type": "array", + "items": { + "type": "string" + } + } + ] } } }, -- cgit v1.2.3-70-g09d2 From 10fca1611d09965c23c5d917d2c41ddc838290b4 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Mon, 17 Jul 2023 09:34:10 +0200 Subject: Added some debug output to see why the locales check errors out on no bash script (#1931) --- .github/workflows/translation-check.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/translation-check.yaml b/.github/workflows/translation-check.yaml index 1d9a5de6..a646d582 100644 --- a/.github/workflows/translation-check.yaml +++ b/.github/workflows/translation-check.yaml @@ -13,9 +13,10 @@ jobs: image: archlinux:latest steps: - uses: actions/checkout@v3 - - run: pacman --noconfirm -Syu python python-pip - - run: pip install --break-system-packages --upgrade pip + - run: pacman --noconfirm -Syu python - run: cd archinstall/locales + - run: pwd + - run: ls -l - run: bash locales_generator.sh - run: git diff --name-only - run: ls -- cgit v1.2.3-70-g09d2 From e41900a25cfbf35e550863a8415a0c7a29c9e9b2 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Mon, 17 Jul 2023 10:13:22 +0200 Subject: removed str() around objects to be serialized. As None should stay intact rather than become 'None' (#1932) --- archinstall/lib/general.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py index c85208ec..473f85a4 100644 --- a/archinstall/lib/general.py +++ b/archinstall/lib/general.py @@ -79,7 +79,8 @@ def jsonify(obj: Any, safe: bool = True) -> Any: return str(obj) if hasattr(obj, "__dict__"): return vars(obj) - return str(obj) + + return obj class JSON(json.JSONEncoder, json.JSONDecoder): """ -- cgit v1.2.3-70-g09d2 From 69c37e7c79b8756e300438fd2d25f6d40db5e04b Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Wed, 19 Jul 2023 02:49:40 -0400 Subject: Uncomment `/etc/locale.gen` entry and use first column for `LANG` variable (#1939) --- archinstall/lib/installer.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 8c5e7648..836b6b79 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -366,7 +366,7 @@ class Installer: with open(f'{self.target}/etc/hostname', 'w') as fh: fh.write(hostname + '\n') - def set_locale(self, locale_config: LocaleConfiguration): + def set_locale(self, locale_config: LocaleConfiguration) -> bool: modifier = '' lang = locale_config.sys_lang encoding = locale_config.sys_enc @@ -386,15 +386,32 @@ class Installer: modifier = f"@{modifier}" # - End patch - with open(f'{self.target}/etc/locale.gen', 'a') as fh: - fh.write(f'{lang}.{encoding}{modifier} {encoding}\n') + locale_gen = self.target / 'etc/locale.gen' + locale_gen_lines = locale_gen.read_text().splitlines(True) - (self.target / "etc" / "locale.conf").write_text(f'LANG={lang}.{encoding}{modifier}\n') + # A locale entry in /etc/locale.gen may or may not contain the encoding + # in the first column of the entry; check for both cases. + entry_re = re.compile(rf'#{lang}(\.{encoding})?{modifier} {encoding}') + + for index, line in enumerate(locale_gen_lines): + if entry_re.match(line): + uncommented_line = line.removeprefix('#') + locale_gen_lines[index] = uncommented_line + locale_gen.write_text(''.join(locale_gen_lines)) + lang_value = uncommented_line.split()[0] + break + else: + error(f"Invalid locale: language '{locale_config.sys_lang}', encoding '{locale_config.sys_enc}'") + return False try: SysCommand(f'/usr/bin/arch-chroot {self.target} locale-gen') except SysCallError as e: error(f'Failed to run locale-gen on target: {e}') + return False + + (self.target / 'etc/locale.conf').write_text(f'LANG={lang_value}\n') + return True def set_timezone(self, zone :str, *args :str, **kwargs :str) -> bool: if not zone: -- cgit v1.2.3-70-g09d2 From d76f4a029604dffe740ef1d44fa5f34ec0b23480 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Tue, 25 Jul 2023 05:16:02 -0400 Subject: Fix boot partition regression (#1942) * Fix boot partition regression * Fix spelling --- archinstall/lib/disk/device_model.py | 14 ++++++++------ archinstall/lib/installer.py | 9 +++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index ad3426b6..b1d07d98 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -789,8 +789,8 @@ class DeviceModification: """ Similar to get_boot_partition() but excludes XBOOTLDR partitions from it's candidates. """ - fliltered = filter(lambda x: x.is_boot() and x.fs_type == FilesystemType.Fat32 and PartitionFlag.XBOOTLDR not in x.flags, self.partitions) - return next(fliltered, None) + filtered = filter(lambda x: x.is_boot() and x.fs_type == FilesystemType.Fat32 and PartitionFlag.XBOOTLDR not in x.flags, self.partitions) + return next(filtered, None) def get_boot_partition(self) -> Optional[PartitionModification]: """ @@ -798,11 +798,13 @@ class DeviceModification: Only returns XBOOTLDR if separate EFI is detected using self.get_efi_partition() """ if efi_partition := self.get_efi_partition(): - fliltered = filter(lambda x: x.is_boot() and x != efi_partition and x.mountpoint, self.partitions) + filtered = filter(lambda x: x.is_boot() and x != efi_partition and x.mountpoint, self.partitions) + if boot_partition := next(filtered, None): + return boot_partition + return efi_partition else: - fliltered = filter(lambda x: x.is_boot() and x.mountpoint, self.partitions) - - return next(fliltered, None) + filtered = filter(lambda x: x.is_boot() and x.mountpoint, self.partitions) + return next(filtered, None) def get_root_partition(self, relative_path: Optional[Path]) -> Optional[PartitionModification]: filtered = filter(lambda x: x.is_root(relative_path), self.partitions) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 836b6b79..f5999002 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -726,10 +726,11 @@ class Installer: # TODO: Ideally we would want to check if another config # points towards the same disk and/or partition. # And in which case we should do some clean up. - bootctl_options = [ - f'--esp-path={efi_partition.mountpoint}' if efi_partition else '', - f'--boot-path={boot_partition.mountpoint}' if boot_partition else '' - ] + bootctl_options = [] + + if efi_partition and boot_partition != efi_partition: + bootctl_options.append(f'--esp-path={efi_partition.mountpoint}') + bootctl_options.append(f'--boot-path={boot_partition.mountpoint}') # Install the boot loader try: -- cgit v1.2.3-70-g09d2 From 439bb5428bb6a6f512f695a83ee6b3b8f6537598 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Tue, 25 Jul 2023 19:17:09 +1000 Subject: Fix 1934 - audio server regression (#1946) * Audio configuration * Update * Update schema --------- Co-authored-by: Daniel Girtler --- archinstall/__init__.py | 3 ++ archinstall/lib/global_menu.py | 27 ++++++++------ archinstall/lib/interactions/general_conf.py | 30 +++++++++++---- archinstall/lib/models/__init__.py | 1 + archinstall/lib/models/audio_configuration.py | 54 +++++++++++++++++++++++++++ archinstall/scripts/guided.py | 19 +++------- archinstall/scripts/swiss.py | 19 ++++------ docs/installing/guided.rst | 2 +- examples/config-sample.json | 22 ++++------- examples/custom-command-sample.json | 1 - examples/interactive_installation.py | 22 +++-------- schema.json | 19 ++++++---- 12 files changed, 137 insertions(+), 82 deletions(-) create mode 100644 archinstall/lib/models/audio_configuration.py diff --git a/archinstall/__init__.py b/archinstall/__init__.py index c4b64912..cfaecd16 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -237,6 +237,9 @@ def load_config(): if arguments.get('bootloader', None) is not None: arguments['bootloader'] = models.Bootloader.from_arg(arguments['bootloader']) + if arguments.get('audio_config', None) is not None: + arguments['audio_config'] = models.AudioConfiguration.parse_arg(arguments['audio_config']) + if arguments.get('disk_encryption', None) is not None and disk_config is not None: password = arguments.get('encryption_password', '') arguments['disk_encryption'] = disk.DiskEncryption.parse_arg( diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 5503d9ce..fb62b7b5 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, List, Optional, Union, Dict, TYPE_CHECKING +from typing import Any, List, Optional, Dict, TYPE_CHECKING from . import disk from .general import secret @@ -9,6 +9,7 @@ from .menu import Selector, AbstractMenu from .mirrors import MirrorConfiguration, MirrorMenu from .models import NetworkConfiguration, NicType from .models.bootloader import Bootloader +from .models.audio_configuration import Audio, AudioConfiguration from .models.users import User from .output import FormattedOutput from .profile.profile_menu import ProfileConfiguration @@ -109,12 +110,11 @@ class GlobalMenu(AbstractMenu): display_func=lambda x: x.profile.name if x else '', preview_func=self._prev_profile ) - self._menu_options['audio'] = \ + self._menu_options['audio_config'] = \ Selector( _('Audio'), lambda preset: self._select_audio(preset), - display_func=lambda x: x if x else '', - default=None + display_func=lambda x: self._display_audio(x) ) self._menu_options['parallel downloads'] = \ Selector( @@ -421,13 +421,18 @@ class GlobalMenu(AbstractMenu): profile_config = ProfileMenu(store, preset=current_profile).run() return profile_config - def _select_audio(self, current: Union[str, None]) -> Optional[str]: - profile_config: Optional[ProfileConfiguration] = self._menu_options['profile_config'].current_selection - if profile_config and profile_config.profile: - is_desktop = profile_config.profile.is_desktop_profile() if profile_config else False - selection = ask_for_audio_selection(is_desktop, current) - return selection - return None + def _select_audio( + self, + current: Optional[AudioConfiguration] = None + ) -> Optional[AudioConfiguration]: + selection = ask_for_audio_selection(current) + return selection + + def _display_audio(self, current: Optional[AudioConfiguration]) -> str: + if not current: + return Audio.no_audio_text() + else: + return current.audio.name def _create_user_account(self, defined_users: List[User]) -> List[User]: users = ask_for_additional_users(defined_users=defined_users) diff --git a/archinstall/lib/interactions/general_conf.py b/archinstall/lib/interactions/general_conf.py index ad9ee386..1c570a69 100644 --- a/archinstall/lib/interactions/general_conf.py +++ b/archinstall/lib/interactions/general_conf.py @@ -5,6 +5,7 @@ from typing import List, Any, Optional, TYPE_CHECKING from ..locale import list_timezones, list_keyboard_languages from ..menu import MenuSelectionType, Menu, TextInput +from ..models.audio_configuration import Audio, AudioConfiguration from ..output import warn from ..packages.packages import validate_package_list from ..storage import storage @@ -55,16 +56,31 @@ def ask_for_a_timezone(preset: Optional[str] = None) -> Optional[str]: return None -def ask_for_audio_selection(desktop: bool = True, preset: Optional[str] = None) -> Optional[str]: - no_audio = str(_('No audio server')) - choices = ['pipewire', 'pulseaudio'] if desktop else ['pipewire', 'pulseaudio', no_audio] - default = 'pipewire' if desktop else no_audio +def ask_for_audio_selection( + current: Optional[AudioConfiguration] = None +) -> Optional[AudioConfiguration]: + choices = [ + Audio.Pipewire.name, + Audio.Pulseaudio.name, + Audio.no_audio_text() + ] - choice = Menu(_('Choose an audio server'), choices, preset_values=preset, default_option=default).run() + preset = current.audio.name if current else None + + choice = Menu( + _('Choose an audio server'), + choices, + preset_values=preset + ).run() match choice.type_: - case MenuSelectionType.Skip: return preset - case MenuSelectionType.Selection: return choice.single_value + case MenuSelectionType.Skip: return current + case MenuSelectionType.Selection: + value = choice.single_value + if value == Audio.no_audio_text(): + return None + else: + return AudioConfiguration(Audio[value]) return None diff --git a/archinstall/lib/models/__init__.py b/archinstall/lib/models/__init__.py index 7415f63f..a1c90e48 100644 --- a/archinstall/lib/models/__init__.py +++ b/archinstall/lib/models/__init__.py @@ -6,3 +6,4 @@ from .network_configuration import ( from .bootloader import Bootloader from .gen import VersionDef, PackageSearchResult, PackageSearch, LocalPackage from .users import PasswordStrength, User +from .audio_configuration import Audio, AudioConfiguration diff --git a/archinstall/lib/models/audio_configuration.py b/archinstall/lib/models/audio_configuration.py new file mode 100644 index 00000000..3a4029db --- /dev/null +++ b/archinstall/lib/models/audio_configuration.py @@ -0,0 +1,54 @@ +from dataclasses import dataclass +from enum import Enum +from typing import Any, TYPE_CHECKING, Dict + +from ..hardware import SysInfo +from ..output import info +from ...default_profiles.applications.pipewire import PipewireProfile + +if TYPE_CHECKING: + _: Any + + +@dataclass +class Audio(Enum): + Pipewire = 'pipewire' + Pulseaudio = 'pulseaudio' + + @staticmethod + def no_audio_text() -> str: + return str(_('No audio server')) + + +@dataclass +class AudioConfiguration: + audio: Audio + + def __dump__(self) -> Dict[str, Any]: + return { + 'audio': self.audio.value + } + + @staticmethod + def parse_arg(arg: Dict[str, Any]) -> 'AudioConfiguration': + return AudioConfiguration( + Audio(arg['audio']) + ) + + def install_audio_config( + self, + installation: Any + ): + info(f'Installing audio server: {self.audio.name}') + + match self.audio: + case Audio.Pipewire: + PipewireProfile().install(installation) + case Audio.Pulseaudio: + installation.add_additional_packages("pulseaudio") + + if SysInfo.requires_sof_fw(): + installation.add_additional_packages('sof-firmware') + + if SysInfo.requires_alsa_fw(): + installation.add_additional_packages('alsa-firmware') diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index c8df590d..605d2b0e 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -8,11 +8,11 @@ from archinstall import SysInfo from archinstall.lib import locale from archinstall.lib import disk from archinstall.lib.global_menu import GlobalMenu -from archinstall.default_profiles.applications.pipewire import PipewireProfile from archinstall.lib.configuration import ConfigurationOutput from archinstall.lib.installer import Installer from archinstall.lib.menu import Menu from archinstall.lib.mirrors import use_mirrors, add_custom_mirrors +from archinstall.lib.models import AudioConfiguration from archinstall.lib.models.bootloader import Bootloader from archinstall.lib.models.network_configuration import NetworkConfiguration from archinstall.lib.networking import check_mirror_reachable @@ -70,7 +70,7 @@ def ask_user_questions(): global_menu.enable('profile_config') # Ask about audio server selection if one is not already set - global_menu.enable('audio') + global_menu.enable('audio_config') # Ask for preferred kernel: global_menu.enable('kernels', mandatory=True) @@ -172,18 +172,9 @@ def perform_installation(mountpoint: Path): if users := archinstall.arguments.get('!users', None): installation.create_users(users) - if audio := archinstall.arguments.get('audio', None): - info(f'Installing audio server: {audio}') - if audio == 'pipewire': - PipewireProfile().install(installation) - elif audio == 'pulseaudio': - installation.add_additional_packages("pulseaudio") - - if SysInfo.requires_sof_fw(): - installation.add_additional_packages('sof-firmware') - - if SysInfo.requires_alsa_fw(): - installation.add_additional_packages('alsa-firmware') + audio_config: Optional[AudioConfiguration] = archinstall.arguments.get('audio_config', None) + if audio_config: + audio_config.install_audio_config(installation) else: info("No audio server will be installed") diff --git a/archinstall/scripts/swiss.py b/archinstall/scripts/swiss.py index a2ab0549..cd532f6d 100644 --- a/archinstall/scripts/swiss.py +++ b/archinstall/scripts/swiss.py @@ -1,7 +1,7 @@ import os from enum import Enum from pathlib import Path -from typing import TYPE_CHECKING, Any, Dict +from typing import TYPE_CHECKING, Any, Dict, Optional import archinstall from archinstall import SysInfo, info, debug @@ -9,13 +9,13 @@ from archinstall.lib import mirrors from archinstall.lib import models from archinstall.lib import disk from archinstall.lib import locale +from archinstall.lib.models import AudioConfiguration from archinstall.lib.networking import check_mirror_reachable from archinstall.lib.profile.profiles_handler import profile_handler from archinstall.lib import menu from archinstall.lib.global_menu import GlobalMenu from archinstall.lib.installer import Installer from archinstall.lib.configuration import ConfigurationOutput -from archinstall.default_profiles.applications.pipewire import PipewireProfile if TYPE_CHECKING: _: Any @@ -95,7 +95,7 @@ class SwissMainMenu(GlobalMenu): options_list = [ 'mirror_config', 'disk_config', 'disk_encryption', 'swap', 'bootloader', 'hostname', '!root-password', - '!users', 'profile_config', 'audio', 'kernels', 'packages', 'additional-repositories', 'network_config', + '!users', 'profile_config', 'audio_config', 'kernels', 'packages', 'additional-repositories', 'network_config', 'timezone', 'ntp' ] @@ -109,7 +109,7 @@ class SwissMainMenu(GlobalMenu): case ExecutionMode.Only_OS: options_list = [ 'mirror_config','bootloader', 'hostname', - '!root-password', '!users', 'profile_config', 'audio', 'kernels', + '!root-password', '!users', 'profile_config', 'audio_config', 'kernels', 'packages', 'additional-repositories', 'network_config', 'timezone', 'ntp' ] @@ -236,14 +236,11 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): if users := archinstall.arguments.get('!users', None): installation.create_users(users) - if audio := archinstall.arguments.get('audio', None): - info(f'Installing audio server: {audio}') - if audio == 'pipewire': - PipewireProfile().install(installation) - elif audio == 'pulseaudio': - installation.add_additional_packages("pulseaudio") + audio_config: Optional[AudioConfiguration] = archinstall.arguments.get('audio_config', None) + if audio_config: + audio_config.install_audio_config(installation) else: - info("No audio server will be installed.") + info("No audio server will be installed") if profile_config := archinstall.arguments.get('profile_config', None): profile_handler.install_profile_config(installation, profile_config) diff --git a/docs/installing/guided.rst b/docs/installing/guided.rst index 0a075282..c5e7f1ed 100644 --- a/docs/installing/guided.rst +++ b/docs/installing/guided.rst @@ -53,7 +53,7 @@ There are three different configuration files, all of which are optional. .. code-block:: json { - "audio": "pipewire", + "audio_config": {"audio": "pipewire"}, "bootloader": "systemd-bootctl", "custom-commands": [ "cd /home/devel; git clone https://aur.archlinux.org/paru.git", diff --git a/examples/config-sample.json b/examples/config-sample.json index 38415b2c..ed1cc38e 100644 --- a/examples/config-sample.json +++ b/examples/config-sample.json @@ -2,7 +2,7 @@ "config_version": "2.5.2", "additional-repositories": [], "archinstall-language": "English", - "audio": "pipewire", + "audio_config": {"audio": "pipewire"}, "bootloader": "Systemd-boot", "debug": false, "disk_config": { @@ -99,19 +99,13 @@ "http://archlinux.mirror.digitalpacific.com.au/$repo/os/$arch": true, } }, - "network_config": { - "nics": [ - { - "dhcp": false, - "dns": [ - "3.3.3.3" - ], - "gateway": "2.2.2.2", - "iface": "enp0s31f6", - "ip": "1.1.1.1" - } - ], - "type": "manual" + "nic": { + "dhcp": true, + "dns": null, + "gateway": null, + "iface": null, + "ip": null, + "type": "nm" }, "no_pkg_lookups": false, "ntp": true, diff --git a/examples/custom-command-sample.json b/examples/custom-command-sample.json index b2250e2c..34d63d74 100644 --- a/examples/custom-command-sample.json +++ b/examples/custom-command-sample.json @@ -1,6 +1,5 @@ { "dry_run": true, - "audio": "none", "bootloader": "systemd-bootctl", "debug": false, "harddrives": [ diff --git a/examples/interactive_installation.py b/examples/interactive_installation.py index 8e82ca7e..e075df9b 100644 --- a/examples/interactive_installation.py +++ b/examples/interactive_installation.py @@ -1,12 +1,11 @@ from pathlib import Path -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Optional import archinstall from archinstall import Installer from archinstall import profile from archinstall import SysInfo from archinstall import mirrors -from archinstall.default_profiles.applications.pipewire import PipewireProfile from archinstall import disk from archinstall import menu from archinstall import models @@ -49,7 +48,7 @@ def ask_user_questions(): global_menu.enable('profile_config') # Ask about audio server selection if one is not already set - global_menu.enable('audio') + global_menu.enable('audio_config') # Ask for preferred kernel: global_menu.enable('kernels', mandatory=True) @@ -151,20 +150,11 @@ def perform_installation(mountpoint: Path): if users := archinstall.arguments.get('!users', None): installation.create_users(users) - if audio := archinstall.arguments.get('audio', None): - info(f'Installing audio server: {audio}') - if audio == 'pipewire': - PipewireProfile().install(installation) - elif audio == 'pulseaudio': - installation.add_additional_packages("pulseaudio") - - if SysInfo.requires_sof_fw(): - installation.add_additional_packages('sof-firmware') - - if SysInfo.requires_alsa_fw(): - installation.add_additional_packages('alsa-firmware') + audio_config: Optional[models.AudioConfiguration] = archinstall.arguments.get('audio_config', None) + if audio_config: + audio_config.install_audio_config(installation) else: - info("No audio server will be installed.") + info("No audio server will be installed") if profile_config := archinstall.arguments.get('profile_config', None): profile.profile_handler.install_profile_config(installation, profile_config) diff --git a/schema.json b/schema.json index b74588a1..5616ed41 100644 --- a/schema.json +++ b/schema.json @@ -12,14 +12,19 @@ "testing" ] }, - "audio": { + "audio_config": { "description": "Audio server to be installed", - "type": "string", - "enum": [ - "pipewire", - "pulseaudio", - "none" - ] + "type": "object", + "properties": { + "audio": { + "description": "Audio server to be installed", + "type": "string", + "enum": [ + "pipewire", + "pulseaudio" + ] + } + }, }, "bootloader": { "description": "Bootloader to be installed", -- cgit v1.2.3-70-g09d2 From a548d7df70102252a557214edc74b77f4859f031 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Tue, 25 Jul 2023 19:19:14 +1000 Subject: Fix 1916 (#1920) * Do not stdout when menu is active * Handle missing libfido2 gracefully * Update --------- Co-authored-by: Daniel Girtler --- archinstall/lib/disk/encryption_menu.py | 25 ++++++++++++++++++++++--- archinstall/lib/disk/fido.py | 9 +++++---- archinstall/lib/menu/menu.py | 9 +++++++++ archinstall/lib/output.py | 14 ++++++++------ 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/archinstall/lib/disk/encryption_menu.py b/archinstall/lib/disk/encryption_menu.py index 89eade2b..234e3b03 100644 --- a/archinstall/lib/disk/encryption_menu.py +++ b/archinstall/lib/disk/encryption_menu.py @@ -71,6 +71,7 @@ class DiskEncryptionMenu(AbstractSubMenu): description=_('Use HSM to unlock encrypted drive'), func=lambda preset: select_hsm(preset), display_func=lambda x: self._display_hsm(x), + preview_func=self._prev_hsm, dependencies=['encryption_password'], default=self._preset.hsm_device, enabled=True @@ -93,8 +94,6 @@ class DiskEncryptionMenu(AbstractSubMenu): if device: return device.manufacturer - if not Fido2.get_fido2_devices(): - return str(_('No HSM devices available')) return None def _prev_disk_layouts(self) -> Optional[str]: @@ -106,6 +105,22 @@ class DiskEncryptionMenu(AbstractSubMenu): return None + def _prev_hsm(self) -> Optional[str]: + try: + Fido2.get_fido2_devices() + except ValueError: + return str(_('Unable to determine fido2 devices. Is libfido2 installed?')) + + fido_device: Optional[Fido2Device] = self._menu_options['HSM'].current_selection + + if fido_device: + output = '{}: {}'.format(str(_('Path')), fido_device.path) + output += '{}: {}'.format(str(_('Manufacturer')), fido_device.manufacturer) + output += '{}: {}'.format(str(_('Product')), fido_device.product) + return output + + return None + def select_encryption_type(preset: EncryptionType) -> Optional[EncryptionType]: title = str(_('Select disk encryption option')) @@ -130,7 +145,11 @@ def select_encrypted_password() -> Optional[str]: def select_hsm(preset: Optional[Fido2Device] = None) -> Optional[Fido2Device]: title = _('Select a FIDO2 device to use for HSM') - fido_devices = Fido2.get_fido2_devices() + + try: + fido_devices = Fido2.get_fido2_devices() + except ValueError: + return None if fido_devices: choice = TableMenu(title, data=fido_devices).run() diff --git a/archinstall/lib/disk/fido.py b/archinstall/lib/disk/fido.py index d8c8fd3c..96a74991 100644 --- a/archinstall/lib/disk/fido.py +++ b/archinstall/lib/disk/fido.py @@ -7,6 +7,7 @@ from typing import List, Optional from .device_model import PartitionModification, Fido2Device from ..general import SysCommand, SysCommandWorker, clear_vt100_escape_codes from ..output import error, info +from ..exceptions import SysCallError class Fido2: @@ -36,13 +37,13 @@ class Fido2: # to prevent continous reloading which will slow # down moving the cursor in the menu if not cls._loaded or reload: - ret: Optional[str] = None try: - ret = SysCommand("systemd-cryptenroll --fido2-device=list").decode('UTF-8') - except: + ret: Optional[str] = SysCommand("systemd-cryptenroll --fido2-device=list").decode('UTF-8') + except SysCallError: error('fido2 support is most likely not installed') + raise ValueError('HSM devices can not be detected, is libfido2 installed?') + if not ret: - error('Unable to retrieve fido2 devices') return [] fido_devices: str = clear_vt100_escape_codes(ret) # type: ignore diff --git a/archinstall/lib/menu/menu.py b/archinstall/lib/menu/menu.py index 768dfe55..358ba5e4 100644 --- a/archinstall/lib/menu/menu.py +++ b/archinstall/lib/menu/menu.py @@ -34,6 +34,11 @@ class MenuSelection: class Menu(TerminalMenu): + _menu_is_active: bool = False + + @staticmethod + def is_menu_active() -> bool: + return Menu._menu_is_active @classmethod def back(cls) -> str: @@ -260,6 +265,8 @@ class Menu(TerminalMenu): return MenuSelection(type_=MenuSelectionType.Skip) def run(self) -> MenuSelection: + Menu._menu_is_active = True + selection = self._show() if selection.type_ == MenuSelectionType.Reset: @@ -277,6 +284,8 @@ class Menu(TerminalMenu): selection.type_ = MenuSelectionType.Skip selection.value = None + Menu._menu_is_active = False + return selection def set_cursor_pos(self,pos :int): diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py index d1c95ec5..9f2a2ae3 100644 --- a/archinstall/lib/output.py +++ b/archinstall/lib/output.py @@ -318,9 +318,11 @@ def log( Journald.log(text, level=level) - # Finally, print the log unless we skipped it based on level. - # We use sys.stdout.write()+flush() instead of print() to try and - # fix issue #94 - if level != logging.DEBUG or storage.get('arguments', {}).get('verbose', False): - sys.stdout.write(f"{text}\n") - sys.stdout.flush() + from .menu import Menu + if not Menu.is_menu_active(): + # Finally, print the log unless we skipped it based on level. + # We use sys.stdout.write()+flush() instead of print() to try and + # fix issue #94 + if level != logging.DEBUG or storage.get('arguments', {}).get('verbose', False): + sys.stdout.write(f"{text}\n") + sys.stdout.flush() -- cgit v1.2.3-70-g09d2 From 728e5773e9e849052049de8974121e21a2c3eb96 Mon Sep 17 00:00:00 2001 From: Mário Victor Ribeiro Silva Date: Tue, 25 Jul 2023 08:24:57 -0300 Subject: feat: add more translations (#1936) --- archinstall/locales/pt_BR/LC_MESSAGES/base.mo | Bin 31068 -> 36093 bytes archinstall/locales/pt_BR/LC_MESSAGES/base.po | 133 +++++++++----------------- 2 files changed, 46 insertions(+), 87 deletions(-) diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo index c990ab44..c536fd1f 100644 Binary files a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo and b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.po b/archinstall/locales/pt_BR/LC_MESSAGES/base.po index 064daed2..487aa08d 100644 --- a/archinstall/locales/pt_BR/LC_MESSAGES/base.po +++ b/archinstall/locales/pt_BR/LC_MESSAGES/base.po @@ -3,19 +3,18 @@ # Rafael Fontenelle # Jefferson Michael # Diogo Silva - msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" -"Last-Translator: Diogo Silva \n" +"Last-Translator: Mário Victor Ribeiro Silva \n" "Language-Team: \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.2.2\n" +"X-Generator: Poedit 3.3.2\n" msgid "[!] A log file has been created here: {} {}" msgstr "[!] Um arquivo de log foi criado aqui: {} {}" @@ -627,7 +626,7 @@ msgid "Manual configuration setting must be a list" msgstr "O ajuste de configuração manual deve ser em lista" msgid "No iface specified for manual configuration" -msgstr "iface não especificada para configuração manual" +msgstr "Nenhum iface especificado para configuração manual" msgid "Manual nic configuration with no auto DHCP requires an IP address" msgstr "A configuração manual de NIC sem DHCP automático requer um endereço IP" @@ -817,9 +816,9 @@ msgstr " - Valor mínimo : 1 ( Permite 1 download paralelo, permite 2 download msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" msgstr " - Desativar/Padrão : 0 ( Desativa os downloads paralelos, permite apenas 1 download por vez )" -#, fuzzy, python-brace-format +#, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {}, ou 0 para desativar]" +msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {max_downloads}, ou 0 para desativar]" msgid "Parallel Downloads" msgstr "Downloads Paralelos" @@ -845,9 +844,8 @@ msgstr "A fonte deve ser armazenada como {}" msgid "Archinstall requires root privileges to run. See --help for more." msgstr "O Archinstall requer privilégios de root para ser executado. Consulte --help para mais informações." -#, fuzzy msgid "Select an execution mode" -msgstr "Selecione uma ação para '{}'" +msgstr "Selecione um modo de execução" msgid "Unable to fetch profile from specified url: {}" msgstr "Não foi possível obter o perfil a partir da URL especificada: {}" @@ -855,56 +853,46 @@ msgstr "Não foi possível obter o perfil a partir da URL especificada: {}" msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" msgstr "Os perfis devem ter nomes únicos, mas foram encontradas definições de perfil com nomes duplicados: {}" -#, fuzzy msgid "Select one or more devices to use and configure" -msgstr "Selecione um ou mais discos rígidos para usar e configurar" +msgstr "Selecione um ou mais dispositivos para usar e configurar" #, fuzzy msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" msgstr "Se você redefinir a seleção de unidades isso também redefinirá o layout da unidade atual. Tem certeza?" -#, fuzzy msgid "Existing Partitions" -msgstr "Adicionando partição...." +msgstr "Partições existentes" -#, fuzzy msgid "Select a partitioning option" -msgstr "Deletar uma partição" +msgstr "Selecione uma opção de particionamento" -#, fuzzy msgid "Enter the root directory of the mounted devices: " -msgstr "Digite um diretório para as configurações serem salvas: " +msgstr "Digite o diretório raiz dos dispositivos montados: " -#, fuzzy msgid "Minimum capacity for /home partition: {}GiB\n" -msgstr "Capacidade mínima para partição /home : {}GB\n" +msgstr "Capacidade mínima para partição /home : {}GiB\n" -#, fuzzy msgid "Minimum capacity for Arch Linux partition: {}GiB" -msgstr "Capacidade mínima para a partição do Arch Linux: {}GB" +msgstr "Capacidade mínima para a partição do Arch Linux: {}GiB" -#, fuzzy msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" msgstr "Esta é uma lista de perfis pré-programados, que podem por exemplo facilitar a instalação de ambientes gráficos" -#, fuzzy msgid "Current profile selection" -msgstr "Layout de partições atual" +msgstr "Seleção de perfil atual" -#, fuzzy msgid "Remove all newly added partitions" -msgstr "Criar uma nova partição" +msgstr "Remova todas as partições recém-adicionadas" -#, fuzzy msgid "Assign mountpoint" -msgstr "Atribuir um ponto de montagem para uma partição" +msgstr "Atribuir um ponto de montagem" #, fuzzy msgid "Mark/Unmark to be formatted (wipes data)" msgstr "Marcar/Desmarcar uma partição para ser formatada (apaga os dados)" msgid "Mark/Unmark as bootable" -msgstr "" +msgstr "Marcar/desmarcar como inicializável" msgid "Change filesystem" msgstr "Mudar arquivo do sistema" @@ -913,23 +901,20 @@ msgstr "Mudar arquivo do sistema" msgid "Mark/Unmark as compressed" msgstr "Marcar/desmarcar a partição como comprimida (apenas btrfs)" -#, fuzzy msgid "Set subvolumes" -msgstr "Deletar subvolume" +msgstr "Definir subvolumes" -#, fuzzy msgid "Delete partition" -msgstr "Deletar uma partição" +msgstr "Deletar partição" msgid "Partition" -msgstr "Partição: " +msgstr "Partição" msgid "This partition is currently encrypted, to format it a filesystem has to be specified" msgstr "Esta partição está criptografada. Para formatá-la, um sistema de arquivos deve ser especificado" -#, fuzzy msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * Os pontos de montagem das partições são relativos aos de dentro da instalação, boot por exemplo seria /boot." +msgstr "Os pontos de montagem das partições são relativos aos de dentro da instalação, boot por exemplo seria /boot." msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." msgstr "Se o ponto de montagem /boot for definido, a partição também será marcada como inicializável." @@ -940,17 +925,14 @@ msgstr "Ponto de montagem: " msgid "Current free sectors on device {}:" msgstr "Setores livres no dispositivo {}:" -#, fuzzy msgid "Total sectors: {}" -msgstr "Não é um diretório válido: {}" +msgstr "Total de setores: {}" -#, fuzzy msgid "Enter the start sector (default: {}): " -msgstr "Digite o setor de início (porcentagem ou número do bloco, padrão: {}): " +msgstr "Digite o setor de início (padrão: {}): " -#, fuzzy msgid "Enter the end sector of the partition (percentage or block number, default: {}): " -msgstr "Digite o setor final da partição (porcentagem ou número de bloco, ex.: {}): " +msgstr "Digite o setor final da partição (porcentagem ou número de bloco, padrão: {}): " msgid "This will remove all newly added partitions, continue?" msgstr "Isso irá remover todas as partições recém-adicionadas, continuar?" @@ -961,7 +943,6 @@ msgstr "Gerenciamento de partições: {}" msgid "Total length: {}" msgstr "Tamanho total: {}" -#, fuzzy msgid "Encryption type" msgstr "Tipo de encriptação" @@ -984,13 +965,11 @@ msgstr "Selecione um dispositivo FIDO2 para usar como HSM" msgid "Use a best-effort default partition layout" msgstr "Apagar todos os discos selecionados e usar um esquema de partições padrão de melhor desempenho" -#, fuzzy msgid "Manual Partitioning" msgstr "Particionamento manual" -#, fuzzy msgid "Pre-mounted configuration" -msgstr "Nenhuma configuração" +msgstr "Configuração pré-montada" msgid "Unknown" msgstr "Desconhecido" @@ -1007,16 +986,14 @@ msgstr "← Voltar" msgid "Disk encryption" msgstr "Encriptação de disco" -#, fuzzy msgid "Configuration" msgstr "Configuração" msgid "Password" msgstr "Senha" -#, fuzzy msgid "All settings will be reset, are you sure?" -msgstr "{} contém partições em fila, isto irá removê-las, tem certeza?" +msgstr "Todas as configurações serão redefinidas, tem certeza?" msgid "Back" msgstr "Voltar" @@ -1030,37 +1007,29 @@ msgstr "Tipo de ambiente: {}" msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" msgstr "O driver proprietário Nvidia não é suportado pelo Sway. É provável que você encontre problemas. Você está de acordo com isso?" -#, fuzzy msgid "Installed packages" -msgstr "Pacotes adicionais" +msgstr "Pacotes instalados" -#, fuzzy msgid "Add profile" msgstr "Adicionar perfil" -#, fuzzy msgid "Edit profile" msgstr "Editar perfil" -#, fuzzy msgid "Delete profile" msgstr "Deletar perfil" -#, fuzzy msgid "Profile name: " msgstr "Nome do perfil: " -#, fuzzy msgid "The profile name you entered is already in use. Try again" -msgstr "O nome de usuário que você digitou já esta em uso. Tente outro" +msgstr "O nome do perfil que você digitou já esta em uso. Tente novamente" -#, fuzzy msgid "Packages to be install with this profile (space separated, leave blank to skip): " -msgstr "Digite pacotes adicionais para instalar (separados por espaço, deixe em branco para pular): " +msgstr "Pacotes a serem instalados com este perfil (separados por espaço, deixe em branco para pular): " -#, fuzzy msgid "Services to be enabled with this profile (space separated, leave blank to skip): " -msgstr "Digite pacotes adicionais para instalar (separados por espaço, deixe em branco para pular): " +msgstr "Serviços a serem ativados com este perfil (separados por espaço, deixe em branco para pular): " msgid "Should this profile be enabled for installation?" msgstr "Este perfil deve ser ativado para instalação?" @@ -1068,14 +1037,13 @@ msgstr "Este perfil deve ser ativado para instalação?" msgid "Create your own" msgstr "Crie o seu próprio" -#, fuzzy msgid "" "\n" "Select a graphics driver or leave blank to install all open-source drivers" msgstr "" "\n" "\n" -"Selecione um driver de vídeo ou deixe em branco para instalar os drivers completamente open-source" +"Selecione um driver gráfico ou deixe em branco para instalar todos os drivers de código aberto" msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" msgstr "O Sway precisa de acesso ao seu seat (conjunto de dispositivos de hardware, como teclado, mouse, etc)" @@ -1087,13 +1055,14 @@ msgid "" msgstr "" "\n" "\n" -"Selecione uma opção para permitir o acesso do Sway ao seu hardware." +"Selecione uma opção para permitir o acesso do Sway ao seu hardware" msgid "Graphics driver" msgstr "Driver gráfico" +#, fuzzy msgid "Greeter" -msgstr "" +msgstr "Recepcionista" msgid "Please chose which greeter to install" msgstr "Por favor, escolha qual greeter instalar" @@ -1101,24 +1070,20 @@ msgstr "Por favor, escolha qual greeter instalar" msgid "This is a list of pre-programmed default_profiles" msgstr "Esta é uma lista de perfis pré-programados (default_profiles)" -#, fuzzy msgid "Disk configuration" msgstr "Configuração do disco" -#, fuzzy msgid "Profiles" -msgstr "Perfil" +msgstr "Perfis" msgid "Finding possible directories to save configuration files ..." msgstr "Procurando possiveis diretórios para salvar os arquivos de configuração ..." -#, fuzzy msgid "Select directory (or directories) for saving configuration files" msgstr "Selecione um ou mais diretórios para salvar arquivos de configuração" -#, fuzzy msgid "Add a custom mirror" -msgstr "Adicionar usuário" +msgstr "Adicionar mirror personalizado" msgid "Change custom mirror" msgstr "Alterar mirror personalizado" @@ -1126,21 +1091,19 @@ msgstr "Alterar mirror personalizado" msgid "Delete custom mirror" msgstr "Excluir mirror personalizado" -#, fuzzy msgid "Enter name (leave blank to skip): " -msgstr "Digite um nome de usuário (deixe em branco para pular): " +msgstr "Digite o nome (deixe em branco para pular): " -#, fuzzy msgid "Enter url (leave blank to skip): " -msgstr "Digite um nome de usuário (deixe em branco para pular): " +msgstr "Digite a url (deixe em branco para pular): " #, fuzzy msgid "Select signature check option" -msgstr "Selecione a opção de encriptação de disco" +msgstr "Selecione uma opção de verificação de assinatura" #, fuzzy msgid "Select signature option" -msgstr "Selecione a opção de encriptação de disco" +msgstr "Selecione uma opção de assinatura" msgid "Custom mirrors" msgstr "Mirrors personalizados" @@ -1148,15 +1111,15 @@ msgstr "Mirrors personalizados" msgid "Defined" msgstr "Definido" -#, fuzzy msgid "Save user configuration (including disk layout)" -msgstr "Salvar configuração de usuário" +msgstr "Salvar configuração de usuário (incluindo layout do disco)" -#, fuzzy msgid "" "Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" "Save directory: " -msgstr "Digite um diretório para as configurações serem salvas: " +msgstr "" +"Digite um diretório para as configurações serem salvas (completamento de tab ativado): \n" +"Salvar diretório: " msgid "" "Do you want to save {} configuration file(s) in the following location?\n" @@ -1170,17 +1133,14 @@ msgstr "" msgid "Saving {} configuration files to {}" msgstr "Salvando arquivos de configuração de {} para {}" -#, fuzzy msgid "Mirrors" -msgstr "Região do mirror" +msgstr "Mirrors" -#, fuzzy msgid "Mirror regions" -msgstr "Região do mirror" +msgstr "Regiões dos mirrors" -#, fuzzy msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - Valor máximo : {} ( Permite {} downloads paralelos, permite {} downloads por vez )" +msgstr " - Valor máximo : {} ( Permite {} downloads paralelos, permite {max_downloads+1} downloads por vez )" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {}, ou 0 para desativar]" @@ -1188,7 +1148,6 @@ msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {}, o msgid "Locales" msgstr "Localidades" -#, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "Usar NetworkManager (necessário para configurar internet graficamente no GNOME e KDE)" -- cgit v1.2.3-70-g09d2 From 08b84d239ca8196e492a8ffb48f0e9e5ff8718a7 Mon Sep 17 00:00:00 2001 From: Alexmelman88 <99257010+Alexmelman88@users.noreply.github.com> Date: Tue, 25 Jul 2023 14:29:22 +0300 Subject: Update Russian translation (#1947) --- archinstall/locales/ru/LC_MESSAGES/base.mo | Bin 49210 -> 49500 bytes archinstall/locales/ru/LC_MESSAGES/base.po | 11 ++--------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/archinstall/locales/ru/LC_MESSAGES/base.mo b/archinstall/locales/ru/LC_MESSAGES/base.mo index a7b5fbd3..e0e227db 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 5f1fe09e..acbda26f 100644 --- a/archinstall/locales/ru/LC_MESSAGES/base.po +++ b/archinstall/locales/ru/LC_MESSAGES/base.po @@ -10,7 +10,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" -"X-Generator: Poedit 3.3.1\n" +"X-Generator: Poedit 3.3.2\n" msgid "[!] A log file has been created here: {} {}" msgstr "[!] Здесь был создан файл журнала: {} {}" @@ -1134,15 +1134,8 @@ msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "Неверный ввод! Повторите попытку с правильным вводом [1 - {}, или 0 - отключить]" msgid "Locales" -msgstr "" +msgstr "Локализации" -#, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "Использовать NetworkManager (необходим для графической настройки интернета в GNOME и KDE)" -#, python-brace-format -#~ msgid "Edit {origkey} :" -#~ msgstr "Редактировать {origkey}:" - -#~ msgid " ! Formatting {archinstall.arguments['harddrives']} in " -#~ msgstr " ! Форматирование {archinstall.arguments['harddrives']} в " -- cgit v1.2.3-70-g09d2 From 1af21c3e9582f3bf88f03ae2d45761b8b4ba3b64 Mon Sep 17 00:00:00 2001 From: Wu Xiaotian Date: Wed, 26 Jul 2023 15:19:45 +0800 Subject: Let the Chinese translation show in the menu item (#1944) Even though the translation files exist, we still can't find Simplified or Traditional Chinese translations from the language menu, this patch fixes that. --- archinstall/lib/translationhandler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py index 5f0f0695..a2e44065 100644 --- a/archinstall/lib/translationhandler.py +++ b/archinstall/lib/translationhandler.py @@ -168,7 +168,7 @@ class TranslationHandler: translation_files = [] for filename in filenames: - if len(filename) == 2 or filename == 'pt_BR': + if len(filename) == 2 or filename in ['pt_BR', 'zh-CN', 'zh-TW']: translation_files.append(filename) return translation_files -- cgit v1.2.3-70-g09d2 From 7326d51161bf6fd7f1c683cf1d7ce09338efe4b7 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Wed, 26 Jul 2023 09:22:01 -0400 Subject: Fix boot loader disk encryption conditional (#1950) --- archinstall/lib/installer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index f5999002..48177013 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -797,7 +797,7 @@ class Installer: options_entry = [] - if root_partition.safe_fs_type.is_crypto(): + if root_partition in self._disk_encryption.partitions: # TODO: We need to detect if the encrypted device is a whole disk encryption, # or simply a partition encryption. Right now we assume it's a partition (and we always have) debug('Root partition is an encrypted device, identifying by PARTUUID: {root_partition.partuuid}') @@ -858,7 +858,7 @@ class Installer: _file = "/etc/default/grub" - if root_partition.safe_fs_type.is_crypto(): + if root_partition in self._disk_encryption.partitions: debug(f"Using UUID {root_partition.uuid} as encrypted root identifier") cmd_line_linux = f"sed -i 's/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"cryptdevice=UUID={root_partition.uuid}:cryptlvm rootfstype={root_partition.safe_fs_type.value}\"/'" @@ -1049,7 +1049,7 @@ TIMEOUT=5 # blkid doesn't trigger on loopback devices really well, # so we'll use the old manual method until we get that sorted out. - if root_partition.safe_fs_type.is_crypto(): + if root_partition in self._disk_encryption.partitions: # TODO: We need to detect if the encrypted device is a whole disk encryption, # or simply a partition encryption. Right now we assume it's a partition (and we always have) debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}') -- cgit v1.2.3-70-g09d2 From 29b528072551f32a0075b3f5c9d91d5ded019534 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Sat, 29 Jul 2023 06:20:55 -0400 Subject: Add `awesome` package to Awesome profile (#1954) --- archinstall/default_profiles/desktops/awesome.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/archinstall/default_profiles/desktops/awesome.py b/archinstall/default_profiles/desktops/awesome.py index bb481914..371e51db 100644 --- a/archinstall/default_profiles/desktops/awesome.py +++ b/archinstall/default_profiles/desktops/awesome.py @@ -14,7 +14,10 @@ class AwesomeProfile(XorgProfile): @property def packages(self) -> List[str]: - return ['alacritty'] + return [ + 'awesome', + 'alacritty' + ] def preview_text(self) -> Optional[str]: text = str(_('Environment type: {}')).format(self.profile_type.value) -- cgit v1.2.3-70-g09d2 From a43344c5ae2249ddb70e535637915b30563c5038 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Sun, 30 Jul 2023 00:28:21 +0200 Subject: Adding pack packages to profiles after they were removed (#1956) * Added back xinit for awesome, since it can be used without a greeter, as well as other useful tools we've had in previous releases * Fixing xinitrc for awesome profile * Attempting to grab xorg packages when installing the desktop profile * Spelling error on xorg-server * Fixed sway value error on seat selection --- archinstall/__init__.py | 4 +-- archinstall/default_profiles/desktops/awesome.py | 32 ++++++++++++++++++++++-- archinstall/default_profiles/desktops/bspwm.py | 22 ++++++++++++++++ archinstall/default_profiles/desktops/sway.py | 2 +- archinstall/default_profiles/xorg.py | 8 +++++- archinstall/lib/general.py | 2 +- 6 files changed, 63 insertions(+), 7 deletions(-) diff --git a/archinstall/__init__.py b/archinstall/__init__.py index cfaecd16..56f0b278 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -145,12 +145,12 @@ def cleanup_empty_args(args: Union[Namespace, Dict]) -> Dict: Takes arguments (dictionary or argparse Namespace) and removes any None values. This ensures clean mergers during dict.update(args) """ - if type(args) == Namespace: + if type(args) is Namespace: args = vars(args) clean_args = {} for key, val in args.items(): - if type(val) == dict: + if isinstance(val, dict): val = cleanup_empty_args(val) if val is not None: diff --git a/archinstall/default_profiles/desktops/awesome.py b/archinstall/default_profiles/desktops/awesome.py index 371e51db..79e0eb71 100644 --- a/archinstall/default_profiles/desktops/awesome.py +++ b/archinstall/default_profiles/desktops/awesome.py @@ -14,9 +14,18 @@ class AwesomeProfile(XorgProfile): @property def packages(self) -> List[str]: - return [ + return super().packages + [ 'awesome', - 'alacritty' + 'alacritty', + 'xorg-xinit', + 'xorg-xrandr', + 'xterm', + 'feh', + 'slock', + 'terminus-font', + 'gnu-free-fonts', + 'ttf-liberation', + 'xsel', ] def preview_text(self) -> Optional[str]: @@ -37,3 +46,22 @@ class AwesomeProfile(XorgProfile): fh.write(awesome_lua) # TODO: Configure the right-click-menu to contain the above packages that were installed. (as a user config) + + # TODO: check if we selected a greeter, + # but for now, awesome is intended to run without one. + with open(f"{install_session.target}/etc/X11/xinit/xinitrc", 'r') as xinitrc: + xinitrc_data = xinitrc.read() + + for line in xinitrc_data.split('\n'): + if "twm &" in line: + xinitrc_data = xinitrc_data.replace(line, f"# {line}") + if "xclock" in line: + xinitrc_data = xinitrc_data.replace(line, f"# {line}") + if "xterm" in line: + xinitrc_data = xinitrc_data.replace(line, f"# {line}") + + xinitrc_data += '\n' + xinitrc_data += 'exec awesome\n' + + with open(f"{install_session.target}/etc/X11/xinit/xinitrc", 'w') as xinitrc: + xinitrc.write(xinitrc_data) \ No newline at end of file diff --git a/archinstall/default_profiles/desktops/bspwm.py b/archinstall/default_profiles/desktops/bspwm.py index f3bc982d..2a29f41b 100644 --- a/archinstall/default_profiles/desktops/bspwm.py +++ b/archinstall/default_profiles/desktops/bspwm.py @@ -13,6 +13,7 @@ class BspwmProfile(XorgProfile): @property def packages(self) -> List[str]: + # return super().packages + [ return [ 'bspwm', 'sxhkd', @@ -28,3 +29,24 @@ class BspwmProfile(XorgProfile): def preview_text(self) -> Optional[str]: text = str(_('Environment type: {}')).format(self.profile_type.value) return text + '\n' + self.packages_text() + + # The wiki specified xinit, but we already use greeter? + # https://wiki.archlinux.org/title/Bspwm#Starting + # + # # TODO: check if we selected a greeter, else run this: + # with open(f"{install_session.target}/etc/X11/xinit/xinitrc", 'r') as xinitrc: + # xinitrc_data = xinitrc.read() + + # for line in xinitrc_data.split('\n'): + # if "twm &" in line: + # xinitrc_data = xinitrc_data.replace(line, f"# {line}") + # if "xclock" in line: + # xinitrc_data = xinitrc_data.replace(line, f"# {line}") + # if "xterm" in line: + # xinitrc_data = xinitrc_data.replace(line, f"# {line}") + + # xinitrc_data += '\n' + # xinitrc_data += 'exec bspwn\n' + + # with open(f"{install_session.target}/etc/X11/xinit/xinitrc", 'w') as xinitrc: + # xinitrc.write(xinitrc_data) diff --git a/archinstall/default_profiles/desktops/sway.py b/archinstall/default_profiles/desktops/sway.py index ae814e46..25d74a88 100644 --- a/archinstall/default_profiles/desktops/sway.py +++ b/archinstall/default_profiles/desktops/sway.py @@ -53,7 +53,7 @@ class SwayProfile(XorgProfile): @property def services(self) -> List[str]: if pref := self.custom_settings.get('seat_access', None): - return [pref.value] + return [pref] return [] def _ask_seat_access(self): diff --git a/archinstall/default_profiles/xorg.py b/archinstall/default_profiles/xorg.py index 553421a4..13154818 100644 --- a/archinstall/default_profiles/xorg.py +++ b/archinstall/default_profiles/xorg.py @@ -1,4 +1,4 @@ -from typing import Any, TYPE_CHECKING +from typing import Any, TYPE_CHECKING, List from archinstall.default_profiles.profile import Profile, ProfileType @@ -19,3 +19,9 @@ class XorgProfile(Profile): description=description, support_gfx_driver=True ) + + @property + def packages(self) -> List[str]: + return [ + 'xorg-server' + ] diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py index 473f85a4..90af25ed 100644 --- a/archinstall/lib/general.py +++ b/archinstall/lib/general.py @@ -211,7 +211,7 @@ class SysCommandWorker: return False def write(self, data: bytes, line_ending :bool = True) -> int: - assert type(data) == bytes # TODO: Maybe we can support str as well and encode it + assert isinstance(data, bytes) # TODO: Maybe we can support str as well and encode it self.make_sure_we_are_executing() -- cgit v1.2.3-70-g09d2 From 355a0dbe06ace619387b26883a8619954aa7475d Mon Sep 17 00:00:00 2001 From: Wu Xiaotian Date: Sun, 30 Jul 2023 15:59:31 +0800 Subject: Update Simplified Chinese translation (#1943) --- archinstall/locales/zh-CN/LC_MESSAGES/base.po | 57 ++++++++++++--------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.po b/archinstall/locales/zh-CN/LC_MESSAGES/base.po index f981a257..77c48924 100644 --- a/archinstall/locales/zh-CN/LC_MESSAGES/base.po +++ b/archinstall/locales/zh-CN/LC_MESSAGES/base.po @@ -3,13 +3,13 @@ msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" -"Last-Translator: wc7086 <190022474@qq.com>\n" +"Last-Translator: yetist \n" "Language-Team: \n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.1.1\n" +"X-Generator: Poedit 3.3.2\n" msgid "[!] A log file has been created here: {} {}" msgstr "[!] 日志文件已在此处创建:{} {}" @@ -809,9 +809,9 @@ msgstr " - 最小值:1(允许 1 个并行下载,同时允许 2 个下载 msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" msgstr " - 禁用/默认值:0(禁用并行下载,同时只允许 1 个下载)" -#, fuzzy, python-brace-format +#, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "输入无效! 使用有效输入重试 [1 到 {},或 0 到禁用]" +msgstr "输入无效! 使用有效输入重试 [1 到 {max_downloads},或 0 禁用]" msgid "Parallel Downloads" msgstr "并行下载" @@ -904,7 +904,7 @@ msgid "This partition is currently encrypted, to format it a filesystem has to b msgstr "此分区当前已加密,要格式化它必须指定文件系统" msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * 分区挂载点是相对于安装目录内部的,例如引导分区(boot)的挂载点为 /boot。" +msgstr "分区挂载点是相对于安装目录内部的,例如引导分区(boot)的挂载点为 /boot。" msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." msgstr "如果设置了挂载点 /boot,则该分区也将被标记为可引导。" @@ -1070,76 +1070,69 @@ msgstr "正在查找可能用于保存配置文件的目录 ..." msgid "Select directory (or directories) for saving configuration files" msgstr "选择一个或多个硬盘驱动器来使用和配置" -#, fuzzy msgid "Add a custom mirror" -msgstr "添加一个用户" +msgstr "添加自定义镜像" msgid "Change custom mirror" -msgstr "" +msgstr "更改自定义镜像" msgid "Delete custom mirror" -msgstr "" +msgstr "删除自定义镜像" -#, fuzzy msgid "Enter name (leave blank to skip): " msgstr "输入用户名(留空跳过): " -#, fuzzy msgid "Enter url (leave blank to skip): " -msgstr "输入用户名(留空跳过): " +msgstr "输入网址(留空跳过): " -#, fuzzy msgid "Select signature check option" -msgstr "选择要添加的接口" +msgstr "选择签名检查选项" -#, fuzzy msgid "Select signature option" -msgstr "选择要添加的接口" +msgstr "选择签名选项" msgid "Custom mirrors" -msgstr "" +msgstr "自定义镜像" msgid "Defined" -msgstr "" +msgstr "已定义" -#, fuzzy msgid "Save user configuration (including disk layout)" -msgstr "保存用户配置" +msgstr "保存用户配置(包括磁盘布局)" -#, fuzzy msgid "" "Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" "Save directory: " -msgstr "输入要保存配置的目录: " +msgstr "" +"输入要保存配置的目录(制表符可补全)\n" +"保存目录: " msgid "" "Do you want to save {} configuration file(s) in the following location?\n" "\n" "{}" msgstr "" +"您想将 {} 个配置文件保存在以下位置吗?\n" +"\n" +"{}" -#, fuzzy msgid "Saving {} configuration files to {}" -msgstr "保存配置" +msgstr "将 {} 配置文件保存到 {}" -#, fuzzy msgid "Mirrors" -msgstr "镜像区域" +msgstr "镜像" -#, fuzzy msgid "Mirror regions" msgstr "镜像区域" -#, fuzzy msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - 最大值:{}(允许 {} 次并行下载,一次允许 {} 次下载)" +msgstr " - 最大值:{}(允许 {} 个并行下载,每次允许 {max_downloads+1} 个下载)" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "输入无效! 使用有效输入重试 [1 到 {},或 0 到禁用]" msgid "Locales" -msgstr "" +msgstr "区域设置" -#, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" -msgstr "使用 NetworkManager(在 GNOME 和 KDE 中以图形方式配置 Internet 所必需的)" +msgstr "使用 NetworkManager(在 GNOME 和 KDE 中以图形方式配置互联网所必需)" -- cgit v1.2.3-70-g09d2 From a1ad25ff4575d874b5634cee57a927a041283886 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Sun, 30 Jul 2023 22:08:51 +0200 Subject: Mkinitcpio turn on output (#1963) * Turning on output for mkinitcpio, otherwise the prompt stand still for a while after enabling fstrim. * Added error message for when mkinitcpio errors out (but also say we're continuing) * Pleasing mypy --- archinstall/lib/installer.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 48177013..94bdff7d 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -569,9 +569,11 @@ class Installer: mkinit.write(f"HOOKS=({' '.join(self._hooks)})\n") try: - SysCommand(f'/usr/bin/arch-chroot {self.target} mkinitcpio {" ".join(flags)}') + SysCommand(f'/usr/bin/arch-chroot {self.target} mkinitcpio {" ".join(flags)}', peek_output=True) return True - except SysCallError: + except SysCallError as error: + if error.worker: + log(error.worker._trace_log.decode()) return False def minimal_installation( @@ -664,7 +666,8 @@ class Installer: # TODO: Use python functions for this SysCommand(f'/usr/bin/arch-chroot {self.target} chmod 700 /root') - self.mkinitcpio(['-P'], locale_config) + if not self.mkinitcpio(['-P'], locale_config): + error(f"Error generating initramfs (continuing anyway)") self.helper_flags['base'] = True -- cgit v1.2.3-70-g09d2 From 3cea9baff6b0b15b766c7095f1d56adece74b58e Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Sun, 30 Jul 2023 23:40:40 +0200 Subject: Muted partprobe (#1964) * Muted partprobe * Missing import --- archinstall/lib/disk/device_handler.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index 9acf0999..d46275d1 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -3,6 +3,7 @@ from __future__ import annotations import json import os import time +import logging from pathlib import Path from typing import List, Dict, Any, Optional, TYPE_CHECKING @@ -23,7 +24,7 @@ from .device_model import ( from ..exceptions import DiskError, UnknownFilesystemFormat from ..general import SysCommand, SysCallError, JSON from ..luks import Luks2 -from ..output import debug, error, info, warn +from ..output import debug, error, info, warn, log from ..utils.util import is_subpath if TYPE_CHECKING: @@ -584,7 +585,10 @@ class DeviceHandler(object): debug(f'Calling partprobe: {command}') SysCommand(command) except SysCallError as err: - error(f'"{command}" failed to run: {err}') + if 'have been written, but we have been unable to inform the kernel of the change' in str(err): + log(f"Partprobe was not able to inform the kernel of the new disk state (ignoring error): {err}", fg="gray", level=logging.INFO) + else: + error(f'"{command}" failed to run (continuing anyway): {err}') def _wipe(self, dev_path: Path): """ -- cgit v1.2.3-70-g09d2 From ada1ffeaad43eee4c7022606d3037d92fae0ad7a Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Sun, 30 Jul 2023 23:44:52 +0200 Subject: Bumping version (#1965) --- archinstall/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archinstall/__init__.py b/archinstall/__init__.py index 56f0b278..39588904 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -39,7 +39,7 @@ if TYPE_CHECKING: _: Any -__version__ = "2.6.0rc1" +__version__ = "2.6.0" storage['__version__'] = __version__ -- cgit v1.2.3-70-g09d2 From 7dd95f8c690b2aa03d36589e33f9a117d1a62d0f Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Mon, 31 Jul 2023 03:38:29 -0400 Subject: Refactor `_add_grub_bootloader()` configuration (#1962) --- archinstall/lib/installer.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 94bdff7d..ba57a001 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -859,19 +859,20 @@ class Installer: ): self.pacman.strap('grub') # no need? - _file = "/etc/default/grub" + grub_default = self.target / 'etc/default/grub' + config = grub_default.read_text() + + cmdline_linux = [] if root_partition in self._disk_encryption.partitions: debug(f"Using UUID {root_partition.uuid} as encrypted root identifier") - cmd_line_linux = f"sed -i 's/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"cryptdevice=UUID={root_partition.uuid}:cryptlvm rootfstype={root_partition.safe_fs_type.value}\"/'" - enable_cryptdisk = "sed -i 's/#GRUB_ENABLE_CRYPTODISK=y/GRUB_ENABLE_CRYPTODISK=y/'" - - SysCommand(f"/usr/bin/arch-chroot {self.target} {enable_cryptdisk} {_file}") - else: - cmd_line_linux = f"sed -i 's/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"rootfstype={root_partition.safe_fs_type.value}\"/'" + cmdline_linux.append(f'cryptdevice=UUID={root_partition.uuid}:cryptlvm') + config = re.sub(r'#(GRUB_ENABLE_CRYPTODISK=y\n)', r'\1', config, 1) - SysCommand(f"/usr/bin/arch-chroot {self.target} {cmd_line_linux} {_file}") + cmdline_linux.append(f'rootfstype={root_partition.safe_fs_type.value}') + config = re.sub(r'(GRUB_CMDLINE_LINUX=")("\n)', rf'\1{" ".join(cmdline_linux)}\2', config, 1) + grub_default.write_text(config) info(f"GRUB boot partition: {boot_partition.dev_path}") -- cgit v1.2.3-70-g09d2 From 9cbb2b75940e3aad1a0236662c45437d70eac1af Mon Sep 17 00:00:00 2001 From: Mário Victor Ribeiro Silva Date: Mon, 31 Jul 2023 04:38:42 -0300 Subject: Parallel downloads (#1952) * refactor: remove max_downloads limit * Update parallel downloads * Update parallel downloads --- archinstall/lib/interactions/general_conf.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/archinstall/lib/interactions/general_conf.py b/archinstall/lib/interactions/general_conf.py index 1c570a69..14fcc3f8 100644 --- a/archinstall/lib/interactions/general_conf.py +++ b/archinstall/lib/interactions/general_conf.py @@ -165,23 +165,20 @@ def ask_additional_packages_to_install(preset: List[str] = []) -> List[str]: def add_number_of_parrallel_downloads(input_number :Optional[int] = None) -> Optional[int]: - max_downloads = 5 - print(_(f"This option enables the number of parallel downloads that can occur during installation")) - print(str(_("Enter the number of parallel downloads to be enabled.\n (Enter a value between 1 to {})\nNote:")).format(max_downloads)) - print(str(_(" - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )")).format(max_downloads, max_downloads, max_downloads + 1)) - print(_(" - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )")) - print(_(" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )")) + max_recommended = 5 + print(_(f"This option enables the number of parallel downloads that can occur during package downloads")) + print(_("Enter the number of parallel downloads to be enabled.\n\nNote:\n")) + print(str(_(" - Maximum recommended value : {} ( Allows {} parallel downloads at a time )")).format(max_recommended, max_recommended)) + print(_(" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n")) while True: try: input_number = int(TextInput(_("[Default value: 0] > ")).run().strip() or 0) if input_number <= 0: input_number = 0 - elif input_number > max_downloads: - input_number = max_downloads break except: - print(str(_("Invalid input! Try again with a valid input [1 to {}, or 0 to disable]")).format(max_downloads)) + print(str(_("Invalid input! Try again with a valid input [or 0 to disable]")).format(max_recommended)) pacman_conf_path = pathlib.Path("/etc/pacman.conf") with pacman_conf_path.open() as f: @@ -190,7 +187,7 @@ def add_number_of_parrallel_downloads(input_number :Optional[int] = None) -> Opt with pacman_conf_path.open("w") as fwrite: for line in pacman_conf: if "ParallelDownloads" in line: - fwrite.write(f"ParallelDownloads = {input_number+1}\n") if not input_number == 0 else fwrite.write("#ParallelDownloads = 0\n") + fwrite.write(f"ParallelDownloads = {input_number}\n") if not input_number == 0 else fwrite.write("#ParallelDownloads = 0\n") else: fwrite.write(f"{line}\n") -- cgit v1.2.3-70-g09d2 From d878496a2fb9e3c7f48261f866cb285dfa987298 Mon Sep 17 00:00:00 2001 From: Wu Xiaotian Date: Mon, 31 Jul 2023 15:39:06 +0800 Subject: Improve the Chinese, Japanese and Korean text menu layout (#1945) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this patch, menus in Korean language would not be aligned: ``` Archinstall 언어 Korean (71%) > Mirrors Locales Defined Disk configuration 부트로더 Systemd-boot 스왑 True ``` After apply this patch, menus in Korean language are aligned: ``` Archinstall 언어 Korean (71%) > Mirrors Locales Defined Disk configuration 부트로더 Systemd-boot 스왑 True ``` --- archinstall/lib/menu/abstract_menu.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/archinstall/lib/menu/abstract_menu.py b/archinstall/lib/menu/abstract_menu.py index eee99747..f12d2b73 100644 --- a/archinstall/lib/menu/abstract_menu.py +++ b/archinstall/lib/menu/abstract_menu.py @@ -1,4 +1,5 @@ from __future__ import annotations +import unicodedata from typing import Callable, Any, List, Iterator, Tuple, Optional, Dict, TYPE_CHECKING @@ -9,6 +10,22 @@ from ..translationhandler import TranslationHandler, Language if TYPE_CHECKING: _: Any +def count_cjk_chars(string): + "Count the total number of CJK characters contained in a string" + return sum(unicodedata.east_asian_width(c) in 'FW' for c in string) + +def cjkljust(string, width, fillbyte=' '): + """Support left alignment of Chinese, Japanese, Korean text + >>> cjkljust('Hello', 15, '*') + 'Hello**********' + >>> cjkljust('你好', 15, '*') + '你好***********' + >>> cjkljust('안녕하세요', 15, '*') + '안녕하세요*****' + >>> cjkljust('こんにちは', 15, '*') + 'こんにちは*****' + """ + return string.ljust(width - count_cjk_chars(string), fillbyte) class Selector: def __init__( @@ -128,7 +145,7 @@ class Selector: if current: padding += 5 - description = str(self._description).ljust(padding, ' ') + description = cjkljust(str(self._description), padding, ' ') current = current else: description = self._description -- cgit v1.2.3-70-g09d2 From f8d269092033f49b9f9621580fa1862e0c3b6cdb Mon Sep 17 00:00:00 2001 From: Himadri Bhattacharjee <107522312+lavafroth@users.noreply.github.com> Date: Mon, 31 Jul 2023 07:40:45 +0000 Subject: move preview text method for profiles into xorg superclass, override when needed (#1918) Co-authored-by: Anton Hvornum --- archinstall/default_profiles/desktops/awesome.py | 6 +----- archinstall/default_profiles/desktops/budgie.py | 4 ---- archinstall/default_profiles/desktops/cinnamon.py | 4 ---- archinstall/default_profiles/desktops/cutefish.py | 4 ---- archinstall/default_profiles/desktops/deepin.py | 4 ---- archinstall/default_profiles/desktops/enlightenment.py | 4 ---- archinstall/default_profiles/desktops/gnome.py | 4 ---- archinstall/default_profiles/desktops/hyperland.py | 4 ---- archinstall/default_profiles/desktops/i3.py | 4 ---- archinstall/default_profiles/desktops/kde.py | 4 ---- archinstall/default_profiles/desktops/lxqt.py | 4 ---- archinstall/default_profiles/desktops/mate.py | 4 ---- archinstall/default_profiles/desktops/qtile.py | 4 ---- archinstall/default_profiles/desktops/sway.py | 4 ---- archinstall/default_profiles/desktops/xfce4.py | 4 ---- archinstall/default_profiles/xorg.py | 6 +++++- 16 files changed, 6 insertions(+), 62 deletions(-) diff --git a/archinstall/default_profiles/desktops/awesome.py b/archinstall/default_profiles/desktops/awesome.py index 79e0eb71..3833ce71 100644 --- a/archinstall/default_profiles/desktops/awesome.py +++ b/archinstall/default_profiles/desktops/awesome.py @@ -1,4 +1,4 @@ -from typing import List, Optional, Any, TYPE_CHECKING +from typing import List, Any, TYPE_CHECKING from archinstall.default_profiles.profile import ProfileType from archinstall.default_profiles.xorg import XorgProfile @@ -28,10 +28,6 @@ class AwesomeProfile(XorgProfile): 'xsel', ] - def preview_text(self) -> Optional[str]: - text = str(_('Environment type: {}')).format(self.profile_type.value) - return text + '\n' + self.packages_text() - def install(self, install_session: 'Installer'): super().install(install_session) diff --git a/archinstall/default_profiles/desktops/budgie.py b/archinstall/default_profiles/desktops/budgie.py index 93be320f..68506d45 100644 --- a/archinstall/default_profiles/desktops/budgie.py +++ b/archinstall/default_profiles/desktops/budgie.py @@ -24,7 +24,3 @@ class BudgieProfile(XorgProfile): @property def default_greeter_type(self) -> Optional[GreeterType]: return GreeterType.Lightdm - - def preview_text(self) -> Optional[str]: - text = str(_('Environment type: {}')).format(self.profile_type.value) - return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/cinnamon.py b/archinstall/default_profiles/desktops/cinnamon.py index 22fd0d9d..a819b4d1 100644 --- a/archinstall/default_profiles/desktops/cinnamon.py +++ b/archinstall/default_profiles/desktops/cinnamon.py @@ -25,7 +25,3 @@ class CinnamonProfile(XorgProfile): @property def default_greeter_type(self) -> Optional[GreeterType]: return GreeterType.Lightdm - - def preview_text(self) -> Optional[str]: - text = str(_('Environment type: {}')).format(self.profile_type.value) - return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/cutefish.py b/archinstall/default_profiles/desktops/cutefish.py index 6f88c47a..c4202920 100644 --- a/archinstall/default_profiles/desktops/cutefish.py +++ b/archinstall/default_profiles/desktops/cutefish.py @@ -23,9 +23,5 @@ class CutefishProfile(XorgProfile): def default_greeter_type(self) -> Optional[GreeterType]: return GreeterType.Sddm - def preview_text(self) -> Optional[str]: - text = str(_('Environment type: {}')).format(self.profile_type.value) - return text + '\n' + self.packages_text() - def install(self, install_session: 'Installer'): super().install(install_session) diff --git a/archinstall/default_profiles/desktops/deepin.py b/archinstall/default_profiles/desktops/deepin.py index 054c8fdf..e6a9f6b5 100644 --- a/archinstall/default_profiles/desktops/deepin.py +++ b/archinstall/default_profiles/desktops/deepin.py @@ -22,7 +22,3 @@ class DeepinProfile(XorgProfile): @property def default_greeter_type(self) -> Optional[GreeterType]: return GreeterType.Lightdm - - def preview_text(self) -> Optional[str]: - text = str(_('Environment type: {}')).format(self.profile_type.value) - return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/enlightenment.py b/archinstall/default_profiles/desktops/enlightenment.py index 164f64fe..7dd7822a 100644 --- a/archinstall/default_profiles/desktops/enlightenment.py +++ b/archinstall/default_profiles/desktops/enlightenment.py @@ -21,7 +21,3 @@ class EnlighenmentProfile(XorgProfile): @property def default_greeter_type(self) -> Optional[GreeterType]: return GreeterType.Lightdm - - def preview_text(self) -> Optional[str]: - text = str(_('Environment type: {}')).format(self.profile_type.value) - return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/gnome.py b/archinstall/default_profiles/desktops/gnome.py index 3cbd49bd..24ade437 100644 --- a/archinstall/default_profiles/desktops/gnome.py +++ b/archinstall/default_profiles/desktops/gnome.py @@ -21,7 +21,3 @@ class GnomeProfile(XorgProfile): @property def default_greeter_type(self) -> Optional[GreeterType]: return GreeterType.Gdm - - def preview_text(self) -> Optional[str]: - text = str(_('Environment type: {}')).format(self.profile_type.value) - return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/hyperland.py b/archinstall/default_profiles/desktops/hyperland.py index e55dd7c4..58ee8cca 100644 --- a/archinstall/default_profiles/desktops/hyperland.py +++ b/archinstall/default_profiles/desktops/hyperland.py @@ -25,7 +25,3 @@ class HyperlandProfile(XorgProfile): @property def default_greeter_type(self) -> Optional[GreeterType]: return GreeterType.Sddm - - def preview_text(self) -> Optional[str]: - text = str(_('Environment type: {}')).format(self.profile_type.value) - return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/i3.py b/archinstall/default_profiles/desktops/i3.py index 93c38906..9c2994de 100644 --- a/archinstall/default_profiles/desktops/i3.py +++ b/archinstall/default_profiles/desktops/i3.py @@ -27,7 +27,3 @@ class I3wmProfile(XorgProfile): @property def default_greeter_type(self) -> Optional[GreeterType]: return GreeterType.Lightdm - - def preview_text(self) -> Optional[str]: - text = str(_('Environment type: {}')).format(self.profile_type.value) - return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/kde.py b/archinstall/default_profiles/desktops/kde.py index cd02e069..b65a9620 100644 --- a/archinstall/default_profiles/desktops/kde.py +++ b/archinstall/default_profiles/desktops/kde.py @@ -26,7 +26,3 @@ class KdeProfile(XorgProfile): @property def default_greeter_type(self) -> Optional[GreeterType]: return GreeterType.Sddm - - def preview_text(self) -> Optional[str]: - text = str(_('Environment type: {}')).format(self.profile_type.value) - return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/lxqt.py b/archinstall/default_profiles/desktops/lxqt.py index 60bf47f2..5d75e08d 100644 --- a/archinstall/default_profiles/desktops/lxqt.py +++ b/archinstall/default_profiles/desktops/lxqt.py @@ -29,7 +29,3 @@ class LxqtProfile(XorgProfile): @property def default_greeter_type(self) -> Optional[GreeterType]: return GreeterType.Sddm - - def preview_text(self) -> Optional[str]: - text = str(_('Environment type: {}')).format(self.profile_type.value) - return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/mate.py b/archinstall/default_profiles/desktops/mate.py index 0ddaaaab..d3c4a6e1 100644 --- a/archinstall/default_profiles/desktops/mate.py +++ b/archinstall/default_profiles/desktops/mate.py @@ -21,7 +21,3 @@ class MateProfile(XorgProfile): @property def default_greeter_type(self) -> Optional[GreeterType]: return GreeterType.Lightdm - - def preview_text(self) -> Optional[str]: - text = str(_('Environment type: {}')).format(self.profile_type.value) - return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/qtile.py b/archinstall/default_profiles/desktops/qtile.py index 66c6fa1b..96e93b1d 100644 --- a/archinstall/default_profiles/desktops/qtile.py +++ b/archinstall/default_profiles/desktops/qtile.py @@ -21,7 +21,3 @@ class QtileProfile(XorgProfile): @property def default_greeter_type(self) -> Optional[GreeterType]: return GreeterType.Lightdm - - def preview_text(self) -> Optional[str]: - text = str(_('Environment type: {}')).format(self.profile_type.value) - return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/desktops/sway.py b/archinstall/default_profiles/desktops/sway.py index 25d74a88..c757797d 100644 --- a/archinstall/default_profiles/desktops/sway.py +++ b/archinstall/default_profiles/desktops/sway.py @@ -73,9 +73,5 @@ class SwayProfile(XorgProfile): def do_on_select(self): self._ask_seat_access() - def preview_text(self) -> Optional[str]: - text = str(_('Environment type: {}')).format(self.profile_type.value) - return text + '\n' + self.packages_text() - def install(self, install_session: 'Installer'): super().install(install_session) diff --git a/archinstall/default_profiles/desktops/xfce4.py b/archinstall/default_profiles/desktops/xfce4.py index bd6c3038..a7f0a7e6 100644 --- a/archinstall/default_profiles/desktops/xfce4.py +++ b/archinstall/default_profiles/desktops/xfce4.py @@ -24,7 +24,3 @@ class Xfce4Profile(XorgProfile): @property def default_greeter_type(self) -> Optional[GreeterType]: return GreeterType.Lightdm - - def preview_text(self) -> Optional[str]: - text = str(_('Environment type: {}')).format(self.profile_type.value) - return text + '\n' + self.packages_text() diff --git a/archinstall/default_profiles/xorg.py b/archinstall/default_profiles/xorg.py index 13154818..c9abf4da 100644 --- a/archinstall/default_profiles/xorg.py +++ b/archinstall/default_profiles/xorg.py @@ -1,4 +1,4 @@ -from typing import Any, TYPE_CHECKING, List +from typing import Any, Optional, TYPE_CHECKING, List from archinstall.default_profiles.profile import Profile, ProfileType @@ -20,6 +20,10 @@ class XorgProfile(Profile): support_gfx_driver=True ) + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + return text + '\n' + self.packages_text() + @property def packages(self) -> List[str]: return [ -- cgit v1.2.3-70-g09d2 From 0852a7cacbeb5e071a6f7ba3ba0f6a639d8ec2d9 Mon Sep 17 00:00:00 2001 From: Hasan Oruc <65034033+Oruch379@users.noreply.github.com> Date: Mon, 31 Jul 2023 10:41:26 +0300 Subject: Updated and corrected translation. (#1910) * Updated and corrected translation. * Updated and corrected translation. --- archinstall/locales/tr/LC_MESSAGES/base.po | 58 +++++++++++++++--------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/archinstall/locales/tr/LC_MESSAGES/base.po b/archinstall/locales/tr/LC_MESSAGES/base.po index 8d82f48a..710ab86f 100644 --- a/archinstall/locales/tr/LC_MESSAGES/base.po +++ b/archinstall/locales/tr/LC_MESSAGES/base.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: 21.05.2022\n" "PO-Revision-Date: 2022-05-25 17:42+0300\n" -"Last-Translator: Schwarze \n" +"Last-Translator: Hasan Oruc \n" "Language-Team: \n" "Language: tr\n" "MIME-Version: 1.0\n" @@ -55,7 +55,7 @@ msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr msgstr "Sadece base, base-devel, linux, linux-firmware, efibootmgr ve tercihi profil paketleri kuruldu." msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." -msgstr "Eğer bir internet tarayıcısı arzu ediyorsanız, Firefox ya da Chromium gibi, sıra gelen ekranda belirtebilirsiniz." +msgstr "Firefox ya da Chromium gibi bir web tarayıcısı isterseniz, sıradaki ekranda belirtebilirsiniz." msgid "Write additional packages to install (space separated, leave blank to skip): " msgstr "Kurulacak ek paketleri yazınız (boşlukla ayrılmış, geçmek için boş bırakın): " @@ -122,13 +122,13 @@ msgid "" msgstr "" "{}\n" "\n" -"Dizinden hangi disk bölümünün nereye mount (monte) edileceğini seçin" +"Dizinden hangi disk bölümünün nereye mount edileceğini (bağlanacağını) seçin" msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * Disk bölümü mount (monte) noktaları iç kurulumla ilişkilidir, örnek olarak boot, /boot olacaktır." +msgstr " * Disk bölümü mount (bağlantı) noktaları iç kurulumla ilişkilidir, örnek olarak boot, /boot olacaktır." msgid "Select where to mount partition (leave blank to remove mountpoint): " -msgstr "Disk bölümünün nereye mount (monte) edileceğini seçin (mount noktasını kaldırmak için boş bırakın): " +msgstr "Disk bölümünün nereye mount edileceğini (bağlanacağını) seçin (mount (bağlantı) noktasını kaldırmak için boş bırakın): " msgid "" "{}\n" @@ -173,7 +173,7 @@ msgid "Archinstall language" msgstr "Archinstall dili" msgid "Wipe all selected drives and use a best-effort default partition layout" -msgstr "Bütün seçilmiş diskleri temizle ve elden gelen en iyi varsayılan disk bölümü düzenini kullan" +msgstr "Bütün seçilmiş diskleri temizle ve olabilecek en iyi varsayılan disk bölümü düzenini kullan" msgid "Select what to do with each individual drive (followed by partition usage)" msgstr "Her bir disk ile ne yapılacağını seçin (disk bölümü kullanımı ile takip edilir)" @@ -200,7 +200,7 @@ msgid "For the best compatibility with your Intel hardware, you may want to use msgstr "Intel donanımınızla en iyi uyumluluk için, tam açık-kaynak ya da Intel ayarlarından birini kullanmak isteyebilirsiniz.\n" msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" -msgstr "Nvidia donanımınızla en iyi uyumluluk için, Nvidia patentli sürücüyü kullanmak isteyebilirsiniz.\n" +msgstr "Nvidia donanımınızla en iyi uyumluluk için, Nvidia sahipli sürücüyü kullanmak isteyebilirsiniz.\n" msgid "" "\n" @@ -269,7 +269,7 @@ msgid "Encryption password" msgstr "Şifreleme parolası" msgid "Swap" -msgstr "Swap (değişim)" +msgstr "Swap (takas)" msgid "Bootloader" msgstr "Önyükleyici" @@ -311,7 +311,7 @@ msgid "" "Do you wish to continue?" msgstr "" "Sabit disk seçimini geçmeye karar verdiniz ve\n" -"{}'e ne mount (monte) edilmişse onu kullanacaksınız (deneysel)\n" +"{}'e ne mount edilmişse (bağlanmışsa) onu kullanacaksınız (deneysel)\n" "UYARI: Archinstall bu kurulumun uygunluğunu kontrol etmeyecek\n" "Devam etmek istiyor musunuz?" @@ -328,7 +328,7 @@ msgid "Clear/Delete all partitions" msgstr "Bütün disk bölümlerini temizle/sil" msgid "Assign mount-point for a partition" -msgstr "Bir disk bölümü için mount (monte) noktası ata" +msgstr "Bir disk bölümü için mount (bağlantı) noktası ata" msgid "Mark/Unmark a partition to be formatted (wipes data)" msgstr "Bir disk bölümünü biçimlendirilmek üzere işaretle/işareti kaldır (veriyi temizler)" @@ -340,7 +340,7 @@ msgid "Mark/Unmark a partition as bootable (automatic for /boot)" msgstr "Bir disk bölümünü önyüklenebilir olarak işaretle/işareti kaldır (/boot için otomatik)" msgid "Set desired filesystem for a partition" -msgstr "Bir disk bölümü için arzu edilen dosya sistemini ayarla" +msgstr "Bir disk bölümü için istenilen dosya sistemini ayarla" msgid "Abort" msgstr "İptal et" @@ -355,7 +355,7 @@ msgid "Timezone" msgstr "Zaman dilimi" msgid "Set/Modify the below options" -msgstr "Aşağıdaki ayarları ayarla/modifiye et" +msgstr "Aşağıdaki ayarları ayarla/değiştir" msgid "Install" msgstr "Kur" @@ -382,7 +382,7 @@ msgid "Create a required super-user with sudo privileges: " msgstr "Gerekli bir sudo yetkilerine sahip süper-kullanıcı oluşturun: " msgid "Enter root password (leave blank to disable root): " -msgstr "Root (kök) şifresi girin (root'u hizmet dışı bırakmak için boş bırakın): " +msgstr "Root (kök) şifresi girin (root'u devre dışı bırakmak için boş bırakın): " msgid "Password for user \"{}\": " msgstr "Kullanıcı \"{}\" için şifre: " @@ -391,7 +391,7 @@ msgid "Verifying that additional packages exist (this might take a few seconds)" msgstr "Ek paketlerin varlığı doğrulanıyor (bu bir kaç saniye alabilir)" msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" -msgstr "Varsayılan zaman sunucularıyla otomatik zaman eşzamanlamasını (NTP) kullanmak ister misiniz?\n" +msgstr "Varsayılan zaman sunucularıyla otomatik zaman eş zamanlamasını (NTP) kullanmak ister misiniz?\n" msgid "" "Hardware time and other post-configuration steps might be required in order for NTP to work.\n" @@ -454,13 +454,13 @@ msgid "Pre-existing pacman lock never exited. Please clean up any existing pacma msgstr "Önceden var olan pacman kilidi çıkış yapmadı. Lütfen archinstall'ı kullanmadan once mevcut olan pacman oturumlarını temizleyin." msgid "Choose which optional additional repositories to enable" -msgstr "Hangi tercihi ek repositorylerin (depoların) aktifleştirileceğini seçin" +msgstr "Hangi tercihi ek depoların (repositorylerin) aktifleştirileceğini seçin" msgid "Add a user" msgstr "Kullanıcı ekle" msgid "Change password" -msgstr "Şifren değiştir" +msgstr "Şifre değiştir" msgid "Promote/Demote user" msgstr "Kullanıcı terfi et/indirge" @@ -488,7 +488,7 @@ msgid "No network configuration" msgstr "Ağ yapılandırması yok" msgid "Set desired subvolumes on a btrfs partition" -msgstr "Btrfs disk bölümünde arzu edilen alt disk bölümülerini ayarlayın" +msgstr "Btrfs disk bölümünde istenilen alt disk bölümülerini ayarlayın" msgid "" "{}\n" @@ -527,7 +527,7 @@ msgid "Not a valid directory: {}" msgstr "Geçerli bir dizin değil: {}" msgid "The password you are using seems to be weak," -msgstr "Kullandığınız şifre zayıf gözüküyor," +msgstr "Kullandığınız şifre zayıf görünüyor," msgid "are you sure you want to use it?" msgstr "Kullanmak istediğinize emin misiniz?" @@ -564,13 +564,13 @@ msgid "" " Fill the desired values for a new subvolume \n" msgstr "" "\n" -"Yeni bir alt disk bölümü için arzu edilen değerleri doldurun\n" +"Yeni bir alt disk bölümü için istenilen değerleri doldurun\n" msgid "Subvolume name " msgstr "Alt disk bölümü ismi " msgid "Subvolume mountpoint" -msgstr "Alt disk bölümü monte noktası" +msgstr "Alt disk bölümü bağlantı noktası" msgid "Subvolume options" msgstr "Alt disk bölümü seçenekleri" @@ -582,7 +582,7 @@ msgid "Subvolume name :" msgstr "Alt disk bölümü ismi :" msgid "Select a mount point :" -msgstr "Bir monte noktası seçin :" +msgstr "Bir bağlantı noktası seçin :" msgid "Select the desired subvolume options " msgstr "İstenilen alt disk bölümü ayarlarını seçin " @@ -651,13 +651,13 @@ msgid "Mark/Unmark a partition as compressed (btrfs only)" msgstr "Bir disk bölümünü sıkıştırılmış olarak işaretle/işareti kaldır (sadece btrfs)" msgid "The password you are using seems to be weak, are you sure you want to use it?" -msgstr "Kullandığınız şifre zayıf gözüküyor, kullanmak istediğinize emin misiniz?" +msgstr "Kullandığınız şifre zayıf görünüyor, kullanmak istediğinize emin misiniz?" msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" -msgstr "Masaüstü ortamları ve otomatik hizalamalı pencere yöneticilerine bir seçenek sunar, örnek olarak gnome, kde, sway" +msgstr "Masaüstü ortamları ve otomatik döşemeli pencere yöneticilerine bir seçenek sunar, örnek olarak gnome, kde, sway" msgid "Select your desired desktop environment" -msgstr "Arzu ettiğiniz masaüstü ortamını seçin" +msgstr "İstediğiniz masaüstü ortamını seçin" msgid "A very basic installation that allows you to customize Arch Linux as you see fit." msgstr "Arch Linux'u istediğin gibi kişiselleştirmeni sağlayan çok basit bir kurulum." @@ -735,7 +735,7 @@ msgid "Value: " msgstr "Değer: " msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" -msgstr "Disk seçmeyi ve disk bölümlendirmeyi geçebilir ve disk kurulumu /mnt dizininde neye mount (monte) ediliyse onu kullanabilirsiniz (deneysel)" +msgstr "Disk seçmeyi ve disk bölümlendirmeyi geçebilir ve disk kurulumu /mnt dizininde neye mount ediliyse (bağlandıysa) onu kullanabilirsiniz (deneysel)" msgid "Select one of the disks or skip and use /mnt as default" msgstr "Disklerden birini seçin ya da geçin ve /mnt dizinini varsayılan olarak kullanın" @@ -760,7 +760,7 @@ msgstr "Bus(veri yolu)-tipi" #, fuzzy msgid "Either root-password or at least 1 user with sudo privileges must be specified" -msgstr "RKök şifresi ya da en azından 1 adet süper-kullanıcı belirtilmek zorunda" +msgstr "Kök şifresi ya da en azından 1 adet süper-kullanıcı belirtilmek zorunda" #, fuzzy msgid "Enter username (leave blank to skip): " @@ -912,7 +912,7 @@ msgid "Mark/Unmark to be formatted (wipes data)" msgstr "Bir disk bölümünü biçimlendirilmek üzere işaretle/işareti kaldır (verileri siler)" msgid "Mark/Unmark as bootable" -msgstr "Önyüklenebilir olarak İşaretle/İşareti kaldır" +msgstr "Önyüklenebilir olarak işaretle/işareti kaldır" msgid "Change filesystem" msgstr "Dosya sistemini değiştir" @@ -937,13 +937,13 @@ msgstr "Bu disk bölümü şu anda şifrelenmiştir, biçimlendirmek için bir d #, fuzzy msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * Disk bölümü mount (monte) noktaları iç kurulumla ilişkilidir, örnek olarak boot, /boot olacaktır." +msgstr " * Disk bölümü mount (bağlantı) noktaları iç kurulumla ilişkilidir, örnek olarak boot, /boot olacaktır." msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." msgstr "mountpoint /boot ayarlanmışsa, bölüm de önyüklenebilir olarak işaretlenecektir." msgid "Mountpoint: " -msgstr "Monte etme noktası: " +msgstr "Bağlantı noktası: " msgid "Current free sectors on device {}:" msgstr "{} cihazındaki mevcut boş sektörler:" -- cgit v1.2.3-70-g09d2 From f7604c81a6133c205ce46f6034907f29feda53ad Mon Sep 17 00:00:00 2001 From: Pedro Chaparro <94259578+PChaparro@users.noreply.github.com> Date: Mon, 31 Jul 2023 13:45:32 -0500 Subject: Translate: Add some missing Spanish translations (#1969) * feat(locales/es): Add some missing spanish translations * feat(locales/es): Run `locales_generator.sh` script to update the `base.mo` file Follow the `archinstall/locales/README.md` instructions to add / update languages. * feat(locales/es): Add some new missing translations * feat(locales/es): Run `locales_generator.sh` script to update the `base.mo` file --- archinstall/locales/es/LC_MESSAGES/base.mo | Bin 24396 -> 30843 bytes archinstall/locales/es/LC_MESSAGES/base.po | 162 ++++++++++++++++++++--------- 2 files changed, 115 insertions(+), 47 deletions(-) diff --git a/archinstall/locales/es/LC_MESSAGES/base.mo b/archinstall/locales/es/LC_MESSAGES/base.mo index 28b9dbe1..3bd2f6b5 100644 Binary files a/archinstall/locales/es/LC_MESSAGES/base.mo and b/archinstall/locales/es/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/es/LC_MESSAGES/base.po b/archinstall/locales/es/LC_MESSAGES/base.po index e00e0e8c..b218b772 100644 --- a/archinstall/locales/es/LC_MESSAGES/base.po +++ b/archinstall/locales/es/LC_MESSAGES/base.po @@ -97,10 +97,10 @@ msgid "Enter a desired filesystem type for the partition" msgstr "Ingrese un tipo de sistema de archivos deseado para la partición" msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " -msgstr "" +msgstr "Seleccione la ubicación de inicio (en unidades parted: s, GB, %, etc. ; predeterminado: {}):" msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " -msgstr "" +msgstr "Seleccione la ubicación de fin (en unidades parted: s, GB, %, etc. ; ej.: {}):" msgid "{} contains queued partitions, this will remove those, are you sure?" msgstr "{} contiene particiones en cola, esto eliminará esas particiones, ¿estás seguro?" @@ -766,23 +766,23 @@ msgid "Select which partitions to encrypt" msgstr "Seleccione qué particiones cifrar" msgid "very weak" -msgstr "" +msgstr "Muy débil" msgid "weak" -msgstr "" +msgstr "Débil" msgid "moderate" -msgstr "" +msgstr "Moderado" msgid "strong" -msgstr "" +msgstr "Fuerte" #, fuzzy msgid "Add subvolume" msgstr " Subvolumen :{:16}" msgid "Edit subvolume" -msgstr "" +msgstr "Editar subvolumen" #, fuzzy msgid "Delete subvolume" @@ -792,51 +792,54 @@ msgid "Configured {} interfaces" msgstr "" msgid "This option enables the number of parallel downloads that can occur during installation" -msgstr "" +msgstr "Esta opción habilita la cantidad de descargas paralelas que pueden ocurrir durante la instalación" msgid "" "Enter the number of parallel downloads to be enabled.\n" " (Enter a value between 1 to {})\n" "Note:" msgstr "" +"Introduzca el número de descargas paralelas que se habilitarán.\n" +" (Ingrese un valor entre 1 y {})\n" +"Nota:" msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" -msgstr "" +msgstr " - Valor máximo : {} ( Habilita {} descargas paralelas, permite {} descargas simultáneas )" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" -msgstr "" +msgstr " - Valor mínimo : 1 ( Habilita 1 descarga paralela, permite 2 descargas simultáneas )" msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" -msgstr "" +msgstr " - Deshabilitar/Predeterminado : 0 ( Deshabilita la descarga paralela, permite solo 1 descarga simultánea )" #, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "" +msgstr "¡Entrada no válida! Inténtelo de nuevo con un valor válido [1 a {max_downloads}, o 0 para deshabilitar]" msgid "Parallel Downloads" -msgstr "" +msgstr "Descargas paralelas" #, fuzzy msgid "ESC to skip" msgstr "Use ESC para saltar" msgid "CTRL+C to reset" -msgstr "" +msgstr "CTRL+C para restablecer" msgid "TAB to select" -msgstr "" +msgstr "TAB para seleccionar" msgid "[Default value: 0] > " -msgstr "" +msgstr "[Valor predeterminado: 0] > " msgid "To be able to use this translation, please install a font manually that supports the language." -msgstr "" +msgstr "Para poder usar esta traducción, instale manualmente una fuente que admita el idioma." msgid "The font should be stored as {}" -msgstr "" +msgstr "La fuente debe almacenarse como {}" msgid "Archinstall requires root privileges to run. See --help for more." -msgstr "" +msgstr "Archinstall requiere privilegios de root para ejecutarse. Consulte --help para más información." #, fuzzy msgid "Select an execution mode" @@ -846,7 +849,7 @@ msgid "Unable to fetch profile from specified url: {}" msgstr "" msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" -msgstr "" +msgstr "Los perfiles deben tener un nombre único, pero se encontraron definiciones de perfil con nombre duplicado: {}" #, fuzzy msgid "Select one or more devices to use and configure" @@ -897,10 +900,10 @@ msgid "Mark/Unmark to be formatted (wipes data)" msgstr "Marcar/Desmarcar una partición para ser formateada (borra los datos)" msgid "Mark/Unmark as bootable" -msgstr "" +msgstr "Marcar/Desmarcar como bootable" msgid "Change filesystem" -msgstr "" +msgstr "Cambiar el sistema de archivos" #, fuzzy msgid "Mark/Unmark as compressed" @@ -915,23 +918,23 @@ msgid "Delete partition" msgstr "Eliminar una partición" msgid "Partition" -msgstr "" +msgstr "Partición" msgid "This partition is currently encrypted, to format it a filesystem has to be specified" -msgstr "" +msgstr "Esta partición está actualmente cifrada, para formatearla se debe especificar un sistema de archivos" #, fuzzy msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." msgstr " * Los puntos de montaje de partición son relativos a la instalación, el arranque sería /boot como ejemplo." msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." -msgstr "" +msgstr "Si se establece el punto de montaje /boot, la partición también se marcará como bootable." msgid "Mountpoint: " -msgstr "" +msgstr "Punto de montaje: " msgid "Current free sectors on device {}:" -msgstr "" +msgstr "Sectores libres actuales en el dispositivo {}:" #, fuzzy msgid "Total sectors: {}" @@ -946,7 +949,7 @@ msgid "Enter the end sector of the partition (percentage or block number, defaul msgstr "Ingrese el sector final de la partición (porcentaje o número de bloque, ej: {}): " msgid "This will remove all newly added partitions, continue?" -msgstr "" +msgstr "Esto eliminará todas las particiones recientemente agregadas, ¿continuar?" msgid "Partition management: {}" msgstr "" @@ -959,10 +962,10 @@ msgid "Encryption type" msgstr "Contraseña de cifrado" msgid "Partitions" -msgstr "" +msgstr "Particiones" msgid "No HSM devices available" -msgstr "" +msgstr "No hay dispositivos HSM disponibles" #, fuzzy msgid "Partitions to be encrypted" @@ -988,19 +991,19 @@ msgid "Pre-mounted configuration" msgstr "Sin configuración" msgid "Unknown" -msgstr "" +msgstr "Desconocido" msgid "Partition encryption" -msgstr "" +msgstr "Cifrado de partición" msgid " ! Formatting {} in " -msgstr "" +msgstr " ! Formateando {} en " msgid "← Back" -msgstr "" +msgstr "← Regresar" msgid "Disk encryption" -msgstr "" +msgstr "Cifrado de disco" #, fuzzy msgid "Configuration" @@ -1015,16 +1018,16 @@ msgid "All settings will be reset, are you sure?" msgstr "{} contiene particiones en cola, esto eliminará esas particiones, ¿estás seguro?" msgid "Back" -msgstr "" +msgstr "Regresar" msgid "Please chose which greeter to install for the chosen profiles: {}" -msgstr "" +msgstr "Por favor, elija qué greeter instalar para los perfiles elegidos: {}" msgid "Environment type: {}" msgstr "" msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" -msgstr "" +msgstr "El controlador propietario de Nvidia no es compatible con Sway. Es probable que encuentre problemas, ¿Desear continuar?" #, fuzzy msgid "Installed packages" @@ -1074,22 +1077,25 @@ msgstr "" "Seleccione un controlador de gráficos o déjelo en blanco para instalar todos los controladores de código abierto" msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" -msgstr "" +msgstr "Sway necesita acceso a sus dispositivos de hardware (teclado, mouse, etc.)" msgid "" "\n" "\n" "Choose an option to give Sway access to your hardware" msgstr "" +"\n" +"\n" +"Elija una opción para darle a Sway acceso a su hardware" msgid "Graphics driver" -msgstr "" +msgstr "Controlador de gráficos" msgid "Greeter" msgstr "" msgid "Please chose which greeter to install" -msgstr "" +msgstr "Por favor, elija qué greeter instalar" msgid "This is a list of pre-programmed default_profiles" msgstr "" @@ -1103,7 +1109,7 @@ msgid "Profiles" msgstr "Perfil" msgid "Finding possible directories to save configuration files ..." -msgstr "" +msgstr "Buscando posibles directorios para guardar los archivos de configuración ..." #, fuzzy msgid "Select directory (or directories) for saving configuration files" @@ -1114,10 +1120,10 @@ msgid "Add a custom mirror" msgstr "Añadir un usuario" msgid "Change custom mirror" -msgstr "" +msgstr "Modificar servidor de réplica personalizado" msgid "Delete custom mirror" -msgstr "" +msgstr "Eliminar servidor de réplica personalizado" #, fuzzy msgid "Enter name (leave blank to skip): " @@ -1136,7 +1142,7 @@ msgid "Select signature option" msgstr "Seleccione el diseño del disco" msgid "Custom mirrors" -msgstr "" +msgstr "Servidores de réplica personalizados" msgid "Defined" msgstr "" @@ -1156,6 +1162,9 @@ msgid "" "\n" "{}" msgstr "" +"¿Desea guardar {} archivo(s) de configuración en la siguiente ubicación?\n" +"\n" +"{}" #, fuzzy msgid "Saving {} configuration files to {}" @@ -1170,10 +1179,10 @@ msgid "Mirror regions" msgstr "Región del servidor" msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr "" +msgstr " - Valor máximo : {} ( Habilita {} descargas paralelas, permite {max_downloads+1} descargas simultáneas )" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" -msgstr "" +msgstr "¡Entrada no válida! Intente nuevamente con un valor válido [1 a {}, o 0 para deshabilitar]" msgid "Locales" msgstr "" @@ -1182,6 +1191,65 @@ msgstr "" msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "Usar NetworkManager (necesario para configurar internet gráficamente en GNOME y KDE)" +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "Configuración manual" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "Esta opción habilita la cantidad de descargas paralelas que pueden ocurrir durante la instalación" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"Introduzca el número de descargas paralelas que se habilitarán.\n" +" (Ingrese un valor entre 1 y {})\n" +"Nota:" + +#, fuzzy +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - Valor máximo : {} ( Habilita {} descargas paralelas, permite {} descargas simultáneas )" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - Deshabilitar/Predeterminado : 0 ( Deshabilita la descarga paralela, permite solo 1 descarga simultánea )" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "¡Entrada no válida! Intente nuevamente con un valor válido [1 a {}, o 0 para deshabilitar]" + +msgid "Total: {} / {}" +msgstr "" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "Todos los valores ingresados pueden tener una unidad como sufijo: B, KB, KiB, MB, MiB ..." + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "Si no se proporciona ninguna unidad, el valor se interpreta como sectores" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "Introduzca el sector de inicio (porcentaje o número de bloque, predeterminado: {}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "Introduzca el sector de inicio (porcentaje o número de bloque, predeterminado: {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "Fabricante" + +msgid "Product" +msgstr "Producto" + #~ msgid "Add :" #~ msgstr "Añadir :" -- cgit v1.2.3-70-g09d2 From 81ef09c336c1fc4dfbe5a324349d95a6bb30ce6d Mon Sep 17 00:00:00 2001 From: Hugo Carvalho Date: Wed, 2 Aug 2023 11:27:47 +0100 Subject: Fix and update Portuguese translation (#1972) --- archinstall/locales/pt/LC_MESSAGES/base.mo | Bin 16336 -> 37201 bytes archinstall/locales/pt/LC_MESSAGES/base.po | 584 ++++++++++++----------------- 2 files changed, 249 insertions(+), 335 deletions(-) diff --git a/archinstall/locales/pt/LC_MESSAGES/base.mo b/archinstall/locales/pt/LC_MESSAGES/base.mo index 67a5f03b..d83a2f46 100644 Binary files a/archinstall/locales/pt/LC_MESSAGES/base.mo and b/archinstall/locales/pt/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pt/LC_MESSAGES/base.po b/archinstall/locales/pt/LC_MESSAGES/base.po index 843e9b7c..e3fa4904 100644 --- a/archinstall/locales/pt/LC_MESSAGES/base.po +++ b/archinstall/locales/pt/LC_MESSAGES/base.po @@ -1,20 +1,25 @@ +# Translators: +# Hugo Carvalho +# msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" -"Language-Team: \n" -"Language: es\n" +"Last-Translator: Hugo Carvalho \n" +"Language-Team: Portuguese\n" +"Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.0.1\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 3.3.2\n" msgid "[!] A log file has been created here: {} {}" -msgstr "[!] Um arquivo de log foi criado aqui: {} {}" +msgstr "[!] Um ficheiro de registo foi criado aqui: {} {}" msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" -msgstr " Por favor envie este problema (e arquivo) para https://github.com/archlinux/archinstall/issues" +msgstr " Submeta este problema (e ficheiro) para https://github.com/archlinux/archinstall/issues" msgid "Do you really want to abort?" msgstr "Queres mesmo abortar?" @@ -23,64 +28,64 @@ msgid "And one more time for verification: " msgstr "Uma última vez para verificação: " msgid "Would you like to use swap on zram?" -msgstr "Gostarias de usar swap em zram?" +msgstr "Gostaria de usar a swap em zram?" msgid "Desired hostname for the installation: " -msgstr "Hostname desejado para a instalação: " +msgstr "Nome do computador desejado para a instalação: " msgid "Username for required superuser with sudo privileges: " -msgstr "Nome de utilizador para o super-utilizador com privilégios sudo: " +msgstr "Nome de utilizador para o superutilizador com privilégios sudo: " msgid "Any additional users to install (leave blank for no users): " msgstr "Algum utilizador adicional para instalar (deixa em branco para nenhum utilizador): " msgid "Should this user be a superuser (sudoer)?" -msgstr "Deve este utilizador ser um super-utilizador (sudoer)?" +msgstr "Deve este utilizador ser um superutilizador (sudoer)?" msgid "Select a timezone" -msgstr "Seleciona um fuso horário" +msgstr "Selecione um fuso horário" msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?" -msgstr "Preferias usar o GRUB como bootloader ao invés de systemd-boot?" +msgstr "Gostaria de utilizar o GRUB como carregador de arranque em vez do systemd-boot?" msgid "Choose a bootloader" -msgstr "Escolhe um bootloader" +msgstr "Escolha um carregador de arranque" msgid "Choose an audio server" -msgstr "Escolhe um servidor de áudio" +msgstr "Escolha um servidor de áudio" msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." msgstr "Apenas pacotes como base, base-devel, linux, linux-firmware, efibootmgr e pacotes opcionais de perfil são instalados." msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." -msgstr "Se queres um navegador web, como firefox ou chromium, deves especificá-lo na próxima pergunta." +msgstr "Se quer um navegador web, como firefox ou chromium, deve especificá-lo na próxima pergunta." msgid "Write additional packages to install (space separated, leave blank to skip): " -msgstr "Escreve pacotes adicionais para instalar (separados por espaço, deixa em branco para não instalar nada): " +msgstr "Escreva pacotes adicionais para instalar (separados por espaço, deixar em branco para ignorar): " msgid "Copy ISO network configuration to installation" -msgstr "Copiar a configuração de rede do ISO para a instalação" +msgstr "Copiar a configuração de rede da ISO para a instalação" msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" -msgstr "Usar NetworkManager (necessário para configurar internet graficamente em GNOME e KDE)" +msgstr "Usar o NetworkManager (necessário para configurar Internet graficamente em GNOME e KDE)" msgid "Select one network interface to configure" msgstr "Selecione uma interface de rede para configurar" msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" -msgstr "Selecciona qual modo configurar para \"{}\" ou pula para usar o modo padrão \"{}\"" +msgstr "Seleccione qual o modo a configurar para \"{}\" ou ignorar para usar o modo padrão \"{}\"" msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " -msgstr "Escreve a IP e subrede para {} (exemplo: 192.168.0.5/24): " +msgstr "Inserir o IP e subrede para {} (exemplo: 192.168.0.5/24): " msgid "Enter your gateway (router) IP address or leave blank for none: " -msgstr "Escreve a tua IP de gateway (router) ou deixa em branco para nenhuma: " +msgstr "Inserir o seu IP de gateway (router) ou deixe em branco para nenhum: " msgid "Enter your DNS servers (space separated, blank for none): " -msgstr "Escreve os servidores DNS (separados por espaço, deixa em branco para nenhum): " +msgstr "Inserir os servidores DNS (separados por espaço, deixe em branco para nenhum): " msgid "Select which filesystem your main partition should use" -msgstr "Seleciona qual sistema de ficheiros a partição principal deverá usar" +msgstr "Selecionar o sistema de ficheiros que a partição principal deve utilizar" msgid "Current partition layout" msgstr "Esquema actual da partições" @@ -93,16 +98,16 @@ msgstr "" "{}" msgid "Enter a desired filesystem type for the partition" -msgstr "Escreve o tipo de sistema de ficheiros desejado para a partição" +msgstr "Inserir o tipo de sistema de ficheiros desejado para a partição" msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " -msgstr "" +msgstr "Inserir o local inicial (em unidades do parted: s, GB, %, etc. ; padrão: {}): " msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " -msgstr "" +msgstr "Inserir o local final (em unidades do parted: s, GB, %, etc. ; padrão: {}): " msgid "{} contains queued partitions, this will remove those, are you sure?" -msgstr "{} contem partições em fila, isto irá remover essas, tens a certeza?" +msgstr "{} contem partições em fila, isto irá remover essas, tem a certeza?" msgid "" "{}\n" @@ -192,13 +197,13 @@ msgid "Select one or more hard drives to use and configure" msgstr "Seleciona um ou mais discos rígidos para usar e configurar" msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." -msgstr "Para uma melhor compatibilidade com o teu hardware AMD, poderás querer usar a opção de todos os drivers open-source ou com proprietários da AMD / ATI." +msgstr "Para uma melhor compatibilidade com o teu hardware AMD, poderás querer usar a opção de todos os controladores de código-aberto ou com proprietários da AMD / ATI." msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" -msgstr "Para uma melhor compatibilidade com o teu hardware Intel, poderás querer usar a opção de todos os drivers open-source ou com proprietários da Intel.\n" +msgstr "Para uma melhor compatibilidade com o teu hardware Intel, poderás querer usar a opção de todos os controladores de código-aberto ou com proprietários da Intel.\n" msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" -msgstr "Para uma melhor compatibilidade com o teu hardware Nvidia, poderás querer usar o driver proprietário da Nvidia.\n" +msgstr "Para uma melhor compatibilidade com o teu hardware Nvidia, poderás querer usar o controlador proprietário da Nvidia.\n" msgid "" "\n" @@ -207,10 +212,10 @@ msgid "" msgstr "" "\n" "\n" -"Seleciona um driver de gráficos ou deixa em branco para instalar todos os drivers open-source" +"Seleciona um controlador de gráficos ou deixa em branco para instalar todos os controladores de código-aberto" msgid "All open-source (default)" -msgstr "Todos os drivers open-source (padrão)" +msgstr "Todos os controladores de código-aberto (padrão)" msgid "Choose which kernels to use or leave blank for default \"{}\"" msgstr "Escolhe quais kernels usar ou deixa em branco para o padrão \"{}\"" @@ -234,16 +239,16 @@ msgid "You need to enter a valid fs-type in order to continue. See `man parted` msgstr "Precisas de colocar um tipo de sistema de ficheiros valido. Consulta o `man parted` para ver as opções validas." msgid "Error: Listing profiles on URL \"{}\" resulted in:" -msgstr "Erro: Listando os perfis em URL \"{}\" resulta em:" +msgstr "Erro: a listar os perfis em URL \"{}\" resulta em:" msgid "Error: Could not decode \"{}\" result as JSON:" -msgstr "Erro: Não foi possível decodificar \"{}\" como JSON:" +msgstr "Erro: não foi possível decodificar \"{}\" como JSON:" msgid "Keyboard layout" msgstr "Esquema do teclado" msgid "Mirror region" -msgstr "Região do mirror" +msgstr "Região do espelho" msgid "Locale language" msgstr "Idioma de localização" @@ -252,15 +257,13 @@ msgid "Locale encoding" msgstr "Codificação de localização" msgid "Drive(s)" -msgstr "Discos rígidos" +msgstr "Unidade(s)" -#, fuzzy msgid "Disk layout" -msgstr "Selecionar esquema de disco" +msgstr "Esquema do disco" -#, fuzzy msgid "Encryption password" -msgstr "Define a palavra-passe de encriptação" +msgstr "Palavra-passe de encriptação" msgid "Swap" msgstr "Swap" @@ -268,15 +271,14 @@ msgstr "Swap" msgid "Bootloader" msgstr "Carregador de arranque" -#, fuzzy msgid "Root password" msgstr "Palavra-passe de root" msgid "Superuser account" -msgstr "Conta de superusuário" +msgstr "Conta de superutilizador" msgid "User account" -msgstr "Conta de usuário" +msgstr "Conta de utilizador" msgid "Profile" msgstr "Perfil" @@ -305,13 +307,13 @@ msgid "" "WARNING: Archinstall won't check the suitability of this setup\n" "Do you wish to continue?" msgstr "" -"Decidiste ignorar a seleção de disco rígido\n" +"Decidiu ignorar a seleção de disco rígido\n" "e usar qualquer configuração de disco rígido montada em {} (experimental)\n" -"ATENÇÃO: Archinstall não verificar a viabilidade desta configuração\n" -"Deseja continuar?" +"ATENÇÃO: O archinstall não verifica a viabilidade desta configuração\n" +"Quer continuar?" msgid "Re-using partition instance: {}" -msgstr "Reutilizando a instância da partição: {}" +msgstr "A reutilizar a instância da partição: {}" msgid "Create a new partition" msgstr "Criar uma nova partição" @@ -399,11 +401,8 @@ msgstr "" msgid "Enter a username to create an additional user (leave blank to skip): " msgstr "Introduzir um nome de utilizador para criar um utilizador adicional (deixar em branco para saltar): " -#, fuzzy msgid "Use ESC to skip\n" -msgstr "" -"Usa ESC para saltar\n" -"\n" +msgstr "Use ESC para ignorar\n" msgid "" "\n" @@ -415,9 +414,8 @@ msgstr "" msgid "Cancel" msgstr "Cancelar" -#, fuzzy msgid "Confirm and exit" -msgstr "Configurar e sair" +msgstr "Confirmar e sair" msgid "Add" msgstr "Adicionar" @@ -431,7 +429,6 @@ msgstr "Editar" msgid "Delete" msgstr "Eliminar" -#, fuzzy msgid "Select an action for '{}'" msgstr "Selecione uma ação para '{}'" @@ -449,27 +446,25 @@ msgstr "" "Esta é a sua configuração escolhida:" msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." -msgstr "Pacman já está a funcionar, a aguardar até 10 minutos para terminar." +msgstr "O pacman já está a funcionar, a aguardar até 10 minutos para terminar." msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." -msgstr "Pacman lock não terminou. Por favor, limpe as sessões de pacman existentes antes de usar o archinstall." +msgstr "O Pacman lock não terminou. Limpar as sessões de pacman existentes antes de usar o archinstall." msgid "Choose which optional additional repositories to enable" msgstr "Escolha quais repositórios adicionais opcionais a ativar" -#, fuzzy msgid "Add a user" -msgstr "Adicionar um utilizador" +msgstr "Adicionar utilizador" -#, fuzzy msgid "Change password" -msgstr "Introduzir nova palavra-passe" +msgstr "Alterar a palavra-passe" msgid "Promote/Demote user" msgstr "Promover/Demover utilizador" msgid "Delete User" -msgstr "Eliminar Utilizador" +msgstr "Eliminar utilizador" msgid "" "\n" @@ -479,25 +474,20 @@ msgstr "" "Definir um novo utilizador\n" msgid "User Name : " -msgstr "Nome de Utilizador : " +msgstr "Nome de utilizador : " -#, fuzzy msgid "Should {} be a superuser (sudoer)?" -msgstr "Deve {} ser um superutilizador (sudoer)?" +msgstr "{} deve ser um superutilizador (sudoer)?" -#, fuzzy msgid "Define users with sudo privilege: " msgstr "Definir utilizadores com privilégio sudo: " -#, fuzzy msgid "No network configuration" msgstr "Nenhuma configuração de rede" -#, fuzzy msgid "Set desired subvolumes on a btrfs partition" msgstr "Definir subvolumes desejados numa partição btrfs" -#, fuzzy msgid "" "{}\n" "\n" @@ -505,7 +495,7 @@ msgid "" msgstr "" "{}\n" "\n" -"Selecte qual partição definir subvolumes" +"Selecione em qual partição definir subvolumes" msgid "Manage btrfs subvolumes for current partition" msgstr "Gerir subvolumes btrfs para a partição atual" @@ -519,16 +509,14 @@ msgstr "Guardar configuração de utilizador" msgid "Save user credentials" msgstr "Guardar credenciais de utilizador" -#, fuzzy msgid "Save disk layout" -msgstr "Selecionar esquema de disco" +msgstr "Guardar esquema de disco" msgid "Save all" msgstr "Guardar tudo" -#, fuzzy msgid "Choose which configuration to save" -msgstr "Escolhe quais configurações guardar" +msgstr "Escolha qual a configuração a guardar" msgid "Enter a directory for the configuration(s) to be saved: " msgstr "Introduz um diretório para as configurações a serem guardadas: " @@ -539,13 +527,11 @@ msgstr "Não é uma diretoria válida: {}" msgid "The password you are using seems to be weak," msgstr "A palavra-passe que estás a usar parece ser fraca," -#, fuzzy msgid "are you sure you want to use it?" -msgstr "tens a certeza que quer usar?" +msgstr "tem certeza que deseja usá-la?" -#, fuzzy msgid "Optional repositories" -msgstr "Repositórios adicionais" +msgstr "Repositórios opcionais" msgid "Save configuration" msgstr "Guardar configuração" @@ -554,15 +540,13 @@ msgid "Missing configurations:\n" msgstr "Configurações em falta:\n" msgid "Either root-password or at least 1 superuser must be specified" -msgstr "É necessário especificar uma senha de root ou pelo menos 1 super-utilizador" +msgstr "É necessário especificar uma palavra-passe de root ou pelo menos 1 super-utilizador" -#, fuzzy msgid "Manage superuser accounts: " -msgstr "Gerir contas superutilizador: " +msgstr "Administrar contas de superutilizador: " -#, fuzzy msgid "Manage ordinary user accounts: " -msgstr "Gerir contas de utilizador: " +msgstr "Administrar contas de utilizador padrão: " msgid " Subvolume :{:16}" msgstr " Subvolume :{:16}" @@ -595,639 +579,569 @@ msgstr "Guardar" msgid "Subvolume name :" msgstr "Nome do subvolume :" -#, fuzzy msgid "Select a mount point :" msgstr "Selecione um ponto de montagem :" msgid "Select the desired subvolume options " msgstr "Selecione as opções desejadas do subvolume " -#, fuzzy msgid "Define users with sudo privilege, by username: " -msgstr "Define utilizadores com privilégio sudo, por nome de utilizador: " +msgstr "Defina utilizadores com privilégio sudo, por nome de utilizador: " -#, fuzzy msgid "[!] A log file has been created here: {}" msgstr "[!] Um ficheiro de registo foi criado aqui: {}" -#, fuzzy msgid "Would you like to use BTRFS subvolumes with a default structure?" -msgstr "Preferias usar o GRUB como bootloader ao invés de systemd-boot?" +msgstr "Deseja usar subvolumes BTRFS com a estrutura padrão?" -#, fuzzy msgid "Would you like to use BTRFS compression?" -msgstr "Gostarias de usar swap em zram?" +msgstr "Deseja usar a compressão BTRFS?" -#, fuzzy msgid "Would you like to create a separate partition for /home?" -msgstr "Gostarias de usar swap em zram?" +msgstr "Deseja criar uma partição separada para /home?" msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" -msgstr "" +msgstr "As unidades selecionadas não tem a capacidade mínima para sugestão automática\n" msgid "Minimum capacity for /home partition: {}GB\n" -msgstr "" +msgstr "Capacidade mínima para partição /home : {}GB\n" msgid "Minimum capacity for Arch Linux partition: {}GB" -msgstr "" +msgstr "Capacidade mínima para a partição do Arch Linux: {}GB" msgid "Continue" -msgstr "" +msgstr "Continuar" msgid "yes" -msgstr "" +msgstr "sim" msgid "no" -msgstr "" +msgstr "não" msgid "set: {}" -msgstr "" +msgstr "definir: {}" msgid "Manual configuration setting must be a list" -msgstr "" +msgstr "O ajuste de configuração manual deve ser em lista" -#, fuzzy msgid "No iface specified for manual configuration" -msgstr "Guardar configuração de utilizador" +msgstr "Nenhum iface especificado para configuração manual" msgid "Manual nic configuration with no auto DHCP requires an IP address" -msgstr "" +msgstr "A configuração manual de NIC sem DHCP automático requer um endereço IP" msgid "Add interface" -msgstr "" +msgstr "Adicionar interface" msgid "Edit interface" -msgstr "" +msgstr "Editar interface" -#, fuzzy msgid "Delete interface" -msgstr "Eliminar Utilizador" +msgstr "Deletar interface" -#, fuzzy msgid "Select interface to add" -msgstr "Selecione uma interface de rede para configurar" +msgstr "Selecione interface para adicionar" -#, fuzzy msgid "Manual configuration" -msgstr "Guardar configuração" +msgstr "Configuração manual" -#, fuzzy msgid "Mark/Unmark a partition as compressed (btrfs only)" -msgstr "Marcar/Desmarcar uma partição como encriptada" +msgstr "Marcar/desmarcar a partição como comprimida (apenas btrfs)" -#, fuzzy msgid "The password you are using seems to be weak, are you sure you want to use it?" -msgstr "A palavra-passe que estás a usar parece ser fraca," +msgstr "A palavra-passe que está a usar parece ser fraca, tem a certeza que deseja utilizá-la?" msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" -msgstr "" +msgstr "Proporciona uma seleção de ambientes gráficos e gerenciadores de janela como por exemplo gnome, kde, sway" msgid "Select your desired desktop environment" -msgstr "" +msgstr "Selecionar o ambiente gráfico desejado" msgid "A very basic installation that allows you to customize Arch Linux as you see fit." -msgstr "" +msgstr "Uma instalação bem básica que permite-lhe personalizar o Arch Linux como desejar." msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" -msgstr "" +msgstr "Proporciona uma seleção de diversos pacotes de servidor para instalar e ativar como por exemplo httpd, nginx, mariadb" msgid "Choose which servers to install, if none then a minimal installation will be done" -msgstr "" +msgstr "Escolher os servidores a instalar, se não houver nenhum, será efetuada uma instalação mínima" msgid "Installs a minimal system as well as xorg and graphics drivers." -msgstr "" +msgstr "Instala um sistema mínimo assim como xorg e controladores de vídeo." msgid "Press Enter to continue." -msgstr "" +msgstr "Prima Enter para continuar." msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?" -msgstr "" +msgstr "Deseja fazer chroot para a nova instalação e realizar configuração pós-instalação?" -#, fuzzy msgid "Are you sure you want to reset this setting?" -msgstr "tens a certeza que quer usar?" +msgstr "Tem a certeza que desejar repor esta configuração?" -#, fuzzy msgid "Select one or more hard drives to use and configure\n" -msgstr "Seleciona um ou mais discos rígidos para usar e configurar" +msgstr "Selecionar uma ou mais unidades a usar e configurar\n" msgid "Any modifications to the existing setting will reset the disk layout!" -msgstr "" +msgstr "Quaisquer modificações na configuração existente irá repor o esquema de disco!" msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" -msgstr "" +msgstr "Se repor a seleção da unidade isso também repor o esquema do disco atual. Tem a certeza?" -#, fuzzy msgid "Save and exit" -msgstr "Configurar e sair" +msgstr "Guardar e sair" -#, fuzzy msgid "" "{}\n" "contains queued partitions, this will remove those, are you sure?" -msgstr "{} contem partições em fila, isto irá remover essas, tens a certeza?" +msgstr "" +"{}\n" +"contém partições enfileiradas, isso irá removê-las, tem certeza?" -#, fuzzy msgid "No audio server" -msgstr "Escolhe um servidor de áudio" +msgstr "Sem servidor de áudio" msgid "(default)" -msgstr "" +msgstr "(por omissão)" -#, fuzzy msgid "Use ESC to skip" -msgstr "" -"Usa ESC para saltar\n" -"\n" +msgstr "Use ESC para ignorar" msgid "" "Use CTRL+C to reset current selection\n" "\n" msgstr "" +"Use CTRL+C para redefinir a seleção atual\n" +"\n" -#, fuzzy msgid "Copy to: " -msgstr "Copiar para :" +msgstr "Copiar para: " -#, fuzzy msgid "Edit: " -msgstr "Editar :" +msgstr "Editar: " msgid "Key: " -msgstr "" +msgstr "Chave: " -#, fuzzy msgid "Edit {}: " -msgstr "Editar :" +msgstr "Editar {}: " msgid "Add: " -msgstr "" +msgstr "Adicionar: " -#, fuzzy msgid "Value: " -msgstr "Valor :" +msgstr "Valor: " msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" -msgstr "" +msgstr "Pode ignorar a seleção de unidade e particionar seja lá o que estiver montado em /mnt (experimental)" msgid "Select one of the disks or skip and use /mnt as default" -msgstr "" +msgstr "Selecionar um dos discos ou ignore e use /mnt como padrão" -#, fuzzy msgid "Select which partitions to mark for formatting:" -msgstr "" -"{}\n" -"\n" -"Seleciona a partição a mascar para formatar" +msgstr "Selecionar quais partições a marcar para formatar:" msgid "Use HSM to unlock encrypted drive" -msgstr "" +msgstr "Usar HSM para desbloquear unidade encriptada" msgid "Device" -msgstr "" +msgstr "Dispositivo" msgid "Size" -msgstr "" +msgstr "Tamanho" msgid "Free space" -msgstr "" +msgstr "Espaço livre" msgid "Bus-type" -msgstr "" +msgstr "Tipo de barramento" -#, fuzzy msgid "Either root-password or at least 1 user with sudo privileges must be specified" -msgstr "É necessário especificar uma senha de root ou pelo menos 1 super-utilizador" +msgstr "Deve-se especificar uma palavra-passe de root ou pelo menos 1 utilizador com privilégios de sudo" -#, fuzzy msgid "Enter username (leave blank to skip): " -msgstr "Introduzir um nome de utilizador para criar um utilizador adicional (deixar em branco para saltar): " +msgstr "Introduza um nome de utilizador (deixe em branco para ignorar): " msgid "The username you entered is invalid. Try again" -msgstr "" +msgstr "O nome de utilizador que introduziu é inválido. Tente novamente" -#, fuzzy msgid "Should \"{}\" be a superuser (sudo)?" -msgstr "Deve {} ser um superutilizador (sudoer)?" +msgstr "\"{}\" deve ser um superutilizador (sudo)?" -#, fuzzy msgid "Select which partitions to encrypt" -msgstr "Seleciona a partição a marcar como encriptada" +msgstr "Selecionar quais as partições encriptar" msgid "very weak" -msgstr "" +msgstr "muito fraca" msgid "weak" -msgstr "" +msgstr "fraca" msgid "moderate" -msgstr "" +msgstr "moderada" msgid "strong" -msgstr "" +msgstr "forte" -#, fuzzy msgid "Add subvolume" -msgstr " Subvolume :{:16}" +msgstr "Adicionar subvolume" msgid "Edit subvolume" -msgstr "" +msgstr "Editar subvolume" -#, fuzzy msgid "Delete subvolume" -msgstr "Eliminar Utilizador" +msgstr "Eliminar subvolume" msgid "Configured {} interfaces" -msgstr "" +msgstr "{} interfaces configuradas" msgid "This option enables the number of parallel downloads that can occur during installation" -msgstr "" +msgstr "Esta opção ativa o número de descargas paralelas que podem ocorrer durante a instalação" msgid "" "Enter the number of parallel downloads to be enabled.\n" " (Enter a value between 1 to {})\n" "Note:" msgstr "" +"Insira o número de descargas paralelas a serem ativadas.\n" +" (Insira um valor entre 1 e {})\n" +"Observação:" msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" -msgstr "" +msgstr " - Valor máximo : {} ( Permite {} descargas paralelas, permite {} descargas de cada vez )" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" -msgstr "" +msgstr " - Valor mínimo : 1 ( Permite 1 descarga paralela, permite 2 descargas de cada vez )" msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" -msgstr "" +msgstr " - Desativar/Padrão : 0 ( Desativa as descargas paralelas, permite apenas 1 descarga de cada vez )" #, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "" +msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {max_downloads}, ou 0 para desativar]" msgid "Parallel Downloads" -msgstr "" +msgstr "Descargas paralelas" -#, fuzzy msgid "ESC to skip" -msgstr "" -"Usa ESC para saltar\n" -"\n" +msgstr "ESC para sair" msgid "CTRL+C to reset" -msgstr "" +msgstr "CTRL+C para reiniciar" msgid "TAB to select" -msgstr "" +msgstr "TAB para selecionar" msgid "[Default value: 0] > " -msgstr "" +msgstr "[Valor padrão: 0] > " msgid "To be able to use this translation, please install a font manually that supports the language." -msgstr "" +msgstr "Para poder usar esta tradução, instale manualmente um tipo de letra que suporte o idioma." msgid "The font should be stored as {}" -msgstr "" +msgstr "O tipo de letra deve ser armazenado como {}" msgid "Archinstall requires root privileges to run. See --help for more." -msgstr "" +msgstr "O Archinstall requer privilégios de root para ser executado. Consulte --help para mais informações." -#, fuzzy msgid "Select an execution mode" -msgstr "Selecione uma ação para '{}'" +msgstr "Selecionar um modo de execução" msgid "Unable to fetch profile from specified url: {}" -msgstr "" +msgstr "Não foi possível obter o perfil a partir do URL especificado: {}" msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" -msgstr "" +msgstr "Os perfis devem ter nomes únicos, mas foram encontradas definições de perfil com nomes duplicados: {}" -#, fuzzy msgid "Select one or more devices to use and configure" -msgstr "Seleciona um ou mais discos rígidos para usar e configurar" +msgstr "Selecionar um ou mais dispositivos a usar e configurar" msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" -msgstr "" +msgstr "Se repor a seleção do dispositivo, também irá repor o esquema atual do disco. Tem a certeza?" -#, fuzzy msgid "Existing Partitions" -msgstr "Adicionando partição...." +msgstr "Partições existentes" -#, fuzzy msgid "Select a partitioning option" -msgstr "Eliminar uma partição" +msgstr "Selecionar uma opção de particionamento" -#, fuzzy msgid "Enter the root directory of the mounted devices: " -msgstr "Introduz um diretório para as configurações a serem guardadas: " +msgstr "Inserir o diretório root dos dispositivos montados: " msgid "Minimum capacity for /home partition: {}GiB\n" -msgstr "" +msgstr "Capacidade mínima para partição /home : {}GiB\n" msgid "Minimum capacity for Arch Linux partition: {}GiB" -msgstr "" +msgstr "Capacidade mínima para a partição do Arch Linux: {}GiB" -#, fuzzy msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" -msgstr "Esta é uma lista de perfis pré-programados, podem facilitar a instalação de ambientes de trabalho" +msgstr "Esta é uma lista de perfis pré-programados, que podem por exemplo facilitar a instalação de ambientes gráficos" -#, fuzzy msgid "Current profile selection" -msgstr "Esquema actual da partições" +msgstr "Seleção de perfil atual" -#, fuzzy msgid "Remove all newly added partitions" -msgstr "Criar uma nova partição" +msgstr "Remover todas as partições recém adicionadas" -#, fuzzy msgid "Assign mountpoint" -msgstr "Atribuir um ponto de montagem para uma partição" +msgstr "Atribuir um ponto de montagem" -#, fuzzy msgid "Mark/Unmark to be formatted (wipes data)" -msgstr "Marcar/Desmarcar uma partição para ser formatada (apaga os dados)" +msgstr "Marcar/Desmarcar para ser formatada (apaga os dados)" msgid "Mark/Unmark as bootable" -msgstr "" +msgstr "Marcar/Desmarcar como partição de aranque" msgid "Change filesystem" -msgstr "" +msgstr "Alterar sistema de ficheiros" -#, fuzzy msgid "Mark/Unmark as compressed" -msgstr "Marcar/Desmarcar uma partição como encriptada" +msgstr "Marcar/Desmarcar como comprimida" -#, fuzzy msgid "Set subvolumes" -msgstr "Eliminar Utilizador" +msgstr "Definir subvolumes" -#, fuzzy msgid "Delete partition" -msgstr "Eliminar uma partição" +msgstr "Eliminar partição" msgid "Partition" -msgstr "" +msgstr "Partição" msgid "This partition is currently encrypted, to format it a filesystem has to be specified" -msgstr "" +msgstr "Esta partição está encriptada. Para formatá-la, deve ser especificado um sistema de ficheiros" -#, fuzzy msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * Os pontos de montagem das partições são relativos à dentro da instalação, o boot seria /boot por exemplo." +msgstr "Os pontos de montagem das partições são relativos aos de dentro da instalação, boot por exemplo seria /boot." msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." -msgstr "" +msgstr "Se o ponto de montagem /boot for definido, a partição também será marcada como inicializável." msgid "Mountpoint: " -msgstr "" +msgstr "Ponto de montagem: " msgid "Current free sectors on device {}:" -msgstr "" +msgstr "Atuais setores livres no dispositivo {}:" -#, fuzzy msgid "Total sectors: {}" -msgstr "Não é uma diretoria válida: {}" +msgstr "Total de setores: {}" -#, fuzzy msgid "Enter the start sector (default: {}): " -msgstr "Escreve o sector de início (percentagem ou número de bloco, padrão: {}): " +msgstr "Inserir o setor inicial (padrão: {}): " -#, fuzzy msgid "Enter the end sector of the partition (percentage or block number, default: {}): " -msgstr "Escreve o sector final da partição (percentagem ou número de bloco, ex: {}): " +msgstr "Inserir o setor final da partição (percentagem ou número de bloco, padrão: {}): " msgid "This will remove all newly added partitions, continue?" -msgstr "" +msgstr "Isto irá remover todas as partições recém adicionadas, continuar?" msgid "Partition management: {}" -msgstr "" +msgstr "Gestão de partições: {}" msgid "Total length: {}" -msgstr "" +msgstr "Tamanho total: {}" -#, fuzzy msgid "Encryption type" -msgstr "Define a palavra-passe de encriptação" +msgstr "Tipo de encriptação" msgid "Partitions" -msgstr "" +msgstr "Partições" msgid "No HSM devices available" -msgstr "" +msgstr "Nenhum dispositivo HSM disponível" -#, fuzzy msgid "Partitions to be encrypted" -msgstr "Seleciona a partição a marcar como encriptada" +msgstr "Partições a serem encriptadas" -#, fuzzy msgid "Select disk encryption option" -msgstr "Seleciona o esquema de disco" +msgstr "Selecionar a opção de encriptação de disco" msgid "Select a FIDO2 device to use for HSM" -msgstr "" +msgstr "Selecionar um dispositivo FIDO2 para usar como HSM" -#, fuzzy msgid "Use a best-effort default partition layout" -msgstr "Limpar todos os discos selecionados e usar um esquema de partições padrão de melhor desempenho" +msgstr "Usar um esquema de partição padrão de melhor desempenho" -#, fuzzy msgid "Manual Partitioning" -msgstr "Guardar configuração" +msgstr "Particionamento manual" -#, fuzzy msgid "Pre-mounted configuration" -msgstr "Nenhuma configuração" +msgstr "Configuração pré-montada" msgid "Unknown" -msgstr "" +msgstr "Desconhecido" msgid "Partition encryption" -msgstr "" +msgstr "Encriptação de partição" msgid " ! Formatting {} in " -msgstr "" +msgstr " ! A formatar {} em " msgid "← Back" -msgstr "" +msgstr "← Voltar" msgid "Disk encryption" -msgstr "" +msgstr "Encriptação de disco" -#, fuzzy msgid "Configuration" -msgstr "Nenhuma configuração" +msgstr "Configuração" -#, fuzzy msgid "Password" -msgstr "Palavra-passe de root" +msgstr "Palavra-passe" -#, fuzzy msgid "All settings will be reset, are you sure?" -msgstr "{} contem partições em fila, isto irá remover essas, tens a certeza?" +msgstr "Todas as definições serão repostas, tem a certeza?" msgid "Back" -msgstr "" +msgstr "Voltar" msgid "Please chose which greeter to install for the chosen profiles: {}" -msgstr "" +msgstr "Escolha qual o ecrã de boas vindas a instalar para os perfis escolhidos: {}" msgid "Environment type: {}" -msgstr "" +msgstr "Tipo de ambiente: {}" msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" -msgstr "" +msgstr "O controlador proprietário Nvidia não é suportado pelo Sway. É provável que encontre problemas. Está de acordo com isso?" -#, fuzzy msgid "Installed packages" -msgstr "Pacotes adicionais" +msgstr "Pacotes instalados" -#, fuzzy msgid "Add profile" -msgstr "Perfil" +msgstr "Adicionar perfil" -#, fuzzy msgid "Edit profile" -msgstr "Perfil" +msgstr "Editar perfil" -#, fuzzy msgid "Delete profile" -msgstr "Eliminar Utilizador" +msgstr "Eliminar perfil" -#, fuzzy msgid "Profile name: " -msgstr "Perfil" +msgstr "Nome do perfil: " msgid "The profile name you entered is already in use. Try again" -msgstr "" +msgstr "O nome do perfil que introduziu já está a ser utilizado. Tente novamente" -#, fuzzy msgid "Packages to be install with this profile (space separated, leave blank to skip): " -msgstr "Escreve pacotes adicionais para instalar (separados por espaço, deixa em branco para não instalar nada): " +msgstr "Pacotes a serem instalados com este perfil (separados por espaço, deixe em branco para ignorar): " -#, fuzzy msgid "Services to be enabled with this profile (space separated, leave blank to skip): " -msgstr "Escreve pacotes adicionais para instalar (separados por espaço, deixa em branco para não instalar nada): " +msgstr "Serviços a serem ativados com este perfil (separados por espaço, deixe em branco para ignorar): " msgid "Should this profile be enabled for installation?" -msgstr "" +msgstr "Este perfil deve ser ativado para instalação?" msgid "Create your own" -msgstr "" +msgstr "Crie o seu próprio" -#, fuzzy msgid "" "\n" "Select a graphics driver or leave blank to install all open-source drivers" msgstr "" "\n" "\n" -"Seleciona um driver de gráficos ou deixa em branco para instalar todos os drivers open-source" +"Selecionar um controlador gráfico ou deixe em branco para instalar todos os controladores de código aberto" msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" -msgstr "" +msgstr "O Sway precisa de acesso ao seu \"seat\" (conjunto de dispositivos de hardware, como teclado, rato, etc)" msgid "" "\n" "\n" "Choose an option to give Sway access to your hardware" msgstr "" +"\n" +"\n" +"Selecionar uma opção para permitir o acesso do Sway ao seu hardware" msgid "Graphics driver" -msgstr "" +msgstr "Controlador gráfico" msgid "Greeter" -msgstr "" +msgstr "Ecrã de boas vindas" msgid "Please chose which greeter to install" -msgstr "" +msgstr "Escolha qual o ecrã de boas vindas a instalar" msgid "This is a list of pre-programmed default_profiles" -msgstr "" +msgstr "Esta é uma lista de perfis pré-programados (default_profiles)" -#, fuzzy msgid "Disk configuration" -msgstr "Nenhuma configuração" +msgstr "Configuração do disco" -#, fuzzy msgid "Profiles" -msgstr "Perfil" +msgstr "Perfis" msgid "Finding possible directories to save configuration files ..." -msgstr "" +msgstr "A procurar possíveis diretórios para guardar os ficheiros de configuração ..." -#, fuzzy msgid "Select directory (or directories) for saving configuration files" -msgstr "Seleciona um ou mais discos rígidos para usar e configurar" +msgstr "Selecionar um ou mais diretórios para guardar ficheiros de configuração" -#, fuzzy msgid "Add a custom mirror" -msgstr "Adicionar um utilizador" +msgstr "Adicionar um espelho personalizado" msgid "Change custom mirror" -msgstr "" +msgstr "Alterar espelho personalizado" msgid "Delete custom mirror" -msgstr "" +msgstr "Eliminar espelho personalizado" -#, fuzzy msgid "Enter name (leave blank to skip): " -msgstr "Introduzir um nome de utilizador para criar um utilizador adicional (deixar em branco para saltar): " +msgstr "Inserir o nome (deixe em branco para ignorar): " -#, fuzzy msgid "Enter url (leave blank to skip): " -msgstr "Introduzir um nome de utilizador para criar um utilizador adicional (deixar em branco para saltar): " +msgstr "Insira o url (deixe em branco para ignorar): " -#, fuzzy msgid "Select signature check option" -msgstr "Seleciona o esquema de disco" +msgstr "Selecionar a opção de verificação da assinatura" -#, fuzzy msgid "Select signature option" -msgstr "Seleciona o esquema de disco" +msgstr "Selecionar a opção de assinatura" msgid "Custom mirrors" -msgstr "" +msgstr "Espelhos personalizados" msgid "Defined" -msgstr "" +msgstr "Definido" -#, fuzzy msgid "Save user configuration (including disk layout)" -msgstr "Guardar configuração de utilizador" +msgstr "Guardar configuração de utilizador (incluindo esquema do disco)" -#, fuzzy msgid "" "Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" "Save directory: " -msgstr "Introduz um diretório para as configurações a serem guardadas: " +msgstr "" +"Insira um diretório para a(s) configuração(ões) serem guardadas (conclusão de tab ativada): \n" +"Guardar diretório: " msgid "" "Do you want to save {} configuration file(s) in the following location?\n" "\n" "{}" msgstr "" +"Pretende guardar o(s) ficheiro(s) de configuração de {} na seguinte localização?\n" +"\n" +"{}" -#, fuzzy msgid "Saving {} configuration files to {}" -msgstr "Guardar configuração" +msgstr "A guardar ficheiros de configuração de {} para {}" -#, fuzzy msgid "Mirrors" -msgstr "Região do mirror" +msgstr "Espelhos" -#, fuzzy msgid "Mirror regions" -msgstr "Região do mirror" +msgstr "Regiões dos espelhos" msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr "" +msgstr " - Valor máximo : {} ( Permite {} descargas paralelas, permite {max_downloads+1} descargas de cada vez )" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" -msgstr "" +msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {}, ou 0 para desativar]" msgid "Locales" -msgstr "" +msgstr "Localidades" -#, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" -msgstr "Usar NetworkManager (necessário para configurar internet graficamente em GNOME e KDE)" +msgstr "Usar o NetworkManager (necessário para configurar a Internet graficamente no GNOME e KDE)" #~ msgid "Add :" #~ msgstr "Adicionar :" -- cgit v1.2.3-70-g09d2 From d93bf84e5928dac930ac0d2f25c4bbae7d328002 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Tue, 5 Sep 2023 21:21:08 +1000 Subject: Fix github action --- .github/workflows/python-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-build.yml b/.github/workflows/python-build.yml index f51ff887..6a3bb87b 100644 --- a/.github/workflows/python-build.yml +++ b/.github/workflows/python-build.yml @@ -11,7 +11,7 @@ jobs: image: archlinux:latest options: --privileged steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Prepare arch run: | pacman-key --init -- cgit v1.2.3-70-g09d2 From bcf1eb3e36a46117cf13fd4ce4f22b8525f3cd3e Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Tue, 5 Sep 2023 21:32:30 +1000 Subject: Update --- .github/workflows/bandit.yaml | 2 +- .github/workflows/flake8.yaml | 2 +- .github/workflows/iso-build.yaml | 2 +- .github/workflows/mypy.yaml | 2 +- .github/workflows/pytest.yaml | 2 +- .github/workflows/python-build.yml | 1 - .github/workflows/python-publish.yml | 2 +- .github/workflows/translation-check.yaml | 2 +- 8 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/bandit.yaml b/.github/workflows/bandit.yaml index 84c63348..4378e8ac 100644 --- a/.github/workflows/bandit.yaml +++ b/.github/workflows/bandit.yaml @@ -6,7 +6,7 @@ jobs: container: image: archlinux:latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: pacman --noconfirm -Syu bandit - name: Security checkup with Bandit run: bandit -r archinstall || exit 0 diff --git a/.github/workflows/flake8.yaml b/.github/workflows/flake8.yaml index a33fb07c..823be79c 100644 --- a/.github/workflows/flake8.yaml +++ b/.github/workflows/flake8.yaml @@ -6,7 +6,7 @@ jobs: container: image: archlinux:latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: pacman --noconfirm -Syu python python-pip - run: pip install --break-system-packages --upgrade pip - run: pip install --break-system-packages flake8 diff --git a/.github/workflows/iso-build.yaml b/.github/workflows/iso-build.yaml index 00e2c13f..252ff645 100644 --- a/.github/workflows/iso-build.yaml +++ b/.github/workflows/iso-build.yaml @@ -26,7 +26,7 @@ jobs: image: archlinux:latest options: --privileged steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: pwd - run: find . - run: cat /etc/os-release diff --git a/.github/workflows/mypy.yaml b/.github/workflows/mypy.yaml index ccca2b9d..90200626 100644 --- a/.github/workflows/mypy.yaml +++ b/.github/workflows/mypy.yaml @@ -6,7 +6,7 @@ jobs: container: image: archlinux:latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: pacman --noconfirm -Syu python mypy python-pip - run: pip install --break-system-packages --upgrade pip - run: pip install --break-system-packages fastapi pydantic diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index 83d2e177..a5d0cb11 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -7,7 +7,7 @@ jobs: image: archlinux:latest options: --privileged steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: pacman --noconfirm -Syu python python-pip qemu gcc - run: python -m pip install --break-system-packages --upgrade pip - run: pip install --break-system-packages pytest diff --git a/.github/workflows/python-build.yml b/.github/workflows/python-build.yml index 6a3bb87b..afb1c6bc 100644 --- a/.github/workflows/python-build.yml +++ b/.github/workflows/python-build.yml @@ -19,7 +19,6 @@ jobs: pacman --noconfirm -Sy python-pip python-pyparted python-simple-term-menu pkgconfig gcc - name: Install build dependencies run: | - python -m pip install --break-system-packages --upgrade pip pip install --break-system-packages --upgrade build twine wheel setuptools installer pip uninstall archinstall -y --break-system-packages - name: Build archinstall diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 8a5bd679..b204252e 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: diff --git a/.github/workflows/translation-check.yaml b/.github/workflows/translation-check.yaml index a646d582..43f114ac 100644 --- a/.github/workflows/translation-check.yaml +++ b/.github/workflows/translation-check.yaml @@ -12,7 +12,7 @@ jobs: container: image: archlinux:latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: pacman --noconfirm -Syu python - run: cd archinstall/locales - run: pwd -- cgit v1.2.3-70-g09d2 From be7ffbdd7e08a08b609cc622addcd59cf683b379 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Tue, 5 Sep 2023 21:38:56 +1000 Subject: Update --- .github/workflows/python-build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/python-build.yml b/.github/workflows/python-build.yml index afb1c6bc..5f870ec0 100644 --- a/.github/workflows/python-build.yml +++ b/.github/workflows/python-build.yml @@ -16,9 +16,11 @@ jobs: run: | pacman-key --init pacman --noconfirm -Sy archlinux-keyring + pacman --noconfirm -Syyu pacman --noconfirm -Sy python-pip python-pyparted python-simple-term-menu pkgconfig gcc - name: Install build dependencies run: | + python -m pip install --break-system-packages --upgrade pip pip install --break-system-packages --upgrade build twine wheel setuptools installer pip uninstall archinstall -y --break-system-packages - name: Build archinstall -- cgit v1.2.3-70-g09d2 From 3ba1a3718e3187eafdaaaab4c1ad6551408cc701 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Thu, 14 Sep 2023 19:59:32 +1000 Subject: Fix 1973 - pipewire (#2030) Co-authored-by: Daniel Girtler --- archinstall/scripts/guided.py | 6 +++--- archinstall/scripts/swiss.py | 6 +++--- examples/interactive_installation.py | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 605d2b0e..51549fa8 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -166,9 +166,6 @@ def perform_installation(mountpoint: Path): archinstall.arguments.get('profile_config', None) ) - if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '': - installation.add_additional_packages(archinstall.arguments.get('packages', None)) - if users := archinstall.arguments.get('!users', None): installation.create_users(users) @@ -178,6 +175,9 @@ def perform_installation(mountpoint: Path): else: info("No audio server will be installed") + if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '': + installation.add_additional_packages(archinstall.arguments.get('packages', None)) + if profile_config := archinstall.arguments.get('profile_config', None): profile_handler.install_profile_config(installation, profile_config) diff --git a/archinstall/scripts/swiss.py b/archinstall/scripts/swiss.py index cd532f6d..80fa0a48 100644 --- a/archinstall/scripts/swiss.py +++ b/archinstall/scripts/swiss.py @@ -230,9 +230,6 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): archinstall.arguments.get('profile_config', None) ) - if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '': - installation.add_additional_packages(archinstall.arguments.get('packages', [])) - if users := archinstall.arguments.get('!users', None): installation.create_users(users) @@ -242,6 +239,9 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): else: info("No audio server will be installed") + if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '': + installation.add_additional_packages(archinstall.arguments.get('packages', [])) + if profile_config := archinstall.arguments.get('profile_config', None): profile_handler.install_profile_config(installation, profile_config) diff --git a/examples/interactive_installation.py b/examples/interactive_installation.py index e075df9b..9eac029c 100644 --- a/examples/interactive_installation.py +++ b/examples/interactive_installation.py @@ -144,9 +144,6 @@ def perform_installation(mountpoint: Path): archinstall.arguments.get('profile_config', None) ) - if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '': - installation.add_additional_packages(archinstall.arguments.get('packages', [])) - if users := archinstall.arguments.get('!users', None): installation.create_users(users) @@ -156,6 +153,9 @@ def perform_installation(mountpoint: Path): else: info("No audio server will be installed") + if archinstall.arguments.get('packages', None) and archinstall.arguments.get('packages', None)[0] != '': + installation.add_additional_packages(archinstall.arguments.get('packages', [])) + if profile_config := archinstall.arguments.get('profile_config', None): profile.profile_handler.install_profile_config(installation, profile_config) -- cgit v1.2.3-70-g09d2 From 2252dcf9bb0f07f5f331b66395d24dce54d50a44 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Thu, 14 Sep 2023 19:03:59 +0900 Subject: Fix typo in README.md (#2050) interative -> interactive --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4bc3104e..9d5c3935 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ Therefore, Archinstall will try its best to not introduce any breaking changes e There are some examples in the `examples/` directory that should serve as a starting point. -The following is a small example of how to script your own *interative* installation: +The following is a small example of how to script your own *interactive* installation: ```python from pathlib import Path -- cgit v1.2.3-70-g09d2 From c8e0b9a4d685b941e3b406bc6f8ecfaef60e1f5f Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Thu, 14 Sep 2023 20:04:25 +1000 Subject: Default to unknown on partition types (#2037) Co-authored-by: Daniel Girtler --- archinstall/lib/disk/device_model.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index b1d07d98..8ea4e06e 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -20,6 +20,7 @@ from ..exceptions import DiskError, SysCallError from ..general import SysCommand from ..output import debug, error from ..storage import storage +from ..output import info if TYPE_CHECKING: _: Any @@ -509,13 +510,15 @@ class BDevice: class PartitionType(Enum): Boot = 'boot' Primary = 'primary' + _Unknown = 'unknown' @classmethod def get_type_from_code(cls, code: int) -> PartitionType: if code == parted.PARTITION_NORMAL: return PartitionType.Primary - - raise DiskError(f'Partition code not supported: {code}') + else: + info(f'Partition code not supported: {code}') + return PartitionType._Unknown def get_partition_code(self) -> Optional[int]: if self == PartitionType.Primary: @@ -659,9 +662,9 @@ class PartitionModification: if partition_info.btrfs_subvol_infos: mountpoint = None subvol_mods = [] - for info in partition_info.btrfs_subvol_infos: + for i in partition_info.btrfs_subvol_infos: subvol_mods.append( - SubvolumeModification.from_existing_subvol_info(info) + SubvolumeModification.from_existing_subvol_info(i) ) else: mountpoint = partition_info.mountpoints[0] if partition_info.mountpoints else None -- cgit v1.2.3-70-g09d2 From dcf3dfef57999d4e13ed83a75e52704cf44d8075 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Thu, 14 Sep 2023 20:05:05 +1000 Subject: Fix 2035 - Profile configuration (#2036) Co-authored-by: Daniel Girtler --- archinstall/lib/profile/profile_model.py | 4 +++- archinstall/lib/profile/profiles_handler.py | 33 +++++++++++++---------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/archinstall/lib/profile/profile_model.py b/archinstall/lib/profile/profile_model.py index 2b52073a..8c955733 100644 --- a/archinstall/lib/profile/profile_model.py +++ b/archinstall/lib/profile/profile_model.py @@ -27,11 +27,13 @@ class ProfileConfiguration: @classmethod def parse_arg(cls, arg: Dict[str, Any]) -> 'ProfileConfiguration': from .profiles_handler import profile_handler + + profile = profile_handler.parse_profile_config(arg['profile']) greeter = arg.get('greeter', None) gfx_driver = arg.get('gfx_driver', None) return ProfileConfiguration( - profile_handler.parse_profile_config(arg['profile']), + profile, GfxDriver(gfx_driver) if gfx_driver else None, GreeterType(greeter) if greeter else None ) diff --git a/archinstall/lib/profile/profiles_handler.py b/archinstall/lib/profile/profiles_handler.py index 74c21824..7e6af3d1 100644 --- a/archinstall/lib/profile/profiles_handler.py +++ b/archinstall/lib/profile/profiles_handler.py @@ -52,9 +52,9 @@ class ProfileHandler: def parse_profile_config(self, profile_config: Dict[str, Any]) -> Optional[Profile]: """ - Deserialize JSON configuration + Deserialize JSON configuration for profile """ - profile = None + profile: Optional[Profile] = None # the order of these is important, we want to # load all the default_profiles from url and custom @@ -97,29 +97,26 @@ class ProfileHandler: if main := profile_config.get('main', None): profile = self.get_profile_by_name(main) if main else None - valid: List[Profile] = [] + if not profile: + return None + + valid_sub_profiles: List[Profile] = [] + invalid_sub_profiles: List[str] = [] details: List[str] = profile_config.get('details', []) - if details: - valid = [] - invalid = [] + if details: for detail in filter(None, details): - if profile := self.get_profile_by_name(detail): - valid.append(profile) + if sub_profile := self.get_profile_by_name(detail): + valid_sub_profiles.append(sub_profile) else: - invalid.append(detail) + invalid_sub_profiles.append(detail) - if invalid: - info('No profile definition found: {}'.format(', '.join(invalid))) + if invalid_sub_profiles: + info('No profile definition found: {}'.format(', '.join(invalid_sub_profiles))) custom_settings = profile_config.get('custom_settings', {}) - for profile in valid: - profile.set_custom_settings( - custom_settings.get(profile.name, {}) - ) - - if profile is not None: - profile.set_current_selection(valid) + profile.set_custom_settings(custom_settings) + profile.set_current_selection(valid_sub_profiles) return profile -- cgit v1.2.3-70-g09d2 From 0258b0335e2174b932ee47159512bca56c607623 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Thu, 14 Sep 2023 20:05:53 +1000 Subject: Fix Bootloader installation (#2032) * Fix broken path * Update * Update --------- Co-authored-by: Daniel Girtler --- archinstall/lib/disk/device_handler.py | 4 +++ archinstall/lib/disk/device_model.py | 1 - archinstall/lib/installer.py | 50 ++++++++++++++++++++++------------ 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index d46275d1..7731bbc3 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -117,6 +117,10 @@ class DeviceHandler(object): return part return None + def get_parent_device_path(self, dev_path: Path) -> Path: + lsblk = get_lsblk_info(dev_path) + return Path(f'/dev/{lsblk.pkname}') + def get_uuid_for_path(self, path: Path) -> Optional[str]: partition = self.find_partition(path) return partition.partuuid if partition else None diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index 8ea4e06e..7611eda5 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -1083,7 +1083,6 @@ def get_lsblk_info(dev_path: Union[Path, str]) -> LsblkInfo: def get_all_lsblk_info() -> List[LsblkInfo]: return _fetch_lsblk_info() - def get_lsblk_by_mountpoint(mountpoint: Path, as_prefix: bool = False) -> List[LsblkInfo]: def _check(infos: List[LsblkInfo]) -> List[LsblkInfo]: devices = [] diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index ba57a001..7337fe6f 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -880,17 +880,31 @@ class Installer: self.pacman.strap('efibootmgr') # TODO: Do we need? Yes, but remove from minimal_installation() instead? try: - SysCommand(f'/usr/bin/arch-chroot {self.target} grub-install --debug --target=x86_64-efi --efi-directory={boot_partition.mountpoint} --bootloader-id=GRUB --removable', peek_output=True) + SysCommand( + f'/usr/bin/arch-chroot {self.target} grub-install ' + f'--debug ' + f'--target=x86_64-efi ' + f'--efi-directory={boot_partition.mountpoint} ' + f'--bootloader-id=GRUB ' + f'--removable', + peek_output=True + ) except SysCallError: try: - SysCommand(f'/usr/bin/arch-chroot {self.target} grub-install --debug --target=x86_64-efi --efi-directory={boot_partition.mountpoint} --bootloader-id=GRUB --removable', peek_output=True) + SysCommand( + f'/usr/bin/arch-chroot {self.target} ' + f'grub-install ' + f'--debug ' + f'--target=x86_64-efi ' + f'--efi-directory={boot_partition.mountpoint} ' + f'--bootloader-id=GRUB ' + f'--removable', + peek_output=True + ) except SysCallError as err: raise DiskError(f"Could not install GRUB to {self.target}{boot_partition.mountpoint}: {err}") else: - device = disk.device_handler.get_device_by_partition_path(boot_partition.safe_dev_path) - - if not device: - raise ValueError(f'Can not find block device: {boot_partition.safe_dev_path}') + parent_dev_path = disk.device_handler.get_parent_device_path(boot_partition.safe_dev_path) try: cmd = f'/usr/bin/arch-chroot' \ @@ -898,14 +912,17 @@ class Installer: f' grub-install' \ f' --debug' \ f' --target=i386-pc' \ - f' --recheck {device.device_info.path}' + f' --recheck {parent_dev_path}' SysCommand(cmd, peek_output=True) except SysCallError as err: raise DiskError(f"Failed to install GRUB boot on {boot_partition.dev_path}: {err}") try: - SysCommand(f'/usr/bin/arch-chroot {self.target} grub-mkconfig -o {boot_partition.mountpoint}/grub/grub.cfg') + SysCommand( + f'/usr/bin/arch-chroot {self.target} ' + f'grub-mkconfig -o {boot_partition.mountpoint}/grub/grub.cfg' + ) except SysCallError as err: raise DiskError(f"Could not configure GRUB: {err}") @@ -923,10 +940,6 @@ class Installer: # partition before the format. root_uuid = get_lsblk_info(root_partition.safe_dev_path).uuid - device = disk.device_handler.get_device_by_partition_path(boot_partition.safe_dev_path) - if not device: - raise ValueError(f'Can not find block device: {boot_partition.safe_dev_path}') - def create_pacman_hook(contents: str): HOOK_DIR = "/etc/pacman.d/hooks" SysCommand(f"/usr/bin/arch-chroot {self.target} mkdir -p {HOOK_DIR}") @@ -940,6 +953,8 @@ class Installer: f' cp' \ f' /usr/share/limine/BOOTX64.EFI' \ f' /boot/EFI/BOOT/' + + SysCommand(cmd, peek_output=True) except SysCallError as err: raise DiskError(f"Failed to install Limine BOOTX64.EFI on {boot_partition.dev_path}: {err}") @@ -957,6 +972,8 @@ When = PostTransaction Exec = /usr/bin/cp /usr/share/limine/BOOTX64.EFI /boot/EFI/BOOT/ """) else: + parent_dev_path = disk.device_handler.get_parent_device_path(boot_partition.safe_dev_path) + try: # The `limine.sys` file, contains stage 3 code. cmd = f'/usr/bin/arch-chroot' \ @@ -972,7 +989,7 @@ Exec = /usr/bin/cp /usr/share/limine/BOOTX64.EFI /boot/EFI/BOOT/ f' {self.target}' \ f' limine' \ f' bios-install' \ - f' {device.device_info.path}' + f' {parent_dev_path}' SysCommand(cmd, peek_output=True) except SysCallError as err: @@ -1062,13 +1079,10 @@ TIMEOUT=5 debug(f'Root partition is an encrypted device identifying by PARTUUID: {root_partition.partuuid}') kernel_parameters.append(f'root=PARTUUID={root_partition.partuuid} rw rootfstype={root_partition.safe_fs_type.value} {" ".join(self._kernel_params)}') - device = disk.device_handler.get_device_by_partition_path(boot_partition.safe_dev_path) - - if not device: - raise ValueError(f'Unable to find block device: {boot_partition.safe_dev_path}') + parent_dev_path = disk.device_handler.get_parent_device_path(boot_partition.safe_dev_path) cmd = f'efibootmgr ' \ - f'--disk {device.device_info.path} ' \ + f'--disk {parent_dev_path} ' \ f'--part {boot_partition.safe_dev_path} ' \ f'--create ' \ f'--label "{label}" ' \ -- cgit v1.2.3-70-g09d2 From 56567221b6984f592ad9f591be814c03d554ca2a Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Thu, 14 Sep 2023 20:06:23 +1000 Subject: Fix 1971 - manual partitioning (#2031) Co-authored-by: Daniel Girtler --- archinstall/lib/disk/device_handler.py | 31 ++++++++++++++++--------------- archinstall/lib/disk/device_model.py | 2 ++ archinstall/lib/disk/partitioning_menu.py | 2 +- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index 7731bbc3..7cd79784 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -246,7 +246,7 @@ class DeviceHandler(object): info(f'luks2 locking device: {dev_path}') luks_handler.lock() - def _validate(self, device_mod: DeviceModification): + def _validate_partitions(self, partitions: List[PartitionModification]): checks = { # verify that all partitions have a path set (which implies that they have been created) lambda x: x.dev_path is None: ValueError('When formatting, all partitions must have a path set'), @@ -257,7 +257,7 @@ class DeviceHandler(object): } for check, exc in checks.items(): - found = next(filter(check, device_mod.partitions), None) + found = next(filter(check, partitions), None) if found is not None: raise exc @@ -270,12 +270,16 @@ class DeviceHandler(object): Format can be given an overriding path, for instance /dev/null to test the formatting functionality and in essence the support for the given filesystem. """ - self._validate(device_mod) + + # don't touch existing partitions + filtered_part = [p for p in device_mod.partitions if not p.exists()] + + self._validate_partitions(filtered_part) # make sure all devices are unmounted - self._umount_all_existing(device_mod) + self._umount_all_existing(device_mod.device_path) - for part_mod in device_mod.partitions: + for part_mod in filtered_part: # partition will be encrypted if enc_conf is not None and part_mod in enc_conf.partitions: self._perform_enc_formatting( @@ -446,10 +450,10 @@ class DeviceHandler(object): return luks_handler - def _umount_all_existing(self, modification: DeviceModification): - info(f'Unmounting all partitions: {modification.device_path}') + def _umount_all_existing(self, device_path: Path): + info(f'Unmounting all existing partitions: {device_path}') - existing_partitions = self._devices[modification.device_path].partition_infos + existing_partitions = self._devices[device_path].partition_infos for partition in existing_partitions: debug(f'Unmounting: {partition.path}') @@ -476,7 +480,7 @@ class DeviceHandler(object): raise DiskError('Too many partitions on disk, MBR disks can only have 3 primary partitions') # make sure all devices are unmounted - self._umount_all_existing(modification) + self._umount_all_existing(modification.device_path) # WARNING: the entire device will be wiped and all data lost if modification.wipe: @@ -489,13 +493,10 @@ class DeviceHandler(object): info(f'Creating partitions: {modification.device_path}') - # TODO sort by delete first - - for part_mod in modification.partitions: - # don't touch existing partitions - if part_mod.exists(): - continue + # don't touch existing partitions + filtered_part = [p for p in modification.partitions if not p.exists()] + for part_mod in filtered_part: # if the entire disk got nuked then we don't have to delete # any existing partitions anymore because they're all gone already requires_delete = modification.wipe is False diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index 7611eda5..28ee3116 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -96,6 +96,7 @@ class DiskLayoutConfiguration: length=Size.parse_args(partition['length']), mount_options=partition['mount_options'], mountpoint=Path(partition['mountpoint']) if partition['mountpoint'] else None, + dev_path=Path(partition['dev_path']) if partition['dev_path'] else None, type=PartitionType(partition['type']), flags=[PartitionFlag[f] for f in partition.get('flags', [])], btrfs_subvols=SubvolumeModification.parse_args(partition.get('btrfs', [])), @@ -750,6 +751,7 @@ class PartitionModification: 'mountpoint': str(self.mountpoint) if self.mountpoint else None, 'mount_options': self.mount_options, 'flags': [f.name for f in self.flags], + 'dev_path': str(self.dev_path) if self.dev_path else None, 'btrfs': [vol.__dump__() for vol in self.btrfs_subvols] } diff --git a/archinstall/lib/disk/partitioning_menu.py b/archinstall/lib/disk/partitioning_menu.py index 4acb4e85..4c4d8fac 100644 --- a/archinstall/lib/disk/partitioning_menu.py +++ b/archinstall/lib/disk/partitioning_menu.py @@ -347,7 +347,7 @@ def manual_partitioning( manual_preset = preset menu_list = PartitioningList(prompt, device, manual_preset) - partitions = menu_list.run() + partitions: List[PartitionModification] = menu_list.run() if menu_list.is_last_choice_cancel(): return preset -- cgit v1.2.3-70-g09d2 From f18553835506417b392aa2dc2ba6e3f017bb7d3d Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Thu, 14 Sep 2023 20:56:06 +1000 Subject: Rename profile in sub-menu (#2033) Co-authored-by: Daniel Girtler --- archinstall/lib/profile/profile_menu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archinstall/lib/profile/profile_menu.py b/archinstall/lib/profile/profile_menu.py index 079a9817..d9e47190 100644 --- a/archinstall/lib/profile/profile_menu.py +++ b/archinstall/lib/profile/profile_menu.py @@ -27,7 +27,7 @@ class ProfileMenu(AbstractSubMenu): def setup_selection_menu_options(self): self._menu_options['profile'] = Selector( - _('Profile'), + _('Type'), lambda x: self._select_profile(x), display_func=lambda x: x.name if x else None, preview_func=self._preview_profile, -- cgit v1.2.3-70-g09d2 From 386fbccffb325f21ee243f9f1d5985e74b6e462e Mon Sep 17 00:00:00 2001 From: Kazunari Sasa Date: Thu, 14 Sep 2023 20:11:42 +0900 Subject: Correct the simple typo in the comment (#2042) --- archinstall/lib/installer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 7337fe6f..4e6e9603 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -600,7 +600,7 @@ class Installer: if part in self._disk_encryption.partitions: if self._disk_encryption.hsm_device: - # Required bby mkinitcpio to add support for fido2-device options + # Required by mkinitcpio to add support for fido2-device options self.pacman.strap('libfido2') if 'sd-encrypt' not in self._hooks: -- cgit v1.2.3-70-g09d2 From 9518a4bf39eb2f7da4e9d6b666e6bd75163f452a Mon Sep 17 00:00:00 2001 From: Andrei Iancu <20689335+andrewKode@users.noreply.github.com> Date: Thu, 14 Sep 2023 14:13:03 +0300 Subject: Add romanian translation to archinstall (#2034) * Add romanian translation for installer * Ad romanian translation. * Update translation --- archinstall/locales/languages.json | 2 +- archinstall/locales/ro/LC_MESSAGES/base.mo | Bin 0 -> 40055 bytes archinstall/locales/ro/LC_MESSAGES/base.po | 1191 ++++++++++++++++++++++++++++ 3 files changed, 1192 insertions(+), 1 deletion(-) create mode 100644 archinstall/locales/ro/LC_MESSAGES/base.mo create mode 100644 archinstall/locales/ro/LC_MESSAGES/base.po diff --git a/archinstall/locales/languages.json b/archinstall/locales/languages.json index 8575108c..0fb05d5e 100644 --- a/archinstall/locales/languages.json +++ b/archinstall/locales/languages.json @@ -132,7 +132,7 @@ {"abbr": "ps", "lang": "Pushto"}, {"abbr": "qu", "lang": "Quechua"}, {"abbr": "rm", "lang": "Romansh"}, - {"abbr": "ro", "lang": "Romanian"}, + {"abbr": "ro", "lang": "Romanian", "translated_lang": "Română"}, {"abbr": "rn", "lang": "Rundi"}, {"abbr": "ru", "lang": "Russian", "translated_lang": "Русский"}, {"abbr": "sg", "lang": "Sango"}, diff --git a/archinstall/locales/ro/LC_MESSAGES/base.mo b/archinstall/locales/ro/LC_MESSAGES/base.mo new file mode 100644 index 00000000..8be1d632 Binary files /dev/null and b/archinstall/locales/ro/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ro/LC_MESSAGES/base.po b/archinstall/locales/ro/LC_MESSAGES/base.po new file mode 100644 index 00000000..76b7c951 --- /dev/null +++ b/archinstall/locales/ro/LC_MESSAGES/base.po @@ -0,0 +1,1191 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: andrewKode iancu.andrei312@gmail.com\n" +"Language-Team: \n" +"Language: ro\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.3.1\n" + +msgid "[!] A log file has been created here: {} {}" +msgstr "[!] Un fișier jurnal a fost creat aici: {}" + +msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" +msgstr " Vă rog să raportați problema (și fișierul) la https://github.com/archlinux/archinstall/issues" + +msgid "Do you really want to abort?" +msgstr "Sunteți sigur(ă) că doriți să închideți?" + +msgid "And one more time for verification: " +msgstr "Încă odată pentru verificare: " + +msgid "Would you like to use swap on zram?" +msgstr "Doriți să folosiți swap pe zram?" + +msgid "Desired hostname for the installation: " +msgstr "Hostname-ul dorit pentru instalare: " + +msgid "Username for required superuser with sudo privileges: " +msgstr "Nume utilizator necesar pentru utilizatorul cu drepturi privilegiate: " + +msgid "Any additional users to install (leave blank for no users): " +msgstr "Alți utilizatori pentru adăugare (lăsați gol pentru niciunul): " + +msgid "Should this user be a superuser (sudoer)?" +msgstr "Acest utilizator ar trebui să aibă drepturi privilegiate (sudoer)?" + +msgid "Select a timezone" +msgstr "Selectați un fus orar" + +msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?" +msgstr "Doriți să folosiți GRUB ca bootloader în locul systemd-boot?" + +msgid "Choose a bootloader" +msgstr "Selectați un bootloader" + +msgid "Choose an audio server" +msgstr "Selectați un server audio" + +msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." +msgstr "Doar pachetele precum base, base-devel, linux, linux-firmware, egibootmgr și cele opționale de profil sunt instalate." + +msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." +msgstr "Dacă doriți un browser, precum firefox sau chromium, puteți să-l specificați în promptul următor." + +msgid "Write additional packages to install (space separated, leave blank to skip): " +msgstr "Scrieți pachete adiționale pe care doriți să le instalați(separare prin spațiu, lăsați liber pentru a ignora): " + +msgid "Copy ISO network configuration to installation" +msgstr "Copiați configurarea ISO prin rețea la instalare" + +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" +msgstr "Folosiți NetworkManager (necesar pentru configurarea setărilor de rețea într-un mod grafic pentru GNOME și KDE)" + +msgid "Select one network interface to configure" +msgstr "Selectați o interfață de rețea pentru configurare" + +msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" +msgstr "Selectați modul de configurare pentru \"{}\" sau folosiți modul predefinit \"{}\"" + +msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " +msgstr "Introduceți IP-ul și subrețeaua pentru {} (example: 192.168.0.5/24): " + +msgid "Enter your gateway (router) IP address or leave blank for none: " +msgstr "Introduceți IP-ul gateway-ului (router) sau lăsați liber pentru niciunul: " + +msgid "Enter your DNS servers (space separated, blank for none): " +msgstr "Introduceți serverele dumneavoastră DNS (separare prin spațiu, lăsați liber pentru a ignora): " + +msgid "Select which filesystem your main partition should use" +msgstr "Selectați sistemul de fișiere dorit pentru partiția dumneavoastră primară" + +msgid "Current partition layout" +msgstr "Schema de partiționare curentă" + +msgid "" +"Select what to do with\n" +"{}" +msgstr "" +"Selectați ce doriți să se întâmple cu\n" +"{}" + +msgid "Enter a desired filesystem type for the partition" +msgstr "Introduceți un sistem de fișiere dorit pentru partiția" + +msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " +msgstr "Introduceți sectorul de început (procentaj sau numărul block-ului, predefinit: {}): " + +msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " +msgstr "Introduceți locația finală (în unități de partiționare: s, GB, %, etc. ; ex: {}): " + +msgid "{} contains queued partitions, this will remove those, are you sure?" +msgstr "{} conține partiții în așteptare, acest proces le va șterge, doriți acest lucru?" + +msgid "" +"{}\n" +"\n" +"Select by index which partitions to delete" +msgstr "" +"{}\n" +"\n" +"Selectați pe baza indexului partițiile pentru ștergere" + +msgid "" +"{}\n" +"\n" +"Select by index which partition to mount where" +msgstr "" +"{}\n" +"\n" +"Selectați pe baza indexului partițiile pentru a fi montate și locația" + +msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Punctele de montare ale partițiilor sunt relevante pentru instalate, de exemplu partiția boot va fi /boot." + +msgid "Select where to mount partition (leave blank to remove mountpoint): " +msgstr "Selectați locul de montare a partiției (lăsați gol pentru a șterge punctul de montare): " + +msgid "" +"{}\n" +"\n" +"Select which partition to mask for formatting" +msgstr "" +"{}\n" +"\n" +"Selectați partiția care urmează să fie marcată pentru formatare" + +msgid "" +"{}\n" +"\n" +"Select which partition to mark as encrypted" +msgstr "" +"{}\n" +"\n" +"Selectați partiția care urmează să fie marcată ca fiind cripitată" + +msgid "" +"{}\n" +"\n" +"Select which partition to mark as bootable" +msgstr "" +"{}\n" +"\n" +"Selectați partiția care urmează să fie marcată ca fiind bootabilă" + +msgid "" +"{}\n" +"\n" +"Select which partition to set a filesystem on" +msgstr "" +"{}\n" +"\n" +"Selectați partiția pentru care urmează să fie setat un sistem de fișiere" + +msgid "Enter a desired filesystem type for the partition: " +msgstr "Introduceți un sistem de fișiere dorit pentru partiție: " + +msgid "Archinstall language" +msgstr "Limba pentru Archinstall" + +msgid "Wipe all selected drives and use a best-effort default partition layout" +msgstr "Șterge toate discurile selectate și folosește cea mai bună schemă de partiționare" + +msgid "Select what to do with each individual drive (followed by partition usage)" +msgstr "Selectați ce doriți să se întâmple cu fiecare disc în parte (urmat de capacitatea utilizată dorită)" + +msgid "Select what you wish to do with the selected block devices" +msgstr "Selectați ce doriți să faceți cu dispozitivele de tip bloc selectate" + +msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments" +msgstr "Aceasta este o listă de profiluri preprogramate, ar putea să ușureze instalarea unor lucruri precum spațiile de lucru" + +msgid "Select keyboard layout" +msgstr "Selectați aranjamentul tastaturii" + +msgid "Select one of the regions to download packages from" +msgstr "Selectați regiunea pentru care se vor descărca pachetele" + +msgid "Select one or more hard drives to use and configure" +msgstr "Selectați unul sau mai multe discuri dure pentru a fi utilizate și configurate" + +msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." +msgstr "Pentru cea mai bună compatibilitate cu hardware-ul dumneavoastră AMD ați putea folosi fie opțiunea All Open-Source sau AMD / ATI." + +msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" +msgstr "Pentru cea mai bună compatibilitate cu hardware-ul dumneavoastră Intel ați putea folosi fie opțiunea All Open-Source sau Intel.\n" + +msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" +msgstr "Pentru cea mai bună compatibilitate cu hardware-ul dumneavoastră Nvidia ați putea folosi opțiunea Nvidia proprietary driver.\n" + +msgid "" +"\n" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"Selectați un driver grafic sau lăsați liber pentru a instala toate driverele open-source" + +msgid "All open-source (default)" +msgstr "Toate open-source (predefinit)" + +msgid "Choose which kernels to use or leave blank for default \"{}\"" +msgstr "Alegeți nucelii care urmează să fie folosiți sau lăsați liber pentru valorarea predefinită \"{}\"" + +msgid "Choose which locale language to use" +msgstr "Alegeți limba locală pentru utilizare" + +msgid "Choose which locale encoding to use" +msgstr "Alegeți codificarea locală pentru utilizare" + +msgid "Select one of the values shown below: " +msgstr "Selecați o valoare din cele afișate mai jos: " + +msgid "Select one or more of the options below: " +msgstr "Selecați una sau mai multe opțiuni din cele afișate mai jos: " + +msgid "Adding partition...." +msgstr "Adaug partiția...." + +msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." +msgstr "Trebuie să introduceți un tip de fișiere de sistem valid pentru a continua. Vedeți 'man parted' pentru tipurile de fișiere de sistem disponibile." + +msgid "Error: Listing profiles on URL \"{}\" resulted in:" +msgstr "Eroare: Listare profiluri pe URL \"{}\" rezultate în:" + +msgid "Error: Could not decode \"{}\" result as JSON:" +msgstr "Eroare: Nu s-a putut decoda rezultatul \"{}\" ca un JSON:" + +msgid "Keyboard layout" +msgstr "Aranjament tastatură" + +msgid "Mirror region" +msgstr "Regiune de oglindire" + +msgid "Locale language" +msgstr "Limba locală" + +msgid "Locale encoding" +msgstr "Codarea locală" + +msgid "Drive(s)" +msgstr "Disc(uri)" + +msgid "Disk layout" +msgstr "Schema discului" + +msgid "Encryption password" +msgstr "Parola de criptare" + +msgid "Swap" +msgstr "Swap" + +msgid "Bootloader" +msgstr "Bootloader" + +msgid "Root password" +msgstr "Parola pentru root" + +msgid "Superuser account" +msgstr "Cont utilizator privilegiat (root)" + +msgid "User account" +msgstr "Cont utilizator" + +msgid "Profile" +msgstr "Profil" + +msgid "Audio" +msgstr "Audio" + +msgid "Kernels" +msgstr "Nuclei" + +msgid "Additional packages" +msgstr "Pachete Adiționale" + +msgid "Network configuration" +msgstr "Configurarea rețelei" + +msgid "Automatic time sync (NTP)" +msgstr "Timpul de sincronizare automate (NTP)" + +msgid "Install ({} config(s) missing)" +msgstr "Instalalare ({} lipsă configurație(ii))" + +msgid "" +"You decided to skip harddrive selection\n" +"and 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?" +msgstr "" +"Ați decis să treceți peste selectarea discurilor dure\n" +"și se va folosi orice configurație de disc montată la {} (experimental)\n" +"ATENȚIE: Archinstall nu va verifica compatibilitatea configurării\n" +"Doriți să continuați?" + +msgid "Re-using partition instance: {}" +msgstr "Reutilizez instanța partiției: {}" + +msgid "Create a new partition" +msgstr "Crează o partiție nouă" + +msgid "Delete a partition" +msgstr "Șterge o partiție" + +msgid "Clear/Delete all partitions" +msgstr "Curăță/Șterge toate partițile" + +msgid "Assign mount-point for a partition" +msgstr "Asignează un punct de montare pentru o partiție" + +msgid "Mark/Unmark a partition to be formatted (wipes data)" +msgstr "Marchează/Demarchează o partiție pentru a fi formatată (șterge toate datele)" + +msgid "Mark/Unmark a partition as encrypted" +msgstr "Marchează/Demarchează o partiție pentru a fi criptată" + +msgid "Mark/Unmark a partition as bootable (automatic for /boot)" +msgstr "Marchează/Demarchează o partiție ca fiind bootabilă (automat pentru /boot)" + +msgid "Set desired filesystem for a partition" +msgstr "Setează sistemul de fișiere dorit pentru o partiție" + +msgid "Abort" +msgstr "Anulează" + +msgid "Hostname" +msgstr "Hostname" + +msgid "Not configured, unavailable unless setup manually" +msgstr "Nu este configurat, indisponibil, doar configurat manual" + +msgid "Timezone" +msgstr "Fus orar" + +msgid "Set/Modify the below options" +msgstr "Setează/Modifică opțiunile de mai jos" + +msgid "Install" +msgstr "Instalează" + +msgid "" +"Use ESC to skip\n" +"\n" +msgstr "" +"Folosiți ESC pentru a ignora\n" +"\n" + +msgid "Suggest partition layout" +msgstr "Sugerează o schemă de partiționare" + +msgid "Enter a password: " +msgstr "Introduceți o parolă: " + +msgid "Enter a encryption password for {}" +msgstr "Introduceți o parolă de criptare pentru {}" + +msgid "Enter disk encryption password (leave blank for no encryption): " +msgstr "Introduceți parola de criptare a discului (lăsați liber pentru a evita criptarea): " + +msgid "Create a required super-user with sudo privileges: " +msgstr "Crează un utilizator cu privilegii (root): " + +msgid "Enter root password (leave blank to disable root): " +msgstr "Introuceți parola pentru root (lăsați liber pentru a dezactiva utilizatorul root): " + +msgid "Password for user \"{}\": " +msgstr "Parola pentru utilizatorul \"{}\": " + +msgid "Verifying that additional packages exist (this might take a few seconds)" +msgstr "Verific dacă pachetele adiționale există (acest proces ar putea dura câteva secunde)" + +msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" +msgstr "Doriți să utilizați sincronizarea timpului automat (NTP) cu serverele de timp predefinite?\n" + +msgid "" +"Hardware time and other post-configuration steps might be required in order for NTP to work.\n" +"For more information, please check the Arch wiki" +msgstr "" +"Timpul pentru hardware alți pași după configurare ar putea fi necesari pentru ca NTP să funcționeze.\n" +"Pentru mai multe informații verificații documentația Arch (Arch Wiki)" + +msgid "Enter a username to create an additional user (leave blank to skip): " +msgstr "Introduceți un nume utilizator pentru a crea un user adițional (lăsați liber pentru a ignora): " + +msgid "Use ESC to skip\n" +msgstr "Foloți ESC pentru a ignora\n" + +msgid "" +"\n" +" Choose an object from the list, and select one of the available actions for it to execute" +msgstr "" +"\n" +" Alegeți un obiect din listă și selectați o acțiune disponibilă pentru a fi executată" + +msgid "Cancel" +msgstr "Opriți" + +msgid "Confirm and exit" +msgstr "Confirmă și ieși" + +msgid "Add" +msgstr "Adaugă" + +msgid "Copy" +msgstr "Copiază" + +msgid "Edit" +msgstr "Editează" + +msgid "Delete" +msgstr "Șterge" + +msgid "Select an action for '{}'" +msgstr "Selectează o acțiune pentru '{}'" + +msgid "Copy to new key:" +msgstr "Copiază la noua cheie:" + +msgid "Unknown nic type: {}. Possible values are {}" +msgstr "Tip necunoscut: {}. Valorile acceptate sunt {}" + +msgid "" +"\n" +"This is your chosen configuration:" +msgstr "" +"\n" +"Aceasta este configurația selectată de către dumneavoastră:" + +msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." +msgstr "Pacman rulează deja, aștept maxim 10 minute pentru a termina." + +msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." +msgstr "Procesul pacman anterior nu s-a terminat corect. Vă rog, ștergeți orice sesiune de pacman existentă înainte de a continuea cu archinstall." + +msgid "Choose which optional additional repositories to enable" +msgstr "Alegeți ce colecție opțională de pachete să fie activată" + +msgid "Add a user" +msgstr "Adaugă un utilizator" + +msgid "Change password" +msgstr "Schimbă parola" + +msgid "Promote/Demote user" +msgstr "Promovează/Retrogradează un utilizator" + +msgid "Delete User" +msgstr "Șterge utilizatorul" + +msgid "" +"\n" +"Define a new user\n" +msgstr "" +"\n" +"Definește un utilizator nou\n" + +msgid "User Name : " +msgstr "Nume utilizator : " + +msgid "Should {} be a superuser (sudoer)?" +msgstr "Ar trebui ca {} să fie un utilizator cu privilegii (sudoer)?" + +msgid "Define users with sudo privilege: " +msgstr "Definiți utilizatorii cu privilegii sudo: " + +msgid "No network configuration" +msgstr "Nu există o configurare de rețea" + +msgid "Set desired subvolumes on a btrfs partition" +msgstr "Setați subvolumele dorite pe o partiție btrfs" + +msgid "" +"{}\n" +"\n" +"Select which partition to set subvolumes on" +msgstr "" +"{}\n" +"\n" +"Selectați partiția pentru care se vor seta subvolumele" + +msgid "Manage btrfs subvolumes for current partition" +msgstr "Administrează subvolumele btrfs pentru partiția curentă" + +msgid "No configuration" +msgstr "Nu există configurație" + +msgid "Save user configuration" +msgstr "Salvează configurația utilizatorului" + +msgid "Save user credentials" +msgstr "Salvează credențialele utilizatorului" + +msgid "Save disk layout" +msgstr "Salvează schema discului" + +msgid "Save all" +msgstr "Salvați tot" + +msgid "Choose which configuration to save" +msgstr "Alegeți configurația dorită pentru salvare" + +msgid "Enter a directory for the configuration(s) to be saved: " +msgstr "Introduceți un director pentru salvarea configurațiilor: " + +msgid "Not a valid directory: {}" +msgstr "Nu este un director valid: {}" + +msgid "The password you are using seems to be weak," +msgstr "Parola pe care doriți să o folosiți pare să fie slabă," + +msgid "are you sure you want to use it?" +msgstr "sigur doriți să o folosiți?" + +msgid "Optional repositories" +msgstr "Colecții de pachete opționale" + +msgid "Save configuration" +msgstr "Salvează configurația" + +msgid "Missing configurations:\n" +msgstr "Configurații lipsă:\n" + +msgid "Either root-password or at least 1 superuser must be specified" +msgstr "Este necesar ca parola pentru utilizatorul root sau măcar un utilizator cu privilegii să fie adăugat" + +msgid "Manage superuser accounts: " +msgstr "Administrează conturile pentru utilizatorii cu privilegii: " + +msgid "Manage ordinary user accounts: " +msgstr "Adminstrează conturile pentru utilizatorii obișnuiți: " + +msgid " Subvolume :{:16}" +msgstr " Subvolum :{:16}" + +msgid " mounted at {:16}" +msgstr " montat la {:16}" + +msgid " with option {}" +msgstr " cu opțiunea {}" + +msgid "" +"\n" +" Fill the desired values for a new subvolume \n" +msgstr "" +"\n" +" Completați valorile dorite pentru un subvolum nou \n" + +msgid "Subvolume name " +msgstr "Nume subvolum " + +msgid "Subvolume mountpoint" +msgstr "Punct de montare subvolum" + +msgid "Subvolume options" +msgstr "Opțiuni subvolum" + +msgid "Save" +msgstr "Salvează" + +msgid "Subvolume name :" +msgstr "Nume subvolum :" + +msgid "Select a mount point :" +msgstr "Seleactați un punct de montare :" + +msgid "Select the desired subvolume options " +msgstr "Selectați opțiunile dorite pentru subvolum " + +msgid "Define users with sudo privilege, by username: " +msgstr "Definiți utilizatorii cu privilegii sudo, după nume utilizator: " + +msgid "[!] A log file has been created here: {}" +msgstr "[!] Un fișier de jurnal a fost creat aici: {}" + +msgid "Would you like to use BTRFS subvolumes with a default structure?" +msgstr "Doriți să folosiți subvolume BTRFS cu o structură predefinită?" + +msgid "Would you like to use BTRFS compression?" +msgstr "Doriți să folosiți compresie pentru BTRFS?" + +msgid "Would you like to create a separate partition for /home?" +msgstr "Doriți să creați o partiție separată pentru /home?" + +msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" +msgstr "Dispozitivele de stocare selectare nu au o capacitate minimă necesară pentru o sugestie automată\n" + +msgid "Minimum capacity for /home partition: {}GB\n" +msgstr "Capacitatea minimă pentru partiția /home: {}GB\n" + +msgid "Minimum capacity for Arch Linux partition: {}GB" +msgstr "Capacitatea minimă pentru partiția Arch Linux: {}GB" + +msgid "Continue" +msgstr "Continuă" + +msgid "yes" +msgstr "da" + +msgid "no" +msgstr "nu" + +msgid "set: {}" +msgstr "setează: {}" + +msgid "Manual configuration setting must be a list" +msgstr "Configurație manuală trebuie să fie o listă" + +msgid "No iface specified for manual configuration" +msgstr "Nu a fost menționat iface pentru configurarea manuală" + +msgid "Manual nic configuration with no auto DHCP requires an IP address" +msgstr "Configurarea manuală nic fără un DHCP necesită o adresă IP" + +msgid "Add interface" +msgstr "Adaugă interfață" + +msgid "Edit interface" +msgstr "Editează interfața" + +msgid "Delete interface" +msgstr "Șterge interfața" + +msgid "Select interface to add" +msgstr "Selectează interfața pentru adăugare" + +msgid "Manual configuration" +msgstr "Configurare manuală" + +msgid "Mark/Unmark a partition as compressed (btrfs only)" +msgstr "Marchează/Demarchează o partiție ca fiind comprimată (doar btrfs)" + +msgid "The password you are using seems to be weak, are you sure you want to use it?" +msgstr "Parola utilizată pare să fie slabă, sigur doriți să o folosiți pe aceasta?" + +msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" +msgstr "Oferă o selecție de medii de lucru și manageri de ferestre precum GNOME, KDE, Sway" + +msgid "Select your desired desktop environment" +msgstr "Selectați mediul de lucru dorit" + +msgid "A very basic installation that allows you to customize Arch Linux as you see fit." +msgstr "O instalare de bază care vă permite să instalați Arch Linux așa cum vă doriți." + +msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" +msgstr "Oferă o selecție de diferite pachete de sistem pentru instalare și activare precum httpd, nginx, mariadb" + +msgid "Choose which servers to install, if none then a minimal installation will be done" +msgstr "Alegeți serverele dorite pentru instalare, dacă nu este selectat niciunul se va realiza o instalare minimală" + +msgid "Installs a minimal system as well as xorg and graphics drivers." +msgstr "Instalează un sistem minimal împreună cu xorg și driverele grafice." + +msgid "Press Enter to continue." +msgstr "Apăsați Enter pentru a continua." + +msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?" +msgstr "Doriți să folosiți modul chroot pentru a intra în noua instalare și pentru a efectua configurații de post-instalare?" + +msgid "Are you sure you want to reset this setting?" +msgstr "Sigur doriți să resetați această setare?" + +msgid "Select one or more hard drives to use and configure\n" +msgstr "Selectați unul sau mai multe discuri dure pentru a utiliza și configura\n" + +msgid "Any modifications to the existing setting will reset the disk layout!" +msgstr "Orice modificare aplicată la setările existene vor resta schema de partiționare a discului!" + +msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" +msgstr "Dacă resetați selecția de discuri de stocare acest lucru va rezulta în resetarea schemei de partiționare existente a discului. Sigur doriți acest lucru?" + +msgid "Save and exit" +msgstr "Salvează și ieși" + +msgid "" +"{}\n" +"contains queued partitions, this will remove those, are you sure?" +msgstr "" +"{}\n" +"conține partiții în așteptare, acest lucru le va șterge, sigur doriți acest lucru?" + +msgid "No audio server" +msgstr "Nu există un server audio" + +msgid "(default)" +msgstr "(predefinit)" + +msgid "Use ESC to skip" +msgstr "Folosiți ESC pentru a ignora" + +msgid "" +"Use CTRL+C to reset current selection\n" +"\n" +msgstr "" +"Folosi'i CTRL+C pentru a reseta selecția curentă\n" +"\n" + +msgid "Copy to: " +msgstr "Copiază la: " + +msgid "Edit: " +msgstr "Editează: " + +msgid "Key: " +msgstr "Cheie: " + +msgid "Edit {}: " +msgstr "Editează {}: " + +msgid "Add: " +msgstr "Adaugă: " + +msgid "Value: " +msgstr "Valoare: " + +msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" +msgstr "Puteți trece peste selectarea unui dispozitiv de stocare și folosiți orice configurație de partiționare este montată la /mnt (experimental)" + +msgid "Select one of the disks or skip and use /mnt as default" +msgstr "Selectați unul din discuri sau ignorați și folosiți /mnt ca predefinit" + +msgid "Select which partitions to mark for formatting:" +msgstr "Selectați partițiile pentru a fi marcate pentru formatare:" + +msgid "Use HSM to unlock encrypted drive" +msgstr "Folosiți HSM pentru deblocarea dispozitivului criptat" + +msgid "Device" +msgstr "Discpozitiv" + +msgid "Size" +msgstr "Mărime" + +msgid "Free space" +msgstr "Spațiu liber" + +msgid "Bus-type" +msgstr "Tip magistrală" + +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "O parolă pentru utilizatorul root sau măcar un utilizator cu privilegii sudo trebuie adăugat" + +msgid "Enter username (leave blank to skip): " +msgstr "Introduceți numele de utilizator (lăsați gol pentru a ignora): " + +msgid "The username you entered is invalid. Try again" +msgstr "Numele de utilizator adăugat este invalid. Încercați din nou" + +msgid "Should \"{}\" be a superuser (sudo)?" +msgstr "Ar trebui ca \"{}\" să fie un utilizator cu privilegii (sudo)?" + +msgid "Select which partitions to encrypt" +msgstr "Selectați partițiile pentru criptare" + +msgid "very weak" +msgstr "foarte slabă" + +msgid "weak" +msgstr "slabă" + +msgid "moderate" +msgstr "moderată" + +msgid "strong" +msgstr "puetrnică" + +msgid "Add subvolume" +msgstr "Adaugă subvolum" + +msgid "Edit subvolume" +msgstr "Editează subvolum" + +msgid "Delete subvolume" +msgstr "Șterge subvolum" + +msgid "Configured {} interfaces" +msgstr "Configurează interfața {}" + +msgid "This option enables the number of parallel downloads that can occur during installation" +msgstr "Această opținea setează numărul de descărcări paralele care pot avea loc în timpul instalării" + +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +" (Enter a value between 1 to {})\n" +"Note:" +msgstr "" +"Introduceți numărul de descărcări simultane pentru a fi activat.\n" +" (Introduceți o valuare între 1 și {max_downloads})\n" +"Info:" + +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - Valoare minimă : 1 ( Permite o descărcare simultană, permite 2 descărcări simultane )" + +msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" +msgstr " - Valoare minimă : 1 ( Permite o descărcare simultană, permite 2 descărcări simultane )" + +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" +msgstr " - Dezactivează/Predefinit: 0 (Dezactivează descărcările simultane, permite o singură descărcare simultană )" + +#, python-brace-format +msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" +msgstr "Input invalid! Încercați din nou cu un input valid [1 pentru {max_downloads}, sau 0 pentru dezactivare]" + +msgid "Parallel Downloads" +msgstr "Descărcări Simultane" + +msgid "ESC to skip" +msgstr "ESC pentru ignorare" + +msgid "CTRL+C to reset" +msgstr "CTRL+C pentru resetare" + +msgid "TAB to select" +msgstr "TAB pentru a selecta" + +msgid "[Default value: 0] > " +msgstr "[Valoare predefinită: 0] > " + +msgid "To be able to use this translation, please install a font manually that supports the language." +msgstr "Pentru a putea folosi această traducere, vă rog să instalați manual un font compatibil cu această limbă." + +msgid "The font should be stored as {}" +msgstr "Fontul ar trebui salvat ca {}" + +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "Archinstall necesită privilegii avansate pentru execuție. Vedeți --help pentru mai multe." + +msgid "Select an execution mode" +msgstr "Selectați un mod de execuție" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "Nu se poate prelua profilul de la URL-ul specificat: {}" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "Profilurile trebuie să aibă nume unicate, dar definiții de profil cu nume duplicate au fost găsite: {}" + +msgid "Select one or more devices to use and configure" +msgstr "Selectați unul sau mai multe discuri dure pentru a fi utilizate și configurate" + +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "Dacă resetați selecția de discuri de stocare acest lucru va rezulta în resetarea schemei de partiționare existente a discului. Sigur doriți acest lucru?" + +msgid "Existing Partitions" +msgstr "Partiții Existente" + +msgid "Select a partitioning option" +msgstr "Selectați o opțiune de partiționare" + +msgid "Enter the root directory of the mounted devices: " +msgstr "Introduceți directorul rădăcină a dispozitivelor montate: " + +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "Capacitatea minimă pentru partiția /home: {}GB\n" + +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Capacitatea minimă pentru partiția Arch Linux: {}GB" + +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "Aceasta este o listă de profiluri pre-programate \"profiluris_bck\", ar putea să ușureze instalarea unor lucruri precum spațiile de lucru" + +msgid "Current profile selection" +msgstr "Selecția curentă a profilului" + +msgid "Remove all newly added partitions" +msgstr "Șterge toate partițiile noi adăugate" + +msgid "Assign mountpoint" +msgstr "Asignează punctul de montare" + +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Marchează/Demarchează pentru formatare (șterge toate datele)" + +msgid "Mark/Unmark as bootable" +msgstr "Marchează/Demarchează ca bootabil" + +msgid "Change filesystem" +msgstr "Schimbă sistemul de fișiere" + +msgid "Mark/Unmark as compressed" +msgstr "Marchează/Demarchează ca comprimat" + +msgid "Set subvolumes" +msgstr "Setează subvolumele" + +msgid "Delete partition" +msgstr "Șterge partiția" + +msgid "Partition" +msgstr "Partiție" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "Partiția curentă este criptată, pentru a fi formatată un sistem de fișiere trebuie specificat" + +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr "Punctele de montare ale partițiilor sunt relevante pentru instalate, de exemplu partiția boot va fi /boot." + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "Dacă punctul de montare /boot este setat, atunci partiția va fi marcată ca fiind bootabilă." + +msgid "Mountpoint: " +msgstr "Punct de montare: " + +msgid "Current free sectors on device {}:" +msgstr "Sectoarele libere curente de pe dispozitiv {}:" + +msgid "Total sectors: {}" +msgstr "Toate sectoarele: {}" + +msgid "Enter the start sector (default: {}): " +msgstr "Introduceți sectorul de început (predefinit: {}): " + +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Introduceți sectorul de final pentru partiționare (procentaj sau numărul block-ului, predefinit: {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "Această acțiune va șterge toate partițiile noi adăugate, continuați?" + +msgid "Partition management: {}" +msgstr "Managementul partiției: {}" + +msgid "Total length: {}" +msgstr "Lungime totală: {}" + +msgid "Encryption type" +msgstr "Tipul de criptare" + +msgid "Partitions" +msgstr "Partiții" + +msgid "No HSM devices available" +msgstr "Nicun dispozitiv HSM disponibil" + +msgid "Partitions to be encrypted" +msgstr "Selectați partițiile pentru criptare" + +msgid "Select disk encryption option" +msgstr "Selectați opțiunea pentru criptarea discului" + +msgid "Select a FIDO2 device to use for HSM" +msgstr "Selectați utilizarea unui device FID02 pentru HSM" + +msgid "Use a best-effort default partition layout" +msgstr "Folosește cea mai bună configurație de partiționare" + +msgid "Manual Partitioning" +msgstr "Partiționare manuală" + +msgid "Pre-mounted configuration" +msgstr "Configurație pre-montată" + +msgid "Unknown" +msgstr "Neștiut" + +msgid "Partition encryption" +msgstr "Criptare partiție" + +msgid " ! Formatting {} in " +msgstr " ! Se formatează {} in " + +msgid "← Back" +msgstr "← Înapoi" + +msgid "Disk encryption" +msgstr "Criptarea discului" + +msgid "Configuration" +msgstr "Configurație" + +msgid "Password" +msgstr "Parola" + +msgid "All settings will be reset, are you sure?" +msgstr "{} conține partiții în așteptare, acest proces le va șterge, doriți acest lucru?" + +msgid "Back" +msgstr "Înapoi" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "Vă rog selectați un manager de afișare pentru profilurile selectate: {}" + +msgid "Environment type: {}" +msgstr "Tip mediu: {}" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "Driverul proprietar Nvidia nu este compatibil cu Sway. Se poate să aveți probleme, sunteți ok cu asta?" + +msgid "Installed packages" +msgstr "Pachete instalate" + +msgid "Add profile" +msgstr "Adaugă profil" + +msgid "Edit profile" +msgstr "Editează profil" + +msgid "Delete profile" +msgstr "Șterge profil" + +msgid "Profile name: " +msgstr "Nume profil: " + +msgid "The profile name you entered is already in use. Try again" +msgstr "Numele de utilizator adăugat este invalid. Încercați din nou" + +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Pachetele care se instalează cu acest profil (separare prin spațiu, lăsați liber pentru a ignora): " + +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Serviciile activate cu acest profil (separare prin spațiu, lăsați liber pentru a ignora): " + +msgid "Should this profile be enabled for installation?" +msgstr "Ar trebui ca acest profil să fie bifat pentru instalare?" + +msgid "Create your own" +msgstr "Crează-ți propriul tău" + +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"Selectați un driver grafic sau lăsați liber pentru a instala toate driverele open-source" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "Sway are nevoie de acces la setup-ul dumneavoastră (colecție de dispozitive hardware ex. tastatura, mouse, etc.)" + +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" +"\n" +"\n" +"Alegeți o opțiune pentru a acorda acces lui Sway la hardware" + +msgid "Graphics driver" +msgstr "Driver grafic" + +msgid "Greeter" +msgstr "Manager afișare" + +msgid "Please chose which greeter to install" +msgstr "Vă rog selectați managerul de afișare pentru instalare" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "Aceasta este o listă cu profiluri pre-programate" + +msgid "Disk configuration" +msgstr "Configurație disc" + +msgid "Profiles" +msgstr "Profiluri" + +msgid "Finding possible directories to save configuration files ..." +msgstr "Caut directoare pentru salvarea fișierelor de configurare ..." + +msgid "Select directory (or directories) for saving configuration files" +msgstr "Selectați directorul (sau directoarele), pentru salvarea fișierelor de configurare" + +msgid "Add a custom mirror" +msgstr "Adaugă o oglindă customizată" + +msgid "Change custom mirror" +msgstr "Schimbă oglinda customizată" + +msgid "Delete custom mirror" +msgstr "Șterge oglinda customizată" + +msgid "Enter name (leave blank to skip): " +msgstr "Introduceți numele (lăsați gol pentru a ignora): " + +msgid "Enter url (leave blank to skip): " +msgstr "Introduceți URL (lăsați gol pentru a ignora): " + +msgid "Select signature check option" +msgstr "Selectați opțiunea pentru verificarea semnăturii" + +msgid "Select signature option" +msgstr "Selectați opțiunea pentru semnătură" + +msgid "Custom mirrors" +msgstr "Oglinzi customizate" + +msgid "Defined" +msgstr "Definit" + +msgid "Save user configuration (including disk layout)" +msgstr "Salvează configurația utilizatorului (schema discului este inclusă)" + +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "" +"Introduceți un director pentru salvarea configurării (configurațiilor, completare automată cu Tab activată)\n" +"Directorul pentru salvare: " + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" +"Doriți să salvați fișierele de configurare {} în această locație?\n" +"\n" +"{]" + +msgid "Saving {} configuration files to {}" +msgstr "Salvez {} fișierele de configurație la {}" + +msgid "Mirrors" +msgstr "Oglinzi" + +msgid "Mirror regions" +msgstr "Regiuni oglinzi" + +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - Valoare maximă : {} ( Permite {} descărcări simultane, permite {max_downloads+1} descărcări în același timp )" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "Input invalid! Încercați din nou cu un input valid [1 la {}, sau 0 pentru dezactivare]" + +msgid "Locales" +msgstr "Locale" + +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Folosiți NetworkManager (necesar pentru configurarea setărilor de rețea într-un mod grafic pentru GNOME și KDE)" + +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "Această opțiune setează numărul de descărcări paralele care pot avea loc în timpul instalării" + +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"Introduceți numărul de descărcări simultane pentru a fi activat.\n" +"\n" +"Notă:\n" + +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - Valoare minimă : {} ( Permite {} descărcări simultane în același timp )" + +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - Dezactivează/Predefinit: 0 (Dezactivează descărcările simultane, permite o singură descărcare în același timp )\n" + +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "Input invalid! Încercați din nou cu un input valid [0 pentru dezactivare]" + +#, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "Configurație invalidă: {error}" + +msgid "Total: {} / {}" +msgstr "Total: {} / {}" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "Toate valorile introduse pot fi notate cu unități precum: B, KB, KiB, MB, MiB ..." + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "Dacă nicio unitate nu este specificate, atunci valorile vor fi interpretate ca sectoare" + +msgid "Enter start (default: sector {}): " +msgstr "Introduceți start (predefinit: sector {}): " + +msgid "Enter end (default: {}): " +msgstr "Introduceți final (predefinit: {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "Nu se pot identifica dispozitive de tip fido2. Este libfido2 instalată?" + +msgid "Path" +msgstr "Cale" + +msgid "Manufacturer" +msgstr "Producător" + +msgid "Product" +msgstr "Produs" -- cgit v1.2.3-70-g09d2 From c3d18fecdf3595277d46bda7fd6855a84b05f49f Mon Sep 17 00:00:00 2001 From: Roxfr <52124613+roxfr@users.noreply.github.com> Date: Thu, 14 Sep 2023 13:16:08 +0200 Subject: New translation for the French language (#2024) * French language translation update Hello, Here is the update of the translation for the French language. Regards, Roxfr * Add files via upload Hello, Here is the new translation for the French language: - Update of the translation via the latest .pot file, - Improved translation. Regards, Roxfr --- archinstall/locales/fr/LC_MESSAGES/base.mo | Bin 28587 -> 38638 bytes archinstall/locales/fr/LC_MESSAGES/base.po | 685 ++++++++++++++++++----------- 2 files changed, 440 insertions(+), 245 deletions(-) diff --git a/archinstall/locales/fr/LC_MESSAGES/base.mo b/archinstall/locales/fr/LC_MESSAGES/base.mo index a19c62cb..b5d5bfa7 100644 Binary files a/archinstall/locales/fr/LC_MESSAGES/base.mo and b/archinstall/locales/fr/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/fr/LC_MESSAGES/base.po b/archinstall/locales/fr/LC_MESSAGES/base.po index d86a4670..cda850cc 100644 --- a/archinstall/locales/fr/LC_MESSAGES/base.po +++ b/archinstall/locales/fr/LC_MESSAGES/base.po @@ -9,13 +9,17 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.2.2\n" +"X-Generator: Poedit 3.3.2\n" msgid "[!] A log file has been created here: {} {}" msgstr "[!] Un fichier journal a été créé ici : {} {}" -msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" -msgstr " Veuillez soumettre ce problème (et le fichier) à https://github.com/archlinux/archinstall/issues" +msgid "" +" Please submit this issue (and file) to https://github.com/archlinux/" +"archinstall/issues" +msgstr "" +" Veuillez soumettre ce problème (et le fichier) à https://github.com/" +"archlinux/archinstall/issues" msgid "Do you really want to abort?" msgstr "Voulez-vous vraiment abandonner ?" @@ -30,10 +34,13 @@ msgid "Desired hostname for the installation: " msgstr "Nom d'hôte souhaité pour l'installation : " msgid "Username for required superuser with sudo privileges: " -msgstr "Nom d'utilisateur pour le superutilisateur requis avec les privilèges sudo : " +msgstr "" +"Nom d'utilisateur pour le superutilisateur requis avec les privilèges sudo : " msgid "Any additional users to install (leave blank for no users): " -msgstr "Utilisateur supplémentaire à installer (laisser vide pour aucun utilisateur) : " +msgstr "" +"Utilisateur supplémentaire à installer (laisser vide pour aucun " +"utilisateur) : " msgid "Should this user be a superuser (sudoer)?" msgstr "Cet utilisateur doit-il être un superutilisateur (sudoer) ?" @@ -42,7 +49,9 @@ msgid "Select a timezone" msgstr "Sélectionner un fuseau horaire" msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?" -msgstr "Souhaitez-vous utiliser GRUB comme chargeur de démarrage au lieu de systemd-boot ?" +msgstr "" +"Souhaitez-vous utiliser GRUB comme chargeur de démarrage au lieu de systemd-" +"boot ?" msgid "Choose a bootloader" msgstr "Choisir un chargeur de démarrage" @@ -50,38 +59,60 @@ msgstr "Choisir un chargeur de démarrage" msgid "Choose an audio server" msgstr "Choisir un serveur audio" -msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." -msgstr "Seuls les packages tels que base, base-devel, linux, linux-firmware, efibootmgr et les packages de profil optionnels sont installés." +msgid "" +"Only packages such as base, base-devel, linux, linux-firmware, efibootmgr " +"and optional profile packages are installed." +msgstr "" +"Seuls les paquets tels que base, base-devel, linux, linux-firmware, " +"efibootmgr et les paquets de profil optionnels sont installés." -msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." -msgstr "Si vous désirez un navigateur Web, tel que firefox ou chrome, vous pouvez le spécifier dans l'invite suivante." +msgid "" +"If you desire a web browser, such as firefox or chromium, you may specify it " +"in the following prompt." +msgstr "" +"Si vous désirez un navigateur Web, tel que firefox ou chrome, vous pouvez le " +"spécifier dans l'invite suivante." -msgid "Write additional packages to install (space separated, leave blank to skip): " -msgstr "Écrire des packages supplémentaires à installer (espaces séparés, laisser vide pour ignorer) : " +msgid "" +"Write additional packages to install (space separated, leave blank to skip): " +msgstr "" +"Saisir les paquets supplémentaires à installer (séparés par des espaces, " +"vide pour ignorer) : " msgid "Copy ISO network configuration to installation" msgstr "Copier la configuration réseau ISO dans l'installation" -msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" -msgstr "Utiliser NetworkManager (nécessaire pour configurer graphiquement Internet dans GNOME et KDE)" +msgid "" +"Use NetworkManager (necessary for configuring internet graphically in GNOME " +"and KDE)" +msgstr "" +"Utiliser NetworkManager (nécessaire pour configurer graphiquement Internet " +"dans GNOME et KDE)" msgid "Select one network interface to configure" msgstr "Sélectionner une interface réseau à configurer" -msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" -msgstr "Sélectionner le mode à configurer pour \"{}\" ou ignorer pour utiliser le mode par défaut \"{}\"" +msgid "" +"Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" +msgstr "" +"Sélectionner le mode à configurer pour \"{}\" ou ignorer pour utiliser le " +"mode par défaut \"{}\"" msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " msgstr "Entrer l'IP et le sous-réseau pour {} (exemple : 192.168.0.5/24) : " msgid "Enter your gateway (router) IP address or leave blank for none: " -msgstr "Entrer l'adresse IP de votre passerelle (routeur) ou laisser vide pour aucune : " +msgstr "" +"Entrer l'adresse IP de votre passerelle (routeur) ou laisser vide pour " +"aucune : " msgid "Enter your DNS servers (space separated, blank for none): " msgstr "Entrer vos serveurs DNS (séparés par des espaces, vide pour aucun) : " msgid "Select which filesystem your main partition should use" -msgstr "Sélectionner le système de fichiers que votre partition principale doit utiliser" +msgstr "" +"Sélectionner le système de fichiers que votre partition principale doit " +"utiliser" msgid "Current partition layout" msgstr "Disposition actuelle des partitions" @@ -96,14 +127,21 @@ msgstr "" msgid "Enter a desired filesystem type for the partition" msgstr "Entrer un type de système de fichiers souhaité pour la partition" -msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " -msgstr "Entrer l'emplacement de départ (en unités séparées : s, Go, %, etc. ; par défaut : {}) : " +msgid "" +"Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " +msgstr "" +"Entrer l'emplacement de départ (en unités séparées : s, Go, %, etc. ; par " +"défaut : {}) : " msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " -msgstr "Entrer l'emplacement de fin (en unités séparées : s, Go, %, etc. ; ex : {}) : " +msgstr "" +"Entrer l'emplacement de fin (en unités séparées : s, Go, %, etc. ; ex : " +"{}) : " msgid "{} contains queued partitions, this will remove those, are you sure?" -msgstr "{} contient des partitions en file d'attente, cela les supprimera, êtes-vous sûr ?" +msgstr "" +"{} contient des partitions en file d'attente, cela les supprimera, êtes-vous " +"sûr ?" msgid "" "{}\n" @@ -123,11 +161,17 @@ msgstr "" "\n" "Sélectionner par index où et quelle partition montée" -msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * Les points de montage de la partition sont relatifs à l'intérieur de l'installation, le démarrage serait /boot par exemple." +msgid "" +" * Partition mount-points are relative to inside the installation, the boot " +"would be /boot as an example." +msgstr "" +" * Les points de montage de la partition sont relatifs à l'intérieur de " +"l'installation, le démarrage serait /boot par exemple." msgid "Select where to mount partition (leave blank to remove mountpoint): " -msgstr "Sélectionner où monter la partition (laisser vide pour supprimer le point de montage) : " +msgstr "" +"Sélectionner où monter la partition (laisser vide pour supprimer le point de " +"montage) : " msgid "" "{}\n" @@ -172,34 +216,58 @@ msgid "Archinstall language" msgstr "Langue d'Archinstall" msgid "Wipe all selected drives and use a best-effort default partition layout" -msgstr "Effacer tous les lecteurs sélectionnés et utiliser une disposition de partition par défaut optimale" +msgstr "" +"Effacer tous les lecteurs sélectionnés et utiliser une disposition de " +"partition par défaut optimale" -msgid "Select what to do with each individual drive (followed by partition usage)" -msgstr "Sélectionner ce qu'il faut faire avec chaque lecteur individuel (suivi de l'utilisation de la partition)" +msgid "" +"Select what to do with each individual drive (followed by partition usage)" +msgstr "" +"Sélectionner ce qu'il faut faire avec chaque lecteur individuel (suivi de " +"l'utilisation de la partition)" msgid "Select what you wish to do with the selected block devices" -msgstr "Sélectionner ce que vous souhaitez faire avec les périphériques de bloc sélectionnés" +msgstr "" +"Sélectionner ce que vous souhaitez faire avec les périphériques de bloc " +"sélectionnés" -msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments" -msgstr "Ceci est une liste de profils préprogrammés, ils pourraient faciliter l'installation d'outils comme les environnements de bureau" +msgid "" +"This is a list of pre-programmed profiles, they might make it easier to " +"install things like desktop environments" +msgstr "" +"Ceci est une liste préprogrammée de profiles, ils pourraient faciliter " +"l'installation d'outils comme les environnements de bureau" msgid "Select keyboard layout" msgstr "Sélectionner la disposition du clavier" msgid "Select one of the regions to download packages from" -msgstr "Sélectionner l'une des régions depuis lesquelles télécharger les packages" +msgstr "" +"Sélectionner l'une des régions depuis lesquelles télécharger les paquets" msgid "Select one or more hard drives to use and configure" msgstr "Sélectionner un ou plusieurs disques durs à utiliser et à configurer" -msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." -msgstr "Pour une meilleure compatibilité avec votre matériel AMD, vous pouvez utiliser les options entièrement open source ou AMD / ATI." +msgid "" +"For the best compatibility with your AMD hardware, you may want to use " +"either the all open-source or AMD / ATI options." +msgstr "" +"Pour une meilleure compatibilité avec votre matériel AMD, vous pouvez " +"utiliser les options entièrement open source ou AMD / ATI." -msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" -msgstr "Pour une compatibilité optimale avec votre matériel Intel, vous pouvez utiliser les options entièrement open source ou Intel.\n" +msgid "" +"For the best compatibility with your Intel hardware, you may want to use " +"either the all open-source or Intel options.\n" +msgstr "" +"Pour une compatibilité optimale avec votre matériel Intel, vous pouvez " +"utiliser les options entièrement open source ou Intel.\n" -msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" -msgstr "Pour une meilleure compatibilité avec votre matériel Nvidia, vous pouvez utiliser le pilote propriétaire Nvidia.\n" +msgid "" +"For the best compatibility with your Nvidia hardware, you may want to use " +"the Nvidia proprietary driver.\n" +msgstr "" +"Pour une meilleure compatibilité avec votre matériel Nvidia, vous pouvez " +"utiliser le pilote propriétaire Nvidia.\n" msgid "" "\n" @@ -208,7 +276,8 @@ msgid "" msgstr "" "\n" "\n" -"Sélectionner un pilote graphique ou laisser vide pour installer tous les pilotes open-source" +"Sélectionner un pilote graphique ou laisser vide pour installer tous les " +"pilotes open-source" msgid "All open-source (default)" msgstr "Tout open-source (par défaut)" @@ -231,8 +300,12 @@ msgstr "Sélectionner une ou plusieurs des options ci-dessous : " msgid "Adding partition...." msgstr "Ajout de la partition...." -msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." -msgstr "Vous devez entrer un type de fs valide pour continuer. Voir `man parted` pour les types de fs valides." +msgid "" +"You need to enter a valid fs-type in order to continue. See `man parted` for " +"valid fs-type's." +msgstr "" +"Vous devez entrer un type de fs valide pour continuer. Voir `man parted` " +"pour les types de fs valides." msgid "Error: Listing profiles on URL \"{}\" resulted in:" msgstr "Erreur : la liste des profils sur l'URL \"{}\" a entraîné :" @@ -253,7 +326,7 @@ msgid "Locale encoding" msgstr "Encodage des paramètres régionaux" msgid "Drive(s)" -msgstr "Disques durs" +msgstr "Disques" msgid "Disk layout" msgstr "Disposition du disque" @@ -286,7 +359,7 @@ msgid "Kernels" msgstr "Noyaux" msgid "Additional packages" -msgstr "Packages supplémentaires" +msgstr "Paquets supplémentaires" msgid "Network configuration" msgstr "Configurer le réseau" @@ -305,7 +378,8 @@ msgid "" msgstr "" "Vous avez décidé d'ignorer la sélection du disque dur\n" "et vous utiliserez la configuration de disque montée sur {} (expérimental)\n" -"ATTENTION : Archinstall ne vérifiera pas l'adéquation de cette configuration\n" +"ATTENTION : Archinstall ne vérifiera pas l'adéquation de cette " +"configuration\n" "Souhaitez-vous continuer ?" msgid "Re-using partition instance: {}" @@ -330,7 +404,8 @@ msgid "Mark/Unmark a partition as encrypted" msgstr "Marquer/Démarquer une partition comme chiffrée" msgid "Mark/Unmark a partition as bootable (automatic for /boot)" -msgstr "Marquer/Démarquer une partition comme amorçable (automatique pour /boot)" +msgstr "" +"Marquer/Démarquer une partition comme amorçable (automatique pour /boot)" msgid "Set desired filesystem for a partition" msgstr "Définir le système de fichiers souhaité pour une partition" @@ -370,7 +445,9 @@ msgid "Enter a encryption password for {}" msgstr "Entrer un mot de passe de chiffrement pour {}" msgid "Enter disk encryption password (leave blank for no encryption): " -msgstr "Entrer le mot de passe de chiffrement du disque (laisser vide pour aucun chiffrement) : " +msgstr "" +"Entrer le mot de passe de chiffrement du disque (laisser vide pour aucun " +"chiffrement) : " msgid "Create a required super-user with sudo privileges: " msgstr "Créer un super-utilisateur requis avec les privilèges sudo : " @@ -381,31 +458,44 @@ msgstr "Entrer le mot de passe root (laisser vide pour désactiver root) : " msgid "Password for user \"{}\": " msgstr "Mot de passe pour l'utilisateur \"{}\" : " -msgid "Verifying that additional packages exist (this might take a few seconds)" -msgstr "Vérifier que des packages supplémentaires existent (cela peut prendre quelques secondes)" +msgid "" +"Verifying that additional packages exist (this might take a few seconds)" +msgstr "" +"Vérifier que des paquets supplémentaires existent (cela peut prendre " +"quelques secondes)" -msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" -msgstr "Souhaitez-vous utiliser la synchronisation automatique de l'heure (NTP) avec les serveurs de temps par défaut ?\n" +msgid "" +"Would you like to use automatic time synchronization (NTP) with the default " +"time servers?\n" +msgstr "" +"Souhaitez-vous utiliser la synchronisation automatique de l'heure (NTP) avec " +"les serveurs de temps par défaut ?\n" msgid "" -"Hardware time and other post-configuration steps might be required in order for NTP to work.\n" +"Hardware time and other post-configuration steps might be required in order " +"for NTP to work.\n" "For more information, please check the Arch wiki" msgstr "" -"Le temps matériel et d'autres étapes de post-configuration peuvent être nécessaires pour que NTP fonctionne.\n" +"Le temps matériel et d'autres étapes de post-configuration peuvent être " +"nécessaires pour que NTP fonctionne.\n" "Pour plus d'informations, veuillez consulter le wiki Arch" msgid "Enter a username to create an additional user (leave blank to skip): " -msgstr "Entrer un nom d'utilisateur pour créer un utilisateur supplémentaire (laisser vide pour ignorer) : " +msgstr "" +"Entrer un nom d'utilisateur pour créer un utilisateur supplémentaire " +"(laisser vide pour ignorer) : " msgid "Use ESC to skip\n" msgstr "Utiliser ESC pour ignorer\n" msgid "" "\n" -" Choose an object from the list, and select one of the available actions for it to execute" +" Choose an object from the list, and select one of the available actions for " +"it to execute" msgstr "" "\n" -"Choisir un objet dans la liste et sélectionner l'une des actions disponibles pour qu'il s'exécute" +"Choisir un objet dans la liste et sélectionner l'une des actions disponibles " +"pour qu'il s'exécute" msgid "Cancel" msgstr "Annuler" @@ -441,14 +531,21 @@ msgstr "" "\n" "Voici la configuration choisie :" -msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." -msgstr "Pacman est déjà en cours d'exécution, attendez au maximum 10 minutes pour qu'il se termine." +msgid "" +"Pacman is already running, waiting maximum 10 minutes for it to terminate." +msgstr "" +"Pacman est déjà en cours d'exécution, attendez au maximum 10 minutes pour " +"qu'il se termine." -msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." -msgstr "Le verrou pacman préexistant n'a jamais été fermé. Veuillez nettoyer toutes les sessions pacman existantes avant d'utiliser archinstall." +msgid "" +"Pre-existing pacman lock never exited. Please clean up any existing pacman " +"sessions before using archinstall." +msgstr "" +"Le verrou pacman préexistant n'a jamais été fermé. Veuillez nettoyer toutes " +"les sessions pacman existantes avant d'utiliser archinstall." msgid "Choose which optional additional repositories to enable" -msgstr "Choisir les référentiels supplémentaires en option à activer" +msgstr "Choisir les dépôts supplémentaires en option à activer" msgid "Add a user" msgstr "Ajouter un utilisateur" @@ -527,7 +624,7 @@ msgid "are you sure you want to use it?" msgstr "êtes-vous sûr de vouloir l'utiliser ?" msgid "Optional repositories" -msgstr "Référentiels supplémentaires" +msgstr "Dépôts supplémentaires" msgid "Save configuration" msgstr "Enregistrer la configuration" @@ -570,7 +667,7 @@ msgid "Subvolume options" msgstr "Options de sous-volume" msgid "Save" -msgstr "Sauver" +msgstr "Sauvegarder" msgid "Subvolume name :" msgstr "Nom du sous-volume :" @@ -582,13 +679,16 @@ msgid "Select the desired subvolume options " msgstr "Sélectionner les options de sous-volume souhaitées " msgid "Define users with sudo privilege, by username: " -msgstr "Définir les utilisateurs avec le privilège sudo, par nom d'utilisateur : " +msgstr "" +"Définir les utilisateurs avec le privilège sudo, par nom d'utilisateur : " msgid "[!] A log file has been created here: {}" msgstr "[!] Un fichier journal a été créé ici : {}" msgid "Would you like to use BTRFS subvolumes with a default structure?" -msgstr "Souhaitez-vous utiliser des sous-volumes BTRFS avec une structure par défaut ?" +msgstr "" +"Souhaitez-vous utiliser des sous-volumes BTRFS avec une structure par " +"défaut ?" msgid "Would you like to use BTRFS compression?" msgstr "Souhaitez-vous utiliser la compression BTRFS ?" @@ -596,8 +696,12 @@ msgstr "Souhaitez-vous utiliser la compression BTRFS ?" msgid "Would you like to create a separate partition for /home?" msgstr "Souhaitez-vous créer une partition séparée pour /home ?" -msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" -msgstr "Les disques sélectionnés n'ont pas la capacité minimale requise pour une suggestion automatique\n" +msgid "" +"The selected drives do not have the minimum capacity required for an " +"automatic suggestion\n" +msgstr "" +"Les disques sélectionnés n'ont pas la capacité minimale requise pour une " +"suggestion automatique\n" msgid "Minimum capacity for /home partition: {}GB\n" msgstr "Capacité minimale pour la partition /home : {} Go\n" @@ -624,7 +728,9 @@ msgid "No iface specified for manual configuration" msgstr "Aucun iface spécifié pour la configuration manuelle" msgid "Manual nic configuration with no auto DHCP requires an IP address" -msgstr "La configuration manuelle de la carte réseau sans DHCP automatique nécessite une adresse IP" +msgstr "" +"La configuration manuelle de la carte réseau sans DHCP automatique nécessite " +"une adresse IP" msgid "Add interface" msgstr "Ajouter une interface" @@ -644,23 +750,42 @@ msgstr "Configuration manuelle" msgid "Mark/Unmark a partition as compressed (btrfs only)" msgstr "Marquer/Démarquer une partition comme compressée (btrfs uniquement)" -msgid "The password you are using seems to be weak, are you sure you want to use it?" -msgstr "Le mot de passe que vous utilisez semble faible, êtes-vous sûr de vouloir l'utiliser ?" +msgid "" +"The password you are using seems to be weak, are you sure you want to use it?" +msgstr "" +"Le mot de passe que vous utilisez semble faible, êtes-vous sûr de vouloir " +"l'utiliser ?" -msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" -msgstr "Fournit une sélection d'environnements de bureau et de gestionnaires de fenêtres en mosaïque, par ex. gnome, kde, sway" +msgid "" +"Provides a selection of desktop environments and tiling window managers, e." +"g. gnome, kde, sway" +msgstr "" +"Fournit une sélection d'environnements de bureau et de gestionnaires de " +"fenêtres en mosaïque, par ex. gnome, kde, sway" msgid "Select your desired desktop environment" msgstr "Sélectionner l'environnement de bureau souhaité" -msgid "A very basic installation that allows you to customize Arch Linux as you see fit." -msgstr "Une installation très basique qui vous permet de personnaliser Arch Linux comme bon vous semble." +msgid "" +"A very basic installation that allows you to customize Arch Linux as you see " +"fit." +msgstr "" +"Une installation très basique qui vous permet de personnaliser Arch Linux " +"comme bon vous semble." -msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" -msgstr "Fournit une sélection de divers paquets de serveur à installer et à activer, par ex. httpd, nginx, mariadb" +msgid "" +"Provides a selection of various server packages to install and enable, e.g. " +"httpd, nginx, mariadb" +msgstr "" +"Fournit une sélection de divers paquets de serveur à installer et à activer, " +"par ex. httpd, nginx, mariadb" -msgid "Choose which servers to install, if none then a minimal installation will be done" -msgstr "Choisir les serveurs à installer, s'il n'y en a pas, une installation minimale sera effectuée" +msgid "" +"Choose which servers to install, if none then a minimal installation will be " +"done" +msgstr "" +"Choisir les serveurs à installer, s'il n'y en a pas, une installation " +"minimale sera effectuée" msgid "Installs a minimal system as well as xorg and graphics drivers." msgstr "Installe un système minimal ainsi que les pilotes graphiques et xorg." @@ -668,8 +793,12 @@ msgstr "Installe un système minimal ainsi que les pilotes graphiques et xorg." msgid "Press Enter to continue." msgstr "Appuyer sur Entrée pour continuer." -msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?" -msgstr "Souhaitez-vous chrooter dans l'installation nouvellement créée et effectuer la configuration post-installation ?" +msgid "" +"Would you like to chroot into the newly created installation and perform " +"post-installation configuration?" +msgstr "" +"Souhaitez-vous chrooter dans l'installation nouvellement créée et effectuer " +"la configuration post-installation ?" msgid "Are you sure you want to reset this setting?" msgstr "Voulez-vous vraiment réinitialiser ce paramètre ?" @@ -678,10 +807,16 @@ msgid "Select one or more hard drives to use and configure\n" msgstr "Sélectionner un ou plusieurs disques durs à utiliser et à configurer\n" msgid "Any modifications to the existing setting will reset the disk layout!" -msgstr "Toute modification du paramètre existant réinitialisera la disposition du disque !" +msgstr "" +"Toute modification du paramètre existant réinitialisera la disposition du " +"disque !" -msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" -msgstr "Si vous réinitialisez la sélection du disque dur, cela réinitialisera également la disposition actuelle du disque. Êtes-vous sûr ?" +msgid "" +"If you reset the harddrive selection this will also reset the current disk " +"layout. Are you sure?" +msgstr "" +"Si vous réinitialisez la sélection du disque dur, cela réinitialisera " +"également la disposition actuelle du disque. Êtes-vous sûr ?" msgid "Save and exit" msgstr "Sauvegarder et quitter" @@ -691,7 +826,8 @@ msgid "" "contains queued partitions, this will remove those, are you sure?" msgstr "" "{}\n" -"contient des partitions en file d'attente, cela les supprimera, êtes-vous sûr ?" +"contient des partitions en file d'attente, cela les supprimera, êtes-vous " +"sûr ?" msgid "No audio server" msgstr "Pas de serveur audio" @@ -722,13 +858,18 @@ msgid "Edit {}: " msgstr "Modifier {} : " msgid "Add: " -msgstr "Ajouter: " +msgstr "Ajouter : " msgid "Value: " -msgstr "Valeur: " +msgstr "Valeur : " -msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" -msgstr "Vous pouvez ignorer la sélection d'un lecteur et le partitionnement et utiliser n'importe quelle configuration de lecteur montée sur /mnt (expérimental)" +msgid "" +"You can skip selecting a drive and partitioning and use whatever drive-setup " +"is mounted at /mnt (experimental)" +msgstr "" +"Vous pouvez ignorer la sélection d'un lecteur et le partitionnement et " +"utiliser n'importe quelle configuration de lecteur montée sur /mnt " +"(expérimental)" msgid "Select one of the disks or skip and use /mnt as default" msgstr "Sélectionner l'un des disques ou ignorer et utiliser /mnt par défaut" @@ -751,8 +892,12 @@ msgstr "Espace libre" msgid "Bus-type" msgstr "Type de bus" -msgid "Either root-password or at least 1 user with sudo privileges must be specified" -msgstr "Le mot de passe root ou au moins 1 utilisateur avec des privilèges sudo doit être spécifié" +msgid "" +"Either root-password or at least 1 user with sudo privileges must be " +"specified" +msgstr "" +"Le mot de passe root ou au moins 1 utilisateur avec des privilèges sudo doit " +"être spécifié" msgid "Enter username (leave blank to skip): " msgstr "Entrer le nom d'utilisateur (laisser vide pour passer) : " @@ -790,8 +935,12 @@ msgstr "Supprimer le sous-volume" msgid "Configured {} interfaces" msgstr "Interfaces {} configurées" -msgid "This option enables the number of parallel downloads that can occur during installation" -msgstr "Cette option active le nombre de téléchargements parallèles qui peuvent se produire pendant l'installation" +msgid "" +"This option enables the number of parallel downloads that can occur during " +"installation" +msgstr "" +"Cette option active le nombre de téléchargements parallèles qui peuvent se " +"produire pendant l'installation" msgid "" "Enter the number of parallel downloads to be enabled.\n" @@ -802,18 +951,34 @@ msgstr "" " (Entrer une valeur comprise entre 1 et {})\n" "Note :" -msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" -msgstr " - Valeur maximale : {} (Autorise {} téléchargements parallèles, autorise {} téléchargements à la fois)" +msgid "" +" - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads " +"at a time )" +msgstr "" +" - Valeur maximale : {} (Autorise {} téléchargements parallèles, autorise {} " +"téléchargements à la fois)" -msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" -msgstr " - Valeur minimale : 1 (Autorise 1 téléchargement parallèle, autorise 2 téléchargements à la fois)" +msgid "" +" - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a " +"time )" +msgstr "" +" - Valeur minimale : 1 (Autorise 1 téléchargement parallèle, autorise 2 " +"téléchargements à la fois)" -msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" -msgstr " - Désactiver/Défaut : 0 (Désactive le téléchargement parallèle, n'autorise qu'un seul téléchargement à la fois)" +msgid "" +" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 " +"download at a time )" +msgstr "" +" - Désactiver/Défaut : 0 (Désactive le téléchargement parallèle, n'autorise " +"qu'un seul téléchargement à la fois)" -#, fuzzy, python-brace-format -msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "Entrée invalide ! Réessayer avec une entrée valide [1 pour {}, ou 0 pour désactiver]" +#, python-brace-format +msgid "" +"Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to " +"disable]" +msgstr "" +"Entrée invalide ! Réessayer avec une entrée valide [1 pour {max_downloads}, " +"ou 0 pour désactiver]" msgid "Parallel Downloads" msgstr "Téléchargements parallèles" @@ -830,132 +995,147 @@ msgstr "TAB pour sélectionner" msgid "[Default value: 0] > " msgstr "[Valeur par défaut : 0] > " -msgid "To be able to use this translation, please install a font manually that supports the language." -msgstr "Pour pouvoir utiliser cette traduction, veuillez installer manuellement une police prenant en charge la langue." +msgid "" +"To be able to use this translation, please install a font manually that " +"supports the language." +msgstr "" +"Pour pouvoir utiliser cette traduction, veuillez installer manuellement une " +"police prenant en charge la langue." msgid "The font should be stored as {}" msgstr "La police doit être stockée sous {}" msgid "Archinstall requires root privileges to run. See --help for more." msgstr "" +"Archinstall nécessite des privilèges root pour s'exécuter. Voir l'aide pour " +"plus d'informations (--help)." -#, fuzzy msgid "Select an execution mode" -msgstr "Sélectionner une action pour '{}'" +msgstr "Sélectionner un mode d'exécution" msgid "Unable to fetch profile from specified url: {}" -msgstr "" +msgstr "Impossible de récupérer le profil à partir de l'URL spécifiée : {}" -msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgid "" +"Profiles must have unique name, but profile definitions with duplicate name " +"found: {}" msgstr "" +"Les profils doivent avoir un nom unique, mais des définitions de profil avec " +"un nom en double ont été trouvées : {}" -#, fuzzy msgid "Select one or more devices to use and configure" -msgstr "Sélectionner un ou plusieurs disques durs à utiliser et à configurer" +msgstr "Sélectionner un ou plusieurs périphériques à utiliser et à configurer" -#, fuzzy -msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" -msgstr "Si vous réinitialisez la sélection du disque dur, cela réinitialisera également la disposition actuelle du disque. Êtes-vous sûr ?" +msgid "" +"If you reset the device selection this will also reset the current disk " +"layout. Are you sure?" +msgstr "" +"Si vous réinitialisez la sélection de périphérique, cela réinitialisera " +"également la disposition actuelle du disque. Etes-vous sûr ?" -#, fuzzy msgid "Existing Partitions" -msgstr "Ajout de la partition...." +msgstr "Partitions existantes" -#, fuzzy msgid "Select a partitioning option" -msgstr "Supprimer une partition" +msgstr "Sélectionner une option de partitionnement" -#, fuzzy msgid "Enter the root directory of the mounted devices: " -msgstr "Saisir un répertoire pour la ou les configuration(s) à enregistrer : " +msgstr "Entrer le répertoire racine des périphériques montés : " -#, fuzzy msgid "Minimum capacity for /home partition: {}GiB\n" -msgstr "Capacité minimale pour la partition /home : {} Go\n" +msgstr "Capacité minimale pour la partition /home : {} Gio\n" -#, fuzzy msgid "Minimum capacity for Arch Linux partition: {}GiB" -msgstr "Capacité minimale pour la partition Arch Linux : {} Go" +msgstr "Capacité minimale pour la partition Arch Linux : {} Gio" -#, fuzzy -msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" -msgstr "Ceci est une liste de profils préprogrammés, ils pourraient faciliter l'installation d'outils comme les environnements de bureau" +msgid "" +"This is a list of pre-programmed profiles_bck, they might make it easier to " +"install things like desktop environments" +msgstr "" +"Ceci est une liste préprogrammée de profiles_bck, ils pourraient faciliter " +"l'installation de choses comme les environnements de bureau" -#, fuzzy msgid "Current profile selection" -msgstr "Disposition actuelle des partitions" +msgstr "Sélection du profil actuel" -#, fuzzy msgid "Remove all newly added partitions" -msgstr "Créer une nouvelle partition" +msgstr "Supprimer toutes les partitions nouvellement ajoutées" -#, fuzzy msgid "Assign mountpoint" -msgstr "Attribuer un point de montage pour une partition" +msgstr "Attribuer un point de montage" -#, fuzzy msgid "Mark/Unmark to be formatted (wipes data)" -msgstr "Marquer/Démarquer une partition à formater (efface les données)" +msgstr "Marquer/Démarquer à formater (efface les données)" msgid "Mark/Unmark as bootable" -msgstr "" +msgstr "Marquer/Démarquer comme amorçable" msgid "Change filesystem" -msgstr "" +msgstr "Changer le système de fichiers" -#, fuzzy msgid "Mark/Unmark as compressed" -msgstr "Marquer/Démarquer une partition comme compressée (btrfs uniquement)" +msgstr "Marquer/Démarquer comme compressé" -#, fuzzy msgid "Set subvolumes" -msgstr "Supprimer le sous-volume" +msgstr "Définir des sous-volumes" -#, fuzzy msgid "Delete partition" -msgstr "Supprimer une partition" +msgstr "Supprimer la partition" msgid "Partition" -msgstr "" +msgstr "Partition" -msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgid "" +"This partition is currently encrypted, to format it a filesystem has to be " +"specified" msgstr "" +"Cette partition est actuellement cryptée, pour la formater, un système de " +"fichiers doit être spécifié" -#, fuzzy -msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * Les points de montage de la partition sont relatifs à l'intérieur de l'installation, le démarrage serait /boot par exemple." +msgid "" +"Partition mount-points are relative to inside the installation, the boot " +"would be /boot as an example." +msgstr "" +"Les points de montage de partition sont relatifs à l'intérieur de " +"l'installation, le démarrage serait /boot par exemple." -msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgid "" +"If mountpoint /boot is set, then the partition will also be marked as " +"bootable." msgstr "" +"Si le point de montage /boot est défini, la partition sera également marquée " +"comme amorçable." msgid "Mountpoint: " -msgstr "" +msgstr "Point de montage : " msgid "Current free sectors on device {}:" -msgstr "" +msgstr "Secteurs actuellement libres sur le périphérique {} :" -#, fuzzy msgid "Total sectors: {}" -msgstr "Répertoire non valide : {}" +msgstr "Total des secteurs : {}" -#, fuzzy msgid "Enter the start sector (default: {}): " -msgstr "Entrer le secteur de début (pourcentage ou numéro de bloc, par défaut : {}) : " +msgstr "Saisir le secteur de départ (par défaut : {}) : " -#, fuzzy -msgid "Enter the end sector of the partition (percentage or block number, default: {}): " -msgstr "Entrer le secteur de fin de la partition (pourcentage ou numéro de bloc, ex : {}) : " +msgid "" +"Enter the end sector of the partition (percentage or block number, default: " +"{}): " +msgstr "" +"Saisir le secteur de fin de la partition (pourcentage ou numéro de bloc, par " +"défaut : {}) : " msgid "This will remove all newly added partitions, continue?" msgstr "" +"Cela supprimera toutes les partitions nouvellement ajoutées, voulez-vous " +"continuer ?" msgid "Partition management: {}" -msgstr "" +msgstr "Gestion des partitions : {}" msgid "Total length: {}" -msgstr "" +msgstr "Total (taille) : {}" -#, fuzzy msgid "Encryption type" msgstr "Type de chiffrement" @@ -963,7 +1143,7 @@ msgid "Partitions" msgstr "Partitions" msgid "No HSM devices available" -msgstr "Aucun appareil HSM disponible" +msgstr "Aucun périphérique HSM disponible" msgid "Partitions to be encrypted" msgstr "Partitions à chiffrer" @@ -972,215 +1152,230 @@ msgid "Select disk encryption option" msgstr "Sélectionner l'option de chiffrement du disque" msgid "Select a FIDO2 device to use for HSM" -msgstr "Sélectionner un appareil FIDO2 à utiliser pour HSM" +msgstr "Sélectionner un périphérique FIDO2 à utiliser pour HSM" -#, fuzzy msgid "Use a best-effort default partition layout" -msgstr "Effacer tous les lecteurs sélectionnés et utiliser une disposition de partition par défaut optimale" +msgstr "Utiliser une disposition de partition optimale par défaut" -#, fuzzy msgid "Manual Partitioning" -msgstr "Configuration manuelle" +msgstr "Partitionnement manuel" -#, fuzzy msgid "Pre-mounted configuration" -msgstr "Aucune configuration" +msgstr "Configuration pré-montée" msgid "Unknown" -msgstr "" +msgstr "Inconnu" msgid "Partition encryption" -msgstr "" +msgstr "Chiffrement des partitions" msgid " ! Formatting {} in " -msgstr "" +msgstr " ! Formatage {} dans " msgid "← Back" -msgstr "" +msgstr "← Retour" msgid "Disk encryption" msgstr "Cryptage du disque" -#, fuzzy msgid "Configuration" -msgstr "Aucune configuration" +msgstr "Configuration" -#, fuzzy msgid "Password" msgstr "Mot de passe" -#, fuzzy msgid "All settings will be reset, are you sure?" -msgstr "{} contient des partitions en file d'attente, cela les supprimera, êtes-vous sûr ?" +msgstr "Tous les paramètres seront réinitialisés, êtes-vous sûr ?" msgid "Back" -msgstr "" +msgstr "Retour" msgid "Please chose which greeter to install for the chosen profiles: {}" msgstr "" +"Veuillez choisir le greeter (interface de connexion) à installer pour les " +"profils choisis : {}" msgid "Environment type: {}" -msgstr "" +msgstr "Type d'environnement : {}" -msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgid "" +"The proprietary Nvidia driver is not supported by Sway. It is likely that " +"you will run into issues, are you okay with that?" msgstr "" +"Le pilote Nvidia propriétaire n'est pas pris en charge par Sway. Il est " +"probable que vous rencontriez des problèmes, êtes-vous d'accord avec cela ?" -#, fuzzy msgid "Installed packages" -msgstr "Packages supplémentaires" +msgstr "Paquets installés" -#, fuzzy msgid "Add profile" -msgstr "Profil" +msgstr "Ajouter un profil" -#, fuzzy msgid "Edit profile" -msgstr "Profil" +msgstr "Editer le profil" -#, fuzzy msgid "Delete profile" -msgstr "Supprimer l'interface" +msgstr "Supprimer le profil" -#, fuzzy msgid "Profile name: " -msgstr "Profil" +msgstr "Nom de profil : " -#, fuzzy msgid "The profile name you entered is already in use. Try again" -msgstr "Le nom d'utilisateur que vous avez saisi n'est pas valide. Réessayer" +msgstr "" +"Le nom de profil que vous avez saisi est déjà utilisé. Essayer à nouveau" -#, fuzzy -msgid "Packages to be install with this profile (space separated, leave blank to skip): " -msgstr "Écrire des packages supplémentaires à installer (espaces séparés, laisser vide pour ignorer) : " +msgid "" +"Packages to be install with this profile (space separated, leave blank to " +"skip): " +msgstr "" +"Saisir les paquets à installer avec ce profil (séparés par des espaces, vide " +"pour ignorer) : " -#, fuzzy -msgid "Services to be enabled with this profile (space separated, leave blank to skip): " -msgstr "Écrire des packages supplémentaires à installer (espaces séparés, laisser vide pour ignorer) : " +msgid "" +"Services to be enabled with this profile (space separated, leave blank to " +"skip): " +msgstr "" +"Saisir les services à activer avec ce profil (séparés par des espaces, vide " +"pour ignorer) : " msgid "Should this profile be enabled for installation?" -msgstr "" +msgstr "Ce profil doit-il être activé pour l'installation ?" msgid "Create your own" -msgstr "" +msgstr "Créer le votre" -#, fuzzy msgid "" "\n" "Select a graphics driver or leave blank to install all open-source drivers" msgstr "" "\n" -"\n" -"Sélectionner un pilote graphique ou laisser vide pour installer tous les pilotes open-source" +"Sélectionner un pilote graphique ou laisser vide pour installer tous les " +"pilotes open source" -msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgid "" +"Sway needs access to your seat (collection of hardware devices i.e. " +"keyboard, mouse, etc)" msgstr "" +"Sway a besoin d'accéder à votre espace (ensemble de périphériques " +"matériels : clavier, souris, etc.)" msgid "" "\n" "\n" "Choose an option to give Sway access to your hardware" msgstr "" +"\n" +"\n" +"Choisir une option pour permettre à Sway d'accéder à votre matériel" msgid "Graphics driver" -msgstr "" +msgstr "Pilote graphique" msgid "Greeter" -msgstr "" +msgstr "Greeter (interface de connexion)" msgid "Please chose which greeter to install" -msgstr "" +msgstr "Veuillez choisir le greeter (interface de connexion) à installer" msgid "This is a list of pre-programmed default_profiles" -msgstr "" +msgstr "Il s'agit d'une liste préprogrammée de default_profiles par défaut" -#, fuzzy msgid "Disk configuration" -msgstr "Aucune configuration" +msgstr "Configuration du disque" -#, fuzzy msgid "Profiles" -msgstr "Profil" +msgstr "Profils" msgid "Finding possible directories to save configuration files ..." msgstr "" +"Recherche des répertoires possibles pour enregistrer les fichiers de " +"configuration..." -#, fuzzy msgid "Select directory (or directories) for saving configuration files" -msgstr "Sélectionner un ou plusieurs disques durs à utiliser et à configurer" +msgstr "" +"Sélectionner le répertoire (ou les répertoires) pour enregistrer les " +"fichiers de configuration" -#, fuzzy msgid "Add a custom mirror" -msgstr "Ajouter un utilisateur" +msgstr "Ajouter un miroir personnalisé" msgid "Change custom mirror" -msgstr "" +msgstr "Changer le miroir personnalisé" msgid "Delete custom mirror" -msgstr "" +msgstr "Supprimer le miroir personnalisé" -#, fuzzy msgid "Enter name (leave blank to skip): " -msgstr "Entrer le nom d'utilisateur (laisser vide pour passer) : " +msgstr "Entrer le nom (laisser vide pour ignorer) : " -#, fuzzy msgid "Enter url (leave blank to skip): " -msgstr "Entrer le nom d'utilisateur (laisser vide pour passer) : " +msgstr "Entrer l'URL (laisser vide pour ignorer) : " -#, fuzzy msgid "Select signature check option" -msgstr "Sélectionner la disposition du disque" +msgstr "Sélectionner l'option de vérification de signature" -#, fuzzy msgid "Select signature option" -msgstr "Sélectionner la disposition du disque" +msgstr "Sélectionner l'option de signature" msgid "Custom mirrors" -msgstr "" +msgstr "Miroirs personnalisés" msgid "Defined" -msgstr "" +msgstr "Défini" -#, fuzzy msgid "Save user configuration (including disk layout)" -msgstr "Enregistrer la configuration utilisateur" +msgstr "" +"Enregistrer la configuration utilisateur (y compris la disposition du disque)" -#, fuzzy msgid "" -"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Enter a directory for the configuration(s) to be saved (tab completion " +"enabled)\n" "Save directory: " -msgstr "Saisir un répertoire pour la ou les configuration(s) à enregistrer : " +msgstr "" +"Saisir un répertoire pour la/les configuration(s) à enregistrer (complétion " +"par tabulation activée)\n" +"Entrer le nom du répertoire de sauvegarde : " msgid "" "Do you want to save {} configuration file(s) in the following location?\n" "\n" "{}" msgstr "" +"Voulez-vous enregistrer {} fichier(s) de configuration à l'emplacement " +"suivant ?\n" +"\n" +"{}" -#, fuzzy msgid "Saving {} configuration files to {}" -msgstr "Enregistrer la configuration" +msgstr "Enregistrement de {} fichiers de configuration dans {}" -#, fuzzy msgid "Mirrors" -msgstr "Région miroir" +msgstr "Miroirs" -#, fuzzy msgid "Mirror regions" -msgstr "Région miroir" +msgstr "Régions miroir" -#, fuzzy -msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - Valeur maximale : {} (Autorise {} téléchargements parallèles, autorise {} téléchargements à la fois)" +msgid "" +" - Maximum value : {} ( Allows {} parallel downloads, allows " +"{max_downloads+1} downloads at a time )" +msgstr "" +" - Valeur maximale : {} (Autorise {} téléchargements parallèles, autorise " +"{max_downloads+1} téléchargements à la fois)" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" -msgstr "Entrée invalide ! Réessayer avec une entrée valide [1 pour {}, ou 0 pour désactiver]" +msgstr "" +"Entrée invalide ! Réessayer avec une entrée valide [1 pour {}, ou 0 pour " +"désactiver]" msgid "Locales" -msgstr "" +msgstr "Paramètres régionaux" -#, fuzzy -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" -msgstr "Utiliser NetworkManager (nécessaire pour configurer graphiquement Internet dans GNOME et KDE)" +msgid "" +"Use NetworkManager (necessary to configure internet graphically in GNOME and " +"KDE)" +msgstr "" +"Utiliser NetworkManager (nécessaire pour configurer graphiquement internet " +"dans GNOME et KDE)" #, python-brace-format #~ msgid "Edit {origkey} :" -- cgit v1.2.3-70-g09d2 From 4e243fa8684cd54f0cbbd6679e259ffb9ee1a738 Mon Sep 17 00:00:00 2001 From: Adrian Sausenthaler Date: Thu, 14 Sep 2023 13:16:35 +0200 Subject: 'Packete' made my eyes bleed; fixed common german mistakes; added missing (#2029) Signed-off-by: Adrian Sausenthaler --- archinstall/locales/de/LC_MESSAGES/base.mo | Bin 37609 -> 38328 bytes archinstall/locales/de/LC_MESSAGES/base.po | 153 ++++++++++++++--------------- 2 files changed, 74 insertions(+), 79 deletions(-) diff --git a/archinstall/locales/de/LC_MESSAGES/base.mo b/archinstall/locales/de/LC_MESSAGES/base.mo index 1e779cb7..7dda3e36 100644 Binary files a/archinstall/locales/de/LC_MESSAGES/base.mo and b/archinstall/locales/de/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/de/LC_MESSAGES/base.po b/archinstall/locales/de/LC_MESSAGES/base.po index 3ac24e3a..02b4b485 100644 --- a/archinstall/locales/de/LC_MESSAGES/base.po +++ b/archinstall/locales/de/LC_MESSAGES/base.po @@ -9,13 +9,13 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.3.1\n" +"X-Generator: Poedit 3.3.2\n" msgid "[!] A log file has been created here: {} {}" msgstr "[!] Eine Logdatei wurde hier erstellt: {} {}" msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" -msgstr "Bitte melden Sie das Problem mit der erstellten Datei auf https://github.com/archlinux/archinstall/issues" +msgstr " Bitte melden Sie das Problem mit der erstellten Datei auf https://github.com/archlinux/archinstall/issues" msgid "Do you really want to abort?" msgstr "Wollen Sie wirklich abbrechen?" @@ -24,7 +24,7 @@ msgid "And one more time for verification: " msgstr "Und noch einmal zur Bestätigung: " msgid "Would you like to use swap on zram?" -msgstr "Möchten Sie Swap mit zram verwenden?" +msgstr "Möchten Sie Auslagerungsspeicher (Swap) als zram verwenden?" msgid "Desired hostname for the installation: " msgstr "Gewünschter Gerätename/Hostname für die Installation: " @@ -51,10 +51,10 @@ msgid "Choose an audio server" msgstr "Bitte wählen Sie einen Audioserver aus" msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." -msgstr "Nur Pakete wie base, base-devel, linux, linux-firmware, efibootmgr und optionale Profilpakete werden installiert" +msgstr "Nur Pakete wie base, base-devel, linux, linux-firmware, efibootmgr und optionale Profilpakete werden installiert." msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." -msgstr "Wenn Sie einen Webbrowser, z.B. Firefox oder Chromium, installieren möchten, können Sie diese nun eingeben." +msgstr "Wenn Sie einen Webbrowser installieren möchten, wie z.B. Firefox oder Chromium, können Sie diese nun eingeben." msgid "Write additional packages to install (space separated, leave blank to skip): " msgstr "Schreiben Sie zusätzliche Pakete die installiert werden sollen (mit einem Leerzeichen getrennt, zum Überspringen leer lassen): " @@ -63,19 +63,19 @@ msgid "Copy ISO network configuration to installation" msgstr "ISO-Netzwerk Einstellungen in die Installation kopieren" msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" -msgstr "NetworkManager benutzen (notwendig um Internet auf grafische Weise in GNOME und KDE einzustellen)" +msgstr "NetworkManager nutzen (notwendig um Internetverbindungen grafisch in GNOME und KDE einzustellen)" msgid "Select one network interface to configure" -msgstr "Bitte wählen Sie ein Netzwerk zur Konfiguration aus" +msgstr "Bitte wählen Sie einen Netzwerkadapter zur Konfiguration aus" msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" -msgstr "Bitte wählen Sie einen Modus zur Konfiguration von \"{}\" aus oder Überspringen um mit dem voreingestellten Modus \"{}\" fortzufahren" +msgstr "Bitte wählen Sie einen Modus zur Konfiguration von \"{}\" aus oder überspringen, um mit dem voreingestellten Modus \"{}\" fortzufahren" msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " -msgstr "Bitte geben Sie eine IP Adresse und ein Subnet für {} ein (z.B. 192.168.0.5/24):" +msgstr "Bitte geben Sie die IP-Adresse mit Subnetzgröße für {} ein (z.B. 192.168.0.5/24): " msgid "Enter your gateway (router) IP address or leave blank for none: " -msgstr "Bitte geben Sie eine Gateway (Router) IP Adresse ein (leer lassen für kein Adresse): " +msgstr "Bitte geben Sie das Gateway (Router) IP-Adresse ein (leer lassen für keines): " msgid "Enter your DNS servers (space separated, blank for none): " msgstr "Bitte geben Sie die DNS-Server ein (mit Leerzeichen getrennt oder leer lassen für keinen Server): " @@ -94,7 +94,7 @@ msgstr "" "{}" msgid "Enter a desired filesystem type for the partition" -msgstr "Bitte wählen Sie einen gewünschten Dateisystemtyp für die Partition aus" +msgstr "Bitte wählen Sie den gewünschten Dateisystemtyp für die Partition aus" msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " msgstr "Bitte geben Sie die Startposition ein (in parted Einheiten: s, GB, %, etc. ; default: {}): " @@ -124,7 +124,7 @@ msgstr "" "Wählen Sie anhand vom Index aus, welche Partitionen wo eingehängt werden sollen" msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * Die Einhängeorte sind relativ zur Installation, zum Beispiel boot würde gemountet auf /boot" +msgstr " * Die Einhängeorte sind relativ zur Installation, boot würde beispielsweise /boot entsprechen." msgid "Select where to mount partition (leave blank to remove mountpoint): " msgstr "Bitte geben Sie an wo die Partition eingehängt werden soll (leer lassen um den Einhängeort zu entfernen): " @@ -154,7 +154,7 @@ msgid "" msgstr "" "{}\n" "\n" -"Bitte wählen Sie aus, welche Partition bootbar ist" +"Bitte wählen Sie aus, welche Partition bootfähig markiert werden soll" msgid "" "{}\n" @@ -163,7 +163,7 @@ msgid "" msgstr "" "{}\n" "\n" -"Bitte wählen Sie aus, auf welcher Partition ein Dateisystem eingerichtet werden soll" +"Bitte wählen Sie aus, auf welcher Partition ein Dateisystem erstelt werden soll" msgid "Enter a desired filesystem type for the partition: " msgstr "Bitte geben Sie einen gewünschten Dateisystemtyp für die Partition ein: " @@ -172,7 +172,7 @@ msgid "Archinstall language" msgstr "Sprache für Archinstall" msgid "Wipe all selected drives and use a best-effort default partition layout" -msgstr "Alle Laufwerke löschen und ein vorgegebenes Partitionslayout verwenden" +msgstr "Alle Laufwerke löschen und ein empfohlenes Partitionslayout verwenden" msgid "Select what to do with each individual drive (followed by partition usage)" msgstr "Bitte geben Sie individuell an, was mit jedem Laufwerk geschehen soll" @@ -181,7 +181,7 @@ msgid "Select what you wish to do with the selected block devices" msgstr "Bitte wählen Sie was mit den ausgewählten Geräten machen wollen" msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments" -msgstr "Dies ist eine Liste von bereits programmierten Profilen, diese ermöglichen einfacher Dinge wie Desktop Umgebungen zu installieren" +msgstr "Dies ist eine Auflistung vorprogrammierter Profile, die es einfacher ermöglichen, Dinge wie Desktop-Umgebungen zu installieren" msgid "Select keyboard layout" msgstr "Bitte wählen Sie ein Tastaturlayout aus" @@ -193,13 +193,13 @@ msgid "Select one or more hard drives to use and configure" msgstr "Bitte wählen Sie ein oder mehrere Laufwerk(e) aus, die konfiguriert und verwendet werden sollen" msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." -msgstr "Für die beste Kompatibilität mit ihrer AMD Hardware, sollten Sie womöglich die Quelloffenen oder AMD / ATI Optionen verwenden." +msgstr "Für die beste Kompatibilität mit ihrer AMD Hardware, sollten Sie womöglich die quelloffenen oder andernfalls AMD/ATI-Optionen verwenden." msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" -msgstr "Für die beste Kompatibilität mit ihrer Intel Hardware, sollten Sie womöglich die Quelloffenen oder Intel Optionen verwenden.\n" +msgstr "Für die beste Kompatibilität mit ihrer Intel Hardware, sollten Sie womöglich die quelloffenen oder andernfalls Intel-Optionen verwenden.\n" msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" -msgstr "Für die beste Kompatibilität mit ihrer Nvidia Hardware, sollten Sie womöglich den proprietären Nvidia-Treiber verwenden.\n" +msgstr "Für die beste Kompatibilität mit ihrer Nvidia-Hardware, sollten Sie den proprietären Nvidia-Treiber verwenden.\n" msgid "" "\n" @@ -208,7 +208,7 @@ msgid "" msgstr "" "\n" "\n" -"Bitte wählen Sie einen Grafikkartentreiber aus oder leer lassen um alle Quelloffenen Treiber zu installieren" +"Bitte wählen Sie einen Grafikkartentreiber aus oder leer lassen, um alle quelloffenen Treiber zu installieren" msgid "All open-source (default)" msgstr "Alle Quelloffene (Standard)" @@ -232,19 +232,19 @@ msgid "Adding partition...." msgstr "Partition wird hinzugefügt..." msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." -msgstr "Bitte geben Sie einen gültigen Dateisystemtyp ein um fortzufahren. Verwenden Sie `man parted` um eine Liste von gültigen Typen einzusehen." +msgstr "Bitte geben Sie einen gültigen Dateisystemtyp ein um fortzufahren. Verwenden Sie `man parted` um eine Liste gültiger Typen einzusehen." msgid "Error: Listing profiles on URL \"{}\" resulted in:" -msgstr "Fehler: Auflistung von Profilen mit URL \"{}\" resultierte in:" +msgstr "Fehler: Auflistung von Profilen mit URL \"{}\" ergab:" msgid "Error: Could not decode \"{}\" result as JSON:" -msgstr "Fehler: \"{}\" konnte nicht in als JSON dekodiert werden:" +msgstr "Fehler: \"{}\" konnte nicht zu JSON dekodiert werden:" msgid "Keyboard layout" msgstr "Tastaturlayout" msgid "Mirror region" -msgstr "Spiegelregion" +msgstr "Spiegelserver-Region" msgid "Locale language" msgstr "Lokale Sprache" @@ -268,28 +268,28 @@ msgid "Bootloader" msgstr "Bootloader" msgid "Root password" -msgstr "Root Passwort" +msgstr "Root-Passwort" msgid "Superuser account" -msgstr "Superuser Konto" +msgstr "Superuser-Konto" msgid "User account" msgstr "Benutzerkonto" msgid "Profile" -msgstr "Profile" +msgstr "Profil" msgid "Audio" msgstr "Audio" msgid "Kernels" -msgstr "Kernels" +msgstr "Kernel" msgid "Additional packages" -msgstr "Zus. Packete" +msgstr "Zusätzliche Pakete" msgid "Network configuration" -msgstr "Netzwerkonfiguration" +msgstr "Netzwerkkonfiguration" msgid "Automatic time sync (NTP)" msgstr "Autom. Zeitsynchronisierung (NTP)" @@ -303,9 +303,9 @@ msgid "" "WARNING: Archinstall won't check the suitability of this setup\n" "Do you wish to continue?" msgstr "" -"Sie haben sich entschieden keine Laufwerke auszuwählen\n" -"und jene Einstellungen zu verwenden welche momentan auf {} eingehängt sind (experimentell)\n" -"WARNUNG: Archinstall wird die Kompabilität der Einstellung nicht überprüfen\n" +"Sie haben sich entschieden, kein Laufwerk auszuwählen\n" +"und somit werden Laufwerkseinstellungen verwendet, welche am Einhängeort {} vorgefunden werden (experimentell)\n" +"WARNUNG: Archinstall wird die Kompatibilität der Einstellung nicht überprüfen\n" "Wollen Sie trotzdem fortfahren?" msgid "Re-using partition instance: {}" @@ -318,19 +318,19 @@ msgid "Delete a partition" msgstr "Partition löschen" msgid "Clear/Delete all partitions" -msgstr "Alle partitionen löschen" +msgstr "Alle Partitionen löschen" msgid "Assign mount-point for a partition" msgstr "Einhängeort für Partition angeben" msgid "Mark/Unmark a partition to be formatted (wipes data)" -msgstr "Markieren welche Partition formatiert werden soll (alle Daten werden gelöscht)" +msgstr "Markieren, ob eine Partition formatiert werden soll (alle Daten werden gelöscht)" msgid "Mark/Unmark a partition as encrypted" -msgstr "Markieren welche Partitionen verschlüsselt werden sollen" +msgstr "Markieren, ob eine Partition verschlüsselt werden soll" msgid "Mark/Unmark a partition as bootable (automatic for /boot)" -msgstr "Markieren welche Partition bootbar ist (automatisch für /boot)" +msgstr "Markieren, ob eine Partition als bootfähig sein soll (automatisch für /boot)" msgid "Set desired filesystem for a partition" msgstr "Bitte wählen Sie einen Dateisystemtyp für die Partition aus" @@ -357,7 +357,7 @@ msgid "" "Use ESC to skip\n" "\n" msgstr "" -"ESC um zu Überspringen\n" +"ESC um zu überspringen\n" "\n" msgid "Suggest partition layout" @@ -370,13 +370,13 @@ msgid "Enter a encryption password for {}" msgstr "Verschlüsselungspasswort angeben für {}" msgid "Enter disk encryption password (leave blank for no encryption): " -msgstr "Geben Sie ein Verschlüsselungspasswort ein (leer lassen um zu Verschlüsselung zu deaktivieren): " +msgstr "Geben Sie ein Verschlüsselungspasswort ein (leer lassen um Verschlüsselung zu deaktivieren): " msgid "Create a required super-user with sudo privileges: " msgstr "Geben Sie einen Superuser mit sudo Privilegien an: " msgid "Enter root password (leave blank to disable root): " -msgstr "Geben sie ein Root Passwort ein (leer lassen um Root zu deaktivieren): " +msgstr "Geben Sie ein Root-Passwort ein (leer lassen um Root zu deaktivieren): " msgid "Password for user \"{}\": " msgstr "Passwort für Benutzer \"{}\": " @@ -395,10 +395,10 @@ msgstr "" "Für weitere Informationen wenden Sie sich bitte an das Arch wiki" msgid "Enter a username to create an additional user (leave blank to skip): " -msgstr "Geben Sie einen weiteren Benutzernamen an, um diesen anzulegen (leer lassen um zu Überspringen): " +msgstr "Geben Sie einen Benutzernamen ein, um diesen zusätzlich anzulegen (leer lassen zum Überspringen): " msgid "Use ESC to skip\n" -msgstr "ESC um zu Überspringen\n" +msgstr "ESC um zu überspringen\n" msgid "" "\n" @@ -551,7 +551,7 @@ msgid " mounted at {:16}" msgstr " Eingehängt bei {:16}" msgid " with option {}" -msgstr "mit Option {}" +msgstr " mit Option {}" msgid "" "\n" @@ -564,7 +564,7 @@ msgid "Subvolume name " msgstr "Subvolume name " msgid "Subvolume mountpoint" -msgstr "Subvolume Einhängepunkt" +msgstr "Subvolume Einhängeort" msgid "Subvolume options" msgstr "Subvolume Optionen" @@ -573,13 +573,13 @@ msgid "Save" msgstr "Speichern" msgid "Subvolume name :" -msgstr "Subvolume name :" +msgstr "Subvolume name:" msgid "Select a mount point :" -msgstr "Bitte wählen Sie einen Einhängepunkt aus :" +msgstr "Bitte wählen Sie einen Einhängeort aus:" msgid "Select the desired subvolume options " -msgstr "Wählen Sie die gewünschten Subvolumen Optionen" +msgstr "Wählen Sie die gewünschten Optionen für das Subvolumen " msgid "Define users with sudo privilege, by username: " msgstr "Geben Sie Benutzer mit sudo Privilegien an: " @@ -663,7 +663,7 @@ msgid "Choose which servers to install, if none then a minimal installation will msgstr "Wählen Sie die gewünschten Server aus, welche installiert werden sollen. Sonst wird eine Minimalinstallation durchgeführt" msgid "Installs a minimal system as well as xorg and graphics drivers." -msgstr "Installiert ein minimales System inklusive Xorg und Grafiktreibern" +msgstr "Installiert ein minimales System inklusive Xorg und Grafiktreibern." msgid "Press Enter to continue." msgstr "Drücken sie Enter um fortzufahren." @@ -728,10 +728,10 @@ msgid "Value: " msgstr "Wert: " msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" -msgstr "Sie können das Auswählen eines Laufwerks überspringen und irgendeine Laufwerkskonfiguration verwenden, die bei /mnt eingehängt ist (experimentell)" +msgstr "Sie können das Auswählen eines Laufwerks überspringen und die Laufwerkseinstellungen verwenden, welche am Einhängeort /mnt vorgefunden werden (experimentell)" msgid "Select one of the disks or skip and use /mnt as default" -msgstr "Wählen Sie eine der Laufwerke aus oder überspringen Sie und verwenden Sie /mnt als Standard" +msgstr "Wählen Sie ein Laufwerk aus oder überspringen, um /mnt als Standard zu verwenden" msgid "Select which partitions to mark for formatting:" msgstr "Bitte wählen Sie aus, welche Partitionen formatiert werden sollen:" @@ -746,7 +746,7 @@ msgid "Size" msgstr "Größe" msgid "Free space" -msgstr "Freier Speicherplatz:" +msgstr "Freier Speicherplatz" msgid "Bus-type" msgstr "Bus-Typ" @@ -755,7 +755,7 @@ msgid "Either root-password or at least 1 user with sudo privileges must be spec msgstr "Entweder root Passwort oder mindestens ein Superuser muss konfiguriert sein" msgid "Enter username (leave blank to skip): " -msgstr "Benutzernamen eingeben (leer lassen um zu Überspringen): " +msgstr "Benutzernamen eingeben (leer lassen zum Überspringen): " msgid "The username you entered is invalid. Try again" msgstr "Der eingegebene Benutzername ist ungültig. Erneut versuchen" @@ -806,10 +806,10 @@ msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloa msgstr " - Maximalwert :{} (Erlaubt {} parallele Downloads, erlaubt {} Downloads gleichzeitig)" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" -msgstr "- Minimalwert : 1 (Erlaubt einen parallelen Download, erlaubt zwei Downloads gleichzeitig)" +msgstr " - Minimalwert : 1 (Erlaubt einen parallelen Download, erlaubt zwei Downloads gleichzeitig)" msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" -msgstr "- Deaktivieren/Standard : 0 (Deaktiviert parallele Downloads, erlaubt nur einen Download gleichzeitig)" +msgstr " - Deaktivieren/Standard : 0 (Deaktiviert parallele Downloads, erlaubt nur einen Download gleichzeitig)" #, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" @@ -819,7 +819,7 @@ msgid "Parallel Downloads" msgstr "Parallele Downloads" msgid "ESC to skip" -msgstr "ESC zum überspringen" +msgstr "ESC zum Überspringen" msgid "CTRL+C to reset" msgstr "Strg+C zum zurücksetzen" @@ -870,7 +870,7 @@ msgid "Minimum capacity for Arch Linux partition: {}GiB" msgstr "Minimaler Speicherplatz für Arch Linux Partition: {}GB" msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" -msgstr "Dies ist eine Liste von bereits programmierten Profilen (Backup), diese ermöglichen es Dinge wie Desktop Umgebungen einfacher zu installieren" +msgstr "Dies ist eine Auflistung vorprogrammierter Profile (Backup), die es einfacher ermöglichen, Dinge wie Desktop-Umgebungen zu installieren" msgid "Current profile selection" msgstr "Momentane Profilauswahl" @@ -906,13 +906,13 @@ msgid "This partition is currently encrypted, to format it a filesystem has to b msgstr "Diese Partition ist aktuell verschlüsselt, zum formatieren muss ein Dateisystem angegeben werden" msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr "Die Einhängeorte sind relativ zur Installation, zum Beispiel boot würde bei /boot eingehängt werden" +msgstr "Die Einhängeorte sind relativ zur Installation, boot würde beispielsweise /boot entsprechen." msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." msgstr "Wenn der Einhängeort auf /boot gesetzt ist, wird die Partition ebenfalls als bootbar makiert." msgid "Mountpoint: " -msgstr "Einhängepunkt: " +msgstr "Einhängeort: " msgid "Current free sectors on device {}:" msgstr "Aktuell freie Sektoren auf dem Gerät {}:" @@ -954,7 +954,7 @@ msgid "Select a FIDO2 device to use for HSM" msgstr "FIDO2-Gerät für HSM auswählen" msgid "Use a best-effort default partition layout" -msgstr "Vorgegebendes Partitionslayout verwenden" +msgstr "Empfohlenes Partitionslayout verwenden" msgid "Manual Partitioning" msgstr "Manuelle Partitionierung" @@ -990,13 +990,13 @@ msgid "Back" msgstr "Zurück" msgid "Please chose which greeter to install for the chosen profiles: {}" -msgstr "Bitte Greeter (Anmeldebildschirm) für das ausgewählte Profil auswählen: {}" +msgstr "Bitte einen Greeter (Begrüßer/Anmeldebildschirm) für das ausgewählte Profil auswählen: {}" msgid "Environment type: {}" msgstr "Umgebungstyp: {}" msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" -msgstr "Der proprietäre Nvidia Treiber wird von Sway nicht unterstptzt. Es ist wahrscheinlich, dass Fehler auftreten werden, trotzdem fortfahren?" +msgstr "Der proprietäre Nvidia-Treiber wird von Sway nicht unterstützt. Es ist wahrscheinlich, dass Fehler auftreten werden, trotzdem fortfahren?" msgid "Installed packages" msgstr "Installiere Pakete" @@ -1017,10 +1017,10 @@ msgid "The profile name you entered is already in use. Try again" msgstr "Der eingegebene Profilname wird bereits verwendet. Erneut versuchen" msgid "Packages to be install with this profile (space separated, leave blank to skip): " -msgstr "Zusätzliche Pakete die mit diesem Profil installiert werden sollen (Mit Leerzeichen trennen, leer lassen zum überspringen): " +msgstr "Zusätzliche Pakete die mit diesem Profil installiert werden sollen (Mit Leerzeichen trennen, leer lassen zum Überspringen): " msgid "Services to be enabled with this profile (space separated, leave blank to skip): " -msgstr "Dienste die mit diesem Profil installiert werden sollen (Mit Leerzeichen trennen, leer lassen zum überspringen): " +msgstr "Dienste, die mit diesem Profil aktiviert werden sollen (Mit Leerzeichen trennen, leer lassen zum Überspringen): " msgid "Should this profile be enabled for installation?" msgstr "Soll dieses Profil für die Installation aktiviert werden?" @@ -1046,7 +1046,7 @@ msgid "" msgstr "" "\n" "\n" -"Option um Sway Zugriff auf Hardware zu geben auswählen" +"Option auswählen, um Sway Zugriff auf deine Hardware zu geben" msgid "Graphics driver" msgstr "Grafiktreiber" @@ -1058,7 +1058,7 @@ msgid "Please chose which greeter to install" msgstr "Bitte den zu installierenden Greeter (Begrüßer/Anmeldebildschirm) auswählen" msgid "This is a list of pre-programmed default_profiles" -msgstr "Dies ist eine liste von vorprogrammierten Standardprofilen:" +msgstr "Dies ist eine Auflistung vorprogrammierter Standardprofile" msgid "Disk configuration" msgstr "Laufwerkskonfiguration" @@ -1072,30 +1072,26 @@ msgstr "Finde mögliche Pfade um Konfigurationsdateien zu speichern..." msgid "Select directory (or directories) for saving configuration files" msgstr "Ordner um Konfigurationsdateien zu erstellen auswählen" -#, fuzzy msgid "Add a custom mirror" -msgstr "Benutzerkonto hinzufügen" +msgstr "Benutzerdefinierten Spiegelserver hinzufügen" msgid "Change custom mirror" -msgstr "Benutzerdefinierte Spiegelserver bearbeiten" +msgstr "Benutzerdefinierten Spiegelserver bearbeiten" msgid "Delete custom mirror" msgstr "Benutzerdefinierten Spiegelserver löschen" -#, fuzzy msgid "Enter name (leave blank to skip): " -msgstr "Geben sie einen weiteren Benutzernamen an der angelegt werden soll (leer lassen um zu Überspringen): " +msgstr "Name eingeben (leer lassen zum Überspringen): " -#, fuzzy msgid "Enter url (leave blank to skip): " -msgstr "Geben sie einen weiteren Benutzernamen an der angelegt werden soll (leer lassen um zu Überspringen): " +msgstr "URL eingeben (leer lassen zum Überspringen): " -#, fuzzy msgid "Select signature check option" -msgstr "Laufwerke-layout auswählen" +msgstr "Signaturprüfungs-Option auswählen" msgid "Select signature option" -msgstr "Signatursoption auswählen" +msgstr "Signatur-Option auswählen" msgid "Custom mirrors" msgstr "Benutzerdefinierte Spiegelserver" @@ -1129,7 +1125,7 @@ msgid "Mirrors" msgstr "Spiegelserver" msgid "Mirror regions" -msgstr "Spiegelserverregionen" +msgstr "Spiegelserver-Regionen" msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" msgstr " - Maximalwert : {} ( Erlaubt {} parallele Downloads, erlaubt {max_downloads+1} Downloads gleichzeitig)" @@ -1138,8 +1134,7 @@ msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "Ungültige Eingabe! Erneut mit gültiger Eingabe versuchen [1 bis {}, oder 0 zum deaktivieren]" msgid "Locales" -msgstr "" +msgstr "Lokalisierung" -#, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" -msgstr "NetworkManager benutzen (notwendig um Internet auf grafische Weise in GNOME und KDE einzustellen)" +msgstr "NetworkManager nutzen (notwendig um Internetverbindungen grafisch in GNOME und KDE einzustellen)" -- cgit v1.2.3-70-g09d2 From 31bd5e80e3a1fa6c99159415981b33d43461ee24 Mon Sep 17 00:00:00 2001 From: Stephen Houser Date: Thu, 14 Sep 2023 07:41:48 -0400 Subject: fix custom-commands issue #2025 (#2026) --- archinstall/lib/general.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py index 90af25ed..c1db6fb3 100644 --- a/archinstall/lib/general.py +++ b/archinstall/lib/general.py @@ -460,10 +460,12 @@ def _pid_exists(pid: int) -> bool: def run_custom_user_commands(commands :List[str], installation :Installer) -> None: for index, command in enumerate(commands): script_path = f"/var/tmp/user-command.{index}.sh" - chroot_path = installation.target / script_path + chroot_path = f"{installation.target}/{script_path}" info(f'Executing custom command "{command}" ...') - chroot_path.write_text(command) + with open(chroot_path, "w") as user_script: + user_script.write(command) + SysCommand(f"arch-chroot {installation.target} bash {script_path}") os.unlink(chroot_path) -- cgit v1.2.3-70-g09d2 From 9e079020b1c7301ee5a645b6eda3cb2bfdaec920 Mon Sep 17 00:00:00 2001 From: yenaras <89593949+yenaras@users.noreply.github.com> Date: Thu, 14 Sep 2023 07:49:46 -0400 Subject: fix instructions for Using a Live ISO Images (#2021) replaced `--break-operating-system` with the correct flag `--break-system-packages` and also added the flag as required for `pip uninstall --break-system-packages` --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9d5c3935..5fb0b92a 100644 --- a/README.md +++ b/README.md @@ -208,11 +208,11 @@ you can replace the version of archinstall with a new version and run that with 1. You need a working network connection 2. Install the build requirements with `pacman -Sy; pacman -S git python-pip gcc pkgconf` *(note that this may or may not work depending on your RAM and current state of the squashfs maximum filesystem free space)* -3. Uninstall the previous version of archinstall with `pip uninstall archinstall` +3. Uninstall the previous version of archinstall with `pip uninstall --break-system-packages archinstall` 4. Now clone the latest repository with `git clone https://github.com/archlinux/archinstall` 5. Enter the repository with `cd archinstall` *At this stage, you can choose to check out a feature branch for instance with `git checkout v2.3.1-rc1`* -6. Build the project and install it using `pip install --break-operating-system .` +6. Build the project and install it using `pip install --break-system-packages .` After this, running archinstall with `python -m archinstall` will run against whatever branch you chose in step 5. -- cgit v1.2.3-70-g09d2 From f270a47f27de7176199293a3777d116bf8101cf8 Mon Sep 17 00:00:00 2001 From: Christian Heusel Date: Thu, 14 Sep 2023 14:10:26 +0200 Subject: add the option to use ly as display manager (#2004) Signed-off-by: Christian Heusel --- archinstall/default_profiles/profile.py | 1 + archinstall/lib/profile/profiles_handler.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/archinstall/default_profiles/profile.py b/archinstall/default_profiles/profile.py index ce07c286..c7683013 100644 --- a/archinstall/default_profiles/profile.py +++ b/archinstall/default_profiles/profile.py @@ -34,6 +34,7 @@ class GreeterType(Enum): Lightdm = 'lightdm' Sddm = 'sddm' Gdm = 'gdm' + Ly = 'ly' class SelectResult(Enum): diff --git a/archinstall/lib/profile/profiles_handler.py b/archinstall/lib/profile/profiles_handler.py index 7e6af3d1..8ea646b6 100644 --- a/archinstall/lib/profile/profiles_handler.py +++ b/archinstall/lib/profile/profiles_handler.py @@ -182,6 +182,9 @@ class ProfileHandler: case GreeterType.Gdm: packages = ['gdm'] service = ['gdm'] + case GreeterType.Ly: + packages = ['ly'] + service = ['ly'] if packages: install_session.add_additional_packages(packages) -- cgit v1.2.3-70-g09d2 From 418351d5efdd9f79047c580a422d6a95c536515e Mon Sep 17 00:00:00 2001 From: Hugo Carvalho Date: Fri, 15 Sep 2023 09:41:20 +0100 Subject: Update Portuguese translation (#1976) More corrections --- archinstall/locales/pt/LC_MESSAGES/base.mo | Bin 37201 -> 39351 bytes archinstall/locales/pt/LC_MESSAGES/base.po | 118 +++++++++++++++++++++-------- 2 files changed, 85 insertions(+), 33 deletions(-) diff --git a/archinstall/locales/pt/LC_MESSAGES/base.mo b/archinstall/locales/pt/LC_MESSAGES/base.mo index d83a2f46..4de0b4b3 100644 Binary files a/archinstall/locales/pt/LC_MESSAGES/base.mo and b/archinstall/locales/pt/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pt/LC_MESSAGES/base.po b/archinstall/locales/pt/LC_MESSAGES/base.po index e3fa4904..79c2860d 100644 --- a/archinstall/locales/pt/LC_MESSAGES/base.po +++ b/archinstall/locales/pt/LC_MESSAGES/base.po @@ -16,7 +16,7 @@ msgstr "" "X-Generator: Poedit 3.3.2\n" msgid "[!] A log file has been created here: {} {}" -msgstr "[!] Um ficheiro de registo foi criado aqui: {} {}" +msgstr "[!] Foi criado um ficheiro de registo aqui: {} {}" msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" msgstr " Submeta este problema (e ficheiro) para https://github.com/archlinux/archinstall/issues" @@ -61,19 +61,19 @@ msgid "If you desire a web browser, such as firefox or chromium, you may specify msgstr "Se quer um navegador web, como firefox ou chromium, deve especificá-lo na próxima pergunta." msgid "Write additional packages to install (space separated, leave blank to skip): " -msgstr "Escreva pacotes adicionais para instalar (separados por espaço, deixar em branco para ignorar): " +msgstr "Inserir pacotes adicionais a instalar (separados por espaço, deixar em branco para ignorar): " msgid "Copy ISO network configuration to installation" msgstr "Copiar a configuração de rede da ISO para a instalação" msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" -msgstr "Usar o NetworkManager (necessário para configurar Internet graficamente em GNOME e KDE)" +msgstr "Usar o Gestor de Redes \"NetworkManager\" (necessário para configurar Internet graficamente em GNOME e KDE)" msgid "Select one network interface to configure" msgstr "Selecione uma interface de rede para configurar" msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" -msgstr "Seleccione qual o modo a configurar para \"{}\" ou ignorar para usar o modo padrão \"{}\"" +msgstr "Selecionar qual o modo a configurar para \"{}\" ou ignorar para usar o modo predefinido \"{}\"" msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " msgstr "Inserir o IP e subrede para {} (exemplo: 192.168.0.5/24): " @@ -101,10 +101,10 @@ msgid "Enter a desired filesystem type for the partition" msgstr "Inserir o tipo de sistema de ficheiros desejado para a partição" msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " -msgstr "Inserir o local inicial (em unidades do parted: s, GB, %, etc. ; padrão: {}): " +msgstr "Inserir o local inicial (em unidades do parted: s, GB, %, etc. ; predefinido: {}): " msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " -msgstr "Inserir o local final (em unidades do parted: s, GB, %, etc. ; padrão: {}): " +msgstr "Inserir o local final (em unidades do parted: s, GB, %, etc. ; predefinido: {}): " msgid "{} contains queued partitions, this will remove those, are you sure?" msgstr "{} contem partições em fila, isto irá remover essas, tem a certeza?" @@ -176,7 +176,7 @@ msgid "Archinstall language" msgstr "Idioma do Archinstall" msgid "Wipe all selected drives and use a best-effort default partition layout" -msgstr "Limpar todos os discos selecionados e usar um esquema de partições padrão de melhor desempenho" +msgstr "Limpar todos os discos selecionados e usar um esquema de partições predefinido de melhor desempenho" msgid "Select what to do with each individual drive (followed by partition usage)" msgstr "Seleciona o que fazer com cada disco individual (seguido de uso de partição)" @@ -191,7 +191,7 @@ msgid "Select keyboard layout" msgstr "Seleciona o esquema de teclado" msgid "Select one of the regions to download packages from" -msgstr "Seleciona uma das regiões para descarregar pacotes" +msgstr "Selecione uma das regiões a partir da qual pretende transferir pacotes" msgid "Select one or more hard drives to use and configure" msgstr "Seleciona um ou mais discos rígidos para usar e configurar" @@ -215,10 +215,10 @@ msgstr "" "Seleciona um controlador de gráficos ou deixa em branco para instalar todos os controladores de código-aberto" msgid "All open-source (default)" -msgstr "Todos os controladores de código-aberto (padrão)" +msgstr "Todos os controladores de código-aberto (predefinido)" msgid "Choose which kernels to use or leave blank for default \"{}\"" -msgstr "Escolhe quais kernels usar ou deixa em branco para o padrão \"{}\"" +msgstr "Escolha os kernels a usar ou deixe em branco para a predefinição \"{}\"" msgid "Choose which locale language to use" msgstr "Escolhe qual idioma de localização usar" @@ -546,7 +546,7 @@ msgid "Manage superuser accounts: " msgstr "Administrar contas de superutilizador: " msgid "Manage ordinary user accounts: " -msgstr "Administrar contas de utilizador padrão: " +msgstr "Administrar contas de utilizador normal: " msgid " Subvolume :{:16}" msgstr " Subvolume :{:16}" @@ -592,7 +592,7 @@ msgid "[!] A log file has been created here: {}" msgstr "[!] Um ficheiro de registo foi criado aqui: {}" msgid "Would you like to use BTRFS subvolumes with a default structure?" -msgstr "Deseja usar subvolumes BTRFS com a estrutura padrão?" +msgstr "Deseja usar subvolumes BTRFS com a estrutura predefinida?" msgid "Would you like to use BTRFS compression?" msgstr "Deseja usar a compressão BTRFS?" @@ -735,7 +735,7 @@ msgid "You can skip selecting a drive and partitioning and use whatever drive-se msgstr "Pode ignorar a seleção de unidade e particionar seja lá o que estiver montado em /mnt (experimental)" msgid "Select one of the disks or skip and use /mnt as default" -msgstr "Selecionar um dos discos ou ignore e use /mnt como padrão" +msgstr "Selecionar um dos discos ou ignorar e usar /mnt como predefinição" msgid "Select which partitions to mark for formatting:" msgstr "Selecionar quais partições a marcar para formatar:" @@ -795,25 +795,25 @@ msgid "Configured {} interfaces" msgstr "{} interfaces configuradas" msgid "This option enables the number of parallel downloads that can occur during installation" -msgstr "Esta opção ativa o número de descargas paralelas que podem ocorrer durante a instalação" +msgstr "Esta opção ativa o número de transferências paralelas que podem ocorrer durante a instalação" msgid "" "Enter the number of parallel downloads to be enabled.\n" " (Enter a value between 1 to {})\n" "Note:" msgstr "" -"Insira o número de descargas paralelas a serem ativadas.\n" -" (Insira um valor entre 1 e {})\n" -"Observação:" +"Inserir o número de transferências paralelas a ativar.\n" +" (Inserir um valor entre 1 e {})\n" +"Nota:" msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" -msgstr " - Valor máximo : {} ( Permite {} descargas paralelas, permite {} descargas de cada vez )" +msgstr " - Valor máximo : {} ( Permite {} transferências paralelas, permite {} transferências de cada vez )" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" -msgstr " - Valor mínimo : 1 ( Permite 1 descarga paralela, permite 2 descargas de cada vez )" +msgstr " - Valor mínimo : 1 ( Permite 1 transferência paralela, permite 2 transferências de cada vez )" msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" -msgstr " - Desativar/Padrão : 0 ( Desativa as descargas paralelas, permite apenas 1 descarga de cada vez )" +msgstr " - Desativar/Padrão : 0 ( Desativa as transferências paralelas, permite apenas 1 transferência de cada vez )" #, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" @@ -832,7 +832,7 @@ msgid "TAB to select" msgstr "TAB para selecionar" msgid "[Default value: 0] > " -msgstr "[Valor padrão: 0] > " +msgstr "[Valor predefinido: 0] > " msgid "To be able to use this translation, please install a font manually that supports the language." msgstr "Para poder usar esta tradução, instale manualmente um tipo de letra que suporte o idioma." @@ -925,10 +925,10 @@ msgid "Total sectors: {}" msgstr "Total de setores: {}" msgid "Enter the start sector (default: {}): " -msgstr "Inserir o setor inicial (padrão: {}): " +msgstr "Inserir o setor inicial (predefinido: {}): " msgid "Enter the end sector of the partition (percentage or block number, default: {}): " -msgstr "Inserir o setor final da partição (percentagem ou número de bloco, padrão: {}): " +msgstr "Inserir o setor final da partição (percentagem ou número de bloco, predefinido: {}): " msgid "This will remove all newly added partitions, continue?" msgstr "Isto irá remover todas as partições recém adicionadas, continuar?" @@ -958,7 +958,7 @@ msgid "Select a FIDO2 device to use for HSM" msgstr "Selecionar um dispositivo FIDO2 para usar como HSM" msgid "Use a best-effort default partition layout" -msgstr "Usar um esquema de partição padrão de melhor desempenho" +msgstr "Usar um esquema de partições predefinido de melhor desempenho" msgid "Manual Partitioning" msgstr "Particionamento manual" @@ -970,7 +970,7 @@ msgid "Unknown" msgstr "Desconhecido" msgid "Partition encryption" -msgstr "Encriptação de partição" +msgstr "Encriptação de partições" msgid " ! Formatting {} in " msgstr " ! A formatar {} em " @@ -979,7 +979,7 @@ msgid "← Back" msgstr "← Voltar" msgid "Disk encryption" -msgstr "Encriptação de disco" +msgstr "Encriptação de discos" msgid "Configuration" msgstr "Configuração" @@ -994,7 +994,7 @@ msgid "Back" msgstr "Voltar" msgid "Please chose which greeter to install for the chosen profiles: {}" -msgstr "Escolha qual o ecrã de boas vindas a instalar para os perfis escolhidos: {}" +msgstr "Escolha qual a interface gráfica de início de sessão a instalar para os perfis escolhidos: {}" msgid "Environment type: {}" msgstr "Tipo de ambiente: {}" @@ -1030,7 +1030,7 @@ msgid "Should this profile be enabled for installation?" msgstr "Este perfil deve ser ativado para instalação?" msgid "Create your own" -msgstr "Crie o seu próprio" +msgstr "Criar o seu próprio" msgid "" "\n" @@ -1041,7 +1041,7 @@ msgstr "" "Selecionar um controlador gráfico ou deixe em branco para instalar todos os controladores de código aberto" msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" -msgstr "O Sway precisa de acesso ao seu \"seat\" (conjunto de dispositivos de hardware, como teclado, rato, etc)" +msgstr "O Sway precisa de acesso ao seu \"seat\" (conjunto de dispositivos de hardware, como o teclado, o rato, etc)" msgid "" "\n" @@ -1056,10 +1056,10 @@ msgid "Graphics driver" msgstr "Controlador gráfico" msgid "Greeter" -msgstr "Ecrã de boas vindas" +msgstr "Interface gráfica de início de sessão" msgid "Please chose which greeter to install" -msgstr "Escolha qual o ecrã de boas vindas a instalar" +msgstr "Escolha qual a interface gráfica de início de sessão a instalar" msgid "This is a list of pre-programmed default_profiles" msgstr "Esta é uma lista de perfis pré-programados (default_profiles)" @@ -1132,7 +1132,7 @@ msgid "Mirror regions" msgstr "Regiões dos espelhos" msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - Valor máximo : {} ( Permite {} descargas paralelas, permite {max_downloads+1} descargas de cada vez )" +msgstr " - Valor máximo : {} ( Permite {} transferências paralelas, permite {max_downloads+1} transferências de cada vez )" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {}, ou 0 para desativar]" @@ -1141,7 +1141,59 @@ msgid "Locales" msgstr "Localidades" msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" -msgstr "Usar o NetworkManager (necessário para configurar a Internet graficamente no GNOME e KDE)" +msgstr "Usar o Gestor de Redes \"NetworkManager\" (necessário para configurar a Internet graficamente no GNOME e KDE)" + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "Não foi possível determinar os dispositivos fido2. A libfido2 está instalada?" + +msgid "Path" +msgstr "Caminho" + +msgid "Manufacturer" +msgstr "Fabricante" + +msgid "Product" +msgstr "Produto" + +msgid "Total: {} / {}" +msgstr "Total: {} / {}" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "Todos os valores inseridos podem ser sufixados com uma unidade: B, KB, KiB, MB, MiB..." + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "Se nenhuma unidade for fornecida, o valor será interpretado como setores" + +msgid "Enter start (default: sector {}): " +msgstr "Inserir o início (predefinido: sector {}): " + +msgid "Enter end (default: {}): " +msgstr "Inserir o fim (predefinido: {}): " + +#, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "Configuração inválida: {error}" + +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "Esta opção ativa o número de transferências paralelas que podem ocorrer durante as transferências de pacotes" + +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"Inserir o número de transferências paralelas a ativar.\n" +"\n" +"Nota:\n" + +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - Valor máximo recomendado : {} ( Permite {} transferências paralelas de cada vez )" + +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - Desativar/Predefinido : 0 ( Desativa as transferências paralelas, permite apenas 1 transferência de cada vez )\n" + +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "Entrada inválida! Tente novamente com uma entrada válida [ou 0 para desativar]" #~ msgid "Add :" #~ msgstr "Adicionar :" -- cgit v1.2.3-70-g09d2 From 06d46ac762f58d051b2dba1aea50a2e3314ab403 Mon Sep 17 00:00:00 2001 From: Campbell Jones Date: Fri, 15 Sep 2023 07:51:36 -0400 Subject: Add option to use slick-greeter with lightdm (#1970) --- archinstall/default_profiles/desktops/budgie.py | 2 +- archinstall/default_profiles/profile.py | 3 ++- archinstall/lib/profile/profiles_handler.py | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/archinstall/default_profiles/desktops/budgie.py b/archinstall/default_profiles/desktops/budgie.py index 68506d45..28c05f45 100644 --- a/archinstall/default_profiles/desktops/budgie.py +++ b/archinstall/default_profiles/desktops/budgie.py @@ -23,4 +23,4 @@ class BudgieProfile(XorgProfile): @property def default_greeter_type(self) -> Optional[GreeterType]: - return GreeterType.Lightdm + return GreeterType.LightdmSlick diff --git a/archinstall/default_profiles/profile.py b/archinstall/default_profiles/profile.py index c7683013..982bd5a3 100644 --- a/archinstall/default_profiles/profile.py +++ b/archinstall/default_profiles/profile.py @@ -31,7 +31,8 @@ class ProfileType(Enum): class GreeterType(Enum): - Lightdm = 'lightdm' + Lightdm = 'lightdm-gtk-greeter' + LightdmSlick = 'lightdm-slick-greeter' Sddm = 'sddm' Gdm = 'gdm' Ly = 'ly' diff --git a/archinstall/lib/profile/profiles_handler.py b/archinstall/lib/profile/profiles_handler.py index 8ea646b6..07910b08 100644 --- a/archinstall/lib/profile/profiles_handler.py +++ b/archinstall/lib/profile/profiles_handler.py @@ -173,6 +173,9 @@ class ProfileHandler: service = None match greeter: + case GreeterType.LightdmSlick: + packages = ['lightdm', 'lightdm-slick-greeter'] + service = ['lightdm'] case GreeterType.Lightdm: packages = ['lightdm', 'lightdm-gtk-greeter'] service = ['lightdm'] @@ -191,6 +194,17 @@ class ProfileHandler: if service: install_session.enable_service(service) + # slick-greeter requires a config change + if greeter == GreeterType.LightdmSlick: + path = install_session.target.joinpath('etc/lightdm/lightdm.conf') + with open(path, 'r') as file: + filedata = file.read() + + filedata = filedata.replace('#greeter-session=example-gtk-gnome', 'greeter-session=lightdm-slick-greeter') + + with open(path, 'w') as file: + file.write(filedata) + def install_gfx_driver(self, install_session: 'Installer', driver: Optional[GfxDriver]): try: -- cgit v1.2.3-70-g09d2 From 2cc0d64619671fbc91a5f3a090c7c239be1d8a84 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Sat, 16 Sep 2023 14:58:20 +0200 Subject: Added a forced buffer flush on each worker stdin write (#2057) --- archinstall/lib/general.py | 1 + 1 file changed, 1 insertion(+) diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py index c1db6fb3..611378ee 100644 --- a/archinstall/lib/general.py +++ b/archinstall/lib/general.py @@ -217,6 +217,7 @@ class SysCommandWorker: if self.child_fd: return os.write(self.child_fd, data + (b'\n' if line_ending else b'')) + os.fsync(self.child_fd) return 0 -- cgit v1.2.3-70-g09d2 From c9e2027725a75f08cee51e1fe02af93650d9c340 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Sat, 16 Sep 2023 17:30:46 -0400 Subject: Fix encryption debug message in `_add_efistub_bootloader()` (#1991) --- archinstall/lib/installer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 4e6e9603..d010e8a1 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -1073,10 +1073,10 @@ TIMEOUT=5 if root_partition in self._disk_encryption.partitions: # TODO: We need to detect if the encrypted device is a whole disk encryption, # or simply a partition encryption. Right now we assume it's a partition (and we always have) - debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}') + debug(f'Root partition is an encrypted device identifying by PARTUUID: {root_partition.partuuid}') kernel_parameters.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:luksdev root=/dev/mapper/luksdev rw rootfstype={root_partition.safe_fs_type.value} {" ".join(self._kernel_params)}') else: - debug(f'Root partition is an encrypted device identifying by PARTUUID: {root_partition.partuuid}') + debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}') kernel_parameters.append(f'root=PARTUUID={root_partition.partuuid} rw rootfstype={root_partition.safe_fs_type.value} {" ".join(self._kernel_params)}') parent_dev_path = disk.device_handler.get_parent_device_path(boot_partition.safe_dev_path) -- cgit v1.2.3-70-g09d2 From 47ed711743f66fd4cabeb270952405674ef985ba Mon Sep 17 00:00:00 2001 From: Wu Xiaotian Date: Sun, 17 Sep 2023 07:08:38 +0800 Subject: Support CJK text alignment (#2012) * Add functions for unicode string alignment * use unicode alignment function to show menu * Allow table content to support unicode text alignment --- archinstall/lib/menu/abstract_menu.py | 21 ++------------------ archinstall/lib/output.py | 37 ++++++++++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/archinstall/lib/menu/abstract_menu.py b/archinstall/lib/menu/abstract_menu.py index f12d2b73..306c500a 100644 --- a/archinstall/lib/menu/abstract_menu.py +++ b/archinstall/lib/menu/abstract_menu.py @@ -1,32 +1,15 @@ from __future__ import annotations -import unicodedata from typing import Callable, Any, List, Iterator, Tuple, Optional, Dict, TYPE_CHECKING from .menu import Menu, MenuSelectionType from ..output import error +from ..output import unicode_ljust from ..translationhandler import TranslationHandler, Language if TYPE_CHECKING: _: Any -def count_cjk_chars(string): - "Count the total number of CJK characters contained in a string" - return sum(unicodedata.east_asian_width(c) in 'FW' for c in string) - -def cjkljust(string, width, fillbyte=' '): - """Support left alignment of Chinese, Japanese, Korean text - >>> cjkljust('Hello', 15, '*') - 'Hello**********' - >>> cjkljust('你好', 15, '*') - '你好***********' - >>> cjkljust('안녕하세요', 15, '*') - '안녕하세요*****' - >>> cjkljust('こんにちは', 15, '*') - 'こんにちは*****' - """ - return string.ljust(width - count_cjk_chars(string), fillbyte) - class Selector: def __init__( self, @@ -145,7 +128,7 @@ class Selector: if current: padding += 5 - description = cjkljust(str(self._description), padding, ' ') + description = unicode_ljust(str(self._description), padding, ' ') current = current else: description = self._description diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py index 9f2a2ae3..63d9c1fb 100644 --- a/archinstall/lib/output.py +++ b/archinstall/lib/output.py @@ -1,6 +1,7 @@ import logging import os import sys +import unicodedata from enum import Enum from pathlib import Path @@ -83,7 +84,7 @@ class FormattedOutput: if capitalize: key = key.capitalize() - key_list.append(key.ljust(width)) + key_list.append(unicode_ljust(key, width)) output += ' | '.join(key_list) + '\n' output += '-' * len(output) + '\n' @@ -99,9 +100,9 @@ class FormattedOutput: value = '*' * width if isinstance(value, (int, float)) or (isinstance(value, str) and value.isnumeric()): - obj_data.append(str(value).rjust(width)) + obj_data.append(unicode_rjust(str(value), width)) else: - obj_data.append(str(value).ljust(width)) + obj_data.append(unicode_ljust(str(value), width)) output += ' | '.join(obj_data) + '\n' @@ -326,3 +327,33 @@ def log( if level != logging.DEBUG or storage.get('arguments', {}).get('verbose', False): sys.stdout.write(f"{text}\n") sys.stdout.flush() + +def _count_wchars(string: str) -> int: + "Count the total number of wide characters contained in a string" + return sum(unicodedata.east_asian_width(c) in 'FW' for c in string) + +def unicode_ljust(string: str, width: int, fillbyte: str = ' ') -> str: + """Return a left-justified unicode string of length width. + >>> unicode_ljust('Hello', 15, '*') + 'Hello**********' + >>> unicode_ljust('你好', 15, '*') + '你好***********' + >>> unicode_ljust('안녕하세요', 15, '*') + '안녕하세요*****' + >>> unicode_ljust('こんにちは', 15, '*') + 'こんにちは*****' + """ + return string.ljust(width - _count_wchars(string), fillbyte) + +def unicode_rjust(string: str, width: int, fillbyte: str = ' ') -> str: + """Return a right-justified unicode string of length width. + >>> unicode_rjust('Hello', 15, '*') + '**********Hello' + >>> unicode_rjust('你好', 15, '*') + '***********你好' + >>> unicode_rjust('안녕하세요', 15, '*') + '*****안녕하세요' + >>> unicode_rjust('こんにちは', 15, '*') + '*****こんにちは' + """ + return string.rjust(width - _count_wchars(string), fillbyte) -- cgit v1.2.3-70-g09d2 From f4a6d11373c61f77236f95b2a97f505c6eab55a2 Mon Sep 17 00:00:00 2001 From: Himadri Bhattacharjee Date: Sat, 16 Sep 2023 23:21:08 +0000 Subject: fix: make nodatacow and compress mutually exclusive (#1998) * fix: make nodatacow and compress mutually exclusive * fix: raise ValueError when both compress and nodatacow flags are set --- archinstall/lib/disk/device_model.py | 10 ++++++++-- archinstall/lib/disk/subvolume_menu.py | 1 - 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index 28ee3116..6eeb0d91 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -412,12 +412,18 @@ class SubvolumeModification: mountpoint = Path(entry['mountpoint']) if entry['mountpoint'] else None + compress = entry.get('compress', False) + nodatacow = entry.get('nodatacow', False) + + if compress and nodatacow: + raise ValueError('compress and nodatacow flags cannot be enabled simultaneously on a btfrs subvolume') + mods.append( SubvolumeModification( entry['name'], mountpoint, - entry.get('compress', False), - entry.get('nodatacow', False) + compress, + nodatacow ) ) diff --git a/archinstall/lib/disk/subvolume_menu.py b/archinstall/lib/disk/subvolume_menu.py index 32a0e616..2b70d7b2 100644 --- a/archinstall/lib/disk/subvolume_menu.py +++ b/archinstall/lib/disk/subvolume_menu.py @@ -46,7 +46,6 @@ class SubvolumeMenu(ListManager): ['nodatacow', 'compress'], skip=True, preset_values=preset_options, - multi=True ).run() if choice.type_ == MenuSelectionType.Selection: -- cgit v1.2.3-70-g09d2 From b9d962bec2466d096b38c5c4e519e79c0162a0eb Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Sun, 17 Sep 2023 03:14:47 -0400 Subject: Refactor `_add_grub_bootloader()` installation (#1968) * Refactor `_add_grub_bootloader()` installation * Remove whitespace --- archinstall/lib/installer.py | 52 +++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index d010e8a1..c32069c3 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -876,45 +876,43 @@ class Installer: info(f"GRUB boot partition: {boot_partition.dev_path}") + command = [ + '/usr/bin/arch-chroot', + str(self.target), + 'grub-install', + '--debug' + ] + if SysInfo.has_uefi(): self.pacman.strap('efibootmgr') # TODO: Do we need? Yes, but remove from minimal_installation() instead? + add_options = [ + '--target=x86_64-efi', + f'--efi-directory={boot_partition.mountpoint}', + '--bootloader-id=GRUB', + '--removable' + ] + + command.extend(add_options) + try: - SysCommand( - f'/usr/bin/arch-chroot {self.target} grub-install ' - f'--debug ' - f'--target=x86_64-efi ' - f'--efi-directory={boot_partition.mountpoint} ' - f'--bootloader-id=GRUB ' - f'--removable', - peek_output=True - ) + SysCommand(command, peek_output=True) except SysCallError: try: - SysCommand( - f'/usr/bin/arch-chroot {self.target} ' - f'grub-install ' - f'--debug ' - f'--target=x86_64-efi ' - f'--efi-directory={boot_partition.mountpoint} ' - f'--bootloader-id=GRUB ' - f'--removable', - peek_output=True - ) + SysCommand(command, peek_output=True) except SysCallError as err: raise DiskError(f"Could not install GRUB to {self.target}{boot_partition.mountpoint}: {err}") else: parent_dev_path = disk.device_handler.get_parent_device_path(boot_partition.safe_dev_path) - try: - cmd = f'/usr/bin/arch-chroot' \ - f' {self.target}' \ - f' grub-install' \ - f' --debug' \ - f' --target=i386-pc' \ - f' --recheck {parent_dev_path}' + add_options = [ + '--target=i386-pc', + '--recheck', + str(parent_dev_path) + ] - SysCommand(cmd, peek_output=True) + try: + SysCommand(command + add_options, peek_output=True) except SysCallError as err: raise DiskError(f"Failed to install GRUB boot on {boot_partition.dev_path}: {err}") -- cgit v1.2.3-70-g09d2 From 15e52b77683f266ee843def2bb35ed02cd8e559d Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Sun, 17 Sep 2023 03:17:48 -0400 Subject: Refactor `_add_efistub_bootloader()` cmdline (#2059) --- archinstall/lib/installer.py | 58 ++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index c32069c3..01c92d3b 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -1047,37 +1047,43 @@ TIMEOUT=5 # points towards the same disk and/or partition. # And in which case we should do some clean up. - for kernel in self.kernels: - # Setup the firmware entry - label = f'Arch Linux ({kernel})' - loader = f"/vmlinuz-{kernel}" + microcode = [] + + if not SysInfo.is_vm(): + vendor = SysInfo.cpu_vendor() + if vendor == "AuthenticAMD": + microcode.append("initrd=\\amd-ucode.img") + elif vendor == "GenuineIntel": + microcode.append("initrd=\\intel-ucode.img") + else: + debug(f"Unknown CPU vendor '{vendor}' detected. Archinstall won't add any ucode to firmware boot entry.") - kernel_parameters = [] + # blkid doesn't trigger on loopback devices really well, + # so we'll use the old manual method until we get that sorted out. - if not SysInfo.is_vm(): - vendor = SysInfo.cpu_vendor() - if vendor == "AuthenticAMD": - kernel_parameters.append("initrd=\\amd-ucode.img") - elif vendor == "GenuineIntel": - kernel_parameters.append("initrd=\\intel-ucode.img") - else: - debug(f"Unknown CPU vendor '{vendor}' detected. Archinstall won't add any ucode to firmware boot entry.") + kernel_parameters = [] - kernel_parameters.append(f"initrd=\\initramfs-{kernel}.img") + if root_partition in self._disk_encryption.partitions: + # TODO: We need to detect if the encrypted device is a whole disk encryption, + # or simply a partition encryption. Right now we assume it's a partition (and we always have) + debug(f'Root partition is an encrypted device identifying by PARTUUID: {root_partition.partuuid}') + kernel_parameters.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:luksdev root=/dev/mapper/luksdev rw rootfstype={root_partition.safe_fs_type.value} {" ".join(self._kernel_params)}') + else: + debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}') + kernel_parameters.append(f'root=PARTUUID={root_partition.partuuid} rw rootfstype={root_partition.safe_fs_type.value} {" ".join(self._kernel_params)}') - # blkid doesn't trigger on loopback devices really well, - # so we'll use the old manual method until we get that sorted out. + parent_dev_path = disk.device_handler.get_parent_device_path(boot_partition.safe_dev_path) - if root_partition in self._disk_encryption.partitions: - # TODO: We need to detect if the encrypted device is a whole disk encryption, - # or simply a partition encryption. Right now we assume it's a partition (and we always have) - debug(f'Root partition is an encrypted device identifying by PARTUUID: {root_partition.partuuid}') - kernel_parameters.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:luksdev root=/dev/mapper/luksdev rw rootfstype={root_partition.safe_fs_type.value} {" ".join(self._kernel_params)}') - else: - debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}') - kernel_parameters.append(f'root=PARTUUID={root_partition.partuuid} rw rootfstype={root_partition.safe_fs_type.value} {" ".join(self._kernel_params)}') + for kernel in self.kernels: + # Setup the firmware entry + label = f'Arch Linux ({kernel})' + loader = f"/vmlinuz-{kernel}" - parent_dev_path = disk.device_handler.get_parent_device_path(boot_partition.safe_dev_path) + cmdline = [] + + cmdline.extend(microcode) + cmdline.append(f"initrd=\\initramfs-{kernel}.img") + cmdline.extend(kernel_parameters) cmd = f'efibootmgr ' \ f'--disk {parent_dev_path} ' \ @@ -1085,7 +1091,7 @@ TIMEOUT=5 f'--create ' \ f'--label "{label}" ' \ f'--loader {loader} ' \ - f'--unicode \'{" ".join(kernel_parameters)}\' ' \ + f'--unicode \'{" ".join(cmdline)}\' ' \ f'--verbose' SysCommand(cmd) -- cgit v1.2.3-70-g09d2 From 91c2906f3cc30903986b77a320e086856f05d080 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Sun, 17 Sep 2023 22:12:34 +1000 Subject: Fix broken action check (#2060) * Fix broken action check --- .github/workflows/translation-check.yaml | 17 +- archinstall/locales/ar/LC_MESSAGES/base.po | 52 +++ archinstall/locales/base.pot | 57 +++ archinstall/locales/cs/LC_MESSAGES/base.po | 62 +++ archinstall/locales/de/LC_MESSAGES/base.po | 63 +++ archinstall/locales/el/LC_MESSAGES/base.po | 62 +++ archinstall/locales/en/LC_MESSAGES/base.po | 52 +++ archinstall/locales/es/LC_MESSAGES/base.po | 61 +-- archinstall/locales/et/LC_MESSAGES/base.po | 63 +++ archinstall/locales/fr/LC_MESSAGES/base.po | 560 +++++++++----------------- archinstall/locales/id/LC_MESSAGES/base.po | 62 +++ archinstall/locales/it/LC_MESSAGES/base.po | 62 +++ archinstall/locales/ka/LC_MESSAGES/base.po | 62 +++ archinstall/locales/ko/LC_MESSAGES/base.po | 62 +++ archinstall/locales/nl/LC_MESSAGES/base.po | 54 +++ archinstall/locales/pl/LC_MESSAGES/base.po | 62 +++ archinstall/locales/pt/LC_MESSAGES/base.po | 27 +- archinstall/locales/pt_BR/LC_MESSAGES/base.po | 63 +++ archinstall/locales/ro/LC_MESSAGES/base.po | 53 +-- archinstall/locales/ru/LC_MESSAGES/base.po | 62 +++ archinstall/locales/sv/LC_MESSAGES/base.po | 54 +++ archinstall/locales/ta/LC_MESSAGES/base.po | 63 +++ archinstall/locales/tr/LC_MESSAGES/base.mo | Bin 31241 -> 31750 bytes archinstall/locales/tr/LC_MESSAGES/base.po | 67 ++- archinstall/locales/uk/LC_MESSAGES/base.po | 62 +++ archinstall/locales/ur/LC_MESSAGES/base.po | 54 +++ archinstall/locales/zh-CN/LC_MESSAGES/base.mo | Bin 32120 -> 33967 bytes archinstall/locales/zh-CN/LC_MESSAGES/base.po | 63 +++ archinstall/locales/zh-TW/LC_MESSAGES/base.po | 63 +++ 29 files changed, 1601 insertions(+), 443 deletions(-) diff --git a/.github/workflows/translation-check.yaml b/.github/workflows/translation-check.yaml index 43f114ac..11418d31 100644 --- a/.github/workflows/translation-check.yaml +++ b/.github/workflows/translation-check.yaml @@ -5,7 +5,7 @@ on: pull_request: paths: - 'archinstall/locales/**' -name: translation file checks +name: Verify local_generate script was run on translation changes jobs: translation-check: runs-on: ubuntu-latest @@ -13,10 +13,11 @@ jobs: image: archlinux:latest steps: - uses: actions/checkout@v4 - - run: pacman --noconfirm -Syu python - - run: cd archinstall/locales - - run: pwd - - run: ls -l - - run: bash locales_generator.sh - - run: git diff --name-only - - run: ls + - run: pacman --noconfirm -Syu python git diffutils + - run: | + cd .. + cp -r archinstall archinstall_orig + cd archinstall/archinstall/locales + bash locales_generator.sh + cd ../../.. + git diff --no-index --name-only archinstall_orig archinstall diff --git a/archinstall/locales/ar/LC_MESSAGES/base.po b/archinstall/locales/ar/LC_MESSAGES/base.po index 68e1e968..41bcda0c 100644 --- a/archinstall/locales/ar/LC_MESSAGES/base.po +++ b/archinstall/locales/ar/LC_MESSAGES/base.po @@ -1090,3 +1090,55 @@ msgstr "" #, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "استخدم مُدير الشبكة (ضروري لإعداد الإنترنت باستخدام واجهة رسومية في جنوم و كيدي)" + +msgid "Total: {} / {}" +msgstr "" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +msgid "Enter start (default: sector {}): " +msgstr "" + +msgid "Enter end (default: {}): " +msgstr "" + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "" + +msgid "Type" +msgstr "" + +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "" + +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" + +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr "" + +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr "" + +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "" diff --git a/archinstall/locales/base.pot b/archinstall/locales/base.pot index 5bc4ae03..75e47c9a 100644 --- a/archinstall/locales/base.pot +++ b/archinstall/locales/base.pot @@ -1161,3 +1161,60 @@ msgid "" "Use NetworkManager (necessary to configure internet graphically in GNOME and " "KDE)" msgstr "" + +msgid "Total: {} / {}" +msgstr "" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +msgid "Enter start (default: sector {}): " +msgstr "" + +msgid "Enter end (default: {}): " +msgstr "" + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "" + +msgid "Type" +msgstr "" + +msgid "" +"This option enables the number of parallel downloads that can occur during " +"package downloads" +msgstr "" + +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" + +msgid "" +" - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr "" + +msgid "" +" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 " +"download at a time )\n" +msgstr "" + +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "" diff --git a/archinstall/locales/cs/LC_MESSAGES/base.po b/archinstall/locales/cs/LC_MESSAGES/base.po index a7eb10ec..bc771879 100644 --- a/archinstall/locales/cs/LC_MESSAGES/base.po +++ b/archinstall/locales/cs/LC_MESSAGES/base.po @@ -1176,3 +1176,65 @@ msgstr "" #, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "Použít NetworkManager (potřebné pro grafickou konfiguraci v GNOME a KDE)" + +msgid "Total: {} / {}" +msgstr "" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "Zadejte počáteční sektor (procenta nebo číslo bloku, výchozí: {}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "Zadejte počáteční sektor (procenta nebo číslo bloku, výchozí: {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "Ruční konfigurace" + +msgid "Type" +msgstr "" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "Tato možnost povolí specifikovaný počet paralelních stahování, která mohou nastat při instalaci" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"Zadejte povolený počet paralelních stahování.\n" +" (Zadejte hodnotu mezi 1 a {})\n" +"Poznámka:" + +#, fuzzy +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - Maximální hodnota : {} (Povolí {} paralelních stahování, povolí {} stahování naráz )" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - Zakázáno/Výchozí : 0 (Zakáže paralelní stahování, povolí pouze 1 stahování naráz)" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "Neplatný vstup! Zkuste to, prosím, znovu s platným vstupem [1 až {}, nebo 0 pro vypnutí]" diff --git a/archinstall/locales/de/LC_MESSAGES/base.po b/archinstall/locales/de/LC_MESSAGES/base.po index 02b4b485..83918b05 100644 --- a/archinstall/locales/de/LC_MESSAGES/base.po +++ b/archinstall/locales/de/LC_MESSAGES/base.po @@ -1138,3 +1138,66 @@ msgstr "Lokalisierung" msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "NetworkManager nutzen (notwendig um Internetverbindungen grafisch in GNOME und KDE einzustellen)" + +#, fuzzy +msgid "Total: {} / {}" +msgstr "Gesamtlänge: {}" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "Bitte geben Sie den Startsektor ein (Standard: {}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "Bitte geben Sie den Startsektor ein (Standard: {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "Manuelle Konfiguration" + +msgid "Type" +msgstr "" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "Diese Option setzt die Nummer an parallelen Downloads, die während der Installtion durchgeführt werden" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"Geben Sie die Nummer an parallelen Downloads an.\n" +" (Wert zwischen 1 und {max_downloads})\n" +"Achtung:" + +#, fuzzy +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - Maximalwert :{} (Erlaubt {} parallele Downloads, erlaubt {} Downloads gleichzeitig)" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - Deaktivieren/Standard : 0 (Deaktiviert parallele Downloads, erlaubt nur einen Download gleichzeitig)" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "Ungültige Eingabe! Erneut mit gültiger Eingabe versuchen [1 bis {}, oder 0 zum deaktivieren]" diff --git a/archinstall/locales/el/LC_MESSAGES/base.po b/archinstall/locales/el/LC_MESSAGES/base.po index 802356ac..592cfe77 100644 --- a/archinstall/locales/el/LC_MESSAGES/base.po +++ b/archinstall/locales/el/LC_MESSAGES/base.po @@ -1183,3 +1183,65 @@ msgstr "" #, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "Χρήση NetworkManager (απαραίτητος για τη διαμόρφωση του δικτύου γραφικά σε GNOME και KDE)" + +msgid "Total: {} / {}" +msgstr "" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "Εισάγετε τον start sector (ποσοστό ή αριθμό block, προκαθορισμένο {}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "Εισάγετε τον start sector (ποσοστό ή αριθμό block, προκαθορισμένο {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "Χειροκίνητη διαμόρφωση" + +msgid "Type" +msgstr "" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "Αυτή η επιλογή θέτει τον αριθμό των παράλληλων λήψεων που μπορούν να συμβούν κατά την εγκατάσταση" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"Εισάγετε τον αριθμό των παράλληλων λήψεων προς ενεργοποίηση.\n" +" (Εισάγετε μία τιμή από 1 μέχρι {})\n" +"Σημείωση:" + +#, fuzzy +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - Μέγιστη τιμή : {} ( Επιτρέπει {} παράλληλες λήψεις, επιτρέπει {} λήψεις σε μία στιγμή )" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - Απενεργοποίηση/Προκαθορισμένο : 0 ( Απενεργοποιεί τις παράλληλες λήψεις, επιτρέπει μόνο 1 λήψη σε μία στιγμή )" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "Μη έγκυρη είσοδος! Προσπαθήστε ξανά με μία έγκυρη είσοδο [1 μέχρι {}, ή 0 για απενεργοποίηση]" diff --git a/archinstall/locales/en/LC_MESSAGES/base.po b/archinstall/locales/en/LC_MESSAGES/base.po index 336a419f..c20d05cf 100644 --- a/archinstall/locales/en/LC_MESSAGES/base.po +++ b/archinstall/locales/en/LC_MESSAGES/base.po @@ -1078,3 +1078,55 @@ msgstr "" msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "" + +msgid "Total: {} / {}" +msgstr "" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +msgid "Enter start (default: sector {}): " +msgstr "" + +msgid "Enter end (default: {}): " +msgstr "" + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "" + +msgid "Type" +msgstr "" + +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "" + +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" + +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr "" + +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr "" + +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "" diff --git a/archinstall/locales/es/LC_MESSAGES/base.po b/archinstall/locales/es/LC_MESSAGES/base.po index b218b772..330d0d9e 100644 --- a/archinstall/locales/es/LC_MESSAGES/base.po +++ b/archinstall/locales/es/LC_MESSAGES/base.po @@ -1191,10 +1191,42 @@ msgstr "" msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "Usar NetworkManager (necesario para configurar internet gráficamente en GNOME y KDE)" +msgid "Total: {} / {}" +msgstr "" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "Todos los valores ingresados pueden tener una unidad como sufijo: B, KB, KiB, MB, MiB ..." + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "Si no se proporciona ninguna unidad, el valor se interpreta como sectores" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "Introduzca el sector de inicio (porcentaje o número de bloque, predeterminado: {}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "Introduzca el sector de inicio (porcentaje o número de bloque, predeterminado: {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "Fabricante" + +msgid "Product" +msgstr "Producto" + #, fuzzy, python-brace-format msgid "Invalid configuration: {error}" msgstr "Configuración manual" +msgid "Type" +msgstr "" + #, fuzzy msgid "This option enables the number of parallel downloads that can occur during package downloads" msgstr "Esta opción habilita la cantidad de descargas paralelas que pueden ocurrir durante la instalación" @@ -1221,35 +1253,6 @@ msgstr " - Deshabilitar/Predeterminado : 0 ( Deshabilita la descarga paralela, p msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "¡Entrada no válida! Intente nuevamente con un valor válido [1 a {}, o 0 para deshabilitar]" -msgid "Total: {} / {}" -msgstr "" - -msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." -msgstr "Todos los valores ingresados pueden tener una unidad como sufijo: B, KB, KiB, MB, MiB ..." - -msgid "If no unit is provided, the value is interpreted as sectors" -msgstr "Si no se proporciona ninguna unidad, el valor se interpreta como sectores" - -#, fuzzy -msgid "Enter start (default: sector {}): " -msgstr "Introduzca el sector de inicio (porcentaje o número de bloque, predeterminado: {}): " - -#, fuzzy -msgid "Enter end (default: {}): " -msgstr "Introduzca el sector de inicio (porcentaje o número de bloque, predeterminado: {}): " - -msgid "Unable to determine fido2 devices. Is libfido2 installed?" -msgstr "" - -msgid "Path" -msgstr "" - -msgid "Manufacturer" -msgstr "Fabricante" - -msgid "Product" -msgstr "Producto" - #~ msgid "Add :" #~ msgstr "Añadir :" diff --git a/archinstall/locales/et/LC_MESSAGES/base.po b/archinstall/locales/et/LC_MESSAGES/base.po index 6a1b9238..f7f15578 100644 --- a/archinstall/locales/et/LC_MESSAGES/base.po +++ b/archinstall/locales/et/LC_MESSAGES/base.po @@ -1155,3 +1155,66 @@ msgstr "" #, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "Kasutage NetworkManagerit (vajalik interneti graafiliseks konfigureerimiseks GNOME-s ja KDE-s)." + +#, fuzzy +msgid "Total: {} / {}" +msgstr "Kogupikkus: {}" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "Sisestage algussektor (vaikimisi: {}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "Sisestage algussektor (vaikimisi: {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "Manuaalne konfiguratsioon" + +msgid "Type" +msgstr "" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "See valik võimaldab installimise ajal valida paralleelsete allalaadimiste arvu" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"Sisestage lubatavate paralleelsete allalaadimiste arv.\n" +" (Sisestage väärtus vahemikus 1 kuni {max_downloads})\n" +"note:" + +#, fuzzy +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - Minimaalne väärtus : 1 ( Võimaldab 1 paralleelset allalaadimist, võimaldab 2 allalaadimist korraga )" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - Keela/Vaikimisi : 0 ( keelab paralleelse allalaadimise, võimaldab ainult 1 allalaadimist korraga )" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "Vale sisestus! Proovige uuesti kehtiva sisendiga [1 {max_downloads} või 0 keelamiseks]." diff --git a/archinstall/locales/fr/LC_MESSAGES/base.po b/archinstall/locales/fr/LC_MESSAGES/base.po index cda850cc..98a5a541 100644 --- a/archinstall/locales/fr/LC_MESSAGES/base.po +++ b/archinstall/locales/fr/LC_MESSAGES/base.po @@ -14,12 +14,8 @@ msgstr "" msgid "[!] A log file has been created here: {} {}" msgstr "[!] Un fichier journal a été créé ici : {} {}" -msgid "" -" Please submit this issue (and file) to https://github.com/archlinux/" -"archinstall/issues" -msgstr "" -" Veuillez soumettre ce problème (et le fichier) à https://github.com/" -"archlinux/archinstall/issues" +msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" +msgstr " Veuillez soumettre ce problème (et le fichier) à https://github.com/archlinux/archinstall/issues" msgid "Do you really want to abort?" msgstr "Voulez-vous vraiment abandonner ?" @@ -34,13 +30,10 @@ msgid "Desired hostname for the installation: " msgstr "Nom d'hôte souhaité pour l'installation : " msgid "Username for required superuser with sudo privileges: " -msgstr "" -"Nom d'utilisateur pour le superutilisateur requis avec les privilèges sudo : " +msgstr "Nom d'utilisateur pour le superutilisateur requis avec les privilèges sudo : " msgid "Any additional users to install (leave blank for no users): " -msgstr "" -"Utilisateur supplémentaire à installer (laisser vide pour aucun " -"utilisateur) : " +msgstr "Utilisateur supplémentaire à installer (laisser vide pour aucun utilisateur) : " msgid "Should this user be a superuser (sudoer)?" msgstr "Cet utilisateur doit-il être un superutilisateur (sudoer) ?" @@ -49,9 +42,7 @@ msgid "Select a timezone" msgstr "Sélectionner un fuseau horaire" msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?" -msgstr "" -"Souhaitez-vous utiliser GRUB comme chargeur de démarrage au lieu de systemd-" -"boot ?" +msgstr "Souhaitez-vous utiliser GRUB comme chargeur de démarrage au lieu de systemd-boot ?" msgid "Choose a bootloader" msgstr "Choisir un chargeur de démarrage" @@ -59,60 +50,38 @@ msgstr "Choisir un chargeur de démarrage" msgid "Choose an audio server" msgstr "Choisir un serveur audio" -msgid "" -"Only packages such as base, base-devel, linux, linux-firmware, efibootmgr " -"and optional profile packages are installed." -msgstr "" -"Seuls les paquets tels que base, base-devel, linux, linux-firmware, " -"efibootmgr et les paquets de profil optionnels sont installés." +msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." +msgstr "Seuls les paquets tels que base, base-devel, linux, linux-firmware, efibootmgr et les paquets de profil optionnels sont installés." -msgid "" -"If you desire a web browser, such as firefox or chromium, you may specify it " -"in the following prompt." -msgstr "" -"Si vous désirez un navigateur Web, tel que firefox ou chrome, vous pouvez le " -"spécifier dans l'invite suivante." +msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." +msgstr "Si vous désirez un navigateur Web, tel que firefox ou chrome, vous pouvez le spécifier dans l'invite suivante." -msgid "" -"Write additional packages to install (space separated, leave blank to skip): " -msgstr "" -"Saisir les paquets supplémentaires à installer (séparés par des espaces, " -"vide pour ignorer) : " +msgid "Write additional packages to install (space separated, leave blank to skip): " +msgstr "Saisir les paquets supplémentaires à installer (séparés par des espaces, vide pour ignorer) : " msgid "Copy ISO network configuration to installation" msgstr "Copier la configuration réseau ISO dans l'installation" -msgid "" -"Use NetworkManager (necessary for configuring internet graphically in GNOME " -"and KDE)" -msgstr "" -"Utiliser NetworkManager (nécessaire pour configurer graphiquement Internet " -"dans GNOME et KDE)" +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" +msgstr "Utiliser NetworkManager (nécessaire pour configurer graphiquement Internet dans GNOME et KDE)" msgid "Select one network interface to configure" msgstr "Sélectionner une interface réseau à configurer" -msgid "" -"Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" -msgstr "" -"Sélectionner le mode à configurer pour \"{}\" ou ignorer pour utiliser le " -"mode par défaut \"{}\"" +msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" +msgstr "Sélectionner le mode à configurer pour \"{}\" ou ignorer pour utiliser le mode par défaut \"{}\"" msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " msgstr "Entrer l'IP et le sous-réseau pour {} (exemple : 192.168.0.5/24) : " msgid "Enter your gateway (router) IP address or leave blank for none: " -msgstr "" -"Entrer l'adresse IP de votre passerelle (routeur) ou laisser vide pour " -"aucune : " +msgstr "Entrer l'adresse IP de votre passerelle (routeur) ou laisser vide pour aucune : " msgid "Enter your DNS servers (space separated, blank for none): " msgstr "Entrer vos serveurs DNS (séparés par des espaces, vide pour aucun) : " msgid "Select which filesystem your main partition should use" -msgstr "" -"Sélectionner le système de fichiers que votre partition principale doit " -"utiliser" +msgstr "Sélectionner le système de fichiers que votre partition principale doit utiliser" msgid "Current partition layout" msgstr "Disposition actuelle des partitions" @@ -127,21 +96,14 @@ msgstr "" msgid "Enter a desired filesystem type for the partition" msgstr "Entrer un type de système de fichiers souhaité pour la partition" -msgid "" -"Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " -msgstr "" -"Entrer l'emplacement de départ (en unités séparées : s, Go, %, etc. ; par " -"défaut : {}) : " +msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " +msgstr "Entrer l'emplacement de départ (en unités séparées : s, Go, %, etc. ; par défaut : {}) : " msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " -msgstr "" -"Entrer l'emplacement de fin (en unités séparées : s, Go, %, etc. ; ex : " -"{}) : " +msgstr "Entrer l'emplacement de fin (en unités séparées : s, Go, %, etc. ; ex : {}) : " msgid "{} contains queued partitions, this will remove those, are you sure?" -msgstr "" -"{} contient des partitions en file d'attente, cela les supprimera, êtes-vous " -"sûr ?" +msgstr "{} contient des partitions en file d'attente, cela les supprimera, êtes-vous sûr ?" msgid "" "{}\n" @@ -161,17 +123,11 @@ msgstr "" "\n" "Sélectionner par index où et quelle partition montée" -msgid "" -" * Partition mount-points are relative to inside the installation, the boot " -"would be /boot as an example." -msgstr "" -" * Les points de montage de la partition sont relatifs à l'intérieur de " -"l'installation, le démarrage serait /boot par exemple." +msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Les points de montage de la partition sont relatifs à l'intérieur de l'installation, le démarrage serait /boot par exemple." msgid "Select where to mount partition (leave blank to remove mountpoint): " -msgstr "" -"Sélectionner où monter la partition (laisser vide pour supprimer le point de " -"montage) : " +msgstr "Sélectionner où monter la partition (laisser vide pour supprimer le point de montage) : " msgid "" "{}\n" @@ -216,58 +172,34 @@ msgid "Archinstall language" msgstr "Langue d'Archinstall" msgid "Wipe all selected drives and use a best-effort default partition layout" -msgstr "" -"Effacer tous les lecteurs sélectionnés et utiliser une disposition de " -"partition par défaut optimale" +msgstr "Effacer tous les lecteurs sélectionnés et utiliser une disposition de partition par défaut optimale" -msgid "" -"Select what to do with each individual drive (followed by partition usage)" -msgstr "" -"Sélectionner ce qu'il faut faire avec chaque lecteur individuel (suivi de " -"l'utilisation de la partition)" +msgid "Select what to do with each individual drive (followed by partition usage)" +msgstr "Sélectionner ce qu'il faut faire avec chaque lecteur individuel (suivi de l'utilisation de la partition)" msgid "Select what you wish to do with the selected block devices" -msgstr "" -"Sélectionner ce que vous souhaitez faire avec les périphériques de bloc " -"sélectionnés" +msgstr "Sélectionner ce que vous souhaitez faire avec les périphériques de bloc sélectionnés" -msgid "" -"This is a list of pre-programmed profiles, they might make it easier to " -"install things like desktop environments" -msgstr "" -"Ceci est une liste préprogrammée de profiles, ils pourraient faciliter " -"l'installation d'outils comme les environnements de bureau" +msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments" +msgstr "Ceci est une liste préprogrammée de profiles, ils pourraient faciliter l'installation d'outils comme les environnements de bureau" msgid "Select keyboard layout" msgstr "Sélectionner la disposition du clavier" msgid "Select one of the regions to download packages from" -msgstr "" -"Sélectionner l'une des régions depuis lesquelles télécharger les paquets" +msgstr "Sélectionner l'une des régions depuis lesquelles télécharger les paquets" msgid "Select one or more hard drives to use and configure" msgstr "Sélectionner un ou plusieurs disques durs à utiliser et à configurer" -msgid "" -"For the best compatibility with your AMD hardware, you may want to use " -"either the all open-source or AMD / ATI options." -msgstr "" -"Pour une meilleure compatibilité avec votre matériel AMD, vous pouvez " -"utiliser les options entièrement open source ou AMD / ATI." +msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." +msgstr "Pour une meilleure compatibilité avec votre matériel AMD, vous pouvez utiliser les options entièrement open source ou AMD / ATI." -msgid "" -"For the best compatibility with your Intel hardware, you may want to use " -"either the all open-source or Intel options.\n" -msgstr "" -"Pour une compatibilité optimale avec votre matériel Intel, vous pouvez " -"utiliser les options entièrement open source ou Intel.\n" +msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" +msgstr "Pour une compatibilité optimale avec votre matériel Intel, vous pouvez utiliser les options entièrement open source ou Intel.\n" -msgid "" -"For the best compatibility with your Nvidia hardware, you may want to use " -"the Nvidia proprietary driver.\n" -msgstr "" -"Pour une meilleure compatibilité avec votre matériel Nvidia, vous pouvez " -"utiliser le pilote propriétaire Nvidia.\n" +msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" +msgstr "Pour une meilleure compatibilité avec votre matériel Nvidia, vous pouvez utiliser le pilote propriétaire Nvidia.\n" msgid "" "\n" @@ -276,8 +208,7 @@ msgid "" msgstr "" "\n" "\n" -"Sélectionner un pilote graphique ou laisser vide pour installer tous les " -"pilotes open-source" +"Sélectionner un pilote graphique ou laisser vide pour installer tous les pilotes open-source" msgid "All open-source (default)" msgstr "Tout open-source (par défaut)" @@ -300,12 +231,8 @@ msgstr "Sélectionner une ou plusieurs des options ci-dessous : " msgid "Adding partition...." msgstr "Ajout de la partition...." -msgid "" -"You need to enter a valid fs-type in order to continue. See `man parted` for " -"valid fs-type's." -msgstr "" -"Vous devez entrer un type de fs valide pour continuer. Voir `man parted` " -"pour les types de fs valides." +msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." +msgstr "Vous devez entrer un type de fs valide pour continuer. Voir `man parted` pour les types de fs valides." msgid "Error: Listing profiles on URL \"{}\" resulted in:" msgstr "Erreur : la liste des profils sur l'URL \"{}\" a entraîné :" @@ -378,8 +305,7 @@ msgid "" msgstr "" "Vous avez décidé d'ignorer la sélection du disque dur\n" "et vous utiliserez la configuration de disque montée sur {} (expérimental)\n" -"ATTENTION : Archinstall ne vérifiera pas l'adéquation de cette " -"configuration\n" +"ATTENTION : Archinstall ne vérifiera pas l'adéquation de cette configuration\n" "Souhaitez-vous continuer ?" msgid "Re-using partition instance: {}" @@ -404,8 +330,7 @@ msgid "Mark/Unmark a partition as encrypted" msgstr "Marquer/Démarquer une partition comme chiffrée" msgid "Mark/Unmark a partition as bootable (automatic for /boot)" -msgstr "" -"Marquer/Démarquer une partition comme amorçable (automatique pour /boot)" +msgstr "Marquer/Démarquer une partition comme amorçable (automatique pour /boot)" msgid "Set desired filesystem for a partition" msgstr "Définir le système de fichiers souhaité pour une partition" @@ -445,9 +370,7 @@ msgid "Enter a encryption password for {}" msgstr "Entrer un mot de passe de chiffrement pour {}" msgid "Enter disk encryption password (leave blank for no encryption): " -msgstr "" -"Entrer le mot de passe de chiffrement du disque (laisser vide pour aucun " -"chiffrement) : " +msgstr "Entrer le mot de passe de chiffrement du disque (laisser vide pour aucun chiffrement) : " msgid "Create a required super-user with sudo privileges: " msgstr "Créer un super-utilisateur requis avec les privilèges sudo : " @@ -458,44 +381,31 @@ msgstr "Entrer le mot de passe root (laisser vide pour désactiver root) : " msgid "Password for user \"{}\": " msgstr "Mot de passe pour l'utilisateur \"{}\" : " -msgid "" -"Verifying that additional packages exist (this might take a few seconds)" -msgstr "" -"Vérifier que des paquets supplémentaires existent (cela peut prendre " -"quelques secondes)" +msgid "Verifying that additional packages exist (this might take a few seconds)" +msgstr "Vérifier que des paquets supplémentaires existent (cela peut prendre quelques secondes)" -msgid "" -"Would you like to use automatic time synchronization (NTP) with the default " -"time servers?\n" -msgstr "" -"Souhaitez-vous utiliser la synchronisation automatique de l'heure (NTP) avec " -"les serveurs de temps par défaut ?\n" +msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" +msgstr "Souhaitez-vous utiliser la synchronisation automatique de l'heure (NTP) avec les serveurs de temps par défaut ?\n" msgid "" -"Hardware time and other post-configuration steps might be required in order " -"for NTP to work.\n" +"Hardware time and other post-configuration steps might be required in order for NTP to work.\n" "For more information, please check the Arch wiki" msgstr "" -"Le temps matériel et d'autres étapes de post-configuration peuvent être " -"nécessaires pour que NTP fonctionne.\n" +"Le temps matériel et d'autres étapes de post-configuration peuvent être nécessaires pour que NTP fonctionne.\n" "Pour plus d'informations, veuillez consulter le wiki Arch" msgid "Enter a username to create an additional user (leave blank to skip): " -msgstr "" -"Entrer un nom d'utilisateur pour créer un utilisateur supplémentaire " -"(laisser vide pour ignorer) : " +msgstr "Entrer un nom d'utilisateur pour créer un utilisateur supplémentaire (laisser vide pour ignorer) : " msgid "Use ESC to skip\n" msgstr "Utiliser ESC pour ignorer\n" msgid "" "\n" -" Choose an object from the list, and select one of the available actions for " -"it to execute" +" Choose an object from the list, and select one of the available actions for it to execute" msgstr "" "\n" -"Choisir un objet dans la liste et sélectionner l'une des actions disponibles " -"pour qu'il s'exécute" +"Choisir un objet dans la liste et sélectionner l'une des actions disponibles pour qu'il s'exécute" msgid "Cancel" msgstr "Annuler" @@ -531,18 +441,11 @@ msgstr "" "\n" "Voici la configuration choisie :" -msgid "" -"Pacman is already running, waiting maximum 10 minutes for it to terminate." -msgstr "" -"Pacman est déjà en cours d'exécution, attendez au maximum 10 minutes pour " -"qu'il se termine." +msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." +msgstr "Pacman est déjà en cours d'exécution, attendez au maximum 10 minutes pour qu'il se termine." -msgid "" -"Pre-existing pacman lock never exited. Please clean up any existing pacman " -"sessions before using archinstall." -msgstr "" -"Le verrou pacman préexistant n'a jamais été fermé. Veuillez nettoyer toutes " -"les sessions pacman existantes avant d'utiliser archinstall." +msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." +msgstr "Le verrou pacman préexistant n'a jamais été fermé. Veuillez nettoyer toutes les sessions pacman existantes avant d'utiliser archinstall." msgid "Choose which optional additional repositories to enable" msgstr "Choisir les dépôts supplémentaires en option à activer" @@ -679,16 +582,13 @@ msgid "Select the desired subvolume options " msgstr "Sélectionner les options de sous-volume souhaitées " msgid "Define users with sudo privilege, by username: " -msgstr "" -"Définir les utilisateurs avec le privilège sudo, par nom d'utilisateur : " +msgstr "Définir les utilisateurs avec le privilège sudo, par nom d'utilisateur : " msgid "[!] A log file has been created here: {}" msgstr "[!] Un fichier journal a été créé ici : {}" msgid "Would you like to use BTRFS subvolumes with a default structure?" -msgstr "" -"Souhaitez-vous utiliser des sous-volumes BTRFS avec une structure par " -"défaut ?" +msgstr "Souhaitez-vous utiliser des sous-volumes BTRFS avec une structure par défaut ?" msgid "Would you like to use BTRFS compression?" msgstr "Souhaitez-vous utiliser la compression BTRFS ?" @@ -696,12 +596,8 @@ msgstr "Souhaitez-vous utiliser la compression BTRFS ?" msgid "Would you like to create a separate partition for /home?" msgstr "Souhaitez-vous créer une partition séparée pour /home ?" -msgid "" -"The selected drives do not have the minimum capacity required for an " -"automatic suggestion\n" -msgstr "" -"Les disques sélectionnés n'ont pas la capacité minimale requise pour une " -"suggestion automatique\n" +msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" +msgstr "Les disques sélectionnés n'ont pas la capacité minimale requise pour une suggestion automatique\n" msgid "Minimum capacity for /home partition: {}GB\n" msgstr "Capacité minimale pour la partition /home : {} Go\n" @@ -728,9 +624,7 @@ msgid "No iface specified for manual configuration" msgstr "Aucun iface spécifié pour la configuration manuelle" msgid "Manual nic configuration with no auto DHCP requires an IP address" -msgstr "" -"La configuration manuelle de la carte réseau sans DHCP automatique nécessite " -"une adresse IP" +msgstr "La configuration manuelle de la carte réseau sans DHCP automatique nécessite une adresse IP" msgid "Add interface" msgstr "Ajouter une interface" @@ -750,42 +644,23 @@ msgstr "Configuration manuelle" msgid "Mark/Unmark a partition as compressed (btrfs only)" msgstr "Marquer/Démarquer une partition comme compressée (btrfs uniquement)" -msgid "" -"The password you are using seems to be weak, are you sure you want to use it?" -msgstr "" -"Le mot de passe que vous utilisez semble faible, êtes-vous sûr de vouloir " -"l'utiliser ?" +msgid "The password you are using seems to be weak, are you sure you want to use it?" +msgstr "Le mot de passe que vous utilisez semble faible, êtes-vous sûr de vouloir l'utiliser ?" -msgid "" -"Provides a selection of desktop environments and tiling window managers, e." -"g. gnome, kde, sway" -msgstr "" -"Fournit une sélection d'environnements de bureau et de gestionnaires de " -"fenêtres en mosaïque, par ex. gnome, kde, sway" +msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" +msgstr "Fournit une sélection d'environnements de bureau et de gestionnaires de fenêtres en mosaïque, par ex. gnome, kde, sway" msgid "Select your desired desktop environment" msgstr "Sélectionner l'environnement de bureau souhaité" -msgid "" -"A very basic installation that allows you to customize Arch Linux as you see " -"fit." -msgstr "" -"Une installation très basique qui vous permet de personnaliser Arch Linux " -"comme bon vous semble." +msgid "A very basic installation that allows you to customize Arch Linux as you see fit." +msgstr "Une installation très basique qui vous permet de personnaliser Arch Linux comme bon vous semble." -msgid "" -"Provides a selection of various server packages to install and enable, e.g. " -"httpd, nginx, mariadb" -msgstr "" -"Fournit une sélection de divers paquets de serveur à installer et à activer, " -"par ex. httpd, nginx, mariadb" +msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" +msgstr "Fournit une sélection de divers paquets de serveur à installer et à activer, par ex. httpd, nginx, mariadb" -msgid "" -"Choose which servers to install, if none then a minimal installation will be " -"done" -msgstr "" -"Choisir les serveurs à installer, s'il n'y en a pas, une installation " -"minimale sera effectuée" +msgid "Choose which servers to install, if none then a minimal installation will be done" +msgstr "Choisir les serveurs à installer, s'il n'y en a pas, une installation minimale sera effectuée" msgid "Installs a minimal system as well as xorg and graphics drivers." msgstr "Installe un système minimal ainsi que les pilotes graphiques et xorg." @@ -793,12 +668,8 @@ msgstr "Installe un système minimal ainsi que les pilotes graphiques et xorg." msgid "Press Enter to continue." msgstr "Appuyer sur Entrée pour continuer." -msgid "" -"Would you like to chroot into the newly created installation and perform " -"post-installation configuration?" -msgstr "" -"Souhaitez-vous chrooter dans l'installation nouvellement créée et effectuer " -"la configuration post-installation ?" +msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?" +msgstr "Souhaitez-vous chrooter dans l'installation nouvellement créée et effectuer la configuration post-installation ?" msgid "Are you sure you want to reset this setting?" msgstr "Voulez-vous vraiment réinitialiser ce paramètre ?" @@ -807,16 +678,10 @@ msgid "Select one or more hard drives to use and configure\n" msgstr "Sélectionner un ou plusieurs disques durs à utiliser et à configurer\n" msgid "Any modifications to the existing setting will reset the disk layout!" -msgstr "" -"Toute modification du paramètre existant réinitialisera la disposition du " -"disque !" +msgstr "Toute modification du paramètre existant réinitialisera la disposition du disque !" -msgid "" -"If you reset the harddrive selection this will also reset the current disk " -"layout. Are you sure?" -msgstr "" -"Si vous réinitialisez la sélection du disque dur, cela réinitialisera " -"également la disposition actuelle du disque. Êtes-vous sûr ?" +msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" +msgstr "Si vous réinitialisez la sélection du disque dur, cela réinitialisera également la disposition actuelle du disque. Êtes-vous sûr ?" msgid "Save and exit" msgstr "Sauvegarder et quitter" @@ -826,8 +691,7 @@ msgid "" "contains queued partitions, this will remove those, are you sure?" msgstr "" "{}\n" -"contient des partitions en file d'attente, cela les supprimera, êtes-vous " -"sûr ?" +"contient des partitions en file d'attente, cela les supprimera, êtes-vous sûr ?" msgid "No audio server" msgstr "Pas de serveur audio" @@ -863,13 +727,8 @@ msgstr "Ajouter : " msgid "Value: " msgstr "Valeur : " -msgid "" -"You can skip selecting a drive and partitioning and use whatever drive-setup " -"is mounted at /mnt (experimental)" -msgstr "" -"Vous pouvez ignorer la sélection d'un lecteur et le partitionnement et " -"utiliser n'importe quelle configuration de lecteur montée sur /mnt " -"(expérimental)" +msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" +msgstr "Vous pouvez ignorer la sélection d'un lecteur et le partitionnement et utiliser n'importe quelle configuration de lecteur montée sur /mnt (expérimental)" msgid "Select one of the disks or skip and use /mnt as default" msgstr "Sélectionner l'un des disques ou ignorer et utiliser /mnt par défaut" @@ -892,12 +751,8 @@ msgstr "Espace libre" msgid "Bus-type" msgstr "Type de bus" -msgid "" -"Either root-password or at least 1 user with sudo privileges must be " -"specified" -msgstr "" -"Le mot de passe root ou au moins 1 utilisateur avec des privilèges sudo doit " -"être spécifié" +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "Le mot de passe root ou au moins 1 utilisateur avec des privilèges sudo doit être spécifié" msgid "Enter username (leave blank to skip): " msgstr "Entrer le nom d'utilisateur (laisser vide pour passer) : " @@ -935,12 +790,8 @@ msgstr "Supprimer le sous-volume" msgid "Configured {} interfaces" msgstr "Interfaces {} configurées" -msgid "" -"This option enables the number of parallel downloads that can occur during " -"installation" -msgstr "" -"Cette option active le nombre de téléchargements parallèles qui peuvent se " -"produire pendant l'installation" +msgid "This option enables the number of parallel downloads that can occur during installation" +msgstr "Cette option active le nombre de téléchargements parallèles qui peuvent se produire pendant l'installation" msgid "" "Enter the number of parallel downloads to be enabled.\n" @@ -951,34 +802,18 @@ msgstr "" " (Entrer une valeur comprise entre 1 et {})\n" "Note :" -msgid "" -" - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads " -"at a time )" -msgstr "" -" - Valeur maximale : {} (Autorise {} téléchargements parallèles, autorise {} " -"téléchargements à la fois)" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr " - Valeur maximale : {} (Autorise {} téléchargements parallèles, autorise {} téléchargements à la fois)" -msgid "" -" - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a " -"time )" -msgstr "" -" - Valeur minimale : 1 (Autorise 1 téléchargement parallèle, autorise 2 " -"téléchargements à la fois)" +msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" +msgstr " - Valeur minimale : 1 (Autorise 1 téléchargement parallèle, autorise 2 téléchargements à la fois)" -msgid "" -" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 " -"download at a time )" -msgstr "" -" - Désactiver/Défaut : 0 (Désactive le téléchargement parallèle, n'autorise " -"qu'un seul téléchargement à la fois)" +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" +msgstr " - Désactiver/Défaut : 0 (Désactive le téléchargement parallèle, n'autorise qu'un seul téléchargement à la fois)" #, python-brace-format -msgid "" -"Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to " -"disable]" -msgstr "" -"Entrée invalide ! Réessayer avec une entrée valide [1 pour {max_downloads}, " -"ou 0 pour désactiver]" +msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" +msgstr "Entrée invalide ! Réessayer avec une entrée valide [1 pour {max_downloads}, ou 0 pour désactiver]" msgid "Parallel Downloads" msgstr "Téléchargements parallèles" @@ -995,20 +830,14 @@ msgstr "TAB pour sélectionner" msgid "[Default value: 0] > " msgstr "[Valeur par défaut : 0] > " -msgid "" -"To be able to use this translation, please install a font manually that " -"supports the language." -msgstr "" -"Pour pouvoir utiliser cette traduction, veuillez installer manuellement une " -"police prenant en charge la langue." +msgid "To be able to use this translation, please install a font manually that supports the language." +msgstr "Pour pouvoir utiliser cette traduction, veuillez installer manuellement une police prenant en charge la langue." msgid "The font should be stored as {}" msgstr "La police doit être stockée sous {}" msgid "Archinstall requires root privileges to run. See --help for more." -msgstr "" -"Archinstall nécessite des privilèges root pour s'exécuter. Voir l'aide pour " -"plus d'informations (--help)." +msgstr "Archinstall nécessite des privilèges root pour s'exécuter. Voir l'aide pour plus d'informations (--help)." msgid "Select an execution mode" msgstr "Sélectionner un mode d'exécution" @@ -1016,22 +845,14 @@ msgstr "Sélectionner un mode d'exécution" msgid "Unable to fetch profile from specified url: {}" msgstr "Impossible de récupérer le profil à partir de l'URL spécifiée : {}" -msgid "" -"Profiles must have unique name, but profile definitions with duplicate name " -"found: {}" -msgstr "" -"Les profils doivent avoir un nom unique, mais des définitions de profil avec " -"un nom en double ont été trouvées : {}" +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "Les profils doivent avoir un nom unique, mais des définitions de profil avec un nom en double ont été trouvées : {}" msgid "Select one or more devices to use and configure" msgstr "Sélectionner un ou plusieurs périphériques à utiliser et à configurer" -msgid "" -"If you reset the device selection this will also reset the current disk " -"layout. Are you sure?" -msgstr "" -"Si vous réinitialisez la sélection de périphérique, cela réinitialisera " -"également la disposition actuelle du disque. Etes-vous sûr ?" +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "Si vous réinitialisez la sélection de périphérique, cela réinitialisera également la disposition actuelle du disque. Etes-vous sûr ?" msgid "Existing Partitions" msgstr "Partitions existantes" @@ -1048,12 +869,8 @@ msgstr "Capacité minimale pour la partition /home : {} Gio\n" msgid "Minimum capacity for Arch Linux partition: {}GiB" msgstr "Capacité minimale pour la partition Arch Linux : {} Gio" -msgid "" -"This is a list of pre-programmed profiles_bck, they might make it easier to " -"install things like desktop environments" -msgstr "" -"Ceci est une liste préprogrammée de profiles_bck, ils pourraient faciliter " -"l'installation de choses comme les environnements de bureau" +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "Ceci est une liste préprogrammée de profiles_bck, ils pourraient faciliter l'installation de choses comme les environnements de bureau" msgid "Current profile selection" msgstr "Sélection du profil actuel" @@ -1085,26 +902,14 @@ msgstr "Supprimer la partition" msgid "Partition" msgstr "Partition" -msgid "" -"This partition is currently encrypted, to format it a filesystem has to be " -"specified" -msgstr "" -"Cette partition est actuellement cryptée, pour la formater, un système de " -"fichiers doit être spécifié" +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "Cette partition est actuellement cryptée, pour la formater, un système de fichiers doit être spécifié" -msgid "" -"Partition mount-points are relative to inside the installation, the boot " -"would be /boot as an example." -msgstr "" -"Les points de montage de partition sont relatifs à l'intérieur de " -"l'installation, le démarrage serait /boot par exemple." +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr "Les points de montage de partition sont relatifs à l'intérieur de l'installation, le démarrage serait /boot par exemple." -msgid "" -"If mountpoint /boot is set, then the partition will also be marked as " -"bootable." -msgstr "" -"Si le point de montage /boot est défini, la partition sera également marquée " -"comme amorçable." +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "Si le point de montage /boot est défini, la partition sera également marquée comme amorçable." msgid "Mountpoint: " msgstr "Point de montage : " @@ -1118,17 +923,11 @@ msgstr "Total des secteurs : {}" msgid "Enter the start sector (default: {}): " msgstr "Saisir le secteur de départ (par défaut : {}) : " -msgid "" -"Enter the end sector of the partition (percentage or block number, default: " -"{}): " -msgstr "" -"Saisir le secteur de fin de la partition (pourcentage ou numéro de bloc, par " -"défaut : {}) : " +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Saisir le secteur de fin de la partition (pourcentage ou numéro de bloc, par défaut : {}) : " msgid "This will remove all newly added partitions, continue?" -msgstr "" -"Cela supprimera toutes les partitions nouvellement ajoutées, voulez-vous " -"continuer ?" +msgstr "Cela supprimera toutes les partitions nouvellement ajoutées, voulez-vous continuer ?" msgid "Partition management: {}" msgstr "Gestion des partitions : {}" @@ -1191,19 +990,13 @@ msgid "Back" msgstr "Retour" msgid "Please chose which greeter to install for the chosen profiles: {}" -msgstr "" -"Veuillez choisir le greeter (interface de connexion) à installer pour les " -"profils choisis : {}" +msgstr "Veuillez choisir le greeter (interface de connexion) à installer pour les profils choisis : {}" msgid "Environment type: {}" msgstr "Type d'environnement : {}" -msgid "" -"The proprietary Nvidia driver is not supported by Sway. It is likely that " -"you will run into issues, are you okay with that?" -msgstr "" -"Le pilote Nvidia propriétaire n'est pas pris en charge par Sway. Il est " -"probable que vous rencontriez des problèmes, êtes-vous d'accord avec cela ?" +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "Le pilote Nvidia propriétaire n'est pas pris en charge par Sway. Il est probable que vous rencontriez des problèmes, êtes-vous d'accord avec cela ?" msgid "Installed packages" msgstr "Paquets installés" @@ -1221,22 +1014,13 @@ msgid "Profile name: " msgstr "Nom de profil : " msgid "The profile name you entered is already in use. Try again" -msgstr "" -"Le nom de profil que vous avez saisi est déjà utilisé. Essayer à nouveau" +msgstr "Le nom de profil que vous avez saisi est déjà utilisé. Essayer à nouveau" -msgid "" -"Packages to be install with this profile (space separated, leave blank to " -"skip): " -msgstr "" -"Saisir les paquets à installer avec ce profil (séparés par des espaces, vide " -"pour ignorer) : " +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Saisir les paquets à installer avec ce profil (séparés par des espaces, vide pour ignorer) : " -msgid "" -"Services to be enabled with this profile (space separated, leave blank to " -"skip): " -msgstr "" -"Saisir les services à activer avec ce profil (séparés par des espaces, vide " -"pour ignorer) : " +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Saisir les services à activer avec ce profil (séparés par des espaces, vide pour ignorer) : " msgid "Should this profile be enabled for installation?" msgstr "Ce profil doit-il être activé pour l'installation ?" @@ -1249,15 +1033,10 @@ msgid "" "Select a graphics driver or leave blank to install all open-source drivers" msgstr "" "\n" -"Sélectionner un pilote graphique ou laisser vide pour installer tous les " -"pilotes open source" +"Sélectionner un pilote graphique ou laisser vide pour installer tous les pilotes open source" -msgid "" -"Sway needs access to your seat (collection of hardware devices i.e. " -"keyboard, mouse, etc)" -msgstr "" -"Sway a besoin d'accéder à votre espace (ensemble de périphériques " -"matériels : clavier, souris, etc.)" +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "Sway a besoin d'accéder à votre espace (ensemble de périphériques matériels : clavier, souris, etc.)" msgid "" "\n" @@ -1287,14 +1066,10 @@ msgid "Profiles" msgstr "Profils" msgid "Finding possible directories to save configuration files ..." -msgstr "" -"Recherche des répertoires possibles pour enregistrer les fichiers de " -"configuration..." +msgstr "Recherche des répertoires possibles pour enregistrer les fichiers de configuration..." msgid "Select directory (or directories) for saving configuration files" -msgstr "" -"Sélectionner le répertoire (ou les répertoires) pour enregistrer les " -"fichiers de configuration" +msgstr "Sélectionner le répertoire (ou les répertoires) pour enregistrer les fichiers de configuration" msgid "Add a custom mirror" msgstr "Ajouter un miroir personnalisé" @@ -1324,16 +1099,13 @@ msgid "Defined" msgstr "Défini" msgid "Save user configuration (including disk layout)" -msgstr "" -"Enregistrer la configuration utilisateur (y compris la disposition du disque)" +msgstr "Enregistrer la configuration utilisateur (y compris la disposition du disque)" msgid "" -"Enter a directory for the configuration(s) to be saved (tab completion " -"enabled)\n" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" "Save directory: " msgstr "" -"Saisir un répertoire pour la/les configuration(s) à enregistrer (complétion " -"par tabulation activée)\n" +"Saisir un répertoire pour la/les configuration(s) à enregistrer (complétion par tabulation activée)\n" "Entrer le nom du répertoire de sauvegarde : " msgid "" @@ -1341,8 +1113,7 @@ msgid "" "\n" "{}" msgstr "" -"Voulez-vous enregistrer {} fichier(s) de configuration à l'emplacement " -"suivant ?\n" +"Voulez-vous enregistrer {} fichier(s) de configuration à l'emplacement suivant ?\n" "\n" "{}" @@ -1355,27 +1126,80 @@ msgstr "Miroirs" msgid "Mirror regions" msgstr "Régions miroir" -msgid "" -" - Maximum value : {} ( Allows {} parallel downloads, allows " -"{max_downloads+1} downloads at a time )" -msgstr "" -" - Valeur maximale : {} (Autorise {} téléchargements parallèles, autorise " -"{max_downloads+1} téléchargements à la fois)" +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr " - Valeur maximale : {} (Autorise {} téléchargements parallèles, autorise {max_downloads+1} téléchargements à la fois)" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" -msgstr "" -"Entrée invalide ! Réessayer avec une entrée valide [1 pour {}, ou 0 pour " -"désactiver]" +msgstr "Entrée invalide ! Réessayer avec une entrée valide [1 pour {}, ou 0 pour désactiver]" msgid "Locales" msgstr "Paramètres régionaux" +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Utiliser NetworkManager (nécessaire pour configurer graphiquement internet dans GNOME et KDE)" + +#, fuzzy +msgid "Total: {} / {}" +msgstr "Total (taille) : {}" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "Saisir le secteur de départ (par défaut : {}) : " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "Saisir le secteur de départ (par défaut : {}) : " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "Configuration manuelle" + +msgid "Type" +msgstr "" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "Cette option active le nombre de téléchargements parallèles qui peuvent se produire pendant l'installation" + +#, fuzzy msgid "" -"Use NetworkManager (necessary to configure internet graphically in GNOME and " -"KDE)" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" msgstr "" -"Utiliser NetworkManager (nécessaire pour configurer graphiquement internet " -"dans GNOME et KDE)" +"Saisir le nombre de téléchargements parallèles à activer.\n" +" (Entrer une valeur comprise entre 1 et {})\n" +"Note :" + +#, fuzzy +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - Valeur maximale : {} (Autorise {} téléchargements parallèles, autorise {} téléchargements à la fois)" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - Désactiver/Défaut : 0 (Désactive le téléchargement parallèle, n'autorise qu'un seul téléchargement à la fois)" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "Entrée invalide ! Réessayer avec une entrée valide [1 pour {}, ou 0 pour désactiver]" #, python-brace-format #~ msgid "Edit {origkey} :" diff --git a/archinstall/locales/id/LC_MESSAGES/base.po b/archinstall/locales/id/LC_MESSAGES/base.po index 1d203264..3511de14 100644 --- a/archinstall/locales/id/LC_MESSAGES/base.po +++ b/archinstall/locales/id/LC_MESSAGES/base.po @@ -1182,3 +1182,65 @@ msgstr "" #, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "Gunakan NetworkManager (diperlukan untuk mengkonfigurasi internet secara grafis di GNOME dan KDE)" + +msgid "Total: {} / {}" +msgstr "" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "Masukkan sektor awal (persentase atau nomor blok, default: {}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "Masukkan sektor awal (persentase atau nomor blok, default: {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "Konfigurasi manual" + +msgid "Type" +msgstr "" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "Opsi ini memungkinkan jumlah unduhan paralel yang dapat terjadi selama instalasi" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"Masukkan jumlah unduhan paralel yang akan diaktifkan.\n" +" (Masukkan nilai antara 1 hingga {})\n" +"Catatan:" + +#, fuzzy +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - Nilai maksimum : {} ( Memungkinkan {} unduhan paralel, memungkinkan {} unduhan sekaligus)" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - Nonaktifkan/Default: 0 (Menonaktifkan pengunduhan paralel, hanya mengizinkan 1 unduhan pada satu waktu)" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "Input tidak valid! Coba lagi dengan input yang valid [1 untuk {}, atau 0 untuk menonaktifkan]" diff --git a/archinstall/locales/it/LC_MESSAGES/base.po b/archinstall/locales/it/LC_MESSAGES/base.po index d9aa0ad6..36dd029d 100644 --- a/archinstall/locales/it/LC_MESSAGES/base.po +++ b/archinstall/locales/it/LC_MESSAGES/base.po @@ -1182,3 +1182,65 @@ msgstr "" #, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "Usa NetworkManager (necessario per configurare graficamente Internet in GNOME e KDE)" + +msgid "Total: {} / {}" +msgstr "" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "Inserisci il settore iniziale (percentuale o numero di blocco, predefinito: {}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "Inserisci il settore iniziale (percentuale o numero di blocco, predefinito: {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "Configurazione manuale" + +msgid "Type" +msgstr "" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "Questa opzione consente di impostare il numero di download paralleli che possono avvenire durante l'installazione" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"Inserisci il numero di download paralleli da abilitare.\n" +" (Inserisci un valore compreso tra 1 e {})\n" +"Nota:" + +#, fuzzy +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - Valore massimo : {} ( Consente {} download parallelo, consente {} download alla volta )" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - Disabilita/Predefinito : 0 ( Disabilita il download parallelo, consente solo 1 download alla volta )" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "Input non valido! Riprova con un input valido [da 1 a {}, o 0 per disabilitare]." diff --git a/archinstall/locales/ka/LC_MESSAGES/base.po b/archinstall/locales/ka/LC_MESSAGES/base.po index 9813aa96..4ecb6259 100644 --- a/archinstall/locales/ka/LC_MESSAGES/base.po +++ b/archinstall/locales/ka/LC_MESSAGES/base.po @@ -1181,3 +1181,65 @@ msgstr "" #, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "NetworkManager-ის გამოყენება (აუცილებელია ინტერნეტის GNOME/KDE-დან მოსარგებად)" + +msgid "Total: {} / {}" +msgstr "" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "შეიყვანეთ საწყისი სექტორი (პროცენტებში ან ბლოკის ნომერი. ნაგულისხმები: {}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "შეიყვანეთ საწყისი სექტორი (პროცენტებში ან ბლოკის ნომერი. ნაგულისხმები: {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "ხელით მორგება" + +msgid "Type" +msgstr "" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "ეს პარამეტრი დაყენებისას მითითებული რაოდენობის პარალელურ გადმოწერას დაუშვებს" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"შეიყვანეთ დასაშვები პარალელური გადმოწერების რაოდენობა.\n" +" (შეიყვანეთ მნიშვნელობა 1-დან {}-მდე)\n" +"დაიმახსოვრეთ:" + +#, fuzzy +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - მინიმალური მნიშვნელობა : {} ( დაუშვებს {} პარალელურ გადმოწერას, დაუშვებს {} ერთდროულ გადმოწერას )" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - გამორთვა/ნაგულისხმები : 0 ( პარალელური გადმოწერების გათიშვა. დროის ერთ მომენტში მხოლოდ ერთი გადმოწერა მოხდება )" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "შეყვანილი რიცხვი არასწორია! თავიდან სცადეთ [1-დან {}-მდე, ან 0, გასათიშად]" diff --git a/archinstall/locales/ko/LC_MESSAGES/base.po b/archinstall/locales/ko/LC_MESSAGES/base.po index e77fb2c9..a36680bd 100644 --- a/archinstall/locales/ko/LC_MESSAGES/base.po +++ b/archinstall/locales/ko/LC_MESSAGES/base.po @@ -1183,3 +1183,65 @@ msgstr "" #, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "NetworkManager 사용 (GNOME 이나 KDE 에서 그래픽으로 인터넷을 구성하는 데 필요)" + +msgid "Total: {} / {}" +msgstr "" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "시작 섹터를 입력하세요 (백분율 또는 블록 번호, 기본값: {}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "시작 섹터를 입력하세요 (백분율 또는 블록 번호, 기본값: {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "수동 구성" + +msgid "Type" +msgstr "" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "이 옵션은 설치 중에 발생할 수 있는 병렬 다운로드 수를 활성화합니다" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"활성화할 병렬 다운로드 수를 입력하세요.\n" +" (1 부터 {} 까지의 숫자중 하나를 입력하세요)\n" +"메모:" + +#, fuzzy +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - 최댓값 : {} ( {} 개의 병렬 다운로드 허용, 한 번에 {} 개의 다운로드 허용 )" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - 비활성화/기본 : - ( 병렬 다운로드 비활성화, 한 번에 1 개의 다운로드만 허용 )" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "잘못된 값입니다! 유효한 값으로 다시 시도해주세요 [1 부터 {} 까지, 비활성화 하려면 0]" diff --git a/archinstall/locales/nl/LC_MESSAGES/base.po b/archinstall/locales/nl/LC_MESSAGES/base.po index 0a79fd85..216f0912 100644 --- a/archinstall/locales/nl/LC_MESSAGES/base.po +++ b/archinstall/locales/nl/LC_MESSAGES/base.po @@ -1207,6 +1207,60 @@ msgstr "" msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "NetworkManager gebruiken (benodigd om internetinstellingen grafisch in te stellen in GNOME en KDE)" +msgid "Total: {} / {}" +msgstr "" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "Voer de beginsector in (percentage of bloknummer - standaard: {}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "Voer de beginsector in (percentage of bloknummer - standaard: {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "Configuratie vastleggen" + +msgid "Type" +msgstr "" + +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "" + +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" + +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr "" + +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr "" + +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "" + #~ msgid "Add :" #~ msgstr "Toevoegen:" diff --git a/archinstall/locales/pl/LC_MESSAGES/base.po b/archinstall/locales/pl/LC_MESSAGES/base.po index f5a99b23..0837c8e8 100644 --- a/archinstall/locales/pl/LC_MESSAGES/base.po +++ b/archinstall/locales/pl/LC_MESSAGES/base.po @@ -1180,6 +1180,68 @@ msgstr "" msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "Użyj programu NetworkManager (niezbędne do graficznej konfiguracji Internetu w środowiskach GNOME i KDE)" +msgid "Total: {} / {}" +msgstr "" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "Wprowadź sektor początkowy (procent lub numer bloku, domyślnie: {}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "Wprowadź sektor początkowy (procent lub numer bloku, domyślnie: {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "Ręczna konfiguracja" + +msgid "Type" +msgstr "" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "Ta opcja pozwala określić maksymalną liczbę pobieranych plików podczas instalacji" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"Wprowadź maksymalną liczbę plików pobieranych jednocześnie.\n" +" (Liczba z zakresu od 1 do {})\n" +"Zauważ:" + +#, fuzzy +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - Maksymalna wartość : {} ( Zwiększa liczbę zadań o {}, co pozwala na pobieranie {} plików jednocześnie )" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - Wyłącz/Domyślne : 0 ( Wyłącza pobieranie wielu plików jednocześnie, więc tylko 1 plik może być pobierany w tym samym czasie )" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "Nieprawidłowa wartość! Spróbuj wprowadzić wartość od 1 do {}, lub 0 aby wyłączyć." + #~ msgid "When picking a directory to save configuration files to, by default we will ignore the following folders: " #~ msgstr "Podczas wybierania katalogu do zapisywania plików konfiguracyjnych, domyślnie ignorowane są następujące foldery: " diff --git a/archinstall/locales/pt/LC_MESSAGES/base.po b/archinstall/locales/pt/LC_MESSAGES/base.po index 79c2860d..8f3a28d4 100644 --- a/archinstall/locales/pt/LC_MESSAGES/base.po +++ b/archinstall/locales/pt/LC_MESSAGES/base.po @@ -1143,18 +1143,6 @@ msgstr "Localidades" msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "Usar o Gestor de Redes \"NetworkManager\" (necessário para configurar a Internet graficamente no GNOME e KDE)" -msgid "Unable to determine fido2 devices. Is libfido2 installed?" -msgstr "Não foi possível determinar os dispositivos fido2. A libfido2 está instalada?" - -msgid "Path" -msgstr "Caminho" - -msgid "Manufacturer" -msgstr "Fabricante" - -msgid "Product" -msgstr "Produto" - msgid "Total: {} / {}" msgstr "Total: {} / {}" @@ -1170,10 +1158,25 @@ msgstr "Inserir o início (predefinido: sector {}): " msgid "Enter end (default: {}): " msgstr "Inserir o fim (predefinido: {}): " +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "Não foi possível determinar os dispositivos fido2. A libfido2 está instalada?" + +msgid "Path" +msgstr "Caminho" + +msgid "Manufacturer" +msgstr "Fabricante" + +msgid "Product" +msgstr "Produto" + #, python-brace-format msgid "Invalid configuration: {error}" msgstr "Configuração inválida: {error}" +msgid "Type" +msgstr "" + msgid "This option enables the number of parallel downloads that can occur during package downloads" msgstr "Esta opção ativa o número de transferências paralelas que podem ocorrer durante as transferências de pacotes" diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.po b/archinstall/locales/pt_BR/LC_MESSAGES/base.po index 487aa08d..cff6f19a 100644 --- a/archinstall/locales/pt_BR/LC_MESSAGES/base.po +++ b/archinstall/locales/pt_BR/LC_MESSAGES/base.po @@ -1151,5 +1151,68 @@ msgstr "Localidades" msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "Usar NetworkManager (necessário para configurar internet graficamente no GNOME e KDE)" +#, fuzzy +msgid "Total: {} / {}" +msgstr "Tamanho total: {}" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "Digite o setor de início (padrão: {}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "Digite o setor de início (padrão: {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "Configuração manual" + +msgid "Type" +msgstr "" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "Esta opção habilita o número de downloads paralelos que podem ocorrer durante a instalação" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"Insira o número de downloads paralelos para serem habilitados.\n" +" (Insira um valor entre 1 e {})\n" +"Observação:" + +#, fuzzy +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - Valor máximo : {} ( Permite {} downloads paralelos, permite {} downloads por vez )" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - Desativar/Padrão : 0 ( Desativa os downloads paralelos, permite apenas 1 download por vez )" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {}, ou 0 para desativar]" + #~ msgid "When picking a directory to save configuration files to, by default we will ignore the following folders: " #~ msgstr "Ao selecionar um diretório para salvar arquivos de configuração, por padrão nós ignoramos as seguintes pastas: " diff --git a/archinstall/locales/ro/LC_MESSAGES/base.po b/archinstall/locales/ro/LC_MESSAGES/base.po index 76b7c951..afc892c2 100644 --- a/archinstall/locales/ro/LC_MESSAGES/base.po +++ b/archinstall/locales/ro/LC_MESSAGES/base.po @@ -1138,31 +1138,6 @@ msgstr "Locale" msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "Folosiți NetworkManager (necesar pentru configurarea setărilor de rețea într-un mod grafic pentru GNOME și KDE)" -msgid "This option enables the number of parallel downloads that can occur during package downloads" -msgstr "Această opțiune setează numărul de descărcări paralele care pot avea loc în timpul instalării" - -msgid "" -"Enter the number of parallel downloads to be enabled.\n" -"\n" -"Note:\n" -msgstr "" -"Introduceți numărul de descărcări simultane pentru a fi activat.\n" -"\n" -"Notă:\n" - -msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" -msgstr " - Valoare minimă : {} ( Permite {} descărcări simultane în același timp )" - -msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" -msgstr " - Dezactivează/Predefinit: 0 (Dezactivează descărcările simultane, permite o singură descărcare în același timp )\n" - -msgid "Invalid input! Try again with a valid input [or 0 to disable]" -msgstr "Input invalid! Încercați din nou cu un input valid [0 pentru dezactivare]" - -#, python-brace-format -msgid "Invalid configuration: {error}" -msgstr "Configurație invalidă: {error}" - msgid "Total: {} / {}" msgstr "Total: {} / {}" @@ -1189,3 +1164,31 @@ msgstr "Producător" msgid "Product" msgstr "Produs" + +#, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "Configurație invalidă: {error}" + +msgid "Type" +msgstr "" + +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "Această opțiune setează numărul de descărcări paralele care pot avea loc în timpul instalării" + +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"Introduceți numărul de descărcări simultane pentru a fi activat.\n" +"\n" +"Notă:\n" + +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - Valoare minimă : {} ( Permite {} descărcări simultane în același timp )" + +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - Dezactivează/Predefinit: 0 (Dezactivează descărcările simultane, permite o singură descărcare în același timp )\n" + +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "Input invalid! Încercați din nou cu un input valid [0 pentru dezactivare]" diff --git a/archinstall/locales/ru/LC_MESSAGES/base.po b/archinstall/locales/ru/LC_MESSAGES/base.po index acbda26f..ccb27344 100644 --- a/archinstall/locales/ru/LC_MESSAGES/base.po +++ b/archinstall/locales/ru/LC_MESSAGES/base.po @@ -1139,3 +1139,65 @@ msgstr "Локализации" msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "Использовать NetworkManager (необходим для графической настройки интернета в GNOME и KDE)" +#, fuzzy +msgid "Total: {} / {}" +msgstr "Весь размер: {}" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "Введите начальный сектор (по умолчанию: {}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "Введите начальный сектор (по умолчанию: {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "Ручная конфигурация" + +msgid "Type" +msgstr "" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "Этот параметр определяет количество параллельных загрузок, которые могут происходить во время установки" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"Введите количество параллельных загрузок, которые будут включены.\n" +" (Введите значение от 1 до {})\n" +"Примечание:" + +#, fuzzy +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - Минимальное значение: {} ( Позволяет {} параллельную загрузку, позволяет {} загрузки одновременно )" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - Отключить/по умолчанию: 0 ( Отключает параллельную загрузку, позволяет только 1 загрузку за один раз )" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "Неверный ввод! Повторите попытку с правильным вводом [1 - {}, или 0 - отключить]" diff --git a/archinstall/locales/sv/LC_MESSAGES/base.po b/archinstall/locales/sv/LC_MESSAGES/base.po index 3566585b..69981cb0 100644 --- a/archinstall/locales/sv/LC_MESSAGES/base.po +++ b/archinstall/locales/sv/LC_MESSAGES/base.po @@ -1190,3 +1190,57 @@ msgstr "" #, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "Använd NetworkManager (nödvändig för konfigurera internet i grafiska miljöerna GNOME och KDE)" + +msgid "Total: {} / {}" +msgstr "" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "Mata in startsektor (procent eller block-nummer, standard: {}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "Mata in startsektor (procent eller block-nummer, standard: {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "Manuell konfiguration" + +msgid "Type" +msgstr "" + +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "" + +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" + +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr "" + +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr "" + +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "" diff --git a/archinstall/locales/ta/LC_MESSAGES/base.po b/archinstall/locales/ta/LC_MESSAGES/base.po index e24a1800..3935f3aa 100644 --- a/archinstall/locales/ta/LC_MESSAGES/base.po +++ b/archinstall/locales/ta/LC_MESSAGES/base.po @@ -1139,3 +1139,66 @@ msgstr "மொழி குறியீடுகள்" msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "NetworkManager ஐப் பயன்படுத்தவும் (GNOME மற்றும் KDE இல் இணையத்தை வரைகலை முறையில் கட்டமைக்க அவசியம்)" + +#, fuzzy +msgid "Total: {} / {}" +msgstr "முழு நீளம்: {}" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "தொடக்கப் பிரிவை உள்ளிடவும் (இயல்புநிலை: {}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "தொடக்கப் பிரிவை உள்ளிடவும் (இயல்புநிலை: {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "கைமுறை கட்டமைப்பு" + +msgid "Type" +msgstr "" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "இந்த விருப்பம் நிறுவலின் போது நிகழக்கூடிய இணையான பதிவிறக்கங்களின் எண்ணிக்கையை செயல்படுத்துகிறது" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"இயக்கப்பட வேண்டிய இணையான பதிவிறக்கங்களின் எண்ணிக்கையை உள்ளிடவும்.\n" +" (1 முதல் {} வரையிலான மதிப்பை உள்ளிடவும்)\n" +"குறிப்பு:" + +#, fuzzy +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - அதிகபட்ச மதிப்பு : {} ( {} இணையான பதிவிறக்கங்களை அனுமதிக்கிறது, ஒரே நேரத்தில் {} பதிவிறக்கங்களை அனுமதிக்கிறது )" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - முடக்கு/இயல்புநிலை: 0 (இணை பதிவிறக்கத்தை முடக்குகிறது, ஒரு நேரத்தில் 1 பதிவிறக்கத்தை மட்டுமே அனுமதிக்கிறது )" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "தவறான உள்ளீடு! சரியான உள்ளீட்டில் [1 முதல் {} வரை அல்லது முடக்க 0 வரை] மீண்டும் முயற்சிக்கவும்" diff --git a/archinstall/locales/tr/LC_MESSAGES/base.mo b/archinstall/locales/tr/LC_MESSAGES/base.mo index 7cea76f3..83d4a108 100644 Binary files a/archinstall/locales/tr/LC_MESSAGES/base.mo and b/archinstall/locales/tr/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/tr/LC_MESSAGES/base.po b/archinstall/locales/tr/LC_MESSAGES/base.po index 710ab86f..7360bbb8 100644 --- a/archinstall/locales/tr/LC_MESSAGES/base.po +++ b/archinstall/locales/tr/LC_MESSAGES/base.po @@ -1165,7 +1165,9 @@ msgstr "Kullanıcı konfigürasyonunu kaydet (Disk düzeni ile birlikte)" msgid "" "Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" "Save directory: " -msgstr "Yapılandırma(lar)ın kaydedilmesi için bir dizin girin (tab ile tamamlama etkin)\n " +msgstr "" +"Yapılandırma(lar)ın kaydedilmesi için bir dizin girin (tab ile tamamlama etkin)\n" +" " msgid "" "Do you want to save {} configuration file(s) in the following location?\n" @@ -1197,3 +1199,66 @@ msgstr "Yerel Ayarlar" #, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "NetworkManager'ı kullan (GNOME ya da KDE'de interneti grafik olarak yapılandırmak için gerekli)" + +#, fuzzy +msgid "Total: {} / {}" +msgstr "Toplam uzunluk: {}" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "Başlangıç kesimini girin (yüzde ya da blok numarası, varsayılan: {}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "Başlangıç kesimini girin (yüzde ya da blok numarası, varsayılan: {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "Manuel yapılandırma" + +msgid "Type" +msgstr "" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "Bu seçenek, yükleme sırasında meydana gelebilecek paralel indirme sayısını etkinleştirir" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"Etkinleştirilecek paralel indirme sayısını girin.\n" +" (1 ile {max_downloads} arasında bir değer girin)\n" +"Not:" + +#, fuzzy +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr "- Maksimum değer : {} ( {} paralel indirmeye izin verir, bir seferde {} indirmeye izin verir)" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr "- Devre Dışı Bırak/Varsayılan : 0 (Paralel indirmeyi devre dışı bırakır, aynı anda yalnızca 1 indirmeye izin verir)" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "Geçersiz girdi! Geçerli bir girdiyle tekrar deneyin [1 ila {} veya devre dışı bırakmak için 0]" diff --git a/archinstall/locales/uk/LC_MESSAGES/base.po b/archinstall/locales/uk/LC_MESSAGES/base.po index d8f7c70c..97e3b027 100644 --- a/archinstall/locales/uk/LC_MESSAGES/base.po +++ b/archinstall/locales/uk/LC_MESSAGES/base.po @@ -1180,3 +1180,65 @@ msgstr "" #, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "Використовувати NetworkManager (необхідний для графічного налаштування Інтернету в GNOME та KDE)" + +msgid "Total: {} / {}" +msgstr "" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "Введіть початковий сектор (відсоток або номер блоку, за замовчуванням: {}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "Введіть початковий сектор (відсоток або номер блоку, за замовчуванням: {}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "Ручне налаштування" + +msgid "Type" +msgstr "" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "Цей параметр вмикає кількість паралельних завантажень, які можуть відбуватися під час встановлення" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"Введіть кількість паралельних завантажень, які потрібно ввімкнути.\n" +" (Введіть значення від 1 до {})\n" +"Примітка:" + +#, fuzzy +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - Максимальне значення : {} ( Дозволяє {} паралельних завантажень, дозволяє {} завантажень за раз )" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - Вимкнути/Типово : 0 ( Вимикає паралельне завантаження, дозволяє лише 1 завантаження за раз )" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "Некоректне введення! Повторіть спробу з валідним введенням [від 1 до {} або 0 для вимкнення]" diff --git a/archinstall/locales/ur/LC_MESSAGES/base.po b/archinstall/locales/ur/LC_MESSAGES/base.po index 551d53f6..8e4b44bb 100644 --- a/archinstall/locales/ur/LC_MESSAGES/base.po +++ b/archinstall/locales/ur/LC_MESSAGES/base.po @@ -1210,6 +1210,60 @@ msgstr "" msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "نیٹ ورک مینجر کا استعمال کریں (GNOME اور KDE میں انٹرنیٹ کو گرافیکلی ترتیب دینے کے لیے ضروری ہے)" +msgid "Total: {} / {}" +msgstr "" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "اسٹارٹ سیکٹر درج کریں (فیصد یا بلاک نمبر، ڈیفالٹ: {}):" + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "اسٹارٹ سیکٹر درج کریں (فیصد یا بلاک نمبر، ڈیفالٹ: {}):" + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "ترتیب کو محفوظ کریں" + +msgid "Type" +msgstr "" + +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "" + +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" + +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr "" + +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr "" + +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "" + #~ msgid "Add :" #~ msgstr "شامل:" diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.mo b/archinstall/locales/zh-CN/LC_MESSAGES/base.mo index a07e57f3..c7208160 100644 Binary files a/archinstall/locales/zh-CN/LC_MESSAGES/base.mo and b/archinstall/locales/zh-CN/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.po b/archinstall/locales/zh-CN/LC_MESSAGES/base.po index 77c48924..b69fdb1f 100644 --- a/archinstall/locales/zh-CN/LC_MESSAGES/base.po +++ b/archinstall/locales/zh-CN/LC_MESSAGES/base.po @@ -1136,3 +1136,66 @@ msgstr "区域设置" msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "使用 NetworkManager(在 GNOME 和 KDE 中以图形方式配置互联网所必需)" + +#, fuzzy +msgid "Total: {} / {}" +msgstr "总长度:{}" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "输入起始扇区(百分比或块号,默认:{}): " + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "输入起始扇区(百分比或块号,默认:{}): " + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "手动配置" + +msgid "Type" +msgstr "" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "此选项启用安装期间可以发生的并行下载数" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"输入要启用的并行下载数。\n" +" (输入一个介于 1 到 {} 之间的值)\n" +"提示:" + +#, fuzzy +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - 最大值:{}(允许 {} 次并行下载,一次允许 {} 次下载)" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - 禁用/默认值:0(禁用并行下载,同时只允许 1 个下载)" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "输入无效! 使用有效输入重试 [1 到 {},或 0 到禁用]" diff --git a/archinstall/locales/zh-TW/LC_MESSAGES/base.po b/archinstall/locales/zh-TW/LC_MESSAGES/base.po index f1aa86ae..592dbdae 100644 --- a/archinstall/locales/zh-TW/LC_MESSAGES/base.po +++ b/archinstall/locales/zh-TW/LC_MESSAGES/base.po @@ -1146,3 +1146,66 @@ msgstr "" #, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "使用 NetworkManager(在 GNOME 和 KDE 透過圖形界面設置網際網路連線所需)" + +#, fuzzy +msgid "Total: {} / {}" +msgstr "總長度:{}" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "輸入起始區塊(默認:{}):" + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "輸入起始區塊(默認:{}):" + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "手動配置" + +msgid "Type" +msgstr "" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "此選項啟用安裝期間可能發生的並行下載數" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"輸入要啟用的並行下載數。\n" +" (輸入一個 1 到 {max_downloads} 之間的值)\n" +"提示:" + +#, fuzzy +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr " - 最小值:1(允許 1 个並行下載,同時允許 2 个下載)" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr " - 禁用/默認:0(禁用並行下載,同時只允許 1 个下載)" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "輸入無效! 請嘗試使用有效輸入重試 [1 到 {max_downloads},或 0 到禁用]" -- cgit v1.2.3-70-g09d2 From 71ecfcc24e249542a4fce38914bcbf75d3f334ba Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Sun, 17 Sep 2023 15:38:19 -0400 Subject: Remove git install from `build_iso.sh` (#2063) --- build_iso.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_iso.sh b/build_iso.sh index e18b9b6b..3955b41a 100755 --- a/build_iso.sh +++ b/build_iso.sh @@ -31,7 +31,7 @@ cat <<- _EOF_ | tee /tmp/archlive/airootfs/root/.zprofile echo "Type archinstall to launch the installer." _EOF_ -pacman --noconfirm -S git archiso +pacman --noconfirm -S archiso cp -r /usr/share/archiso/configs/releng/* /tmp/archlive -- cgit v1.2.3-70-g09d2 From ca09e1e63d611a0141a68789503cfa472e85e9f9 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Sun, 17 Sep 2023 17:02:29 -0400 Subject: Refactor `_add_efistub_bootloader()` kernel parameters (#2061) * Refactor `_add_efistub_bootloader()` kernel parameters * Remove obsolete comment --- archinstall/lib/installer.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 01c92d3b..09e91ab8 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -1058,19 +1058,21 @@ TIMEOUT=5 else: debug(f"Unknown CPU vendor '{vendor}' detected. Archinstall won't add any ucode to firmware boot entry.") - # blkid doesn't trigger on loopback devices really well, - # so we'll use the old manual method until we get that sorted out. - kernel_parameters = [] if root_partition in self._disk_encryption.partitions: # TODO: We need to detect if the encrypted device is a whole disk encryption, # or simply a partition encryption. Right now we assume it's a partition (and we always have) debug(f'Root partition is an encrypted device identifying by PARTUUID: {root_partition.partuuid}') - kernel_parameters.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:luksdev root=/dev/mapper/luksdev rw rootfstype={root_partition.safe_fs_type.value} {" ".join(self._kernel_params)}') + kernel_parameters.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:luksdev') + kernel_parameters.append('root=/dev/mapper/luksdev') else: debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}') - kernel_parameters.append(f'root=PARTUUID={root_partition.partuuid} rw rootfstype={root_partition.safe_fs_type.value} {" ".join(self._kernel_params)}') + kernel_parameters.append(f'root=PARTUUID={root_partition.partuuid}') + + kernel_parameters.append('rw') + kernel_parameters.append(f'rootfstype={root_partition.safe_fs_type.value}') + kernel_parameters.extend(self._kernel_params) parent_dev_path = disk.device_handler.get_parent_device_path(boot_partition.safe_dev_path) -- cgit v1.2.3-70-g09d2 From 12b7017240a040fd4fbebf7c5794a1ca5560f0ea Mon Sep 17 00:00:00 2001 From: Alexander Seiler Date: Mon, 18 Sep 2023 14:04:36 +0200 Subject: Fix many typos (#1692) Signed-off-by: Alexander Seiler --- archinstall/__init__.py | 2 +- archinstall/default_profiles/profile.py | 6 +++--- archinstall/lib/disk/device_model.py | 2 +- archinstall/lib/disk/fido.py | 2 +- archinstall/lib/general.py | 2 +- archinstall/lib/global_menu.py | 4 ++-- archinstall/lib/installer.py | 10 +++++----- archinstall/lib/interactions/__init__.py | 2 +- archinstall/lib/interactions/general_conf.py | 2 +- archinstall/lib/menu/list_manager.py | 2 +- archinstall/lib/menu/menu.py | 2 +- archinstall/lib/output.py | 2 +- archinstall/lib/translationhandler.py | 2 +- archinstall/scripts/guided.py | 4 ++-- archinstall/scripts/swiss.py | 6 +++--- examples/interactive_installation.py | 4 ++-- 16 files changed, 27 insertions(+), 27 deletions(-) diff --git a/archinstall/__init__.py b/archinstall/__init__.py index 39588904..7645ae39 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -43,7 +43,7 @@ __version__ = "2.6.0" storage['__version__'] = __version__ -# add the custome _ as a builtin, it can now be used anywhere in the +# add the custom _ as a builtin, it can now be used anywhere in the # project to mark strings as translatable with _('translate me') DeferredTranslation.install() diff --git a/archinstall/default_profiles/profile.py b/archinstall/default_profiles/profile.py index 982bd5a3..49a9c19d 100644 --- a/archinstall/default_profiles/profile.py +++ b/archinstall/default_profiles/profile.py @@ -81,7 +81,7 @@ class Profile: def packages(self) -> List[str]: """ Returns a list of packages that should be installed when - this profile is among the choosen ones + this profile is among the chosen ones """ return self._packages @@ -128,7 +128,7 @@ class Profile: """ Set the custom settings for the profile. This is also called when the settings are parsed from the config - and can be overriden to perform further actions based on the profile + and can be overridden to perform further actions based on the profile """ self.custom_settings = settings @@ -179,7 +179,7 @@ class Profile: def preview_text(self) -> Optional[str]: """ Used for preview text in profiles_bck. If a description is set for a - profile it will automatically display that one in the preivew. + profile it will automatically display that one in the preview. If no preview or a different text should be displayed just """ if self.description: diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index 6eeb0d91..69038b01 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -202,7 +202,7 @@ class Size: # not sure why we would ever wanna convert to percentages if target_unit == Unit.Percent and total_size is None: - raise ValueError('Missing paramter total size to be able to convert to percentage') + raise ValueError('Missing parameter total size to be able to convert to percentage') if self.unit == target_unit: return self diff --git a/archinstall/lib/disk/fido.py b/archinstall/lib/disk/fido.py index 96a74991..9eeba56a 100644 --- a/archinstall/lib/disk/fido.py +++ b/archinstall/lib/disk/fido.py @@ -34,7 +34,7 @@ class Fido2: /dev/hidraw1 Yubico YubiKey OTP+FIDO+CCID """ - # to prevent continous reloading which will slow + # to prevent continuous reloading which will slow # down moving the cursor in the menu if not cls._loaded or reload: try: diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py index 611378ee..e22e7eed 100644 --- a/archinstall/lib/general.py +++ b/archinstall/lib/general.py @@ -30,7 +30,7 @@ if TYPE_CHECKING: def generate_password(length :int = 64) -> str: - haystack = string.printable # digits, ascii_letters, punctiation (!"#$[] etc) and whitespace + haystack = string.printable # digits, ascii_letters, punctuation (!"#$[] etc) and whitespace return ''.join(secrets.choice(haystack) for i in range(length)) diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index fb62b7b5..b38dac0b 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -15,7 +15,7 @@ from .output import FormattedOutput from .profile.profile_menu import ProfileConfiguration from .storage import storage from .configuration import save_config -from .interactions import add_number_of_parrallel_downloads +from .interactions import add_number_of_parallel_downloads from .interactions import ask_additional_packages_to_install from .interactions import ask_for_additional_users from .interactions import ask_for_audio_selection @@ -119,7 +119,7 @@ class GlobalMenu(AbstractMenu): self._menu_options['parallel downloads'] = \ Selector( _('Parallel Downloads'), - lambda preset: add_number_of_parrallel_downloads(preset), + lambda preset: add_number_of_parallel_downloads(preset), display_func=lambda x: x if x else '0', default=0 ) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 09e91ab8..34c9441f 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -194,7 +194,7 @@ class Installer: for part_mod in sorted_part_mods: if luks_handler := luks_handlers.get(part_mod): # mount encrypted partition - self._mount_luks_partiton(part_mod, luks_handler) + self._mount_luks_partition(part_mod, luks_handler) else: # partition is not encrypted self._mount_partition(part_mod) @@ -219,7 +219,7 @@ class Installer: if part_mod.fs_type == disk.FilesystemType.Btrfs and part_mod.dev_path: self._mount_btrfs_subvol(part_mod.dev_path, part_mod.btrfs_subvols) - def _mount_luks_partiton(self, part_mod: disk.PartitionModification, luks_handler: Luks2): + def _mount_luks_partition(self, part_mod: disk.PartitionModification, luks_handler: Luks2): # it would be none if it's btrfs as the subvolumes will have the mountpoints defined if part_mod.mountpoint and luks_handler.mapper_dev: target = self.target / part_mod.relative_mountpoint @@ -315,7 +315,7 @@ class Installer: raise RequirementError(f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n Error: {err}') if not gen_fstab: - raise RequirementError(f'Genrating fstab returned empty value') + raise RequirementError(f'Generating fstab returned empty value') with open(fstab_path, 'a') as fp: fp.write(gen_fstab) @@ -434,7 +434,7 @@ class Installer: return False - def activate_time_syncronization(self) -> None: + def activate_time_synchronization(self) -> None: info('Activating systemd-timesyncd for time synchronization using Arch Linux and ntp.org NTP servers') self.enable_service('systemd-timesyncd') @@ -1008,7 +1008,7 @@ When = PostTransaction Exec = /bin/sh -c \\"/usr/bin/limine bios-install /dev/disk/by-uuid/{root_uuid} && /usr/bin/cp /usr/share/limine/limine-bios.sys /boot/\\" """) - # Limine does not ship with a default configuation file. We are going to + # Limine does not ship with a default configuration file. We are going to # create a basic one that is similar to the one GRUB generates. try: config = f""" diff --git a/archinstall/lib/interactions/__init__.py b/archinstall/lib/interactions/__init__.py index 53be8e7a..50c0012d 100644 --- a/archinstall/lib/interactions/__init__.py +++ b/archinstall/lib/interactions/__init__.py @@ -11,7 +11,7 @@ from .disk_conf import ( from .general_conf import ( ask_ntp, ask_hostname, ask_for_a_timezone, ask_for_audio_selection, select_archinstall_language, ask_additional_packages_to_install, - add_number_of_parrallel_downloads, select_additional_repositories + add_number_of_parallel_downloads, select_additional_repositories ) from .system_conf import ( diff --git a/archinstall/lib/interactions/general_conf.py b/archinstall/lib/interactions/general_conf.py index 14fcc3f8..8dd6e94f 100644 --- a/archinstall/lib/interactions/general_conf.py +++ b/archinstall/lib/interactions/general_conf.py @@ -164,7 +164,7 @@ def ask_additional_packages_to_install(preset: List[str] = []) -> List[str]: return packages -def add_number_of_parrallel_downloads(input_number :Optional[int] = None) -> Optional[int]: +def add_number_of_parallel_downloads(input_number :Optional[int] = None) -> Optional[int]: max_recommended = 5 print(_(f"This option enables the number of parallel downloads that can occur during package downloads")) print(_("Enter the number of parallel downloads to be enabled.\n\nNote:\n")) diff --git a/archinstall/lib/menu/list_manager.py b/archinstall/lib/menu/list_manager.py index be31fdf0..54fb6a1b 100644 --- a/archinstall/lib/menu/list_manager.py +++ b/archinstall/lib/menu/list_manager.py @@ -80,7 +80,7 @@ class ListManager: self._data = self.handle_action(choice.value, None, self._data) elif choice.value in self._terminate_actions: break - else: # an entry of the existing selection was choosen + else: # an entry of the existing selection was chosen selected_entry = data_formatted[choice.value] # type: ignore self._run_actions_on_entry(selected_entry) diff --git a/archinstall/lib/menu/menu.py b/archinstall/lib/menu/menu.py index 358ba5e4..3bd31b88 100644 --- a/archinstall/lib/menu/menu.py +++ b/archinstall/lib/menu/menu.py @@ -123,7 +123,7 @@ class Menu(TerminalMenu): :param allow_reset: This will explicitly handle a ctrl+c instead and return that specific state :type allow_reset: bool - param allow_reset_warning_msg: If raise_error_on_interrupt is True the warnign is set, a user confirmation is displayed + param allow_reset_warning_msg: If raise_error_on_interrupt is True the warning is set, a user confirmation is displayed type allow_reset_warning_msg: str :param extra_bottom_space: Add an extra empty line at the end of the menu diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py index 63d9c1fb..62a1ba27 100644 --- a/archinstall/lib/output.py +++ b/archinstall/lib/output.py @@ -22,7 +22,7 @@ class FormattedOutput: ) -> Dict[str, Any]: """ the original values returned a dataclass as dict thru the call to some specific methods - this version allows thru the parameter class_formatter to call a dynamicly selected formatting method. + this version allows thru the parameter class_formatter to call a dynamically selected formatting method. Can transmit a filter list to the class_formatter, """ if class_formatter: diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py index a2e44065..33230562 100644 --- a/archinstall/lib/translationhandler.py +++ b/archinstall/lib/translationhandler.py @@ -138,7 +138,7 @@ class TranslationHandler: def get_language_by_abbr(self, abbr: str) -> Language: """ - Get a language object by its abbrevation, e.g. en + Get a language object by its abbreviation, e.g. en """ try: return next(filter(lambda x: x.abbr == abbr, self._translated_languages)) diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 51549fa8..d7cf16cd 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -185,7 +185,7 @@ def perform_installation(mountpoint: Path): installation.set_timezone(timezone) if archinstall.arguments.get('ntp', False): - installation.activate_time_syncronization() + installation.activate_time_synchronization() if archinstall.accessibility_tools_in_use(): installation.enable_espeakup() @@ -193,7 +193,7 @@ def perform_installation(mountpoint: Path): if (root_pw := archinstall.arguments.get('!root-password', None)) and len(root_pw): installation.user_set_pw('root', root_pw) - # This step must be after profile installs to allow profiles_bck to install language pre-requisits. + # This step must be after profile installs to allow profiles_bck to install language pre-requisites. # After which, this step will set the language both for console and x11 if x11 was installed for instance. installation.set_keyboard_language(locale_config.kb_layout) diff --git a/archinstall/scripts/swiss.py b/archinstall/scripts/swiss.py index 80fa0a48..c04ccca4 100644 --- a/archinstall/scripts/swiss.py +++ b/archinstall/scripts/swiss.py @@ -54,7 +54,7 @@ class SetupMenu(GlobalMenu): super().setup_selection_menu_options() self._menu_options['mode'] = menu.Selector( - 'Excution mode', + 'Execution mode', lambda x : select_mode(), display_func=lambda x: x.value if x else '', default=ExecutionMode.Full) @@ -249,7 +249,7 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): installation.set_timezone(timezone) if archinstall.arguments.get('ntp', False): - installation.activate_time_syncronization() + installation.activate_time_synchronization() if archinstall.accessibility_tools_in_use(): installation.enable_espeakup() @@ -257,7 +257,7 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): if (root_pw := archinstall.arguments.get('!root-password', None)) and len(root_pw): installation.user_set_pw('root', root_pw) - # This step must be after profile installs to allow profiles_bck to install language pre-requisits. + # This step must be after profile installs to allow profiles_bck to install language pre-requisites. # After which, this step will set the language both for console and x11 if x11 was installed for instance. installation.set_keyboard_language(locale_config.kb_layout) diff --git a/examples/interactive_installation.py b/examples/interactive_installation.py index 9eac029c..f8cc75fc 100644 --- a/examples/interactive_installation.py +++ b/examples/interactive_installation.py @@ -163,7 +163,7 @@ def perform_installation(mountpoint: Path): installation.set_timezone(timezone) if archinstall.arguments.get('ntp', False): - installation.activate_time_syncronization() + installation.activate_time_synchronization() if archinstall.accessibility_tools_in_use(): installation.enable_espeakup() @@ -171,7 +171,7 @@ def perform_installation(mountpoint: Path): if (root_pw := archinstall.arguments.get('!root-password', None)) and len(root_pw): installation.user_set_pw('root', root_pw) - # This step must be after profile installs to allow profiles_bck to install language pre-requisits. + # This step must be after profile installs to allow profiles_bck to install language pre-requisites. # After which, this step will set the language both for console and x11 if x11 was installed for instance. installation.set_keyboard_language(locale_config.kb_layout) -- cgit v1.2.3-70-g09d2 From 6ec2bd4b557867ac12580797b7f15b5e52682c22 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Mon, 18 Sep 2023 09:22:55 -0400 Subject: Add `_get_kernel_params()` (#2064) --- archinstall/lib/installer.py | 91 +++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 51 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 34c9441f..2eb63279 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -715,6 +715,44 @@ class Installer: return root return None + def _get_kernel_params(self, root_partition: disk.PartitionModification) -> List[str]: + kernel_parameters = [] + + if root_partition in self._disk_encryption.partitions: + # TODO: We need to detect if the encrypted device is a whole disk encryption, + # or simply a partition encryption. Right now we assume it's a partition (and we always have) + debug('Root partition is an encrypted device, identifying by PARTUUID: {root_partition.partuuid}') + + if self._disk_encryption and self._disk_encryption.hsm_device: + # Note: lsblk UUID must be used, not PARTUUID for sd-encrypt to work + kernel_parameters.append(f'rd.luks.name={root_partition.uuid}=luksdev') + # Note: tpm2-device and fido2-device don't play along very well: + # https://github.com/archlinux/archinstall/pull/1196#issuecomment-1129715645 + kernel_parameters.append('rd.luks.options=fido2-device=auto,password-echo=no') + else: + kernel_parameters.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:luksdev') + + kernel_parameters.append('root=/dev/mapper/luksdev') + else: + debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}') + kernel_parameters.append(f'root=PARTUUID={root_partition.partuuid}') + + # Zswap should be disabled when using zram. + # https://github.com/archlinux/archinstall/issues/881 + if self._zram_enabled: + kernel_parameters.append('zswap.enabled=0') + + for sub_vol in root_partition.btrfs_subvols: + if sub_vol.is_root(): + kernel_parameters.append(f'rootflags=subvol={sub_vol.name}') + break + + kernel_parameters.append('rw') + kernel_parameters.append(f'rootfstype={root_partition.safe_fs_type.fs_type_mount}') + kernel_parameters.extend(self._kernel_params) + + return kernel_parameters + def _add_systemd_bootloader( self, boot_partition: disk.PartitionModification, @@ -798,42 +836,7 @@ class Installer: "Archinstall won't add any ucode to systemd-boot config.", ) - options_entry = [] - - if root_partition in self._disk_encryption.partitions: - # TODO: We need to detect if the encrypted device is a whole disk encryption, - # or simply a partition encryption. Right now we assume it's a partition (and we always have) - debug('Root partition is an encrypted device, identifying by PARTUUID: {root_partition.partuuid}') - - if self._disk_encryption and self._disk_encryption.hsm_device: - # Note: lsblk UUID must be used, not PARTUUID for sd-encrypt to work - options_entry.append(f'rd.luks.name={root_partition.uuid}=luksdev') - # Note: tpm2-device and fido2-device don't play along very well: - # https://github.com/archlinux/archinstall/pull/1196#issuecomment-1129715645 - options_entry.append('rd.luks.options=fido2-device=auto,password-echo=no') - else: - options_entry.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:luksdev') - - options_entry.append('root=/dev/mapper/luksdev') - else: - debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}') - options_entry.append(f'root=PARTUUID={root_partition.partuuid}') - - # Zswap should be disabled when using zram. - # https://github.com/archlinux/archinstall/issues/881 - if self._zram_enabled: - options_entry.append('zswap.enabled=0') - - for sub_vol in root_partition.btrfs_subvols: - if sub_vol.is_root(): - options_entry.append(f'rootflags=subvol={sub_vol.name}') - break - - options_entry.append('rw') - options_entry.append(f'rootfstype={root_partition.safe_fs_type.fs_type_mount}') - options_entry.extend(self._kernel_params) - - options = 'options ' + ' '.join(options_entry) + '\n' + options = 'options ' + ' '.join(self._get_kernel_params(root_partition)) + '\n' for kernel in self.kernels: for variant in ("", "-fallback"): @@ -1058,21 +1061,7 @@ TIMEOUT=5 else: debug(f"Unknown CPU vendor '{vendor}' detected. Archinstall won't add any ucode to firmware boot entry.") - kernel_parameters = [] - - if root_partition in self._disk_encryption.partitions: - # TODO: We need to detect if the encrypted device is a whole disk encryption, - # or simply a partition encryption. Right now we assume it's a partition (and we always have) - debug(f'Root partition is an encrypted device identifying by PARTUUID: {root_partition.partuuid}') - kernel_parameters.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:luksdev') - kernel_parameters.append('root=/dev/mapper/luksdev') - else: - debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}') - kernel_parameters.append(f'root=PARTUUID={root_partition.partuuid}') - - kernel_parameters.append('rw') - kernel_parameters.append(f'rootfstype={root_partition.safe_fs_type.value}') - kernel_parameters.extend(self._kernel_params) + kernel_parameters = self._get_kernel_params(root_partition) parent_dev_path = disk.device_handler.get_parent_device_path(boot_partition.safe_dev_path) -- cgit v1.2.3-70-g09d2 From f6acdf5b5e30a3bf3b8c38c3221295126afff088 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Tue, 19 Sep 2023 06:47:24 +1000 Subject: Update README with instructions (#2066) * Update README with instructions * Update * Update --------- Co-authored-by: Daniel Girtler --- README.md | 95 ++++++++++++++++++++++++++++++++------------------------------- 1 file changed, 49 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 5fb0b92a..a90fead5 100644 --- a/README.md +++ b/README.md @@ -53,58 +53,25 @@ To load the configuration file into `archinstall` run the following command archinstall --config --creds ``` -# Available Languages - -Archinstall is available in different languages which have been contributed and are maintained by the community. -Current translations are listed below and vary in the amount of translations per language -``` -English -Arabic -Brazilian Portuguese -Czech -Dutch -Estonian -French -Georgian -German -Indonesian -Italian -Korean -Modern Greek -Polish -Portuguese -Russian -Spanish -Swedish -Tamil -Turkish -Ukrainian -Urdu -``` - -Any contributions to the translations are more than welcome, -to get started please follow [the guide](https://github.com/archlinux/archinstall/blob/master/archinstall/locales/README.md) - # Help or Issues -Submit an issue here on GitHub, or submit a post in the discord help channel.
-When doing so, attach the `/var/log/archinstall/install.log` to the issue ticket. This helps us help you! +If any issues are encountered please submit an issue here on Github or submit a post in the discord help channel. +When submitting an issue, pleasee: +* Provide the stacktrace of the output if there is any +* Attach the `/var/log/archinstall/install.log` to the issue ticket. This helps us help you! + * To extract the log from the ISO image, one way is to use
+ ```curl -F'file=@/var/log/archinstall/install.log' https://0x0.st``` -# Mission Statement -Archinstall promises to ship a [guided installer](https://github.com/archlinux/archinstall/blob/master/archinstall/scripts/guided.py) that follows -the [Arch Principles](https://wiki.archlinux.org/index.php/Arch_Linux#Principles) as well as a library to manage services, packages and other Arch Linux aspects. - -The guided installer will provide user-friendly options along the way, but the keyword here is options, they are optional and will never be forced upon anyone. -The guided installer itself is also optional to use if so desired and not forced upon anyone. - ---- - -Archinstall has one fundamental function which is to be a flexible library to manage services, packages and other aspects inside the installed system. -This library is in turn used by the provided guided installer but is also for anyone who wants to script their own installations. +# Available Languages -Therefore, Archinstall will try its best to not introduce any breaking changes except for major releases which may break backwards compatibility after notifying about such changes. +Archinstall is available in different languages which have been contributed and are maintained by the community. +The language can be switched inside the installer (first menu entry). Bare in mind that not all languages provide +full translations as we rely on contributors to do the translations. Each language has an indicator that shows +how much has been translated. +Any contributions to the translations are more than welcome, +to get started please follow [the guide](https://github.com/archlinux/archinstall/blob/master/archinstall/locales/README.md) # Scripting your own installation @@ -236,6 +203,42 @@ There's also a [Building and Testing](https://github.com/archlinux/archinstall/w It will go through everything from packaging, building and running *(with qemu)* the installer against a dev branch. +# FAQ + +## How to dual boot with Windows + +`archinstall` can be used to install Arch alongside an existing Windows installation. +Below are the necessary steps: +* After the Windows installation make sure there is some unallocated space for a Linux installation available +* Boot into the ISO and run`archinstall` +* Select `Disk configuration` -> `Manual partitioning` +* Select the disk on which Windows resides +* Chose `Create a new partition` +* Select a filesystem type +* Now the location of the new partition has to be specified as start and end sectors (values can be suffixed with various units) +* Assign mountpoint `/` +* Back in the partitioning menu, assign the `Boot/ESP` partition the mountpoint `/boot` +* This is all for the partitioning menu, select `Confirm and exit` to return to the main menu +* Set any additional settings you would like to have for the installation +* After completing the setup start the installation + + +# Mission Statement + +Archinstall promises to ship a [guided installer](https://github.com/archlinux/archinstall/blob/master/archinstall/scripts/guided.py) that follows +the [Arch Principles](https://wiki.archlinux.org/index.php/Arch_Linux#Principles) as well as a library to manage services, packages and other Arch Linux aspects. + +The guided installer will provide user-friendly options along the way, but the keyword here is options, they are optional and will never be forced upon anyone. +The guided installer itself is also optional to use if so desired and not forced upon anyone. + +--- + +Archinstall has one fundamental function which is to be a flexible library to manage services, packages and other aspects inside the installed system. +This library is in turn used by the provided guided installer but is also for anyone who wants to script their own installations. + +Therefore, Archinstall will try its best to not introduce any breaking changes except for major releases which may break backwards compatibility after notifying about such changes. + + # Contributing Please see [CONTRIBUTING.md](https://github.com/archlinux/archinstall/blob/master/CONTRIBUTING.md) -- cgit v1.2.3-70-g09d2 From c0ff55d55b855f6975f4e588f6368ccb2a4294ac Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Tue, 19 Sep 2023 06:47:46 +1000 Subject: Allow assigning mountpoint on existing partitions (#2067) Co-authored-by: Daniel Girtler --- archinstall/lib/disk/partitioning_menu.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/archinstall/lib/disk/partitioning_menu.py b/archinstall/lib/disk/partitioning_menu.py index 4c4d8fac..549c7f34 100644 --- a/archinstall/lib/disk/partitioning_menu.py +++ b/archinstall/lib/disk/partitioning_menu.py @@ -61,14 +61,13 @@ class PartitioningList(ListManager): if not selection.exists(): not_filter += [self._actions['mark_formatting']] else: - # only allow these options if the existing partition + # only allow options if the existing partition # was marked as formatting, otherwise we run into issues where # 1. select a new fs -> potentially mark as wipe now # 2. Switch back to old filesystem -> should unmark wipe now, but # how do we know it was the original one? not_filter += [ self._actions['set_filesystem'], - self._actions['assign_mountpoint'], self._actions['mark_bootable'], self._actions['btrfs_mark_compressed'], self._actions['btrfs_set_subvolumes'] -- cgit v1.2.3-70-g09d2 From 33d084f16a9055df0327930abe43f7b91429cf24 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Tue, 19 Sep 2023 16:51:20 -0400 Subject: Use `SUPPORTED` file for locale options (#2069) * Use `SUPPORTED` file for locale options * Add glibc to `PKGBUILD` depends --- PKGBUILD | 1 + archinstall/lib/locale/locale.py | 23 ++++++++--------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index 315256e3..c332d043 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -16,6 +16,7 @@ depends=( 'coreutils' 'cryptsetup' 'e2fsprogs' + 'glibc' 'kbd' 'pciutils' 'procps-ng' diff --git a/archinstall/lib/locale/locale.py b/archinstall/lib/locale/locale.py index c3294e83..90f20cc6 100644 --- a/archinstall/lib/locale/locale.py +++ b/archinstall/lib/locale/locale.py @@ -11,21 +11,14 @@ def list_keyboard_languages() -> Iterator[str]: def list_locales() -> List[str]: - with open('/etc/locale.gen', 'r') as fp: - locales = [] - # before the list of locales begins there's an empty line with a '#' in front - # so we'll collect the localels from bottom up and halt when we're donw - entries = fp.readlines() - entries.reverse() - - for entry in entries: - text = entry.replace('#', '').strip() - if text == '': - break - locales.append(text) - - locales.reverse() - return locales + locales = [] + + with open('/usr/share/i18n/SUPPORTED') as file: + for line in file: + if line != 'C.UTF-8 UTF-8\n': + locales.append(line.rstrip()) + + return locales def list_x11_keyboard_languages() -> Iterator[str]: -- cgit v1.2.3-70-g09d2 From b1e00687953de168bbc31ea435b2c5bdc7c61e39 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Wed, 20 Sep 2023 02:04:23 -0400 Subject: Use `_preset` for defaults in `LocaleMenu` (#2071) * Use `_preset` for defaults in `LocaleMenu` * Fix adjacent typos --- archinstall/lib/locale/locale_menu.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/archinstall/lib/locale/locale_menu.py b/archinstall/lib/locale/locale_menu.py index 29dd775d..2e254315 100644 --- a/archinstall/lib/locale/locale_menu.py +++ b/archinstall/lib/locale/locale_menu.py @@ -52,9 +52,9 @@ class LocaleMenu(AbstractSubMenu): def __init__( self, data_store: Dict[str, Any], - locele_conf: LocaleConfiguration + locale_conf: LocaleConfiguration ): - self._preset = locele_conf + self._preset = locale_conf super().__init__(data_store=data_store) def setup_selection_menu_options(self): @@ -62,19 +62,19 @@ class LocaleMenu(AbstractSubMenu): Selector( _('Keyboard layout'), lambda preset: self._select_kb_layout(preset), - default='us', + default=self._preset.kb_layout, enabled=True) self._menu_options['sys-language'] = \ Selector( _('Locale language'), lambda preset: select_locale_lang(preset), - default='en_US', + default=self._preset.sys_lang, enabled=True) self._menu_options['sys-encoding'] = \ Selector( _('Locale encoding'), lambda preset: select_locale_enc(preset), - default='UTF-8', + default=self._preset.sys_enc, enabled=True) def run(self, allow_reset: bool = True) -> LocaleConfiguration: -- cgit v1.2.3-70-g09d2 From 3e2999bc2752c76e666d916bdb80608ad74528bf Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Wed, 20 Sep 2023 14:49:29 -0400 Subject: Fix acquisition of UUID (#2077) * Fix acquisition of UUID * Fix inadequate solution * Add check for UUID --- archinstall/lib/disk/device_handler.py | 26 +++++++++++++++++--------- archinstall/lib/installer.py | 6 +----- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index 7cd79784..a95e21e3 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -291,6 +291,11 @@ class DeviceHandler(object): else: self._perform_formatting(part_mod.safe_fs_type, part_mod.safe_dev_path) + lsblk_info = self._fetch_part_info(part_mod.safe_dev_path) + + part_mod.partuuid = lsblk_info.partuuid + part_mod.uuid = lsblk_info.uuid + def _perform_partitioning( self, part_mod: PartitionModification, @@ -354,15 +359,10 @@ class DeviceHandler(object): # the partition has a real path now as it was created part_mod.dev_path = Path(partition.path) - - lsblk_info = self._fetch_partuuid(part_mod.dev_path) - - part_mod.partuuid = lsblk_info.partuuid - part_mod.uuid = lsblk_info.uuid except PartitionException as ex: raise DiskError(f'Unable to add partition, most likely due to overlapping sectors: {ex}') from ex - def _fetch_partuuid(self, path: Path) -> LsblkInfo: + def _fetch_part_info(self, path: Path) -> LsblkInfo: attempts = 3 lsblk_info: Optional[LsblkInfo] = None @@ -371,16 +371,24 @@ class DeviceHandler(object): time.sleep(attempt_nr + 1) lsblk_info = get_lsblk_info(path) - if lsblk_info.partuuid: + if lsblk_info.partuuid and lsblk_info.uuid: break self.partprobe(path) - if not lsblk_info or not lsblk_info.partuuid: + if not lsblk_info: + debug(f'Unable to get partition information: {path}') + raise DiskError(f'Unable to get partition information: {path}') + + if not lsblk_info.partuuid: debug(f'Unable to determine new partition uuid: {path}\n{lsblk_info}') raise DiskError(f'Unable to determine new partition uuid: {path}') - debug(f'partuuid found: {lsblk_info.json()}') + if not lsblk_info.uuid: + debug(f'Unable to determine new uuid: {path}\n{lsblk_info}') + raise DiskError(f'Unable to determine new uuid: {path}') + + debug(f'partition information found: {lsblk_info.json()}') return lsblk_info diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 2eb63279..cd6f651d 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -8,8 +8,6 @@ import time from pathlib import Path from typing import Any, List, Optional, TYPE_CHECKING, Union, Dict, Callable -from ..lib.disk.device_model import get_lsblk_info - from . import disk from .exceptions import DiskError, ServiceException, RequirementError, HardwareIncompatibilityError, SysCallError from .general import SysCommand @@ -937,9 +935,7 @@ class Installer: self.pacman.strap('limine') info(f"Limine boot partition: {boot_partition.dev_path}") - # XXX: We cannot use `root_partition.uuid` since corresponds to the UUID of the root - # partition before the format. - root_uuid = get_lsblk_info(root_partition.safe_dev_path).uuid + root_uuid = root_partition.uuid def create_pacman_hook(contents: str): HOOK_DIR = "/etc/pacman.d/hooks" -- cgit v1.2.3-70-g09d2 From da9599a9f6f9264d657d0529b4fd53df2897b0c0 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Thu, 21 Sep 2023 03:46:55 -0400 Subject: Rework `_get_kernel_params()` for `_add_grub_bootloader()` (#2079) * Rework `_get_kernel_params()` for `_add_grub_bootloader()` * Remove uncommenting of GRUB config line for encrypted /boot support --- archinstall/lib/installer.py | 59 ++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index cd6f651d..d71e6ad3 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -713,42 +713,60 @@ class Installer: return root return None - def _get_kernel_params(self, root_partition: disk.PartitionModification) -> List[str]: + def _get_kernel_params( + self, + root_partition: disk.PartitionModification, + id_root: bool = True, + partuuid: bool = True + ) -> List[str]: kernel_parameters = [] if root_partition in self._disk_encryption.partitions: # TODO: We need to detect if the encrypted device is a whole disk encryption, # or simply a partition encryption. Right now we assume it's a partition (and we always have) - debug('Root partition is an encrypted device, identifying by PARTUUID: {root_partition.partuuid}') if self._disk_encryption and self._disk_encryption.hsm_device: - # Note: lsblk UUID must be used, not PARTUUID for sd-encrypt to work - kernel_parameters.append(f'rd.luks.name={root_partition.uuid}=luksdev') + debug(f'Root partition is an encrypted device, identifying by UUID: {root_partition.uuid}') + # Note: UUID must be used, not PARTUUID for sd-encrypt to work + kernel_parameters.append(f'rd.luks.name={root_partition.uuid}=root') # Note: tpm2-device and fido2-device don't play along very well: # https://github.com/archlinux/archinstall/pull/1196#issuecomment-1129715645 kernel_parameters.append('rd.luks.options=fido2-device=auto,password-echo=no') + elif partuuid: + debug(f'Root partition is an encrypted device, identifying by PARTUUID: {root_partition.partuuid}') + kernel_parameters.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:root') else: - kernel_parameters.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:luksdev') - - kernel_parameters.append('root=/dev/mapper/luksdev') - else: - debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}') - kernel_parameters.append(f'root=PARTUUID={root_partition.partuuid}') + debug(f'Root partition is an encrypted device, identifying by UUID: {root_partition.uuid}') + kernel_parameters.append(f'cryptdevice=UUID={root_partition.uuid}:root') + + if id_root: + kernel_parameters.append('root=/dev/mapper/root') + elif id_root: + if partuuid: + debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}') + kernel_parameters.append(f'root=PARTUUID={root_partition.partuuid}') + else: + debug(f'Identifying root partition by UUID: {root_partition.uuid}') + kernel_parameters.append(f'root=UUID={root_partition.uuid}') # Zswap should be disabled when using zram. # https://github.com/archlinux/archinstall/issues/881 if self._zram_enabled: kernel_parameters.append('zswap.enabled=0') - for sub_vol in root_partition.btrfs_subvols: - if sub_vol.is_root(): - kernel_parameters.append(f'rootflags=subvol={sub_vol.name}') - break + if id_root: + for sub_vol in root_partition.btrfs_subvols: + if sub_vol.is_root(): + kernel_parameters.append(f'rootflags=subvol={sub_vol.name}') + break + + kernel_parameters.append('rw') - kernel_parameters.append('rw') kernel_parameters.append(f'rootfstype={root_partition.safe_fs_type.fs_type_mount}') kernel_parameters.extend(self._kernel_params) + debug(f'kernel parameters: {" ".join(kernel_parameters)}') + return kernel_parameters def _add_systemd_bootloader( @@ -863,16 +881,9 @@ class Installer: grub_default = self.target / 'etc/default/grub' config = grub_default.read_text() - cmdline_linux = [] - - if root_partition in self._disk_encryption.partitions: - debug(f"Using UUID {root_partition.uuid} as encrypted root identifier") - - cmdline_linux.append(f'cryptdevice=UUID={root_partition.uuid}:cryptlvm') - config = re.sub(r'#(GRUB_ENABLE_CRYPTODISK=y\n)', r'\1', config, 1) + kernel_parameters = ' '.join(self._get_kernel_params(root_partition, False, False)) + config = re.sub(r'(GRUB_CMDLINE_LINUX=")("\n)', rf'\1{kernel_parameters}\2', config, 1) - cmdline_linux.append(f'rootfstype={root_partition.safe_fs_type.value}') - config = re.sub(r'(GRUB_CMDLINE_LINUX=")("\n)', rf'\1{" ".join(cmdline_linux)}\2', config, 1) grub_default.write_text(config) info(f"GRUB boot partition: {boot_partition.dev_path}") -- cgit v1.2.3-70-g09d2 From 0e93d5b468291bccd4d4df9b07cffa1ccf7bb21e Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Thu, 21 Sep 2023 18:34:11 +1000 Subject: Update instructions to run archinstall from ISO (#2076) Update instructions to run archinstall from ISO --- README.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a90fead5..acc377ec 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,13 @@ The definitions of the profiles and what packages they will install can be seen ## Using a Live ISO Image If you want to test a commit, branch or bleeding edge release from the repository using the vanilla Arch Live ISO image, -you can replace the version of archinstall with a new version and run that with the steps described below: +you can replace the version of archinstall with a new version and run that with the steps described below. + +*Note: When booting from a live USB then the space on the ramdisk is limited and may not be sufficient to allow +running a re-installation or upgrade of the installer. In case one runs into this issue, any of the following can be used +- Resize the root partition on the fly https://wiki.archlinux.org/title/Archiso#Adjusting_the_size_of_root_partition_on_the_fly +- The boot parameter `copytoram=y` (https://gitlab.archlinux.org/archlinux/mkinitcpio/mkinitcpio-archiso/-/blob/master/docs/README.bootparams#L26) +can be specified which will copy the root filesystem to tmpfs.* 1. You need a working network connection 2. Install the build requirements with `pacman -Sy; pacman -S git python-pip gcc pkgconf` @@ -179,9 +185,10 @@ you can replace the version of archinstall with a new version and run that with 4. Now clone the latest repository with `git clone https://github.com/archlinux/archinstall` 5. Enter the repository with `cd archinstall` *At this stage, you can choose to check out a feature branch for instance with `git checkout v2.3.1-rc1`* -6. Build the project and install it using `pip install --break-system-packages .` - -After this, running archinstall with `python -m archinstall` will run against whatever branch you chose in step 5. +6. To run the source code, there are 2 different options: + - Run a specific branch version from source directly using `python -m archinstall`, in most cases this will work just fine, the + rare case it will not work is if the source has introduced any new dependencies that are not installed yet + - Installing the branch version with `pip install --break-system-packages .` and `archinstall` ## Without a Live ISO Image @@ -193,7 +200,9 @@ This can be done by installing `pacman -S arch-install-scripts util-linux` local # losetup -a | grep "testimage.img" | awk -F ":" '{print $1}' # pip install --upgrade archinstall # python -m archinstall --script guided - # qemu-system-x86_64 -enable-kvm -machine q35,accel=kvm -device intel-iommu -cpu host -m 4096 -boot order=d -drive file=./testimage.img,format=raw -drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF_CODE.fd -drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF_VARS.fd + # qemu-system-x86_64 -enable-kvm -machine q35,accel=kvm -device intel-iommu -cpu host -m 4096 -boot order=d -drive +file=./testimage.img,format=raw -drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF_CODE.fd +-drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF_VARS.fd This will create a *20 GB* `testimage.img` and create a loop device which we can use to format and install to.
`archinstall` is installed and executed in [guided mode](#docs-todo). Once the installation is complete, ~~you can use qemu/kvm to boot the test media.~~
-- cgit v1.2.3-70-g09d2 From c75ae97f007f7f7d1d8921e1bd38bcc01b6035f1 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Thu, 21 Sep 2023 19:48:49 +1000 Subject: Sort the profiles with ignore case (#2080) Co-authored-by: Daniel Girtler --- archinstall/lib/profile/profiles_handler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/archinstall/lib/profile/profiles_handler.py b/archinstall/lib/profile/profiles_handler.py index 07910b08..7810db2f 100644 --- a/archinstall/lib/profile/profiles_handler.py +++ b/archinstall/lib/profile/profiles_handler.py @@ -371,6 +371,7 @@ class ProfileHandler: Helper function to perform a profile selection """ options = {p.name: p for p in selectable_profiles} + options = dict((k, v) for k, v in sorted(options.items(), key=lambda x: x[0].upper())) warning = str(_('Are you sure you want to reset this setting?')) @@ -388,7 +389,7 @@ class ProfileHandler: allow_reset=allow_reset, allow_reset_warning_msg=warning, multi=multi, - sort=True, + sort=False, preview_command=self.preview_text, preview_size=0.5 ).run() -- cgit v1.2.3-70-g09d2 From 6d908e8cd7d395a1f8f2473b2dc9ca3e2ace1226 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Thu, 21 Sep 2023 11:13:22 -0400 Subject: Fix `_add_efistub_bootloader()` partition number with partn (#2084) --- archinstall/lib/disk/device_handler.py | 7 ++++++- archinstall/lib/disk/device_model.py | 3 +++ archinstall/lib/installer.py | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index a95e21e3..494319a1 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -293,6 +293,7 @@ class DeviceHandler(object): lsblk_info = self._fetch_part_info(part_mod.safe_dev_path) + part_mod.partn = lsblk_info.partn part_mod.partuuid = lsblk_info.partuuid part_mod.uuid = lsblk_info.uuid @@ -371,7 +372,7 @@ class DeviceHandler(object): time.sleep(attempt_nr + 1) lsblk_info = get_lsblk_info(path) - if lsblk_info.partuuid and lsblk_info.uuid: + if lsblk_info.partn and lsblk_info.partuuid and lsblk_info.uuid: break self.partprobe(path) @@ -380,6 +381,10 @@ class DeviceHandler(object): debug(f'Unable to get partition information: {path}') raise DiskError(f'Unable to get partition information: {path}') + if not lsblk_info.partn: + debug(f'Unable to determine new partition number: {path}\n{lsblk_info}') + raise DiskError(f'Unable to determine new partition number: {path}') + if not lsblk_info.partuuid: debug(f'Unable to determine new partition uuid: {path}\n{lsblk_info}') raise DiskError(f'Unable to determine new partition uuid: {path}') diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index 69038b01..ec0207a1 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -627,6 +627,7 @@ class PartitionModification: # only set if the device was created or exists dev_path: Optional[Path] = None + partn: Optional[int] = None partuuid: Optional[str] = None uuid: Optional[str] = None @@ -933,6 +934,7 @@ class LsblkInfo: ptuuid: str = '' rota: bool = False tran: Optional[str] = None + partn: Optional[int] = None partuuid: Optional[str] = None parttype :Optional[str] = None uuid: Optional[str] = None @@ -957,6 +959,7 @@ class LsblkInfo: 'ptuuid': self.ptuuid, 'rota': self.rota, 'tran': self.tran, + 'partn': self.partn, 'partuuid': self.partuuid, 'parttype' : self.parttype, 'uuid': self.uuid, diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index d71e6ad3..d0aa2252 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -1085,7 +1085,7 @@ TIMEOUT=5 cmd = f'efibootmgr ' \ f'--disk {parent_dev_path} ' \ - f'--part {boot_partition.safe_dev_path} ' \ + f'--part {boot_partition.partn} ' \ f'--create ' \ f'--label "{label}" ' \ f'--loader {loader} ' \ -- cgit v1.2.3-70-g09d2 From 8e40de85fb2fcde81ac994335286a863c6e46c86 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Fri, 22 Sep 2023 19:43:12 +1000 Subject: Only parse profile classes when loading modules (#2088) Co-authored-by: Daniel Girtler --- archinstall/lib/profile/profiles_handler.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/archinstall/lib/profile/profiles_handler.py b/archinstall/lib/profile/profiles_handler.py index 7810db2f..03039321 100644 --- a/archinstall/lib/profile/profiles_handler.py +++ b/archinstall/lib/profile/profiles_handler.py @@ -2,6 +2,7 @@ from __future__ import annotations import importlib.util import sys +import inspect from collections import Counter from functools import cached_property from pathlib import Path @@ -276,12 +277,15 @@ class ProfileHandler: profiles = [] for k, v in module.__dict__.items(): if isinstance(v, type) and v.__module__ == module.__name__: - try: - cls_ = v() - if isinstance(cls_, Profile): - profiles.append(cls_) - except Exception: - debug(f'Cannot import {module}, it does not appear to be a Profile class') + bases = inspect.getmro(v) + + if Profile in bases: + try: + cls_ = v() + if isinstance(cls_, Profile): + profiles.append(cls_) + except Exception: + debug(f'Cannot import {module}, it does not appear to be a Profile class') return profiles -- cgit v1.2.3-70-g09d2 From db057aee1e1258e9c4b2b5b2a3561052bdd3b4b3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 11:43:27 +0200 Subject: Add renovate.json (#2086) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- renovate.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 renovate.json diff --git a/renovate.json b/renovate.json new file mode 100644 index 00000000..39a2b6e9 --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base" + ] +} -- cgit v1.2.3-70-g09d2 From d6ef345fc3549db0127050fa8b762bc8ca1cfb3f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 11:45:03 +0200 Subject: chore(deps): update dependency dev/mypy to v1.5.1 (#2089) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 83c68e1d..861b7f5b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ Source = "https://github.com/archlinux/archinstall" [project.optional-dependencies] dev = [ - "mypy==1.1.1", + "mypy==1.5.1", "pre-commit==3.3.1", ] doc = ["sphinx"] -- cgit v1.2.3-70-g09d2 From 4f9e8d2381d9603f99d37fe38462a2f98d059d37 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 11:46:10 +0200 Subject: chore(deps): update dependency dev/pre-commit to v3.4.0 (#2090) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 861b7f5b..36ee0492 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ Source = "https://github.com/archlinux/archinstall" [project.optional-dependencies] dev = [ "mypy==1.5.1", - "pre-commit==3.3.1", + "pre-commit==3.4.0", ] doc = ["sphinx"] -- cgit v1.2.3-70-g09d2 From d6e3a4651f2ff944a00e8acfb316db8be2fbcf3e Mon Sep 17 00:00:00 2001 From: Sxmourai <49468969+Sxmourai@users.noreply.github.com> Date: Fri, 22 Sep 2023 11:46:59 +0200 Subject: Renamed hyperland to hyprland, fixed seatd via post_installation and added waybar-hyprland (#1824) * Renamed hyperland to hyprland, fixed seatd via post_installation and installed waybar * Removed the launching of seatd on the installation process * Starting to add nvidia support, and automatic configuring of hyprland * Starting to add auto configuration of hyprland... But this will need maintenance * Added hyprpaper auto config Gonna make waybar auto config next * Waybar auto config is starting... I can't test rn I'm on vacation and my connection is quite bad (68 days for arch iso) * Added wlogout support (and swaylock) * Fixed file managers printing * Starting to add a shell config... Definitely don't push this * Reverted custom-shell config (create a separate PR) * Removed systemd-logind, as that was just for testing the selector * Added polkit as an option for the seat. As it's a dependency of the hyprland package * Flake8 fix * The name change wasn't propegated to the menu * Added newline at the end of general_conf.py to not alter it * Removed newline at the end of general_conf.py to not alter it * Renamed the Hyprland class --------- Co-authored-by: Anton Hvornum Co-authored-by: Anton Hvornum --- archinstall/default_profiles/desktops/hyperland.py | 27 ---------- archinstall/default_profiles/desktops/hyprland.py | 62 ++++++++++++++++++++++ archinstall/lib/interactions/general_conf.py | 2 +- 3 files changed, 63 insertions(+), 28 deletions(-) delete mode 100644 archinstall/default_profiles/desktops/hyperland.py create mode 100644 archinstall/default_profiles/desktops/hyprland.py diff --git a/archinstall/default_profiles/desktops/hyperland.py b/archinstall/default_profiles/desktops/hyperland.py deleted file mode 100644 index 58ee8cca..00000000 --- a/archinstall/default_profiles/desktops/hyperland.py +++ /dev/null @@ -1,27 +0,0 @@ -from typing import List, Optional, Any, TYPE_CHECKING - -from archinstall.default_profiles.profile import ProfileType, GreeterType -from archinstall.default_profiles.xorg import XorgProfile - -if TYPE_CHECKING: - _: Any - - -class HyperlandProfile(XorgProfile): - def __init__(self): - super().__init__('Hyperland', ProfileType.DesktopEnv, description='') - - @property - def packages(self) -> List[str]: - return [ - "hyprland", - "dunst", - "xdg-desktop-portal-hyprland", - "kitty", - "qt5-wayland", - "qt6-wayland" - ] - - @property - def default_greeter_type(self) -> Optional[GreeterType]: - return GreeterType.Sddm diff --git a/archinstall/default_profiles/desktops/hyprland.py b/archinstall/default_profiles/desktops/hyprland.py new file mode 100644 index 00000000..f464c828 --- /dev/null +++ b/archinstall/default_profiles/desktops/hyprland.py @@ -0,0 +1,62 @@ +from enum import Enum +from typing import List, Optional, TYPE_CHECKING, Any + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile +from archinstall.lib.menu import Menu + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +class SeatAccess(Enum): + seatd = 'seatd' + polkit = 'polkit' + + +class HyprlandProfile(XorgProfile): + def __init__(self): + super().__init__('Hyprland', ProfileType.DesktopEnv, description='') + + self.custom_settings = {'seat_access': None} + + @property + def packages(self) -> List[str]: + return [ + "hyprland", + "dunst", + "xdg-desktop-portal-hyprland", + "qt5-wayland", + "qt6-wayland" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Sddm + + @property + def services(self) -> List[str]: + if pref := self.custom_settings.get('seat_access', None): + return [pref] + return [] + + def _ask_seat_access(self): + # need to activate seat service and add to seat group + title = str(_('Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)')) + title += str(_('\n\nChoose an option to give Hyprland access to your hardware')) + + options = [e.value for e in SeatAccess] + default = None + + if seat := self.custom_settings.get('seat_access', None): + default = seat + + choice = Menu(title, options, skip=False, preset_values=default).run() + self.custom_settings['seat_access'] = choice.single_value + + def do_on_select(self): + self._ask_seat_access() + + def install(self, install_session: 'Installer'): + super().install(install_session) diff --git a/archinstall/lib/interactions/general_conf.py b/archinstall/lib/interactions/general_conf.py index 8dd6e94f..56598e25 100644 --- a/archinstall/lib/interactions/general_conf.py +++ b/archinstall/lib/interactions/general_conf.py @@ -218,4 +218,4 @@ def select_additional_repositories(preset: List[str]) -> List[str]: case MenuSelectionType.Reset: return [] case MenuSelectionType.Selection: return choice.single_value - return [] + return [] \ No newline at end of file -- cgit v1.2.3-70-g09d2 From ca3051ebf3ddd45f90a3bd3244df4640fb0b1c58 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Fri, 22 Sep 2023 16:47:50 +0200 Subject: Added kitty, dolphin, wofi to hyprland (#2092) --- archinstall/default_profiles/desktops/hyprland.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/archinstall/default_profiles/desktops/hyprland.py b/archinstall/default_profiles/desktops/hyprland.py index f464c828..065ca6f2 100644 --- a/archinstall/default_profiles/desktops/hyprland.py +++ b/archinstall/default_profiles/desktops/hyprland.py @@ -26,6 +26,9 @@ class HyprlandProfile(XorgProfile): return [ "hyprland", "dunst", + "kitty", + "dolphin", + "wofi", "xdg-desktop-portal-hyprland", "qt5-wayland", "qt6-wayland" -- cgit v1.2.3-70-g09d2 From ad6cbcfd3d5e21177150dd82f9c00661dff4e98e Mon Sep 17 00:00:00 2001 From: Thomas Aldrian Date: Sat, 23 Sep 2023 03:11:21 +0100 Subject: Fix GRUB with non-/boot ESP (#2015) * Fix GRUB with non-/boot ESP Fixes #2001 * GRUB EFI fixes * Fix flake8 and mypy --------- Co-authored-by: Anton Hvornum Co-authored-by: Daniel Girtler --- archinstall/lib/disk/device_model.py | 9 +++++++-- archinstall/lib/installer.py | 18 ++++++++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index ec0207a1..ad2628ae 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -800,20 +800,25 @@ class DeviceModification: def get_efi_partition(self) -> Optional[PartitionModification]: """ Similar to get_boot_partition() but excludes XBOOTLDR partitions from it's candidates. + Also works with ESP flag. """ - filtered = filter(lambda x: x.is_boot() and x.fs_type == FilesystemType.Fat32 and PartitionFlag.XBOOTLDR not in x.flags, self.partitions) + filtered = filter(lambda x: (x.is_boot() or PartitionFlag.ESP in x.flags) and x.fs_type == FilesystemType.Fat32 and PartitionFlag.XBOOTLDR not in x.flags, self.partitions) return next(filtered, None) def get_boot_partition(self) -> Optional[PartitionModification]: """ Returns the first partition marked as XBOOTLDR (PARTTYPE id of bc13c2ff-...) or Boot and has a mountpoint. Only returns XBOOTLDR if separate EFI is detected using self.get_efi_partition() + Will return None if no suitable partition is found. """ if efi_partition := self.get_efi_partition(): filtered = filter(lambda x: x.is_boot() and x != efi_partition and x.mountpoint, self.partitions) if boot_partition := next(filtered, None): return boot_partition - return efi_partition + if efi_partition.is_boot(): + return efi_partition + else: + return None else: filtered = filter(lambda x: x.is_boot() and x.mountpoint, self.partitions) return next(filtered, None) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index d0aa2252..afc1184b 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -874,7 +874,8 @@ class Installer: def _add_grub_bootloader( self, boot_partition: disk.PartitionModification, - root_partition: disk.PartitionModification + root_partition: disk.PartitionModification, + efi_partition: Optional[disk.PartitionModification] ): self.pacman.strap('grub') # no need? @@ -895,12 +896,15 @@ class Installer: '--debug' ] - if SysInfo.has_uefi(): + if SysInfo.has_uefi() and efi_partition is not None: + info(f"GRUB EFI partition: {efi_partition.dev_path}") + self.pacman.strap('efibootmgr') # TODO: Do we need? Yes, but remove from minimal_installation() instead? add_options = [ '--target=x86_64-efi', - f'--efi-directory={boot_partition.mountpoint}', + f'--efi-directory={efi_partition.mountpoint}' + f'--boot-directory={boot_partition.mountpoint if boot_partition else "/boot"}' '--bootloader-id=GRUB', '--removable' ] @@ -913,8 +917,10 @@ class Installer: try: SysCommand(command, peek_output=True) except SysCallError as err: - raise DiskError(f"Could not install GRUB to {self.target}{boot_partition.mountpoint}: {err}") + raise DiskError(f"Could not install GRUB to {self.target}{efi_partition.mountpoint}: {err}") else: + info(f"GRUB boot partition: {boot_partition.dev_path}") + parent_dev_path = disk.device_handler.get_parent_device_path(boot_partition.safe_dev_path) add_options = [ @@ -931,7 +937,7 @@ class Installer: try: SysCommand( f'/usr/bin/arch-chroot {self.target} ' - f'grub-mkconfig -o {boot_partition.mountpoint}/grub/grub.cfg' + f'grub-mkconfig -o {boot_partition.mountpoint if boot_partition else "/boot"}/grub/grub.cfg' ) except SysCallError as err: raise DiskError(f"Could not configure GRUB: {err}") @@ -1131,7 +1137,7 @@ TIMEOUT=5 case Bootloader.Systemd: self._add_systemd_bootloader(boot_partition, root_partition, efi_partition) case Bootloader.Grub: - self._add_grub_bootloader(boot_partition, root_partition) + self._add_grub_bootloader(boot_partition, root_partition, efi_partition) case Bootloader.Efistub: self._add_efistub_bootloader(boot_partition, root_partition) case Bootloader.Limine: -- cgit v1.2.3-70-g09d2 From 5513ac0af580970d12bd5d375a97d7118dd7be98 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Sat, 23 Sep 2023 15:15:11 +0200 Subject: Bumped version to: v2.6.1 --- archinstall/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archinstall/__init__.py b/archinstall/__init__.py index 7645ae39..f89d7c39 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -39,7 +39,7 @@ if TYPE_CHECKING: _: Any -__version__ = "2.6.0" +__version__ = "2.6.1" storage['__version__'] = __version__ -- cgit v1.2.3-70-g09d2 -- cgit v1.2.3-70-g09d2 From f078c74692e64e4316107175b3b0d8f021fff3ba Mon Sep 17 00:00:00 2001 From: Niko Hoffrén Date: Sun, 24 Sep 2023 01:48:09 +0300 Subject: Fix small typo in README.md (#2095) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index acc377ec..2420673f 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ archinstall --config --creds -- cgit v1.2.3-70-g09d2 From ab5de3e2e6f5afba81d441cc9a46ebadaad88732 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Sat, 23 Sep 2023 20:51:20 -0400 Subject: Add support for loop devices (#2097) --- archinstall/lib/disk/device_handler.py | 15 +++++++++++++-- archinstall/lib/disk/device_model.py | 5 ++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index 494319a1..9646103f 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -10,7 +10,7 @@ from typing import List, Dict, Any, Optional, TYPE_CHECKING from parted import ( # type: ignore Disk, Geometry, FileSystem, PartitionException, DiskLabelException, - getAllDevices, freshDisk, Partition, Device + getDevice, getAllDevices, freshDisk, Partition, Device ) from .device_model import ( @@ -45,7 +45,18 @@ class DeviceHandler(object): def load_devices(self): block_devices = {} - for device in getAllDevices(): + devices = getAllDevices() + + try: + loop_devices = SysCommand(['losetup', '-a']) + except SysCallError as err: + debug(f'Failed to get loop devices: {err}') + else: + for ld_info in str(loop_devices).splitlines(): + loop_device = getDevice(ld_info.split(':', maxsplit=1)[0]) + devices.append(loop_device) + + for device in devices: if get_lsblk_info(device.path).type == 'rom': continue diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index ad2628ae..b520ad1b 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -374,7 +374,10 @@ class _DeviceInfo: @classmethod def from_disk(cls, disk: Disk) -> _DeviceInfo: device = disk.device - device_type = parted.devices[device.type] + if device.type == 18: + device_type = 'loop' + else: + device_type = parted.devices[device.type] sector_size = Size(device.sectorSize, Unit.B) free_space = [DeviceGeometry(g, sector_size) for g in disk.getFreeSpaceRegions()] -- cgit v1.2.3-70-g09d2 From abd5aeba6a53814def65115764acef722b15e049 Mon Sep 17 00:00:00 2001 From: "K.B.Dharun Krishna" Date: Sun, 24 Sep 2023 06:33:04 +0530 Subject: po: update Tamil translation (#2096) * po: update Tamil translation * Update translation workflow check * Update translation workflow check * Update translation workflow check * Update translation files * Update translation workflow check --------- Co-authored-by: Daniel Girtler --- .github/workflows/translation-check.yaml | 11 +++-- archinstall/locales/ar/LC_MESSAGES/base.po | 9 ++++ archinstall/locales/base.pot | 11 +++++ archinstall/locales/cs/LC_MESSAGES/base.po | 9 ++++ archinstall/locales/de/LC_MESSAGES/base.po | 14 ++++++ archinstall/locales/el/LC_MESSAGES/base.po | 9 ++++ archinstall/locales/en/LC_MESSAGES/base.po | 9 ++++ archinstall/locales/es/LC_MESSAGES/base.po | 14 ++++++ archinstall/locales/et/LC_MESSAGES/base.po | 14 ++++++ archinstall/locales/fr/LC_MESSAGES/base.po | 14 ++++++ archinstall/locales/id/LC_MESSAGES/base.po | 9 ++++ archinstall/locales/it/LC_MESSAGES/base.po | 9 ++++ archinstall/locales/ka/LC_MESSAGES/base.po | 9 ++++ archinstall/locales/ko/LC_MESSAGES/base.po | 9 ++++ archinstall/locales/nl/LC_MESSAGES/base.po | 9 ++++ archinstall/locales/pl/LC_MESSAGES/base.po | 9 ++++ archinstall/locales/pt/LC_MESSAGES/base.po | 14 ++++++ archinstall/locales/pt_BR/LC_MESSAGES/base.po | 14 ++++++ archinstall/locales/ro/LC_MESSAGES/base.po | 14 ++++++ archinstall/locales/ru/LC_MESSAGES/base.po | 14 ++++++ archinstall/locales/sv/LC_MESSAGES/base.po | 9 ++++ archinstall/locales/ta/LC_MESSAGES/base.mo | Bin 66233 -> 69508 bytes archinstall/locales/ta/LC_MESSAGES/base.po | 64 ++++++++++++++------------ archinstall/locales/tr/LC_MESSAGES/base.po | 14 ++++++ archinstall/locales/uk/LC_MESSAGES/base.po | 9 ++++ archinstall/locales/ur/LC_MESSAGES/base.po | 9 ++++ archinstall/locales/zh-CN/LC_MESSAGES/base.po | 14 ++++++ archinstall/locales/zh-TW/LC_MESSAGES/base.po | 14 ++++++ 28 files changed, 325 insertions(+), 32 deletions(-) diff --git a/.github/workflows/translation-check.yaml b/.github/workflows/translation-check.yaml index 11418d31..c0abbaa6 100644 --- a/.github/workflows/translation-check.yaml +++ b/.github/workflows/translation-check.yaml @@ -14,10 +14,15 @@ jobs: steps: - uses: actions/checkout@v4 - run: pacman --noconfirm -Syu python git diffutils - - run: | + - name: Verify all translation scripts are up to date + run: | cd .. cp -r archinstall archinstall_orig cd archinstall/archinstall/locales - bash locales_generator.sh + bash locales_generator.sh 1> /dev/null cd ../../.. - git diff --no-index --name-only archinstall_orig archinstall + git diff \ + --quiet --no-index --name-only \ + archinstall_orig/archinstall/locales \ + archinstall/archinstall/locales \ + || (echo "Translation files have not been updated after translation, please run ./locales_generator.sh once more and commit" && exit 1) diff --git a/archinstall/locales/ar/LC_MESSAGES/base.po b/archinstall/locales/ar/LC_MESSAGES/base.po index 41bcda0c..88fedf65 100644 --- a/archinstall/locales/ar/LC_MESSAGES/base.po +++ b/archinstall/locales/ar/LC_MESSAGES/base.po @@ -1142,3 +1142,12 @@ msgstr "" msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "" + +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" diff --git a/archinstall/locales/base.pot b/archinstall/locales/base.pot index 75e47c9a..7ab331c3 100644 --- a/archinstall/locales/base.pot +++ b/archinstall/locales/base.pot @@ -1218,3 +1218,14 @@ msgstr "" msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "" + +msgid "" +"Hyprland needs access to your seat (collection of hardware devices i.e. " +"keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" diff --git a/archinstall/locales/cs/LC_MESSAGES/base.po b/archinstall/locales/cs/LC_MESSAGES/base.po index bc771879..29cb405a 100644 --- a/archinstall/locales/cs/LC_MESSAGES/base.po +++ b/archinstall/locales/cs/LC_MESSAGES/base.po @@ -1238,3 +1238,12 @@ msgstr " - Zakázáno/Výchozí : 0 (Zakáže paralelní stahování, povolí #, fuzzy msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "Neplatný vstup! Zkuste to, prosím, znovu s platným vstupem [1 až {}, nebo 0 pro vypnutí]" + +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" diff --git a/archinstall/locales/de/LC_MESSAGES/base.po b/archinstall/locales/de/LC_MESSAGES/base.po index 83918b05..285eddb1 100644 --- a/archinstall/locales/de/LC_MESSAGES/base.po +++ b/archinstall/locales/de/LC_MESSAGES/base.po @@ -1201,3 +1201,17 @@ msgstr " - Deaktivieren/Standard : 0 (Deaktiviert parallele Downloads, erlaubt n #, fuzzy msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "Ungültige Eingabe! Erneut mit gültiger Eingabe versuchen [1 bis {}, oder 0 zum deaktivieren]" + +#, fuzzy +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "Sway benötigt Zugriff auf ihren Seat (Sammlung von Hardwaregeräten wie Tastatur, Maus, usw.)" + +#, fuzzy +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" +"\n" +"\n" +"Option auswählen, um Sway Zugriff auf deine Hardware zu geben" diff --git a/archinstall/locales/el/LC_MESSAGES/base.po b/archinstall/locales/el/LC_MESSAGES/base.po index 592cfe77..2a603b37 100644 --- a/archinstall/locales/el/LC_MESSAGES/base.po +++ b/archinstall/locales/el/LC_MESSAGES/base.po @@ -1245,3 +1245,12 @@ msgstr " - Απενεργοποίηση/Προκαθορισμένο : 0 ( Απ #, fuzzy msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "Μη έγκυρη είσοδος! Προσπαθήστε ξανά με μία έγκυρη είσοδο [1 μέχρι {}, ή 0 για απενεργοποίηση]" + +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" diff --git a/archinstall/locales/en/LC_MESSAGES/base.po b/archinstall/locales/en/LC_MESSAGES/base.po index c20d05cf..116839fc 100644 --- a/archinstall/locales/en/LC_MESSAGES/base.po +++ b/archinstall/locales/en/LC_MESSAGES/base.po @@ -1130,3 +1130,12 @@ msgstr "" msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "" + +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" diff --git a/archinstall/locales/es/LC_MESSAGES/base.po b/archinstall/locales/es/LC_MESSAGES/base.po index 330d0d9e..fe447b22 100644 --- a/archinstall/locales/es/LC_MESSAGES/base.po +++ b/archinstall/locales/es/LC_MESSAGES/base.po @@ -1253,6 +1253,20 @@ msgstr " - Deshabilitar/Predeterminado : 0 ( Deshabilita la descarga paralela, p msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "¡Entrada no válida! Intente nuevamente con un valor válido [1 a {}, o 0 para deshabilitar]" +#, fuzzy +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "Sway necesita acceso a sus dispositivos de hardware (teclado, mouse, etc.)" + +#, fuzzy +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" +"\n" +"\n" +"Elija una opción para darle a Sway acceso a su hardware" + #~ msgid "Add :" #~ msgstr "Añadir :" diff --git a/archinstall/locales/et/LC_MESSAGES/base.po b/archinstall/locales/et/LC_MESSAGES/base.po index f7f15578..60c616c7 100644 --- a/archinstall/locales/et/LC_MESSAGES/base.po +++ b/archinstall/locales/et/LC_MESSAGES/base.po @@ -1218,3 +1218,17 @@ msgstr " - Keela/Vaikimisi : 0 ( keelab paralleelse allalaadimise, võimaldab ai #, fuzzy msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "Vale sisestus! Proovige uuesti kehtiva sisendiga [1 {max_downloads} või 0 keelamiseks]." + +#, fuzzy +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "Sway vajab juurdepääsu teie seatile (riistvaraseadmete kogum, st klaviatuur, hiir jne." + +#, fuzzy +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" +"\n" +"\n" +"Valige valik, et anda Sway'le juurdepääs teie riistvarale" diff --git a/archinstall/locales/fr/LC_MESSAGES/base.po b/archinstall/locales/fr/LC_MESSAGES/base.po index 98a5a541..3666eda9 100644 --- a/archinstall/locales/fr/LC_MESSAGES/base.po +++ b/archinstall/locales/fr/LC_MESSAGES/base.po @@ -1201,6 +1201,20 @@ msgstr " - Désactiver/Défaut : 0 (Désactive le téléchargement parallèle, n msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "Entrée invalide ! Réessayer avec une entrée valide [1 pour {}, ou 0 pour désactiver]" +#, fuzzy +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "Sway a besoin d'accéder à votre espace (ensemble de périphériques matériels : clavier, souris, etc.)" + +#, fuzzy +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" +"\n" +"\n" +"Choisir une option pour permettre à Sway d'accéder à votre matériel" + #, python-brace-format #~ msgid "Edit {origkey} :" #~ msgstr "Modifier {origkey} :" diff --git a/archinstall/locales/id/LC_MESSAGES/base.po b/archinstall/locales/id/LC_MESSAGES/base.po index 3511de14..fcd089ac 100644 --- a/archinstall/locales/id/LC_MESSAGES/base.po +++ b/archinstall/locales/id/LC_MESSAGES/base.po @@ -1244,3 +1244,12 @@ msgstr " - Nonaktifkan/Default: 0 (Menonaktifkan pengunduhan paralel, hanya meng #, fuzzy msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "Input tidak valid! Coba lagi dengan input yang valid [1 untuk {}, atau 0 untuk menonaktifkan]" + +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" diff --git a/archinstall/locales/it/LC_MESSAGES/base.po b/archinstall/locales/it/LC_MESSAGES/base.po index 36dd029d..8f721d0c 100644 --- a/archinstall/locales/it/LC_MESSAGES/base.po +++ b/archinstall/locales/it/LC_MESSAGES/base.po @@ -1244,3 +1244,12 @@ msgstr " - Disabilita/Predefinito : 0 ( Disabilita il download parallelo, consen #, fuzzy msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "Input non valido! Riprova con un input valido [da 1 a {}, o 0 per disabilitare]." + +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" diff --git a/archinstall/locales/ka/LC_MESSAGES/base.po b/archinstall/locales/ka/LC_MESSAGES/base.po index 4ecb6259..d2bd3f9c 100644 --- a/archinstall/locales/ka/LC_MESSAGES/base.po +++ b/archinstall/locales/ka/LC_MESSAGES/base.po @@ -1243,3 +1243,12 @@ msgstr " - გამორთვა/ნაგულისხმები : 0 ( #, fuzzy msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "შეყვანილი რიცხვი არასწორია! თავიდან სცადეთ [1-დან {}-მდე, ან 0, გასათიშად]" + +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" diff --git a/archinstall/locales/ko/LC_MESSAGES/base.po b/archinstall/locales/ko/LC_MESSAGES/base.po index a36680bd..e7e6ddee 100644 --- a/archinstall/locales/ko/LC_MESSAGES/base.po +++ b/archinstall/locales/ko/LC_MESSAGES/base.po @@ -1245,3 +1245,12 @@ msgstr " - 비활성화/기본 : - ( 병렬 다운로드 비활성화, 한 번 #, fuzzy msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "잘못된 값입니다! 유효한 값으로 다시 시도해주세요 [1 부터 {} 까지, 비활성화 하려면 0]" + +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" diff --git a/archinstall/locales/nl/LC_MESSAGES/base.po b/archinstall/locales/nl/LC_MESSAGES/base.po index 216f0912..5b38511e 100644 --- a/archinstall/locales/nl/LC_MESSAGES/base.po +++ b/archinstall/locales/nl/LC_MESSAGES/base.po @@ -1261,6 +1261,15 @@ msgstr "" msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "" +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" + #~ msgid "Add :" #~ msgstr "Toevoegen:" diff --git a/archinstall/locales/pl/LC_MESSAGES/base.po b/archinstall/locales/pl/LC_MESSAGES/base.po index 0837c8e8..b1971216 100644 --- a/archinstall/locales/pl/LC_MESSAGES/base.po +++ b/archinstall/locales/pl/LC_MESSAGES/base.po @@ -1242,6 +1242,15 @@ msgstr " - Wyłącz/Domyślne : 0 ( Wyłącza pobieranie wielu plików jedno msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "Nieprawidłowa wartość! Spróbuj wprowadzić wartość od 1 do {}, lub 0 aby wyłączyć." +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" + #~ msgid "When picking a directory to save configuration files to, by default we will ignore the following folders: " #~ msgstr "Podczas wybierania katalogu do zapisywania plików konfiguracyjnych, domyślnie ignorowane są następujące foldery: " diff --git a/archinstall/locales/pt/LC_MESSAGES/base.po b/archinstall/locales/pt/LC_MESSAGES/base.po index 8f3a28d4..54fd3cb8 100644 --- a/archinstall/locales/pt/LC_MESSAGES/base.po +++ b/archinstall/locales/pt/LC_MESSAGES/base.po @@ -1198,6 +1198,20 @@ msgstr " - Desativar/Predefinido : 0 ( Desativa as transferências paralelas, pe msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "Entrada inválida! Tente novamente com uma entrada válida [ou 0 para desativar]" +#, fuzzy +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "O Sway precisa de acesso ao seu \"seat\" (conjunto de dispositivos de hardware, como o teclado, o rato, etc)" + +#, fuzzy +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" +"\n" +"\n" +"Selecionar uma opção para permitir o acesso do Sway ao seu hardware" + #~ msgid "Add :" #~ msgstr "Adicionar :" diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.po b/archinstall/locales/pt_BR/LC_MESSAGES/base.po index cff6f19a..164cc35f 100644 --- a/archinstall/locales/pt_BR/LC_MESSAGES/base.po +++ b/archinstall/locales/pt_BR/LC_MESSAGES/base.po @@ -1214,5 +1214,19 @@ msgstr " - Desativar/Padrão : 0 ( Desativa os downloads paralelos, permite apen msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "Entrada inválida! Tente novamente com uma entrada válida [1 para {}, ou 0 para desativar]" +#, fuzzy +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "O Sway precisa de acesso ao seu seat (conjunto de dispositivos de hardware, como teclado, mouse, etc)" + +#, fuzzy +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" +"\n" +"\n" +"Selecione uma opção para permitir o acesso do Sway ao seu hardware" + #~ msgid "When picking a directory to save configuration files to, by default we will ignore the following folders: " #~ msgstr "Ao selecionar um diretório para salvar arquivos de configuração, por padrão nós ignoramos as seguintes pastas: " diff --git a/archinstall/locales/ro/LC_MESSAGES/base.po b/archinstall/locales/ro/LC_MESSAGES/base.po index afc892c2..98384c4f 100644 --- a/archinstall/locales/ro/LC_MESSAGES/base.po +++ b/archinstall/locales/ro/LC_MESSAGES/base.po @@ -1192,3 +1192,17 @@ msgstr " - Dezactivează/Predefinit: 0 (Dezactivează descărcările simultane, msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "Input invalid! Încercați din nou cu un input valid [0 pentru dezactivare]" + +#, fuzzy +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "Sway are nevoie de acces la setup-ul dumneavoastră (colecție de dispozitive hardware ex. tastatura, mouse, etc.)" + +#, fuzzy +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" +"\n" +"\n" +"Alegeți o opțiune pentru a acorda acces lui Sway la hardware" diff --git a/archinstall/locales/ru/LC_MESSAGES/base.po b/archinstall/locales/ru/LC_MESSAGES/base.po index ccb27344..58a2488f 100644 --- a/archinstall/locales/ru/LC_MESSAGES/base.po +++ b/archinstall/locales/ru/LC_MESSAGES/base.po @@ -1201,3 +1201,17 @@ msgstr " - Отключить/по умолчанию: 0 ( Отключает п #, fuzzy msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "Неверный ввод! Повторите попытку с правильным вводом [1 - {}, или 0 - отключить]" + +#, fuzzy +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "Sway необходим доступ к вашему компьютеру (набор аппаратных устройств, т.е. клавиатура, мышь и т.д.)" + +#, fuzzy +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" +"\n" +"\n" +"Выберите опцию, чтобы предоставить Sway доступ к вашему оборудованию" diff --git a/archinstall/locales/sv/LC_MESSAGES/base.po b/archinstall/locales/sv/LC_MESSAGES/base.po index 69981cb0..80bd99ba 100644 --- a/archinstall/locales/sv/LC_MESSAGES/base.po +++ b/archinstall/locales/sv/LC_MESSAGES/base.po @@ -1244,3 +1244,12 @@ msgstr "" msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "" + +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" diff --git a/archinstall/locales/ta/LC_MESSAGES/base.mo b/archinstall/locales/ta/LC_MESSAGES/base.mo index 1f4e6155..c6cade73 100644 Binary files a/archinstall/locales/ta/LC_MESSAGES/base.mo and b/archinstall/locales/ta/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ta/LC_MESSAGES/base.po b/archinstall/locales/ta/LC_MESSAGES/base.po index 3935f3aa..e408bf2d 100644 --- a/archinstall/locales/ta/LC_MESSAGES/base.po +++ b/archinstall/locales/ta/LC_MESSAGES/base.po @@ -9,7 +9,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.3.1\n" +"X-Generator: Poedit 3.3.2\n" msgid "[!] A log file has been created here: {} {}" msgstr "[!] ஒரு பதிவு கோப்பு இங்கே உருவாக்கப்பட்டது: {} {}" @@ -1108,7 +1108,7 @@ msgid "" "Save directory: " msgstr "" "உள்ளமைவு(களை) சேமிக்கப்படுவதற்கான கோப்பகத்தை உள்ளிடவும் (தாவல் நிறைவு இயக்கப்பட்டது)\n" -"கோப்பகத்தை சேமி:" +"கோப்பகத்தை சேமி: " msgid "" "Do you want to save {} configuration file(s) in the following location?\n" @@ -1129,7 +1129,7 @@ msgid "Mirror regions" msgstr "கண்ணாடிப் பகுதிகள்" msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr "- அதிகபட்ச மதிப்பு: {} ( {} இணையான பதிவிறக்கங்களை அனுமதிக்கிறது, ஒரே நேரத்தில் {max download+1} பதிவிறக்க அனுமதிக்கிறது )" +msgstr " - அதிகபட்ச மதிப்பு : {} ( {} இணையான பதிவிறக்கங்களை அனுமதிக்கிறது, ஒரே நேரத்தில் {max_downloads+1} பதிவிறக்கங்களை அனுமதிக்கிறது )" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "தவறான உள்ளீடு! சரியான உள்ளீட்டில் [1 முதல் {} வரை அல்லது முடக்க 0 வரை] மீண்டும் முயற்சிக்கவும்" @@ -1140,65 +1140,71 @@ msgstr "மொழி குறியீடுகள்" msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "NetworkManager ஐப் பயன்படுத்தவும் (GNOME மற்றும் KDE இல் இணையத்தை வரைகலை முறையில் கட்டமைக்க அவசியம்)" -#, fuzzy msgid "Total: {} / {}" -msgstr "முழு நீளம்: {}" +msgstr "மொத்தம்: {} / {}" msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." -msgstr "" +msgstr "உள்ளிடப்பட்ட அனைத்து மதிப்புகளையும் ஒரு அலகுடன் பின்னொட்டு இடலாம்: B, KB, KiB, MB, MiB..." msgid "If no unit is provided, the value is interpreted as sectors" -msgstr "" +msgstr "அலகு வழங்கப்படவில்லை எனில், மதிப்பு பிரிவுகளாக விளக்கப்படும்" -#, fuzzy msgid "Enter start (default: sector {}): " -msgstr "தொடக்கப் பிரிவை உள்ளிடவும் (இயல்புநிலை: {}): " +msgstr "தொடக்கத்தை உள்ளிடவும் (இயல்பு: sector {}): " -#, fuzzy msgid "Enter end (default: {}): " -msgstr "தொடக்கப் பிரிவை உள்ளிடவும் (இயல்புநிலை: {}): " +msgstr "முடிவை உள்ளிடவும் (இயல்பு: {}): " msgid "Unable to determine fido2 devices. Is libfido2 installed?" -msgstr "" +msgstr "தீர்மானிக்க முடியவில்லை fido2 சாதனங்களை. libfido2 நிறுவப்பட்டுள்ளதா?" msgid "Path" -msgstr "" +msgstr "பாதை" msgid "Manufacturer" -msgstr "" +msgstr "உற்பத்தியாளர்" msgid "Product" -msgstr "" +msgstr "தயாரிப்பு" -#, fuzzy, python-brace-format +#, python-brace-format msgid "Invalid configuration: {error}" -msgstr "கைமுறை கட்டமைப்பு" +msgstr "தவறான கட்டமைப்பு: {error}" msgid "Type" -msgstr "" +msgstr "வகை" -#, fuzzy msgid "This option enables the number of parallel downloads that can occur during package downloads" -msgstr "இந்த விருப்பம் நிறுவலின் போது நிகழக்கூடிய இணையான பதிவிறக்கங்களின் எண்ணிக்கையை செயல்படுத்துகிறது" +msgstr "தொகுப்பு பதிவிறக்கங்களின் போது ஏற்படும் இணையான பதிவிறக்கங்களின் எண்ணிக்கையை இந்த விருப்பம் செயல்படுத்துகிறது" -#, fuzzy msgid "" "Enter the number of parallel downloads to be enabled.\n" "\n" "Note:\n" msgstr "" "இயக்கப்பட வேண்டிய இணையான பதிவிறக்கங்களின் எண்ணிக்கையை உள்ளிடவும்.\n" -" (1 முதல் {} வரையிலான மதிப்பை உள்ளிடவும்)\n" -"குறிப்பு:" +"\n" +"குறிப்பு:\n" -#, fuzzy msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" -msgstr " - அதிகபட்ச மதிப்பு : {} ( {} இணையான பதிவிறக்கங்களை அனுமதிக்கிறது, ஒரே நேரத்தில் {} பதிவிறக்கங்களை அனுமதிக்கிறது )" +msgstr " - பரிந்துரைக்கப்பட்ட அதிகபட்ச மதிப்பு: {} (ஒரு நேரத்தில் {} இணையான பதிவிறக்கங்களை அனுமதிக்கிறது)" -#, fuzzy msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" -msgstr " - முடக்கு/இயல்புநிலை: 0 (இணை பதிவிறக்கத்தை முடக்குகிறது, ஒரு நேரத்தில் 1 பதிவிறக்கத்தை மட்டுமே அனுமதிக்கிறது )" +msgstr " - முடக்கு/இயல்புநிலை: 0 (இணை பதிவிறக்கத்தை முடக்குகிறது, ஒரு நேரத்தில் 1 பதிவிறக்கத்தை மட்டுமே அனுமதிக்கிறது)\n" -#, fuzzy msgid "Invalid input! Try again with a valid input [or 0 to disable]" -msgstr "தவறான உள்ளீடு! சரியான உள்ளீட்டில் [1 முதல் {} வரை அல்லது முடக்க 0 வரை] மீண்டும் முயற்சிக்கவும்" +msgstr "தவறான உள்ளீடு! சரியான உள்ளீட்டுடன் மீண்டும் முயற்சிக்கவும் [அல்லது முடக்க 0]" + +#, fuzzy +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "ஸ்வேக்கு உங்கள் இருக்கைக்கான அணுகல் தேவை (வன்பொருள் சாதனங்களின் சேகரிப்பு அதாவது விசைப்பலகை, சுட்டி போன்றவை)" + +#, fuzzy +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" +"\n" +"\n" +"உங்கள் வன்பொருளுக்கான அணுகலை வழங்குவதற்கான விருப்பத்தைத் தேர்வுசெய்யவும்" diff --git a/archinstall/locales/tr/LC_MESSAGES/base.po b/archinstall/locales/tr/LC_MESSAGES/base.po index 7360bbb8..7e9dc95d 100644 --- a/archinstall/locales/tr/LC_MESSAGES/base.po +++ b/archinstall/locales/tr/LC_MESSAGES/base.po @@ -1262,3 +1262,17 @@ msgstr "- Devre Dışı Bırak/Varsayılan : 0 (Paralel indirmeyi devre dışı #, fuzzy msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "Geçersiz girdi! Geçerli bir girdiyle tekrar deneyin [1 ila {} veya devre dışı bırakmak için 0]" + +#, fuzzy +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "Sway'in seat'e erişmesi gerekir (klavye, fare vb. donanım aygıtlarının birleşimi)" + +#, fuzzy +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" +"\n" +"\n" +"Sway'in donanımınıza erişmesine izin vermek için bir seçenek belirleyin" diff --git a/archinstall/locales/uk/LC_MESSAGES/base.po b/archinstall/locales/uk/LC_MESSAGES/base.po index 97e3b027..0573cf51 100644 --- a/archinstall/locales/uk/LC_MESSAGES/base.po +++ b/archinstall/locales/uk/LC_MESSAGES/base.po @@ -1242,3 +1242,12 @@ msgstr " - Вимкнути/Типово : 0 ( Вимикає паралельн #, fuzzy msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "Некоректне введення! Повторіть спробу з валідним введенням [від 1 до {} або 0 для вимкнення]" + +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" diff --git a/archinstall/locales/ur/LC_MESSAGES/base.po b/archinstall/locales/ur/LC_MESSAGES/base.po index 8e4b44bb..1cbbefed 100644 --- a/archinstall/locales/ur/LC_MESSAGES/base.po +++ b/archinstall/locales/ur/LC_MESSAGES/base.po @@ -1264,6 +1264,15 @@ msgstr "" msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "" +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" + #~ msgid "Add :" #~ msgstr "شامل:" diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.po b/archinstall/locales/zh-CN/LC_MESSAGES/base.po index b69fdb1f..c92f5e6b 100644 --- a/archinstall/locales/zh-CN/LC_MESSAGES/base.po +++ b/archinstall/locales/zh-CN/LC_MESSAGES/base.po @@ -1199,3 +1199,17 @@ msgstr " - 禁用/默认值:0(禁用并行下载,同时只允许 1 个下 #, fuzzy msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "输入无效! 使用有效输入重试 [1 到 {},或 0 到禁用]" + +#, fuzzy +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "Sway 需要访问您的用户环境(硬件设备的集合,例如键盘,鼠标等)" + +#, fuzzy +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" +"\n" +"\n" +"选择一个选项来给 Sway 提供对您硬件的访问权限" diff --git a/archinstall/locales/zh-TW/LC_MESSAGES/base.po b/archinstall/locales/zh-TW/LC_MESSAGES/base.po index 592dbdae..9e98f3d9 100644 --- a/archinstall/locales/zh-TW/LC_MESSAGES/base.po +++ b/archinstall/locales/zh-TW/LC_MESSAGES/base.po @@ -1209,3 +1209,17 @@ msgstr " - 禁用/默認:0(禁用並行下載,同時只允許 1 个下載 #, fuzzy msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "輸入無效! 請嘗試使用有效輸入重試 [1 到 {max_downloads},或 0 到禁用]" + +#, fuzzy +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "Sway 需要訪問您的用戶環境(硬體設備的集合,例如鍵盤,滑鼠等)" + +#, fuzzy +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" +"\n" +"\n" +"選擇一個選項以提供 Sway 對您硬件的訪問權限" -- cgit v1.2.3-70-g09d2 From 877e34de6353691fb9707828a5880c27655f1d1b Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Sun, 24 Sep 2023 17:36:32 +1000 Subject: Fix line break (#2101) Co-authored-by: Daniel Girtler --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 2420673f..84520d6b 100644 --- a/README.md +++ b/README.md @@ -200,9 +200,7 @@ This can be done by installing `pacman -S arch-install-scripts util-linux` local # losetup -a | grep "testimage.img" | awk -F ":" '{print $1}' # pip install --upgrade archinstall # python -m archinstall --script guided - # qemu-system-x86_64 -enable-kvm -machine q35,accel=kvm -device intel-iommu -cpu host -m 4096 -boot order=d -drive -file=./testimage.img,format=raw -drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF_CODE.fd --drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF_VARS.fd + # qemu-system-x86_64 -enable-kvm -machine q35,accel=kvm -device intel-iommu -cpu host -m 4096 -boot order=d -drive file=./testimage.img,format=raw -drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF_CODE.fd -drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF_VARS.fd This will create a *20 GB* `testimage.img` and create a loop device which we can use to format and install to.
`archinstall` is installed and executed in [guided mode](#docs-todo). Once the installation is complete, ~~you can use qemu/kvm to boot the test media.~~
-- cgit v1.2.3-70-g09d2 From 360a1b4f337e45b2dc26c9af067e53fbd364231f Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Sun, 24 Sep 2023 18:14:02 +1000 Subject: Unify json functions (#2102) Co-authored-by: Daniel Girtler --- archinstall/lib/disk/device_model.py | 34 +++++++++++++------------ archinstall/lib/general.py | 8 +++--- archinstall/lib/models/audio_configuration.py | 2 +- archinstall/lib/models/bootloader.py | 8 +++--- archinstall/lib/models/network_configuration.py | 6 ++--- archinstall/lib/output.py | 2 -- 6 files changed, 29 insertions(+), 31 deletions(-) diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index b520ad1b..8bc41e0c 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -49,10 +49,10 @@ class DiskLayoutConfiguration: if self.config_type == DiskLayoutType.Pre_mount and self.relative_mountpoint is None: raise ValueError('Must set a relative mountpoint when layout type is pre-mount"') - def __dump__(self) -> Dict[str, Any]: + def json(self) -> Dict[str, Any]: return { 'config_type': self.config_type.value, - 'device_modifications': [mod.__dump__() for mod in self.device_modifications] + 'device_modifications': [mod.json() for mod in self.device_modifications] } @classmethod @@ -171,12 +171,12 @@ class Size: raise ValueError('Percent unit size must specify a total size') return self.total_size # type: ignore - def __dump__(self) -> Dict[str, Any]: + def json(self) -> Dict[str, Any]: return { 'value': self.value, 'unit': self.unit.name, - 'sector_size': self.sector_size.__dump__() if self.sector_size else None, - 'total_size': self._total_size.__dump__() if self._total_size else None + 'sector_size': self.sector_size.json() if self.sector_size else None, + 'total_size': self._total_size.json() if self._total_size else None } @classmethod @@ -457,7 +457,7 @@ class SubvolumeModification: return self.mountpoint == Path('/') return False - def __dump__(self) -> Dict[str, Any]: + def json(self) -> Dict[str, Any]: return { 'name': str(self.name), 'mountpoint': str(self.mountpoint), @@ -466,12 +466,7 @@ class SubvolumeModification: } def table_data(self) -> Dict[str, Any]: - return { - 'name': str(self.name), - 'mountpoint': str(self.mountpoint), - 'compress': self.compress, - 'nodatacow': self.nodatacow - } + return self.json() class DeviceGeometry: @@ -755,14 +750,14 @@ class PartitionModification: 'obj_id': self.obj_id, 'status': self.status.value, 'type': self.type.value, - 'start': self.start.__dump__(), - 'length': self.length.__dump__(), + 'start': self.start.json(), + 'length': self.length.json(), 'fs_type': self.fs_type.value if self.fs_type else '', 'mountpoint': str(self.mountpoint) if self.mountpoint else None, 'mount_options': self.mount_options, 'flags': [f.name for f in self.flags], 'dev_path': str(self.dev_path) if self.dev_path else None, - 'btrfs': [vol.__dump__() for vol in self.btrfs_subvols] + 'btrfs': [vol.json() for vol in self.btrfs_subvols] } def table_data(self) -> Dict[str, Any]: @@ -830,7 +825,7 @@ class DeviceModification: filtered = filter(lambda x: x.is_root(relative_path), self.partitions) return next(filtered, None) - def __dump__(self) -> Dict[str, Any]: + def json(self) -> Dict[str, Any]: """ Called when generating configuration files """ @@ -922,6 +917,13 @@ class Fido2Device: 'product': self.product } + def table_data(self) -> Dict[str, str]: + return { + 'Path': str(self.path), + 'Manufacturer': self.manufacturer, + 'Product': self.product + } + @classmethod def parse_arg(cls, arg: Dict[str, str]) -> 'Fido2Device': return Fido2Device( diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py index e22e7eed..71981fb6 100644 --- a/archinstall/lib/general.py +++ b/archinstall/lib/general.py @@ -69,8 +69,6 @@ def jsonify(obj: Any, safe: bool = True) -> Any: # a dictionary representation of the object so that it can be # processed by the json library. return jsonify(obj.json(), safe) - if hasattr(obj, '__dump__'): - return obj.__dump__() if isinstance(obj, (datetime, date)): return obj.isoformat() if isinstance(obj, (list, set, tuple)): @@ -462,13 +460,13 @@ def run_custom_user_commands(commands :List[str], installation :Installer) -> No for index, command in enumerate(commands): script_path = f"/var/tmp/user-command.{index}.sh" chroot_path = f"{installation.target}/{script_path}" - + info(f'Executing custom command "{command}" ...') with open(chroot_path, "w") as user_script: user_script.write(command) - + SysCommand(f"arch-chroot {installation.target} bash {script_path}") - + os.unlink(chroot_path) diff --git a/archinstall/lib/models/audio_configuration.py b/archinstall/lib/models/audio_configuration.py index 3a4029db..88cd5d8e 100644 --- a/archinstall/lib/models/audio_configuration.py +++ b/archinstall/lib/models/audio_configuration.py @@ -24,7 +24,7 @@ class Audio(Enum): class AudioConfiguration: audio: Audio - def __dump__(self) -> Dict[str, Any]: + def json(self) -> Dict[str, Any]: return { 'audio': self.audio.value } diff --git a/archinstall/lib/models/bootloader.py b/archinstall/lib/models/bootloader.py index be9812a0..fa3f32c8 100644 --- a/archinstall/lib/models/bootloader.py +++ b/archinstall/lib/models/bootloader.py @@ -14,12 +14,12 @@ class Bootloader(Enum): Efistub = 'Efistub' Limine = 'Limine' - def json(self): + def json(self) -> str: return self.value - @classmethod - def values(cls) -> List[str]: - return [e.value for e in cls] + @staticmethod + def values() -> List[str]: + return [e.value for e in Bootloader] @classmethod def get_default(cls) -> Bootloader: diff --git a/archinstall/lib/models/network_configuration.py b/archinstall/lib/models/network_configuration.py index fac7bbef..1777df62 100644 --- a/archinstall/lib/models/network_configuration.py +++ b/archinstall/lib/models/network_configuration.py @@ -42,7 +42,7 @@ class Nic: 'dns': self.dns } - def __dump__(self) -> Dict[str, Any]: + def json(self) -> Dict[str, Any]: return { 'iface': self.iface, 'ip': self.ip, @@ -94,10 +94,10 @@ class NetworkConfiguration: type: NicType nics: List[Nic] = field(default_factory=list) - def __dump__(self) -> Dict[str, Any]: + def json(self) -> Dict[str, Any]: config: Dict[str, Any] = {'type': self.type.value} if self.nics: - config['nics'] = [n.__dump__() for n in self.nics] + config['nics'] = [n.json() for n in self.nics] return config diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py index 62a1ba27..945a6c4f 100644 --- a/archinstall/lib/output.py +++ b/archinstall/lib/output.py @@ -38,8 +38,6 @@ class FormattedOutput: raise ValueError('Unsupported formatting call') elif hasattr(o, 'table_data'): return o.table_data() - elif hasattr(o, 'json'): - return o.json() elif is_dataclass(o): return asdict(o) else: -- cgit v1.2.3-70-g09d2 From 9e3e4a5df5b6ff106bcbf30273cdb0de9af7e02e Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Sun, 24 Sep 2023 05:18:52 -0400 Subject: Refactor microcode (#2099) --- archinstall/lib/hardware.py | 32 ++++++++++++++++++++++++++-- archinstall/lib/installer.py | 50 +++++++++++++++++--------------------------- 2 files changed, 49 insertions(+), 33 deletions(-) diff --git a/archinstall/lib/hardware.py b/archinstall/lib/hardware.py index 85f903e1..56d3bc7b 100644 --- a/archinstall/lib/hardware.py +++ b/archinstall/lib/hardware.py @@ -10,6 +10,32 @@ from .networking import list_interfaces, enrich_iface_types from .output import debug +class CpuVendor(Enum): + AuthenticAMD = 'amd' + GenuineIntel = 'intel' + _Unknown = 'unknown' + + @classmethod + def get_vendor(cls, name: str) -> 'CpuVendor': + if vendor := getattr(cls, name, None): + return vendor + else: + debug(f"Unknown CPU vendor '{name}' detected.") + return cls._Unknown + + def _has_microcode(self) -> bool: + match self: + case CpuVendor.AuthenticAMD | CpuVendor.GenuineIntel: + return True + case _: + return False + + def get_ucode(self) -> Optional[Path]: + if self._has_microcode(): + return Path(self.value + '-ucode.img') + return None + + class GfxPackage(Enum): IntelMediaDriver = 'intel-media-driver' LibvaIntelDriver = 'libva-intel-driver' @@ -180,8 +206,10 @@ class SysInfo: return any('intel' in x.lower() for x in SysInfo._graphics_devices()) @staticmethod - def cpu_vendor() -> Optional[str]: - return _sys_info.cpu_info.get('vendor_id', None) + def cpu_vendor() -> Optional[CpuVendor]: + if vendor := _sys_info.cpu_info.get('vendor_id'): + return CpuVendor.get_vendor(vendor) + return None @staticmethod def cpu_model() -> Optional[str]: diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index afc1184b..05eb5867 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -574,6 +574,12 @@ class Installer: log(error.worker._trace_log.decode()) return False + def _get_microcode(self) -> Optional[Path]: + if not SysInfo.is_vm(): + if vendor := SysInfo.cpu_vendor(): + return vendor.get_ucode() + return None + def minimal_installation( self, testing: bool = False, @@ -610,18 +616,11 @@ class Installer: if not SysInfo.has_uefi(): self.base_packages.append('grub') - if not SysInfo.is_vm(): - vendor = SysInfo.cpu_vendor() - if vendor == "AuthenticAMD": - self.base_packages.append("amd-ucode") - if (ucode := Path(f"{self.target}/boot/amd-ucode.img")).exists(): - ucode.unlink() - elif vendor == "GenuineIntel": - self.base_packages.append("intel-ucode") - if (ucode := Path(f"{self.target}/boot/intel-ucode.img")).exists(): - ucode.unlink() - else: - debug(f"Unknown CPU vendor '{vendor}' detected. Archinstall won't install any ucode") + if ucode := self._get_microcode(): + (self.target / 'boot' / ucode).unlink(missing_ok=True) + self.base_packages.append(ucode.stem) + else: + debug('Archinstall will not install any ucode.') # Determine whether to enable multilib/testing repositories before running pacstrap if testing flag is set. # This action takes place on the host system as pacstrap copies over package repository lists. @@ -840,17 +839,10 @@ class Installer: microcode = [] - if not SysInfo.is_vm(): - vendor = SysInfo.cpu_vendor() - if vendor == "AuthenticAMD": - microcode.append('initrd /amd-ucode.img\n') - elif vendor == "GenuineIntel": - microcode.append('initrd /intel-ucode.img\n') - else: - debug( - f"Unknown CPU vendor '{vendor}' detected.", - "Archinstall won't add any ucode to systemd-boot config.", - ) + if ucode := self._get_microcode(): + microcode.append(f'initrd /{ucode}\n') + else: + debug('Archinstall will not add any ucode to systemd-boot config.') options = 'options ' + ' '.join(self._get_kernel_params(root_partition)) + '\n' @@ -1065,14 +1057,10 @@ TIMEOUT=5 microcode = [] - if not SysInfo.is_vm(): - vendor = SysInfo.cpu_vendor() - if vendor == "AuthenticAMD": - microcode.append("initrd=\\amd-ucode.img") - elif vendor == "GenuineIntel": - microcode.append("initrd=\\intel-ucode.img") - else: - debug(f"Unknown CPU vendor '{vendor}' detected. Archinstall won't add any ucode to firmware boot entry.") + if ucode := self._get_microcode(): + microcode.append(f'initrd=\\{ucode}') + else: + debug('Archinstall will not add any ucode to firmware boot entry.') kernel_parameters = self._get_kernel_params(root_partition) -- cgit v1.2.3-70-g09d2 From b141609990fa4f7305443ee6ea6fe8796604c539 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Sun, 24 Sep 2023 19:47:38 +1000 Subject: Fix 1669 | Refactor display of sizes in tables (#2100) * Use sector as default display * Display tables in sector size * Refactor size * Update * Update * fix flake8 --------- Co-authored-by: Daniel Girtler --- archinstall/lib/disk/__init__.py | 1 + archinstall/lib/disk/device_model.py | 169 +++++++++++++++++++----------- archinstall/lib/disk/partitioning_menu.py | 39 ++++--- archinstall/lib/installer.py | 2 +- archinstall/lib/interactions/disk_conf.py | 55 ++++++---- examples/config-sample.json | 17 +-- examples/full_automated_installation.py | 15 +-- 7 files changed, 178 insertions(+), 120 deletions(-) diff --git a/archinstall/lib/disk/__init__.py b/archinstall/lib/disk/__init__.py index cdc96373..24dafef5 100644 --- a/archinstall/lib/disk/__init__.py +++ b/archinstall/lib/disk/__init__.py @@ -14,6 +14,7 @@ from .device_model import ( PartitionTable, Unit, Size, + SectorSize, SubvolumeModification, DeviceGeometry, PartitionType, diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index 8bc41e0c..08861a63 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -93,7 +93,7 @@ class DiskLayoutConfiguration: status=ModificationStatus(partition['status']), fs_type=FilesystemType(partition['fs_type']), start=Size.parse_args(partition['start']), - length=Size.parse_args(partition['length']), + length=Size.parse_args(partition['size']), mount_options=partition['mount_options'], mountpoint=Path(partition['mountpoint']) if partition['mountpoint'] else None, dev_path=Path(partition['dev_path']) if partition['dev_path'] else None, @@ -138,80 +138,89 @@ class Unit(Enum): sectors = 'sectors' # size in sector - Percent = '%' # size in percentile - @staticmethod def get_all_units() -> List[str]: return [u.name for u in Unit] + @staticmethod + def get_si_units() -> List[Unit]: + return [u for u in Unit if 'i' not in u.name and u.name != 'sectors'] + @dataclass -class Size: +class SectorSize: value: int unit: Unit - sector_size: Optional[Size] = None # only required when unit is sector - total_size: Optional[Size] = None # required when operating on percentages def __post_init__(self): - if self.unit == Unit.sectors and self.sector_size is None: - raise ValueError('Sector size is required when unit is sectors') - elif self.unit == Unit.Percent: - if self.value < 0 or self.value > 100: - raise ValueError('Percentage must be between 0 and 100') - elif self.total_size is None: - raise ValueError('Total size is required when unit is percentage') + match self.unit: + case Unit.sectors: + raise ValueError('Unit type sector not allowed for SectorSize') - @property - def _total_size(self) -> Size: + @staticmethod + def default() -> SectorSize: + return SectorSize(512, Unit.B) + + def json(self) -> Dict[str, Any]: + return { + 'value': self.value, + 'unit': self.unit.name, + } + + @classmethod + def parse_args(cls, arg: Dict[str, Any]) -> SectorSize: + return SectorSize( + arg['value'], + Unit[arg['unit']] + ) + + def normalize(self) -> int: """ - Save method to get the total size, mainly to satisfy mypy - This shouldn't happen as the Size object fails instantiation on missing total size + will normalize the value of the unit to Byte """ - if self.unit == Unit.Percent and self.total_size is None: - raise ValueError('Percent unit size must specify a total size') - return self.total_size # type: ignore + return int(self.value * self.unit.value) # type: ignore + + +@dataclass +class Size: + value: int + unit: Unit + sector_size: SectorSize + + def __post_init__(self): + if not isinstance(self.sector_size, SectorSize): + raise ValueError('sector size must be of type SectorSize') def json(self) -> Dict[str, Any]: return { 'value': self.value, 'unit': self.unit.name, - 'sector_size': self.sector_size.json() if self.sector_size else None, - 'total_size': self._total_size.json() if self._total_size else None + 'sector_size': self.sector_size.json() if self.sector_size else None } @classmethod def parse_args(cls, size_arg: Dict[str, Any]) -> Size: sector_size = size_arg['sector_size'] - total_size = size_arg['total_size'] return Size( size_arg['value'], Unit[size_arg['unit']], - Size.parse_args(sector_size) if sector_size else None, - Size.parse_args(total_size) if total_size else None + SectorSize.parse_args(sector_size), ) def convert( self, target_unit: Unit, - sector_size: Optional[Size] = None, - total_size: Optional[Size] = None + sector_size: Optional[SectorSize] = None ) -> Size: if target_unit == Unit.sectors and sector_size is None: raise ValueError('If target has unit sector, a sector size must be provided') - # not sure why we would ever wanna convert to percentages - if target_unit == Unit.Percent and total_size is None: - raise ValueError('Missing parameter total size to be able to convert to percentage') - if self.unit == target_unit: return self - elif self.unit == Unit.Percent: - amount = int(self._total_size._normalize() * (self.value / 100)) - return Size(amount, Unit.B) elif self.unit == Unit.sectors: norm = self._normalize() - return Size(norm, Unit.B).convert(target_unit, sector_size) + return Size(norm, Unit.B, self.sector_size).convert(target_unit, sector_size) else: if target_unit == Unit.sectors and sector_size is not None: norm = self._normalize() @@ -219,7 +228,7 @@ class Size: return Size(sectors, Unit.sectors, sector_size) else: value = int(self._normalize() / target_unit.value) # type: ignore - return Size(value, target_unit) + return Size(value, target_unit, self.sector_size) def as_text(self) -> str: return self.format_size( @@ -230,31 +239,45 @@ class Size: def format_size( self, target_unit: Unit, - sector_size: Optional[Size] = None, + sector_size: Optional[SectorSize] = None, include_unit: bool = True ) -> str: - if self.unit == Unit.Percent: - return f'{self.value}%' - else: - target_size = self.convert(target_unit, sector_size) - if include_unit: - return f'{target_size.value} {target_unit.name}' - return f'{target_size.value}' + target_size = self.convert(target_unit, sector_size) + + if include_unit: + return f'{target_size.value} {target_unit.name}' + return f'{target_size.value}' + + def format_highest(self, include_unit: bool = True) -> str: + si_units = Unit.get_si_units() + all_si_values = [self.convert(si) for si in si_units] + filtered = filter(lambda x: x.value >= 1, all_si_values) + + # we have to get the max by the unit value as we're interested + # in getting the value in the highest possible unit without floats + si_value = max(filtered, key=lambda x: x.unit.value) + + if include_unit: + return f'{si_value.value} {si_value.unit.name}' + return f'{si_value.value}' def _normalize(self) -> int: """ will normalize the value of the unit to Byte """ - if self.unit == Unit.Percent: - return self.convert(Unit.B).value - elif self.unit == Unit.sectors and self.sector_size is not None: - return self.value * self.sector_size._normalize() + if self.unit == Unit.sectors and self.sector_size is not None: + return self.value * self.sector_size.normalize() return int(self.value * self.unit.value) # type: ignore def __sub__(self, other: Size) -> Size: src_norm = self._normalize() dest_norm = other._normalize() - return Size(abs(src_norm - dest_norm), Unit.B) + return Size(abs(src_norm - dest_norm), Unit.B, self.sector_size) + + def __add__(self, other: Size) -> Size: + src_norm = self._normalize() + dest_norm = other._normalize() + return Size(abs(src_norm + dest_norm), Unit.B, self.sector_size) def __lt__(self, other): return self._normalize() < other._normalize() @@ -296,14 +319,22 @@ class _PartitionInfo: mountpoints: List[Path] btrfs_subvol_infos: List[_BtrfsSubvolumeInfo] = field(default_factory=list) + @property + def sector_size(self) -> SectorSize: + sector_size = self.partition.geometry.device.sectorSize + return SectorSize(sector_size, Unit.B) + def table_data(self) -> Dict[str, Any]: + end = self.start + self.length + part_info = { 'Name': self.name, 'Type': self.type.value, 'Filesystem': self.fs_type.value if self.fs_type else str(_('Unknown')), 'Path': str(self.path), - 'Start': self.start.format_size(Unit.MiB), - 'Length': self.length.format_size(Unit.MiB), + 'Start': self.start.format_size(Unit.sectors, self.sector_size, include_unit=False), + 'End': end.format_size(Unit.sectors, self.sector_size, include_unit=False), + 'Size': self.length.format_highest(), 'Flags': ', '.join([f.name for f in self.flags]) } @@ -327,10 +358,14 @@ class _PartitionInfo: start = Size( partition.geometry.start, Unit.sectors, - Size(partition.disk.device.sectorSize, Unit.B) + SectorSize(partition.disk.device.sectorSize, Unit.B) ) - length = Size(int(partition.getLength(unit='B')), Unit.B) + length = Size( + int(partition.getLength(unit='B')), + Unit.B, + SectorSize(partition.disk.device.sectorSize, Unit.B) + ) return _PartitionInfo( partition=partition, @@ -355,7 +390,7 @@ class _DeviceInfo: type: str total_size: Size free_space_regions: List[DeviceGeometry] - sector_size: Size + sector_size: SectorSize read_only: bool dirty: bool @@ -365,7 +400,7 @@ class _DeviceInfo: 'Model': self.model, 'Path': str(self.path), 'Type': self.type, - 'Size': self.total_size.format_size(Unit.MiB), + 'Size': self.total_size.format_highest(), 'Free space': int(total_free_space), 'Sector size': self.sector_size.value, 'Read only': self.read_only @@ -379,15 +414,17 @@ class _DeviceInfo: else: device_type = parted.devices[device.type] - sector_size = Size(device.sectorSize, Unit.B) + sector_size = SectorSize(device.sectorSize, Unit.B) free_space = [DeviceGeometry(g, sector_size) for g in disk.getFreeSpaceRegions()] + sector_size = SectorSize(device.sectorSize, Unit.B) + return _DeviceInfo( model=device.model.strip(), path=Path(device.path), type=device_type, sector_size=sector_size, - total_size=Size(int(device.getLength(unit='B')), Unit.B), + total_size=Size(int(device.getLength(unit='B')), Unit.B, sector_size), free_space_regions=free_space, read_only=device.readOnly, dirty=device.dirty @@ -470,7 +507,7 @@ class SubvolumeModification: class DeviceGeometry: - def __init__(self, geometry: Geometry, sector_size: Size): + def __init__(self, geometry: Geometry, sector_size: SectorSize): self._geometry = geometry self._sector_size = sector_size @@ -498,7 +535,7 @@ class DeviceGeometry: 'Sector size': self._sector_size.value, 'Start (sector/B)': start_str, 'End (sector/B)': end_str, - 'Length (sectors/B)': length_str + 'Size (sectors/B)': length_str } @@ -751,7 +788,7 @@ class PartitionModification: 'status': self.status.value, 'type': self.type.value, 'start': self.start.json(), - 'length': self.length.json(), + 'size': self.length.json(), 'fs_type': self.fs_type.value if self.fs_type else '', 'mountpoint': str(self.mountpoint) if self.mountpoint else None, 'mount_options': self.mount_options, @@ -764,12 +801,15 @@ class PartitionModification: """ Called for displaying data in table format """ + end = self.start + self.length + part_mod = { 'Status': self.status.value, 'Device': str(self.dev_path) if self.dev_path else '', 'Type': self.type.value, - 'Start': self.start.format_size(Unit.MiB), - 'Length': self.length.format_size(Unit.MiB), + 'Start': self.start.format_size(Unit.sectors, self.start.sector_size, include_unit=False), + 'End': end.format_size(Unit.sectors, self.start.sector_size, include_unit=False), + 'Size': self.length.format_highest(), 'FS type': self.fs_type.value if self.fs_type else 'Unknown', 'Mountpoint': self.mountpoint if self.mountpoint else '', 'Mount options': ', '.join(self.mount_options), @@ -938,7 +978,7 @@ class LsblkInfo: name: str = '' path: Path = Path() pkname: str = '' - size: Size = field(default_factory=lambda: Size(0, Unit.B)) + size: Size = field(default_factory=lambda: Size(0, Unit.B, SectorSize.default())) log_sec: int = 0 pttype: str = '' ptuuid: str = '' @@ -1017,7 +1057,8 @@ class LsblkInfo: if isinstance(getattr(lsblk_info, data_field), Path): val = Path(blockdevice[lsblk_field]) elif isinstance(getattr(lsblk_info, data_field), Size): - val = Size(blockdevice[lsblk_field], Unit.B) + sector_size = SectorSize(blockdevice['log-sec'], Unit.B) + val = Size(blockdevice[lsblk_field], Unit.B, sector_size) else: val = blockdevice[lsblk_field] diff --git a/archinstall/lib/disk/partitioning_menu.py b/archinstall/lib/disk/partitioning_menu.py index 549c7f34..c5263b82 100644 --- a/archinstall/lib/disk/partitioning_menu.py +++ b/archinstall/lib/disk/partitioning_menu.py @@ -5,7 +5,7 @@ from pathlib import Path from typing import Any, Dict, TYPE_CHECKING, List, Optional, Tuple from .device_model import PartitionModification, FilesystemType, BDevice, Size, Unit, PartitionType, PartitionFlag, \ - ModificationStatus, DeviceGeometry + ModificationStatus, DeviceGeometry, SectorSize from ..menu import Menu, ListManager, MenuSelection, TextInput from ..output import FormattedOutput, warn from .subvolume_menu import SubvolumeMenu @@ -194,42 +194,47 @@ class PartitioningList(ListManager): def _validate_value( self, - sector_size: Size, + sector_size: SectorSize, total_size: Size, - value: str + text: str, + start: Optional[Size] ) -> Optional[Size]: - match = re.match(r'([0-9]+)([a-zA-Z|%]*)', value, re.I) + match = re.match(r'([0-9]+)([a-zA-Z|%]*)', text, re.I) if match: - value, unit = match.groups() + str_value, unit = match.groups() - if unit == '%': - unit = Unit.Percent.name + if unit == '%' and start: + available = total_size - start + value = int(available.value * (int(str_value) / 100)) + unit = available.unit.name + else: + value = int(str_value) if unit and unit not in Unit.get_all_units(): return None unit = Unit[unit] if unit else Unit.sectors - return Size(int(value), unit, sector_size, total_size) + return Size(value, unit, sector_size) return None def _enter_size( self, - sector_size: Size, + sector_size: SectorSize, total_size: Size, prompt: str, - default: Size + default: Size, + start: Optional[Size], ) -> Size: while True: value = TextInput(prompt).run().strip() - size: Optional[Size] = None if not value: size = default else: - size = self._validate_value(sector_size, total_size, value) + size = self._validate_value(sector_size, total_size, value, start) if size: return size @@ -247,7 +252,7 @@ class PartitioningList(ListManager): total_bytes = device_info.total_size.format_size(Unit.B) prompt += str(_('Total: {} / {}')).format(total_sectors, total_bytes) + '\n\n' - prompt += str(_('All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB...')) + '\n' + prompt += str(_('All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB...')) + '\n' prompt += str(_('If no unit is provided, the value is interpreted as sectors')) + '\n' print(prompt) @@ -260,13 +265,14 @@ class PartitioningList(ListManager): device_info.sector_size, device_info.total_size, start_prompt, - default_start + default_start, + None ) if start_size.value == largest_free_area.start: end_size = Size(largest_free_area.end, Unit.sectors, device_info.sector_size) else: - end_size = Size(100, Unit.Percent, total_size=device_info.total_size) + end_size = device_info.total_size # prompt until valid end sector was entered end_prompt = str(_('Enter end (default: {}): ')).format(end_size.as_text()) @@ -274,7 +280,8 @@ class PartitioningList(ListManager): device_info.sector_size, device_info.total_size, end_prompt, - end_size + end_size, + start_size ) return start_size, end_size diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 05eb5867..a238bb8f 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -163,7 +163,7 @@ class Installer: lsblk_info = disk.get_lsblk_by_mountpoint(boot_mount) if len(lsblk_info) > 0: - if lsblk_info[0].size < disk.Size(200, disk.Unit.MiB): + if lsblk_info[0].size < disk.Size(200, disk.Unit.MiB, disk.SectorSize.default()): raise DiskError( f'The boot partition mounted at {boot_mount} is not large enough to install a boot loader. ' f'Please resize it to at least 200MiB and re-run the installation.' diff --git a/archinstall/lib/interactions/disk_conf.py b/archinstall/lib/interactions/disk_conf.py index 78e4cff4..8542ab75 100644 --- a/archinstall/lib/interactions/disk_conf.py +++ b/archinstall/lib/interactions/disk_conf.py @@ -170,13 +170,13 @@ def select_disk_config( return None -def _boot_partition() -> disk.PartitionModification: +def _boot_partition(sector_size: disk.SectorSize) -> disk.PartitionModification: if SysInfo.has_uefi(): - start = disk.Size(1, disk.Unit.MiB) - size = disk.Size(512, disk.Unit.MiB) + start = disk.Size(1, disk.Unit.MiB, sector_size) + size = disk.Size(512, disk.Unit.MiB, sector_size) else: - start = disk.Size(3, disk.Unit.MiB) - size = disk.Size(203, disk.Unit.MiB) + start = disk.Size(3, disk.Unit.MiB, sector_size) + size = disk.Size(203, disk.Unit.MiB, sector_size) # boot partition return disk.PartitionModification( @@ -215,8 +215,9 @@ def suggest_single_disk_layout( if not filesystem_type: filesystem_type = select_main_filesystem_format(advanced_options) - min_size_to_allow_home_part = disk.Size(40, disk.Unit.GiB) - root_partition_size = disk.Size(20, disk.Unit.GiB) + sector_size = device.device_info.sector_size + min_size_to_allow_home_part = disk.Size(40, disk.Unit.GiB, sector_size) + root_partition_size = disk.Size(20, disk.Unit.GiB, sector_size) using_subvolumes = False using_home_partition = False compression = False @@ -244,7 +245,7 @@ def suggest_single_disk_layout( # Also re-align the start to 1MiB since we don't need the first sectors # like we do in MBR layouts where the boot loader is installed traditionally. - boot_partition = _boot_partition() + boot_partition = _boot_partition(sector_size) device_modification.add_partition(boot_partition) if not using_subvolumes: @@ -259,11 +260,11 @@ def suggest_single_disk_layout( using_home_partition = False # root partition - start = disk.Size(513, disk.Unit.MiB) if SysInfo.has_uefi() else disk.Size(206, disk.Unit.MiB) + start = disk.Size(513, disk.Unit.MiB, sector_size) if SysInfo.has_uefi() else disk.Size(206, disk.Unit.MiB, sector_size) # Set a size for / (/root) if using_subvolumes or device_size_gib < min_size_to_allow_home_part or not using_home_partition: - length = disk.Size(100, disk.Unit.Percent, total_size=device.device_info.total_size) + length = device.device_info.total_size - start else: length = min(device.device_info.total_size, root_partition_size) @@ -294,11 +295,14 @@ def suggest_single_disk_layout( # If we don't want to use subvolumes, # But we want to be able to re-use data between re-installs.. # A second partition for /home would be nice if we have the space for it + start = root_partition.length + length = device.device_info.total_size - root_partition.length + home_partition = disk.PartitionModification( status=disk.ModificationStatus.Create, type=disk.PartitionType.Primary, - start=root_partition.length, - length=disk.Size(100, disk.Unit.Percent, total_size=device.device_info.total_size), + start=start, + length=length, mountpoint=Path('/home'), fs_type=filesystem_type, mount_options=['compress=zstd'] if compression else [] @@ -319,9 +323,9 @@ def suggest_multi_disk_layout( # Not really a rock solid foundation of information to stand on, but it's a start: # https://www.reddit.com/r/btrfs/comments/m287gp/partition_strategy_for_two_physical_disks/ # https://www.reddit.com/r/btrfs/comments/9us4hr/what_is_your_btrfs_partitionsubvolumes_scheme/ - min_home_partition_size = disk.Size(40, disk.Unit.GiB) + min_home_partition_size = disk.Size(40, disk.Unit.GiB, disk.SectorSize.default()) # rough estimate taking in to account user desktops etc. TODO: Catch user packages to detect size? - desired_root_partition_size = disk.Size(20, disk.Unit.GiB) + desired_root_partition_size = disk.Size(20, disk.Unit.GiB, disk.SectorSize.default()) compression = False if not filesystem_type: @@ -362,28 +366,41 @@ def suggest_multi_disk_layout( root_device_modification = disk.DeviceModification(root_device, wipe=True) home_device_modification = disk.DeviceModification(home_device, wipe=True) + root_device_sector_size = root_device_modification.device.device_info.sector_size + home_device_sector_size = home_device_modification.device.device_info.sector_size + # add boot partition to the root device - boot_partition = _boot_partition() + boot_partition = _boot_partition(root_device_sector_size) root_device_modification.add_partition(boot_partition) + if SysInfo.has_uefi(): + root_start = disk.Size(513, disk.Unit.MiB, root_device_sector_size) + else: + root_start = disk.Size(206, disk.Unit.MiB, root_device_sector_size) + + root_length = root_device.device_info.total_size - root_start + # add root partition to the root device root_partition = disk.PartitionModification( status=disk.ModificationStatus.Create, type=disk.PartitionType.Primary, - start=disk.Size(513, disk.Unit.MiB) if SysInfo.has_uefi() else disk.Size(206, disk.Unit.MiB), - length=disk.Size(100, disk.Unit.Percent, total_size=root_device.device_info.total_size), + start=root_start, + length=root_length, mountpoint=Path('/'), mount_options=['compress=zstd'] if compression else [], fs_type=filesystem_type ) root_device_modification.add_partition(root_partition) + start = disk.Size(1, disk.Unit.MiB, home_device_sector_size) + length = home_device.device_info.total_size - start + # add home partition to home device home_partition = disk.PartitionModification( status=disk.ModificationStatus.Create, type=disk.PartitionType.Primary, - start=disk.Size(1, disk.Unit.MiB), - length=disk.Size(100, disk.Unit.Percent, total_size=home_device.device_info.total_size), + start=start, + length=length, mountpoint=Path('/home'), mount_options=['compress=zstd'] if compression else [], fs_type=filesystem_type, diff --git a/examples/config-sample.json b/examples/config-sample.json index ed1cc38e..d43f7ea6 100644 --- a/examples/config-sample.json +++ b/examples/config-sample.json @@ -17,9 +17,8 @@ "Boot" ], "fs_type": "fat32", - "length": { + "size": { "sector_size": null, - "total_size": null, "unit": "MiB", "value": 512 }, @@ -28,7 +27,6 @@ "obj_id": "2c3fa2d5-2c79-4fab-86ec-22d0ea1543c0", "start": { "sector_size": null, - "total_size": null, "unit": "MiB", "value": 1 }, @@ -39,9 +37,8 @@ "btrfs": [], "flags": [], "fs_type": "ext4", - "length": { + "size": { "sector_size": null, - "total_size": null, "unit": "GiB", "value": 20 }, @@ -50,7 +47,6 @@ "obj_id": "3e7018a0-363b-4d05-ab83-8e82d13db208", "start": { "sector_size": null, - "total_size": null, "unit": "MiB", "value": 513 }, @@ -61,14 +57,8 @@ "btrfs": [], "flags": [], "fs_type": "ext4", - "length": { + "size": { "sector_size": null, - "total_size": { - "sector_size": null, - "total_size": null, - "unit": "B", - "value": 250148290560 - }, "unit": "Percent", "value": 100 }, @@ -77,7 +67,6 @@ "obj_id": "ce58b139-f041-4a06-94da-1f8bad775d3f", "start": { "sector_size": null, - "total_size": null, "unit": "GiB", "value": 20 }, diff --git a/examples/full_automated_installation.py b/examples/full_automated_installation.py index 79e85348..d25575d4 100644 --- a/examples/full_automated_installation.py +++ b/examples/full_automated_installation.py @@ -23,8 +23,8 @@ device_modification = disk.DeviceModification(device, wipe=True) boot_partition = disk.PartitionModification( status=disk.ModificationStatus.Create, type=disk.PartitionType.Primary, - start=disk.Size(1, disk.Unit.MiB), - length=disk.Size(512, disk.Unit.MiB), + start=disk.Size(1, disk.Unit.MiB, device.device_info.sector_size), + length=disk.Size(512, disk.Unit.MiB, device.device_info.sector_size), mountpoint=Path('/boot'), fs_type=disk.FilesystemType.Fat32, flags=[disk.PartitionFlag.Boot] @@ -35,20 +35,23 @@ device_modification.add_partition(boot_partition) root_partition = disk.PartitionModification( status=disk.ModificationStatus.Create, type=disk.PartitionType.Primary, - start=disk.Size(513, disk.Unit.MiB), - length=disk.Size(20, disk.Unit.GiB), + start=disk.Size(513, disk.Unit.MiB, device.device_info.sector_size), + length=disk.Size(20, disk.Unit.GiB, device.device_info.sector_size), mountpoint=None, fs_type=fs_type, mount_options=[], ) device_modification.add_partition(root_partition) +start_home = root_partition.length +length_home = device.device_info.total_size - start_home + # create a new home partition home_partition = disk.PartitionModification( status=disk.ModificationStatus.Create, type=disk.PartitionType.Primary, - start=root_partition.length, - length=disk.Size(100, disk.Unit.Percent, total_size=device.device_info.total_size), + start=start_home, + length=length_home, mountpoint=Path('/home'), fs_type=fs_type, mount_options=[] -- cgit v1.2.3-70-g09d2 From c427391543d464856c8153d17ddeef2c693fcf95 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Tue, 26 Sep 2023 09:56:34 +0200 Subject: Disabled fstrim on BTRFS by default (#2109) * Disabled fstrim on BTRFS by default * Changed variable name to conform with suggestion in #2109 --- archinstall/lib/installer.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index a238bb8f..8a0acf64 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -587,6 +587,7 @@ class Installer: hostname: str = 'archinstall', locale_config: LocaleConfiguration = LocaleConfiguration.default() ): + _disable_fstrim = False for mod in self._disk_config.device_modifications: for part in mod.partitions: if part.fs_type is not None: @@ -597,6 +598,10 @@ class Installer: if (binary := part.fs_type.installation_binary) is not None: self._binaries.append(binary) + # https://github.com/archlinux/archinstall/issues/1837 + if part.fs_type.fs_type_mount == 'btrfs': + _disable_fstrim = True + # There is not yet an fsck tool for NTFS. If it's being used for the root filesystem, the hook should be removed. if part.fs_type.fs_type_mount == 'ntfs3' and part.mountpoint == self.target: if 'fsck' in self._hooks: @@ -651,7 +656,10 @@ class Installer: # periodic TRIM by default. # # https://github.com/archlinux/archinstall/issues/880 - self.enable_periodic_trim() + # https://github.com/archlinux/archinstall/issues/1837 + # https://github.com/archlinux/archinstall/issues/1841 + if not _disable_fstrim: + self.enable_periodic_trim() # TODO: Support locale and timezone # os.remove(f'{self.target}/etc/localtime') -- cgit v1.2.3-70-g09d2 From 717a22371fd25aac10d1f435b65842e800fd9d7e Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Tue, 26 Sep 2023 04:57:45 -0400 Subject: Fix `mountpoint` for pre-mounted disk configuration (#2113) * Fix `mountpoint` for pre-mounted disk configuration * Add missing commas --- archinstall/lib/disk/device_handler.py | 4 +++- archinstall/lib/disk/device_model.py | 22 ++++++---------------- archinstall/lib/installer.py | 6 +++--- archinstall/lib/interactions/disk_conf.py | 1 - examples/auto_discovery_mounted.py | 1 - 5 files changed, 12 insertions(+), 22 deletions(-) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index 9646103f..4cb35c03 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -594,7 +594,9 @@ class DeviceHandler(object): if is_subpath(mountpoint, base_mountpoint): path = Path(part_info.disk.device.path) part_mods.setdefault(path, []) - part_mods[path].append(PartitionModification.from_existing_partition(part_info)) + part_mod = PartitionModification.from_existing_partition(part_info) + part_mod.mountpoint = mountpoint.root / mountpoint.relative_to(base_mountpoint) + part_mods[path].append(part_mod) break device_mods: List[DeviceModification] = [] diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index 08861a63..b1f012f7 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -42,12 +42,6 @@ class DiskLayoutType(Enum): class DiskLayoutConfiguration: config_type: DiskLayoutType device_modifications: List[DeviceModification] = field(default_factory=list) - # used for pre-mounted config - relative_mountpoint: Optional[Path] = None - - def __post_init__(self): - if self.config_type == DiskLayoutType.Pre_mount and self.relative_mountpoint is None: - raise ValueError('Must set a relative mountpoint when layout type is pre-mount"') def json(self) -> Dict[str, Any]: return { @@ -487,10 +481,8 @@ class SubvolumeModification: raise ValueError('Mountpoint is not specified') - def is_root(self, relative_mountpoint: Optional[Path] = None) -> bool: + def is_root(self) -> bool: if self.mountpoint: - if relative_mountpoint is not None: - return self.mountpoint.relative_to(relative_mountpoint) == Path('.') return self.mountpoint == Path('/') return False @@ -742,14 +734,12 @@ class PartitionModification: """ return any(set(self.flags) & set(self._boot_indicator_flags)) - def is_root(self, relative_mountpoint: Optional[Path] = None) -> bool: - if relative_mountpoint is not None and self.mountpoint is not None: - return self.mountpoint.relative_to(relative_mountpoint) == Path('.') - elif self.mountpoint is not None: + def is_root(self) -> bool: + if self.mountpoint is not None: return Path('/') == self.mountpoint else: for subvol in self.btrfs_subvols: - if subvol.is_root(relative_mountpoint): + if subvol.is_root(): return True return False @@ -861,8 +851,8 @@ class DeviceModification: filtered = filter(lambda x: x.is_boot() and x.mountpoint, self.partitions) return next(filtered, None) - def get_root_partition(self, relative_path: Optional[Path]) -> Optional[PartitionModification]: - filtered = filter(lambda x: x.is_root(relative_path), self.partitions) + def get_root_partition(self) -> Optional[PartitionModification]: + filtered = filter(lambda x: x.is_root(), self.partitions) return next(filtered, None) def json(self) -> Dict[str, Any]: diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 8a0acf64..f8f59cc0 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -716,7 +716,7 @@ class Installer: def _get_root_partition(self) -> Optional[disk.PartitionModification]: for mod in self._disk_config.device_modifications: - if root := mod.get_root_partition(self._disk_config.relative_mountpoint): + if root := mod.get_root_partition(): return root return None @@ -903,8 +903,8 @@ class Installer: add_options = [ '--target=x86_64-efi', - f'--efi-directory={efi_partition.mountpoint}' - f'--boot-directory={boot_partition.mountpoint if boot_partition else "/boot"}' + f'--efi-directory={efi_partition.mountpoint}', + f'--boot-directory={boot_partition.mountpoint if boot_partition else "/boot"}', '--bootloader-id=GRUB', '--removable' ] diff --git a/archinstall/lib/interactions/disk_conf.py b/archinstall/lib/interactions/disk_conf.py index 8542ab75..253a623d 100644 --- a/archinstall/lib/interactions/disk_conf.py +++ b/archinstall/lib/interactions/disk_conf.py @@ -139,7 +139,6 @@ def select_disk_config( return disk.DiskLayoutConfiguration( config_type=disk.DiskLayoutType.Pre_mount, - relative_mountpoint=path, device_modifications=mods ) diff --git a/examples/auto_discovery_mounted.py b/examples/auto_discovery_mounted.py index 0bd30cd1..e3cb80b7 100644 --- a/examples/auto_discovery_mounted.py +++ b/examples/auto_discovery_mounted.py @@ -9,5 +9,4 @@ mods = disk.device_handler.detect_pre_mounted_mods(root_mount_dir) disk_config = disk.DiskLayoutConfiguration( disk.DiskLayoutType.Pre_mount, device_modifications=mods, - relative_mountpoint=Path('/mnt/archinstall') ) -- cgit v1.2.3-70-g09d2 From bc8d4ea2346b09adc641ae51648ac67df939d27a Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Tue, 26 Sep 2023 18:57:50 +1000 Subject: Fix 2103 (#2114) Co-authored-by: Daniel Girtler --- archinstall/lib/disk/device_handler.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index 4cb35c03..8af33fde 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -49,12 +49,11 @@ class DeviceHandler(object): try: loop_devices = SysCommand(['losetup', '-a']) - except SysCallError as err: - debug(f'Failed to get loop devices: {err}') - else: for ld_info in str(loop_devices).splitlines(): loop_device = getDevice(ld_info.split(':', maxsplit=1)[0]) devices.append(loop_device) + except Exception as err: + debug(f'Failed to get loop devices: {err}') for device in devices: if get_lsblk_info(device.path).type == 'rom': -- cgit v1.2.3-70-g09d2 From a811decea5db5ce7c26e8e42cd49c233fa959fe1 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Wed, 27 Sep 2023 01:39:35 -0400 Subject: Fix mount debug message (#2116) --- archinstall/lib/disk/device_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index 8af33fde..066d3295 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -553,7 +553,7 @@ class DeviceHandler(object): command = f'mount {mount_fs} {str_options} {dev_path} {target_mountpoint}' - debug(f'Mounting {dev_path}: command') + debug(f'Mounting {dev_path}: {command}') try: SysCommand(command) -- cgit v1.2.3-70-g09d2 From 0b4909b49210c2363406327b5b13c6dd779c115c Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Wed, 27 Sep 2023 02:49:20 -0400 Subject: Use unpacking for list creation (#2117) --- archinstall/lib/installer.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index f8f59cc0..5d1bb7dc 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -857,17 +857,17 @@ class Installer: for kernel in self.kernels: for variant in ("", "-fallback"): # Setup the loader entry - with open(entries_dir / f'{self.init_time}_{kernel}{variant}.conf', 'w') as entry: - entry_lines: List[str] = [] - - entry_lines.extend(comments) - entry_lines.append(f'title Arch Linux ({kernel}{variant})\n') - entry_lines.append(f'linux /vmlinuz-{kernel}\n') - entry_lines.extend(microcode) - entry_lines.append(f'initrd /initramfs-{kernel}{variant}.img\n') - entry_lines.append(options) - - entry.writelines(entry_lines) + entry = [ + *comments, + f'title Arch Linux ({kernel}{variant})\n', + f'linux /vmlinuz-{kernel}\n', + *microcode, + f'initrd /initramfs-{kernel}{variant}.img\n', + options, + ] + + entry_conf = entries_dir / f'{self.init_time}_{kernel}{variant}.conf' + entry_conf.write_text(''.join(entry)) self.helper_flags['bootloader'] = 'systemd' @@ -1079,11 +1079,11 @@ TIMEOUT=5 label = f'Arch Linux ({kernel})' loader = f"/vmlinuz-{kernel}" - cmdline = [] - - cmdline.extend(microcode) - cmdline.append(f"initrd=\\initramfs-{kernel}.img") - cmdline.extend(kernel_parameters) + cmdline = [ + *microcode, + f"initrd=\\initramfs-{kernel}.img", + *kernel_parameters, + ] cmd = f'efibootmgr ' \ f'--disk {parent_dev_path} ' \ -- cgit v1.2.3-70-g09d2 From 454b4bf7e9649b8669bc7a68d97a9829afb2bd7a Mon Sep 17 00:00:00 2001 From: Wise Date: Wed, 27 Sep 2023 08:49:55 +0200 Subject: tr translation files updated (#2115) --- archinstall/locales/tr/LC_MESSAGES/base.mo | Bin 31750 -> 40360 bytes archinstall/locales/tr/LC_MESSAGES/base.po | 196 ++++++++++------------------- 2 files changed, 64 insertions(+), 132 deletions(-) diff --git a/archinstall/locales/tr/LC_MESSAGES/base.mo b/archinstall/locales/tr/LC_MESSAGES/base.mo index 83d4a108..a39d5ffb 100644 Binary files a/archinstall/locales/tr/LC_MESSAGES/base.mo and b/archinstall/locales/tr/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/tr/LC_MESSAGES/base.po b/archinstall/locales/tr/LC_MESSAGES/base.po index 7e9dc95d..add12ffd 100644 --- a/archinstall/locales/tr/LC_MESSAGES/base.po +++ b/archinstall/locales/tr/LC_MESSAGES/base.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: 21.05.2022\n" -"PO-Revision-Date: 2022-05-25 17:42+0300\n" +"PO-Revision-Date: 2023-09-26 23:01+0200\n" "Last-Translator: Hasan Oruc \n" "Language-Team: \n" "Language: tr\n" @@ -10,13 +10,13 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 3.0.1\n" +"X-Generator: Poedit 3.3.2\n" msgid "[!] A log file has been created here: {} {}" -msgstr "[!] Burada bir günlük (log) dosyası oluşturuldu: {} {}" +msgstr "[!] Burada bir günlük dosyası oluşturuldu: {} {}" msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" -msgstr " Lütfen bu sorunu (ve dosyayı) https://github.com/archlinux/archinstall/issues 'a bildirin" +msgstr " Lütfen bu sorunu (ve dosyayı) https://github.com/archlinux/archinstall/issues adresine gönderin" msgid "Do you really want to abort?" msgstr "Gerçekten iptal etmek istiyor musunuz?" @@ -28,13 +28,13 @@ msgid "Would you like to use swap on zram?" msgstr "\"Swap\"i (takas) zram üzerinde kullanmak ister misiniz?" msgid "Desired hostname for the installation: " -msgstr "Kurulum için tercih edilen bilgisayar ismi: " +msgstr "Kurulum için istenen ana bilgisayar adı: " msgid "Username for required superuser with sudo privileges: " -msgstr "Sudo yetkilerine sahip, gerekli bir süper kullanıcı için kullanıcı adı: " +msgstr "Sudo ayrıcalıklarına sahip gerekli süper kullanıcı için kullanıcı adı: " msgid "Any additional users to install (leave blank for no users): " -msgstr "Kurulum için ek kullanıcılar (ek kullanıcı istemiyorsanız boş bırakın): " +msgstr "Yüklenecek herhangi bir ek kullanıcı (kullanıcı yok için boş bırakın): " msgid "Should this user be a superuser (sudoer)?" msgstr "Bu kullanıcı bir süper kullanıcı (sudoer) olmalı mı?" @@ -530,7 +530,7 @@ msgid "The password you are using seems to be weak," msgstr "Kullandığınız şifre zayıf görünüyor," msgid "are you sure you want to use it?" -msgstr "Kullanmak istediğinize emin misiniz?" +msgstr "kullanmak istediğinize emin misiniz?" msgid "Optional repositories" msgstr "Tercihi depolar" @@ -665,7 +665,6 @@ msgstr "Arch Linux'u istediğin gibi kişiselleştirmeni sağlayan çok basit bi msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" msgstr "Kurmak ve aktif etmek için bazı seçilmiş çeşitli sunucu paketleri sunar, örnek olarak httpd, nginx, mariadb" -#, fuzzy msgid "Choose which servers to install, if none then a minimal installation will be done" msgstr "Hangi sunucuların kurulacağını seçin, eğer hiçbiri seçilmezse minimal bir kurulum gerçekleştirilecektir" @@ -758,24 +757,20 @@ msgstr "Boş alan" msgid "Bus-type" msgstr "Bus(veri yolu)-tipi" -#, fuzzy msgid "Either root-password or at least 1 user with sudo privileges must be specified" msgstr "Kök şifresi ya da en azından 1 adet süper-kullanıcı belirtilmek zorunda" -#, fuzzy msgid "Enter username (leave blank to skip): " msgstr "Ek kullanıcı oluşturmak için bir kullanıcı adı girin (geçmek için boş bırakın): " msgid "The username you entered is invalid. Try again" msgstr "Girdiğiniz kullanıcı adı geçersiz. Tekrar deneyin" -#, fuzzy msgid "Should \"{}\" be a superuser (sudo)?" msgstr "{} bir süper kullanıcı (sudoer) olmalı mı?" -#, fuzzy msgid "Select which partitions to encrypt" -msgstr "Hangi disk bölümünün şifrelenmiş olarak işaretleneceğini seçin" +msgstr "Hangi disk bölümünün şifrelemek için işaretleneceğini seçin" msgid "very weak" msgstr "çok zayıf" @@ -789,14 +784,12 @@ msgstr "ortalama" msgid "strong" msgstr "güçlü" -#, fuzzy msgid "Add subvolume" -msgstr "alt disk bölümü ekle :{:16}" +msgstr "Alt disk bölümü ekle" msgid "Edit subvolume" msgstr "Alt disk bölümünü düzenle" -#, fuzzy msgid "Delete subvolume" msgstr "Alt disk bölümü sil" @@ -816,13 +809,13 @@ msgstr "" "Not:" msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" -msgstr "- Maksimum değer : {} ( {} paralel indirmeye izin verir, bir seferde {} indirmeye izin verir)" +msgstr " - Maksimum değer : {} ( {} paralel indirmeye izin verir, bir seferde {} indirmeye izin verir)" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" -msgstr "- Minimum değer : 1 ( 1 paralel indirmeye izin verir, bir seferde 2 indirmeye izin verir )" +msgstr " - Minimum değer : 1 ( 1 paralel indirmeye izin verir, bir seferde 2 indirmeye izin verir )" msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" -msgstr "- Devre Dışı Bırak/Varsayılan : 0 (Paralel indirmeyi devre dışı bırakır, aynı anda yalnızca 1 indirmeye izin verir)" +msgstr " - Devre Dışı Bırak/Varsayılan : 0 (Paralel indirmeyi devre dışı bırakır, aynı anda yalnızca 1 indirmeye izin verir)" #, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" @@ -831,7 +824,6 @@ msgstr "Geçersiz Giriş! Geçerli bir girişle tekrar deneyin [1 - {max_downloa msgid "Parallel Downloads" msgstr "Paralel İndirmeler" -#, fuzzy msgid "ESC to skip" msgstr "Geçmek için ESC'ye basın" @@ -853,9 +845,8 @@ msgstr "Yazı tipi {} olarak saklanmalıdır." msgid "Archinstall requires root privileges to run. See --help for more." msgstr "Archinstall'ın çalışması için kök ayrıcalıkları gerekir. Daha fazla bilgi için --help'e bakın." -#, fuzzy msgid "Select an execution mode" -msgstr "'{}' için bir eylem seçin" +msgstr "Bir çalıştırma modu seçin" msgid "Unable to fetch profile from specified url: {}" msgstr "Belirtilen url'den profil getirilemiyor: {}" @@ -863,51 +854,40 @@ msgstr "Belirtilen url'den profil getirilemiyor: {}" msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" msgstr "Profiller benzersiz ada sahip olmalıdır, ancak yinelenen ada sahip profil tanımları bulundu: {}" -#, fuzzy msgid "Select one or more devices to use and configure" -msgstr "Kullanmak ve yapılandırmak için bir veya daha fazla sabit disk seçin" +msgstr "Kullanılacak ve yapılandırılacak bir veya daha fazla cihaz seçin" -#, fuzzy msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" -msgstr "Eğer sabit disk seçimini sıfırlarsanız bu ayrıca mevcut disk şemasını da sıfırlayacaktır. Emin misiniz?" +msgstr "Aygıt seçimini sıfırlarsanız, bu aynı zamanda geçerli disk düzenini de sıfırlayacaktır. Emin misiniz?" -#, fuzzy msgid "Existing Partitions" -msgstr "Disk bölümü ekleniyor…." +msgstr "Mevcut Bölmeler" -#, fuzzy msgid "Select a partitioning option" -msgstr "Disk bölümü sil" +msgstr "Bir bölümleme seçeneği belirleyin" -#, fuzzy msgid "Enter the root directory of the mounted devices: " -msgstr "Yapılandırma(lar)ın kaydedilmesi için bir dizin girin: " +msgstr "Takılı aygıtların kök dizinini girin: " -#, fuzzy msgid "Minimum capacity for /home partition: {}GiB\n" msgstr "/home disk bölümü için minimum kapasite: {}GiB\n" -#, fuzzy msgid "Minimum capacity for Arch Linux partition: {}GiB" msgstr "Arch Linux disk bölümü için minimum kapasite: {}GiB" -#, fuzzy +# “profiles_bck” bir yer tutucu olabilir. O yuzden sabit tuttum. msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" -msgstr "Bu ön-programlanmış profillerin bir listesidir, bunlar masaüstü ortamları gibi şeyler kurmayı kolaylaştırabilir" +msgstr "Bu, önceden programlanmış profiles_bck listesidir, masaüstü ortamları gibi şeyleri yüklemeyi kolaylaştırabilirler" -#, fuzzy msgid "Current profile selection" msgstr "Mevcut disk bölümü düzeni" -#, fuzzy msgid "Remove all newly added partitions" msgstr "Yeni eklenen tüm disk bölümleri kaldırın" -#, fuzzy msgid "Assign mountpoint" msgstr "Bir disk bölümü için monte noktası ata" -#, fuzzy msgid "Mark/Unmark to be formatted (wipes data)" msgstr "Bir disk bölümünü biçimlendirilmek üzere işaretle/işareti kaldır (verileri siler)" @@ -917,15 +897,12 @@ msgstr "Önyüklenebilir olarak işaretle/işareti kaldır" msgid "Change filesystem" msgstr "Dosya sistemini değiştir" -#, fuzzy msgid "Mark/Unmark as compressed" -msgstr "Bir disk bölümünü sıkıştırılmış olarak işaretle/işareti kaldır (sadece btrfs)" +msgstr "Sıkıştırıldı olarak işaretle/işareti kaldır" -#, fuzzy msgid "Set subvolumes" msgstr "Alt disk bölümü ayarla" -#, fuzzy msgid "Delete partition" msgstr "Disk bölümü sil" @@ -935,12 +912,11 @@ msgstr "Disk bölümü" msgid "This partition is currently encrypted, to format it a filesystem has to be specified" msgstr "Bu disk bölümü şu anda şifrelenmiştir, biçimlendirmek için bir dosya sistemi belirtilmelidir" -#, fuzzy msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * Disk bölümü mount (bağlantı) noktaları iç kurulumla ilişkilidir, örnek olarak boot, /boot olacaktır." +msgstr "Bölüm bağlama noktaları kurulumun içine görelidir, örnek olarak önyükleme /boot olacaktır." msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." -msgstr "mountpoint /boot ayarlanmışsa, bölüm de önyüklenebilir olarak işaretlenecektir." +msgstr "Mountpoint /boot ayarlanmışsa, bölüm de önyüklenebilir olarak işaretlenecektir." msgid "Mountpoint: " msgstr "Bağlantı noktası: " @@ -948,17 +924,14 @@ msgstr "Bağlantı noktası: " msgid "Current free sectors on device {}:" msgstr "{} cihazındaki mevcut boş sektörler:" -#, fuzzy msgid "Total sectors: {}" -msgstr "Geçerli bir dizin değil: {}" +msgstr "Toplam sektörler: {}" -#, fuzzy msgid "Enter the start sector (default: {}): " -msgstr "Başlangıç kesimini girin (yüzde ya da blok numarası, varsayılan: {}): " +msgstr "Başlangıç sektörünü girin (varsayılan: {}): " -#, fuzzy msgid "Enter the end sector of the partition (percentage or block number, default: {}): " -msgstr "Disk bölümünün bitiş kesimini girin (yüzde ya da blok numarası, ör: {}): " +msgstr "Bölümün bitiş sektörünü girin (yüzde veya blok numarası, varsayılan: {}): " msgid "This will remove all newly added partitions, continue?" msgstr "Bu işlem yeni eklenmiş tüm bölümleri kaldıracaktır, devam edilsin?" @@ -969,9 +942,8 @@ msgstr "Bölüm yönetimi: {}" msgid "Total length: {}" msgstr "Toplam uzunluk: {}" -#, fuzzy msgid "Encryption type" -msgstr "Şifreleme parolası" +msgstr "Şifreleme türü" msgid "Partitions" msgstr "Disk bölümleri" @@ -979,9 +951,8 @@ msgstr "Disk bölümleri" msgid "No HSM devices available" msgstr "Kullanılabilir HSM cihazı yok" -#, fuzzy msgid "Partitions to be encrypted" -msgstr "Hangi disk bölümünün şifrelenmiş olarak işaretleneceğini seçin" +msgstr "Şifrelenecek bölümler" msgid "Select disk encryption option" msgstr "Disk şifreleme seçeneğini seçin" @@ -989,17 +960,14 @@ msgstr "Disk şifreleme seçeneğini seçin" msgid "Select a FIDO2 device to use for HSM" msgstr "HSM için kullanılacak bir FIDO2 cihazı seçin" -#, fuzzy msgid "Use a best-effort default partition layout" -msgstr "Bütün seçilmiş diskleri temizle ve elden gelen en iyi varsayılan disk bölümü düzenini kullan" +msgstr "En iyi çabayı gösteren bir varsayılan bölüm düzeni kullanın" -#, fuzzy msgid "Manual Partitioning" msgstr "Manuel yapılandırma" -#, fuzzy msgid "Pre-mounted configuration" -msgstr "Yapılandırma yok" +msgstr "Önceden monte edilmiş konfigürasyon" msgid "Unknown" msgstr "Bilinmeyen" @@ -1016,15 +984,12 @@ msgstr "← Geri" msgid "Disk encryption" msgstr "Disk şifrelemesi" -#, fuzzy msgid "Configuration" msgstr "Konfigürasyon" -#, fuzzy msgid "Password" msgstr "Şifre" -#, fuzzy msgid "All settings will be reset, are you sure?" msgstr "Bütün ayarlar sıfırlanacak, emin misiniz?" @@ -1040,36 +1005,29 @@ msgstr "Ortam türü: {}" msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" msgstr "Tescilli Nvidia sürücüsü Sway tarafından desteklenmiyor. Sorunlarla karşılaşmanız muhtemeldir, bu sizin için uygun mu?" -#, fuzzy msgid "Installed packages" -msgstr "İndirilmiş paketler" +msgstr "Yüklü paketler" -#, fuzzy msgid "Add profile" msgstr "Profil ekle" -#, fuzzy msgid "Edit profile" msgstr "Profil düzenle" -#, fuzzy msgid "Delete profile" msgstr "Profil sil" -#, fuzzy msgid "Profile name: " -msgstr "Profil ismi:" +msgstr "Profil ismi: " msgid "The profile name you entered is already in use. Try again" msgstr "Girdiğiniz profil adı zaten kullanılıyor. Tekrar deneyin" -#, fuzzy msgid "Packages to be install with this profile (space separated, leave blank to skip): " msgstr "Kurulacak ek paketleri yazınız (boşlukla ayrılmış, geçmek için boş bırakın): " -#, fuzzy msgid "Services to be enabled with this profile (space separated, leave blank to skip): " -msgstr "Kurulacak ek paketleri yazınız (boşlukla ayrılmış, geçmek için boş bırakın): " +msgstr "Bu profille etkinleştirilecek hizmetler (boşluk bırakılmış, atlamak için boş bırakın): " msgid "Should this profile be enabled for installation?" msgstr "Bu profil kurulum için etkinleştirilmeli mi?" @@ -1077,14 +1035,12 @@ msgstr "Bu profil kurulum için etkinleştirilmeli mi?" msgid "Create your own" msgstr "Kendinizinkini oluşturun" -#, fuzzy msgid "" "\n" "Select a graphics driver or leave blank to install all open-source drivers" msgstr "" "\n" -"\n" -"Bir grafik sürücüsü seçin ya da bütün açık-kaynak sürücüleri kurmak için boş bırakın" +"Bir grafik sürücüsü seçin veya tüm açık kaynak sürücülerini yüklemek için boş bırakın" msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" msgstr "Sway'in seat'e erişmesi gerekir (klavye, fare vb. donanım aygıtlarının birleşimi)" @@ -1110,22 +1066,18 @@ msgstr "Lütfen hangi karşılayıcının kurulacağını seçin" msgid "This is a list of pre-programmed default_profiles" msgstr "Bu, önceden programlanmış default_profiles listesidir" -#, fuzzy msgid "Disk configuration" -msgstr "Yapılandırma yok" +msgstr "Disk yapılandırması" -#, fuzzy msgid "Profiles" msgstr "Profiller" msgid "Finding possible directories to save configuration files ..." msgstr "Yapılandırma dosyalarını kaydetmek için olası dizinler aranıyor..." -#, fuzzy msgid "Select directory (or directories) for saving configuration files" -msgstr "Kullanmak ve yapılandırmak için bir veya daha fazla sabit disk seçin" +msgstr "Yapılandırma dosyalarını kaydetmek için dizin (veya dizinler) seçin" -#, fuzzy msgid "Add a custom mirror" msgstr "Özel indirme sunucusu ekleyin" @@ -1135,19 +1087,15 @@ msgstr "Özel indirme sunucusunu değiştirin" msgid "Delete custom mirror" msgstr "Özel indirme sunucusunu silin" -#, fuzzy msgid "Enter name (leave blank to skip): " msgstr "İsim girin (geçmek için boş bırakın): " -#, fuzzy msgid "Enter url (leave blank to skip): " -msgstr "url girin (geçmek için boş bırakın): " +msgstr "Url girin (geçmek için boş bırakın): " -#, fuzzy msgid "Select signature check option" -msgstr "İmzalama kontrol opsiyonu seçin" +msgstr "İmza kontrolü seçeneğini seçin" -#, fuzzy msgid "Select signature option" msgstr "İmza seçeneğini seçin" @@ -1157,17 +1105,15 @@ msgstr "Kişisel depo aynası" msgid "Defined" msgstr "Tanımlı" -#, fuzzy msgid "Save user configuration (including disk layout)" msgstr "Kullanıcı konfigürasyonunu kaydet (Disk düzeni ile birlikte)" -#, fuzzy msgid "" "Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" "Save directory: " msgstr "" -"Yapılandırma(lar)ın kaydedilmesi için bir dizin girin (tab ile tamamlama etkin)\n" -" " +"Kaydedilecek yapılandırma(lar) için bir dizin girin (sekme tamamlama etkin)\n" +"Dizini kaydet: " msgid "" "Do you want to save {} configuration file(s) in the following location?\n" @@ -1175,20 +1121,17 @@ msgid "" "{}" msgstr "{} konfigürasyon dosya(lar)ını gösterilen konuma kaydetmek ister miydiniz?" -#, fuzzy msgid "Saving {} configuration files to {}" msgstr "{} konfigürasyon dosyaları {} konumuna kaydediliyor" -#, fuzzy msgid "Mirrors" -msgstr "Depo sunucusu" +msgstr "Ayna sunucular" -#, fuzzy msgid "Mirror regions" -msgstr "Deop sunucusu bölgesi" +msgstr "Ayna sunucusu bölgesi" msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr "Maximum değer : {} ( {} paralel indirmelerine izin verir, Tek seferde {max_download+1} indirmeye izin verir.)" +msgstr " Maximum değer : {} ( {} paralel indirmelerine izin verir, Tek seferde {max_download+1} indirmeye izin verir.)" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" msgstr "Geçersiz girdi! Geçerli bir girdiyle tekrar deneyin [1 ila {} veya devre dışı bırakmak için 0]" @@ -1196,78 +1139,67 @@ msgstr "Geçersiz girdi! Geçerli bir girdiyle tekrar deneyin [1 ila {} veya dev msgid "Locales" msgstr "Yerel Ayarlar" -#, fuzzy msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "NetworkManager'ı kullan (GNOME ya da KDE'de interneti grafik olarak yapılandırmak için gerekli)" -#, fuzzy msgid "Total: {} / {}" -msgstr "Toplam uzunluk: {}" +msgstr "Toplam: {} / {}" msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." -msgstr "" +msgstr "Girilen tüm değerlerin sonuna bir birim eklenebilir: B, KB, KiB, MB, MiB…" msgid "If no unit is provided, the value is interpreted as sectors" -msgstr "" +msgstr "Herhangi bir birim belirtilmezse, değer sektörler olarak yorumlanır" -#, fuzzy msgid "Enter start (default: sector {}): " -msgstr "Başlangıç kesimini girin (yüzde ya da blok numarası, varsayılan: {}): " +msgstr "Başlangıç girin (varsayılan: sektör {}): " -#, fuzzy msgid "Enter end (default: {}): " -msgstr "Başlangıç kesimini girin (yüzde ya da blok numarası, varsayılan: {}): " +msgstr "Bitiş girin (varsayılan: {}): " msgid "Unable to determine fido2 devices. Is libfido2 installed?" -msgstr "" +msgstr "Fido2 cihazları belirlenemiyor. libfido2 yüklü mü?" msgid "Path" -msgstr "" +msgstr "Yol/Konum" msgid "Manufacturer" -msgstr "" +msgstr "Üretici firma" msgid "Product" -msgstr "" +msgstr "Ürün" -#, fuzzy, python-brace-format +#, python-brace-format msgid "Invalid configuration: {error}" -msgstr "Manuel yapılandırma" +msgstr "Geçersiz yapılandırma: {error}" msgid "Type" -msgstr "" +msgstr "Tür" -#, fuzzy msgid "This option enables the number of parallel downloads that can occur during package downloads" -msgstr "Bu seçenek, yükleme sırasında meydana gelebilecek paralel indirme sayısını etkinleştirir" +msgstr "Bu seçenek, paket indirmeleri sırasında oluşabilecek paralel indirme sayısını etkinleştirir" -#, fuzzy msgid "" "Enter the number of parallel downloads to be enabled.\n" "\n" "Note:\n" msgstr "" "Etkinleştirilecek paralel indirme sayısını girin.\n" -" (1 ile {max_downloads} arasında bir değer girin)\n" -"Not:" +"\n" +"Not:\n" -#, fuzzy msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" -msgstr "- Maksimum değer : {} ( {} paralel indirmeye izin verir, bir seferde {} indirmeye izin verir)" +msgstr " - Önerilen maksimum değer : {} ( Bir seferde {} paralel indirmeye izin verir )" -#, fuzzy msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" -msgstr "- Devre Dışı Bırak/Varsayılan : 0 (Paralel indirmeyi devre dışı bırakır, aynı anda yalnızca 1 indirmeye izin verir)" +msgstr " - Devre Dışı / Varsayılan : 0 (Paralel indirmeyi devre dışı bırakır, bir seferde yalnızca 1 indirmeye izin verir)\n" -#, fuzzy msgid "Invalid input! Try again with a valid input [or 0 to disable]" -msgstr "Geçersiz girdi! Geçerli bir girdiyle tekrar deneyin [1 ila {} veya devre dışı bırakmak için 0]" +msgstr "Geçersiz girdi! Geçerli bir girişle [veya devre dışı bırakmak için 0 ile] tekrar deneyin" -#, fuzzy msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" -msgstr "Sway'in seat'e erişmesi gerekir (klavye, fare vb. donanım aygıtlarının birleşimi)" +msgstr "Hyprland’in donanımlarınıza erişmesi gerekir (klavye, fare vb. donanım aygıtlarının toplanması)" -#, fuzzy msgid "" "\n" "\n" @@ -1275,4 +1207,4 @@ msgid "" msgstr "" "\n" "\n" -"Sway'in donanımınıza erişmesine izin vermek için bir seçenek belirleyin" +"Hyprland’e donanımınıza erişim izni vermek için bir seçenek belirleyin" -- cgit v1.2.3-70-g09d2 From 2394e64dc2d84df3de1616d166ea9fce9b9144cc Mon Sep 17 00:00:00 2001 From: T13nou <64090484+T13nou@users.noreply.github.com> Date: Wed, 27 Sep 2023 08:50:14 +0200 Subject: FR Translation updated (#2112) --- archinstall/locales/fr/LC_MESSAGES/base.po | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/archinstall/locales/fr/LC_MESSAGES/base.po b/archinstall/locales/fr/LC_MESSAGES/base.po index 3666eda9..fe15bfe3 100644 --- a/archinstall/locales/fr/LC_MESSAGES/base.po +++ b/archinstall/locales/fr/LC_MESSAGES/base.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: Roxfr \n" +"PO-Revision-Date: 2023/09/25\n" +"Last-Translator: T13nou \n" "Language-Team: \n" "Language: fr\n" "MIME-Version: 1.0\n" @@ -1146,7 +1146,7 @@ msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." msgstr "" msgid "If no unit is provided, the value is interpreted as sectors" -msgstr "" +msgstr "Si aucune unité n'est fournie, la valeur est interprétée comme des secteurs"" #, fuzzy msgid "Enter start (default: sector {}): " @@ -1157,23 +1157,23 @@ msgid "Enter end (default: {}): " msgstr "Saisir le secteur de départ (par défaut : {}) : " msgid "Unable to determine fido2 devices. Is libfido2 installed?" -msgstr "" +msgstr "Impossible de déterminer les appareils fido2. Est-ce que libfido2 est installé ?" msgid "Path" -msgstr "" +msgstr "Chemin" msgid "Manufacturer" -msgstr "" +msgstr "Fabricant" msgid "Product" -msgstr "" +msgstr "Produit" #, fuzzy, python-brace-format msgid "Invalid configuration: {error}" msgstr "Configuration manuelle" msgid "Type" -msgstr "" +msgstr "Type" #, fuzzy msgid "This option enables the number of parallel downloads that can occur during package downloads" -- cgit v1.2.3-70-g09d2 From 072519ad11448053cc062ac4efa66c624b4aa887 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Wed, 27 Sep 2023 11:20:13 -0400 Subject: Simplify command in `README.md` example (#2122) --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 84520d6b..5c799116 100644 --- a/README.md +++ b/README.md @@ -196,8 +196,7 @@ To test this without a live ISO, the simplest approach is to use a local image a This can be done by installing `pacman -S arch-install-scripts util-linux` locally and doing the following: # truncate -s 20G testimage.img - # losetup -fP ./testimage.img - # losetup -a | grep "testimage.img" | awk -F ":" '{print $1}' + # losetup --partscan --show --find ./testimage.img # pip install --upgrade archinstall # python -m archinstall --script guided # qemu-system-x86_64 -enable-kvm -machine q35,accel=kvm -device intel-iommu -cpu host -m 4096 -boot order=d -drive file=./testimage.img,format=raw -drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF_CODE.fd -drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF_VARS.fd -- cgit v1.2.3-70-g09d2 From 72856ed5ec869012c8d2ab0f1d17467653156556 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Wed, 27 Sep 2023 23:17:26 +0200 Subject: Bumping version to: v2.6.2 (#2123) --- archinstall/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archinstall/__init__.py b/archinstall/__init__.py index f89d7c39..9bf747c6 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -39,7 +39,7 @@ if TYPE_CHECKING: _: Any -__version__ = "2.6.1" +__version__ = "2.6.2" storage['__version__'] = __version__ -- cgit v1.2.3-70-g09d2 From 1e296b263714017596beeca27744a51c75f29504 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Thu, 28 Sep 2023 08:44:18 +1000 Subject: Fix 2118 (#2119) * Update locales generation * Update README * Disable translation check --------- Co-authored-by: Daniel Girtler --- .github/workflows/translation-check.yaml | 56 ++++++++++++++++---------------- README.md | 8 +++++ archinstall/locales/README.md | 10 +++--- archinstall/locales/locales_generator.sh | 43 ++++++++++++++++++++++-- 4 files changed, 81 insertions(+), 36 deletions(-) diff --git a/.github/workflows/translation-check.yaml b/.github/workflows/translation-check.yaml index c0abbaa6..188eeeb8 100644 --- a/.github/workflows/translation-check.yaml +++ b/.github/workflows/translation-check.yaml @@ -1,28 +1,28 @@ -on: - push: - paths: - - 'archinstall/locales/**' - pull_request: - paths: - - 'archinstall/locales/**' -name: Verify local_generate script was run on translation changes -jobs: - translation-check: - runs-on: ubuntu-latest - container: - image: archlinux:latest - steps: - - uses: actions/checkout@v4 - - run: pacman --noconfirm -Syu python git diffutils - - name: Verify all translation scripts are up to date - run: | - cd .. - cp -r archinstall archinstall_orig - cd archinstall/archinstall/locales - bash locales_generator.sh 1> /dev/null - cd ../../.. - git diff \ - --quiet --no-index --name-only \ - archinstall_orig/archinstall/locales \ - archinstall/archinstall/locales \ - || (echo "Translation files have not been updated after translation, please run ./locales_generator.sh once more and commit" && exit 1) +#on: +# push: +# paths: +# - 'archinstall/locales/**' +# pull_request: +# paths: +# - 'archinstall/locales/**' +#name: Verify local_generate script was run on translation changes +#jobs: +# translation-check: +# runs-on: ubuntu-latest +# container: +# image: archlinux:latest +# steps: +# - uses: actions/checkout@v4 +# - run: pacman --noconfirm -Syu python git diffutils +# - name: Verify all translation scripts are up to date +# run: | +# cd .. +# cp -r archinstall archinstall_orig +# cd archinstall/archinstall/locales +# bash locales_generator.sh 1> /dev/null +# cd ../../.. +# git diff \ +# --quiet --no-index --name-only \ +# archinstall_orig/archinstall/locales \ +# archinstall/archinstall/locales \ +# || (echo "Translation files have not been updated after translation, please run ./locales_generator.sh once more and commit" && exit 1) diff --git a/README.md b/README.md index 5c799116..741d57e7 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,14 @@ how much has been translated. Any contributions to the translations are more than welcome, to get started please follow [the guide](https://github.com/archlinux/archinstall/blob/master/archinstall/locales/README.md) +## Fonts +The ISO does not ship with ship with all fonts needed for different languages. +Fonts that are using a different character set than Latin will not be displayed correctly. If those languages +want to be selected than a proper font has to be set manually in the console. + +All available console fonts can be found in `/usr/share/kbd/consolefonts` and can be set with `setfont LatGrkCyr-8x16`. + + # Scripting your own installation ## Scripting interactive installation diff --git a/archinstall/locales/README.md b/archinstall/locales/README.md index e1266209..de1aa83a 100644 --- a/archinstall/locales/README.md +++ b/archinstall/locales/README.md @@ -14,15 +14,15 @@ can be set with `setfont LatGrkCyr-8x16` ## Adding new languages -New languages can be added simply by creating a new folder with the proper language abbreviation (see list `languages.json` if unsure). +New languages can be added simply by creating a new folder with the proper language abbreviation (see list `languages.json` if unsure). Run the following command to create a new template for a language ``` mkdir -p /LC_MESSAGES/ && touch /LC_MESSAGES/base.po ``` -After that run the script `./locales_generator.sh` it will automatically populate the new `base.po` file with the strings that -need to be translated into the new language. -For example the `base.po` might contain something like the following now +After that run the script `./locales_generator.sh ` it will automatically populate the new `base.po` file with the strings that +need to be translated into the new language. +For example the `base.po` might contain something like the following now ``` #: lib/user_interaction.py:82 msgid "Do you really want to abort?" @@ -30,7 +30,7 @@ msgstr "" ``` The `msgid` is the identifier of the string in the code as well as the default text to be displayed, meaning that if no -translation is provided for a language then this is the text that is going to be shown. +translation is provided for a language then this is the text that is going to be shown. To perform translations for a language this file can be edited manually or the neat `poedit` can be used (https://poedit.net/). If editing the file manually, write the translation in the `msgstr` part diff --git a/archinstall/locales/locales_generator.sh b/archinstall/locales/locales_generator.sh index cdd5be31..5386c3e6 100755 --- a/archinstall/locales/locales_generator.sh +++ b/archinstall/locales/locales_generator.sh @@ -2,11 +2,48 @@ cd $(dirname "$0")/.. -find . -type f -iname "*.py" | xargs xgettext --join-existing --no-location --omit-header -d base -o locales/base.pot +function update_lang() { + file=$1 -for file in $(find locales/ -name "base.po"); do echo "Updating: $file" path=$(dirname $file) msgmerge --quiet --no-location --width 512 --backup none --update $file locales/base.pot msgfmt -o $path/base.mo $file -done +} + + +function generate_all() { + for file in $(find locales/ -name "base.po"); do + update_lang "$file" + done +} + +function generate_single_lang() { + lang_file="locales/$1/LC_MESSAGES/base.po" + + if [ ! -f "$lang_file" ]; then + echo "Language files not found: $lang_file" + exit 1 + fi + + update_lang "$lang_file" +} + + + +if [ $# -eq 0 ] + then + echo "Usage: locales_generator.sh " + exit 1 +fi + +lang=$1 + +# Update the base file containing all translatable string +find . -type f -iname "*.py" | xargs xgettext --join-existing --no-location --omit-header -d base -o locales/base.pot + +case "$lang" in + "all") generate_all + ;; + *) generate_single_lang "$lang" +esac -- cgit v1.2.3-70-g09d2 From 3695c37bc4c530cc381ccee86e3e190f50a4a416 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Thu, 28 Sep 2023 07:00:06 -0400 Subject: Fix mount command whitespace (#2126) --- archinstall/lib/disk/device_handler.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index 066d3295..f2835b7b 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -546,12 +546,16 @@ class DeviceHandler(object): info(f'Device already mounted at {target_mountpoint}') return - str_options = ','.join(options) - str_options = f'-o {str_options}' if str_options else '' + cmd = ['mount'] - mount_fs = f'-t {mount_fs}' if mount_fs else '' + if len(options): + cmd.extend(('-o', ','.join(options))) + if mount_fs: + cmd.extend(('-t', mount_fs)) - command = f'mount {mount_fs} {str_options} {dev_path} {target_mountpoint}' + cmd.extend((str(dev_path), str(target_mountpoint))) + + command = ' '.join(cmd) debug(f'Mounting {dev_path}: {command}') -- cgit v1.2.3-70-g09d2 From 7a9a934ae38324f11d7d3be580a3ec329a73f96f Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Thu, 28 Sep 2023 18:29:54 -0400 Subject: Refactor `_add_efistub_bootloader()` use cmd list (#2127) --- archinstall/lib/installer.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 5d1bb7dc..f0df7322 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -1076,23 +1076,22 @@ TIMEOUT=5 for kernel in self.kernels: # Setup the firmware entry - label = f'Arch Linux ({kernel})' - loader = f"/vmlinuz-{kernel}" - cmdline = [ *microcode, f"initrd=\\initramfs-{kernel}.img", *kernel_parameters, ] - cmd = f'efibootmgr ' \ - f'--disk {parent_dev_path} ' \ - f'--part {boot_partition.partn} ' \ - f'--create ' \ - f'--label "{label}" ' \ - f'--loader {loader} ' \ - f'--unicode \'{" ".join(cmdline)}\' ' \ - f'--verbose' + cmd = [ + 'efibootmgr', + '--disk', str(parent_dev_path), + '--part', str(boot_partition.partn), + '--create', + '--label', f'Arch Linux ({kernel})', + '--loader', f"/vmlinuz-{kernel}", + '--unicode', ' '.join(cmdline), + '--verbose' + ] SysCommand(cmd) -- cgit v1.2.3-70-g09d2 From 5f5b95f24515de0d265a96e6919622d185f10fa3 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Fri, 29 Sep 2023 01:11:30 -0400 Subject: Fix GRUB boot directory (#2130) --- archinstall/lib/installer.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index f0df7322..e7895a1a 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -889,6 +889,13 @@ class Installer: info(f"GRUB boot partition: {boot_partition.dev_path}") + if boot_partition == root_partition and root_partition.mountpoint: + boot_dir = root_partition.mountpoint / 'boot' + elif boot_partition.mountpoint: + boot_dir = boot_partition.mountpoint + else: + raise ValueError('Could not detect boot directory') + command = [ '/usr/bin/arch-chroot', str(self.target), @@ -904,7 +911,7 @@ class Installer: add_options = [ '--target=x86_64-efi', f'--efi-directory={efi_partition.mountpoint}', - f'--boot-directory={boot_partition.mountpoint if boot_partition else "/boot"}', + f'--boot-directory={boot_dir}', '--bootloader-id=GRUB', '--removable' ] @@ -937,7 +944,7 @@ class Installer: try: SysCommand( f'/usr/bin/arch-chroot {self.target} ' - f'grub-mkconfig -o {boot_partition.mountpoint if boot_partition else "/boot"}/grub/grub.cfg' + f'grub-mkconfig -o {boot_dir}/grub/grub.cfg' ) except SysCallError as err: raise DiskError(f"Could not configure GRUB: {err}") -- cgit v1.2.3-70-g09d2 From 9f5c2bb70b0a4551eaa871164a3c9d71c1e65086 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Fri, 29 Sep 2023 10:09:28 -0400 Subject: Add support for ESP partition flag (#2133) --- archinstall/lib/disk/device_model.py | 18 +++++++++++------- archinstall/lib/disk/partitioning_menu.py | 7 +++++++ archinstall/lib/installer.py | 11 +++++++++-- archinstall/lib/interactions/disk_conf.py | 4 +++- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index b1f012f7..6992bccb 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -658,7 +658,8 @@ class PartitionModification: partuuid: Optional[str] = None uuid: Optional[str] = None - _boot_indicator_flags = [PartitionFlag.Boot, PartitionFlag.XBOOTLDR] + _efi_indicator_flags = (PartitionFlag.Boot, PartitionFlag.ESP) + _boot_indicator_flags = (PartitionFlag.Boot, PartitionFlag.XBOOTLDR) def __post_init__(self): # needed to use the object as a dictionary key due to hash func @@ -728,6 +729,13 @@ class PartitionModification: raise ValueError('Mountpoint is not specified') + def is_efi(self) -> bool: + return ( + any(set(self.flags) & set(self._efi_indicator_flags)) + and self.fs_type == FilesystemType.Fat32 + and PartitionFlag.XBOOTLDR not in self.flags + ) + def is_boot(self) -> bool: """ Returns True if any of the boot indicator flags are found in self.flags @@ -828,9 +836,8 @@ class DeviceModification: def get_efi_partition(self) -> Optional[PartitionModification]: """ Similar to get_boot_partition() but excludes XBOOTLDR partitions from it's candidates. - Also works with ESP flag. """ - filtered = filter(lambda x: (x.is_boot() or PartitionFlag.ESP in x.flags) and x.fs_type == FilesystemType.Fat32 and PartitionFlag.XBOOTLDR not in x.flags, self.partitions) + filtered = filter(lambda x: x.is_efi() and x.mountpoint, self.partitions) return next(filtered, None) def get_boot_partition(self) -> Optional[PartitionModification]: @@ -843,10 +850,7 @@ class DeviceModification: filtered = filter(lambda x: x.is_boot() and x != efi_partition and x.mountpoint, self.partitions) if boot_partition := next(filtered, None): return boot_partition - if efi_partition.is_boot(): - return efi_partition - else: - return None + return efi_partition else: filtered = filter(lambda x: x.is_boot() and x.mountpoint, self.partitions) return next(filtered, None) diff --git a/archinstall/lib/disk/partitioning_menu.py b/archinstall/lib/disk/partitioning_menu.py index c5263b82..a9478158 100644 --- a/archinstall/lib/disk/partitioning_menu.py +++ b/archinstall/lib/disk/partitioning_menu.py @@ -6,6 +6,7 @@ from typing import Any, Dict, TYPE_CHECKING, List, Optional, Tuple from .device_model import PartitionModification, FilesystemType, BDevice, Size, Unit, PartitionType, PartitionFlag, \ ModificationStatus, DeviceGeometry, SectorSize +from ..hardware import SysInfo from ..menu import Menu, ListManager, MenuSelection, TextInput from ..output import FormattedOutput, warn from .subvolume_menu import SubvolumeMenu @@ -105,10 +106,14 @@ class PartitioningList(ListManager): entry.mountpoint = self._prompt_mountpoint() if entry.mountpoint == Path('/boot'): entry.set_flag(PartitionFlag.Boot) + if SysInfo.has_uefi(): + entry.set_flag(PartitionFlag.ESP) case 'mark_formatting' if entry: self._prompt_formatting(entry) case 'mark_bootable' if entry: entry.invert_flag(PartitionFlag.Boot) + if SysInfo.has_uefi(): + entry.invert_flag(PartitionFlag.ESP) case 'set_filesystem' if entry: fs_type = self._prompt_partition_fs_type() if fs_type: @@ -310,6 +315,8 @@ class PartitioningList(ListManager): if partition.mountpoint == Path('/boot'): partition.set_flag(PartitionFlag.Boot) + if SysInfo.has_uefi(): + partition.set_flag(PartitionFlag.ESP) return partition diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index e7895a1a..585389ed 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -903,15 +903,22 @@ class Installer: '--debug' ] - if SysInfo.has_uefi() and efi_partition is not None: + if SysInfo.has_uefi(): + if not efi_partition: + raise ValueError('Could not detect efi partition') + info(f"GRUB EFI partition: {efi_partition.dev_path}") self.pacman.strap('efibootmgr') # TODO: Do we need? Yes, but remove from minimal_installation() instead? + boot_dir_arg = [] + if boot_partition != efi_partition: + boot_dir_arg.append(f'--boot-directory={boot_dir}') + add_options = [ '--target=x86_64-efi', f'--efi-directory={efi_partition.mountpoint}', - f'--boot-directory={boot_dir}', + *boot_dir_arg, '--bootloader-id=GRUB', '--removable' ] diff --git a/archinstall/lib/interactions/disk_conf.py b/archinstall/lib/interactions/disk_conf.py index 253a623d..84a3196c 100644 --- a/archinstall/lib/interactions/disk_conf.py +++ b/archinstall/lib/interactions/disk_conf.py @@ -170,9 +170,11 @@ def select_disk_config( def _boot_partition(sector_size: disk.SectorSize) -> disk.PartitionModification: + flags = [disk.PartitionFlag.Boot] if SysInfo.has_uefi(): start = disk.Size(1, disk.Unit.MiB, sector_size) size = disk.Size(512, disk.Unit.MiB, sector_size) + flags.append(disk.PartitionFlag.ESP) else: start = disk.Size(3, disk.Unit.MiB, sector_size) size = disk.Size(203, disk.Unit.MiB, sector_size) @@ -185,7 +187,7 @@ def _boot_partition(sector_size: disk.SectorSize) -> disk.PartitionModification: length=size, mountpoint=Path('/boot'), fs_type=disk.FilesystemType.Fat32, - flags=[disk.PartitionFlag.Boot] + flags=flags ) -- cgit v1.2.3-70-g09d2 From a78ab490bef63fabaf8064bd139c4144786f9b20 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Sat, 30 Sep 2023 15:56:22 +0200 Subject: Bumping version to: v2.6.3 (#2135) --- archinstall/__init__.py | 2 +- archinstall/locales/ar/LC_MESSAGES/base.po | 3 +++ archinstall/locales/base.pot | 4 ++++ archinstall/locales/cs/LC_MESSAGES/base.po | 3 +++ archinstall/locales/de/LC_MESSAGES/base.po | 3 +++ archinstall/locales/el/LC_MESSAGES/base.po | 3 +++ archinstall/locales/en/LC_MESSAGES/base.po | 3 +++ archinstall/locales/es/LC_MESSAGES/base.po | 4 ++++ archinstall/locales/et/LC_MESSAGES/base.po | 3 +++ archinstall/locales/id/LC_MESSAGES/base.po | 3 +++ archinstall/locales/it/LC_MESSAGES/base.po | 3 +++ archinstall/locales/ka/LC_MESSAGES/base.po | 3 +++ archinstall/locales/ko/LC_MESSAGES/base.po | 3 +++ archinstall/locales/nl/LC_MESSAGES/base.po | 3 +++ archinstall/locales/pl/LC_MESSAGES/base.po | 3 +++ archinstall/locales/pt/LC_MESSAGES/base.po | 4 ++++ archinstall/locales/pt_BR/LC_MESSAGES/base.po | 3 +++ archinstall/locales/ro/LC_MESSAGES/base.po | 4 ++++ archinstall/locales/ru/LC_MESSAGES/base.po | 3 +++ archinstall/locales/sv/LC_MESSAGES/base.po | 3 +++ archinstall/locales/ta/LC_MESSAGES/base.po | 4 ++++ archinstall/locales/tr/LC_MESSAGES/base.po | 4 ++++ archinstall/locales/uk/LC_MESSAGES/base.po | 3 +++ archinstall/locales/ur/LC_MESSAGES/base.po | 3 +++ archinstall/locales/zh-CN/LC_MESSAGES/base.po | 3 +++ archinstall/locales/zh-TW/LC_MESSAGES/base.po | 3 +++ 26 files changed, 82 insertions(+), 1 deletion(-) diff --git a/archinstall/__init__.py b/archinstall/__init__.py index 9bf747c6..07b85f96 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -39,7 +39,7 @@ if TYPE_CHECKING: _: Any -__version__ = "2.6.2" +__version__ = "2.6.3" storage['__version__'] = __version__ diff --git a/archinstall/locales/ar/LC_MESSAGES/base.po b/archinstall/locales/ar/LC_MESSAGES/base.po index 88fedf65..aca71ae0 100644 --- a/archinstall/locales/ar/LC_MESSAGES/base.po +++ b/archinstall/locales/ar/LC_MESSAGES/base.po @@ -1151,3 +1151,6 @@ msgid "" "\n" "Choose an option to give Hyprland access to your hardware" msgstr "" + +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" diff --git a/archinstall/locales/base.pot b/archinstall/locales/base.pot index 7ab331c3..dda2fe4c 100644 --- a/archinstall/locales/base.pot +++ b/archinstall/locales/base.pot @@ -1229,3 +1229,7 @@ msgid "" "\n" "Choose an option to give Hyprland access to your hardware" msgstr "" + +msgid "" +"All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" diff --git a/archinstall/locales/cs/LC_MESSAGES/base.po b/archinstall/locales/cs/LC_MESSAGES/base.po index 29cb405a..e8115e9e 100644 --- a/archinstall/locales/cs/LC_MESSAGES/base.po +++ b/archinstall/locales/cs/LC_MESSAGES/base.po @@ -1247,3 +1247,6 @@ msgid "" "\n" "Choose an option to give Hyprland access to your hardware" msgstr "" + +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" diff --git a/archinstall/locales/de/LC_MESSAGES/base.po b/archinstall/locales/de/LC_MESSAGES/base.po index 285eddb1..8292d7ae 100644 --- a/archinstall/locales/de/LC_MESSAGES/base.po +++ b/archinstall/locales/de/LC_MESSAGES/base.po @@ -1215,3 +1215,6 @@ msgstr "" "\n" "\n" "Option auswählen, um Sway Zugriff auf deine Hardware zu geben" + +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" diff --git a/archinstall/locales/el/LC_MESSAGES/base.po b/archinstall/locales/el/LC_MESSAGES/base.po index 2a603b37..f5a07f52 100644 --- a/archinstall/locales/el/LC_MESSAGES/base.po +++ b/archinstall/locales/el/LC_MESSAGES/base.po @@ -1254,3 +1254,6 @@ msgid "" "\n" "Choose an option to give Hyprland access to your hardware" msgstr "" + +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" diff --git a/archinstall/locales/en/LC_MESSAGES/base.po b/archinstall/locales/en/LC_MESSAGES/base.po index 116839fc..a225b70b 100644 --- a/archinstall/locales/en/LC_MESSAGES/base.po +++ b/archinstall/locales/en/LC_MESSAGES/base.po @@ -1139,3 +1139,6 @@ msgid "" "\n" "Choose an option to give Hyprland access to your hardware" msgstr "" + +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" diff --git a/archinstall/locales/es/LC_MESSAGES/base.po b/archinstall/locales/es/LC_MESSAGES/base.po index fe447b22..ca0c2513 100644 --- a/archinstall/locales/es/LC_MESSAGES/base.po +++ b/archinstall/locales/es/LC_MESSAGES/base.po @@ -1267,6 +1267,10 @@ msgstr "" "\n" "Elija una opción para darle a Sway acceso a su hardware" +#, fuzzy +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "Todos los valores ingresados pueden tener una unidad como sufijo: B, KB, KiB, MB, MiB ..." + #~ msgid "Add :" #~ msgstr "Añadir :" diff --git a/archinstall/locales/et/LC_MESSAGES/base.po b/archinstall/locales/et/LC_MESSAGES/base.po index 60c616c7..66e40fe2 100644 --- a/archinstall/locales/et/LC_MESSAGES/base.po +++ b/archinstall/locales/et/LC_MESSAGES/base.po @@ -1232,3 +1232,6 @@ msgstr "" "\n" "\n" "Valige valik, et anda Sway'le juurdepääs teie riistvarale" + +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" diff --git a/archinstall/locales/id/LC_MESSAGES/base.po b/archinstall/locales/id/LC_MESSAGES/base.po index fcd089ac..a5ae2b5b 100644 --- a/archinstall/locales/id/LC_MESSAGES/base.po +++ b/archinstall/locales/id/LC_MESSAGES/base.po @@ -1253,3 +1253,6 @@ msgid "" "\n" "Choose an option to give Hyprland access to your hardware" msgstr "" + +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" diff --git a/archinstall/locales/it/LC_MESSAGES/base.po b/archinstall/locales/it/LC_MESSAGES/base.po index 8f721d0c..cc4e0ec6 100644 --- a/archinstall/locales/it/LC_MESSAGES/base.po +++ b/archinstall/locales/it/LC_MESSAGES/base.po @@ -1253,3 +1253,6 @@ msgid "" "\n" "Choose an option to give Hyprland access to your hardware" msgstr "" + +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" diff --git a/archinstall/locales/ka/LC_MESSAGES/base.po b/archinstall/locales/ka/LC_MESSAGES/base.po index d2bd3f9c..e6994fe8 100644 --- a/archinstall/locales/ka/LC_MESSAGES/base.po +++ b/archinstall/locales/ka/LC_MESSAGES/base.po @@ -1252,3 +1252,6 @@ msgid "" "\n" "Choose an option to give Hyprland access to your hardware" msgstr "" + +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" diff --git a/archinstall/locales/ko/LC_MESSAGES/base.po b/archinstall/locales/ko/LC_MESSAGES/base.po index e7e6ddee..ac29631a 100644 --- a/archinstall/locales/ko/LC_MESSAGES/base.po +++ b/archinstall/locales/ko/LC_MESSAGES/base.po @@ -1254,3 +1254,6 @@ msgid "" "\n" "Choose an option to give Hyprland access to your hardware" msgstr "" + +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" diff --git a/archinstall/locales/nl/LC_MESSAGES/base.po b/archinstall/locales/nl/LC_MESSAGES/base.po index 5b38511e..6ec26221 100644 --- a/archinstall/locales/nl/LC_MESSAGES/base.po +++ b/archinstall/locales/nl/LC_MESSAGES/base.po @@ -1270,6 +1270,9 @@ msgid "" "Choose an option to give Hyprland access to your hardware" msgstr "" +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" + #~ msgid "Add :" #~ msgstr "Toevoegen:" diff --git a/archinstall/locales/pl/LC_MESSAGES/base.po b/archinstall/locales/pl/LC_MESSAGES/base.po index b1971216..4a673027 100644 --- a/archinstall/locales/pl/LC_MESSAGES/base.po +++ b/archinstall/locales/pl/LC_MESSAGES/base.po @@ -1251,6 +1251,9 @@ msgid "" "Choose an option to give Hyprland access to your hardware" msgstr "" +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" + #~ msgid "When picking a directory to save configuration files to, by default we will ignore the following folders: " #~ msgstr "Podczas wybierania katalogu do zapisywania plików konfiguracyjnych, domyślnie ignorowane są następujące foldery: " diff --git a/archinstall/locales/pt/LC_MESSAGES/base.po b/archinstall/locales/pt/LC_MESSAGES/base.po index 54fd3cb8..555127e1 100644 --- a/archinstall/locales/pt/LC_MESSAGES/base.po +++ b/archinstall/locales/pt/LC_MESSAGES/base.po @@ -1212,6 +1212,10 @@ msgstr "" "\n" "Selecionar uma opção para permitir o acesso do Sway ao seu hardware" +#, fuzzy +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "Todos os valores inseridos podem ser sufixados com uma unidade: B, KB, KiB, MB, MiB..." + #~ msgid "Add :" #~ msgstr "Adicionar :" diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.po b/archinstall/locales/pt_BR/LC_MESSAGES/base.po index 164cc35f..9a72c32a 100644 --- a/archinstall/locales/pt_BR/LC_MESSAGES/base.po +++ b/archinstall/locales/pt_BR/LC_MESSAGES/base.po @@ -1228,5 +1228,8 @@ msgstr "" "\n" "Selecione uma opção para permitir o acesso do Sway ao seu hardware" +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" + #~ msgid "When picking a directory to save configuration files to, by default we will ignore the following folders: " #~ msgstr "Ao selecionar um diretório para salvar arquivos de configuração, por padrão nós ignoramos as seguintes pastas: " diff --git a/archinstall/locales/ro/LC_MESSAGES/base.po b/archinstall/locales/ro/LC_MESSAGES/base.po index 98384c4f..ab90314c 100644 --- a/archinstall/locales/ro/LC_MESSAGES/base.po +++ b/archinstall/locales/ro/LC_MESSAGES/base.po @@ -1206,3 +1206,7 @@ msgstr "" "\n" "\n" "Alegeți o opțiune pentru a acorda acces lui Sway la hardware" + +#, fuzzy +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "Toate valorile introduse pot fi notate cu unități precum: B, KB, KiB, MB, MiB ..." diff --git a/archinstall/locales/ru/LC_MESSAGES/base.po b/archinstall/locales/ru/LC_MESSAGES/base.po index 58a2488f..2cde9180 100644 --- a/archinstall/locales/ru/LC_MESSAGES/base.po +++ b/archinstall/locales/ru/LC_MESSAGES/base.po @@ -1215,3 +1215,6 @@ msgstr "" "\n" "\n" "Выберите опцию, чтобы предоставить Sway доступ к вашему оборудованию" + +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" diff --git a/archinstall/locales/sv/LC_MESSAGES/base.po b/archinstall/locales/sv/LC_MESSAGES/base.po index 80bd99ba..c0aac646 100644 --- a/archinstall/locales/sv/LC_MESSAGES/base.po +++ b/archinstall/locales/sv/LC_MESSAGES/base.po @@ -1253,3 +1253,6 @@ msgid "" "\n" "Choose an option to give Hyprland access to your hardware" msgstr "" + +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" diff --git a/archinstall/locales/ta/LC_MESSAGES/base.po b/archinstall/locales/ta/LC_MESSAGES/base.po index e408bf2d..57437bfe 100644 --- a/archinstall/locales/ta/LC_MESSAGES/base.po +++ b/archinstall/locales/ta/LC_MESSAGES/base.po @@ -1208,3 +1208,7 @@ msgstr "" "\n" "\n" "உங்கள் வன்பொருளுக்கான அணுகலை வழங்குவதற்கான விருப்பத்தைத் தேர்வுசெய்யவும்" + +#, fuzzy +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "உள்ளிடப்பட்ட அனைத்து மதிப்புகளையும் ஒரு அலகுடன் பின்னொட்டு இடலாம்: B, KB, KiB, MB, MiB..." diff --git a/archinstall/locales/tr/LC_MESSAGES/base.po b/archinstall/locales/tr/LC_MESSAGES/base.po index add12ffd..45888494 100644 --- a/archinstall/locales/tr/LC_MESSAGES/base.po +++ b/archinstall/locales/tr/LC_MESSAGES/base.po @@ -1208,3 +1208,7 @@ msgstr "" "\n" "\n" "Hyprland’e donanımınıza erişim izni vermek için bir seçenek belirleyin" + +#, fuzzy +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "Girilen tüm değerlerin sonuna bir birim eklenebilir: B, KB, KiB, MB, MiB…" diff --git a/archinstall/locales/uk/LC_MESSAGES/base.po b/archinstall/locales/uk/LC_MESSAGES/base.po index 0573cf51..afa16540 100644 --- a/archinstall/locales/uk/LC_MESSAGES/base.po +++ b/archinstall/locales/uk/LC_MESSAGES/base.po @@ -1251,3 +1251,6 @@ msgid "" "\n" "Choose an option to give Hyprland access to your hardware" msgstr "" + +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" diff --git a/archinstall/locales/ur/LC_MESSAGES/base.po b/archinstall/locales/ur/LC_MESSAGES/base.po index 1cbbefed..fbb906ff 100644 --- a/archinstall/locales/ur/LC_MESSAGES/base.po +++ b/archinstall/locales/ur/LC_MESSAGES/base.po @@ -1273,6 +1273,9 @@ msgid "" "Choose an option to give Hyprland access to your hardware" msgstr "" +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" + #~ msgid "Add :" #~ msgstr "شامل:" diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.po b/archinstall/locales/zh-CN/LC_MESSAGES/base.po index c92f5e6b..4c620836 100644 --- a/archinstall/locales/zh-CN/LC_MESSAGES/base.po +++ b/archinstall/locales/zh-CN/LC_MESSAGES/base.po @@ -1213,3 +1213,6 @@ msgstr "" "\n" "\n" "选择一个选项来给 Sway 提供对您硬件的访问权限" + +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" diff --git a/archinstall/locales/zh-TW/LC_MESSAGES/base.po b/archinstall/locales/zh-TW/LC_MESSAGES/base.po index 9e98f3d9..6befb3c4 100644 --- a/archinstall/locales/zh-TW/LC_MESSAGES/base.po +++ b/archinstall/locales/zh-TW/LC_MESSAGES/base.po @@ -1223,3 +1223,6 @@ msgstr "" "\n" "\n" "選擇一個選項以提供 Sway 對您硬件的訪問權限" + +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" -- cgit v1.2.3-70-g09d2 From a34e374634ce991061d3226db92d1327f2283e54 Mon Sep 17 00:00:00 2001 From: "K.B.Dharun Krishna" Date: Sun, 1 Oct 2023 02:27:35 +0530 Subject: po: update Tamil translation (#2105) * po: update Tamil translation * po: update translation files * po: update translation files, Tamil translation * Revert "po: update translation files, Tamil translation" This reverts commit 98ef415138a23151a3dad9f4a846114427d9db36. * po: update Tamil translation --------- Signed-off-by: K.B.Dharun Krishna --- archinstall/locales/fr/LC_MESSAGES/base.po | 3 +++ archinstall/locales/ta/LC_MESSAGES/base.mo | Bin 69508 -> 70504 bytes archinstall/locales/ta/LC_MESSAGES/base.po | 9 +++------ 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/archinstall/locales/fr/LC_MESSAGES/base.po b/archinstall/locales/fr/LC_MESSAGES/base.po index fe15bfe3..ab95c979 100644 --- a/archinstall/locales/fr/LC_MESSAGES/base.po +++ b/archinstall/locales/fr/LC_MESSAGES/base.po @@ -1215,6 +1215,9 @@ msgstr "" "\n" "Choisir une option pour permettre à Sway d'accéder à votre matériel" +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" + #, python-brace-format #~ msgid "Edit {origkey} :" #~ msgstr "Modifier {origkey} :" diff --git a/archinstall/locales/ta/LC_MESSAGES/base.mo b/archinstall/locales/ta/LC_MESSAGES/base.mo index c6cade73..677b72c9 100644 Binary files a/archinstall/locales/ta/LC_MESSAGES/base.mo and b/archinstall/locales/ta/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ta/LC_MESSAGES/base.po b/archinstall/locales/ta/LC_MESSAGES/base.po index 57437bfe..3c107d4a 100644 --- a/archinstall/locales/ta/LC_MESSAGES/base.po +++ b/archinstall/locales/ta/LC_MESSAGES/base.po @@ -1195,11 +1195,9 @@ msgstr " - முடக்கு/இயல்புநிலை: 0 (இணை msgid "Invalid input! Try again with a valid input [or 0 to disable]" msgstr "தவறான உள்ளீடு! சரியான உள்ளீட்டுடன் மீண்டும் முயற்சிக்கவும் [அல்லது முடக்க 0]" -#, fuzzy msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" -msgstr "ஸ்வேக்கு உங்கள் இருக்கைக்கான அணுகல் தேவை (வன்பொருள் சாதனங்களின் சேகரிப்பு அதாவது விசைப்பலகை, சுட்டி போன்றவை)" +msgstr "Hyprland க்கு உங்கள் இருக்கைக்கான அணுகல் தேவை (வன்பொருள் சாதனங்களின் சேகரிப்பு அதாவது விசைப்பலகை, சுட்டி போன்றவை)" -#, fuzzy msgid "" "\n" "\n" @@ -1207,8 +1205,7 @@ msgid "" msgstr "" "\n" "\n" -"உங்கள் வன்பொருளுக்கான அணுகலை வழங்குவதற்கான விருப்பத்தைத் தேர்வுசெய்யவும்" +"உங்கள் வன்பொருளுக்கு Hypland அணுகலை வழங்குவதற்கான விருப்பத்தைத் தேர்வு செய்யவும்" -#, fuzzy msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." -msgstr "உள்ளிடப்பட்ட அனைத்து மதிப்புகளையும் ஒரு அலகுடன் பின்னொட்டு இடலாம்: B, KB, KiB, MB, MiB..." +msgstr "உள்ளிடப்பட்ட அனைத்து மதிப்புகளையும் ஒரு அலகுடன் பின்னொட்டு இடலாம்: %, B, KB, KiB, MB, MiB..." -- cgit v1.2.3-70-g09d2 From 66fd201045e5b59adc8f5d352ab78956ce4dcb91 Mon Sep 17 00:00:00 2001 From: Alexmelman88 <99257010+Alexmelman88@users.noreply.github.com> Date: Sun, 1 Oct 2023 12:51:04 +0300 Subject: Update Russian translation (#2136) --- archinstall/locales/ru/LC_MESSAGES/base.mo | Bin 49500 -> 52869 bytes archinstall/locales/ru/LC_MESSAGES/base.po | 549 ++++++++++++++++++++--------- 2 files changed, 391 insertions(+), 158 deletions(-) diff --git a/archinstall/locales/ru/LC_MESSAGES/base.mo b/archinstall/locales/ru/LC_MESSAGES/base.mo index e0e227db..908eba6c 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 2cde9180..dd308a5a 100644 --- a/archinstall/locales/ru/LC_MESSAGES/base.po +++ b/archinstall/locales/ru/LC_MESSAGES/base.po @@ -9,14 +9,19 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" "X-Generator: Poedit 3.3.2\n" msgid "[!] A log file has been created here: {} {}" msgstr "[!] Здесь был создан файл журнала: {} {}" -msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" -msgstr " Пожалуйста, отправьте эту проблему (и файл) по адресу https://github.com/archlinux/archinstall/issues" +msgid "" +" Please submit this issue (and file) to https://github.com/archlinux/" +"archinstall/issues" +msgstr "" +" Пожалуйста, отправьте эту проблему (и файл) по адресу https://github.com/" +"archlinux/archinstall/issues" msgid "Do you really want to abort?" msgstr "Вы действительно хотите прекратить?" @@ -31,10 +36,13 @@ msgid "Desired hostname for the installation: " msgstr "Желаемое имя хоста для установки: " msgid "Username for required superuser with sudo privileges: " -msgstr "Имя пользователя для требуемого суперпользователя с привилегиями sudo: " +msgstr "" +"Имя пользователя для требуемого суперпользователя с привилегиями sudo: " msgid "Any additional users to install (leave blank for no users): " -msgstr "Любые дополнительные пользователи для установки (оставьте пустым, если пользователей нет): " +msgstr "" +"Любые дополнительные пользователи для установки (оставьте пустым, если " +"пользователей нет): " msgid "Should this user be a superuser (sudoer)?" msgstr "Должен ли этот пользователь быть суперпользователем (sudoer)?" @@ -51,38 +59,59 @@ msgstr "Выберите загрузчик" msgid "Choose an audio server" msgstr "Выберите звуковой сервер" -msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." -msgstr "Устанавливаются только такие пакеты, как base, base-devel, linux, linux-firmware, efibootmgr и дополнительные пакеты профиля." +msgid "" +"Only packages such as base, base-devel, linux, linux-firmware, efibootmgr " +"and optional profile packages are installed." +msgstr "" +"Устанавливаются только такие пакеты, как base, base-devel, linux, linux-" +"firmware, efibootmgr и дополнительные пакеты профиля." -msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." -msgstr "Если вы хотите использовать веб-браузер, например, firefox или chromium, вы можете указать его в следующем запросе." +msgid "" +"If you desire a web browser, such as firefox or chromium, you may specify it " +"in the following prompt." +msgstr "" +"Если вы хотите использовать веб-браузер, например, firefox или chromium, вы " +"можете указать его в следующем запросе." -msgid "Write additional packages to install (space separated, leave blank to skip): " -msgstr "Напишите дополнительные пакеты для установки (разделите пробелами, оставьте пустым, чтобы пропустить): " +msgid "" +"Write additional packages to install (space separated, leave blank to skip): " +msgstr "" +"Напишите дополнительные пакеты для установки (разделите пробелами, оставьте " +"пустым, чтобы пропустить): " msgid "Copy ISO network configuration to installation" msgstr "Копировать сетевую конфигурацию ISO в установку" -msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" -msgstr "Использовать NetworkManager (необходим для графической настройки интернета в GNOME и KDE)" +msgid "" +"Use NetworkManager (necessary for configuring internet graphically in GNOME " +"and KDE)" +msgstr "" +"Использовать NetworkManager (необходим для графической настройки интернета в " +"GNOME и KDE)" msgid "Select one network interface to configure" msgstr "Выберите один сетевой интерфейс для настройки" -msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" -msgstr "Выберите режим для конфигурации \"{}\" или пропустите, чтобы использовать режим по умолчанию \"{}\"." +msgid "" +"Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" +msgstr "" +"Выберите режим для конфигурации \"{}\" или пропустите, чтобы использовать " +"режим по умолчанию \"{}\"." msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " msgstr "Введите IP-адрес и подсеть для {} (пример: 192.168.0.5/24): " msgid "Enter your gateway (router) IP address or leave blank for none: " -msgstr "Введите IP-адрес вашего шлюза (маршрутизатора) или оставьте пустым, если его нет: " +msgstr "" +"Введите IP-адрес вашего шлюза (маршрутизатора) или оставьте пустым, если его " +"нет: " msgid "Enter your DNS servers (space separated, blank for none): " msgstr "Введите ваши DNS-серверы (через пробел, пустой - нет): " msgid "Select which filesystem your main partition should use" -msgstr "Выберите, какую файловую систему должен использовать ваш основной раздел" +msgstr "" +"Выберите, какую файловую систему должен использовать ваш основной раздел" msgid "Current partition layout" msgstr "Текущая разметка разделов" @@ -97,11 +126,16 @@ msgstr "" msgid "Enter a desired filesystem type for the partition" msgstr "Введите желаемый тип файловой системы для раздела" -msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " -msgstr "Введите начальное значение (в раздельных блоках: с, ГБ, % и т.д.; по умолчанию: {}): " +msgid "" +"Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " +msgstr "" +"Введите начальное значение (в раздельных блоках: с, ГБ, % и т.д.; по " +"умолчанию: {}): " msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " -msgstr "Введите конечное значение (в раздельных блоках: с, Гб, % и т.д.; например: {}): " +msgstr "" +"Введите конечное значение (в раздельных блоках: с, Гб, % и т.д.; например: " +"{}): " msgid "{} contains queued partitions, this will remove those, are you sure?" msgstr "{} содержит разделы в очереди, это удалит их, вы уверены?" @@ -124,11 +158,17 @@ msgstr "" "\n" "Выберите по индексу, какой раздел куда монтировать" -msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * Точки монтирования разделов являются относительными внутри установки, например, загрузочный будет /boot." +msgid "" +" * Partition mount-points are relative to inside the installation, the boot " +"would be /boot as an example." +msgstr "" +" * Точки монтирования разделов являются относительными внутри установки, " +"например, загрузочный будет /boot." msgid "Select where to mount partition (leave blank to remove mountpoint): " -msgstr "Выберите куда монтировать раздел (оставьте пустым, чтобы удалить точку монтирования): " +msgstr "" +"Выберите куда монтировать раздел (оставьте пустым, чтобы удалить точку " +"монтирования): " msgid "" "{}\n" @@ -173,16 +213,25 @@ msgid "Archinstall language" msgstr "Язык Archinstall" msgid "Wipe all selected drives and use a best-effort default partition layout" -msgstr "Стереть все выбранные диски и использовать оптимальную схему разделов по умолчанию" +msgstr "" +"Стереть все выбранные диски и использовать оптимальную схему разделов по " +"умолчанию" -msgid "Select what to do with each individual drive (followed by partition usage)" -msgstr "Выберите, что делать с каждым отдельным диском (с последующим использованием разделов)" +msgid "" +"Select what to do with each individual drive (followed by partition usage)" +msgstr "" +"Выберите, что делать с каждым отдельным диском (с последующим использованием " +"разделов)" msgid "Select what you wish to do with the selected block devices" msgstr "Выберите, что вы хотите сделать с выбранными блочными устройствами" -msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments" -msgstr "Это список предварительно запрограммированных профилей, они могут облегчить установку таких вещей, как окружения рабочего стола" +msgid "" +"This is a list of pre-programmed profiles, they might make it easier to " +"install things like desktop environments" +msgstr "" +"Это список предварительно запрограммированных профилей, они могут облегчить " +"установку таких вещей, как окружения рабочего стола" msgid "Select keyboard layout" msgstr "Выберите раскладку клавиатуры" @@ -191,16 +240,29 @@ msgid "Select one of the regions to download packages from" msgstr "Выберите один из регионов для загрузки пакетов" msgid "Select one or more hard drives to use and configure" -msgstr "Выберите один или несколько жестких дисков для использования и настройте их" +msgstr "" +"Выберите один или несколько жестких дисков для использования и настройте их" -msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." -msgstr "Для наилучшей совместимости с оборудованием AMD вы можете использовать либо все варианты с открытым исходным кодом, либо AMD / ATI." +msgid "" +"For the best compatibility with your AMD hardware, you may want to use " +"either the all open-source or AMD / ATI options." +msgstr "" +"Для наилучшей совместимости с оборудованием AMD вы можете использовать либо " +"все варианты с открытым исходным кодом, либо AMD / ATI." -msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" -msgstr "Для лучшей совместимости с оборудованием Intel вы можете использовать либо все варианты с открытым исходным кодом, либо Intel.\n" +msgid "" +"For the best compatibility with your Intel hardware, you may want to use " +"either the all open-source or Intel options.\n" +msgstr "" +"Для лучшей совместимости с оборудованием Intel вы можете использовать либо " +"все варианты с открытым исходным кодом, либо Intel.\n" -msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" -msgstr "Для наилучшей совместимости с оборудованием Nvidia вы можете использовать проприетарный драйвер Nvidia.\n" +msgid "" +"For the best compatibility with your Nvidia hardware, you may want to use " +"the Nvidia proprietary driver.\n" +msgstr "" +"Для наилучшей совместимости с оборудованием Nvidia вы можете использовать " +"проприетарный драйвер Nvidia.\n" msgid "" "\n" @@ -209,13 +271,15 @@ msgid "" msgstr "" "\n" "\n" -"Выберите графический драйвер или оставьте пустым, чтобы установить все драйверы с открытым исходным кодом" +"Выберите графический драйвер или оставьте пустым, чтобы установить все " +"драйверы с открытым исходным кодом" msgid "All open-source (default)" msgstr "Все с открытым исходным кодом (по умолчанию)" msgid "Choose which kernels to use or leave blank for default \"{}\"" -msgstr "Выберите, какие ядра использовать, или оставьте пустым по умолчанию \"{}\"." +msgstr "" +"Выберите, какие ядра использовать, или оставьте пустым по умолчанию \"{}\"." msgid "Choose which locale language to use" msgstr "Выберите, какой язык локали использовать" @@ -232,8 +296,12 @@ msgstr "Выберите один или несколько из приведе msgid "Adding partition...." msgstr "Добавление раздела...." -msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." -msgstr "Чтобы продолжить, вам нужно ввести действительный fs-тип. Смотрите `man parted` для правильных fs-типов." +msgid "" +"You need to enter a valid fs-type in order to continue. See `man parted` for " +"valid fs-type's." +msgstr "" +"Чтобы продолжить, вам нужно ввести действительный fs-тип. Смотрите `man " +"parted` для правильных fs-типов." msgid "Error: Listing profiles on URL \"{}\" resulted in:" msgstr "Ошибка: Перечисление профилей по URL \"{}\" привело к:" @@ -305,7 +373,8 @@ msgid "" "Do you wish to continue?" msgstr "" "Вы решили пропустить выбор жесткого диска\n" -"и будете использовать любой диск, смонтированный по адресу {} (экспериментально)\n" +"и будете использовать любой диск, смонтированный по адресу {} " +"(экспериментально)\n" "ПРЕДУПРЕЖДЕНИЕ: Archinstall не будет проверять пригодность этой установки.\n" "Вы хотите продолжить?" @@ -325,13 +394,16 @@ msgid "Assign mount-point for a partition" msgstr "Назначить точку монтирования для раздела" msgid "Mark/Unmark a partition to be formatted (wipes data)" -msgstr "Пометить/снять отметку с раздела, который будет отформатирован (стирание данных)" +msgstr "" +"Пометить/снять отметку с раздела, который будет отформатирован (стирание " +"данных)" msgid "Mark/Unmark a partition as encrypted" msgstr "Пометить/снять отметку с раздела как зашифрованный" msgid "Mark/Unmark a partition as bootable (automatic for /boot)" -msgstr "Пометить/снять отметку с раздела как загрузочный (автоматически для /boot)" +msgstr "" +"Пометить/снять отметку с раздела как загрузочный (автоматически для /boot)" msgid "Set desired filesystem for a partition" msgstr "Установите желаемую файловую систему для раздела" @@ -371,7 +443,8 @@ msgid "Enter a encryption password for {}" msgstr "Введите пароль шифрования для {}" msgid "Enter disk encryption password (leave blank for no encryption): " -msgstr "Введите пароль шифрования диска (оставьте пустым для отсутствия шифрования): " +msgstr "" +"Введите пароль шифрования диска (оставьте пустым для отсутствия шифрования): " msgid "Create a required super-user with sudo privileges: " msgstr "Создайте необходимого суперпользователя с привилегиями sudo: " @@ -382,31 +455,43 @@ msgstr "Введите пароль root (оставьте пустым, что msgid "Password for user \"{}\": " msgstr "Пароль для пользователя \"{}\": " -msgid "Verifying that additional packages exist (this might take a few seconds)" -msgstr "Проверка наличия дополнительных пакетов (это может занять несколько секунд)" +msgid "" +"Verifying that additional packages exist (this might take a few seconds)" +msgstr "" +"Проверка наличия дополнительных пакетов (это может занять несколько секунд)" -msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" -msgstr "Вы хотите использовать автоматическую синхронизацию времени (NTP) с серверами времени по умолчанию?\n" +msgid "" +"Would you like to use automatic time synchronization (NTP) with the default " +"time servers?\n" +msgstr "" +"Вы хотите использовать автоматическую синхронизацию времени (NTP) с " +"серверами времени по умолчанию?\n" msgid "" -"Hardware time and other post-configuration steps might be required in order for NTP to work.\n" +"Hardware time and other post-configuration steps might be required in order " +"for NTP to work.\n" "For more information, please check the Arch wiki" msgstr "" -"Для работы NTP может потребоваться аппаратное время и другие шаги после конфигурации.\n" +"Для работы NTP может потребоваться аппаратное время и другие шаги после " +"конфигурации.\n" "Для получения дополнительной информации, пожалуйста, ознакомьтесь с ArchWiki" msgid "Enter a username to create an additional user (leave blank to skip): " -msgstr "Введите имя пользователя для создания дополнительного пользователя (оставьте пустым, чтобы пропустить): " +msgstr "" +"Введите имя пользователя для создания дополнительного пользователя (оставьте " +"пустым, чтобы пропустить): " msgid "Use ESC to skip\n" msgstr "Используйте ESC, чтобы пропустить\n" msgid "" "\n" -" Choose an object from the list, and select one of the available actions for it to execute" +" Choose an object from the list, and select one of the available actions for " +"it to execute" msgstr "" "\n" -" Выберите объект из списка и выберите одно из доступных действий для его выполнения" +" Выберите объект из списка и выберите одно из доступных действий для его " +"выполнения" msgid "Cancel" msgstr "Отменить" @@ -442,11 +527,17 @@ msgstr "" "\n" "Это выбранная вами конфигурация:" -msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." -msgstr "Pacman уже запущен, ожидание его завершения составляет максимум 10 минут." +msgid "" +"Pacman is already running, waiting maximum 10 minutes for it to terminate." +msgstr "" +"Pacman уже запущен, ожидание его завершения составляет максимум 10 минут." -msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." -msgstr "Существовавшая ранее блокировка pacman не завершилась. Пожалуйста, очистите все существующие сессии pacman перед использованием archinstall." +msgid "" +"Pre-existing pacman lock never exited. Please clean up any existing pacman " +"sessions before using archinstall." +msgstr "" +"Существовавшая ранее блокировка pacman не завершилась. Пожалуйста, очистите " +"все существующие сессии pacman перед использованием archinstall." msgid "Choose which optional additional repositories to enable" msgstr "Выберите, какие дополнительные репозитории следует включить" @@ -537,7 +628,8 @@ msgid "Missing configurations:\n" msgstr "Отсутствующие конфигурации:\n" msgid "Either root-password or at least 1 superuser must be specified" -msgstr "Должен быть указан либо пароль root, либо как минимум 1 суперпользователь" +msgstr "" +"Должен быть указан либо пароль root, либо как минимум 1 суперпользователь" msgid "Manage superuser accounts: " msgstr "Управление учетными записями суперпользователей: " @@ -597,8 +689,12 @@ msgstr "Хотите ли вы использовать сжатие BTRFS?" msgid "Would you like to create a separate partition for /home?" msgstr "Хотите ли вы создать отдельный раздел для /home?" -msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" -msgstr "Выбранные диски не имеют минимальной емкости, необходимой для автоматического предложения\n" +msgid "" +"The selected drives do not have the minimum capacity required for an " +"automatic suggestion\n" +msgstr "" +"Выбранные диски не имеют минимальной емкости, необходимой для " +"автоматического предложения\n" msgid "Minimum capacity for /home partition: {}GB\n" msgstr "Минимальный размер раздела /home: {}GB\n" @@ -625,7 +721,9 @@ msgid "No iface specified for manual configuration" msgstr "Не указан iface для ручной настройки" msgid "Manual nic configuration with no auto DHCP requires an IP address" -msgstr "Ручная конфигурация сетевого адаптера без автоматического DHCP требует IP-адреса" +msgstr "" +"Ручная конфигурация сетевого адаптера без автоматического DHCP требует IP-" +"адреса" msgid "Add interface" msgstr "Добавить интерфейс" @@ -645,44 +743,74 @@ msgstr "Ручная конфигурация" msgid "Mark/Unmark a partition as compressed (btrfs only)" msgstr "Пометить/снять отметку с раздела как сжатый (только для btrfs)" -msgid "The password you are using seems to be weak, are you sure you want to use it?" -msgstr "Пароль, который вы используете, кажется слабым, вы уверены, что хотите его использовать?" +msgid "" +"The password you are using seems to be weak, are you sure you want to use it?" +msgstr "" +"Пароль, который вы используете, кажется слабым, вы уверены, что хотите его " +"использовать?" -msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" -msgstr "Предоставляет выбор окружений рабочего стола и тайловых оконных менеджеров, например, gnome, kde, sway" +msgid "" +"Provides a selection of desktop environments and tiling window managers, e." +"g. gnome, kde, sway" +msgstr "" +"Предоставляет выбор окружений рабочего стола и тайловых оконных менеджеров, " +"например, gnome, kde, sway" msgid "Select your desired desktop environment" msgstr "Выберите желаемое окружение рабочего стола" -msgid "A very basic installation that allows you to customize Arch Linux as you see fit." -msgstr "Очень базовая установка, позволяющая настроить Arch Linux по своему усмотрению." +msgid "" +"A very basic installation that allows you to customize Arch Linux as you see " +"fit." +msgstr "" +"Очень базовая установка, позволяющая настроить Arch Linux по своему " +"усмотрению." -msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" -msgstr "Предоставляет выбор различных пакетов сервера для установки и включения, например, httpd, nginx, mariadb" +msgid "" +"Provides a selection of various server packages to install and enable, e.g. " +"httpd, nginx, mariadb" +msgstr "" +"Предоставляет выбор различных пакетов сервера для установки и включения, " +"например, httpd, nginx, mariadb" -msgid "Choose which servers to install, if none then a minimal installation will be done" -msgstr "Выберите серверы для установки, если их нет, то будет выполнена минимальная установка" +msgid "" +"Choose which servers to install, if none then a minimal installation will be " +"done" +msgstr "" +"Выберите серверы для установки, если их нет, то будет выполнена минимальная " +"установка" msgid "Installs a minimal system as well as xorg and graphics drivers." -msgstr "Устанавливает минимальную систему, а также xorg и графические драйверы." +msgstr "" +"Устанавливает минимальную систему, а также xorg и графические драйверы." msgid "Press Enter to continue." msgstr "Нажмите Enter, чтобы продолжить." -msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?" -msgstr "Хотите ли вы использовать chroot в новой созданной установке и выполнить настройку после установки?" +msgid "" +"Would you like to chroot into the newly created installation and perform " +"post-installation configuration?" +msgstr "" +"Хотите ли вы использовать chroot в новой созданной установке и выполнить " +"настройку после установки?" msgid "Are you sure you want to reset this setting?" msgstr "Вы уверены, что хотите сбросить эту настройку?" msgid "Select one or more hard drives to use and configure\n" -msgstr "Выберите один или несколько жестких дисков для использования и настройки\n" +msgstr "" +"Выберите один или несколько жестких дисков для использования и настройки\n" msgid "Any modifications to the existing setting will reset the disk layout!" -msgstr "Любые изменения существующей настройки приведут к сбросу разметки диска!" +msgstr "" +"Любые изменения существующей настройки приведут к сбросу разметки диска!" -msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" -msgstr "Если вы сбросите выбор жесткого диска, это также сбросит текущую разметку диска. Вы уверены?" +msgid "" +"If you reset the harddrive selection this will also reset the current disk " +"layout. Are you sure?" +msgstr "" +"Если вы сбросите выбор жесткого диска, это также сбросит текущую разметку " +"диска. Вы уверены?" msgid "Save and exit" msgstr "Сохранить и выйти" @@ -728,8 +856,12 @@ msgstr "Добавить: " msgid "Value: " msgstr "Значение: " -msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" -msgstr "Вы можете не выбирать диск и разметку и использовать любой диск, смонтированный в /mnt (экспериментально)" +msgid "" +"You can skip selecting a drive and partitioning and use whatever drive-setup " +"is mounted at /mnt (experimental)" +msgstr "" +"Вы можете не выбирать диск и разметку и использовать любой диск, " +"смонтированный в /mnt (экспериментально)" msgid "Select one of the disks or skip and use /mnt as default" msgstr "Выберите один из дисков или пропустите и используйте /mnt по умолчанию" @@ -752,8 +884,12 @@ msgstr "Свободное место" msgid "Bus-type" msgstr "Тип шины" -msgid "Either root-password or at least 1 user with sudo privileges must be specified" -msgstr "Должен быть указан либо пароль root, либо хотя бы 1 пользователь с привилегиями sudo" +msgid "" +"Either root-password or at least 1 user with sudo privileges must be " +"specified" +msgstr "" +"Должен быть указан либо пароль root, либо хотя бы 1 пользователь с " +"привилегиями sudo" msgid "Enter username (leave blank to skip): " msgstr "Введите имя пользователя (оставьте пустым, чтобы пропустить): " @@ -791,8 +927,12 @@ msgstr "Удалить подтом" msgid "Configured {} interfaces" msgstr "Настроено интерфейсов: {}" -msgid "This option enables the number of parallel downloads that can occur during installation" -msgstr "Этот параметр определяет количество параллельных загрузок, которые могут происходить во время установки" +msgid "" +"This option enables the number of parallel downloads that can occur during " +"installation" +msgstr "" +"Этот параметр определяет количество параллельных загрузок, которые могут " +"происходить во время установки" msgid "" "Enter the number of parallel downloads to be enabled.\n" @@ -803,18 +943,34 @@ msgstr "" " (Введите значение от 1 до {})\n" "Примечание:" -msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" -msgstr " - Минимальное значение: {} ( Позволяет {} параллельную загрузку, позволяет {} загрузки одновременно )" +msgid "" +" - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads " +"at a time )" +msgstr "" +" - Минимальное значение: {} ( Позволяет {} параллельную загрузку, позволяет " +"{} загрузки одновременно )" -msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" -msgstr " - Минимальное значение: 1 ( Позволяет 1 параллельную загрузку, позволяет 2 загрузки одновременно )" +msgid "" +" - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a " +"time )" +msgstr "" +" - Минимальное значение: 1 ( Позволяет 1 параллельную загрузку, позволяет 2 " +"загрузки одновременно )" -msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" -msgstr " - Отключить/по умолчанию: 0 ( Отключает параллельную загрузку, позволяет только 1 загрузку за один раз )" +msgid "" +" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 " +"download at a time )" +msgstr "" +" - Отключить/по умолчанию: 0 ( Отключает параллельную загрузку, позволяет " +"только 1 загрузку за один раз )" #, python-brace-format -msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "Неверный ввод! Повторите попытку с правильным вводом [1 - {max_downloads}, или 0 - отключить]" +msgid "" +"Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to " +"disable]" +msgstr "" +"Неверный ввод! Повторите попытку с правильным вводом [1 - {max_downloads}, " +"или 0 - отключить]" msgid "Parallel Downloads" msgstr "Параллельные загрузки" @@ -831,14 +987,20 @@ msgstr "TAB, чтобы выбрать" msgid "[Default value: 0] > " msgstr "[Значение по умолчанию: 0] > " -msgid "To be able to use this translation, please install a font manually that supports the language." -msgstr "Чтобы иметь возможность использовать этот перевод, пожалуйста, установите вручную шрифт, поддерживающий данный язык." +msgid "" +"To be able to use this translation, please install a font manually that " +"supports the language." +msgstr "" +"Чтобы иметь возможность использовать этот перевод, пожалуйста, установите " +"вручную шрифт, поддерживающий данный язык." msgid "The font should be stored as {}" msgstr "Шрифт должен быть сохранен как {}" msgid "Archinstall requires root privileges to run. See --help for more." -msgstr "Для запуска Archinstall требуются привилегии root. Для получения дополнительной информации смотрите --help." +msgstr "" +"Для запуска Archinstall требуются привилегии root. Для получения " +"дополнительной информации смотрите --help." msgid "Select an execution mode" msgstr "Выберите режим выполнения" @@ -846,14 +1008,22 @@ msgstr "Выберите режим выполнения" msgid "Unable to fetch profile from specified url: {}" msgstr "Невозможно получить профиль из указанного url: {}" -msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" -msgstr "Профили должны иметь уникальное имя, но найдены определения профиля с дублирующимся именем: {}" +msgid "" +"Profiles must have unique name, but profile definitions with duplicate name " +"found: {}" +msgstr "" +"Профили должны иметь уникальное имя, но найдены определения профиля с " +"дублирующимся именем: {}" msgid "Select one or more devices to use and configure" msgstr "Выберите одно или несколько устройств для использования и настройки" -msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" -msgstr "Если вы сбросите выбор устройства, это также сбросит текущую разметку дисков. Вы уверены?" +msgid "" +"If you reset the device selection this will also reset the current disk " +"layout. Are you sure?" +msgstr "" +"Если вы сбросите выбор устройства, это также сбросит текущую разметку " +"дисков. Вы уверены?" msgid "Existing Partitions" msgstr "Существующие разделы" @@ -870,8 +1040,12 @@ msgstr "Минимальный размер раздела /home: {}GiB\n" msgid "Minimum capacity for Arch Linux partition: {}GiB" msgstr "Минимальный размер раздела Arch Linux: {}GiB" -msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" -msgstr "Это список предварительно запрограммированных профилей, они могут облегчить установку таких вещей, как окружения рабочего стола" +msgid "" +"This is a list of pre-programmed profiles_bck, they might make it easier to " +"install things like desktop environments" +msgstr "" +"Это список предварительно запрограммированных профилей, они могут облегчить " +"установку таких вещей, как окружения рабочего стола" msgid "Current profile selection" msgstr "Текущий выбор профиля" @@ -903,14 +1077,26 @@ msgstr "Удалить раздел" msgid "Partition" msgstr "Раздел" -msgid "This partition is currently encrypted, to format it a filesystem has to be specified" -msgstr "Этот раздел в настоящее время зашифрован, для его форматирования необходимо указать файловую систему" +msgid "" +"This partition is currently encrypted, to format it a filesystem has to be " +"specified" +msgstr "" +"Этот раздел в настоящее время зашифрован, для его форматирования необходимо " +"указать файловую систему" -msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr "Точки монтирования разделов являются относительными внутри установки, например, загрузочный раздел будет /boot." +msgid "" +"Partition mount-points are relative to inside the installation, the boot " +"would be /boot as an example." +msgstr "" +"Точки монтирования разделов являются относительными внутри установки, " +"например, загрузочный раздел будет /boot." -msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." -msgstr "Если установлена точка монтирования /boot, то раздел также будет помечен как загрузочный." +msgid "" +"If mountpoint /boot is set, then the partition will also be marked as " +"bootable." +msgstr "" +"Если установлена точка монтирования /boot, то раздел также будет помечен как " +"загрузочный." msgid "Mountpoint: " msgstr "Точка монтирования: " @@ -924,8 +1110,11 @@ msgstr "Всего секторов: {}" msgid "Enter the start sector (default: {}): " msgstr "Введите начальный сектор (по умолчанию: {}): " -msgid "Enter the end sector of the partition (percentage or block number, default: {}): " -msgstr "Введите конечный сектор раздела (процент или номер блока, по умолчанию: {}): " +msgid "" +"Enter the end sector of the partition (percentage or block number, default: " +"{}): " +msgstr "" +"Введите конечный сектор раздела (процент или номер блока, по умолчанию: {}): " msgid "This will remove all newly added partitions, continue?" msgstr "Это приведет к удалению всех вновь добавленных разделов, продолжить?" @@ -991,13 +1180,19 @@ msgid "Back" msgstr "Назад" msgid "Please chose which greeter to install for the chosen profiles: {}" -msgstr "Пожалуйста, выберите, какой экран приветствия установить для выбранных профилей: {}" +msgstr "" +"Пожалуйста, выберите, какой экран приветствия установить для выбранных " +"профилей: {}" msgid "Environment type: {}" msgstr "Тип окружения рабочего стола: {}" -msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" -msgstr "Проприетарный драйвер Nvidia не поддерживается Sway. Вполне вероятно, что вы столкнетесь с проблемами, вы согласны с этим?" +msgid "" +"The proprietary Nvidia driver is not supported by Sway. It is likely that " +"you will run into issues, are you okay with that?" +msgstr "" +"Проприетарный драйвер Nvidia не поддерживается Sway. Вполне вероятно, что вы " +"столкнетесь с проблемами, вы согласны с этим?" msgid "Installed packages" msgstr "Устанавливаемые пакеты" @@ -1017,11 +1212,19 @@ msgstr "Имя профиля: " msgid "The profile name you entered is already in use. Try again" msgstr "Введенное вами имя профиля уже используется. Попробуйте еще раз" -msgid "Packages to be install with this profile (space separated, leave blank to skip): " -msgstr "Пакеты, которые будут установлены с этим профилем (разделенные пробелами, оставьте пустым, чтобы пропустить): " +msgid "" +"Packages to be install with this profile (space separated, leave blank to " +"skip): " +msgstr "" +"Пакеты, которые будут установлены с этим профилем (разделенные пробелами, " +"оставьте пустым, чтобы пропустить): " -msgid "Services to be enabled with this profile (space separated, leave blank to skip): " -msgstr "Службы, которые должны быть включены с помощью этого профиля (разделенные пробелами, оставьте пустым, чтобы пропустить): " +msgid "" +"Services to be enabled with this profile (space separated, leave blank to " +"skip): " +msgstr "" +"Службы, которые должны быть включены с помощью этого профиля (разделенные " +"пробелами, оставьте пустым, чтобы пропустить): " msgid "Should this profile be enabled for installation?" msgstr "Должен ли этот профиль быть включен для установки?" @@ -1034,10 +1237,15 @@ msgid "" "Select a graphics driver or leave blank to install all open-source drivers" msgstr "" "\n" -"Выберите графический драйвер или оставьте пустым, чтобы установить все драйверы с открытым исходным кодом" +"Выберите графический драйвер или оставьте пустым, чтобы установить все " +"драйверы с открытым исходным кодом" -msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" -msgstr "Sway необходим доступ к вашему компьютеру (набор аппаратных устройств, т.е. клавиатура, мышь и т.д.)" +msgid "" +"Sway needs access to your seat (collection of hardware devices i.e. " +"keyboard, mouse, etc)" +msgstr "" +"Sway необходим доступ к вашему компьютеру (набор аппаратных устройств, т.е. " +"клавиатура, мышь и т.д.)" msgid "" "\n" @@ -1103,10 +1311,12 @@ msgid "Save user configuration (including disk layout)" msgstr "Сохранить конфигурацию пользователя (включая разметку диска)" msgid "" -"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Enter a directory for the configuration(s) to be saved (tab completion " +"enabled)\n" "Save directory: " msgstr "" -"Введите каталог для сохранения конфигурации (-ций) (включено заполнение вкладок)\n" +"Введите каталог для сохранения конфигурации (-ций) (включено заполнение " +"вкладок)\n" "Каталог сохранения: " msgid "" @@ -1127,86 +1337,106 @@ msgstr "Зеркала" msgid "Mirror regions" msgstr "Регионы зеркала" -msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" -msgstr " - Максимальное значение: {} ( Позволяет {} параллельных загрузок, позволяет {max_downloads+1} загрузок одновременно )" +msgid "" +" - Maximum value : {} ( Allows {} parallel downloads, allows " +"{max_downloads+1} downloads at a time )" +msgstr "" +" - Максимальное значение: {} ( Позволяет {} параллельных загрузок, позволяет " +"{max_downloads+1} загрузок одновременно )" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" -msgstr "Неверный ввод! Повторите попытку с правильным вводом [1 - {}, или 0 - отключить]" +msgstr "" +"Неверный ввод! Повторите попытку с правильным вводом [1 - {}, или 0 - " +"отключить]" msgid "Locales" msgstr "Локализации" -msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" -msgstr "Использовать NetworkManager (необходим для графической настройки интернета в GNOME и KDE)" +msgid "" +"Use NetworkManager (necessary to configure internet graphically in GNOME and " +"KDE)" +msgstr "" +"Использовать NetworkManager (необходим для графической настройки интернета в " +"GNOME и KDE)" -#, fuzzy msgid "Total: {} / {}" -msgstr "Весь размер: {}" +msgstr "Весь размер: {} / {}" msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." msgstr "" +"Все вводимые значения могут иметь суффикс с единицей измерения: Б, Кб, КиБ, " +"Мб, МиБ..." msgid "If no unit is provided, the value is interpreted as sectors" msgstr "" +"Если единица измерения не указана, то значение интерпретируется как сектор" -#, fuzzy msgid "Enter start (default: sector {}): " -msgstr "Введите начальный сектор (по умолчанию: {}): " +msgstr "Введите начало (по умолчанию: сектор {}): " -#, fuzzy msgid "Enter end (default: {}): " -msgstr "Введите начальный сектор (по умолчанию: {}): " +msgstr "Введите конец (по умолчанию: сектор {}): " msgid "Unable to determine fido2 devices. Is libfido2 installed?" -msgstr "" +msgstr "Невозможно определить устройства fido2. Установлена ли libfido2?" msgid "Path" -msgstr "" +msgstr "Путь" msgid "Manufacturer" -msgstr "" +msgstr "Производитель" msgid "Product" -msgstr "" +msgstr "Продукт" -#, fuzzy, python-brace-format +#, python-brace-format msgid "Invalid configuration: {error}" -msgstr "Ручная конфигурация" +msgstr "Неверная конфигурация: {error}" msgid "Type" -msgstr "" +msgstr "Тип" -#, fuzzy -msgid "This option enables the number of parallel downloads that can occur during package downloads" -msgstr "Этот параметр определяет количество параллельных загрузок, которые могут происходить во время установки" +msgid "" +"This option enables the number of parallel downloads that can occur during " +"package downloads" +msgstr "" +"Этот параметр определяет количество параллельных загрузок, которые могут " +"происходить во время загрузки пакетов" -#, fuzzy msgid "" "Enter the number of parallel downloads to be enabled.\n" "\n" "Note:\n" msgstr "" "Введите количество параллельных загрузок, которые будут включены.\n" -" (Введите значение от 1 до {})\n" -"Примечание:" +"\n" +"Примечание:\n" -#, fuzzy -msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" -msgstr " - Минимальное значение: {} ( Позволяет {} параллельную загрузку, позволяет {} загрузки одновременно )" +msgid "" +" - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr "" +" - Максимальное рекомендуемое значение: {} ( Позволяет {} параллельных " +"загрузок одновременно )" -#, fuzzy -msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" -msgstr " - Отключить/по умолчанию: 0 ( Отключает параллельную загрузку, позволяет только 1 загрузку за один раз )" +msgid "" +" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 " +"download at a time )\n" +msgstr "" +" - Отключить/по умолчанию: 0 ( Отключает параллельную загрузку, позволяет " +"только 1 загрузку за один раз )\n" -#, fuzzy msgid "Invalid input! Try again with a valid input [or 0 to disable]" -msgstr "Неверный ввод! Повторите попытку с правильным вводом [1 - {}, или 0 - отключить]" +msgstr "" +"Неверный ввод! Повторите попытку с правильным вводом [ или 0, чтобы " +"отключить ]" -#, fuzzy -msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" -msgstr "Sway необходим доступ к вашему компьютеру (набор аппаратных устройств, т.е. клавиатура, мышь и т.д.)" +msgid "" +"Hyprland needs access to your seat (collection of hardware devices i.e. " +"keyboard, mouse, etc)" +msgstr "" +"Hyprland необходим доступ к вашему компьютеру ( набор аппаратных устройств, " +"т.е. клавиатура, мышь и т.д. )" -#, fuzzy msgid "" "\n" "\n" @@ -1214,7 +1444,10 @@ msgid "" msgstr "" "\n" "\n" -"Выберите опцию, чтобы предоставить Sway доступ к вашему оборудованию" +"Выберите опцию, чтобы предоставить Hyprland доступ к вашему оборудованию" -msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgid "" +"All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." msgstr "" +"Все вводимые значения могут иметь суффикс с единицей измерения: %, Б, Кб, " +"КиБ, Мб, МиБ..." -- cgit v1.2.3-70-g09d2 From 71048721ecc7b06b227080688bbf02950ffc1942 Mon Sep 17 00:00:00 2001 From: Ansh Goel <117522662+ATamblingPoder@users.noreply.github.com> Date: Sun, 1 Oct 2023 15:21:28 +0530 Subject: Added first Hindi language support (#2134) --- archinstall/locales/hi/LC_MESSAGES/base.mo | Bin 0 -> 978 bytes archinstall/locales/hi/LC_MESSAGES/base.po | 1144 ++++++++++++++++++++++++++++ 2 files changed, 1144 insertions(+) create mode 100644 archinstall/locales/hi/LC_MESSAGES/base.mo create mode 100644 archinstall/locales/hi/LC_MESSAGES/base.po diff --git a/archinstall/locales/hi/LC_MESSAGES/base.mo b/archinstall/locales/hi/LC_MESSAGES/base.mo new file mode 100644 index 00000000..6be9c982 Binary files /dev/null and b/archinstall/locales/hi/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/hi/LC_MESSAGES/base.po b/archinstall/locales/hi/LC_MESSAGES/base.po new file mode 100644 index 00000000..ffd3cc44 --- /dev/null +++ b/archinstall/locales/hi/LC_MESSAGES/base.po @@ -0,0 +1,1144 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: hi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.0.1\n" + +msgid "[!] A log file has been created here: {} {}" +msgstr "" + +msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" +msgstr "" + +msgid "Do you really want to abort?" +msgstr "" + +msgid "And one more time for verification: " +msgstr "सत्यापन के लिए एक बार और: " + +msgid "Would you like to use swap on zram?" +msgstr "" + +msgid "Desired hostname for the installation: " +msgstr "" + +msgid "Username for required superuser with sudo privileges: " +msgstr "" + +msgid "Any additional users to install (leave blank for no users): " +msgstr "" + +msgid "Should this user be a superuser (sudoer)?" +msgstr "" + +msgid "Select a timezone" +msgstr "एक समयक्षेत्र चुनें" + +msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?" +msgstr "" + +msgid "Choose a bootloader" +msgstr "" + +msgid "Choose an audio server" +msgstr "" + +msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." +msgstr "" + +msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." +msgstr "" + +msgid "Write additional packages to install (space separated, leave blank to skip): " +msgstr "" + +msgid "Copy ISO network configuration to installation" +msgstr "" + +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" +msgstr "" + +msgid "Select one network interface to configure" +msgstr "" + +msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" +msgstr "" + +msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " +msgstr "" + +msgid "Enter your gateway (router) IP address or leave blank for none: " +msgstr "" + +msgid "Enter your DNS servers (space separated, blank for none): " +msgstr "" + +msgid "Select which filesystem your main partition should use" +msgstr "" + +msgid "Current partition layout" +msgstr "" + +msgid "" +"Select what to do with\n" +"{}" +msgstr "" + +msgid "Enter a desired filesystem type for the partition" +msgstr "" + +msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " +msgstr "" + +msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " +msgstr "" + +msgid "{} contains queued partitions, this will remove those, are you sure?" +msgstr "" + +msgid "" +"{}\n" +"\n" +"Select by index which partitions to delete" +msgstr "" + +msgid "" +"{}\n" +"\n" +"Select by index which partition to mount where" +msgstr "" + +msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr "" + +msgid "Select where to mount partition (leave blank to remove mountpoint): " +msgstr "" + +msgid "" +"{}\n" +"\n" +"Select which partition to mask for formatting" +msgstr "" + +msgid "" +"{}\n" +"\n" +"Select which partition to mark as encrypted" +msgstr "" + +msgid "" +"{}\n" +"\n" +"Select which partition to mark as bootable" +msgstr "" + +msgid "" +"{}\n" +"\n" +"Select which partition to set a filesystem on" +msgstr "" + +msgid "Enter a desired filesystem type for the partition: " +msgstr "" + +msgid "Archinstall language" +msgstr "" + +msgid "Wipe all selected drives and use a best-effort default partition layout" +msgstr "" + +msgid "Select what to do with each individual drive (followed by partition usage)" +msgstr "" + +msgid "Select what you wish to do with the selected block devices" +msgstr "" + +msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments" +msgstr "" + +msgid "Select keyboard layout" +msgstr "" + +msgid "Select one of the regions to download packages from" +msgstr "" + +msgid "Select one or more hard drives to use and configure" +msgstr "" + +msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." +msgstr "" + +msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" +msgstr "" + +msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" + +msgid "All open-source (default)" +msgstr "" + +msgid "Choose which kernels to use or leave blank for default \"{}\"" +msgstr "" + +msgid "Choose which locale language to use" +msgstr "" + +msgid "Choose which locale encoding to use" +msgstr "" + +msgid "Select one of the values shown below: " +msgstr "" + +msgid "Select one or more of the options below: " +msgstr "" + +msgid "Adding partition...." +msgstr "" + +msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." +msgstr "" + +msgid "Error: Listing profiles on URL \"{}\" resulted in:" +msgstr "" + +msgid "Error: Could not decode \"{}\" result as JSON:" +msgstr "" + +msgid "Keyboard layout" +msgstr "" + +msgid "Mirror region" +msgstr "" + +msgid "Locale language" +msgstr "" + +msgid "Locale encoding" +msgstr "" + +msgid "Drive(s)" +msgstr "" + +msgid "Disk layout" +msgstr "" + +msgid "Encryption password" +msgstr "" + +msgid "Swap" +msgstr "" + +msgid "Bootloader" +msgstr "" + +msgid "Root password" +msgstr "" + +msgid "Superuser account" +msgstr "" + +msgid "User account" +msgstr "" + +msgid "Profile" +msgstr "" + +msgid "Audio" +msgstr "" + +msgid "Kernels" +msgstr "" + +msgid "Additional packages" +msgstr "" + +msgid "Network configuration" +msgstr "" + +msgid "Automatic time sync (NTP)" +msgstr "" + +msgid "Install ({} config(s) missing)" +msgstr "" + +msgid "" +"You decided to skip harddrive selection\n" +"and 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?" +msgstr "" + +msgid "Re-using partition instance: {}" +msgstr "" + +msgid "Create a new partition" +msgstr "" + +msgid "Delete a partition" +msgstr "" + +msgid "Clear/Delete all partitions" +msgstr "" + +msgid "Assign mount-point for a partition" +msgstr "" + +msgid "Mark/Unmark a partition to be formatted (wipes data)" +msgstr "" + +msgid "Mark/Unmark a partition as encrypted" +msgstr "" + +msgid "Mark/Unmark a partition as bootable (automatic for /boot)" +msgstr "" + +msgid "Set desired filesystem for a partition" +msgstr "" + +msgid "Abort" +msgstr "" + +msgid "Hostname" +msgstr "" + +msgid "Not configured, unavailable unless setup manually" +msgstr "" + +msgid "Timezone" +msgstr "" + +msgid "Set/Modify the below options" +msgstr "" + +msgid "Install" +msgstr "" + +msgid "" +"Use ESC to skip\n" +"\n" +msgstr "" + +msgid "Suggest partition layout" +msgstr "" + +msgid "Enter a password: " +msgstr "" + +msgid "Enter a encryption password for {}" +msgstr "" + +msgid "Enter disk encryption password (leave blank for no encryption): " +msgstr "" + +msgid "Create a required super-user with sudo privileges: " +msgstr "" + +msgid "Enter root password (leave blank to disable root): " +msgstr "" + +msgid "Password for user \"{}\": " +msgstr "" + +msgid "Verifying that additional packages exist (this might take a few seconds)" +msgstr "" + +msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" +msgstr "" + +msgid "" +"Hardware time and other post-configuration steps might be required in order for NTP to work.\n" +"For more information, please check the Arch wiki" +msgstr "" + +msgid "Enter a username to create an additional user (leave blank to skip): " +msgstr "" + +msgid "Use ESC to skip\n" +msgstr "" + +msgid "" +"\n" +" Choose an object from the list, and select one of the available actions for it to execute" +msgstr "" + +msgid "Cancel" +msgstr "" + +msgid "Confirm and exit" +msgstr "" + +msgid "Add" +msgstr "" + +msgid "Copy" +msgstr "" + +msgid "Edit" +msgstr "" + +msgid "Delete" +msgstr "" + +msgid "Select an action for '{}'" +msgstr "" + +msgid "Copy to new key:" +msgstr "" + +msgid "Unknown nic type: {}. Possible values are {}" +msgstr "" + +msgid "" +"\n" +"This is your chosen configuration:" +msgstr "" + +msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." +msgstr "" + +msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." +msgstr "" + +msgid "Choose which optional additional repositories to enable" +msgstr "" + +msgid "Add a user" +msgstr "" + +msgid "Change password" +msgstr "" + +msgid "Promote/Demote user" +msgstr "" + +msgid "Delete User" +msgstr "" + +msgid "" +"\n" +"Define a new user\n" +msgstr "" + +msgid "User Name : " +msgstr "" + +msgid "Should {} be a superuser (sudoer)?" +msgstr "" + +msgid "Define users with sudo privilege: " +msgstr "" + +msgid "No network configuration" +msgstr "" + +msgid "Set desired subvolumes on a btrfs partition" +msgstr "" + +msgid "" +"{}\n" +"\n" +"Select which partition to set subvolumes on" +msgstr "" + +msgid "Manage btrfs subvolumes for current partition" +msgstr "" + +msgid "No configuration" +msgstr "" + +msgid "Save user configuration" +msgstr "" + +msgid "Save user credentials" +msgstr "" + +msgid "Save disk layout" +msgstr "" + +msgid "Save all" +msgstr "" + +msgid "Choose which configuration to save" +msgstr "" + +msgid "Enter a directory for the configuration(s) to be saved: " +msgstr "" + +msgid "Not a valid directory: {}" +msgstr "" + +msgid "The password you are using seems to be weak," +msgstr "" + +msgid "are you sure you want to use it?" +msgstr "" + +msgid "Optional repositories" +msgstr "" + +msgid "Save configuration" +msgstr "" + +msgid "Missing configurations:\n" +msgstr "" + +msgid "Either root-password or at least 1 superuser must be specified" +msgstr "" + +msgid "Manage superuser accounts: " +msgstr "" + +msgid "Manage ordinary user accounts: " +msgstr "" + +msgid " Subvolume :{:16}" +msgstr "" + +msgid " mounted at {:16}" +msgstr "" + +msgid " with option {}" +msgstr "" + +msgid "" +"\n" +" Fill the desired values for a new subvolume \n" +msgstr "" + +msgid "Subvolume name " +msgstr "" + +msgid "Subvolume mountpoint" +msgstr "" + +msgid "Subvolume options" +msgstr "" + +msgid "Save" +msgstr "" + +msgid "Subvolume name :" +msgstr "" + +msgid "Select a mount point :" +msgstr "" + +msgid "Select the desired subvolume options " +msgstr "" + +msgid "Define users with sudo privilege, by username: " +msgstr "" + +msgid "[!] A log file has been created here: {}" +msgstr "" + +msgid "Would you like to use BTRFS subvolumes with a default structure?" +msgstr "" + +msgid "Would you like to use BTRFS compression?" +msgstr "" + +msgid "Would you like to create a separate partition for /home?" +msgstr "" + +msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" +msgstr "" + +msgid "Minimum capacity for /home partition: {}GB\n" +msgstr "" + +msgid "Minimum capacity for Arch Linux partition: {}GB" +msgstr "" + +msgid "Continue" +msgstr "" + +msgid "yes" +msgstr "" + +msgid "no" +msgstr "" + +msgid "set: {}" +msgstr "" + +msgid "Manual configuration setting must be a list" +msgstr "" + +msgid "No iface specified for manual configuration" +msgstr "" + +msgid "Manual nic configuration with no auto DHCP requires an IP address" +msgstr "" + +msgid "Add interface" +msgstr "" + +msgid "Edit interface" +msgstr "" + +msgid "Delete interface" +msgstr "" + +msgid "Select interface to add" +msgstr "" + +msgid "Manual configuration" +msgstr "" + +msgid "Mark/Unmark a partition as compressed (btrfs only)" +msgstr "" + +msgid "The password you are using seems to be weak, are you sure you want to use it?" +msgstr "" + +msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" +msgstr "" + +msgid "Select your desired desktop environment" +msgstr "" + +msgid "A very basic installation that allows you to customize Arch Linux as you see fit." +msgstr "" + +msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" +msgstr "" + +msgid "Choose which servers to install, if none then a minimal installation will be done" +msgstr "" + +msgid "Installs a minimal system as well as xorg and graphics drivers." +msgstr "" + +msgid "Press Enter to continue." +msgstr "" + +msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?" +msgstr "" + +msgid "Are you sure you want to reset this setting?" +msgstr "" + +msgid "Select one or more hard drives to use and configure\n" +msgstr "" + +msgid "Any modifications to the existing setting will reset the disk layout!" +msgstr "" + +msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" +msgstr "" + +msgid "Save and exit" +msgstr "सहेजें और छोड़ें" + +msgid "" +"{}\n" +"contains queued partitions, this will remove those, are you sure?" +msgstr "" + +msgid "No audio server" +msgstr "" + +msgid "(default)" +msgstr "" + +msgid "Use ESC to skip" +msgstr "छोड़ने के लिए ESC का उपयोग करें" + +msgid "" +"Use CTRL+C to reset current selection\n" +"\n" +msgstr "" + +msgid "Copy to: " +msgstr "" + +msgid "Edit: " +msgstr "" + +msgid "Key: " +msgstr "" + +msgid "Edit {}: " +msgstr "" + +msgid "Add: " +msgstr "" + +msgid "Value: " +msgstr "" + +msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" +msgstr "" + +msgid "Select one of the disks or skip and use /mnt as default" +msgstr "" + +msgid "Select which partitions to mark for formatting:" +msgstr "" + +msgid "Use HSM to unlock encrypted drive" +msgstr "" + +msgid "Device" +msgstr "" + +msgid "Size" +msgstr "" + +msgid "Free space" +msgstr "खाली जगह" + +msgid "Bus-type" +msgstr "" + +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "" + +msgid "Enter username (leave blank to skip): " +msgstr "" + +msgid "The username you entered is invalid. Try again" +msgstr "" + +msgid "Should \"{}\" be a superuser (sudo)?" +msgstr "" + +msgid "Select which partitions to encrypt" +msgstr "" + +msgid "very weak" +msgstr "बहुत कमजोर" + +msgid "weak" +msgstr "कमजोर" + +msgid "moderate" +msgstr "मध्यम" + +msgid "strong" +msgstr "मज़बूत" + +msgid "Add subvolume" +msgstr "" + +msgid "Edit subvolume" +msgstr "" + +msgid "Delete subvolume" +msgstr "" + +msgid "Configured {} interfaces" +msgstr "" + +msgid "This option enables the number of parallel downloads that can occur during installation" +msgstr "" + +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +" (Enter a value between 1 to {})\n" +"Note:" +msgstr "" + +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr "" + +msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" +msgstr "" + +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" +msgstr "" + +#, python-brace-format +msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" +msgstr "" + +msgid "Parallel Downloads" +msgstr "" + +msgid "ESC to skip" +msgstr "" + +msgid "CTRL+C to reset" +msgstr "" + +msgid "TAB to select" +msgstr "" + +msgid "[Default value: 0] > " +msgstr "" + +msgid "To be able to use this translation, please install a font manually that supports the language." +msgstr "" + +msgid "The font should be stored as {}" +msgstr "" + +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "" + +msgid "Select an execution mode" +msgstr "" + +msgid "Unable to fetch profile from specified url: {}" +msgstr "" + +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" + +msgid "Select one or more devices to use and configure" +msgstr "" + +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "" + +msgid "Existing Partitions" +msgstr "" + +msgid "Select a partitioning option" +msgstr "" + +msgid "Enter the root directory of the mounted devices: " +msgstr "" + +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "" + +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "" + +msgid "This is a list of pre-programmed profiles_bck, they might make it easier to install things like desktop environments" +msgstr "" + +msgid "Current profile selection" +msgstr "" + +msgid "Remove all newly added partitions" +msgstr "" + +msgid "Assign mountpoint" +msgstr "" + +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "" + +msgid "Mark/Unmark as bootable" +msgstr "" + +msgid "Change filesystem" +msgstr "" + +msgid "Mark/Unmark as compressed" +msgstr "" + +msgid "Set subvolumes" +msgstr "" + +msgid "Delete partition" +msgstr "" + +msgid "Partition" +msgstr "" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "" + +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr "" + +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" + +msgid "Mountpoint: " +msgstr "" + +msgid "Current free sectors on device {}:" +msgstr "" + +msgid "Total sectors: {}" +msgstr "" + +msgid "Enter the start sector (default: {}): " +msgstr "" + +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "" + +msgid "This will remove all newly added partitions, continue?" +msgstr "" + +msgid "Partition management: {}" +msgstr "" + +msgid "Total length: {}" +msgstr "" + +msgid "Encryption type" +msgstr "" + +msgid "Partitions" +msgstr "" + +msgid "No HSM devices available" +msgstr "" + +msgid "Partitions to be encrypted" +msgstr "" + +msgid "Select disk encryption option" +msgstr "" + +msgid "Select a FIDO2 device to use for HSM" +msgstr "" + +msgid "Use a best-effort default partition layout" +msgstr "" + +msgid "Manual Partitioning" +msgstr "" + +msgid "Pre-mounted configuration" +msgstr "" + +msgid "Unknown" +msgstr "" + +msgid "Partition encryption" +msgstr "" + +msgid " ! Formatting {} in " +msgstr "" + +msgid " Back" +msgstr "" + +msgid "Disk encryption" +msgstr "" + +msgid "Configuration" +msgstr "" + +msgid "Password" +msgstr "पासवर्ड" + +msgid "All settings will be reset, are you sure?" +msgstr "" + +msgid "Back" +msgstr "" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "" + +msgid "Environment type: {}" +msgstr "" + +msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" +msgstr "" + +msgid "Installed packages" +msgstr "" + +msgid "Add profile" +msgstr "" + +msgid "Edit profile" +msgstr "" + +msgid "Delete profile" +msgstr "" + +msgid "Profile name: " +msgstr "" + +msgid "The profile name you entered is already in use. Try again" +msgstr "" + +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "" + +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "" + +msgid "Should this profile be enabled for installation?" +msgstr "" + +msgid "Create your own" +msgstr "" + +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "" + +msgid "Graphics driver" +msgstr "" + +msgid "Greeter" +msgstr "" + +msgid "Please chose which greeter to install" +msgstr "" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "" + +msgid "Disk configuration" +msgstr "" + +msgid "Profiles" +msgstr "" + +msgid "Finding possible directories to save configuration files ..." +msgstr "" + +msgid "Select directory (or directories) for saving configuration files" +msgstr "" + +msgid "Add a custom mirror" +msgstr "" + +msgid "Change custom mirror" +msgstr "" + +msgid "Delete custom mirror" +msgstr "" + +msgid "Enter name (leave blank to skip): " +msgstr "" + +msgid "Enter url (leave blank to skip): " +msgstr "" + +msgid "Select signature check option" +msgstr "" + +msgid "Select signature option" +msgstr "" + +msgid "Custom mirrors" +msgstr "" + +msgid "Defined" +msgstr "" + +msgid "Save user configuration (including disk layout)" +msgstr "" + +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "" + +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" + +msgid "Saving {} configuration files to {}" +msgstr "" + +msgid "Mirrors" +msgstr "" + +msgid "Mirror regions" +msgstr "" + +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a time )" +msgstr "" + +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "" + +msgid "Locales" +msgstr "" + +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "" + +msgid "Total: {} / {}" +msgstr "" + +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "" + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "" + +msgid "Enter start (default: sector {}): " +msgstr "" + +msgid "Enter end (default: {}): " +msgstr "" + +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "" + +msgid "Path" +msgstr "" + +msgid "Manufacturer" +msgstr "" + +msgid "Product" +msgstr "" + +#, python-brace-format +msgid "Invalid configuration: {error}" +msgstr "" + +msgid "Type" +msgstr "" + +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "" + +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" + +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr "" + +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr "" + +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "" + +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" + +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "" + +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "" -- cgit v1.2.3-70-g09d2 From 8257e9f73f6d8f233731b5632863a5d7a4ae827e Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Mon, 2 Oct 2023 01:50:20 -0400 Subject: Fix missing info for pre-mounted disk configuration (#2143) --- archinstall/lib/disk/device_handler.py | 9 ++++++++- archinstall/lib/disk/device_model.py | 9 +++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index f2835b7b..6927671d 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -83,7 +83,9 @@ class DeviceHandler(object): _PartitionInfo.from_partition( partition, fs_type, + lsblk_info.partn, lsblk_info.partuuid, + lsblk_info.uuid, lsblk_info.mountpoints, subvol_infos ) @@ -598,7 +600,12 @@ class DeviceHandler(object): path = Path(part_info.disk.device.path) part_mods.setdefault(path, []) part_mod = PartitionModification.from_existing_partition(part_info) - part_mod.mountpoint = mountpoint.root / mountpoint.relative_to(base_mountpoint) + if part_mod.mountpoint: + part_mod.mountpoint = mountpoint.root / mountpoint.relative_to(base_mountpoint) + else: + for subvol in part_mod.btrfs_subvols: + if sm := subvol.mountpoint: + subvol.mountpoint = sm.root / sm.relative_to(base_mountpoint) part_mods[path].append(part_mod) break diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index 6992bccb..26169485 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -308,7 +308,9 @@ class _PartitionInfo: start: Size length: Size flags: List[PartitionFlag] + partn: int partuuid: str + uuid: str disk: Disk mountpoints: List[Path] btrfs_subvol_infos: List[_BtrfsSubvolumeInfo] = field(default_factory=list) @@ -342,7 +344,9 @@ class _PartitionInfo: cls, partition: Partition, fs_type: Optional[FilesystemType], + partn: int, partuuid: str, + uuid: str, mountpoints: List[Path], btrfs_subvol_infos: List[_BtrfsSubvolumeInfo] = [] ) -> _PartitionInfo: @@ -370,7 +374,9 @@ class _PartitionInfo: start=start, length=length, flags=flags, + partn=partn, partuuid=partuuid, + uuid=uuid, disk=partition.disk, mountpoints=mountpoints, btrfs_subvol_infos=btrfs_subvol_infos @@ -713,6 +719,9 @@ class PartitionModification: length=partition_info.length, fs_type=partition_info.fs_type, dev_path=partition_info.path, + partn=partition_info.partn, + partuuid=partition_info.partuuid, + uuid=partition_info.uuid, flags=partition_info.flags, mountpoint=mountpoint, btrfs_subvols=subvol_mods -- cgit v1.2.3-70-g09d2 From a095e393d8517e99f8832c447fd2ef0902cb6ca6 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Mon, 2 Oct 2023 01:50:58 -0400 Subject: Fix ArchInstall ISO build (#2142) --- build_iso.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build_iso.sh b/build_iso.sh index 3955b41a..1049479c 100755 --- a/build_iso.sh +++ b/build_iso.sh @@ -6,7 +6,9 @@ packages_file="/tmp/archlive/packages.x86_64" # Packages to add to the archiso profile packages packages=( + gcc git + pkgconfig python python-pip python-build @@ -24,7 +26,7 @@ cat <<- _EOF_ | tee /tmp/archlive/airootfs/root/.zprofile rm -rf dist python -m build --wheel --no-isolation - pip install dist/archinstall*.whl + pip install dist/archinstall*.whl --break-system-packages echo "This is an unofficial ISO for development and testing of archinstall. No support will be provided." echo "This ISO was built from Git SHA $GITHUB_SHA" -- cgit v1.2.3-70-g09d2 From 5c903df55fac449baae1e9cc23b04f6beeb55364 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Mon, 2 Oct 2023 21:01:23 +1100 Subject: Simplify SysCommand decoding (#2121) --- archinstall/lib/disk/device_handler.py | 17 ++++----- archinstall/lib/disk/device_model.py | 11 +++--- archinstall/lib/disk/fido.py | 7 +--- archinstall/lib/general.py | 13 +++++-- archinstall/lib/installer.py | 23 +++++------ archinstall/lib/locale.py | 61 ----------------------------- archinstall/lib/locale/__init__.py | 12 ++++-- archinstall/lib/locale/locale.py | 61 ----------------------------- archinstall/lib/locale/locale_menu.py | 2 +- archinstall/lib/locale/utils.py | 70 ++++++++++++++++++++++++++++++++++ archinstall/lib/luks.py | 2 +- archinstall/lib/packages/packages.py | 2 +- 12 files changed, 117 insertions(+), 164 deletions(-) delete mode 100644 archinstall/lib/locale.py delete mode 100644 archinstall/lib/locale/locale.py create mode 100644 archinstall/lib/locale/utils.py diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index 6927671d..baed2f6f 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -154,20 +154,19 @@ class DeviceHandler(object): mountpoint = Path(common_prefix) try: - result = SysCommand(f'btrfs subvolume list {mountpoint}') + result = SysCommand(f'btrfs subvolume list {mountpoint}').decode() except SysCallError as err: debug(f'Failed to read btrfs subvolume information: {err}') return subvol_infos try: - if decoded := result.decode('utf-8'): - # ID 256 gen 16 top level 5 path @ - for line in decoded.splitlines(): - # expected output format: - # ID 257 gen 8 top level 5 path @home - name = Path(line.split(' ')[-1]) - sub_vol_mountpoint = lsblk_info.btrfs_subvol_info.get(name, None) - subvol_infos.append(_BtrfsSubvolumeInfo(name, sub_vol_mountpoint)) + # ID 256 gen 16 top level 5 path @ + for line in result.splitlines(): + # expected output format: + # ID 257 gen 8 top level 5 path @home + name = Path(line.split(' ')[-1]) + sub_vol_mountpoint = lsblk_info.btrfs_subvol_info.get(name, None) + subvol_infos.append(_BtrfsSubvolumeInfo(name, sub_vol_mountpoint)) except json.decoder.JSONDecodeError as err: error(f"Could not decode lsblk JSON: {result}") raise err diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index 26169485..4ac53b0c 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -1111,12 +1111,12 @@ def _fetch_lsblk_info(dev_path: Optional[Union[Path, str]] = None, retry: int = for retry_attempt in range(retry): try: - result = SysCommand(f'lsblk --json -b -o+{lsblk_fields} {dev_path}') + result = SysCommand(f'lsblk --json -b -o+{lsblk_fields} {dev_path}').decode() break except SysCallError as err: # Get the output minus the message/info from lsblk if it returns a non-zero exit code. if err.worker: - err_str = err.worker.decode('UTF-8') + err_str = err.worker.decode() debug(f'Error calling lsblk: {err_str}') else: raise err @@ -1127,10 +1127,9 @@ def _fetch_lsblk_info(dev_path: Optional[Union[Path, str]] = None, retry: int = time.sleep(1) try: - if decoded := result.decode('utf-8'): - block_devices = json.loads(decoded) - blockdevices = block_devices['blockdevices'] - return [LsblkInfo.from_json(device) for device in blockdevices] + block_devices = json.loads(result) + blockdevices = block_devices['blockdevices'] + return [LsblkInfo.from_json(device) for device in blockdevices] except json.decoder.JSONDecodeError as err: error(f"Could not decode lsblk JSON: {result}") raise err diff --git a/archinstall/lib/disk/fido.py b/archinstall/lib/disk/fido.py index 9eeba56a..49904c17 100644 --- a/archinstall/lib/disk/fido.py +++ b/archinstall/lib/disk/fido.py @@ -2,7 +2,7 @@ from __future__ import annotations import getpass from pathlib import Path -from typing import List, Optional +from typing import List from .device_model import PartitionModification, Fido2Device from ..general import SysCommand, SysCommandWorker, clear_vt100_escape_codes @@ -38,14 +38,11 @@ class Fido2: # down moving the cursor in the menu if not cls._loaded or reload: try: - ret: Optional[str] = SysCommand("systemd-cryptenroll --fido2-device=list").decode('UTF-8') + ret = SysCommand("systemd-cryptenroll --fido2-device=list").decode() except SysCallError: error('fido2 support is most likely not installed') raise ValueError('HSM devices can not be detected, is libfido2 installed?') - if not ret: - return [] - fido_devices: str = clear_vt100_escape_codes(ret) # type: ignore manufacturer_pos = 0 diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py index 71981fb6..3697cf2d 100644 --- a/archinstall/lib/general.py +++ b/archinstall/lib/general.py @@ -430,10 +430,15 @@ class SysCommand: return True - def decode(self, *args, **kwargs) -> Optional[str]: - if self.session: - return self.session._trace_log.decode(*args, **kwargs) - return None + def decode(self, encoding: str = 'utf-8', errors='backslashreplace', strip: bool = True) -> str: + if not self.session: + raise ValueError('No session available to decode') + + val = self.session._trace_log.decode(encoding, errors=errors) + + if strip: + return val.strip() + return val @property def exit_code(self) -> Optional[int]: diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 585389ed..ad98d9a8 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -131,7 +131,7 @@ class Installer: We need to wait for it before we continue since we opted in to use a custom mirror/region. """ info('Waiting for time sync (systemd-timesyncd.service) to complete.') - while SysCommand('timedatectl show --property=NTPSynchronized --value').decode().rstrip() != 'yes': + while SysCommand('timedatectl show --property=NTPSynchronized --value').decode() != 'yes': time.sleep(1) info('Waiting for automatic mirror selection (reflector) to complete.') @@ -282,7 +282,7 @@ class Installer: if enable_resume: resume_uuid = SysCommand(f'findmnt -no UUID -T {self.target}{file}').decode('UTF-8').strip() - resume_offset = SysCommand(f'/usr/bin/filefrag -v {self.target}{file}').decode('UTF-8').split('0:', 1)[1].split(":", 1)[1].split("..", 1)[0].strip() + resume_offset = SysCommand(f'/usr/bin/filefrag -v {self.target}{file}').decode().split('0:', 1)[1].split(":", 1)[1].split("..", 1)[0].strip() self._hooks.append('resume') self._kernel_params.append(f'resume=UUID={resume_uuid}') @@ -312,9 +312,6 @@ class Installer: except SysCallError as err: raise RequirementError(f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n Error: {err}') - if not gen_fstab: - raise RequirementError(f'Generating fstab returned empty value') - with open(fstab_path, 'a') as fp: fp.write(gen_fstab) @@ -1318,17 +1315,21 @@ TIMEOUT=5 if os.path.splitext(service_name)[1] not in ('.service', '.target', '.timer'): service_name += '.service' # Just to be safe - last_execution_time = b''.join(SysCommand(f"systemctl show --property=ActiveEnterTimestamp --no-pager {service_name}", environment_vars={'SYSTEMD_COLORS': '0'})) - last_execution_time = last_execution_time.lstrip(b'ActiveEnterTimestamp=').strip() + last_execution_time = SysCommand( + f"systemctl show --property=ActiveEnterTimestamp --no-pager {service_name}", + environment_vars={'SYSTEMD_COLORS': '0'} + ).decode().lstrip('ActiveEnterTimestamp=') + if not last_execution_time: return None - return last_execution_time.decode('UTF-8') + return last_execution_time def _service_state(self, service_name: str) -> str: if os.path.splitext(service_name)[1] not in ('.service', '.target', '.timer'): service_name += '.service' # Just to be safe - state = b''.join(SysCommand(f'systemctl show --no-pager -p SubState --value {service_name}', environment_vars={'SYSTEMD_COLORS': '0'})) - - return state.strip().decode('UTF-8') + return SysCommand( + f'systemctl show --no-pager -p SubState --value {service_name}', + environment_vars={'SYSTEMD_COLORS': '0'} + ).decode() diff --git a/archinstall/lib/locale.py b/archinstall/lib/locale.py deleted file mode 100644 index ab158984..00000000 --- a/archinstall/lib/locale.py +++ /dev/null @@ -1,61 +0,0 @@ -from itertools import takewhile -from pathlib import Path -from typing import Iterator, List - -from .exceptions import ServiceException, SysCallError -from .general import SysCommand -from .output import error - - -def list_keyboard_languages() -> Iterator[str]: - for line in SysCommand("localectl --no-pager list-keymaps", environment_vars={'SYSTEMD_COLORS': '0'}): - yield line.decode('UTF-8').strip() - - -def list_locales() -> List[str]: - entries = Path('/etc/locale.gen').read_text().splitlines() - # Before the list of locales begins there's an empty line with a '#' in front - # so we'll collect the locales from bottom up and halt when we're done. - locales = list(takewhile(bool, map(lambda entry: entry.strip('\n\t #'), reversed(entries)))) - locales.reverse() - return locales - - -def list_x11_keyboard_languages() -> Iterator[str]: - for line in SysCommand("localectl --no-pager list-x11-keymap-layouts", environment_vars={'SYSTEMD_COLORS': '0'}): - yield line.decode('UTF-8').strip() - - -def verify_keyboard_layout(layout :str) -> bool: - for language in list_keyboard_languages(): - if layout.lower() == language.lower(): - return True - return False - - -def verify_x11_keyboard_layout(layout :str) -> bool: - for language in list_x11_keyboard_languages(): - if layout.lower() == language.lower(): - return True - return False - - -def set_keyboard_language(locale :str) -> bool: - if len(locale.strip()): - if not verify_keyboard_layout(locale): - error(f"Invalid keyboard locale specified: {locale}") - return False - - try: - SysCommand(f'localectl set-keymap {locale}') - except SysCallError as err: - raise ServiceException(f"Unable to set locale '{locale}' for console: {err}") - - return True - - return False - - -def list_timezones() -> Iterator[str]: - for line in SysCommand("timedatectl --no-pager list-timezones", environment_vars={'SYSTEMD_COLORS': '0'}): - yield line.decode('UTF-8').strip() diff --git a/archinstall/lib/locale/__init__.py b/archinstall/lib/locale/__init__.py index 6c32d6f3..90f1aecc 100644 --- a/archinstall/lib/locale/__init__.py +++ b/archinstall/lib/locale/__init__.py @@ -1,6 +1,10 @@ from .locale_menu import LocaleConfiguration -from .locale import ( - list_keyboard_languages, list_locales, list_x11_keyboard_languages, - verify_keyboard_layout, verify_x11_keyboard_layout, set_kb_layout, - list_timezones +from .utils import ( + list_keyboard_languages, + list_locales, + list_x11_keyboard_languages, + verify_keyboard_layout, + verify_x11_keyboard_layout, + list_timezones, + set_kb_layout ) diff --git a/archinstall/lib/locale/locale.py b/archinstall/lib/locale/locale.py deleted file mode 100644 index 90f20cc6..00000000 --- a/archinstall/lib/locale/locale.py +++ /dev/null @@ -1,61 +0,0 @@ -from typing import Iterator, List - -from ..exceptions import ServiceException, SysCallError -from ..general import SysCommand -from ..output import error - - -def list_keyboard_languages() -> Iterator[str]: - for line in SysCommand("localectl --no-pager list-keymaps", environment_vars={'SYSTEMD_COLORS': '0'}): - yield line.decode('UTF-8').strip() - - -def list_locales() -> List[str]: - locales = [] - - with open('/usr/share/i18n/SUPPORTED') as file: - for line in file: - if line != 'C.UTF-8 UTF-8\n': - locales.append(line.rstrip()) - - return locales - - -def list_x11_keyboard_languages() -> Iterator[str]: - for line in SysCommand("localectl --no-pager list-x11-keymap-layouts", environment_vars={'SYSTEMD_COLORS': '0'}): - yield line.decode('UTF-8').strip() - - -def verify_keyboard_layout(layout :str) -> bool: - for language in list_keyboard_languages(): - if layout.lower() == language.lower(): - return True - return False - - -def verify_x11_keyboard_layout(layout :str) -> bool: - for language in list_x11_keyboard_languages(): - if layout.lower() == language.lower(): - return True - return False - - -def set_kb_layout(locale :str) -> bool: - if len(locale.strip()): - if not verify_keyboard_layout(locale): - error(f"Invalid keyboard locale specified: {locale}") - return False - - try: - SysCommand(f'localectl set-keymap {locale}') - except SysCallError as err: - raise ServiceException(f"Unable to set locale '{locale}' for console: {err}") - - return True - - return False - - -def list_timezones() -> Iterator[str]: - for line in SysCommand("timedatectl --no-pager list-timezones", environment_vars={'SYSTEMD_COLORS': '0'}): - yield line.decode('UTF-8').strip() diff --git a/archinstall/lib/locale/locale_menu.py b/archinstall/lib/locale/locale_menu.py index 2e254315..729b3b6e 100644 --- a/archinstall/lib/locale/locale_menu.py +++ b/archinstall/lib/locale/locale_menu.py @@ -1,7 +1,7 @@ from dataclasses import dataclass from typing import Dict, Any, TYPE_CHECKING, Optional -from .locale import set_kb_layout, list_keyboard_languages, list_locales +from .utils import list_keyboard_languages, list_locales, set_kb_layout from ..menu import Selector, AbstractSubMenu, MenuSelectionType, Menu if TYPE_CHECKING: diff --git a/archinstall/lib/locale/utils.py b/archinstall/lib/locale/utils.py new file mode 100644 index 00000000..330ca0ce --- /dev/null +++ b/archinstall/lib/locale/utils.py @@ -0,0 +1,70 @@ +from typing import Iterator, List + +from ..exceptions import ServiceException, SysCallError +from ..general import SysCommand +from ..output import error + + +def list_keyboard_languages() -> Iterator[str]: + for line in SysCommand( + "localectl --no-pager list-keymaps", + environment_vars={'SYSTEMD_COLORS': '0'} + ).decode(): + yield line + + +def list_locales() -> List[str]: + locales = [] + + with open('/usr/share/i18n/SUPPORTED') as file: + for line in file: + if line != 'C.UTF-8 UTF-8\n': + locales.append(line.rstrip()) + + return locales + + +def list_x11_keyboard_languages() -> Iterator[str]: + for line in SysCommand( + "localectl --no-pager list-x11-keymap-layouts", + environment_vars={'SYSTEMD_COLORS': '0'} + ).decode(): + yield line + + +def verify_keyboard_layout(layout :str) -> bool: + for language in list_keyboard_languages(): + if layout.lower() == language.lower(): + return True + return False + + +def verify_x11_keyboard_layout(layout :str) -> bool: + for language in list_x11_keyboard_languages(): + if layout.lower() == language.lower(): + return True + return False + + +def set_kb_layout(locale :str) -> bool: + if len(locale.strip()): + if not verify_keyboard_layout(locale): + error(f"Invalid keyboard locale specified: {locale}") + return False + + try: + SysCommand(f'localectl set-keymap {locale}') + except SysCallError as err: + raise ServiceException(f"Unable to set locale '{locale}' for console: {err}") + + return True + + return False + + +def list_timezones() -> Iterator[str]: + for line in SysCommand( + "timedatectl --no-pager list-timezones", + environment_vars={'SYSTEMD_COLORS': '0'} + ).decode(): + yield line diff --git a/archinstall/lib/luks.py b/archinstall/lib/luks.py index 84b0e6fd..ea09ae7c 100644 --- a/archinstall/lib/luks.py +++ b/archinstall/lib/luks.py @@ -116,7 +116,7 @@ class Luks2: command = f'/usr/bin/cryptsetup luksUUID {self.luks_dev_path}' try: - return SysCommand(command).decode().strip() # type: ignore + return SysCommand(command).decode() except SysCallError as err: info(f'Unable to get UUID for Luks device: {self.luks_dev_path}') raise err diff --git a/archinstall/lib/packages/packages.py b/archinstall/lib/packages/packages.py index b71b0ce8..7491df07 100644 --- a/archinstall/lib/packages/packages.py +++ b/archinstall/lib/packages/packages.py @@ -37,7 +37,7 @@ def group_search(name :str) -> List[PackageSearchResult]: raise err # Just to be sure some code didn't slip through the exception - data = response.read().decode('UTF-8') + data = response.read().decode('utf-8') return [PackageSearchResult(**package) for package in json.loads(data)['results']] -- cgit v1.2.3-70-g09d2 From edbc13590366e93bb8a85eacf104d5613bc5793a Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Tue, 3 Oct 2023 18:31:17 +1100 Subject: Extend the mypy checks (#2120) * Extend the mypy checks * Update * Update * Update --------- Co-authored-by: Daniel Girtler --- archinstall/__init__.py | 4 ++-- archinstall/lib/disk/device_model.py | 12 ++++++------ archinstall/lib/disk/encryption_menu.py | 7 ++++--- archinstall/lib/global_menu.py | 11 +++++++---- archinstall/lib/installer.py | 14 ++++++++++---- archinstall/lib/menu/abstract_menu.py | 2 +- archinstall/lib/models/gen.py | 20 +++++++++++++++++--- archinstall/lib/packages/packages.py | 4 ++-- archinstall/lib/profile/profiles_handler.py | 6 +++--- archinstall/lib/translationhandler.py | 2 +- pyproject.toml | 2 +- 11 files changed, 54 insertions(+), 30 deletions(-) diff --git a/archinstall/__init__.py b/archinstall/__init__.py index 07b85f96..11b47c48 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -225,8 +225,8 @@ def load_config(): if arguments.get('servers', None) is not None: storage['_selected_servers'] = arguments.get('servers', None) - if arguments.get('network_config', None) is not None: - config = NetworkConfiguration.parse_arg(arguments.get('network_config')) + if (net_config := arguments.get('network_config', None)) is not None: + config = NetworkConfiguration.parse_arg(net_config) arguments['network_config'] = config if arguments.get('!users', None) is not None or arguments.get('!superusers', None) is not None: diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index 4ac53b0c..cd955851 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -308,9 +308,9 @@ class _PartitionInfo: start: Size length: Size flags: List[PartitionFlag] - partn: int - partuuid: str - uuid: str + partn: Optional[int] + partuuid: Optional[str] + uuid: Optional[str] disk: Disk mountpoints: List[Path] btrfs_subvol_infos: List[_BtrfsSubvolumeInfo] = field(default_factory=list) @@ -344,9 +344,9 @@ class _PartitionInfo: cls, partition: Partition, fs_type: Optional[FilesystemType], - partn: int, - partuuid: str, - uuid: str, + partn: Optional[int], + partuuid: Optional[str], + uuid: Optional[str], mountpoints: List[Path], btrfs_subvol_infos: List[_BtrfsSubvolumeInfo] = [] ) -> _PartitionInfo: diff --git a/archinstall/lib/disk/encryption_menu.py b/archinstall/lib/disk/encryption_menu.py index 234e3b03..c3a1c32f 100644 --- a/archinstall/lib/disk/encryption_menu.py +++ b/archinstall/lib/disk/encryption_menu.py @@ -3,6 +3,7 @@ from typing import Dict, Optional, Any, TYPE_CHECKING, List from ..disk import ( DeviceModification, + DiskLayoutConfiguration, PartitionModification, DiskEncryption, EncryptionType @@ -26,7 +27,7 @@ if TYPE_CHECKING: class DiskEncryptionMenu(AbstractSubMenu): def __init__( self, - mods: List[DeviceModification], + disk_config: DiskLayoutConfiguration, data_store: Dict[str, Any], preset: Optional[DiskEncryption] = None ): @@ -35,7 +36,7 @@ class DiskEncryptionMenu(AbstractSubMenu): else: self._preset = DiskEncryption() - self._modifications = mods + self._disk_config = disk_config super().__init__(data_store=data_store) def setup_selection_menu_options(self): @@ -59,7 +60,7 @@ class DiskEncryptionMenu(AbstractSubMenu): self._menu_options['partitions'] = \ Selector( _('Partitions'), - func=lambda preset: select_partitions_to_encrypt(self._modifications.device_modifications, preset), + func=lambda preset: select_partitions_to_encrypt(self._disk_config.device_modifications, preset), display_func=lambda x: f'{len(x)} {_("Partitions")}' if x else None, dependencies=['encryption_password'], default=self._preset.partitions, diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index b38dac0b..deda2ef6 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -176,8 +176,11 @@ class GlobalMenu(AbstractMenu): self._menu_options['abort'] = Selector(_('Abort'), exec_func=lambda n,v:exit(1)) def _missing_configs(self) -> List[str]: - def check(s): - return self._menu_options.get(s).has_selection() + def check(s) -> bool: + obj = self._menu_options.get(s) + if obj and obj.has_selection(): + return True + return False def has_superuser() -> bool: sel = self._menu_options['!users'] @@ -228,7 +231,7 @@ class GlobalMenu(AbstractMenu): return config.type.display_msg() def _disk_encryption(self, preset: Optional[disk.DiskEncryption]) -> Optional[disk.DiskEncryption]: - mods: Optional[List[disk.DeviceModification]] = self._menu_options['disk_config'].current_selection + mods: Optional[disk.DiskLayoutConfiguration] = self._menu_options['disk_config'].current_selection if not mods: # this should not happen as the encryption menu has the disk_config as dependency @@ -263,7 +266,7 @@ class GlobalMenu(AbstractMenu): def _prev_additional_pkgs(self): selector = self._menu_options['packages'] - if selector.has_selection(): + if selector.current_selection: packages: List[str] = selector.current_selection return format_cols(packages, None) return None diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index ad98d9a8..8e716d3d 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -131,7 +131,11 @@ class Installer: We need to wait for it before we continue since we opted in to use a custom mirror/region. """ info('Waiting for time sync (systemd-timesyncd.service) to complete.') - while SysCommand('timedatectl show --property=NTPSynchronized --value').decode() != 'yes': + + while True: + time_val = SysCommand('timedatectl show --property=NTPSynchronized --value').decode() + if time_val and time_val.strip() == 'yes': + break time.sleep(1) info('Waiting for automatic mirror selection (reflector) to complete.') @@ -237,7 +241,7 @@ class Installer: gen_enc_file = self._disk_encryption.should_generate_encryption_file(part_mod) luks_handler = Luks2( - part_mod.dev_path, + part_mod.safe_dev_path, mapper_name=part_mod.mapper_name, password=self._disk_encryption.encryption_password ) @@ -281,8 +285,10 @@ class Installer: self._fstab_entries.append(f'{file} none swap defaults 0 0') if enable_resume: - resume_uuid = SysCommand(f'findmnt -no UUID -T {self.target}{file}').decode('UTF-8').strip() - resume_offset = SysCommand(f'/usr/bin/filefrag -v {self.target}{file}').decode().split('0:', 1)[1].split(":", 1)[1].split("..", 1)[0].strip() + resume_uuid = SysCommand(f'findmnt -no UUID -T {self.target}{file}').decode() + resume_offset = SysCommand( + f'/usr/bin/filefrag -v {self.target}{file}' + ).decode().split('0:', 1)[1].split(":", 1)[1].split("..", 1)[0].strip() self._hooks.append('resume') self._kernel_params.append(f'resume=UUID={resume_uuid}') diff --git a/archinstall/lib/menu/abstract_menu.py b/archinstall/lib/menu/abstract_menu.py index 306c500a..053f3c30 100644 --- a/archinstall/lib/menu/abstract_menu.py +++ b/archinstall/lib/menu/abstract_menu.py @@ -14,7 +14,7 @@ class Selector: def __init__( self, description: str, - func: Optional[Callable[[str], Any]] = None, + func: Optional[Callable[[Any], Any]] = None, display_func: Optional[Callable] = None, default: Optional[Any] = None, enabled: bool = False, diff --git a/archinstall/lib/models/gen.py b/archinstall/lib/models/gen.py index cc8d7605..fb7e5751 100644 --- a/archinstall/lib/models/gen.py +++ b/archinstall/lib/models/gen.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Optional, List +from typing import Optional, List, Dict, Any @dataclass @@ -87,6 +87,10 @@ class PackageSearchResult: makedepends: List[str] checkdepends: List[str] + @staticmethod + def from_json(data: Dict[str, Any]) -> 'PackageSearchResult': + return PackageSearchResult(**data) + @property def pkg_version(self) -> str: return self.pkgver @@ -107,8 +111,18 @@ class PackageSearch: page: int results: List[PackageSearchResult] - def __post_init__(self): - self.results = [PackageSearchResult(**x) for x in self.results] + @staticmethod + def from_json(data: Dict[str, Any]) -> 'PackageSearch': + results = [PackageSearchResult.from_json(r) for r in data['results']] + + return PackageSearch( + version=data['version'], + limit=data['limit'], + valid=data['valid'], + num_pages=data['num_pages'], + page=data['page'], + results=results + ) @dataclass diff --git a/archinstall/lib/packages/packages.py b/archinstall/lib/packages/packages.py index 7491df07..e495b03f 100644 --- a/archinstall/lib/packages/packages.py +++ b/archinstall/lib/packages/packages.py @@ -55,8 +55,8 @@ def package_search(package :str) -> PackageSearch: raise PackageError(f"Could not locate package: [{response.code}] {response}") data = response.read().decode('UTF-8') - - return PackageSearch(**json.loads(data)) + json_data = json.loads(data) + return PackageSearch.from_json(json_data) def find_package(package :str) -> List[PackageSearchResult]: diff --git a/archinstall/lib/profile/profiles_handler.py b/archinstall/lib/profile/profiles_handler.py index 03039321..515cdfe9 100644 --- a/archinstall/lib/profile/profiles_handler.py +++ b/archinstall/lib/profile/profiles_handler.py @@ -138,16 +138,16 @@ class ProfileHandler: profiles = [profiles] for profile in profiles: - self._profiles.append(profile) + self.profiles.append(profile) - self._verify_unique_profile_names(self._profiles) + self._verify_unique_profile_names(self.profiles) def remove_custom_profiles(self, profiles: Union[TProfile, List[TProfile]]): if not isinstance(profiles, list): profiles = [profiles] remove_names = [p.name for p in profiles] - self._profiles = [p for p in self._profiles if p.name not in remove_names] + self._profiles = [p for p in self.profiles if p.name not in remove_names] def get_profile_by_name(self, name: str) -> Optional[Profile]: return next(filter(lambda x: x.name == name, self.profiles), None) # type: ignore diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py index 33230562..3ea4c70e 100644 --- a/archinstall/lib/translationhandler.py +++ b/archinstall/lib/translationhandler.py @@ -206,4 +206,4 @@ class DeferredTranslation: @classmethod def install(cls): import builtins - builtins._ = cls + builtins._ = cls # type: ignore diff --git a/pyproject.toml b/pyproject.toml index 36ee0492..445aa277 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,7 +61,7 @@ packages = ["archinstall"] python_version = "3.11" files = "archinstall/" exclude = "tests" -#check_untyped_defs=true +check_untyped_defs=true [tool.bandit] targets = ["archinstall"] -- cgit v1.2.3-70-g09d2 From dc69acd4b43931f9fd3a267d78834d1a38fbb10f Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Mon, 9 Oct 2023 06:40:59 -0400 Subject: Fix keyboard layout and timezone menus (#2153) --- archinstall/lib/interactions/general_conf.py | 4 ++-- archinstall/lib/locale/locale_menu.py | 2 +- archinstall/lib/locale/utils.py | 23 ++++++++++------------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/archinstall/lib/interactions/general_conf.py b/archinstall/lib/interactions/general_conf.py index 56598e25..a23426d0 100644 --- a/archinstall/lib/interactions/general_conf.py +++ b/archinstall/lib/interactions/general_conf.py @@ -44,7 +44,7 @@ def ask_for_a_timezone(preset: Optional[str] = None) -> Optional[str]: choice = Menu( _('Select a timezone'), - list(timezones), + timezones, preset_values=preset, default_option=default ).run() @@ -95,7 +95,7 @@ def select_language(preset: Optional[str] = None) -> Optional[str]: """ kb_lang = list_keyboard_languages() # sort alphabetically and then by length - sorted_kb_lang = sorted(sorted(list(kb_lang)), key=len) + sorted_kb_lang = sorted(kb_lang, key=lambda x: (len(x), x)) choice = Menu( _('Select keyboard layout'), diff --git a/archinstall/lib/locale/locale_menu.py b/archinstall/lib/locale/locale_menu.py index 729b3b6e..75cc1332 100644 --- a/archinstall/lib/locale/locale_menu.py +++ b/archinstall/lib/locale/locale_menu.py @@ -139,7 +139,7 @@ def select_kb_layout(preset: Optional[str] = None) -> Optional[str]: """ kb_lang = list_keyboard_languages() # sort alphabetically and then by length - sorted_kb_lang = sorted(sorted(list(kb_lang)), key=len) + sorted_kb_lang = sorted(kb_lang, key=lambda x: (len(x), x)) choice = Menu( _('Select keyboard layout'), diff --git a/archinstall/lib/locale/utils.py b/archinstall/lib/locale/utils.py index 330ca0ce..d7641d50 100644 --- a/archinstall/lib/locale/utils.py +++ b/archinstall/lib/locale/utils.py @@ -1,16 +1,15 @@ -from typing import Iterator, List +from typing import List from ..exceptions import ServiceException, SysCallError from ..general import SysCommand from ..output import error -def list_keyboard_languages() -> Iterator[str]: - for line in SysCommand( +def list_keyboard_languages() -> List[str]: + return SysCommand( "localectl --no-pager list-keymaps", environment_vars={'SYSTEMD_COLORS': '0'} - ).decode(): - yield line + ).decode().splitlines() def list_locales() -> List[str]: @@ -24,12 +23,11 @@ def list_locales() -> List[str]: return locales -def list_x11_keyboard_languages() -> Iterator[str]: - for line in SysCommand( +def list_x11_keyboard_languages() -> List[str]: + return SysCommand( "localectl --no-pager list-x11-keymap-layouts", environment_vars={'SYSTEMD_COLORS': '0'} - ).decode(): - yield line + ).decode().splitlines() def verify_keyboard_layout(layout :str) -> bool: @@ -62,9 +60,8 @@ def set_kb_layout(locale :str) -> bool: return False -def list_timezones() -> Iterator[str]: - for line in SysCommand( +def list_timezones() -> List[str]: + return SysCommand( "timedatectl --no-pager list-timezones", environment_vars={'SYSTEMD_COLORS': '0'} - ).decode(): - yield line + ).decode().splitlines() -- cgit v1.2.3-70-g09d2 From 5e59acf937c3bb9cfe6a3b7a0a264b9df00239ee Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Tue, 10 Oct 2023 04:00:22 -0400 Subject: Add handling of signal interrupt and EOF at input prompts (#2154) --- archinstall/lib/configuration.py | 2 +- archinstall/lib/interactions/disk_conf.py | 5 ++++- archinstall/lib/interactions/general_conf.py | 15 ++++++++------- archinstall/lib/interactions/manage_users_conf.py | 6 +++++- archinstall/lib/interactions/utils.py | 7 ++++++- archinstall/lib/menu/text_input.py | 11 ++++++++++- 6 files changed, 34 insertions(+), 12 deletions(-) diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py index aeeddbb8..95e237d7 100644 --- a/archinstall/lib/configuration.py +++ b/archinstall/lib/configuration.py @@ -177,5 +177,5 @@ def save_config(config: Dict): case "all": config_output.save(dest_path) - except KeyboardInterrupt: + except (KeyboardInterrupt, EOFError): return diff --git a/archinstall/lib/interactions/disk_conf.py b/archinstall/lib/interactions/disk_conf.py index 84a3196c..c18119ec 100644 --- a/archinstall/lib/interactions/disk_conf.py +++ b/archinstall/lib/interactions/disk_conf.py @@ -134,7 +134,10 @@ def select_disk_config( output = "You will use whatever drive-setup is mounted at the specified directory\n" output += "WARNING: Archinstall won't check the suitability of this setup\n" - path = prompt_dir(str(_('Enter the root directory of the mounted devices: ')), output) + try: + path = prompt_dir(str(_('Enter the root directory of the mounted devices: ')), output) + except (KeyboardInterrupt, EOFError): + return preset mods = disk.device_handler.detect_pre_mounted_mods(path) return disk.DiskLayoutConfiguration( diff --git a/archinstall/lib/interactions/general_conf.py b/archinstall/lib/interactions/general_conf.py index a23426d0..b12a6fb8 100644 --- a/archinstall/lib/interactions/general_conf.py +++ b/archinstall/lib/interactions/general_conf.py @@ -28,14 +28,15 @@ def ask_ntp(preset: bool = True) -> bool: def ask_hostname(preset: str = '') -> str: - while True: - hostname = TextInput( - str(_('Desired hostname for the installation: ')), - preset - ).run().strip() + hostname = TextInput( + str(_('Desired hostname for the installation: ')), + preset + ).run().strip() + + if not hostname: + return preset - if hostname: - return hostname + return hostname def ask_for_a_timezone(preset: Optional[str] = None) -> Optional[str]: diff --git a/archinstall/lib/interactions/manage_users_conf.py b/archinstall/lib/interactions/manage_users_conf.py index 879578da..ca912283 100644 --- a/archinstall/lib/interactions/manage_users_conf.py +++ b/archinstall/lib/interactions/manage_users_conf.py @@ -75,7 +75,11 @@ class UserList(ListManager): prompt = '\n\n' + str(_('Enter username (leave blank to skip): ')) while True: - username = input(prompt).strip(' ') + try: + username = input(prompt).strip(' ') + except (KeyboardInterrupt, EOFError): + return None + if not username: return None if not self._check_for_correct_username(username): diff --git a/archinstall/lib/interactions/utils.py b/archinstall/lib/interactions/utils.py index f6b5b2d3..fdbb4625 100644 --- a/archinstall/lib/interactions/utils.py +++ b/archinstall/lib/interactions/utils.py @@ -17,7 +17,12 @@ def get_password(prompt: str = '') -> Optional[str]: if not prompt: prompt = _("Enter a password: ") - while password := getpass.getpass(prompt): + while True: + try: + password = getpass.getpass(prompt) + except (KeyboardInterrupt, EOFError): + break + if len(password.strip()) <= 0: break diff --git a/archinstall/lib/menu/text_input.py b/archinstall/lib/menu/text_input.py index 05ca0f22..971df5fd 100644 --- a/archinstall/lib/menu/text_input.py +++ b/archinstall/lib/menu/text_input.py @@ -1,4 +1,5 @@ import readline +import sys class TextInput: @@ -12,6 +13,14 @@ class TextInput: def run(self) -> str: readline.set_pre_input_hook(self._hook) - result = input(self._prompt) + try: + result = input(self._prompt) + except (KeyboardInterrupt, EOFError): + # To make sure any output that may follow + # will be on the line after the prompt + sys.stdout.write('\n') + sys.stdout.flush() + + result = '' readline.set_pre_input_hook() return result -- cgit v1.2.3-70-g09d2 From 7930ea58fd3c55b38ff73949d74e9227ff351b60 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Fri, 13 Oct 2023 03:50:35 -0400 Subject: Fix install text initialization (#2162) --- archinstall/lib/global_menu.py | 4 ++-- archinstall/lib/menu/abstract_menu.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index deda2ef6..86c341a7 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -211,11 +211,11 @@ class GlobalMenu(AbstractMenu): return False return self._validate_bootloader() is None - def _update_install_text(self, name: str, value: str): + def _update_install_text(self, name: Optional[str] = None, value: Any = None): text = self._install_text() self._menu_options['install'].update_description(text) - def post_callback(self, name: str, value: str): + def post_callback(self, name: Optional[str] = None, value: Any = None): self._update_install_text(name, value) def _install_text(self): diff --git a/archinstall/lib/menu/abstract_menu.py b/archinstall/lib/menu/abstract_menu.py index 053f3c30..2ceb6ca7 100644 --- a/archinstall/lib/menu/abstract_menu.py +++ b/archinstall/lib/menu/abstract_menu.py @@ -252,7 +252,7 @@ class AbstractMenu: """ will be called before each action in the menu """ return - def post_callback(self, selection_name: str, value: Any): + def post_callback(self, selection_name: Optional[str] = None, value: Any = None): """ will be called after each action in the menu """ return True @@ -299,6 +299,7 @@ class AbstractMenu: def run(self, allow_reset: bool = False): self._sync_all() + self.post_callback() cursor_pos = None while True: -- cgit v1.2.3-70-g09d2 From d1ea6db9aaad367dac183c7eb31083830600a4b8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 13 Oct 2023 09:56:36 +0200 Subject: chore(deps): update dependency dev/mypy to v1.6.0 (#2155) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 445aa277..c6202d64 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ Source = "https://github.com/archlinux/archinstall" [project.optional-dependencies] dev = [ - "mypy==1.5.1", + "mypy==1.6.0", "pre-commit==3.4.0", ] doc = ["sphinx"] -- cgit v1.2.3-70-g09d2 From 3c2ed1c121f0cbe6340753f50e5c58b83675990b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 13 Oct 2023 20:44:04 +0200 Subject: chore(deps): update dependency dev/pre-commit to v3.5.0 (#2164) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c6202d64..551b9241 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ Source = "https://github.com/archlinux/archinstall" [project.optional-dependencies] dev = [ "mypy==1.6.0", - "pre-commit==3.4.0", + "pre-commit==3.5.0", ] doc = ["sphinx"] -- cgit v1.2.3-70-g09d2 From 07b0bb18351c5fec332fc27808f9c51996acbae1 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Sun, 15 Oct 2023 03:26:34 -0400 Subject: Fix `MOUNT_POINT` for pre-mounted disk configuration (#2168) --- archinstall/lib/interactions/disk_conf.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/archinstall/lib/interactions/disk_conf.py b/archinstall/lib/interactions/disk_conf.py index c18119ec..bf24a22c 100644 --- a/archinstall/lib/interactions/disk_conf.py +++ b/archinstall/lib/interactions/disk_conf.py @@ -11,6 +11,7 @@ from ..menu import TableMenu from ..menu.menu import MenuSelectionType from ..output import FormattedOutput, debug from ..utils.util import prompt_dir +from ..storage import storage if TYPE_CHECKING: _: Any @@ -140,6 +141,8 @@ def select_disk_config( return preset mods = disk.device_handler.detect_pre_mounted_mods(path) + storage['MOUNT_POINT'] = Path(path) + return disk.DiskLayoutConfiguration( config_type=disk.DiskLayoutType.Pre_mount, device_modifications=mods -- cgit v1.2.3-70-g09d2 From c4c5cc42248645142828d09ec0322cdefa49209a Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Sun, 15 Oct 2023 20:26:27 +1100 Subject: User error info and version check (#2169) * Add user information for error * Show output if newer version available * Update * Update * flake8 --------- Co-authored-by: Daniel Girtler --- archinstall/__init__.py | 71 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 19 deletions(-) diff --git a/archinstall/__init__.py b/archinstall/__init__.py index 11b47c48..3949d38a 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -1,6 +1,8 @@ """Arch Linux installer - guided, templates etc.""" import importlib import os +import time +import traceback from argparse import ArgumentParser, Namespace from pathlib import Path from typing import TYPE_CHECKING, Any, Dict, Union @@ -21,6 +23,7 @@ from . import default_profiles from .lib.hardware import SysInfo, GfxDriver from .lib.installer import Installer, accessibility_tools_in_use from .lib.output import FormattedOutput, log, error, debug, warn, info +from .lib.pacman import Pacman from .lib.storage import storage from .lib.global_menu import GlobalMenu from .lib.boot import Boot @@ -34,7 +37,6 @@ from .lib.general import ( run_custom_user_commands, json_stream_to_structure, secret ) - if TYPE_CHECKING: _: Any @@ -42,7 +44,6 @@ if TYPE_CHECKING: __version__ = "2.6.3" storage['__version__'] = __version__ - # add the custom _ as a builtin, it can now be used anywhere in the # project to mark strings as translatable with _('translate me') DeferredTranslation.install() @@ -57,12 +58,10 @@ debug(f"Graphics devices detected: {SysInfo._graphics_devices().keys()}") # For support reasons, we'll log the disk layout pre installation to match against post-installation layout debug(f"Disk states before installing: {disk.disk_layouts()}") - if os.getuid() != 0: print(_("Archinstall requires root privileges to run. See --help for more.")) exit(1) - parser = ArgumentParser() @@ -82,14 +81,17 @@ def define_arguments(): parser.add_argument("--dry-run", "--dry_run", action="store_true", help="Generates a configuration file and then exits instead of performing an installation") parser.add_argument("--script", default="guided", nargs="?", help="Script to run for installation", type=str) - parser.add_argument("--mount-point","--mount_point", nargs="?", type=str, help="Define an alternate mount point for installation") + parser.add_argument("--mount-point", "--mount_point", nargs="?", type=str, + help="Define an alternate mount point for installation") parser.add_argument("--debug", action="store_true", default=False, help="Adds debug info into the log") - parser.add_argument("--offline", action="store_true", default=False, help="Disabled online upstream services such as package search and key-ring auto update.") - parser.add_argument("--no-pkg-lookups", action="store_true", default=False, help="Disabled package validation specifically prior to starting installation.") + parser.add_argument("--offline", action="store_true", default=False, + help="Disabled online upstream services such as package search and key-ring auto update.") + parser.add_argument("--no-pkg-lookups", action="store_true", default=False, + help="Disabled package validation specifically prior to starting installation.") parser.add_argument("--plugin", nargs="?", type=str) -def parse_unspecified_argument_list(unknowns :list, multiple :bool = False, err :bool = False) -> dict: +def parse_unspecified_argument_list(unknowns: list, multiple: bool = False, err: bool = False) -> dict: """We accept arguments not defined to the parser. (arguments "ad hoc"). Internally argparse return to us a list of words so we have to parse its contents, manually. We accept following individual syntax for each argument @@ -105,32 +107,32 @@ def parse_unspecified_argument_list(unknowns :list, multiple :bool = False, err argument value value ... which isn't am error if multiple is specified """ - tmp_list = unknowns[:] # wastes a few bytes, but avoids any collateral effect of the destructive nature of the pop method() + tmp_list = unknowns[:] # wastes a few bytes, but avoids any collateral effect of the destructive nature of the pop method() config = {} key = None last_key = None while tmp_list: - element = tmp_list.pop(0) # retrieve an element of the list - if element.startswith('--'): # is an argument ? - if '=' in element: # uses the arg=value syntax ? + element = tmp_list.pop(0) # retrieve an element of the list + if element.startswith('--'): # is an argument ? + if '=' in element: # uses the arg=value syntax ? key, value = [x.strip() for x in element[2:].split('=', 1)] config[key] = value - last_key = key # for multiple handling - key = None # we have the kwy value pair we need + last_key = key # for multiple handling + key = None # we have the kwy value pair we need else: key = element[2:] - config[key] = True # every argument starts its lifecycle as boolean + config[key] = True # every argument starts its lifecycle as boolean else: if element == '=': continue if key: config[key] = element - last_key = key # multiple + last_key = key # multiple key = None else: if multiple and last_key: - if isinstance(config[last_key],str): - config[last_key] = [config[last_key],element] + if isinstance(config[last_key], str): + config[last_key] = [config[last_key], element] else: config[last_key].append(element) elif err: @@ -158,6 +160,7 @@ def cleanup_empty_args(args: Union[Namespace, Dict]) -> Dict: return clean_args + def get_arguments() -> Dict[str, Any]: """ The handling of parameters from the command line Is done on following steps: @@ -275,12 +278,25 @@ def plugin(f, *args, **kwargs): plugins[f.__name__] = f -def run_as_a_module(): +def _check_new_version(): + info("Checking version...") + Pacman.run("-Sy") + upgrade = Pacman.run("-Qu archinstall").decode() + + if upgrade: + text = f'New version available: {upgrade}' + info(text) + time.sleep(3) + + +def main(): """ This can either be run as the compiled and installed application: python setup.py install OR straight as a module: python -m archinstall In any case we will be attempting to load the provided script to be run from the scripts/ folder """ + _check_new_version() + script = arguments.get('script', None) if script is None: @@ -289,3 +305,20 @@ def run_as_a_module(): mod_name = f'archinstall.scripts.{script}' # by loading the module we'll automatically run the script importlib.import_module(mod_name) + + +def run_as_a_module(): + try: + main() + except Exception as e: + err = ''.join(traceback.format_exception(e)) + error(err) + + text = ( + 'Archinstall experienced the above error. If you think this is a bug, please report it to\n' + 'https://github.com/archlinux/archinstall and include the log file "/var/log/archinstall/install.log".\n\n' + 'Hint: To extract the log from a live ISO \ncurl -F\'file=@/var/log/archinstall/install.log\' https://0x0.st\n' + ) + + warn(text) + exit(1) -- cgit v1.2.3-70-g09d2 From 5dda32f17ef0190955fe12251764c0088af20ccd Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Mon, 16 Oct 2023 10:42:42 -0400 Subject: Refactor `_add_systemd_bootloader()` newlines (#2173) * Refactor `_add_systemd_bootloader()` newlines * Revert f-string change --- archinstall/lib/installer.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 8e716d3d..39298204 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -818,15 +818,14 @@ class Installer: # Modify or create a loader.conf loader_conf = loader_dir / 'loader.conf' - default = f'default {self.init_time}_{self.kernels[0]}.conf\n' + default = f'default {self.init_time}_{self.kernels[0]}.conf' try: - with loader_conf.open() as loader: - loader_data = loader.readlines() + loader_data = loader_conf.read_text().splitlines() except FileNotFoundError: loader_data = [ default, - 'timeout 15\n' + 'timeout 15' ] else: for index, line in enumerate(loader_data): @@ -836,41 +835,40 @@ class Installer: # We add in the default timeout to support dual-boot loader_data[index] = line.removeprefix('#') - with loader_conf.open('w') as loader: - loader.writelines(loader_data) + loader_conf.write_text('\n'.join(loader_data) + '\n') # Ensure that the $BOOT/loader/entries/ directory exists before we try to create files in it entries_dir = loader_dir / 'entries' entries_dir.mkdir(parents=True, exist_ok=True) comments = ( - '# Created by: archinstall\n', - f'# Created on: {self.init_time}\n' + '# Created by: archinstall', + f'# Created on: {self.init_time}' ) microcode = [] if ucode := self._get_microcode(): - microcode.append(f'initrd /{ucode}\n') + microcode.append(f'initrd /{ucode}') else: debug('Archinstall will not add any ucode to systemd-boot config.') - options = 'options ' + ' '.join(self._get_kernel_params(root_partition)) + '\n' + options = 'options ' + ' '.join(self._get_kernel_params(root_partition)) for kernel in self.kernels: for variant in ("", "-fallback"): # Setup the loader entry entry = [ *comments, - f'title Arch Linux ({kernel}{variant})\n', - f'linux /vmlinuz-{kernel}\n', + f'title Arch Linux ({kernel}{variant})', + f'linux /vmlinuz-{kernel}', *microcode, - f'initrd /initramfs-{kernel}{variant}.img\n', + f'initrd /initramfs-{kernel}{variant}.img', options, ] entry_conf = entries_dir / f'{self.init_time}_{kernel}{variant}.conf' - entry_conf.write_text(''.join(entry)) + entry_conf.write_text('\n'.join(entry) + '\n') self.helper_flags['bootloader'] = 'systemd' -- cgit v1.2.3-70-g09d2 From 332ec0d6236ca863cb6a2101849555935066549f Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Tue, 17 Oct 2023 05:21:39 -0400 Subject: Fix reset in locales menu (#2178) --- archinstall/lib/locale/locale_menu.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/archinstall/lib/locale/locale_menu.py b/archinstall/lib/locale/locale_menu.py index 75cc1332..db119f20 100644 --- a/archinstall/lib/locale/locale_menu.py +++ b/archinstall/lib/locale/locale_menu.py @@ -80,6 +80,9 @@ class LocaleMenu(AbstractSubMenu): def run(self, allow_reset: bool = True) -> LocaleConfiguration: super().run(allow_reset=allow_reset) + if not self._data_store: + return LocaleConfiguration.default() + return LocaleConfiguration( self._data_store['keyboard-layout'], self._data_store['sys-language'], -- cgit v1.2.3-70-g09d2 From bc3b3a35e6408144587f8c2ace95c4ac68d53bcc Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Tue, 17 Oct 2023 05:23:09 -0400 Subject: Add support for unified kernel image (#1519) --- archinstall/lib/global_menu.py | 10 ++ archinstall/lib/installer.py | 166 ++++++++++++++++++++-------- archinstall/lib/interactions/__init__.py | 2 +- archinstall/lib/interactions/system_conf.py | 16 +++ archinstall/scripts/guided.py | 9 +- schema.json | 4 + 6 files changed, 160 insertions(+), 47 deletions(-) diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 86c341a7..e4aa1235 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -4,6 +4,7 @@ from typing import Any, List, Optional, Dict, TYPE_CHECKING from . import disk from .general import secret +from .hardware import SysInfo from .locale.locale_menu import LocaleConfiguration, LocaleMenu from .menu import Selector, AbstractMenu from .mirrors import MirrorConfiguration, MirrorMenu @@ -20,6 +21,7 @@ from .interactions import ask_additional_packages_to_install from .interactions import ask_for_additional_users from .interactions import ask_for_audio_selection from .interactions import ask_for_bootloader +from .interactions import ask_for_uki from .interactions import ask_for_swap from .interactions import ask_hostname from .interactions import ask_to_configure_network @@ -85,6 +87,11 @@ class GlobalMenu(AbstractMenu): lambda preset: ask_for_bootloader(preset), display_func=lambda x: x.value, default=Bootloader.get_default()) + self._menu_options['uki'] = \ + Selector( + _('Unified kernel images'), + lambda preset: ask_for_uki(preset), + default=False) self._menu_options['hostname'] = \ Selector( _('Hostname'), @@ -216,6 +223,9 @@ class GlobalMenu(AbstractMenu): self._menu_options['install'].update_description(text) def post_callback(self, name: Optional[str] = None, value: Any = None): + if not SysInfo.has_uefi(): + self._menu_options['uki'].set_enabled(False) + self._update_install_text(name, value) def _install_text(self): diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 39298204..4d6c65b3 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -542,18 +542,13 @@ class Installer: return True - def mkinitcpio(self, flags: List[str], locale_config: LocaleConfiguration) -> bool: + def mkinitcpio(self, flags: List[str]) -> bool: for plugin in plugins.values(): if hasattr(plugin, 'on_mkinitcpio'): # Allow plugins to override the usage of mkinitcpio altogether. if plugin.on_mkinitcpio(self): return True - # mkinitcpio will error out if there's no vconsole. - if (vconsole := Path(f"{self.target}/etc/vconsole.conf")).exists() is False: - with vconsole.open('w') as fh: - fh.write(f"KEYMAP={locale_config.kb_layout}\n") - with open(f'{self.target}/etc/mkinitcpio.conf', 'w') as mkinit: mkinit.write(f"MODULES=({' '.join(self.modules)})\n") mkinit.write(f"BINARIES=({' '.join(self._binaries)})\n") @@ -587,6 +582,7 @@ class Installer: self, testing: bool = False, multilib: bool = False, + mkinitcpio: bool = True, hostname: str = 'archinstall', locale_config: LocaleConfiguration = LocaleConfiguration.default() ): @@ -674,7 +670,7 @@ class Installer: # TODO: Use python functions for this SysCommand(f'/usr/bin/arch-chroot {self.target} chmod 700 /root') - if not self.mkinitcpio(['-P'], locale_config): + if mkinitcpio and not self.mkinitcpio(['-P']): error(f"Error generating initramfs (continuing anyway)") self.helper_flags['base'] = True @@ -783,7 +779,8 @@ class Installer: self, boot_partition: disk.PartitionModification, root_partition: disk.PartitionModification, - efi_partition: Optional[disk.PartitionModification] + efi_partition: Optional[disk.PartitionModification], + uki_enabled: bool = False ): self.pacman.strap('efibootmgr') @@ -815,11 +812,18 @@ class Installer: loader_dir = self.target / 'boot/loader' loader_dir.mkdir(parents=True, exist_ok=True) + default_kernel = self.kernels[0] + if uki_enabled: + default_entry = f'arch-{default_kernel}.efi' + else: + entry_name = self.init_time + '_{kernel}{variant}.conf' + default_entry = entry_name.format(kernel=default_kernel, variant='') + + default = f'default {default_entry}' + # Modify or create a loader.conf loader_conf = loader_dir / 'loader.conf' - default = f'default {self.init_time}_{self.kernels[0]}.conf' - try: loader_data = loader_conf.read_text().splitlines() except FileNotFoundError: @@ -837,6 +841,9 @@ class Installer: loader_conf.write_text('\n'.join(loader_data) + '\n') + if uki_enabled: + return + # Ensure that the $BOOT/loader/entries/ directory exists before we try to create files in it entries_dir = loader_dir / 'entries' entries_dir.mkdir(parents=True, exist_ok=True) @@ -867,7 +874,8 @@ class Installer: options, ] - entry_conf = entries_dir / f'{self.init_time}_{kernel}{variant}.conf' + name = entry_name.format(kernel=kernel, variant=variant) + entry_conf = entries_dir / name entry_conf.write_text('\n'.join(entry) + '\n') self.helper_flags['bootloader'] = 'systemd' @@ -876,17 +884,19 @@ class Installer: self, boot_partition: disk.PartitionModification, root_partition: disk.PartitionModification, - efi_partition: Optional[disk.PartitionModification] + efi_partition: Optional[disk.PartitionModification], + uki_enabled: bool = False ): self.pacman.strap('grub') # no need? - grub_default = self.target / 'etc/default/grub' - config = grub_default.read_text() + if not uki_enabled: + grub_default = self.target / 'etc/default/grub' + config = grub_default.read_text() - kernel_parameters = ' '.join(self._get_kernel_params(root_partition, False, False)) - config = re.sub(r'(GRUB_CMDLINE_LINUX=")("\n)', rf'\1{kernel_parameters}\2', config, 1) + kernel_parameters = ' '.join(self._get_kernel_params(root_partition, False, False)) + config = re.sub(r'(GRUB_CMDLINE_LINUX=")("\n)', rf'\1{kernel_parameters}\2', config, 1) - grub_default.write_text(config) + grub_default.write_text(config) info(f"GRUB boot partition: {boot_partition.dev_path}") @@ -1067,7 +1077,8 @@ TIMEOUT=5 def _add_efistub_bootloader( self, boot_partition: disk.PartitionModification, - root_partition: disk.PartitionModification + root_partition: disk.PartitionModification, + uki_enabled: bool = False ): self.pacman.strap('efibootmgr') @@ -1078,41 +1089,103 @@ TIMEOUT=5 # points towards the same disk and/or partition. # And in which case we should do some clean up. - microcode = [] + if not uki_enabled: + loader = '/vmlinuz-{kernel}' - if ucode := self._get_microcode(): - microcode.append(f'initrd=\\{ucode}') - else: - debug('Archinstall will not add any ucode to firmware boot entry.') + microcode = [] + + if ucode := self._get_microcode(): + microcode.append(f'initrd=/{ucode}') + else: + debug('Archinstall will not add any ucode to firmware boot entry.') - kernel_parameters = self._get_kernel_params(root_partition) + entries = ( + *microcode, + 'initrd=/initramfs-{kernel}.img', + *self._get_kernel_params(root_partition) + ) + + cmdline = tuple(' '.join(entries)) + else: + loader = '/EFI/Linux/arch-{kernel}.efi' + cmdline = tuple() parent_dev_path = disk.device_handler.get_parent_device_path(boot_partition.safe_dev_path) + cmd_template = ( + 'efibootmgr', + '--create', + '--disk', str(parent_dev_path), + '--part', str(boot_partition.partn), + '--label', 'Arch Linux ({kernel})', + '--loader', loader, + '--unicode', *cmdline, + '--verbose' + ) + for kernel in self.kernels: # Setup the firmware entry - cmdline = [ - *microcode, - f"initrd=\\initramfs-{kernel}.img", - *kernel_parameters, - ] - - cmd = [ - 'efibootmgr', - '--disk', str(parent_dev_path), - '--part', str(boot_partition.partn), - '--create', - '--label', f'Arch Linux ({kernel})', - '--loader', f"/vmlinuz-{kernel}", - '--unicode', ' '.join(cmdline), - '--verbose' - ] - + cmd = [arg.format(kernel=kernel) for arg in cmd_template] SysCommand(cmd) self.helper_flags['bootloader'] = "efistub" - def add_bootloader(self, bootloader: Bootloader): + def _config_uki( + self, + root_partition: disk.PartitionModification, + efi_partition: Optional[disk.PartitionModification] + ): + if not efi_partition or not efi_partition.mountpoint: + raise ValueError(f'Could not detect ESP at mountpoint {self.target}') + + # Set up kernel command line + with open(self.target / 'etc/kernel/cmdline', 'w') as cmdline: + kernel_parameters = self._get_kernel_params(root_partition) + cmdline.write(' '.join(kernel_parameters) + '\n') + + ucode = self._get_microcode() + + esp = efi_partition.mountpoint + + diff_mountpoint = None + if esp != Path('/efi'): + diff_mountpoint = str(esp) + + image_re = re.compile('(.+_image="/([^"]+).+\n)') + uki_re = re.compile('#((.+_uki=")/[^/]+(.+\n))') + + # Modify .preset files + for kernel in self.kernels: + preset = self.target / 'etc/mkinitcpio.d' / (kernel + '.preset') + config = preset.read_text().splitlines(True) + + for index, line in enumerate(config): + if not ucode and line.startswith('ALL_microcode='): + config[index] = '#' + line + # Avoid storing redundant image file + elif m := image_re.match(line): + image = self.target / m.group(2) + image.unlink(missing_ok=True) + config[index] = '#' + m.group(1) + elif m := uki_re.match(line): + if diff_mountpoint: + config[index] = m.group(2) + diff_mountpoint + m.group(3) + else: + config[index] = m.group(1) + elif line.startswith('#default_options='): + config[index] = line.removeprefix('#') + + preset.write_text(''.join(config)) + + # Directory for the UKIs + uki_dir = self.target / esp.relative_to(Path('/')) / 'EFI/Linux' + uki_dir.mkdir(parents=True, exist_ok=True) + + # Build the UKIs + if not self.mkinitcpio(['-P']): + error(f"Error generating initramfs (continuing anyway)") + + def add_bootloader(self, bootloader: Bootloader, uki_enabled: bool = False): """ Adds a bootloader to the installation instance. Archinstall supports one of three types: @@ -1143,13 +1216,16 @@ TIMEOUT=5 info(f'Adding bootloader {bootloader.value} to {boot_partition.dev_path}') + if uki_enabled: + self._config_uki(root_partition, efi_partition) + match bootloader: case Bootloader.Systemd: - self._add_systemd_bootloader(boot_partition, root_partition, efi_partition) + self._add_systemd_bootloader(boot_partition, root_partition, efi_partition, uki_enabled) case Bootloader.Grub: - self._add_grub_bootloader(boot_partition, root_partition, efi_partition) + self._add_grub_bootloader(boot_partition, root_partition, efi_partition, uki_enabled) case Bootloader.Efistub: - self._add_efistub_bootloader(boot_partition, root_partition) + self._add_efistub_bootloader(boot_partition, root_partition, uki_enabled) case Bootloader.Limine: self._add_limine_bootloader(boot_partition, root_partition) diff --git a/archinstall/lib/interactions/__init__.py b/archinstall/lib/interactions/__init__.py index 50c0012d..4b696a78 100644 --- a/archinstall/lib/interactions/__init__.py +++ b/archinstall/lib/interactions/__init__.py @@ -15,5 +15,5 @@ from .general_conf import ( ) from .system_conf import ( - select_kernel, ask_for_bootloader, select_driver, ask_for_swap + select_kernel, ask_for_bootloader, ask_for_uki, select_driver, ask_for_swap ) diff --git a/archinstall/lib/interactions/system_conf.py b/archinstall/lib/interactions/system_conf.py index 0e5e0f1e..aa72748e 100644 --- a/archinstall/lib/interactions/system_conf.py +++ b/archinstall/lib/interactions/system_conf.py @@ -65,6 +65,22 @@ def ask_for_bootloader(preset: Bootloader) -> Bootloader: return preset +def ask_for_uki(preset: bool = True) -> bool: + if preset: + preset_val = Menu.yes() + else: + preset_val = Menu.no() + + prompt = _('Would you like to use unified kernel images?') + choice = Menu(prompt, Menu.yes_no(), default_option=Menu.no(), preset_values=preset_val).run() + + match choice.type_: + case MenuSelectionType.Skip: return preset + case MenuSelectionType.Selection: return False if choice.value == Menu.no() else True + + return preset + + def select_driver(options: List[GfxDriver] = [], current_value: Optional[GfxDriver] = None) -> Optional[GfxDriver]: """ Some what convoluted function, whose job is simple. diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index d7cf16cd..fdf05c99 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -56,6 +56,8 @@ def ask_user_questions(): # Ask which boot-loader to use (will only ask if we're in UEFI mode, otherwise will default to GRUB) global_menu.enable('bootloader') + global_menu.enable('uki') + global_menu.enable('swap') # Get the hostname for the machine @@ -111,6 +113,7 @@ def perform_installation(mountpoint: Path): # Retrieve list of additional repositories and set boolean values appropriately enable_testing = 'testing' in archinstall.arguments.get('additional-repositories', []) enable_multilib = 'multilib' in archinstall.arguments.get('additional-repositories', []) + run_mkinitcpio = not archinstall.arguments.get('uki') locale_config: locale.LocaleConfiguration = archinstall.arguments['locale_config'] disk_encryption: disk.DiskEncryption = archinstall.arguments.get('disk_encryption', None) @@ -141,6 +144,7 @@ def perform_installation(mountpoint: Path): installation.minimal_installation( testing=enable_testing, multilib=enable_multilib, + mkinitcpio=run_mkinitcpio, hostname=archinstall.arguments.get('hostname', 'archlinux'), locale_config=locale_config ) @@ -154,7 +158,10 @@ def perform_installation(mountpoint: Path): if archinstall.arguments.get("bootloader") == Bootloader.Grub and SysInfo.has_uefi(): installation.add_additional_packages("grub") - installation.add_bootloader(archinstall.arguments["bootloader"]) + installation.add_bootloader( + archinstall.arguments["bootloader"], + archinstall.arguments["uki"] + ) # If user selected to copy the current ISO network configuration # Perform a copy of the config diff --git a/schema.json b/schema.json index 5616ed41..b1d45f64 100644 --- a/schema.json +++ b/schema.json @@ -35,6 +35,10 @@ "efistub" ] }, + "uki": { + "description": "Set to true to use a unified kernel images", + "type": "boolean" + }, "custom-commands": { "description": "Custom commands to be run post install", "type": "array", -- cgit v1.2.3-70-g09d2 From 8117c0eed8cdeffc542026a82a51cb10fd6c1ec4 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Wed, 18 Oct 2023 01:54:29 -0400 Subject: Fix EFISTUB `cmdline` (#2181) --- archinstall/lib/installer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 4d6c65b3..2aa0d9dd 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -1105,10 +1105,10 @@ TIMEOUT=5 *self._get_kernel_params(root_partition) ) - cmdline = tuple(' '.join(entries)) + cmdline = [' '.join(entries)] else: loader = '/EFI/Linux/arch-{kernel}.efi' - cmdline = tuple() + cmdline = [] parent_dev_path = disk.device_handler.get_parent_device_path(boot_partition.safe_dev_path) -- cgit v1.2.3-70-g09d2 From ce9828c2934937217daee6ba178dafedf6cabe6d Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Wed, 18 Oct 2023 16:57:44 +1100 Subject: Fixes for prev PR (#2175) * Fixes for prev PR * Update --------- Co-authored-by: Daniel Girtler --- archinstall/__init__.py | 21 ++++++++++++++++++--- archinstall/lib/exceptions.py | 16 ++++++++-------- archinstall/lib/networking.py | 15 +-------------- archinstall/scripts/guided.py | 7 ------- archinstall/scripts/only_hd.py | 13 +------------ archinstall/scripts/swiss.py | 12 ------------ 6 files changed, 28 insertions(+), 56 deletions(-) diff --git a/archinstall/__init__.py b/archinstall/__init__.py index 3949d38a..b9a0c2fb 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -89,6 +89,8 @@ def define_arguments(): parser.add_argument("--no-pkg-lookups", action="store_true", default=False, help="Disabled package validation specifically prior to starting installation.") parser.add_argument("--plugin", nargs="?", type=str) + parser.add_argument("--skip-version-check", action="store_true", + help="Skip the version check when running archinstall") def parse_unspecified_argument_list(unknowns: list, multiple: bool = False, err: bool = False) -> dict: @@ -280,8 +282,20 @@ def plugin(f, *args, **kwargs): def _check_new_version(): info("Checking version...") - Pacman.run("-Sy") - upgrade = Pacman.run("-Qu archinstall").decode() + + try: + Pacman.run("-Sy") + except Exception as e: + debug(f'Failed to perform version check: {e}') + info(f'Arch Linux mirrors are not reachable. Please check your internet connection') + exit(1) + + upgrade = None + + try: + upgrade = Pacman.run("-Qu archinstall").decode() + except Exception as e: + debug(f'Failed determine pacman version: {e}') if upgrade: text = f'New version available: {upgrade}' @@ -295,7 +309,8 @@ def main(): OR straight as a module: python -m archinstall In any case we will be attempting to load the provided script to be run from the scripts/ folder """ - _check_new_version() + if not arguments.get('skip_version_check', False): + _check_new_version() script = arguments.get('script', None) diff --git a/archinstall/lib/exceptions.py b/archinstall/lib/exceptions.py index 53458d2c..80926e0b 100644 --- a/archinstall/lib/exceptions.py +++ b/archinstall/lib/exceptions.py @@ -4,19 +4,19 @@ if TYPE_CHECKING: from .general import SysCommandWorker -class RequirementError(BaseException): +class RequirementError(Exception): pass -class DiskError(BaseException): +class DiskError(Exception): pass -class UnknownFilesystemFormat(BaseException): +class UnknownFilesystemFormat(Exception): pass -class SysCallError(BaseException): +class SysCallError(Exception): def __init__(self, message :str, exit_code :Optional[int] = None, worker :Optional['SysCommandWorker'] = None) -> None: super(SysCallError, self).__init__(message) self.message = message @@ -24,17 +24,17 @@ class SysCallError(BaseException): self.worker = worker -class HardwareIncompatibilityError(BaseException): +class HardwareIncompatibilityError(Exception): pass -class ServiceException(BaseException): +class ServiceException(Exception): pass -class PackageError(BaseException): +class PackageError(Exception): pass -class Deprecated(BaseException): +class Deprecated(Exception): pass diff --git a/archinstall/lib/networking.py b/archinstall/lib/networking.py index 2a086f39..fb26bd3d 100644 --- a/archinstall/lib/networking.py +++ b/archinstall/lib/networking.py @@ -8,7 +8,7 @@ from urllib.parse import urlencode from urllib.request import urlopen from .exceptions import SysCallError -from .output import error, info, debug +from .output import error, info from .pacman import Pacman @@ -32,19 +32,6 @@ def list_interfaces(skip_loopback :bool = True) -> Dict[str, str]: return interfaces -def check_mirror_reachable() -> bool: - info("Testing connectivity to the Arch Linux mirrors...") - try: - Pacman.run("-Sy") - return True - except SysCallError as err: - if os.geteuid() != 0: - error("check_mirror_reachable() uses 'pacman -Sy' which requires root.") - debug(f'exit_code: {err.exit_code}, Error: {err.message}') - - return False - - def update_keyring() -> bool: info("Updating archlinux-keyring ...") try: diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index fdf05c99..6acbdbf3 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -1,4 +1,3 @@ -import os from pathlib import Path from typing import Any, TYPE_CHECKING, Optional @@ -15,7 +14,6 @@ from archinstall.lib.mirrors import use_mirrors, add_custom_mirrors from archinstall.lib.models import AudioConfiguration from archinstall.lib.models.bootloader import Bootloader from archinstall.lib.models.network_configuration import NetworkConfiguration -from archinstall.lib.networking import check_mirror_reachable from archinstall.lib.profile.profiles_handler import profile_handler if TYPE_CHECKING: @@ -232,11 +230,6 @@ def perform_installation(mountpoint: Path): debug(f"Disk states after installing: {disk.disk_layouts()}") -if archinstall.arguments.get('skip-mirror-check', False) is False and check_mirror_reachable() is False: - log_file = os.path.join(archinstall.storage.get('LOG_PATH', None), archinstall.storage.get('LOG_FILE', None)) - info(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'.") - exit(1) - if not archinstall.arguments.get('silent'): ask_user_questions() diff --git a/archinstall/scripts/only_hd.py b/archinstall/scripts/only_hd.py index d0ee1e39..2293cbee 100644 --- a/archinstall/scripts/only_hd.py +++ b/archinstall/scripts/only_hd.py @@ -1,16 +1,10 @@ -import os from pathlib import Path import archinstall -from archinstall import info, debug +from archinstall import debug from archinstall.lib.installer import Installer from archinstall.lib.configuration import ConfigurationOutput from archinstall.lib import disk -from archinstall.lib.networking import check_mirror_reachable - -if archinstall.arguments.get('help'): - print("See `man archinstall` for help.") - exit(0) def ask_user_questions(): @@ -58,11 +52,6 @@ def perform_installation(mountpoint: Path): debug(f"Disk states after installing: {disk.disk_layouts()}") -if archinstall.arguments.get('skip-mirror-check', False) is False and check_mirror_reachable() is False: - log_file = os.path.join(archinstall.storage.get('LOG_PATH', None), archinstall.storage.get('LOG_FILE', None)) - info(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'") - exit(1) - if not archinstall.arguments.get('silent'): ask_user_questions() diff --git a/archinstall/scripts/swiss.py b/archinstall/scripts/swiss.py index c04ccca4..1db304a8 100644 --- a/archinstall/scripts/swiss.py +++ b/archinstall/scripts/swiss.py @@ -1,4 +1,3 @@ -import os from enum import Enum from pathlib import Path from typing import TYPE_CHECKING, Any, Dict, Optional @@ -10,7 +9,6 @@ from archinstall.lib import models from archinstall.lib import disk from archinstall.lib import locale from archinstall.lib.models import AudioConfiguration -from archinstall.lib.networking import check_mirror_reachable from archinstall.lib.profile.profiles_handler import profile_handler from archinstall.lib import menu from archinstall.lib.global_menu import GlobalMenu @@ -21,11 +19,6 @@ if TYPE_CHECKING: _: Any -if archinstall.arguments.get('help'): - print("See `man archinstall` for help.") - exit(0) - - class ExecutionMode(Enum): Full = 'full' Lineal = 'lineal' @@ -290,11 +283,6 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode): debug(f"Disk states after installing: {disk.disk_layouts()}") -if not check_mirror_reachable(): - log_file = os.path.join(archinstall.storage.get('LOG_PATH', None), archinstall.storage.get('LOG_FILE', None)) - info(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'") - exit(1) - param_mode = archinstall.arguments.get('mode', ExecutionMode.Full.value).lower() try: -- cgit v1.2.3-70-g09d2 From 78ab858b3bba488925f08df917350a854e52dc2d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 08:00:16 +0200 Subject: chore(deps): update dependency dev/mypy to v1.6.1 (#2184) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 551b9241..1e70f9f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ Source = "https://github.com/archlinux/archinstall" [project.optional-dependencies] dev = [ - "mypy==1.6.0", + "mypy==1.6.1", "pre-commit==3.5.0", ] doc = ["sphinx"] -- cgit v1.2.3-70-g09d2 From 474861c15516cc046758cc9b148ce2608dd1c9be Mon Sep 17 00:00:00 2001 From: "Celestial.y" <129247596+clsty@users.noreply.github.com> Date: Mon, 23 Oct 2023 02:26:51 -0500 Subject: Update translation of zh-CN (#2165) --- archinstall/locales/README.md | 2 + archinstall/locales/zh-CN/LC_MESSAGES/base.mo | Bin 33967 -> 36412 bytes archinstall/locales/zh-CN/LC_MESSAGES/base.po | 176 ++++++++++++-------------- 3 files changed, 85 insertions(+), 93 deletions(-) diff --git a/archinstall/locales/README.md b/archinstall/locales/README.md index de1aa83a..2e18f6a1 100644 --- a/archinstall/locales/README.md +++ b/archinstall/locales/README.md @@ -32,6 +32,8 @@ msgstr "" The `msgid` is the identifier of the string in the code as well as the default text to be displayed, meaning that if no translation is provided for a language then this is the text that is going to be shown. +## Perform translations + To perform translations for a language this file can be edited manually or the neat `poedit` can be used (https://poedit.net/). If editing the file manually, write the translation in the `msgstr` part diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.mo b/archinstall/locales/zh-CN/LC_MESSAGES/base.mo index c7208160..b7d388bf 100644 Binary files a/archinstall/locales/zh-CN/LC_MESSAGES/base.mo and b/archinstall/locales/zh-CN/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/zh-CN/LC_MESSAGES/base.po b/archinstall/locales/zh-CN/LC_MESSAGES/base.po index 4c620836..f5ada902 100644 --- a/archinstall/locales/zh-CN/LC_MESSAGES/base.po +++ b/archinstall/locales/zh-CN/LC_MESSAGES/base.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" -"Last-Translator: yetist \n" +"Last-Translator: clsty \n" "Language-Team: \n" "Language: zh_CN\n" "MIME-Version: 1.0\n" @@ -12,7 +12,7 @@ msgstr "" "X-Generator: Poedit 3.3.2\n" msgid "[!] A log file has been created here: {} {}" -msgstr "[!] 日志文件已在此处创建:{} {}" +msgstr "[!] 一份日志文件已在此处创建:{} {}" msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" msgstr " 请将此问题(和文件)提交到 https://github.com/archlinux/archinstall/issues" @@ -51,16 +51,16 @@ msgid "Choose an audio server" msgstr "请选择一个音频服务器(audio server)" msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." -msgstr "仅安装基本软件包(base),基本开发软件包(base-devel),Linux 内核(linux),Linux 固件(linux-firmware),efibootmgr 和可选的配置文件软件包(profile packages)。" +msgstr "仅安装基本软件包(base)、基本开发软件包(base-devel)、Linux 内核(linux)、Linux 固件(linux-firmware)、efibootmgr 和可选的配置文件软件包(profile packages)。" msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." -msgstr "如果您需要一个网络浏览器,例如 firefox 或 chromium,你可以在下面的提示中指定它。" +msgstr "如果您需要一个网络浏览器,例如 firefox 或 chromium,您可以在下面的提示中指定它。" msgid "Write additional packages to install (space separated, leave blank to skip): " msgstr "输入要安装的其他软件包(空格分隔,留空以跳过):" msgid "Copy ISO network configuration to installation" -msgstr "将 ISO 中的网络配置复制到安装中" +msgstr "将 ISO 中的网络配置复制到安装目标中" msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" msgstr "使用 NetworkManager(在 GNOME 和 KDE 中以图形方式配置 Internet 所必需的)" @@ -69,13 +69,13 @@ msgid "Select one network interface to configure" msgstr "选择要配置的网络接口" msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" -msgstr "选择要为 \"{}\" 配置的模式或跳过以使用默认模式 \"{}\"" +msgstr "选择要为“{}”配置的模式或跳过以使用默认模式“{}”" msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " msgstr "输入 {} 的 IP 和子网(例如:192.168.0.5/24):" msgid "Enter your gateway (router) IP address or leave blank for none: " -msgstr "请输入您的网关(路由器)IP地址,如果没有请留空:" +msgstr "请输入您的网关(路由器)IP 地址,如果没有请留空:" msgid "Enter your DNS servers (space separated, blank for none): " msgstr "请输入您的 DNS 服务器地址(以空格分隔,如果没有请留空):" @@ -121,10 +121,10 @@ msgid "" msgstr "" "{}\n" "\n" -"按索引选择要挂载的分区" +"按索引选择要挂载的分区及挂载位置" msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr " * 分区挂载点是相对于安装目录内部的,例如引导分区(boot)的挂载点为 /boot。" +msgstr " * 分区挂载点是相对于安装目标的目录内部的,例如引导分区(boot)的挂载点为 /boot。" msgid "Select where to mount partition (leave blank to remove mountpoint): " msgstr "选择要挂载分区的位置(留空表示移除挂载点):" @@ -178,16 +178,16 @@ msgid "Select what to do with each individual drive (followed by partition usage msgstr "选择对每个单独的驱动器执行的操作(后跟分区使用情况)" msgid "Select what you wish to do with the selected block devices" -msgstr "选择要对所选硬盘驱动器执行的操作" +msgstr "选择要对所选的(一个或多个)块设备(block device)执行的操作" msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments" -msgstr "这是预先编写的配置文件列表,它们可能会使安装桌面环境等内容变得更加容易" +msgstr "这份列表列出了预先编写的配置文件,它们可能会使安装桌面环境等内容变得更加容易" msgid "Select keyboard layout" msgstr "选择键盘布局" msgid "Select one of the regions to download packages from" -msgstr "选择要从中下载软件包的区域(可多选)" +msgstr "选择一个要从中下载软件包的区域" msgid "Select one or more hard drives to use and configure" msgstr "选择要使用和配置的硬盘驱动器(可多选)" @@ -235,7 +235,7 @@ msgid "You need to enter a valid fs-type in order to continue. See `man parted` msgstr "您需要输入有效的文件系统类型才能继续。请参阅 `man parted` 以获取有效的文件系统类型。" msgid "Error: Listing profiles on URL \"{}\" resulted in:" -msgstr "错误:在 URL “{}” 上列出配置文件时出错:" +msgstr "错误:列出位于 URL “{}” 上的配置文件时出错:" msgid "Error: Could not decode \"{}\" result as JSON:" msgstr "错误:无法将“{}”结果解码为 JSON:" @@ -304,8 +304,8 @@ msgid "" "Do you wish to continue?" msgstr "" "您决定跳过硬盘驱动器选择\n" -"并将使用挂载在 {}(实验性)上的任何驱动器设置\n" -"警告:Archinstall 不会检查此设置的适用性\n" +"并将使用挂载在 {} 上的任何驱动器设置(实验性)\n" +"警告:Archinstall 将不会检查此设置的适用性\n" "您是否要继续?" msgid "Re-using partition instance: {}" @@ -330,7 +330,7 @@ msgid "Mark/Unmark a partition as encrypted" msgstr "将分区标记/取消标记为加密" msgid "Mark/Unmark a partition as bootable (automatic for /boot)" -msgstr "将分区标记/取消标记为可引导(/boot 自动引导)" +msgstr "将分区标记/取消标记为可引导(/boot 会自动设置为可引导)" msgid "Set desired filesystem for a partition" msgstr "为分区设置所需的文件系统" @@ -342,7 +342,7 @@ msgid "Hostname" msgstr "主机名" msgid "Not configured, unavailable unless setup manually" -msgstr "未配置,除非手动设置,否则不可用" +msgstr "未配置;除非手动设置,否则不可用" msgid "Timezone" msgstr "时区" @@ -361,7 +361,7 @@ msgstr "" "\n" msgid "Suggest partition layout" -msgstr "建议的分区布局" +msgstr "建议分区布局" msgid "Enter a password: " msgstr "输入密码:" @@ -373,7 +373,7 @@ msgid "Enter disk encryption password (leave blank for no encryption): " msgstr "输入磁盘加密密码(留空则不加密):" msgid "Create a required super-user with sudo privileges: " -msgstr "创建具有 sudo 权限的必需超级用户:" +msgstr "创建所需的具有 sudo 权限的超级用户:" msgid "Enter root password (leave blank to disable root): " msgstr "输入 root 密码(留空以禁用 root):" @@ -382,7 +382,7 @@ msgid "Password for user \"{}\": " msgstr "用户“{}”的密码:" msgid "Verifying that additional packages exist (this might take a few seconds)" -msgstr "正在验证附加软件包是否存在(这可能需要几秒钟)" +msgstr "正在验证附加软件包是否存在(这可能需要几秒或几十秒)" msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" msgstr "您是否希望使用默认时间服务器进行自动时间同步(NTP)?\n" @@ -391,8 +391,8 @@ msgid "" "Hardware time and other post-configuration steps might be required in order for NTP to work.\n" "For more information, please check the Arch wiki" msgstr "" -"可能需要硬件时间和其他后配置步骤才能使 NTP 正常工作。\n" -"有关更多信息,请查看 Arch wiki" +"为了使 NTP 正常工作,可能需要在之后进行硬件时间及其他配置的步骤。\n" +"更多信息,请查阅 Arch wiki" msgid "Enter a username to create an additional user (leave blank to skip): " msgstr "输入用户名以创建其他用户(留空以跳过):" @@ -405,7 +405,7 @@ msgid "" " Choose an object from the list, and select one of the available actions for it to execute" msgstr "" "\n" -" 从列表中选择一个对象,并选择要执行的操作" +" 从列表中选取一个对象,并选择一个要执行的可选操作" msgid "Cancel" msgstr "取消" @@ -448,7 +448,7 @@ msgid "Pre-existing pacman lock never exited. Please clean up any existing pacma msgstr "预先存在的 pacman 锁从未退出。请在使用 archinstall 之前清理任何现有的 pacman 会话。" msgid "Choose which optional additional repositories to enable" -msgstr "选择要启用的可选附加存储库" +msgstr "选择要启用的可选附加仓库" msgid "Add a user" msgstr "添加一个用户" @@ -473,7 +473,7 @@ msgid "User Name : " msgstr "用户名:" msgid "Should {} be a superuser (sudoer)?" -msgstr "是否将{}设置为超级用户(sudoer)?" +msgstr "是否将 {} 设置为超级用户(sudoer)?" msgid "Define users with sudo privilege: " msgstr "定义具有 sudo 权限的用户:" @@ -527,7 +527,7 @@ msgid "are you sure you want to use it?" msgstr "您确定要使用它吗?" msgid "Optional repositories" -msgstr "可选存储库" +msgstr "可选仓库" msgid "Save configuration" msgstr "保存配置" @@ -558,7 +558,7 @@ msgid "" " Fill the desired values for a new subvolume \n" msgstr "" "\n" -" 填写新子卷所需的值 \n" +" 为新子卷填写所需的值 \n" msgid "Subvolume name " msgstr "子卷名称 " @@ -585,10 +585,10 @@ msgid "Define users with sudo privilege, by username: " msgstr "通过用户名定义具有 sudo 权限的用户:" msgid "[!] A log file has been created here: {}" -msgstr "[!] 已在此处创建日志文件:{}" +msgstr "[!] 已在此处创建一份日志文件:{}" msgid "Would you like to use BTRFS subvolumes with a default structure?" -msgstr "您想使用带有默认结构的 BTRFS 子卷吗?" +msgstr "您想以默认结构使用 BTRFS 子卷吗?" msgid "Would you like to use BTRFS compression?" msgstr "您想使用 BTRFS 压缩吗?" @@ -597,7 +597,7 @@ msgid "Would you like to create a separate partition for /home?" msgstr "您想为 /home 创建一个单独的分区吗?" msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" -msgstr "所选驱动器没有自动建议所需的最小容量\n" +msgstr "所选驱动器不具有自动建议所需的最小容量\n" msgid "Minimum capacity for /home partition: {}GB\n" msgstr "/home 分区的最小容量:{}GB\n" @@ -618,13 +618,13 @@ msgid "set: {}" msgstr "设置:{}" msgid "Manual configuration setting must be a list" -msgstr "手动配置设置必须是一个列表" +msgstr "手动配置的设置必须是一个列表" msgid "No iface specified for manual configuration" msgstr "没有为手动配置指定网卡接口" msgid "Manual nic configuration with no auto DHCP requires an IP address" -msgstr "没有自动 DHCP 的手动网卡配置需要 IP 地址" +msgstr "不具备自动 DHCP 的手动网卡配置需要一个 IP 地址" msgid "Add interface" msgstr "添加接口" @@ -648,7 +648,7 @@ msgid "The password you are using seems to be weak, are you sure you want to use msgstr "您正在使用的密码似乎很弱,您确定要使用它吗?" msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" -msgstr "提供一系列桌面环境和平铺窗口管理器的选择,例如 gnome、kde、sway" +msgstr "提供一系列桌面环境和平铺窗口管理器供选择,例如 gnome、kde、sway" msgid "Select your desired desktop environment" msgstr "选择您所需的桌面环境" @@ -657,13 +657,13 @@ msgid "A very basic installation that allows you to customize Arch Linux as you msgstr "一个非常基本的安装,允许您根据需要自定义 Arch Linux。" msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" -msgstr "提供一系列可安装和启用的各种服务器软件包的选择,例如 httpd、nginx、mariadb" +msgstr "提供一系列的多种服务器软件包以供安装启用,例如 httpd、nginx、mariadb" msgid "Choose which servers to install, if none then a minimal installation will be done" -msgstr "选择要安装的服务器,如果没有,则将执行最小安装" +msgstr "选择要安装的服务器,若无则将执行最小化安装" msgid "Installs a minimal system as well as xorg and graphics drivers." -msgstr "安装一个最小系统以及 xorg 和显卡驱动程序。" +msgstr "安装一个最小化系统以及 xorg 和显卡驱动程序。" msgid "Press Enter to continue." msgstr "按 Enter 继续。" @@ -681,7 +681,7 @@ msgid "Any modifications to the existing setting will reset the disk layout!" msgstr "对现有设置的任何修改都将重置磁盘布局!" msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" -msgstr "如果重置硬盘驱动器选择,则当前磁盘布局也将重置。你确定吗?" +msgstr "如果重置硬盘驱动器选择,则当前磁盘布局也将重置。您确定吗?" msgid "Save and exit" msgstr "保存并退出" @@ -738,7 +738,7 @@ msgid "Use HSM to unlock encrypted drive" msgstr "使用 HSM 解锁加密驱动器" msgid "Device" -msgstr "驱动器" +msgstr "设备" msgid "Size" msgstr "大小" @@ -801,7 +801,7 @@ msgstr "" "提示:" msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" -msgstr " - 最大值:{}(允许 {} 次并行下载,一次允许 {} 次下载)" +msgstr " - 最大值:{}(允许 {} 个并行下载,即同时允许 {} 个下载)" msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" msgstr " - 最小值:1(允许 1 个并行下载,同时允许 2 个下载)" @@ -811,7 +811,7 @@ msgstr " - 禁用/默认值:0(禁用并行下载,同时只允许 1 个下 #, python-brace-format msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" -msgstr "输入无效! 使用有效输入重试 [1 到 {max_downloads},或 0 禁用]" +msgstr "输入无效!请重试一个有效输入 [1 到 {max_downloads},或 0 以禁用]" msgid "Parallel Downloads" msgstr "并行下载" @@ -844,13 +844,13 @@ msgid "Unable to fetch profile from specified url: {}" msgstr "无法从指定的 URL 获取配置文件:{}" msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" -msgstr "配置文件必须具有唯一的名称,但找到具有重复名称的配置文件定义:{}" +msgstr "配置文件必须具有唯一的名称,但找到了具有重复名称的配置文件定义:{}" msgid "Select one or more devices to use and configure" -msgstr "选择要使用和配置的驱动器(可多选)" +msgstr "选择要使用并配置的设备(可多选)" msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" -msgstr "如果重置硬盘驱动器选择,则当前磁盘布局也将重置。你确定吗?" +msgstr "如果重置设备选择,则当前磁盘布局也将重置。您确定吗?" msgid "Existing Partitions" msgstr "现有分区" @@ -880,7 +880,7 @@ msgid "Assign mountpoint" msgstr "分配挂载点" msgid "Mark/Unmark to be formatted (wipes data)" -msgstr "将分区标记/取消标记为格式化(清除数据)" +msgstr "将分区标记/取消标记为格式化(擦除数据)" msgid "Mark/Unmark as bootable" msgstr "将分区标记/取消标记为可引导" @@ -889,7 +889,7 @@ msgid "Change filesystem" msgstr "更改文件系统" msgid "Mark/Unmark as compressed" -msgstr "将分区标记/取消标记为压缩" +msgstr "标记/取消标记为压缩" msgid "Set subvolumes" msgstr "设置子卷" @@ -904,7 +904,7 @@ msgid "This partition is currently encrypted, to format it a filesystem has to b msgstr "此分区当前已加密,要格式化它必须指定文件系统" msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." -msgstr "分区挂载点是相对于安装目录内部的,例如引导分区(boot)的挂载点为 /boot。" +msgstr "分区挂载点是相对于安装目标的目录内部的,例如引导分区(boot)的挂载点为 /boot。" msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." msgstr "如果设置了挂载点 /boot,则该分区也将被标记为可引导。" @@ -913,7 +913,7 @@ msgid "Mountpoint: " msgstr "挂载点:" msgid "Current free sectors on device {}:" -msgstr "驱动器 {} 上当前可用的扇区:" +msgstr "设备 {} 上当前可用的扇区:" msgid "Total sectors: {}" msgstr "总扇区数:{}" @@ -982,7 +982,7 @@ msgid "Password" msgstr "密码" msgid "All settings will be reset, are you sure?" -msgstr "所有设置将被重置,你确定吗?" +msgstr "所有设置将被重置,您确定吗?" msgid "Back" msgstr "返回" @@ -994,7 +994,7 @@ msgid "Environment type: {}" msgstr "环境类型:{}" msgid "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?" -msgstr "Sway 不支持 Nvidia 的专有驱动。 您可能会遇到问题,您确定要继续吗?" +msgstr "Sway 不支持 Nvidia 的专有驱动。您可能会遇到问题,您确定要继续吗?" msgid "Installed packages" msgstr "已安装的软件包" @@ -1012,19 +1012,19 @@ msgid "Profile name: " msgstr "配置文件名称:" msgid "The profile name you entered is already in use. Try again" -msgstr "您输入的配置文件名称已被使用。 请重试" +msgstr "您输入的配置文件名称已被使用。请重试" msgid "Packages to be install with this profile (space separated, leave blank to skip): " -msgstr "编写要安装的附加软件包(空格分隔,留空跳过):" +msgstr "要与此配置文件一同安装的软件包(空格分隔,留空跳过):" msgid "Services to be enabled with this profile (space separated, leave blank to skip): " -msgstr "编写要启用的附加服务(空格分隔,留空跳过):" +msgstr "要与此配置文件一同启用的服务(空格分隔,留空跳过):" msgid "Should this profile be enabled for installation?" msgstr "是否启用此配置文件进行安装?" msgid "Create your own" -msgstr "创建自己的" +msgstr "创建您自己的" msgid "" "\n" @@ -1035,7 +1035,7 @@ msgstr "" "选择一个显卡驱动程序,或留空以安装所有开源驱动程序" msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" -msgstr "Sway 需要访问您的用户环境(硬件设备的集合,例如键盘,鼠标等)" +msgstr "Sway 需要访问您的 seat(硬件设备的集合,例如键盘、鼠标等)" msgid "" "\n" @@ -1056,7 +1056,7 @@ msgid "Please chose which greeter to install" msgstr "请选择要安装的登录管理器" msgid "This is a list of pre-programmed default_profiles" -msgstr "这是预设的默认配置文件列表" +msgstr "这份列表列出了预编写的默认配置文件" msgid "Disk configuration" msgstr "磁盘配置" @@ -1068,7 +1068,7 @@ msgid "Finding possible directories to save configuration files ..." msgstr "正在查找可能用于保存配置文件的目录 ..." msgid "Select directory (or directories) for saving configuration files" -msgstr "选择一个或多个硬盘驱动器来使用和配置" +msgstr "选择一个或多个目录保存配置文件" msgid "Add a custom mirror" msgstr "添加自定义镜像" @@ -1080,10 +1080,10 @@ msgid "Delete custom mirror" msgstr "删除自定义镜像" msgid "Enter name (leave blank to skip): " -msgstr "输入用户名(留空跳过): " +msgstr "输入用户名(留空跳过):" msgid "Enter url (leave blank to skip): " -msgstr "输入网址(留空跳过): " +msgstr "输入网址(留空跳过):" msgid "Select signature check option" msgstr "选择签名检查选项" @@ -1104,8 +1104,8 @@ msgid "" "Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" "Save directory: " msgstr "" -"输入要保存配置的目录(制表符可补全)\n" -"保存目录: " +"输入要保存配置的目录(可按 TAB 补全)\n" +"保存目录:" msgid "" "Do you want to save {} configuration file(s) in the following location?\n" @@ -1117,7 +1117,7 @@ msgstr "" "{}" msgid "Saving {} configuration files to {}" -msgstr "将 {} 配置文件保存到 {}" +msgstr "正在将 {} 配置文件保存到 {}" msgid "Mirrors" msgstr "镜像" @@ -1129,7 +1129,7 @@ msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {max_downl msgstr " - 最大值:{}(允许 {} 个并行下载,每次允许 {max_downloads+1} 个下载)" msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" -msgstr "输入无效! 使用有效输入重试 [1 到 {},或 0 到禁用]" +msgstr "输入无效!请重试一个有效输入 [1 到 {},或 0 以禁用]" msgid "Locales" msgstr "区域设置" @@ -1137,74 +1137,64 @@ msgstr "区域设置" msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" msgstr "使用 NetworkManager(在 GNOME 和 KDE 中以图形方式配置互联网所必需)" -#, fuzzy msgid "Total: {} / {}" -msgstr "总长度:{}" +msgstr "总长度:{} / {}" msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." -msgstr "" +msgstr "所有输入的值都可以后缀一个单位:B、KB、KiB、MB、MiB……" msgid "If no unit is provided, the value is interpreted as sectors" -msgstr "" +msgstr "若没有指定单位,则值被作为扇区块号" -#, fuzzy msgid "Enter start (default: sector {}): " -msgstr "输入起始扇区(百分比或块号,默认:{}): " +msgstr "输入起始扇区位置(默认:扇区 {}): " -#, fuzzy msgid "Enter end (default: {}): " -msgstr "输入起始扇区(百分比或块号,默认:{}): " +msgstr "输入末尾扇区位置(默认:{}): " msgid "Unable to determine fido2 devices. Is libfido2 installed?" -msgstr "" +msgstr "无法确定 fido2 设备。libfido2 是否已安装?" msgid "Path" -msgstr "" +msgstr "路径" msgid "Manufacturer" -msgstr "" +msgstr "制造商" msgid "Product" -msgstr "" +msgstr "产品" -#, fuzzy, python-brace-format +#, python-brace-format msgid "Invalid configuration: {error}" -msgstr "手动配置" +msgstr "无效的配置:{error}" msgid "Type" -msgstr "" +msgstr "类型" -#, fuzzy msgid "This option enables the number of parallel downloads that can occur during package downloads" -msgstr "此选项启用安装期间可以发生的并行下载数" +msgstr "此选项启用软件包下载期间可以发生的并行下载数" -#, fuzzy msgid "" "Enter the number of parallel downloads to be enabled.\n" "\n" "Note:\n" msgstr "" "输入要启用的并行下载数。\n" -" (输入一个介于 1 到 {} 之间的值)\n" -"提示:" +"\n" +"提示:\n" -#, fuzzy msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" -msgstr " - 最大值:{}(允许 {} 次并行下载,一次允许 {} 次下载)" +msgstr " - 最大推荐值:{}(每次同时允许 {} 个并行下载)" -#, fuzzy msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" -msgstr " - 禁用/默认值:0(禁用并行下载,同时只允许 1 个下载)" +msgstr " - 禁用/默认值:0(禁用并行下载,同时只允许 1 个下载)\n" -#, fuzzy msgid "Invalid input! Try again with a valid input [or 0 to disable]" -msgstr "输入无效! 使用有效输入重试 [1 到 {},或 0 到禁用]" +msgstr "输入无效!请重试有效输入 [或 0 以禁用]" -#, fuzzy msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" -msgstr "Sway 需要访问您的用户环境(硬件设备的集合,例如键盘,鼠标等)" +msgstr "Hyprland 需要访问您的 seat(硬件设备的集合,例如键盘、鼠标等)" -#, fuzzy msgid "" "\n" "\n" @@ -1212,7 +1202,7 @@ msgid "" msgstr "" "\n" "\n" -"选择一个选项来给 Sway 提供对您硬件的访问权限" +"选择一个选项来给 Hyprland 提供对您硬件的访问权限" msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." -msgstr "" +msgstr "所有输入的值都可以后缀一个单位:%、B、KB、KiB、MB、MiB……" -- cgit v1.2.3-70-g09d2 From 2831c97338ca3b60dc1c697d158cd3f6281746d7 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Wed, 25 Oct 2023 03:27:18 -0400 Subject: Fix password preview (#2190) --- archinstall/lib/output.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py index 945a6c4f..62a1ba27 100644 --- a/archinstall/lib/output.py +++ b/archinstall/lib/output.py @@ -38,6 +38,8 @@ class FormattedOutput: raise ValueError('Unsupported formatting call') elif hasattr(o, 'table_data'): return o.table_data() + elif hasattr(o, 'json'): + return o.json() elif is_dataclass(o): return asdict(o) else: -- cgit v1.2.3-70-g09d2 From 5b6cab27f10bffa343eeff573b2bf90262da634b Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Tue, 31 Oct 2023 12:57:59 -0400 Subject: Fix logic error in `_fetch_lsblk_info()` loop (#2196) --- archinstall/lib/disk/device_model.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index cd955851..15e68116 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -1106,10 +1106,7 @@ def _fetch_lsblk_info(dev_path: Optional[Union[Path, str]] = None, retry: int = if not dev_path: dev_path = '' - if retry == 0: - retry = 1 - - for retry_attempt in range(retry): + for retry_attempt in range(retry + 1): try: result = SysCommand(f'lsblk --json -b -o+{lsblk_fields} {dev_path}').decode() break @@ -1121,7 +1118,7 @@ def _fetch_lsblk_info(dev_path: Optional[Union[Path, str]] = None, retry: int = else: raise err - if retry_attempt == retry - 1: + if retry_attempt == retry: raise err time.sleep(1) -- cgit v1.2.3-70-g09d2 From c9755b35e7364d208d836ec051d365354be6dd63 Mon Sep 17 00:00:00 2001 From: Wellinton Kricowski Date: Mon, 6 Nov 2023 13:55:50 -0300 Subject: PT BR translation improvements (#2199) --- archinstall/locales/pt_BR/LC_MESSAGES/base.po | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.po b/archinstall/locales/pt_BR/LC_MESSAGES/base.po index 9a72c32a..4ccb439d 100644 --- a/archinstall/locales/pt_BR/LC_MESSAGES/base.po +++ b/archinstall/locales/pt_BR/LC_MESSAGES/base.po @@ -1156,10 +1156,10 @@ msgid "Total: {} / {}" msgstr "Tamanho total: {}" msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." -msgstr "" +msgstr "Todos os valores inseridos podem ser seguidos por uma unidade: B, KB, KiB, MB, MiB..." msgid "If no unit is provided, the value is interpreted as sectors" -msgstr "" +msgstr "Se nenhuma unidade for fornecida, o valor será interpretado como setores" #, fuzzy msgid "Enter start (default: sector {}): " @@ -1173,20 +1173,20 @@ msgid "Unable to determine fido2 devices. Is libfido2 installed?" msgstr "" msgid "Path" -msgstr "" +msgstr "Caminho" msgid "Manufacturer" -msgstr "" +msgstr "Fabricante" msgid "Product" -msgstr "" +msgstr "Produto" #, fuzzy, python-brace-format msgid "Invalid configuration: {error}" msgstr "Configuração manual" msgid "Type" -msgstr "" +msgstr "Tipo" #, fuzzy msgid "This option enables the number of parallel downloads that can occur during package downloads" @@ -1229,7 +1229,7 @@ msgstr "" "Selecione uma opção para permitir o acesso do Sway ao seu hardware" msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." -msgstr "" +msgstr "Todos os valores inseridos podem ser seguidos por uma unidade: B, KB, KiB, MB, MiB..." #~ msgid "When picking a directory to save configuration files to, by default we will ignore the following folders: " #~ msgstr "Ao selecionar um diretório para salvar arquivos de configuração, por padrão nós ignoramos as seguintes pastas: " -- cgit v1.2.3-70-g09d2 From 940468117a64ecc493bde62bd7f27dc9642e976a Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Thu, 9 Nov 2023 06:47:30 -0500 Subject: Fix logic error in `encrypt()` loop (#2207) --- archinstall/lib/luks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/archinstall/lib/luks.py b/archinstall/lib/luks.py index ea09ae7c..4b3b3bc7 100644 --- a/archinstall/lib/luks.py +++ b/archinstall/lib/luks.py @@ -90,14 +90,14 @@ class Luks2: # Retry formatting the volume because archinstall can some times be too quick # which generates a "Device /dev/sdX does not exist or access denied." between # setting up partitions and us trying to encrypt it. - for retry_attempt in range(storage['DISK_RETRY_ATTEMPTS']): + for retry_attempt in range(storage['DISK_RETRY_ATTEMPTS'] + 1): try: SysCommand(cryptsetup_args) break except SysCallError as err: time.sleep(storage['DISK_TIMEOUTS']) - if retry_attempt != storage['DISK_RETRY_ATTEMPTS'] - 1: + if retry_attempt != storage['DISK_RETRY_ATTEMPTS']: continue if err.exit_code == 1: -- cgit v1.2.3-70-g09d2 From af6713e8df0194bf2d293fa83d7ed5d6acaf7cb8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 9 Nov 2023 12:50:26 +0100 Subject: chore(deps): update dependency simple-term-menu to v1.6.2 (#2203) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1e70f9f7..8ef14bf4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ classifiers = [ ] dependencies = [ "pyparted==3.12.0", - "simple-term-menu==1.6.1", + "simple-term-menu==1.6.2", ] [project.urls] -- cgit v1.2.3-70-g09d2 From f6106324eb71905df9b832eb973a08a1f45fa5af Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 9 Nov 2023 20:10:21 +0100 Subject: chore(deps): update dependency simple-term-menu to v1.6.3 (#2208) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8ef14bf4..19320338 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ classifiers = [ ] dependencies = [ "pyparted==3.12.0", - "simple-term-menu==1.6.2", + "simple-term-menu==1.6.3", ] [project.urls] -- cgit v1.2.3-70-g09d2 From 30a374a65b84c2d7dfbb13a4643fb27f31bc71e2 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Mon, 20 Nov 2023 06:54:04 -0500 Subject: Fix parsing pre-mounted disk configuration from configuration file (#2221) --- archinstall/lib/disk/device_model.py | 31 +++++++++++++++++++++++++++---- archinstall/lib/interactions/disk_conf.py | 3 ++- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py index 15e68116..54b4932b 100644 --- a/archinstall/lib/disk/device_model.py +++ b/archinstall/lib/disk/device_model.py @@ -42,12 +42,20 @@ class DiskLayoutType(Enum): class DiskLayoutConfiguration: config_type: DiskLayoutType device_modifications: List[DeviceModification] = field(default_factory=list) + # used for pre-mounted config + mountpoint: Optional[Path] = None def json(self) -> Dict[str, Any]: - return { - 'config_type': self.config_type.value, - 'device_modifications': [mod.json() for mod in self.device_modifications] - } + if self.config_type == DiskLayoutType.Pre_mount: + return { + 'config_type': self.config_type.value, + 'mountpoint': str(self.mountpoint) + } + else: + return { + 'config_type': self.config_type.value, + 'device_modifications': [mod.json() for mod in self.device_modifications] + } @classmethod def parse_arg(cls, disk_config: Dict[str, List[Dict[str, Any]]]) -> Optional[DiskLayoutConfiguration]: @@ -64,6 +72,21 @@ class DiskLayoutConfiguration: device_modifications=device_modifications ) + if config_type == DiskLayoutType.Pre_mount.value: + if not (mountpoint := disk_config.get('mountpoint')): + raise ValueError('Must set a mountpoint when layout type is pre-mount') + + path = Path(str(mountpoint)) + + mods = device_handler.detect_pre_mounted_mods(path) + device_modifications.extend(mods) + + storage['MOUNT_POINT'] = path + + config.mountpoint = path + + return config + for entry in disk_config.get('device_modifications', []): device_path = Path(entry.get('device', None)) if entry.get('device', None) else None diff --git a/archinstall/lib/interactions/disk_conf.py b/archinstall/lib/interactions/disk_conf.py index bf24a22c..8e9643df 100644 --- a/archinstall/lib/interactions/disk_conf.py +++ b/archinstall/lib/interactions/disk_conf.py @@ -145,7 +145,8 @@ def select_disk_config( return disk.DiskLayoutConfiguration( config_type=disk.DiskLayoutType.Pre_mount, - device_modifications=mods + device_modifications=mods, + mountpoint=path ) preset_devices = [mod.device for mod in preset.device_modifications] if preset else [] -- cgit v1.2.3-70-g09d2 From 6ee6d1eda05d3f69f6aaabac55a741e693449994 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Mon, 20 Nov 2023 06:55:45 -0500 Subject: Remove `select_language()` duplicate of `select_kb_layout()` (#2151) * Remove `select_language()` duplicate of `select_kb_layout()` * Added a deprecation warning on select_language() * Moved select_language() back into it's original location, just to keep the PR diff minimal * Removed import for now, to please flake8 --------- Co-authored-by: Anton Hvornum --- archinstall/lib/interactions/general_conf.py | 29 ++++++++-------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/archinstall/lib/interactions/general_conf.py b/archinstall/lib/interactions/general_conf.py index b12a6fb8..a879552e 100644 --- a/archinstall/lib/interactions/general_conf.py +++ b/archinstall/lib/interactions/general_conf.py @@ -3,7 +3,7 @@ from __future__ import annotations import pathlib from typing import List, Any, Optional, TYPE_CHECKING -from ..locale import list_timezones, list_keyboard_languages +from ..locale import list_timezones from ..menu import MenuSelectionType, Menu, TextInput from ..models.audio_configuration import Audio, AudioConfiguration from ..output import warn @@ -87,29 +87,16 @@ def ask_for_audio_selection( def select_language(preset: Optional[str] = None) -> Optional[str]: - """ - Asks the user to select a language - Usually this is combined with :ref:`archinstall.list_keyboard_languages`. - - :return: The language/dictionary key of the selected language - :rtype: str - """ - kb_lang = list_keyboard_languages() - # sort alphabetically and then by length - sorted_kb_lang = sorted(kb_lang, key=lambda x: (len(x), x)) + from ..locale.locale_menu import select_kb_layout - choice = Menu( - _('Select keyboard layout'), - sorted_kb_lang, - preset_values=preset, - sort=False - ).run() + # We'll raise an exception in an upcoming version. + # from ..exceptions import Deprecated + # raise Deprecated("select_language() has been deprecated, use select_kb_layout() instead.") - match choice.type_: - case MenuSelectionType.Skip: return preset - case MenuSelectionType.Selection: return choice.single_value + # No need to translate this i feel, as it's a short lived message. + warn("select_language() is deprecated, use select_kb_layout() instead. select_language() will be removed in a future version") - return None + return select_kb_layout(preset) def select_archinstall_language(languages: List[Language], preset: Language) -> Language: -- cgit v1.2.3-70-g09d2 From f876ddc68eedb57f315b345aa45703b7acaabc98 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Mon, 20 Nov 2023 06:56:12 -0500 Subject: Remove superfluous use of `partprobe` (#2212) --- archinstall/lib/disk/device_handler.py | 5 ----- archinstall/lib/luks.py | 1 - 2 files changed, 6 deletions(-) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index baed2f6f..50e8c59c 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -378,7 +378,6 @@ class DeviceHandler(object): attempts = 3 lsblk_info: Optional[LsblkInfo] = None - self.partprobe(path) for attempt_nr in range(attempts): time.sleep(attempt_nr + 1) lsblk_info = get_lsblk_info(path) @@ -386,8 +385,6 @@ class DeviceHandler(object): if lsblk_info.partn and lsblk_info.partuuid and lsblk_info.uuid: break - self.partprobe(path) - if not lsblk_info: debug(f'Unable to get partition information: {path}') raise DiskError(f'Unable to get partition information: {path}') @@ -526,8 +523,6 @@ class DeviceHandler(object): requires_delete = modification.wipe is False self._perform_partitioning(part_mod, modification.device, disk, requires_delete=requires_delete) - self.partprobe(modification.device.device_info.path) - def mount( self, dev_path: Path, diff --git a/archinstall/lib/luks.py b/archinstall/lib/luks.py index 4b3b3bc7..c917420e 100644 --- a/archinstall/lib/luks.py +++ b/archinstall/lib/luks.py @@ -162,7 +162,6 @@ class Luks2: # Get crypt-information about the device by doing a reverse lookup starting with the partition path # For instance: /dev/sda - disk.device_handler.partprobe(self.luks_dev_path) lsblk_info = disk.get_lsblk_info(self.luks_dev_path) # For each child (sub-partition/sub-device) -- cgit v1.2.3-70-g09d2 From f16af43949085b06478d2e4c45ed61fa8e595171 Mon Sep 17 00:00:00 2001 From: codefiles <11915375+codefiles@users.noreply.github.com> Date: Mon, 20 Nov 2023 06:58:09 -0500 Subject: Fix GPT end alignment (#2210) --- archinstall/lib/interactions/disk_conf.py | 61 ++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/archinstall/lib/interactions/disk_conf.py b/archinstall/lib/interactions/disk_conf.py index 8e9643df..85b377b7 100644 --- a/archinstall/lib/interactions/disk_conf.py +++ b/archinstall/lib/interactions/disk_conf.py @@ -176,9 +176,9 @@ def select_disk_config( return None -def _boot_partition(sector_size: disk.SectorSize) -> disk.PartitionModification: +def _boot_partition(sector_size: disk.SectorSize, using_gpt: bool) -> disk.PartitionModification: flags = [disk.PartitionFlag.Boot] - if SysInfo.has_uefi(): + if using_gpt: start = disk.Size(1, disk.Unit.MiB, sector_size) size = disk.Size(512, disk.Unit.MiB, sector_size) flags.append(disk.PartitionFlag.ESP) @@ -242,6 +242,8 @@ def suggest_single_disk_layout( device_modification = disk.DeviceModification(device, wipe=True) + using_gpt = SysInfo.has_uefi() + # Used for reference: https://wiki.archlinux.org/title/partitioning # 2 MiB is unallocated for GRUB on BIOS. Potentially unneeded for other bootloaders? @@ -253,7 +255,7 @@ def suggest_single_disk_layout( # Also re-align the start to 1MiB since we don't need the first sectors # like we do in MBR layouts where the boot loader is installed traditionally. - boot_partition = _boot_partition(sector_size) + boot_partition = _boot_partition(sector_size, using_gpt) device_modification.add_partition(boot_partition) if not using_subvolumes: @@ -267,20 +269,25 @@ def suggest_single_disk_layout( else: using_home_partition = False + align_buffer = disk.Size(1, disk.Unit.MiB, sector_size) + # root partition - start = disk.Size(513, disk.Unit.MiB, sector_size) if SysInfo.has_uefi() else disk.Size(206, disk.Unit.MiB, sector_size) + root_start = boot_partition.start + boot_partition.length # Set a size for / (/root) if using_subvolumes or device_size_gib < min_size_to_allow_home_part or not using_home_partition: - length = device.device_info.total_size - start + root_length = device.device_info.total_size - root_start else: - length = min(device.device_info.total_size, root_partition_size) + root_length = min(device.device_info.total_size, root_partition_size) + + if using_gpt and not using_home_partition: + root_length -= align_buffer root_partition = disk.PartitionModification( status=disk.ModificationStatus.Create, type=disk.PartitionType.Primary, - start=start, - length=length, + start=root_start, + length=root_length, mountpoint=Path('/') if not using_subvolumes else None, fs_type=filesystem_type, mount_options=['compress=zstd'] if compression else [], @@ -303,14 +310,17 @@ def suggest_single_disk_layout( # If we don't want to use subvolumes, # But we want to be able to re-use data between re-installs.. # A second partition for /home would be nice if we have the space for it - start = root_partition.length - length = device.device_info.total_size - root_partition.length + home_start = root_partition.length + home_length = device.device_info.total_size - root_partition.length + + if using_gpt: + home_length -= align_buffer home_partition = disk.PartitionModification( status=disk.ModificationStatus.Create, type=disk.PartitionType.Primary, - start=start, - length=length, + start=home_start, + length=home_length, mountpoint=Path('/home'), fs_type=filesystem_type, mount_options=['compress=zstd'] if compression else [] @@ -377,17 +387,21 @@ def suggest_multi_disk_layout( root_device_sector_size = root_device_modification.device.device_info.sector_size home_device_sector_size = home_device_modification.device.device_info.sector_size + root_align_buffer = disk.Size(1, disk.Unit.MiB, root_device_sector_size) + home_align_buffer = disk.Size(1, disk.Unit.MiB, home_device_sector_size) + + using_gpt = SysInfo.has_uefi() + # add boot partition to the root device - boot_partition = _boot_partition(root_device_sector_size) + boot_partition = _boot_partition(root_device_sector_size, using_gpt) root_device_modification.add_partition(boot_partition) - if SysInfo.has_uefi(): - root_start = disk.Size(513, disk.Unit.MiB, root_device_sector_size) - else: - root_start = disk.Size(206, disk.Unit.MiB, root_device_sector_size) - + root_start = boot_partition.start + boot_partition.length root_length = root_device.device_info.total_size - root_start + if using_gpt: + root_length -= root_align_buffer + # add root partition to the root device root_partition = disk.PartitionModification( status=disk.ModificationStatus.Create, @@ -400,15 +414,18 @@ def suggest_multi_disk_layout( ) root_device_modification.add_partition(root_partition) - start = disk.Size(1, disk.Unit.MiB, home_device_sector_size) - length = home_device.device_info.total_size - start + home_start = home_align_buffer + home_length = home_device.device_info.total_size - home_start + + if using_gpt: + home_length -= home_align_buffer # add home partition to home device home_partition = disk.PartitionModification( status=disk.ModificationStatus.Create, type=disk.PartitionType.Primary, - start=start, - length=length, + start=home_start, + length=home_length, mountpoint=Path('/home'), mount_options=['compress=zstd'] if compression else [], fs_type=filesystem_type, -- cgit v1.2.3-70-g09d2 From e6344f93f7e476d05bbcd642f2ed91fdde545870 Mon Sep 17 00:00:00 2001 From: czapek <32851089+48cf@users.noreply.github.com> Date: Tue, 21 Nov 2023 10:19:17 +0100 Subject: Fix Limine bootloader deployment (#2216) * Add `get_unique_path_for_device` to `DeviceHandler` * Fix Limine bootloader deployment * Fail if UKI is enabled with Limine * Support more configuration options with Limine * Fix linter errors * Fix boot partition fs_type check for Limine --- archinstall/lib/disk/device_handler.py | 14 ++++ archinstall/lib/global_menu.py | 7 +- archinstall/lib/installer.py | 135 +++++++++++++++------------------ 3 files changed, 81 insertions(+), 75 deletions(-) diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index 50e8c59c..fcf52013 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -133,6 +133,20 @@ class DeviceHandler(object): lsblk = get_lsblk_info(dev_path) return Path(f'/dev/{lsblk.pkname}') + def get_unique_path_for_device(self, dev_path: Path) -> Optional[Path]: + paths = Path('/dev/disk/by-id').glob('*') + linked_targets = {p.resolve(): p for p in paths} + linked_wwn_targets = {p: linked_targets[p] for p in linked_targets + if p.name.startswith('wwn-') or p.name.startswith('nvme-eui.')} + + if dev_path in linked_wwn_targets: + return linked_wwn_targets[dev_path] + + if dev_path in linked_targets: + return linked_targets[dev_path] + + return None + def get_uuid_for_path(self, path: Path) -> Optional[str]: partition = self.find_partition(path) return partition.partuuid if partition else None diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index e4aa1235..d3d87603 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -363,8 +363,11 @@ class GlobalMenu(AbstractMenu): if boot_partition is None: return "Boot partition not found" - if bootloader == Bootloader.Limine and boot_partition.fs_type == disk.FilesystemType.Btrfs: - return "Limine bootloader does not support booting from BTRFS filesystem" + if bootloader == Bootloader.Limine: + if boot_partition.fs_type != disk.FilesystemType.Fat32: + return "Limine does not support booting from filesystems other than FAT32" + elif self._menu_options['uki'].current_selection: + return "Limine does not support booting UKIs" return None diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 2aa0d9dd..e2ca5e2b 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -972,70 +972,54 @@ class Installer: def _add_limine_bootloader( self, boot_partition: disk.PartitionModification, + efi_partition: Optional[disk.PartitionModification], root_partition: disk.PartitionModification ): self.pacman.strap('limine') - info(f"Limine boot partition: {boot_partition.dev_path}") - root_uuid = root_partition.uuid + info(f"Limine boot partition: {boot_partition.dev_path}") - def create_pacman_hook(contents: str): - HOOK_DIR = "/etc/pacman.d/hooks" - SysCommand(f"/usr/bin/arch-chroot {self.target} mkdir -p {HOOK_DIR}") - SysCommand(f"/usr/bin/arch-chroot {self.target} sh -c \"echo '{contents}' > {HOOK_DIR}/liminedeploy.hook\"") + limine_path = self.target / 'usr' / 'share' / 'limine' + hook_command = None if SysInfo.has_uefi(): + if not efi_partition: + raise ValueError('Could not detect efi partition') + elif not efi_partition.mountpoint: + raise ValueError('EFI partition is not mounted') + + info(f"Limine EFI partition: {efi_partition.dev_path}") + try: - # The `limine.sys` file, contains stage 3 code. - cmd = f'/usr/bin/arch-chroot' \ - f' {self.target}' \ - f' cp' \ - f' /usr/share/limine/BOOTX64.EFI' \ - f' /boot/EFI/BOOT/' - - SysCommand(cmd, peek_output=True) - except SysCallError as err: - raise DiskError(f"Failed to install Limine BOOTX64.EFI on {boot_partition.dev_path}: {err}") + efi_dir_path = self.target / efi_partition.mountpoint.relative_to('/') / 'EFI' / 'BOOT' + efi_dir_path.mkdir(parents=True, exist_ok=True) - # Create the EFI limine pacman hook. - create_pacman_hook(""" -[Trigger] -Operation = Install -Operation = Upgrade -Type = Package -Target = limine + for file in ('BOOTIA32.EFI', 'BOOTX64.EFI'): + shutil.copy(limine_path / file, efi_dir_path) + except Exception as err: + raise DiskError(f'Failed to install Limine in {self.target}{efi_partition.mountpoint}: {err}') -[Action] -Description = Deploying Limine after upgrade... -When = PostTransaction -Exec = /usr/bin/cp /usr/share/limine/BOOTX64.EFI /boot/EFI/BOOT/ - """) + hook_command = f'/usr/bin/cp /usr/share/limine/BOOTIA32.EFI {efi_partition.mountpoint}/EFI/BOOT/' \ + f' && /usr/bin/cp /usr/share/limine/BOOTX64.EFI {efi_partition.mountpoint}/EFI/BOOT/' else: parent_dev_path = disk.device_handler.get_parent_device_path(boot_partition.safe_dev_path) - try: - # The `limine.sys` file, contains stage 3 code. - cmd = f'/usr/bin/arch-chroot' \ - f' {self.target}' \ - f' cp' \ - f' /usr/share/limine/limine-bios.sys' \ - f' /boot/limine-bios.sys' + if unique_path := disk.device_handler.get_unique_path_for_device(parent_dev_path): + parent_dev_path = unique_path - SysCommand(cmd, peek_output=True) + try: + # The `limine-bios.sys` file contains stage 3 code. + shutil.copy(limine_path / 'limine-bios.sys', self.target / 'boot') # `limine bios-install` deploys the stage 1 and 2 to the disk. - cmd = f'/usr/bin/arch-chroot' \ - f' {self.target}' \ - f' limine' \ - f' bios-install' \ - f' {parent_dev_path}' + SysCommand(f'/usr/bin/arch-chroot {self.target} limine bios-install {parent_dev_path}', peek_output=True) + except Exception as err: + raise DiskError(f'Failed to install Limine on {parent_dev_path}: {err}') - SysCommand(cmd, peek_output=True) - except SysCallError as err: - raise DiskError(f"Failed to install Limine on {boot_partition.dev_path}: {err}") + hook_command = f'/usr/bin/limine bios-install {parent_dev_path}' \ + f' && /usr/bin/cp /usr/share/limine/limine-bios.sys /boot/' - create_pacman_hook(f""" -[Trigger] + hook_contents = f'''[Trigger] Operation = Install Operation = Upgrade Type = Package @@ -1044,33 +1028,38 @@ Target = limine [Action] Description = Deploying Limine after upgrade... When = PostTransaction -# XXX: Kernel name descriptors cannot be used since they are not persistent and -# can change after each boot. -Exec = /bin/sh -c \\"/usr/bin/limine bios-install /dev/disk/by-uuid/{root_uuid} && /usr/bin/cp /usr/share/limine/limine-bios.sys /boot/\\" - """) +Exec = /bin/sh -c "{hook_command}" +''' - # Limine does not ship with a default configuration file. We are going to - # create a basic one that is similar to the one GRUB generates. - try: - config = f""" -TIMEOUT=5 - -:Arch Linux - PROTOCOL=linux - KERNEL_PATH=boot:///vmlinuz-linux - CMDLINE=root=UUID={root_uuid} rw rootfstype={root_partition.safe_fs_type.value} loglevel=3 - MODULE_PATH=boot:///initramfs-linux.img - -:Arch Linux (fallback) - PROTOCOL=linux - KERNEL_PATH=boot:///vmlinuz-linux - CMDLINE=root=UUID={root_uuid} rw rootfstype={root_partition.safe_fs_type.value} loglevel=3 - MODULE_PATH=boot:///initramfs-linux-fallback.img - """ - - SysCommand(f"/usr/bin/arch-chroot {self.target} sh -c \"echo '{config}' > /boot/limine.cfg\"") - except SysCallError as err: - raise DiskError(f"Could not configure Limine: {err}") + hooks_dir = self.target / 'etc' / 'pacman.d' / 'hooks' + hooks_dir.mkdir(parents=True, exist_ok=True) + + hook_path = hooks_dir / '99-limine.hook' + hook_path.write_text(hook_contents) + + microcode = [] + + if ucode := self._get_microcode(): + microcode = [f'MODULE_PATH=boot:///{ucode}'] + + kernel_params = ' '.join(self._get_kernel_params(root_partition)) + config_contents = 'TIMEOUT=5\n' + + for kernel in self.kernels: + for variant in ('', '-fallback'): + entry = [ + f'PROTOCOL=linux', + f'KERNEL_PATH=boot:///vmlinuz-{kernel}', + *microcode, + f'MODULE_PATH=boot:///initramfs-{kernel}{variant}.img', + f'CMDLINE={kernel_params}', + ] + + config_contents += f'\n:Arch Linux ({kernel}{variant})\n' + config_contents += '\n'.join([f' {it}' for it in entry]) + '\n' + + config_path = self.target / 'boot' / 'limine.cfg' + config_path.write_text(config_contents) self.helper_flags['bootloader'] = "limine" @@ -1227,7 +1216,7 @@ TIMEOUT=5 case Bootloader.Efistub: self._add_efistub_bootloader(boot_partition, root_partition, uki_enabled) case Bootloader.Limine: - self._add_limine_bootloader(boot_partition, root_partition) + self._add_limine_bootloader(boot_partition, efi_partition, root_partition) def add_additional_packages(self, packages: Union[str, List[str]]) -> bool: return self.pacman.strap(packages) -- cgit v1.2.3-70-g09d2 From cc806d9c4ce29212c2848c15b4b184feace3e1ac Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Tue, 21 Nov 2023 14:34:30 +0100 Subject: Started a re-write of the docs (#1967) * Started a re-write of the docs, using CSV for tables and re-organizing parameter definitions * Added final config options in --config, some have external references which needs to be populated * Forgot to escape a comma * Clarified a note * Added disk configuration and disk encryption docs * Changed way of installing using source and python * Added a 'list script' that lists available scripts. This could be converted to a argparse later. But this does the trick for now. And it's added to ease documentation and listing of available options. * Added a 'Known issues' section, as well as renamed the issues tab * Finished up the known issues section * Added a section regarding --plugin * Added plugin description, tweaked disk_config to the latest changes from #2221 * Added custom-commands docs, and improved some creds and known issue links --- archinstall/__init__.py | 3 +- archinstall/scripts/list.py | 10 + docs/_static/style.css | 3 + docs/_templates/layout.html | 4 + docs/archinstall/Application.rst | 10 - docs/archinstall/Profile.rst | 16 - docs/archinstall/general.rst | 117 ------- docs/archinstall/plugins.rst | 57 ++++ docs/cli_parameters/config/config_options.csv | 24 ++ docs/cli_parameters/config/custom_commands.rst | 22 ++ docs/cli_parameters/config/disk_config.rst | 245 ++++++++++++++ docs/cli_parameters/config/disk_encryption.rst | 19 ++ docs/cli_parameters/config/manual_options.csv | 4 + docs/examples/binary.rst | 23 -- docs/examples/python.rst | 83 +++-- docs/help/issues.rst | 33 -- docs/help/known_issues.rst | 103 ++++++ docs/help/report_bug.rst | 33 ++ docs/index.rst | 38 +-- docs/installing/binary.rst | 52 --- docs/installing/guided.rst | 425 ++++++++++++++----------- docs/installing/python.rst | 6 +- 22 files changed, 827 insertions(+), 503 deletions(-) create mode 100644 archinstall/scripts/list.py create mode 100644 docs/_static/style.css create mode 100644 docs/_templates/layout.html delete mode 100644 docs/archinstall/Application.rst delete mode 100644 docs/archinstall/Profile.rst delete mode 100644 docs/archinstall/general.rst create mode 100644 docs/archinstall/plugins.rst create mode 100644 docs/cli_parameters/config/config_options.csv create mode 100644 docs/cli_parameters/config/custom_commands.rst create mode 100644 docs/cli_parameters/config/disk_config.rst create mode 100644 docs/cli_parameters/config/disk_encryption.rst create mode 100644 docs/cli_parameters/config/manual_options.csv delete mode 100644 docs/examples/binary.rst delete mode 100644 docs/help/issues.rst create mode 100644 docs/help/known_issues.rst create mode 100644 docs/help/report_bug.rst delete mode 100644 docs/installing/binary.rst diff --git a/archinstall/__init__.py b/archinstall/__init__.py index b9a0c2fb..67230c71 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -1,6 +1,7 @@ """Arch Linux installer - guided, templates etc.""" import importlib import os +import sys import time import traceback from argparse import ArgumentParser, Namespace @@ -58,7 +59,7 @@ debug(f"Graphics devices detected: {SysInfo._graphics_devices().keys()}") # For support reasons, we'll log the disk layout pre installation to match against post-installation layout debug(f"Disk states before installing: {disk.disk_layouts()}") -if os.getuid() != 0: +if 'sphinx' not in sys.modules and os.getuid() != 0: print(_("Archinstall requires root privileges to run. See --help for more.")) exit(1) diff --git a/archinstall/scripts/list.py b/archinstall/scripts/list.py new file mode 100644 index 00000000..0e0363a1 --- /dev/null +++ b/archinstall/scripts/list.py @@ -0,0 +1,10 @@ +import glob +import pathlib + +print("The following are viable --script options:") + +for script in [pathlib.Path(x) for x in glob.glob(f"{pathlib.Path(__file__).parent}/*.py")]: + if script.stem in ['__init__', 'list']: + continue + + print(f" {script.stem}") \ No newline at end of file diff --git a/docs/_static/style.css b/docs/_static/style.css new file mode 100644 index 00000000..579fe077 --- /dev/null +++ b/docs/_static/style.css @@ -0,0 +1,3 @@ +.wy-nav-content { + max-width: none; +} \ No newline at end of file diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html new file mode 100644 index 00000000..b0a44806 --- /dev/null +++ b/docs/_templates/layout.html @@ -0,0 +1,4 @@ +{% extends "!layout.html" %} +{% block extrahead %} + +{% endblock %} \ No newline at end of file diff --git a/docs/archinstall/Application.rst b/docs/archinstall/Application.rst deleted file mode 100644 index c6eb0d4d..00000000 --- a/docs/archinstall/Application.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _archinstall.Application: - -archinstall.Application -======================= - -This class enables access to pre-programmed application configurations. -This is not to be confused with :ref:`archinstall.Profile` which is for pre-programmed profiles for a wider set of installation sets. - - -.. autofunction:: archinstall.Application diff --git a/docs/archinstall/Profile.rst b/docs/archinstall/Profile.rst deleted file mode 100644 index a4c1f3bb..00000000 --- a/docs/archinstall/Profile.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _archinstall.Profile: - -archinstall.Profile -=================== - -This class enables access to pre-programmed profiles. -This is not to be confused with :ref:`archinstall.Application` which is for pre-programmed application profiles. - -Profiles in general is a set or group of installation steps. -Where as applications are a specific set of instructions for a very specific application. - -An example would be the *(currently fictional)* profile called `database`. -The profile `database` might contain the application profile `postgresql`. -And that's the difference between :ref:`archinstall.Profile` and :ref:`archinstall.Application`. - -.. autofunction:: archinstall.Profile diff --git a/docs/archinstall/general.rst b/docs/archinstall/general.rst deleted file mode 100644 index 970d40f2..00000000 --- a/docs/archinstall/general.rst +++ /dev/null @@ -1,117 +0,0 @@ -.. _archinstall.helpers: - -.. warning:: - All these helper functions are mostly, if not all, related to outside-installation-instructions. Meaning the calls will affect your current running system - and not touch your installed system. - -Profile related helpers -======================= - -.. autofunction:: archinstall.list_profiles - -Packages -======== - -.. autofunction:: archinstall.find_package - -.. autofunction:: archinstall.find_packages - -Locale related -============== - -.. autofunction:: archinstall.list_keyboard_languages - -.. autofunction:: archinstall.search_keyboard_layout - -.. autofunction:: archinstall.set_keyboard_language - -.. - autofunction:: archinstall.Installer.set_keyboard_layout - -Services -======== - -.. autofunction:: archinstall.service_state - -Mirrors -======= - -.. autofunction:: archinstall.filter_mirrors_by_region - -.. autofunction:: archinstall.add_custom_mirrors - -.. autofunction:: archinstall.insert_mirrors - -.. autofunction:: archinstall.use_mirrors - -.. autofunction:: archinstall.re_rank_mirrors - -.. autofunction:: archinstall.list_mirrors - -Disk related -============ - -.. autofunction:: archinstall.BlockDevice - -.. autofunction:: archinstall.Partition - -.. autofunction:: archinstall.Filesystem - -.. autofunction:: archinstall.device_state - -.. autofunction:: archinstall.all_blockdevices - -Luks (Disk encryption) -====================== - -.. autofunction:: archinstall.luks2 - -Networking -========== - -.. autofunction:: archinstall.get_hw_addr - -.. autofunction:: archinstall.list_interfaces - -.. autofunction:: archinstall.check_mirror_reachable - -.. autofunction:: archinstall.update_keyring - -.. autofunction:: archinstall.enrich_iface_types - -.. autofunction:: archinstall.get_interface_from_mac - -.. autofunction:: archinstall.wireless_scan - -.. autofunction:: archinstall.get_wireless_networks - -General -======= - -.. autofunction:: archinstall.log - -.. autofunction:: archinstall.locate_binary - -.. autofunction:: archinstall.SysCommand - -.. autofunction:: archinstall.SysCommandWorker - -Exceptions -========== - -.. autofunction:: archinstall.RequirementError - -.. autofunction:: archinstall.DiskError - -.. autofunction:: archinstall.ProfileError - -.. autofunction:: archinstall.SysCallError - -.. autofunction:: archinstall.ProfileNotFound - -.. autofunction:: archinstall.HardwareIncompatibilityError - -.. autofunction:: archinstall.PermissionError - -.. autofunction:: archinstall.UserError - -.. autofunction:: archinstall.ServiceException diff --git a/docs/archinstall/plugins.rst b/docs/archinstall/plugins.rst new file mode 100644 index 00000000..898f9006 --- /dev/null +++ b/docs/archinstall/plugins.rst @@ -0,0 +1,57 @@ +.. _archinstall.Plugins: + +Python Plugins +============== + +``archinstall`` supports plugins via two methods. + +First method is directly via the ``--plugin`` parameter when running as a CLI tool. This will load a specific plugin locally or remotely via a path. + +The second method is via Python's built in `plugin discovery`_ using `entry points`_ categorized as ``archinstall.plugin``. + +``--plugin`` parameter +---------------------- + +The parameter has the benefit of being stored in the ``--conf`` state, meaning when re-running an installation — the plugin will automatically be loaded. +It's limitation is that it requires an initial path to be known and written and be cumbersome. + +Plugin Discovery +---------------- + +This method allows for multiple plugins to be loaded with the drawback that they have to be installed beforehand on the system running ``archinstall``. +This mainly targets those who build their own ISO's and package specific setups for their needs. + + +What's supported? +----------------- + +Currently the documentation for this is scarse. Until that is resolved, the best way to find supported features is to search the source code for `plugin.on_ `_ as this will give a clear indication of which calls are made to plugins. + +How does it work? +----------------- + +``archinstall`` plugins use a discovery-driven approach where plugins are queried for certain functions. +As an example, if a plugin has the following function: + +.. code-block:: python + + def on_pacstrap(*packages): + ... + +The function :code:`archinstall.Pacman().strap(["some packages"])` is hardcoded to iterate plugins and look for :code:`on_pacstrap` in the plugin. +If the function exists, :code:`.strap()` will call the plugin's function and replace the initial package list with the result from the plugin. + +The best way to document these calls is currently undecided, as it's hard to document this behavior dynamically. + +Writing your own? +----------------- + +The simplest way currently is to look at a reference implementation or the community. Two of these are: + +* `torxed/archinstall-aur `_ +* `phisch/archinstall-aur `_ + +And search for `plugin.on_ `_ in the code base to find what ``archinstall`` will look for. PR's are welcome to widen the support for this. + +.. _plugin discovery: https://packaging.python.org/en/latest/specifications/entry-points/ +.. _entry points: https://docs.python.org/3/library/importlib.metadata.html#entry-points \ No newline at end of file diff --git a/docs/cli_parameters/config/config_options.csv b/docs/cli_parameters/config/config_options.csv new file mode 100644 index 00000000..1861b1e1 --- /dev/null +++ b/docs/cli_parameters/config/config_options.csv @@ -0,0 +1,24 @@ +Key,Value(s),Description,Required +additional-repositories,[ `multilib `_!, `testing `_ ],Enables one or more of the testing and multilib repositories before proceeding with installation,No +archinstall-language,`lang `__,Sets the TUI language used *(make sure to use the ``lang`` value not the ``abbr``)*,No +audio_config,`pipewire `_!, `pulseaudio `_,Audioserver to be installed,No +bootloader,`Systemd-boot `_!, `grub `_,Bootloader to be installed *(grub being mandatory on BIOS machines)*,Yes +debug,``true``!, ``false``,Enables debug output,No +disk_config,*Read more under* :ref:`disk config`,Contains the desired disk setup to be used during installation,No +disk_encryption,*Read more about under* :ref:`disk encryption`,Parameters for disk encryption applied ontop of ``disk_config``,No +hostname,``str``,A string definining your machines hostname on the network *(defaults to ``archinstall``)*,No +kernels,[ `linux `_!, `linux-hardened `_!, `linux-lts `_!, `linux-rt `_!, `linux-rt-lts `_!, `linux-zen `_ ],Defines which kernels should be installed and setup in the boot loader options,Yes +custom-commands,*Read more under* :ref:`custom commands`,Custom commands that will be run post-install chrooted inside the installed system,No +locale_config,{kb_layout: `lang `__!, sys_enc: `Character encoding `_!, sys_lang: `locale `_},Defines the keyboard key map!, system encoding and system locale,No +mirror_config,{custom_mirrors: [ https://... ]!, mirror_regions: { "Worldwide": [ "https://geo.mirror.pkgbuild.com/$repo/os/$arch" ] } },Sets various mirrors *(defaults to ISO's ``/etc/pacman.d/mirrors`` if not defined)*,No +network_config,*`see options under Network Configuration`*,Sets which type of *(if any)* network configuration should be used,No +no_pkg_lookups,``true``!, ``false``,Disabled package checking against https://archlinux.org/packages/,No +ntp,``true``!, ``false``,enables or disables `NTP `_ during installation,No +offline,``true``!, ``false``,enables or disables certain online checks such as mirror reachability etc,No +packages,[ !, !, ... ],A list of packages to install during installation,No +parallel downloads,0-∞,sets a given number of paralell downloads to be used by `pacman `_,No +profile_config,*`read more under the profiles section`*,Installs a given profile if defined,No +script,`guided `__! *(default)*!, `minimal `__!, `only_hdd `_!, `swiss `_!, `unattended `_,When used to autorun an installation!, this sets which script to autorun with,No +silent,``true``!, ``false``,disables or enables user questions using the TUI,No +swap,``true``!, ``false``,enables or disables swap,No +timezone,`timezone `_,sets a timezone for the installed system,No \ No newline at end of file diff --git a/docs/cli_parameters/config/custom_commands.rst b/docs/cli_parameters/config/custom_commands.rst new file mode 100644 index 00000000..c1529020 --- /dev/null +++ b/docs/cli_parameters/config/custom_commands.rst @@ -0,0 +1,22 @@ +.. _custom commands: + +Custom Commands +=============== + +| Custom commands is a configuration entry that allows for executing custom commands post-installation. +| The commands are executed with `arch-chroot `_. + +The option takes a list of arguments, an example is: + +.. code-block:: json + + { + "custom-commands": [ + "hostname new-hostname" + ] + } + +| The following example will set a new hostname in the installed system. +| The example is just to illustrate that the command is not run in the ISO but inside the installed system after the base system is installed. + +More examples can be found in the code repository under `examples/ `_ \ No newline at end of file diff --git a/docs/cli_parameters/config/disk_config.rst b/docs/cli_parameters/config/disk_config.rst new file mode 100644 index 00000000..ed5f42c1 --- /dev/null +++ b/docs/cli_parameters/config/disk_config.rst @@ -0,0 +1,245 @@ +.. _disk config: + +Disk Configuration +================== + +There are only three modes in the ``disk_config`` option. They are described in more detail below. + +"Leave as is" +-------------- + +.. code-block:: json + + { + "config_type": "pre_mounted_config", + "mountpoint": "/mnt/archinstall" + } + +This mode will not perform any partitioning what so ever. +Instead it relies on what's mounted manually by the user under ``/mnt/archinstall``. + +Given the following disk example: + +.. code-block:: + + /mnt/archinstall (/dev/sda2) + ├── boot (/dev/sda1) + └── home (/dev/sda3) + +Runing ``archinstall --conf your.json --silent`` where the above JSON is configured. The disk will be left alone — and a working system will be installed to the above folders and mountpoints will be translated into the installed system. + +.. note:: + + Some disk layouts can be too complicated to detect, such as RAID setups. Please do report those setups on the `Issue Tracker `__ so we can support them. + +Best Effort +----------- + +.. warning:: + + This mode will wipe data! + +.. note:: + + Note that this options is similar to the manual partitioning but is generated through the menu system! And the best effort layout might deviate slightly from some wiki guidelines in order to facilitate some optional configurations at a later stage. + +.. code-block:: json + + { + "disk_config": { + "config_type": "default_layout", + "device_modifications": [ + { + "device": "/dev/sda", + "wipe": true, + "partitions": "..." + } + ] + } + } + +This mode will attempt to configure a sane default layout on the selected disks. +Based on the chosen filesystem, and potential optional settings for said filesystem — different default layouts will be provided. + +Manual Partitioning +------------------- + +.. code-block:: json + + { + "disk_config": { + "config_type": "manual_partitioning", + "device_modifications": [ + "filesystem struct" + ] + } + } + +Manual partitioning is the most complex one of the three. It offers you near endless flexibility of how to partition your disk. It integrates against `pyparted `__ and some control logic in ``archinstall`` that deals with creating things like subvolumes and compression. + +Sizes are by default ``sector`` units, but other units are supported. + +The options supplied to ``manual_partitioning`` are dictionary definitions, where the following parameters must exist: + +.. csv-table:: JSON options + :file: ./manual_options.csv + :widths: 15, 15, 65, 5 + :escape: ! + :header-rows: 1 + +Each partition definition heavily relies on what filesystem is used. +Below follow two of the more common filesystems, anything else will best be described by running ``archinstall`` to generate a desired configuration for the desired filesystem type — and copy the relevant parts for permanent configurations. + +.. warning:: + + Important to note that the units and positions in the examples below — are highly user specific! + +FAT32 +^^^^^ + +.. code-block:: json + + { + "btrfs": [], + "flags": [ + "Boot" + ], + "fs_type": "fat32", + "length": { + "sector_size": null, + "total_size": null, + "unit": "B", + "value": 99982592 + }, + "mount_options": [], + "mountpoint": "/boot", + "obj_id": "369f31a8-2781-4d6b-96e7-75680552b7c9", + "start": { + "sector_size": { + "sector_size": null, + "total_size": null, + "unit": "B", + "value": 512 + }, + "total_size": null, + "unit": "sectors", + "value": 34 + }, + "status": "create", + "type": "primary" + } + +.. note:: + + The ``Boot`` flag will make ``archinstall`` automatically set the correct ESP partition GUID if the system is booted with ``EFI`` support. The GUID will then be set to ``C12A7328-F81F-11D2-BA4B-00A0C93EC93B``. + +EXT4 +^^^^ + +.. code-block:: json + + { + "btrfs": [], + "flags": [], + "fs_type": "ext4", + "length": { + "sector_size": null, + "total_size": null, + "unit": "B", + "value": 15805127360 + }, + "mount_options": [], + "mountpoint": "/", + "obj_id": "3e75d045-21a4-429d-897e-8ec19a006e8b", + "start": { + "sector_size": { + "sector_size": null, + "total_size": null, + "unit": "B", + "value": 512 + }, + "total_size": { + "sector_size": null, + "total_size": null, + "unit": "B", + "value": 16106127360 + }, + "unit": "MB", + "value": 301 + }, + "status": "create", + "type": "primary" + } + +BTRFS +^^^^^ + +The BTRFS filesystem is inherently more complicated, thus the options are a bit more involved. +This example contains both subvolumes and compression. + +.. note:: + + Note that the ``"mountpoint": null`` is used for the overall partition, and instead individual subvolumes have mountpoints set. + +.. code-block:: json + + { + "btrfs": [ + { + "compress": false, + "mountpoint": "/", + "name": "@", + "nodatacow": false + }, + { + "compress": false, + "mountpoint": "/home", + "name": "@home", + "nodatacow": false + }, + { + "compress": false, + "mountpoint": "/var/log", + "name": "@log", + "nodatacow": false + }, + { + "compress": false, + "mountpoint": "/var/cache/pacman/pkg", + "name": "@pkg", + "nodatacow": false + }, + { + "compress": false, + "mountpoint": "/.snapshots", + "name": "@.snapshots", + "nodatacow": false + } + ], + "dev_path": null, + "flags": [], + "fs_type": "btrfs", + "mount_options": [ + "compress=zstd" + ], + "mountpoint": null, + "obj_id": "d712357f-97cc-40f8-a095-24ff244d4539", + "size": { + "sector_size": { + "unit": "B", + "value": 512 + }, + "unit": "B", + "value": 15568207872 + }, + "start": { + "sector_size": { + "unit": "B", + "value": 512 + }, + "unit": "MiB", + "value": 513 + }, + "status": "create", + "type": "primary" + } \ No newline at end of file diff --git a/docs/cli_parameters/config/disk_encryption.rst b/docs/cli_parameters/config/disk_encryption.rst new file mode 100644 index 00000000..df2e2fa7 --- /dev/null +++ b/docs/cli_parameters/config/disk_encryption.rst @@ -0,0 +1,19 @@ +.. _disk encryption: + +Disk Encryption +=============== + +Disk encryption consists of a top level entry in the user configuration. + +.. code-block:: json + + { + "disk_encryption": { + "encryption_type": "luks", + "partitions": [ + "d712357f-97cc-40f8-a095-24ff244d4539" + ] + } + } + +The ``UID`` in the ``partitions`` list is an internal reference to the ``obj_id`` in the :ref:`disk config` entries. \ No newline at end of file diff --git a/docs/cli_parameters/config/manual_options.csv b/docs/cli_parameters/config/manual_options.csv new file mode 100644 index 00000000..2fcc26f0 --- /dev/null +++ b/docs/cli_parameters/config/manual_options.csv @@ -0,0 +1,4 @@ +Key,Value(s),Description,Required +device,``str``,Which block-device to format,yes +partitions,[ {key: val} ],The data describing the change/addition in a partition,yes +wipe,``bool``,clear the disk before adding any partitions,No \ No newline at end of file diff --git a/docs/examples/binary.rst b/docs/examples/binary.rst deleted file mode 100644 index 51dbd1dd..00000000 --- a/docs/examples/binary.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _examples.binary: - -Binary executable -================= - -.. warning:: The binary option is limited and stiff. It's hard to modify or create your own installer-scripts this way unless you compile the source manually. If your usecase needs custom scripts, either use the pypi setup method or you'll need to adjust the PKGBUILD prior to building the arch package. - -The binary executable is a standalone compiled version of the library. -It's compiled using `nuitka `_ with the flag `--standalone`. - -Executing the binary --------------------- - -As an example we'll use the `guided `_ installer. -To run the `guided` installed, all you have to do *(after installing or compiling the binary)*, is run: - - -.. code-block:: console - - ./archinstall guided - -As mentioned, the binary is a bit rudimentary and only supports executing whatever is found directly under `./archinstall/examples`. -Anything else won't be found. This is subject to change in the future to make it a bit more flexible. diff --git a/docs/examples/python.rst b/docs/examples/python.rst index b5478fe2..7fb3f6c3 100644 --- a/docs/examples/python.rst +++ b/docs/examples/python.rst @@ -4,7 +4,7 @@ Python module ============= Archinstall supports running in `module mode `_. -The way the library is invoked in module mode is limited to executing scripts under the **example** folder. +The way the library is invoked in module mode is limited to executing scripts under the `scripts`_ folder. It's therefore important to place any script or profile you wish to invoke in the examples folder prior to building and installing. @@ -12,7 +12,7 @@ Pre-requisites -------------- We'll assume you've followed the :ref:`installing.python.manual` method. -Before actually installing the library, you will need to place your custom installer-scripts under `./archinstall/examples/` as a python file. +Before actually installing the library, you will need to place your custom installer-scripts under `scripts`_ as a python file. More on how you create these in the next section. @@ -24,36 +24,73 @@ Creating a script ----------------- Lets create a `test_installer` - installer as an example. This is assuming that the folder `./archinstall` is a git-clone of the main repo. -We begin by creating `./archinstall/examples/test_installer.py`. The placement here is important later. +We begin by creating "`scripts`_:code:`/test_installer.py`". The placement here is important later. -This script can now already be called using `python -m archinstall test_installer` after a successful installation of the library itself. +This script can now already be called using :code:`python -m archinstall test_installer` after a successful installation of the library itself. But the script won't do much. So we'll do something simple like list all the hard drives as an example. -To do this, we'll begin by importing `archinstall` in our `./archinstall/examples/test_installer.py` and call some functions. +To do this, we'll begin by importing :code:`archinstall` in our "`scripts`_:code:`/test_installer.py`" and call a function whtin ``archinstall``. .. code-block:: python import archinstall - all_drives = archinstall.all_blockdevices(partitions=False) - print(list(all_drives.keys())) + print(archinstall.disk.device_handler.devices) + +Now, go ahead and reference the :ref:`installing.python.manual` installation method. +After runnig ``python -m archinstall test_installer`` it should print something that looks like: + +.. code-block:: text + + [ + BDevice( + disk=, + device_info=_DeviceInfo( + model='PC801 NVMe SK hynix 512GB', + path=PosixPath('/dev/nvme0n1'), + type='nvme', + total_size=Size(value=512110190592, unit=, + sector_size=SectorSize(value=512, unit=)), + free_space_regions=[ + , + , + ], + sector_size=SectorSize(value=512, unit=), + read_only=False, + dirty=False + ), + partition_infos=[ + _PartitionInfo( + partition=, + name='primary', + type=, + fs_type=, + path='/dev/nvme0n1p1', + start=Size(value=2048, unit=, sector_size=SectorSize(value=512, unit=)), + length=Size(value=535822336, unit=, sector_size=SectorSize(value=512, unit=)), + flags=[ + , + + ], + partn=1, + partuuid='a26be943-c193-41f4-9930-9341cf5f6b19', + uuid='6EE9-2C00', + disk=, + mountpoints=[ + PosixPath('/boot') + ], + btrfs_subvol_infos=[] + ), + _PartitionInfo(...) + ] + ) + ] + +That means your script is in the right place, and ``archinstall`` is working as intended. -This should print out a list of drives and some meta-information about them. -As an example, this will do just fine. - -Now, go ahead and install the library either as a user-module or system-wide. - -Calling a module ----------------- - -Assuming you've followed the example in `Creating a script`_, you can now safely call it with: - -.. code-block:: console - - python -m archinstall test_installer +.. note:: -This should now print all available drives on your system. + Most calls, including the one above requires `root `_ privileges. -.. note:: - This should work on any system, not just Arch Linux based ones. But note that other functions in the library rely heavily on Arch Linux based commands to execute the installation steps. Such as `arch-chroot`. +.. _scripts: https://github.com/archlinux/archinstall/tree/master/archinstall/scripts \ No newline at end of file diff --git a/docs/help/issues.rst b/docs/help/issues.rst deleted file mode 100644 index 7d690b65..00000000 --- a/docs/help/issues.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. _help.issues: - -Issue tracker & bugs -==================== - -Issues and bugs should be reported over at `https://github.com/archlinux/archinstall/issues `_. - -General questions, enhancements and security issues can be reported over there too. -For quick issues or if you need help, head over to the Discord server which has a help channel. - -Log files ---------- - -| When submitting a help ticket, please include the :code:`/var/log/archinstall/install.log`. -| It can be found both on the live ISO but also in the installed filesystem if the base packages were strapped in. - -.. tip:: - | An easy way to submit logs is ``curl -F'file=@/var/log/archinstall/install.log' https://0x0.st``. - | Use caution when submitting other log files, but ``archinstall`` pledges to keep ``install.log`` safe for posting publicly! - -| There are additional log files under ``/var/log/archinstall/`` that can be useful: - - - ``/var/log/archinstall/user_configuration.json`` - Stores most of the guided answers in the installer - - ``/var/log/archinstall/user_credentials.json`` - Stores any usernames or passwords, can be passed to ``--creds`` - - ``/var/log/archinstall/user_disk_layouts.json`` - Stores the chosen disks and their layouts - - ``/var/log/archinstall/install.log`` - A log file over what steps were taken by archinstall - - ``/var/log/archinstall/cmd_history.txt`` - A complete command history, command by command in order - - ``/var/log/archinstall/cmd_output.txt`` - A raw output from all the commands that were executed by archinstall - -.. warning:: - - We only try to guarantee that ``/var/log/archinstall/install.log`` is free from sensitive information. - Any other log file should be pasted with **utmost care**! diff --git a/docs/help/known_issues.rst b/docs/help/known_issues.rst new file mode 100644 index 00000000..2f1f62cb --- /dev/null +++ b/docs/help/known_issues.rst @@ -0,0 +1,103 @@ +.. _help.known_issues: + +Known Issues +============ + +| Some issues are out of the `archinstall`_ projects scope, and the ones we know of are listed below. + +.. _waiting for time sync: + +Waiting for time sync `#2144`_ +------------------------------ + +| The usual root cause of this is the network topology. +| More specifically `timedatectl show`_ cannot perform a proper time sync against the default servers. + +| A *"fix"* for this is mentioned in the issue above. +| That is to configure ``/etc/systemd/timesyncd.conf`` and restart ``systemd-timesyncd.service``. + +.. note:: + + A proposal to override the time sync check has been put up for discussion in `#2144`_. + +Missing Nvidia Proprietary Driver `#2002`_ +------------------------------------------ + +| In some instances, the nvidia driver might not have all the nessecary packages installed. +| This is due to the kernel selection and/or hardware setups requiring additional packages to work properly. + +A common workaround is to install the package `linux-headers`_ and `nvidia-dkms`_ + +ARM, 32bit and other CPU types error out `#1686`_, `#2185`_ +----------------------------------------------------------- + +| This is a bit of a catch-all known issue. +| Officially `x86_64`_ is only supported by Arch Linux. +| Hence little effort have been put into supporting other platforms. + +| In theory, other architectures should work but small quirks might arise. + +| PR's are welcome but please be respectful of the delays in merging. +| Other fixes, issues or features will be prioritized for the above reasons. + +Keyring is out of date `#2213`_ +------------------------------- + +| Missing key-issues tend to be that the `archlinux-keyring`_ package is out of date, usually as a result of an outdated ISO. +| There is an attempt from upstream to fix this issue, and it's the `archlinux-keyring-wkd-sync.service`_ + +| The service starts almost immediately during boot, and if network is not configured in time — the service will fail. +| Subsequently the ``archinstall`` run might operate on a old keyring despite there being an update service for this. + +| There is really no way to reliably over time work around this issue in ``archinstall``. +| Instead, efforts to the upstream service should be considered the way forward. And/or keys not expiring betwene a sane ammount of ISO's. + +.. note:: + + The issue can happen on new ISO's too even as little as a few days after release, as some keys might expire right after the keyring is *"burnt in"* to the ISO. + +.. note:: + + Another common issue relating to the network not being configured, is that time might not be set correctly - resulting in the keyring not being able to update. See :ref:`waiting for time sync`. + +AUR packages +------------ + +| This is also a catch-all issue. +| `AUR is unsupported `_, and until that changes we cannot use AUR packages to solve feature requests in ``archinstall``. + +| This means that feature requests like supporting filesystems such as `ZFS`_ can not be added, and issues cannot be solved by using AUR packages either. + +.. note:: + + But in spirit of giving the community options, ``archinstall`` supports :ref:`archinstall.Plugins`, which means you can run ``archinstall --plugin `` and source an AUR plugin. + + `torxed/archinstall-aur `_ is a reference implementation for plugins: + + .. code-block:: console + + # archinstall --plugin https://archlinux.life/aur-plugin + + `phisch/archinstall-aur `_ is another alternative: + + .. code-block:: console + + # archinstall --plugin https://raw.githubusercontent.com/phisch/archinstall-aur/master/archinstall-aur.py + + .. warning:: + + This will allow for unsupported usage of AUR during installation. + +.. _#2213: https://github.com/archlinux/archinstall/issues/2213 +.. _#2185: https://github.com/archlinux/archinstall/issues/2185 +.. _#2144: https://github.com/archlinux/archinstall/issues/2144 +.. _#2002: https://github.com/archlinux/archinstall/issues/2002 +.. _#1686: https://github.com/archlinux/archinstall/issues/1686 +.. _linux-headers: https://archlinux.org/packages/core/x86_64/linux-headers/ +.. _nvidia-dkms: https://archlinux.org/packages/extra/x86_64/nvidia-dkms/ +.. _x86_64: https://wiki.archlinux.org/title/Frequently_asked_questions#What_architectures_does_Arch_support? +.. _archlinux-keyring: https://archlinux.org/packages/core/any/archlinux-keyring/ +.. _archlinux-keyring-wkd-sync.service: https://gitlab.archlinux.org/archlinux/archlinux-keyring/-/blob/7e672dad10652a80d1cc575d75cdb46442cd7f96/wkd_sync/archlinux-keyring-wkd-sync.service.in +.. _ZFS: https://aur.archlinux.org/packages/zfs-linux +.. _archinstall: https://github.com/archlinux/archinstall/ +.. _timedatectl show: https://github.com/archlinux/archinstall/blob/e6344f93f7e476d05bbcd642f2ed91fdde545870/archinstall/lib/installer.py#L136 \ No newline at end of file diff --git a/docs/help/report_bug.rst b/docs/help/report_bug.rst new file mode 100644 index 00000000..bd0ac50a --- /dev/null +++ b/docs/help/report_bug.rst @@ -0,0 +1,33 @@ +.. _help.issues: + +Report Issues & Bugs +==================== + +Issues and bugs should be reported over at `https://github.com/archlinux/archinstall/issues `_. + +General questions, enhancements and security issues can be reported over there too. +For quick issues or if you need help, head over to the Discord server which has a help channel. + +Log files +--------- + +| When submitting a help ticket, please include the :code:`/var/log/archinstall/install.log`. +| It can be found both on the live ISO but also in the installed filesystem if the base packages were strapped in. + +.. tip:: + | An easy way to submit logs is ``curl -F'file=@/var/log/archinstall/install.log' https://0x0.st``. + | Use caution when submitting other log files, but ``archinstall`` pledges to keep ``install.log`` safe for posting publicly! + +| There are additional log files under ``/var/log/archinstall/`` that can be useful: + + - ``/var/log/archinstall/user_configuration.json`` - Stores most of the guided answers in the installer + - ``/var/log/archinstall/user_credentials.json`` - Stores any usernames or passwords, can be passed to ``--creds`` + - ``/var/log/archinstall/user_disk_layouts.json`` - Stores the chosen disks and their layouts + - ``/var/log/archinstall/install.log`` - A log file over what steps were taken by archinstall + - ``/var/log/archinstall/cmd_history.txt`` - A complete command history, command by command in order + - ``/var/log/archinstall/cmd_output.txt`` - A raw output from all the commands that were executed by archinstall + +.. warning:: + + We only try to guarantee that ``/var/log/archinstall/install.log`` is free from sensitive information. + Any other log file should be pasted with **utmost care**! diff --git a/docs/index.rst b/docs/index.rst index a76a58d6..6a81bbb0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -8,17 +8,15 @@ archinstall Documentation Some of the features of Archinstall are: -* **No external dependencies or installation requirements.** Runs without any external requirements or installation processes. - * **Context friendly.** The library always executes calls in sequential order to ensure installation-steps don't overlap or execute in the wrong order. It also supports *(and uses)* context wrappers to ensure cleanup and final tasks such as ``mkinitcpio`` are called when needed. -* **Full transparency** Logs and insights can be found at ``/var/log/archinstall`` both in the live ISO and the installed system. +* **Full transparency** Logs and insights can be found at ``/var/log/archinstall`` both in the live ISO and partially on the installed system. * **Accessibility friendly** Archinstall works with ``espeakup`` and other accessibility tools thanks to the use of a TUI. .. toctree:: - :maxdepth: 3 - :caption: Running the installer + :maxdepth: 1 + :caption: Running Archinstall installing/guided @@ -26,8 +24,9 @@ Some of the features of Archinstall are: :maxdepth: 3 :caption: Getting help + help/known_issues + help/report_bug help/discord - help/issues .. toctree:: :maxdepth: 3 @@ -35,35 +34,10 @@ Some of the features of Archinstall are: installing/python examples/python - -.. toctree:: - :maxdepth: 3 - :caption: Archinstall as a binary - - installing/binary - examples/binary -.. - examples/scripting - -.. - .. toctree:: - :maxdepth: 3 - :caption: Programming Guide - -.. - programming_guide/requirements - programming_guide/basic_concept + archinstall/plugins .. toctree:: :maxdepth: 3 :caption: API Reference archinstall/Installer - archinstall/Profile - archinstall/Application - -.. toctree:: - :maxdepth: 3 - :caption: API Helper functions - - archinstall/general diff --git a/docs/installing/binary.rst b/docs/installing/binary.rst deleted file mode 100644 index eeb9d79d..00000000 --- a/docs/installing/binary.rst +++ /dev/null @@ -1,52 +0,0 @@ -.. _installing.binary: - -Binary executable -================= - -Archinstall can be compiled into a standalone executable. -For Arch Linux based systems, there's a package for this called `archinstall `_. - -.. warning:: - This is not required if you're running archinstall on a pre-built ISO. The installation is only required if you're creating your own scripted installations. - -Using pacman ------------- - -Archinstall is on the `official repositories `_. - -.. code-block:: console - - sudo pacman -S archinstall - -Using PKGBUILD --------------- - -The `source `_ contains a binary `PKGBUILD `_ which can be either copied straight off the website or cloned using :code:`git clone https://github.com/Torxed/archinstall`. - -Once you've obtained the `PKGBUILD`, building it is pretty straight forward. - -.. code-block:: console - - makepkg -s - -Which should produce an `archinstall-X.x.z-1.pkg.tar.zst` which can be installed using: - -.. code-block:: console - - sudo pacman -U archinstall-X.x.z-1.pkg.tar.zst - -.. note:: - - For a complete guide on the build process, please consult the `PKGBUILD on ArchWiki `_. - -Manual compilation ------------------- - -You can compile the source manually without using a custom mirror or the `PKGBUILD` that is shipped. -Simply clone or download the source, and while standing in the cloned folder `./archinstall`, execute: - -.. code-block:: console - - nuitka3 --standalone --show-progress archinstall - -This requires the `nuitka `_ package as well as `python3` to be installed locally. diff --git a/docs/installing/guided.rst b/docs/installing/guided.rst index c5e7f1ed..dcedfc10 100644 --- a/docs/installing/guided.rst +++ b/docs/installing/guided.rst @@ -3,20 +3,16 @@ Guided installation =================== -| This is the default script the Arch Linux `Archinstall package `_. -| It will guide you through a very basic installation of Arch Linux. +Archinstall ships with a pre-programmed `Guided Installer`_ guiding you through the mandatory steps as well as some optional configurations that can be done. .. note:: - There are other scripts and they can be invoked by executing `archinstall \ No newline at end of file -- cgit v1.2.3-70-g09d2 From bb67c5a3a9ee95b3819ad4154f3cdbbbe1cf5bc5 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Wed, 22 Nov 2023 21:52:27 +0100 Subject: Added nvidia-dkms when installing propri driver to (#2241) * Added nvidia-dkms when installing propri driver to fix #2233, #2214 and #2002 * Forgot class-name infront of NvidiaDKMS --- archinstall/lib/hardware.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/archinstall/lib/hardware.py b/archinstall/lib/hardware.py index 56d3bc7b..d9342a98 100644 --- a/archinstall/lib/hardware.py +++ b/archinstall/lib/hardware.py @@ -42,6 +42,7 @@ class GfxPackage(Enum): LibvaMesaDriver = 'libva-mesa-driver' Mesa = "mesa" Nvidia = 'nvidia' + NvidiaDKMS = 'nvidia-dkms' NvidiaOpen = 'nvidia-open' VulkanIntel = 'vulkan-intel' VulkanRadeon = 'vulkan-radeon' @@ -108,7 +109,10 @@ class GfxDriver(Enum): GfxPackage.LibvaMesaDriver ] case GfxDriver.NvidiaProprietary: - return [GfxPackage.Nvidia] + return [ + GfxPackage.Nvidia, + GfxPackage.NvidiaDKMS + ] case GfxDriver.VMOpenSource: return [ GfxPackage.Mesa, -- cgit v1.2.3-70-g09d2 From 8b774a9cfa9e885df57c47f0690813892a39ed18 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Wed, 22 Nov 2023 21:56:07 +0100 Subject: Redirect readthedocs (#2243) * Adding permanent redirect for readthedocs * Have to remove pyparted because readthedocs can't build it --- docs/requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index b052f388..7d44d729 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,2 @@ sphinx_rtd_theme -pyparted simple-term-menu \ No newline at end of file -- cgit v1.2.3-70-g09d2 From db326590ba31f17f5fd7113a112e787627f2f464 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Wed, 22 Nov 2023 22:12:29 +0100 Subject: Redirect readthedocs (#2244) * Adding permanent redirect for readthedocs * Have to remove pyparted because readthedocs can't build it * Removed pyparted from pyproject.toml as readthedocs read that one too afterall. --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 19320338..614bb590 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,6 @@ classifiers = [ "Operating System :: POSIX :: Linux", ] dependencies = [ - "pyparted==3.12.0", "simple-term-menu==1.6.3", ] -- cgit v1.2.3-70-g09d2 From d7eb35f4ee49cbb8fe065db445a84000883d69b5 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Wed, 22 Nov 2023 22:24:29 +0100 Subject: Nuking docs to allow for redirect (ouff) (#2245) --- docs/archinstall/Installer.rst | 11 - docs/archinstall/plugins.rst | 57 ----- docs/cli_parameters/config/config_options.csv | 24 --- docs/cli_parameters/config/custom_commands.rst | 22 -- docs/cli_parameters/config/disk_config.rst | 245 --------------------- docs/cli_parameters/config/disk_encryption.rst | 19 -- docs/cli_parameters/config/manual_options.csv | 4 - docs/examples/python.rst | 96 --------- docs/flowcharts/BlockDeviceSelection.svg | 3 - docs/flowcharts/DiskSelectionProcess.drawio | 1 - docs/help/discord.rst | 14 -- docs/help/known_issues.rst | 103 --------- docs/help/report_bug.rst | 33 --- docs/installing/guided.rst | 287 ------------------------- docs/installing/python.rst | 59 ----- 15 files changed, 978 deletions(-) delete mode 100644 docs/archinstall/Installer.rst delete mode 100644 docs/archinstall/plugins.rst delete mode 100644 docs/cli_parameters/config/config_options.csv delete mode 100644 docs/cli_parameters/config/custom_commands.rst delete mode 100644 docs/cli_parameters/config/disk_config.rst delete mode 100644 docs/cli_parameters/config/disk_encryption.rst delete mode 100644 docs/cli_parameters/config/manual_options.csv delete mode 100644 docs/examples/python.rst delete mode 100644 docs/flowcharts/BlockDeviceSelection.svg delete mode 100644 docs/flowcharts/DiskSelectionProcess.drawio delete mode 100644 docs/help/discord.rst delete mode 100644 docs/help/known_issues.rst delete mode 100644 docs/help/report_bug.rst delete mode 100644 docs/installing/guided.rst delete mode 100644 docs/installing/python.rst diff --git a/docs/archinstall/Installer.rst b/docs/archinstall/Installer.rst deleted file mode 100644 index d1bbaa7c..00000000 --- a/docs/archinstall/Installer.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. _archinstall.Installer: - -archinstall.Installer -===================== - -The installer is the main class for accessing an installation-instance. -You can look at this class as the installation you have or will perform. - -Anything related to **inside** the installation, will be found in this class. - -.. autofunction:: archinstall.Installer diff --git a/docs/archinstall/plugins.rst b/docs/archinstall/plugins.rst deleted file mode 100644 index 898f9006..00000000 --- a/docs/archinstall/plugins.rst +++ /dev/null @@ -1,57 +0,0 @@ -.. _archinstall.Plugins: - -Python Plugins -============== - -``archinstall`` supports plugins via two methods. - -First method is directly via the ``--plugin`` parameter when running as a CLI tool. This will load a specific plugin locally or remotely via a path. - -The second method is via Python's built in `plugin discovery`_ using `entry points`_ categorized as ``archinstall.plugin``. - -``--plugin`` parameter ----------------------- - -The parameter has the benefit of being stored in the ``--conf`` state, meaning when re-running an installation — the plugin will automatically be loaded. -It's limitation is that it requires an initial path to be known and written and be cumbersome. - -Plugin Discovery ----------------- - -This method allows for multiple plugins to be loaded with the drawback that they have to be installed beforehand on the system running ``archinstall``. -This mainly targets those who build their own ISO's and package specific setups for their needs. - - -What's supported? ------------------ - -Currently the documentation for this is scarse. Until that is resolved, the best way to find supported features is to search the source code for `plugin.on_ `_ as this will give a clear indication of which calls are made to plugins. - -How does it work? ------------------ - -``archinstall`` plugins use a discovery-driven approach where plugins are queried for certain functions. -As an example, if a plugin has the following function: - -.. code-block:: python - - def on_pacstrap(*packages): - ... - -The function :code:`archinstall.Pacman().strap(["some packages"])` is hardcoded to iterate plugins and look for :code:`on_pacstrap` in the plugin. -If the function exists, :code:`.strap()` will call the plugin's function and replace the initial package list with the result from the plugin. - -The best way to document these calls is currently undecided, as it's hard to document this behavior dynamically. - -Writing your own? ------------------ - -The simplest way currently is to look at a reference implementation or the community. Two of these are: - -* `torxed/archinstall-aur `_ -* `phisch/archinstall-aur `_ - -And search for `plugin.on_ `_ in the code base to find what ``archinstall`` will look for. PR's are welcome to widen the support for this. - -.. _plugin discovery: https://packaging.python.org/en/latest/specifications/entry-points/ -.. _entry points: https://docs.python.org/3/library/importlib.metadata.html#entry-points \ No newline at end of file diff --git a/docs/cli_parameters/config/config_options.csv b/docs/cli_parameters/config/config_options.csv deleted file mode 100644 index 1861b1e1..00000000 --- a/docs/cli_parameters/config/config_options.csv +++ /dev/null @@ -1,24 +0,0 @@ -Key,Value(s),Description,Required -additional-repositories,[ `multilib `_!, `testing `_ ],Enables one or more of the testing and multilib repositories before proceeding with installation,No -archinstall-language,`lang `__,Sets the TUI language used *(make sure to use the ``lang`` value not the ``abbr``)*,No -audio_config,`pipewire `_!, `pulseaudio `_,Audioserver to be installed,No -bootloader,`Systemd-boot `_!, `grub `_,Bootloader to be installed *(grub being mandatory on BIOS machines)*,Yes -debug,``true``!, ``false``,Enables debug output,No -disk_config,*Read more under* :ref:`disk config`,Contains the desired disk setup to be used during installation,No -disk_encryption,*Read more about under* :ref:`disk encryption`,Parameters for disk encryption applied ontop of ``disk_config``,No -hostname,``str``,A string definining your machines hostname on the network *(defaults to ``archinstall``)*,No -kernels,[ `linux `_!, `linux-hardened `_!, `linux-lts `_!, `linux-rt `_!, `linux-rt-lts `_!, `linux-zen `_ ],Defines which kernels should be installed and setup in the boot loader options,Yes -custom-commands,*Read more under* :ref:`custom commands`,Custom commands that will be run post-install chrooted inside the installed system,No -locale_config,{kb_layout: `lang `__!, sys_enc: `Character encoding `_!, sys_lang: `locale `_},Defines the keyboard key map!, system encoding and system locale,No -mirror_config,{custom_mirrors: [ https://... ]!, mirror_regions: { "Worldwide": [ "https://geo.mirror.pkgbuild.com/$repo/os/$arch" ] } },Sets various mirrors *(defaults to ISO's ``/etc/pacman.d/mirrors`` if not defined)*,No -network_config,*`see options under Network Configuration`*,Sets which type of *(if any)* network configuration should be used,No -no_pkg_lookups,``true``!, ``false``,Disabled package checking against https://archlinux.org/packages/,No -ntp,``true``!, ``false``,enables or disables `NTP `_ during installation,No -offline,``true``!, ``false``,enables or disables certain online checks such as mirror reachability etc,No -packages,[ !, !, ... ],A list of packages to install during installation,No -parallel downloads,0-∞,sets a given number of paralell downloads to be used by `pacman `_,No -profile_config,*`read more under the profiles section`*,Installs a given profile if defined,No -script,`guided `__! *(default)*!, `minimal `__!, `only_hdd `_!, `swiss `_!, `unattended `_,When used to autorun an installation!, this sets which script to autorun with,No -silent,``true``!, ``false``,disables or enables user questions using the TUI,No -swap,``true``!, ``false``,enables or disables swap,No -timezone,`timezone `_,sets a timezone for the installed system,No \ No newline at end of file diff --git a/docs/cli_parameters/config/custom_commands.rst b/docs/cli_parameters/config/custom_commands.rst deleted file mode 100644 index c1529020..00000000 --- a/docs/cli_parameters/config/custom_commands.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. _custom commands: - -Custom Commands -=============== - -| Custom commands is a configuration entry that allows for executing custom commands post-installation. -| The commands are executed with `arch-chroot `_. - -The option takes a list of arguments, an example is: - -.. code-block:: json - - { - "custom-commands": [ - "hostname new-hostname" - ] - } - -| The following example will set a new hostname in the installed system. -| The example is just to illustrate that the command is not run in the ISO but inside the installed system after the base system is installed. - -More examples can be found in the code repository under `examples/ `_ \ No newline at end of file diff --git a/docs/cli_parameters/config/disk_config.rst b/docs/cli_parameters/config/disk_config.rst deleted file mode 100644 index ed5f42c1..00000000 --- a/docs/cli_parameters/config/disk_config.rst +++ /dev/null @@ -1,245 +0,0 @@ -.. _disk config: - -Disk Configuration -================== - -There are only three modes in the ``disk_config`` option. They are described in more detail below. - -"Leave as is" --------------- - -.. code-block:: json - - { - "config_type": "pre_mounted_config", - "mountpoint": "/mnt/archinstall" - } - -This mode will not perform any partitioning what so ever. -Instead it relies on what's mounted manually by the user under ``/mnt/archinstall``. - -Given the following disk example: - -.. code-block:: - - /mnt/archinstall (/dev/sda2) - ├── boot (/dev/sda1) - └── home (/dev/sda3) - -Runing ``archinstall --conf your.json --silent`` where the above JSON is configured. The disk will be left alone — and a working system will be installed to the above folders and mountpoints will be translated into the installed system. - -.. note:: - - Some disk layouts can be too complicated to detect, such as RAID setups. Please do report those setups on the `Issue Tracker `__ so we can support them. - -Best Effort ------------ - -.. warning:: - - This mode will wipe data! - -.. note:: - - Note that this options is similar to the manual partitioning but is generated through the menu system! And the best effort layout might deviate slightly from some wiki guidelines in order to facilitate some optional configurations at a later stage. - -.. code-block:: json - - { - "disk_config": { - "config_type": "default_layout", - "device_modifications": [ - { - "device": "/dev/sda", - "wipe": true, - "partitions": "..." - } - ] - } - } - -This mode will attempt to configure a sane default layout on the selected disks. -Based on the chosen filesystem, and potential optional settings for said filesystem — different default layouts will be provided. - -Manual Partitioning -------------------- - -.. code-block:: json - - { - "disk_config": { - "config_type": "manual_partitioning", - "device_modifications": [ - "filesystem struct" - ] - } - } - -Manual partitioning is the most complex one of the three. It offers you near endless flexibility of how to partition your disk. It integrates against `pyparted `__ and some control logic in ``archinstall`` that deals with creating things like subvolumes and compression. - -Sizes are by default ``sector`` units, but other units are supported. - -The options supplied to ``manual_partitioning`` are dictionary definitions, where the following parameters must exist: - -.. csv-table:: JSON options - :file: ./manual_options.csv - :widths: 15, 15, 65, 5 - :escape: ! - :header-rows: 1 - -Each partition definition heavily relies on what filesystem is used. -Below follow two of the more common filesystems, anything else will best be described by running ``archinstall`` to generate a desired configuration for the desired filesystem type — and copy the relevant parts for permanent configurations. - -.. warning:: - - Important to note that the units and positions in the examples below — are highly user specific! - -FAT32 -^^^^^ - -.. code-block:: json - - { - "btrfs": [], - "flags": [ - "Boot" - ], - "fs_type": "fat32", - "length": { - "sector_size": null, - "total_size": null, - "unit": "B", - "value": 99982592 - }, - "mount_options": [], - "mountpoint": "/boot", - "obj_id": "369f31a8-2781-4d6b-96e7-75680552b7c9", - "start": { - "sector_size": { - "sector_size": null, - "total_size": null, - "unit": "B", - "value": 512 - }, - "total_size": null, - "unit": "sectors", - "value": 34 - }, - "status": "create", - "type": "primary" - } - -.. note:: - - The ``Boot`` flag will make ``archinstall`` automatically set the correct ESP partition GUID if the system is booted with ``EFI`` support. The GUID will then be set to ``C12A7328-F81F-11D2-BA4B-00A0C93EC93B``. - -EXT4 -^^^^ - -.. code-block:: json - - { - "btrfs": [], - "flags": [], - "fs_type": "ext4", - "length": { - "sector_size": null, - "total_size": null, - "unit": "B", - "value": 15805127360 - }, - "mount_options": [], - "mountpoint": "/", - "obj_id": "3e75d045-21a4-429d-897e-8ec19a006e8b", - "start": { - "sector_size": { - "sector_size": null, - "total_size": null, - "unit": "B", - "value": 512 - }, - "total_size": { - "sector_size": null, - "total_size": null, - "unit": "B", - "value": 16106127360 - }, - "unit": "MB", - "value": 301 - }, - "status": "create", - "type": "primary" - } - -BTRFS -^^^^^ - -The BTRFS filesystem is inherently more complicated, thus the options are a bit more involved. -This example contains both subvolumes and compression. - -.. note:: - - Note that the ``"mountpoint": null`` is used for the overall partition, and instead individual subvolumes have mountpoints set. - -.. code-block:: json - - { - "btrfs": [ - { - "compress": false, - "mountpoint": "/", - "name": "@", - "nodatacow": false - }, - { - "compress": false, - "mountpoint": "/home", - "name": "@home", - "nodatacow": false - }, - { - "compress": false, - "mountpoint": "/var/log", - "name": "@log", - "nodatacow": false - }, - { - "compress": false, - "mountpoint": "/var/cache/pacman/pkg", - "name": "@pkg", - "nodatacow": false - }, - { - "compress": false, - "mountpoint": "/.snapshots", - "name": "@.snapshots", - "nodatacow": false - } - ], - "dev_path": null, - "flags": [], - "fs_type": "btrfs", - "mount_options": [ - "compress=zstd" - ], - "mountpoint": null, - "obj_id": "d712357f-97cc-40f8-a095-24ff244d4539", - "size": { - "sector_size": { - "unit": "B", - "value": 512 - }, - "unit": "B", - "value": 15568207872 - }, - "start": { - "sector_size": { - "unit": "B", - "value": 512 - }, - "unit": "MiB", - "value": 513 - }, - "status": "create", - "type": "primary" - } \ No newline at end of file diff --git a/docs/cli_parameters/config/disk_encryption.rst b/docs/cli_parameters/config/disk_encryption.rst deleted file mode 100644 index df2e2fa7..00000000 --- a/docs/cli_parameters/config/disk_encryption.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _disk encryption: - -Disk Encryption -=============== - -Disk encryption consists of a top level entry in the user configuration. - -.. code-block:: json - - { - "disk_encryption": { - "encryption_type": "luks", - "partitions": [ - "d712357f-97cc-40f8-a095-24ff244d4539" - ] - } - } - -The ``UID`` in the ``partitions`` list is an internal reference to the ``obj_id`` in the :ref:`disk config` entries. \ No newline at end of file diff --git a/docs/cli_parameters/config/manual_options.csv b/docs/cli_parameters/config/manual_options.csv deleted file mode 100644 index 2fcc26f0..00000000 --- a/docs/cli_parameters/config/manual_options.csv +++ /dev/null @@ -1,4 +0,0 @@ -Key,Value(s),Description,Required -device,``str``,Which block-device to format,yes -partitions,[ {key: val} ],The data describing the change/addition in a partition,yes -wipe,``bool``,clear the disk before adding any partitions,No \ No newline at end of file diff --git a/docs/examples/python.rst b/docs/examples/python.rst deleted file mode 100644 index 7fb3f6c3..00000000 --- a/docs/examples/python.rst +++ /dev/null @@ -1,96 +0,0 @@ -.. _examples.python: - -Python module -============= - -Archinstall supports running in `module mode `_. -The way the library is invoked in module mode is limited to executing scripts under the `scripts`_ folder. - -It's therefore important to place any script or profile you wish to invoke in the examples folder prior to building and installing. - -Pre-requisites --------------- - -We'll assume you've followed the :ref:`installing.python.manual` method. -Before actually installing the library, you will need to place your custom installer-scripts under `scripts`_ as a python file. - -More on how you create these in the next section. - -.. warning:: - - This is subject to change in the future as this method is currently a bit stiff. The script path will become a parameter. But for now, this is by design. - -Creating a script ------------------ - -Lets create a `test_installer` - installer as an example. This is assuming that the folder `./archinstall` is a git-clone of the main repo. -We begin by creating "`scripts`_:code:`/test_installer.py`". The placement here is important later. - -This script can now already be called using :code:`python -m archinstall test_installer` after a successful installation of the library itself. -But the script won't do much. So we'll do something simple like list all the hard drives as an example. - -To do this, we'll begin by importing :code:`archinstall` in our "`scripts`_:code:`/test_installer.py`" and call a function whtin ``archinstall``. - -.. code-block:: python - - import archinstall - - print(archinstall.disk.device_handler.devices) - -Now, go ahead and reference the :ref:`installing.python.manual` installation method. -After runnig ``python -m archinstall test_installer`` it should print something that looks like: - -.. code-block:: text - - [ - BDevice( - disk=, - device_info=_DeviceInfo( - model='PC801 NVMe SK hynix 512GB', - path=PosixPath('/dev/nvme0n1'), - type='nvme', - total_size=Size(value=512110190592, unit=, - sector_size=SectorSize(value=512, unit=)), - free_space_regions=[ - , - , - ], - sector_size=SectorSize(value=512, unit=), - read_only=False, - dirty=False - ), - partition_infos=[ - _PartitionInfo( - partition=, - name='primary', - type=, - fs_type=, - path='/dev/nvme0n1p1', - start=Size(value=2048, unit=, sector_size=SectorSize(value=512, unit=)), - length=Size(value=535822336, unit=, sector_size=SectorSize(value=512, unit=)), - flags=[ - , - - ], - partn=1, - partuuid='a26be943-c193-41f4-9930-9341cf5f6b19', - uuid='6EE9-2C00', - disk=, - mountpoints=[ - PosixPath('/boot') - ], - btrfs_subvol_infos=[] - ), - _PartitionInfo(...) - ] - ) - ] - -That means your script is in the right place, and ``archinstall`` is working as intended. - -.. note:: - - Most calls, including the one above requires `root `_ privileges. - - -.. _scripts: https://github.com/archlinux/archinstall/tree/master/archinstall/scripts \ No newline at end of file diff --git a/docs/flowcharts/BlockDeviceSelection.svg b/docs/flowcharts/BlockDeviceSelection.svg deleted file mode 100644 index 2d63f674..00000000 --- a/docs/flowcharts/BlockDeviceSelection.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -
Select BlockDevices
Select BlockDevices
No
No
Yes
Yes
Empty Selection
Empty Selection
Yes / No
Yes / No
Encrypt Root
Encrypt Root
No
No
Yes
Yes
Multiple BD's
Multiple BD's
Select /boot, / and optional mounts
Select /boot, / and...
Yes
Yes
No
No
Contains Partitions
Contains Partitions
No
No
Yes
Yes
Old /boot has content
Old /boot has cont...
Select Root FIlesystem
Select Root FIlesyst...
Mount Partitions
Mount Partitions
Install on /mnt
Install on /mnt
Yes
Yes
Wipe /Boot
Wipe /Boot
Clear old
systemd-boot files
Clear old...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/flowcharts/DiskSelectionProcess.drawio b/docs/flowcharts/DiskSelectionProcess.drawio deleted file mode 100644 index 7c8a1fcb..00000000 --- a/docs/flowcharts/DiskSelectionProcess.drawio +++ /dev/null @@ -1 +0,0 @@ -7VvZdqM4EP2anHlKDpsxPMZ20sl0kl7S05meNwVkYCIjt5C3+fqRjFgFNnbwksQv3a5CCKG6dWsROdP7o/knAsb+PXYhOtMUd36mD840TVUsi/3HNYtYY5tC4ZHAFYMyxWPwH0zuFNpJ4MKoMJBijGgwLiodHIbQoQUdIATPisOGGBWfOgYelBSPDkCy9ilwqR9rLa2b6W9g4PnJk1XTjq+MQDJYvEnkAxfPcir96kzvE4xp/Gs070PENy/Zl6fbxRO6ezE//fkt+g3+6n3+8fDzPJ7sepNb0lcgMKRbT/3y2bLtaXf4oFrf7ga/rYfB/dW5IV6NLpL9gi7bPiFiQn3s4RCgq0zbI3gSupDPqjIpG3OH8Vgo/4WULgQWwIRipvLpCImr7C3I4m8mKBedRPzFxUQYzAvSQkhDHNI+RpgsV6obpmF3BkwfUYJfYO5K17IuL3V+R4BQTt/T+vZlbzmve80upetxLznSmOggEEWBs5wUECoGKYmcDAtxyHcieoHU8cWAeCf59pWAt8ZqYlyEJ8SBK8bpwnkA8eCq+cwUmsynIR5BtoXsPgIRoMG0uDggnMtLx2UAYj8EhjaAqljkFKCJeNIjRNytNaWHsPMygNPAYXRQRt3MDyh8HIPlDswYExURwy0v4KSyl+t53EypYbj1E9/mo1NHVSS01gFiBYRqQLeZdaeQUDjP7b1soOSqKehG8K0lxFlGXmrCSH6OuAzl9SatpAhNMukDPrFGBUHskwzMhmSgKtVga50NKqEjs8GvCt8/IHbUA2HnbUScCpBp3mx4N50TtftTuRl3H7/9Y4fnmrGS0c6Vi65qCCw0Bp6Y7isOQpobgofDiC2mjMz0qduHLlMC69VoTPl7xCEswKEEXeLj0fOELa23JoAVQhJH1TUYBYjD7AaiKaSBAyrCHECBF3KMMHtCUh3r2COD0GOSmUk/lm7Bkru3H/7UbsP4Z7UQ/6rRXcNiCp9WU7YKhs6ETJdGkTOUgodvQXWbU1OdieE8oDnOZFJKmex3xphcSAhz2yBd4LpqSizxpiDEjFeVjVGZJ8Bq02sNw2y3koprQa5cqIZmv44QxVRGp+QvZskP4rWLu/Ll4f6YtSsza+iQxZg//Tsvn0/VQB1SOnrJvPahywHlSMqBd5nWJ02ztSlXZ13G9XqCaZUXKqGkSlA6VHmwx0j3YXBbPbCG7vbTnVJl8rqf8FY0g5Gm9AZ/yOg7Zfi7zvC1piFtdxm+Kpn9naTwbbt5coCzLjxZDfPhPbm9HGjSrjSb8HmZgvbTig6ELvsXj3nFD/jkI2ZdeupZN3dpQ99jlloJQLul1KJd723dS+tzw2N20srVqHKVuFVh8arucCGRy/K62qbFG7G0ah6VqTuSpftsv0AQ8q7aV5bGBpx6T7nY7olb7xxbLqbKzL1/GtjskOjgNGC1TQO5gxvdFj3Qo2gjVKfvbQX7nab0FWd5pYq98lCwXOQfvjJ4FQS140o6LAk5X5CbrwoUH/Cg5LBdhKHcqP64EQkQR6xU0/cYoAz14AHKOkWjlVSgNv02pemh2WaRh1EoWOQGjHkcinYRd1T5i4G0rRAfaynXtwhGi4jCkQSaU/eg7oxLb+rjO+seaHKFcmzpRFt5gAsiP11Wy0yQ7FrDD4iOJitI1p0/JuAtwNXV6RqHLgGgnA9sdiQkM0Cd3fO8sS8H3ucnqyvxlDPgbcjS7eWd/DMunuCNKvK57W0o2crSnnXTbGIrtwMt1yjZquCXzA70C0/tKN9hY5e21I0js6Xe1inxqbrbnsiblncJERwLkcv13VMwhrH/96q+PPq4Bd3OCKVUwXWMTjNG2VkFpx9dMve+2aN9Uqj+FNJUS3WEYRenqPkUsq16MHnP/LkGgoDwnIN3lUzEdrP3zGTT47/iwtA9F50mZqWKuHYipNazVaMBIRntEBITs78PjXGW/ZWtfvU/ \ No newline at end of file diff --git a/docs/help/discord.rst b/docs/help/discord.rst deleted file mode 100644 index f0c7c279..00000000 --- a/docs/help/discord.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _help.discord: - -Discord -======= - -There's a discord channel which is frequented by some `contributors `_. - -| To join the server, head over to `https://discord.gg/cqXU88y `_ and join in. -| There's not many rules other than common sense and to treat others with respect. The general chat is for off-topic things as well. - -There's the ``@Party Animals`` role if you want notifications of new releases which is posted in the ``#Release Party`` channel. -Another thing is the ``@Contributors`` role can be activated by contributors by writing ``!verify`` and follow the verification process. - -Hop in, we hope to see you there! : ) diff --git a/docs/help/known_issues.rst b/docs/help/known_issues.rst deleted file mode 100644 index 2f1f62cb..00000000 --- a/docs/help/known_issues.rst +++ /dev/null @@ -1,103 +0,0 @@ -.. _help.known_issues: - -Known Issues -============ - -| Some issues are out of the `archinstall`_ projects scope, and the ones we know of are listed below. - -.. _waiting for time sync: - -Waiting for time sync `#2144`_ ------------------------------- - -| The usual root cause of this is the network topology. -| More specifically `timedatectl show`_ cannot perform a proper time sync against the default servers. - -| A *"fix"* for this is mentioned in the issue above. -| That is to configure ``/etc/systemd/timesyncd.conf`` and restart ``systemd-timesyncd.service``. - -.. note:: - - A proposal to override the time sync check has been put up for discussion in `#2144`_. - -Missing Nvidia Proprietary Driver `#2002`_ ------------------------------------------- - -| In some instances, the nvidia driver might not have all the nessecary packages installed. -| This is due to the kernel selection and/or hardware setups requiring additional packages to work properly. - -A common workaround is to install the package `linux-headers`_ and `nvidia-dkms`_ - -ARM, 32bit and other CPU types error out `#1686`_, `#2185`_ ------------------------------------------------------------ - -| This is a bit of a catch-all known issue. -| Officially `x86_64`_ is only supported by Arch Linux. -| Hence little effort have been put into supporting other platforms. - -| In theory, other architectures should work but small quirks might arise. - -| PR's are welcome but please be respectful of the delays in merging. -| Other fixes, issues or features will be prioritized for the above reasons. - -Keyring is out of date `#2213`_ -------------------------------- - -| Missing key-issues tend to be that the `archlinux-keyring`_ package is out of date, usually as a result of an outdated ISO. -| There is an attempt from upstream to fix this issue, and it's the `archlinux-keyring-wkd-sync.service`_ - -| The service starts almost immediately during boot, and if network is not configured in time — the service will fail. -| Subsequently the ``archinstall`` run might operate on a old keyring despite there being an update service for this. - -| There is really no way to reliably over time work around this issue in ``archinstall``. -| Instead, efforts to the upstream service should be considered the way forward. And/or keys not expiring betwene a sane ammount of ISO's. - -.. note:: - - The issue can happen on new ISO's too even as little as a few days after release, as some keys might expire right after the keyring is *"burnt in"* to the ISO. - -.. note:: - - Another common issue relating to the network not being configured, is that time might not be set correctly - resulting in the keyring not being able to update. See :ref:`waiting for time sync`. - -AUR packages ------------- - -| This is also a catch-all issue. -| `AUR is unsupported `_, and until that changes we cannot use AUR packages to solve feature requests in ``archinstall``. - -| This means that feature requests like supporting filesystems such as `ZFS`_ can not be added, and issues cannot be solved by using AUR packages either. - -.. note:: - - But in spirit of giving the community options, ``archinstall`` supports :ref:`archinstall.Plugins`, which means you can run ``archinstall --plugin `` and source an AUR plugin. - - `torxed/archinstall-aur `_ is a reference implementation for plugins: - - .. code-block:: console - - # archinstall --plugin https://archlinux.life/aur-plugin - - `phisch/archinstall-aur `_ is another alternative: - - .. code-block:: console - - # archinstall --plugin https://raw.githubusercontent.com/phisch/archinstall-aur/master/archinstall-aur.py - - .. warning:: - - This will allow for unsupported usage of AUR during installation. - -.. _#2213: https://github.com/archlinux/archinstall/issues/2213 -.. _#2185: https://github.com/archlinux/archinstall/issues/2185 -.. _#2144: https://github.com/archlinux/archinstall/issues/2144 -.. _#2002: https://github.com/archlinux/archinstall/issues/2002 -.. _#1686: https://github.com/archlinux/archinstall/issues/1686 -.. _linux-headers: https://archlinux.org/packages/core/x86_64/linux-headers/ -.. _nvidia-dkms: https://archlinux.org/packages/extra/x86_64/nvidia-dkms/ -.. _x86_64: https://wiki.archlinux.org/title/Frequently_asked_questions#What_architectures_does_Arch_support? -.. _archlinux-keyring: https://archlinux.org/packages/core/any/archlinux-keyring/ -.. _archlinux-keyring-wkd-sync.service: https://gitlab.archlinux.org/archlinux/archlinux-keyring/-/blob/7e672dad10652a80d1cc575d75cdb46442cd7f96/wkd_sync/archlinux-keyring-wkd-sync.service.in -.. _ZFS: https://aur.archlinux.org/packages/zfs-linux -.. _archinstall: https://github.com/archlinux/archinstall/ -.. _timedatectl show: https://github.com/archlinux/archinstall/blob/e6344f93f7e476d05bbcd642f2ed91fdde545870/archinstall/lib/installer.py#L136 \ No newline at end of file diff --git a/docs/help/report_bug.rst b/docs/help/report_bug.rst deleted file mode 100644 index bd0ac50a..00000000 --- a/docs/help/report_bug.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. _help.issues: - -Report Issues & Bugs -==================== - -Issues and bugs should be reported over at `https://github.com/archlinux/archinstall/issues `_. - -General questions, enhancements and security issues can be reported over there too. -For quick issues or if you need help, head over to the Discord server which has a help channel. - -Log files ---------- - -| When submitting a help ticket, please include the :code:`/var/log/archinstall/install.log`. -| It can be found both on the live ISO but also in the installed filesystem if the base packages were strapped in. - -.. tip:: - | An easy way to submit logs is ``curl -F'file=@/var/log/archinstall/install.log' https://0x0.st``. - | Use caution when submitting other log files, but ``archinstall`` pledges to keep ``install.log`` safe for posting publicly! - -| There are additional log files under ``/var/log/archinstall/`` that can be useful: - - - ``/var/log/archinstall/user_configuration.json`` - Stores most of the guided answers in the installer - - ``/var/log/archinstall/user_credentials.json`` - Stores any usernames or passwords, can be passed to ``--creds`` - - ``/var/log/archinstall/user_disk_layouts.json`` - Stores the chosen disks and their layouts - - ``/var/log/archinstall/install.log`` - A log file over what steps were taken by archinstall - - ``/var/log/archinstall/cmd_history.txt`` - A complete command history, command by command in order - - ``/var/log/archinstall/cmd_output.txt`` - A raw output from all the commands that were executed by archinstall - -.. warning:: - - We only try to guarantee that ``/var/log/archinstall/install.log`` is free from sensitive information. - Any other log file should be pasted with **utmost care**! diff --git a/docs/installing/guided.rst b/docs/installing/guided.rst deleted file mode 100644 index dcedfc10..00000000 --- a/docs/installing/guided.rst +++ /dev/null @@ -1,287 +0,0 @@ -.. _guided: - -Guided installation -=================== - -Archinstall ships with a pre-programmed `Guided Installer`_ guiding you through the mandatory steps as well as some optional configurations that can be done. - -.. note:: - - Other pre-programmed scripts can be invoked by executing :code:`archinstall \ No newline at end of file +| **archinstall** is library which can be used to install Arch Linux. +| The library comes packaged with different pre-configured installers, such as the default :ref:`guided` installer. +| +| A demo of the :ref:`guided` installer can be seen here: `https://www.youtube.com/watch?v=9Xt7X_Iqg6E `_. + +Some of the features of Archinstall are: + +* **Context friendly.** The library always executes calls in sequential order to ensure installation-steps don't overlap or execute in the wrong order. It also supports *(and uses)* context wrappers to ensure cleanup and final tasks such as ``mkinitcpio`` are called when needed. + +* **Full transparency** Logs and insights can be found at ``/var/log/archinstall`` both in the live ISO and partially on the installed system. + +* **Accessibility friendly** Archinstall works with ``espeakup`` and other accessibility tools thanks to the use of a TUI. + +.. toctree:: + :maxdepth: 1 + :caption: Running Archinstall + + installing/guided + +.. toctree:: + :maxdepth: 3 + :caption: Getting help + + help/known_issues + help/report_bug + help/discord + +.. toctree:: + :maxdepth: 3 + :caption: Archinstall as a library + + installing/python + examples/python + archinstall/plugins + +.. toctree:: + :maxdepth: 3 + :caption: API Reference + + archinstall/Installer diff --git a/docs/installing/guided.rst b/docs/installing/guided.rst new file mode 100644 index 00000000..dcedfc10 --- /dev/null +++ b/docs/installing/guided.rst @@ -0,0 +1,287 @@ +.. _guided: + +Guided installation +=================== + +Archinstall ships with a pre-programmed `Guided Installer`_ guiding you through the mandatory steps as well as some optional configurations that can be done. + +.. note:: + + Other pre-programmed scripts can be invoked by executing :code:`archinstall