From fe237eac3ce29cd90f002cfb41a596e3f3830193 Mon Sep 17 00:00:00 2001 From: advaithm Date: Sun, 4 Apr 2021 07:24:52 +0530 Subject: networkmanager support --- archinstall/lib/user_interaction.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 80db7be1..37f06ae8 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -94,7 +94,7 @@ def ask_to_configure_network(): # Optionally configure one network interface. #while 1: # {MAC: Ifname} - interfaces = {'ISO-CONFIG' : 'Copy ISO network configuration to installation', **list_interfaces()} + interfaces = {'ISO-CONFIG' : 'Copy ISO network configuration to installation','NetworkManager':'Use NetworkManager to control and manage you internet conntetion', **list_interfaces()} nic = generic_select(interfaces.values(), "Select one network interface to configure (leave blank to skip): ") if nic and nic != 'Copy ISO network configuration to installation': @@ -119,9 +119,9 @@ def ask_to_configure_network(): if len(dns_input := input('Enter your DNS servers (space separated, blank for none): ').strip()): dns = dns_input.split(' ') - return {'nic': nic, 'dhcp': False, 'ip': ip, 'gateway' : gateway, 'dns' : dns} + return {'nic': nic, 'dhcp': False, 'ip': ip, 'gateway' : gateway, 'dns' : dns}s else: - return {'nic': nic} + return {'nic': nic,'NetworkManager':True} elif nic: return nic -- cgit v1.2.3-70-g09d2 From 454d712d088091af4818c2cd8638e2152bdd4d54 Mon Sep 17 00:00:00 2001 From: advaithm Date: Sun, 4 Apr 2021 09:03:49 +0530 Subject: fixed typo --- archinstall/lib/user_interaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 37f06ae8..c05a09ee 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -119,7 +119,7 @@ def ask_to_configure_network(): if len(dns_input := input('Enter your DNS servers (space separated, blank for none): ').strip()): dns = dns_input.split(' ') - return {'nic': nic, 'dhcp': False, 'ip': ip, 'gateway' : gateway, 'dns' : dns}s + return {'nic': nic, 'dhcp': False, 'ip': ip, 'gateway' : gateway, 'dns' : dns} else: return {'nic': nic,'NetworkManager':True} elif nic: -- cgit v1.2.3-70-g09d2 From f42d52624e19478bea0a57b03fb46119375714f2 Mon Sep 17 00:00:00 2001 From: advaithm Date: Sun, 4 Apr 2021 09:08:17 +0530 Subject: moved around the if block --- archinstall/lib/user_interaction.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index c05a09ee..83e59e90 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -98,6 +98,8 @@ def ask_to_configure_network(): nic = generic_select(interfaces.values(), "Select one network interface to configure (leave blank to skip): ") if nic and nic != 'Copy ISO network configuration to installation': + if nic == 'Use NetworkManager to control and manage you internet conntetion': + return {'nic': nic,'NetworkManager':True} mode = generic_select(['DHCP (auto detect)', 'IP (static)'], f"Select which mode to configure for {nic}: ") if mode == 'IP (static)': while 1: @@ -121,7 +123,7 @@ def ask_to_configure_network(): return {'nic': nic, 'dhcp': False, 'ip': ip, 'gateway' : gateway, 'dns' : dns} else: - return {'nic': nic,'NetworkManager':True} + return {'nic': nic} elif nic: return nic -- cgit v1.2.3-70-g09d2 From 9daa3f49242227e0cc6c2272b9322578edaab644 Mon Sep 17 00:00:00 2001 From: advaithm Date: Sun, 4 Apr 2021 09:26:28 +0530 Subject: fixed some typos and changed up how we detect if we have to enable/install network manager --- archinstall/lib/user_interaction.py | 4 ++-- examples/guided.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 83e59e90..f8b4d9c5 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -94,11 +94,11 @@ def ask_to_configure_network(): # Optionally configure one network interface. #while 1: # {MAC: Ifname} - interfaces = {'ISO-CONFIG' : 'Copy ISO network configuration to installation','NetworkManager':'Use NetworkManager to control and manage you internet conntetion', **list_interfaces()} + interfaces = {'ISO-CONFIG' : 'Copy ISO network configuration to installation','NetworkManager':'Use NetworkManager to control and manage your internet connection', **list_interfaces()} nic = generic_select(interfaces.values(), "Select one network interface to configure (leave blank to skip): ") if nic and nic != 'Copy ISO network configuration to installation': - if nic == 'Use NetworkManager to control and manage you internet conntetion': + if nic == 'Use NetworkManager to control and manage your internet connection': return {'nic': nic,'NetworkManager':True} mode = generic_select(['DHCP (auto detect)', 'IP (static)'], f"Select which mode to configure for {nic}: ") if mode == 'IP (static)': diff --git a/examples/guided.py b/examples/guided.py index 5ca45008..1e077169 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -317,7 +317,7 @@ def perform_installation(device, boot_partition, language, mirrors): # Perform a copy of the config if archinstall.arguments.get('nic', None) == 'Copy ISO network configuration to installation': installation.copy_ISO_network_config(enable_services=True) # Sources the ISO network configuration to the install medium. - elif archinstall.arguments.get('NetworkManager',None) == True: + elif archinstall.arguments.get('nic',None) == 'Use NetworkManager to control and manage your internet connection': installation.add_additional_packages("networkmanager") installation.enable_service('NetworkManager.service') # Otherwise, if a interface was selected, configure that interface -- cgit v1.2.3-70-g09d2 From d9984550b6ad4f4e7d659adb32cef541e85d69d9 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Tue, 6 Apr 2021 18:04:03 -0400 Subject: Move choice into guided installation instead of DEs Arch wiki says packages should enable the user services automatically --- archinstall/lib/user_interaction.py | 8 ++++++++ examples/guided.py | 10 +++++++++- profiles/applications/pipewire.py | 4 ++-- profiles/awesome.py | 6 ------ profiles/cinnamon.py | 6 ------ profiles/gnome.py | 6 ------ profiles/i3-gaps.py | 8 +------- profiles/i3-wm.py | 6 ------ profiles/kde.py | 6 ------ profiles/xfce4.py | 6 ------ 10 files changed, 20 insertions(+), 46 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 58f88bd2..3c5115ea 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -89,6 +89,14 @@ def ask_for_a_timezone(): level=LOG_LEVELS.Warning, fg='red' ) + +def ask_for_audio_selection(): + audio = "pulseaudio" # Default for most desktop environments + pipewire_choice = input("Would you like to install the pipewire audio server? [Y/n] ").lower() + if pipewire_choice == "y": + audio = "pipewire" + + return audio def ask_to_configure_network(): # Optionally configure one network interface. diff --git a/examples/guided.py b/examples/guided.py index 81cc2991..cd45bef5 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -182,6 +182,10 @@ def ask_user_questions(): ) exit(1) + # Ask about audio server selection (this right now just asks for pipewire and defaults to pulseaudio otherwise) + if not archinstall.arguments.get('audio', None): + archinstall.arguments['audio'] = archinstall.ask_for_audio_selection() + # Additional packages (with some light weight error handling for invalid package names) if not archinstall.arguments.get('packages', None): archinstall.arguments['packages'] = [package for package in input('Write additional packages to install (space separated, leave blank to skip): ').split(' ') if len(package)] @@ -329,7 +333,11 @@ def perform_installation(device, boot_partition, language, mirrors): installation.enable_service('systemd-networkd') installation.enable_service('systemd-resolved') - + print('This audio server will be used: ' + archinstall.arguments.get('audio', None)) + if archinstall.arguments.get('audio', None) == 'pipewire': + print('Installing pipewire ...') + installation.install_profile('pipewire') + 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/profiles/applications/pipewire.py b/profiles/applications/pipewire.py index 2d9f6a6c..aea5b50d 100644 --- a/profiles/applications/pipewire.py +++ b/profiles/applications/pipewire.py @@ -1,5 +1,5 @@ import archinstall -__packages__ = ["pipewire", "pipewire-alsa", "pipewire-docs", "pipewire-jack", "pipewire-media-session", "pipewire-pulse", "gst-plugin-pipewire", "libpulse"] +packages = ["pipewire", "pipewire-alsa", "pipewire-docs", "pipewire-jack", "pipewire-media-session", "pipewire-pulse", "gst-plugin-pipewire", "libpulse"] -installation.add_additional_packages(__packages__) +installation.add_additional_packages(packages) diff --git a/profiles/awesome.py b/profiles/awesome.py index fcad1839..8004fc62 100644 --- a/profiles/awesome.py +++ b/profiles/awesome.py @@ -25,12 +25,6 @@ def _prep_function(*args, **kwargs): # through importlib.util.spec_from_file_location("awesome", "/somewhere/awesome.py") # or through conventional import awesome if __name__ == 'awesome': - # Install the pipewire audio server if the user wants to use it - pipewire_choice = input("Would you like to install the pipewire audio server? [Y/n] ").lower() - if choice == "y": - pipewire = archinstall.Application(installation, 'pipewire') - pipewire.install() - # Install the application awesome from the template under /applications/ awesome = archinstall.Application(installation, 'awesome') awesome.install() diff --git a/profiles/cinnamon.py b/profiles/cinnamon.py index a3225c30..dac38bd3 100644 --- a/profiles/cinnamon.py +++ b/profiles/cinnamon.py @@ -22,12 +22,6 @@ def _prep_function(*args, **kwargs): # through importlib.util.spec_from_file_location("cinnamon", "/somewhere/cinnamon.py") # or through conventional import cinnamon if __name__ == 'cinnamon': - # Install the pipewire audio server if the user wants to use it - pipewire_choice = input("Would you like to install the pipewire audio server? [Y/n] ").lower() - if choice == "y": - pipewire = archinstall.Application(installation, 'pipewire') - pipewire.install() - # Install dependency profiles installation.install_profile('xorg') diff --git a/profiles/gnome.py b/profiles/gnome.py index d13e6eee..63fcd57d 100644 --- a/profiles/gnome.py +++ b/profiles/gnome.py @@ -23,12 +23,6 @@ def _prep_function(*args, **kwargs): # through importlib.util.spec_from_file_location("gnome", "/somewhere/gnome.py") # or through conventional import gnome if __name__ == 'gnome': - # Install the pipewire audio server if the user wants to use it - pipewire_choice = input("Would you like to install the pipewire audio server? [Y/n] ").lower() - if choice == "y": - pipewire = archinstall.Application(installation, 'pipewire') - pipewire.install() - # Install dependency profiles installation.install_profile('xorg') diff --git a/profiles/i3-gaps.py b/profiles/i3-gaps.py index f75dfb11..50511dce 100644 --- a/profiles/i3-gaps.py +++ b/profiles/i3-gaps.py @@ -29,13 +29,7 @@ def _post_install(*args, **kwargs): return True -if __name__ == 'i3-wm': - # Install the pipewire audio server if the user wants to use it - pipewire_choice = input("Would you like to install the pipewire audio server? [Y/n] ").lower() - if choice == "y": - pipewire = archinstall.Application(installation, 'pipewire') - pipewire.install() - +if __name__ == 'i3-wm': # Install dependency profiles installation.install_profile('xorg') # gaps is installed by deafult so we are overriding it here diff --git a/profiles/i3-wm.py b/profiles/i3-wm.py index 6d40065c..cd6cbc81 100644 --- a/profiles/i3-wm.py +++ b/profiles/i3-wm.py @@ -29,12 +29,6 @@ def _post_install(*args, **kwargs): return True if __name__ == 'i3-wm': - # Install the pipewire audio server if the user wants to use it - pipewire_choice = input("Would you like to install the pipewire audio server? [Y/n] ").lower() - if choice == "y": - pipewire = archinstall.Application(installation, 'pipewire') - pipewire.install() - # Install dependency profiles installation.install_profile('xorg') # we are installing lightdm to auto start i3 diff --git a/profiles/kde.py b/profiles/kde.py index dabba57b..0207ed22 100644 --- a/profiles/kde.py +++ b/profiles/kde.py @@ -32,12 +32,6 @@ def _post_install(*args, **kwargs): # through importlib.util.spec_from_file_location("kde", "/somewhere/kde.py") # or through conventional import kde if __name__ == 'kde': - # Install the pipewire audio server if the user wants to use it - pipewire_choice = input("Would you like to install the pipewire audio server? [Y/n] ").lower() - if choice == "y": - pipewire = archinstall.Application(installation, 'pipewire') - pipewire.install() - # Install dependency profiles installation.install_profile('xorg') diff --git a/profiles/xfce4.py b/profiles/xfce4.py index 8e418578..36c9958a 100644 --- a/profiles/xfce4.py +++ b/profiles/xfce4.py @@ -23,12 +23,6 @@ def _prep_function(*args, **kwargs): # through importlib.util.spec_from_file_location("xfce4", "/somewhere/xfce4.py") # or through conventional import xfce4 if __name__ == 'xfce4': - # Install the pipewire audio server if the user wants to use it - pipewire_choice = input("Would you like to install the pipewire audio server? [Y/n] ").lower() - if choice == "y": - pipewire = archinstall.Application(installation, 'pipewire') - pipewire.install() - # Install dependency profiles installation.install_profile('xorg') -- cgit v1.2.3-70-g09d2 From 50c354471f4c27b0c30153902802fb396c76f2b0 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Wed, 7 Apr 2021 09:29:50 +0200 Subject: Change phrasing to indicate pulseaudio as default --- archinstall/lib/user_interaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 3c5115ea..4ab9b9a9 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -92,7 +92,7 @@ def ask_for_a_timezone(): def ask_for_audio_selection(): audio = "pulseaudio" # Default for most desktop environments - pipewire_choice = input("Would you like to install the pipewire audio server? [Y/n] ").lower() + pipewire_choice = input("Would you like to install pipewire instead of pulseaudio as the default audio server? [Y/n] ").lower() if pipewire_choice == "y": audio = "pipewire" -- cgit v1.2.3-70-g09d2 From f4233e73f0b70f236a9fd357ee11bf194224a4fb Mon Sep 17 00:00:00 2001 From: Dylan Taylor Date: Wed, 7 Apr 2021 21:31:28 -0400 Subject: Assume yes is the user's intention if empty response for pipewire prompt --- archinstall/lib/user_interaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index c5ff17ca..b94bf3f5 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -107,7 +107,7 @@ def ask_for_a_timezone(): def ask_for_audio_selection(): audio = "pulseaudio" # Default for most desktop environments pipewire_choice = input("Would you like to install pipewire instead of pulseaudio as the default audio server? [Y/n] ").lower() - if pipewire_choice == "y": + if pipewire_choice in ("y", ""): audio = "pipewire" return audio -- cgit v1.2.3-70-g09d2 From 1292c07796b763b926fd5edb21905a663eaef8f0 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Sun, 11 Apr 2021 10:20:33 +0200 Subject: Fixed PR #273. Moved the graphic drivers into hardware since they are hardware specific, in the long run maybe we move them into 'drivers' or something. And moved the user interaction from gfx_drivers into user_interactions. And removed the import from installer.py to __init__.py since we don't want to import 'global functions' in extension imports. --- archinstall/lib/hardware.py | 19 +++++++++++ archinstall/lib/installer.py | 1 - archinstall/lib/user_interaction.py | 67 +++++++++++++++++++++++++++++++++++++ profiles/sway.py | 2 +- profiles/xorg.py | 2 +- 5 files changed, 88 insertions(+), 3 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/hardware.py b/archinstall/lib/hardware.py index 3da333de..047b3491 100644 --- a/archinstall/lib/hardware.py +++ b/archinstall/lib/hardware.py @@ -2,6 +2,25 @@ import os, subprocess, json from .general import sys_command from .networking import list_interfaces, enrichIfaceTypes from typing import Optional + +AVAILABLE_GFX_DRIVERS = { + # Sub-dicts are layer-2 options to be selected + # and lists are a list of packages to be installed + 'AMD / ATI' : { + 'amd' : ['xf86-video-amdgpu'], + 'ati' : ['xf86-video-ati'] + }, + 'intel' : ['xf86-video-intel'], + 'nvidia' : { + 'open source' : ['xf86-video-nouveau'], + 'proprietary' : ['nvidia'] + }, + 'mesa' : ['mesa'], + 'fbdev' : ['xf86-video-fbdev'], + 'vesa' : ['xf86-video-vesa'], + 'vmware' : ['xf86-video-vmware'] +} + def hasWifi()->bool: return 'WIRELESS' in enrichIfaceTypes(list_interfaces().values()).values() diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 5523b1e1..484e7407 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -10,7 +10,6 @@ from .systemd import Networkd from .output import log, LOG_LEVELS from .storage import storage from .hardware import * -from .gfx_drivers import * # Any package that the Installer() is responsible for (optional and the default ones) __packages__ = ["base", "base-devel", "linux", "linux-firmware", "efibootmgr", "nano", "ntp", "iwd"] diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index b94bf3f5..d17691de 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -5,6 +5,8 @@ from .locale_helpers import search_keyboard_layout from .output import log, LOG_LEVELS from .storage import storage from .networking import list_interfaces +from .general import sys_command +from .hardware import AVAILABLE_GFX_DRIVERS ## TODO: Some inconsistencies between the selection processes. ## Some return the keys from the options, some the values? @@ -363,3 +365,68 @@ def select_mirror_regions(mirrors, show_top_mirrors=True): return selected_mirrors raise RequirementError("Selecting mirror region require a least one region to be given as an option.") + +def select_driver(options=AVAILABLE_GFX_DRIVERS): + """ + Some what convoluted function, which's 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) + """ + drivers = sorted(list(options)) + + if len(drivers) >= 1: + for index, driver in enumerate(drivers): + print(f"{index}: {driver}") + + print(' -- The above list are supported graphic card drivers. --') + print(' -- You need to select (and read about) which one you need. --') + + lspci = sys_command(f'/usr/bin/lspci') + for line in lspci.trace_log.split(b'\r\n'): + if b' vga ' in line.lower(): + if b'nvidia' in line.lower(): + print(' ** nvidia card detected, suggested driver: nvidia **') + elif b'amd' in line.lower(): + print(' ** AMD card detected, suggested driver: AMD / ATI **') + + selected_driver = input('Select your graphics card driver: ') + initial_option = selected_driver + + # Disabled search for now, only a few profiles exist anyway + # + #print(' -- You can enter ? or help to search for more drivers --') + #if selected_driver.lower() in ('?', 'help'): + # filter_string = input('Search for layout containing (example: "sv-"): ') + # new_options = search_keyboard_layout(filter_string) + # return select_language(new_options) + if selected_driver.isdigit() and (pos := int(selected_driver)) <= len(drivers)-1: + selected_driver = options[drivers[pos]] + elif selected_driver in options: + selected_driver = options[options.index(selected_driver)] + elif len(selected_driver) == 0: + raise RequirementError("At least one graphics driver is needed to support a graphical environment. Please restart the installer and try again.") + else: + raise RequirementError("Selected driver does not exist.") + + if type(selected_driver) == dict: + driver_options = sorted(list(selected_driver)) + for index, driver_package_group in enumerate(driver_options): + print(f"{index}: {driver_package_group}") + + selected_driver_package_group = input(f'Which driver-type do you want for {initial_option}: ') + if selected_driver_package_group.isdigit() and (pos := int(selected_driver_package_group)) <= len(driver_options)-1: + selected_driver_package_group = selected_driver[driver_options[pos]] + elif selected_driver_package_group in selected_driver: + selected_driver_package_group = selected_driver[selected_driver.index(selected_driver_package_group)] + elif len(selected_driver_package_group) == 0: + raise RequirementError(f"At least one driver package is required for a graphical environment using {selected_driver}. Please restart the installer and try again.") + else: + raise RequirementError(f"Selected driver-type does not exist for {initial_option}.") + + return selected_driver_package_group + + return selected_driver + + raise RequirementError("Selecting drivers require a least one profile to be given as an option.") \ No newline at end of file diff --git a/profiles/sway.py b/profiles/sway.py index 10e30753..f1d2c1f1 100644 --- a/profiles/sway.py +++ b/profiles/sway.py @@ -10,7 +10,7 @@ def _prep_function(*args, **kwargs): for more input before any other installer steps start. """ - __builtins__['_gfx_driver_packages'] = archinstall.lib.gfx_drivers.select_driver() + __builtins__['_gfx_driver_packages'] = archinstall.select_driver() return True diff --git a/profiles/xorg.py b/profiles/xorg.py index 6ee72487..42597a37 100644 --- a/profiles/xorg.py +++ b/profiles/xorg.py @@ -12,7 +12,7 @@ def _prep_function(*args, **kwargs): for more input before any other installer steps start. """ - __builtins__['_gfx_driver_packages'] = archinstall.lib.gfx_drivers.select_driver() + __builtins__['_gfx_driver_packages'] = archinstall.select_driver() # TODO: Add language section and/or merge it with the locale selected # earlier in for instance guided.py installer. -- cgit v1.2.3-70-g09d2 From 87ca382f6c69bfbf7d7db73e179b37ec008cbdf7 Mon Sep 17 00:00:00 2001 From: Dylan Taylor Date: Sun, 11 Apr 2021 21:34:31 -0400 Subject: Use generic_select for GPU driver Make it so there isn't a space in the key Try to simplify things more --- archinstall/lib/gfx_drivers.py | 84 ------------------------------------- archinstall/lib/hardware.py | 2 +- archinstall/lib/user_interaction.py | 49 +++++----------------- 3 files changed, 11 insertions(+), 124 deletions(-) delete mode 100644 archinstall/lib/gfx_drivers.py (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/gfx_drivers.py b/archinstall/lib/gfx_drivers.py deleted file mode 100644 index e1fb2824..00000000 --- a/archinstall/lib/gfx_drivers.py +++ /dev/null @@ -1,84 +0,0 @@ -import archinstall - -AVAILABLE_DRIVERS = { - # Sub-dicts are layer-2 options to be selected - # and lists are a list of packages to be installed - 'AMD / ATI' : { - 'amd' : ['xf86-video-amdgpu'], - 'ati' : ['xf86-video-ati'] - }, - 'intel' : ['xf86-video-intel'], - 'nvidia' : { - 'open source' : ['xf86-video-nouveau'], - 'proprietary' : ['nvidia'] - }, - 'mesa' : ['mesa'], - 'fbdev' : ['xf86-video-fbdev'], - 'vesa' : ['xf86-video-vesa'], - 'vmware' : ['xf86-video-vmware'] -} - -def select_driver(options=AVAILABLE_DRIVERS): - """ - Some what convoluted function, which's 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) - """ - drivers = sorted(list(options)) - - if len(drivers) >= 1: - for index, driver in enumerate(drivers): - print(f"{index}: {driver}") - - print(' -- The above list are supported graphic card drivers. --') - print(' -- You need to select (and read about) which one you need. --') - - lspci = archinstall.sys_command(f'/usr/bin/lspci') - for line in lspci.trace_log.split(b'\r\n'): - if b' vga ' in line.lower(): - if b'nvidia' in line.lower(): - print(' ** nvidia card detected, suggested driver: nvidia **') - elif b'amd' in line.lower(): - print(' ** AMD card detected, suggested driver: AMD / ATI **') - - selected_driver = input('Select your graphics card driver: ') - initial_option = selected_driver - - # Disabled search for now, only a few profiles exist anyway - # - #print(' -- You can enter ? or help to search for more drivers --') - #if selected_driver.lower() in ('?', 'help'): - # filter_string = input('Search for layout containing (example: "sv-"): ') - # new_options = search_keyboard_layout(filter_string) - # return select_language(new_options) - if selected_driver.isdigit() and (pos := int(selected_driver)) <= len(drivers)-1: - selected_driver = options[drivers[pos]] - elif selected_driver in options: - selected_driver = options[options.index(selected_driver)] - elif len(selected_driver) == 0: - raise archinstall.RequirementError("At least one graphics driver is needed to support a graphical environment. Please restart the installer and try again.") - else: - raise archinstall.RequirementError("Selected driver does not exist.") - - if type(selected_driver) == dict: - driver_options = sorted(list(selected_driver)) - for index, driver_package_group in enumerate(driver_options): - print(f"{index}: {driver_package_group}") - - selected_driver_package_group = input(f'Which driver-type do you want for {initial_option}: ') - if selected_driver_package_group.isdigit() and (pos := int(selected_driver_package_group)) <= len(driver_options)-1: - selected_driver_package_group = selected_driver[driver_options[pos]] - elif selected_driver_package_group in selected_driver: - selected_driver_package_group = selected_driver[selected_driver.index(selected_driver_package_group)] - elif len(selected_driver_package_group) == 0: - raise archinstall.RequirementError(f"At least one driver package is required for a graphical environment using {selected_driver}. Please restart the installer and try again.") - else: - raise archinstall.RequirementError(f"Selected driver-type does not exist for {initial_option}.") - - return selected_driver_package_group - - return selected_driver - - raise archinstall.RequirementError("Selecting drivers require a least one profile to be given as an option.") \ No newline at end of file diff --git a/archinstall/lib/hardware.py b/archinstall/lib/hardware.py index 047b3491..d6cf982c 100644 --- a/archinstall/lib/hardware.py +++ b/archinstall/lib/hardware.py @@ -12,7 +12,7 @@ AVAILABLE_GFX_DRIVERS = { }, 'intel' : ['xf86-video-intel'], 'nvidia' : { - 'open source' : ['xf86-video-nouveau'], + 'open-source' : ['xf86-video-nouveau'], 'proprietary' : ['nvidia'] }, 'mesa' : ['mesa'], diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 425dc9a9..114c7c6a 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -412,15 +412,7 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS): (The template xorg is for beginner users, not advanced, and should there for appeal to the general public first and edge cases later) """ - drivers = sorted(list(options)) - - if len(drivers) >= 1: - for index, driver in enumerate(drivers): - print(f"{index}: {driver}") - - print(' -- The above list are supported graphic card drivers. --') - print(' -- You need to select (and read about) which one you need. --') - + if len(options) >= 1: lspci = sys_command(f'/usr/bin/lspci') for line in lspci.trace_log.split(b'\r\n'): if b' vga ' in line.lower(): @@ -429,37 +421,16 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS): elif b'amd' in line.lower(): print(' ** AMD card detected, suggested driver: AMD / ATI **') - selected_driver = input('Select your graphics card driver: ') + selected_driver = generic_select(options, input_text="Select your graphics card driver: ", sort=True) initial_option = selected_driver - # Disabled search for now, only a few profiles exist anyway - # - #print(' -- You can enter ? or help to search for more drivers --') - #if selected_driver.lower() in ('?', 'help'): - # filter_string = input('Search for layout containing (example: "sv-"): ') - # new_options = search_keyboard_layout(filter_string) - # return select_language(new_options) - if selected_driver.isdigit() and (pos := int(selected_driver)) <= len(drivers)-1: - selected_driver = options[drivers[pos]] - elif selected_driver in options: - selected_driver = options[options.index(selected_driver)] - elif len(selected_driver) == 0: - raise RequirementError("At least one graphics driver is needed to support a graphical environment. Please restart the installer and try again.") - else: - raise RequirementError("Selected driver does not exist.") - - if type(selected_driver) == dict: - driver_options = sorted(list(selected_driver)) - for index, driver_package_group in enumerate(driver_options): - print(f"{index}: {driver_package_group}") - - selected_driver_package_group = input(f'Which driver-type do you want for {initial_option}: ') - if selected_driver_package_group.isdigit() and (pos := int(selected_driver_package_group)) <= len(driver_options)-1: - selected_driver_package_group = selected_driver[driver_options[pos]] - elif selected_driver_package_group in selected_driver: - selected_driver_package_group = selected_driver[selected_driver.index(selected_driver_package_group)] - elif len(selected_driver_package_group) == 0: - raise RequirementError(f"At least one driver package is required for a graphical environment using {selected_driver}. Please restart the installer and try again.") + if type(options[initial_option]) == dict: + driver_options = sorted(options[initial_option].keys()) + + selected_driver_package_group = generic_select(driver_options, input_text=f"Which driver-type do you want for {initial_option}: ") + if selected_driver_package_group in options[initial_option].keys(): + print(options[initial_option][selected_driver_package_group]) + selected_driver = options[initial_option][selected_driver_package_group] else: raise RequirementError(f"Selected driver-type does not exist for {initial_option}.") @@ -467,4 +438,4 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS): return selected_driver - raise RequirementError("Selecting drivers require a least one profile to be given as an option.") \ No newline at end of file + raise RequirementError("Selecting drivers require a least one profile to be given as an option.") -- cgit v1.2.3-70-g09d2 From 6e5ea3aa65f94f7d7059aaa3ccc51e292d45dfff Mon Sep 17 00:00:00 2001 From: Владислав Date: Sat, 17 Apr 2021 10:56:46 +0300 Subject: Fix selecting language layout by name --- archinstall/lib/user_interaction.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index df8668af..10df6755 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -358,8 +358,7 @@ def select_language(options, show_only_country_codes=True): # all possible language layouts, and we might want to write # for instance sv-latin1 (if we know that exists) without having to # go through the search step. - elif selected_language in options: - selected_language = options[options.index(selected_language)] + elif selected_language in languages: return selected_language else: raise RequirementError("Selected language does not exist.") -- cgit v1.2.3-70-g09d2 From c63d2140a46ee4001f855a575499cd51b7d0c0ee Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Sat, 17 Apr 2021 09:59:51 +0200 Subject: Removed merge artifact. --- archinstall/lib/user_interaction.py | 1 - 1 file changed, 1 deletion(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 82ddab7a..822f63be 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -411,7 +411,6 @@ def select_mirror_regions(mirrors, show_top_mirrors=True): raise RequirementError("Selected region does not exist.") return selected_mirrors -<<<<<<< HEAD raise RequirementError("Selecting mirror region require a least one region to be given as an option.") -- cgit v1.2.3-70-g09d2 From 9991b19a064536defd22a7941504a4699aa906b0 Mon Sep 17 00:00:00 2001 From: SecondThundeR Date: Sat, 17 Apr 2021 12:54:04 +0300 Subject: Initial rework of generic_select function Here are list of changes: - Removed `sort` parameter, since every function has a sorted list in most cases - Added two new parameters to disable local output of options items and allow empty input from the user by returning None - Added a while loop, where it returns each time a RequirementError is raised - Added log info for each input error to help figure out what the problem is - Changed the check of the stripped input length to compare with 0, since the length cannot be less than 0 - Changed `isdigit` to `isnumeric`, which returns False if given digit is negative - Slightly changed a check for an out of range error - Removed displaying the list of available options when input is incorrect, in order to prevent the screen from overflowing - Added log info if options list is empty - Added log info if options are not dictionary or list - Added dictionary values conversion to accept only list and dictionaries as option - Added sorting dictionary values by default --- archinstall/lib/user_interaction.py | 65 +++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 20 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 10df6755..57558e14 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -209,7 +209,7 @@ def ask_for_main_filesystem_format(): value = generic_select(options.values(), "Select which filesystem your main partition should use (by number or name): ") return next((key for key, val in options.items() if val == value), None) -def generic_select(options, input_text="Select one of the above by index or absolute value: ", sort=True): +def generic_select(options, input_text="Select one of the above by index or absolute value: ", allow_empty_input=True, options_output=True): """ A generic select function that does not output anything other than the options and their indexes. As an example: @@ -220,26 +220,51 @@ def generic_select(options, input_text="Select one of the above by index or abso 3: third option """ - if type(options) == dict: options = list(options) - if sort: options = sorted(list(options)) - if len(options) <= 0: raise RequirementError('generic_select() requires at least one option to operate.') - - for index, option in enumerate(options): - print(f"{index}: {option}") - - selected_option = input(input_text) - if len(selected_option.strip()) <= 0: - return None - elif selected_option.isdigit(): - selected_option = int(selected_option) - if selected_option > len(options): - raise RequirementError(f'Selected option "{selected_option}" is out of range') - selected_option = options[selected_option] - elif selected_option in options: - pass # We gave a correct absolute value - else: - raise RequirementError(f'Selected option "{selected_option}" does not exist in available options: {options}') + # Checking if options are different from `list` or `dict` + if type(options) not in [list, dict]: + log(" * It looks like there are something wrong with provided options. Maybe it's time to open an issue on GitHub! * ", fg='red') + log(" * Here are the link: https://github.com/archlinux/archinstall/issues * ", fg='yellow') + raise RequirementError("generic_select() reqiures list or dictionary as options.") + if type(options) == dict: options = sorted(list(options.values())) # To allow only `list` and `dict`, converting values of options and sorting them here. Therefore, now we can only provide the dictionary itself + # if sort: options = sorted(list(options)) # Moved sorting for dictionaries, as some lists are already sorted, when passed here + if len(options) == 0: + log(" * It looks like there are no options to choose from. Maybe it's time to open an issue on GitHub! * ", fg='red') + log(" * Here are the link: https://github.com/archlinux/archinstall/issues * ", fg='yellow') + raise RequirementError('generic_select() requires at least one option to operate.') + + # Disable the output of options items, if another function displays something different from this + if options_output: + for index, option in enumerate(options): + print(f"{index}: {option}") + + # The new changes introduce a single while loop for all inputs processed by this function + # Now the try...except...else block handles validation for invalid input from the user + while True: + try: + selected_option = input(input_text) + if len(selected_option.strip()) == 0: + # `allow_empty_input` parameter handles return of None on empty input, if necessary + # Otherwise raise `RequirementError` + if allow_empty_input: + return None + raise RequirementError('Please select an option to continue') + # Replaced `isdigit` with` isnumeric` to discard all negative numbers + elif selected_option.isnumeric(): + selected_option = int(selected_option) + if selected_option >= len(options): + raise RequirementError(f'Selected option "{selected_option}" is out of range') + selected_option = options[selected_option] + elif selected_option in options: + break # We gave a correct absolute value + else: + raise RequirementError(f'Selected option "{selected_option}" does not exist in available options') + except RequirementError as err: + log(f" * {err} * ", fg='red') + continue + else: + break + return selected_option def select_disk(dict_o_disks): -- cgit v1.2.3-70-g09d2 From a316846121caf4b26f96bed8dbe057b649cc409d Mon Sep 17 00:00:00 2001 From: SecondThundeR Date: Sat, 17 Apr 2021 16:35:21 +0300 Subject: Replace input with generic_select where necessary Here are list of changes: > From now on, `generic_select` will be called "Select function", for clarity - Slightly updated select function - Removed options output for some functions, where it's better to do with select function - Added sorting for all lists passed to select function - Replaced `dict.values()` with `dict` as options parameter - Simplified input checking for all functions that use the select function - Added temporary *(for now)* workaround for passing `?` and `help` inputs - Merged fix for `partition.format()` --- archinstall/lib/user_interaction.py | 109 +++++++++++++++--------------------- examples/guided.py | 6 +- profiles/desktop.py | 2 +- profiles/i3.py | 2 +- profiles/xorg.py | 32 +++-------- 5 files changed, 57 insertions(+), 94 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 57558e14..776650b5 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -154,9 +154,13 @@ def ask_to_configure_network(): # Optionally configure one network interface. #while 1: # {MAC: Ifname} - interfaces = {'ISO-CONFIG' : 'Copy ISO network configuration to installation','NetworkManager':'Use NetworkManager to control and manage your internet connection', **list_interfaces()} + interfaces = { + 'ISO-CONFIG' : 'Copy ISO network configuration to installation', + 'NetworkManager':'Use NetworkManager to control and manage your internet connection', + **list_interfaces() + } - nic = generic_select(interfaces.values(), "Select one network interface to configure (leave blank to skip): ") + nic = generic_select(interfaces, "Select one network interface to configure (leave blank to skip): ") if nic and nic != 'Copy ISO network configuration to installation': if nic == 'Use NetworkManager to control and manage your internet connection': return {'nic': nic,'NetworkManager':True} @@ -190,12 +194,12 @@ def ask_to_configure_network(): def ask_for_disk_layout(): options = { - 'keep-existing' : 'Keep existing partition layout and select which ones to use where.', - 'format-all' : 'Format entire drive and setup a basic partition scheme.', - 'abort' : 'Abort the installation.' + 'keep-existing' : 'Keep existing partition layout and select which ones to use where', + 'format-all' : 'Format entire drive and setup a basic partition scheme', + 'abort' : 'Abort the installation' } - value = generic_select(options.values(), "Found partitions on the selected drive, (select by number) what you want to do: ") + value = generic_select(options, "Found partitions on the selected drive, (select by number) what you want to do: ", False) return next((key for key, val in options.items() if val == value), None) def ask_for_main_filesystem_format(): @@ -206,7 +210,7 @@ def ask_for_main_filesystem_format(): 'f2fs' : 'f2fs' } - value = generic_select(options.values(), "Select which filesystem your main partition should use (by number or name): ") + value = generic_select(options, "Select which filesystem your main partition should use (by number or name): ", False) return next((key for key, val in options.items() if val == value), None) def generic_select(options, input_text="Select one of the above by index or absolute value: ", allow_empty_input=True, options_output=True): @@ -215,9 +219,12 @@ def generic_select(options, input_text="Select one of the above by index or abso other than the options and their indexes. As an example: generic_select(["first", "second", "third option"]) - 1: first - 2: second - 3: third option + 0: first + 1: second + 2: third option + + When the user has entered the option correctly, + this function returns an item from list, a string, or None """ # Checking if options are different from `list` or `dict` @@ -226,14 +233,14 @@ def generic_select(options, input_text="Select one of the above by index or abso log(" * Here are the link: https://github.com/archlinux/archinstall/issues * ", fg='yellow') raise RequirementError("generic_select() reqiures list or dictionary as options.") if type(options) == dict: options = sorted(list(options.values())) # To allow only `list` and `dict`, converting values of options and sorting them here. Therefore, now we can only provide the dictionary itself - # if sort: options = sorted(list(options)) # Moved sorting for dictionaries, as some lists are already sorted, when passed here if len(options) == 0: log(" * It looks like there are no options to choose from. Maybe it's time to open an issue on GitHub! * ", fg='red') log(" * Here are the link: https://github.com/archlinux/archinstall/issues * ", fg='yellow') raise RequirementError('generic_select() requires at least one option to operate.') - # Disable the output of options items, if another function displays something different from this + # Added ability to disable the output of options items, + # if another function displays something different from this if options_output: for index, option in enumerate(options): print(f"{index}: {option}") @@ -282,18 +289,10 @@ def select_disk(dict_o_disks): if len(drives) >= 1: for index, drive in enumerate(drives): print(f"{index}: {drive} ({dict_o_disks[drive]['size'], dict_o_disks[drive].device, dict_o_disks[drive]['label']})") - drive = input('Select one of the above disks (by number or full path) or write /mnt to skip partitioning: ') - if drive.strip() == '/mnt': - return None - elif drive.isdigit(): - drive = int(drive) - if drive >= len(drives): - raise DiskError(f'Selected option "{drive}" is out of range') - drive = dict_o_disks[drives[drive]] - elif drive in dict_o_disks: - drive = dict_o_disks[drive] - else: - raise DiskError(f'Selected drive does not exist: "{drive}"') + drive = generic_select(drives, 'Select one of the above disks (by number or full path) or leave blank to skip partitioning: ', True, False) + if not drive: + return drive + drive = dict_o_disks[drive] return drive raise DiskError('select_disk() requires a non-empty dictionary of disks to select from.') @@ -312,24 +311,10 @@ def select_profile(options): profiles = sorted(list(options)) if len(profiles) >= 1: - for index, profile in enumerate(profiles): - print(f"{index}: {profile}") - - print(' -- The above list is a set of pre-programmed profiles. --') + print(' -- The below list is a set of pre-programmed profiles. --') print(' -- They might make it easier to install things like desktop environments. --') - print(' -- (Leave blank and hit enter to skip this step and continue) --') - selected_profile = input('Enter a pre-programmed profile name if you want to install one: ') - - if len(selected_profile.strip()) <= 0: - return None - - if selected_profile.isdigit() and (pos := int(selected_profile)) <= len(profiles)-1: - selected_profile = profiles[pos] - elif selected_profile in options: - selected_profile = options[options.index(selected_profile)] - else: - RequirementError("Selected profile does not exist.") + selected_profile = generic_select(profiles, 'Enter a pre-programmed profile name if you want to install one or leave blank to skip this step: ') return Profile(None, selected_profile) raise RequirementError("Selecting profiles require a least one profile to be given as an option.") @@ -359,12 +344,17 @@ def select_language(options, show_only_country_codes=True): for index, language in enumerate(languages): print(f"{index}: {language}") - print(' -- You can enter ? or help to search for more languages, or skip to use US layout --') - selected_language = input('Select one of the above keyboard languages (by number or full name): ') + # Current workaround for passing `generic_select`, + # if these values are provided as input + languages.extend(['?', 'help']) + languages_length = len(languages) + + print(f' -- You can enter ? ({languages_length - 2}) or help ({languages_length - 1}) to search for more languages, or skip to use US layout --') + selected_language = generic_select(languages, 'Select one of the above keyboard languages (by number or full name): ', True, False) - if len(selected_language.strip()) == 0: + if not selected_language: return DEFAULT_KEYBOARD_LANGUAGE - elif selected_language.lower() in ('?', 'help'): + elif selected_language in ('?', 'help'): while True: filter_string = input('Search for layout containing (example: "sv-"): ') new_options = list(search_keyboard_layout(filter_string)) @@ -375,18 +365,13 @@ def select_language(options, show_only_country_codes=True): return select_language(new_options, show_only_country_codes=False) - elif selected_language.isdigit() and (pos := int(selected_language)) <= len(languages)-1: - selected_language = languages[pos] - return selected_language # I'm leaving "options" on purpose here. # Since languages possibly contains a filtered version of # all possible language layouts, and we might want to write # for instance sv-latin1 (if we know that exists) without having to # go through the search step. - elif selected_language in languages: - return selected_language - else: - raise RequirementError("Selected language does not exist.") + + return selected_language raise RequirementError("Selecting languages require a least one language to be given as an option.") @@ -413,23 +398,17 @@ def select_mirror_regions(mirrors, show_top_mirrors=True): print_large_list(regions, margin_bottom=4) print(' -- You can skip this step by leaving the option blank --') - selected_mirror = input('Select one of the above regions to download packages from (by number or full name): ') - if len(selected_mirror.strip()) == 0: + selected_mirror = generic_select(regions, 'Select one of the above regions to download packages from (by number or full name): ', True, False) + if not selected_mirror: # Returning back empty options which can be both used to # do "if x:" logic as well as do `x.get('mirror', {}).get('sub', None)` chaining return {} - elif selected_mirror.isdigit() and int(selected_mirror) <= len(regions)-1: - # I'm leaving "mirrors" on purpose here. - # Since region possibly contains a known region of - # all possible regions, and we might want to write - # for instance Sweden (if we know that exists) without having to - # go through the search step. - region = regions[int(selected_mirror)] - selected_mirrors[region] = mirrors[region] - elif selected_mirror in mirrors: - selected_mirrors[selected_mirror] = mirrors[selected_mirror] - else: - raise RequirementError("Selected region does not exist.") + # I'm leaving "mirrors" on purpose here. + # Since region possibly contains a known region of + # all possible regions, and we might want to write + # for instance Sweden (if we know that exists) without having to + # go through the search step. + selected_mirrors[selected_mirror] = mirrors[selected_mirror] return selected_mirrors diff --git a/examples/guided.py b/examples/guided.py index c0d22023..89868148 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -76,7 +76,9 @@ def ask_user_questions(): archinstall.log(f" ** The root would be a simple / and the boot partition /boot (as all paths are relative inside the installation). **") while True: # Select a partition - partition = archinstall.generic_select(partition_mountpoints.keys(), + # If we provide keys as options, it's better to convert them to list and sort before passing + mountpoints_list = sorted(list(partition_mountpoints.keys())) + partition = archinstall.generic_select(mountpoints_list, "Select a partition by number that you want to set a mount-point for (leave blank when done): ") if not partition: break @@ -106,7 +108,7 @@ def ask_user_questions(): # we have to check if we support it. We can do this by formatting /dev/null with the partitions filesystem. # There's a nice wrapper for this on the partition object itself that supports a path-override during .format() try: - partition.format(new_filesystem, path='/dev/null', log_formating=False, allow_formatting=True) + partition.format(new_filesystem, path='/dev/null', log_formatting=False, allow_formatting=True) except archinstall.UnknownFilesystemFormat: archinstall.log(f"Selected filesystem is not supported yet. If you want archinstall to support '{new_filesystem}', please create a issue-ticket suggesting it on github at https://github.com/archlinux/archinstall/issues.") archinstall.log(f"Until then, please enter another supported filesystem.") diff --git a/profiles/desktop.py b/profiles/desktop.py index 846182e2..b46bf6fd 100644 --- a/profiles/desktop.py +++ b/profiles/desktop.py @@ -17,7 +17,7 @@ def _prep_function(*args, **kwargs): """ supported_desktops = ['gnome', 'kde', 'awesome', 'sway', 'cinnamon', 'xfce4', 'lxqt', 'i3', 'budgie', 'mate'] - desktop = archinstall.generic_select(supported_desktops, 'Select your desired desktop environment: ') + desktop = archinstall.generic_select(supported_desktops, 'Select your desired desktop environment: ', False) # Temporarily store the selected desktop profile # in a session-safe location, since this module will get reloaded diff --git a/profiles/i3.py b/profiles/i3.py index 67028b2d..fc427186 100644 --- a/profiles/i3.py +++ b/profiles/i3.py @@ -17,7 +17,7 @@ def _prep_function(*args, **kwargs): """ supported_configurations = ['i3-wm', 'i3-gaps'] - desktop = archinstall.generic_select(supported_configurations, 'Select your desired configuration: ') + desktop = archinstall.generic_select(supported_configurations, 'Select your desired configuration: ', False) # Temporarily store the selected desktop profile # in a session-safe location, since this module will get reloaded diff --git a/profiles/xorg.py b/profiles/xorg.py index e905d533..8c6f686d 100644 --- a/profiles/xorg.py +++ b/profiles/xorg.py @@ -33,10 +33,7 @@ def select_driver(options): drivers = sorted(list(options)) if len(drivers) >= 1: - for index, driver in enumerate(drivers): - print(f"{index}: {driver}") - - print(' -- The above list are supported graphic card drivers. --') + print(' -- The below list are supported graphic card drivers. --') print(' -- You need to select (and read about) which one you need. --') lspci = archinstall.sys_command(f'/usr/bin/lspci') @@ -47,7 +44,7 @@ def select_driver(options): elif b'amd' in line.lower(): print(' ** AMD card detected, suggested driver: AMD / ATI **') - selected_driver = input('Select your graphics card driver: ') + selected_driver = archinstall.generic_select(drivers, 'Select your graphics card driver: ', False) initial_option = selected_driver # Disabled search for now, only a few profiles exist anyway @@ -57,29 +54,14 @@ def select_driver(options): # filter_string = input('Search for layout containing (example: "sv-"): ') # new_options = search_keyboard_layout(filter_string) # return select_language(new_options) - if selected_driver.isdigit() and (pos := int(selected_driver)) <= len(drivers)-1: - selected_driver = options[drivers[pos]] - elif selected_driver in options: - selected_driver = options[options.index(selected_driver)] - elif len(selected_driver) == 0: - raise archinstall.RequirementError("At least one graphics driver is needed to support a graphical environment. Please restart the installer and try again.") - else: - raise archinstall.RequirementError("Selected driver does not exist.") + + selected_driver = options[selected_driver] if type(selected_driver) == dict: driver_options = sorted(list(selected_driver)) - for index, driver_package_group in enumerate(driver_options): - print(f"{index}: {driver_package_group}") - - selected_driver_package_group = input(f'Which driver-type do you want for {initial_option}: ') - if selected_driver_package_group.isdigit() and (pos := int(selected_driver_package_group)) <= len(driver_options)-1: - selected_driver_package_group = selected_driver[driver_options[pos]] - elif selected_driver_package_group in selected_driver: - selected_driver_package_group = selected_driver[selected_driver.index(selected_driver_package_group)] - elif len(selected_driver_package_group) == 0: - raise archinstall.RequirementError(f"At least one driver package is required for a graphical environment using {selected_driver}. Please restart the installer and try again.") - else: - raise archinstall.RequirementError(f"Selected driver-type does not exist for {initial_option}.") + + selected_driver_package_group = archinstall.generic_select(driver_options, f'Which driver-type do you want for {initial_option}: ', False) + selected_driver_package_group = selected_driver[selected_driver_package_group] return selected_driver_package_group -- cgit v1.2.3-70-g09d2 From d2eacffff8e5ad560c55300477cdf28f475eb36c Mon Sep 17 00:00:00 2001 From: SecondThundeR Date: Mon, 19 Apr 2021 20:34:35 +0300 Subject: Update some functions Here are list of changes: - Added IP/subnet validation using Python's `ipaddress` module - Added workaround for network configuration modes where user can enter DHCP or IP without brackets. - Returned local printing options for some functions to keep `The above list...` - Moved booleans for `generic_select` below options and text parameters - Imported some functions from `archinstall` to reduce the`archinstall.` part of the lines. - Reduced variable name length for simplicity - Fixed some typos --- archinstall/lib/user_interaction.py | 67 ++++++++++++++++++++++++++++++------- profiles/desktop.py | 3 +- profiles/i3.py | 3 +- profiles/xorg.py | 22 +++++++----- 4 files changed, 72 insertions(+), 23 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 776650b5..b5c07f6a 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -1,5 +1,5 @@ import getpass, pathlib, os, shutil, re -import sys, time, signal +import sys, time, signal, ipaddress from .exceptions import * from .profiles import Profile from .locale_helpers import search_keyboard_layout @@ -164,13 +164,25 @@ def ask_to_configure_network(): if nic and nic != 'Copy ISO network configuration to installation': if nic == 'Use NetworkManager to control and manage your internet connection': return {'nic': nic,'NetworkManager':True} - mode = generic_select(['DHCP (auto detect)', 'IP (static)'], f"Select which mode to configure for {nic}: ") - if mode == 'IP (static)': + + # Current workaround: + # For selecting modes without entering text within brackets, + # printing out this part separate from options, passed in + # `generic_select` + modes = ['DHCP (auto detect)', 'IP (static)'] + for index, mode in enumerate(modes): + print(f"{index}: {mode}") + + mode = generic_select(['DHCP', 'IP'], f"Select which mode to configure for {nic} or leave blank for DHCP: ", + options_output=False) + if mode == 'IP': while 1: ip = input(f"Enter the IP and subnet for {nic} (example: 192.168.0.5/24): ").strip() - if ip: + # Implemented new check for correct IP/subnet input + try: + ipaddress.ip_interface(ip) break - else: + except ValueError: log( "You need to enter a valid IP in IP-config mode.", level=LOG_LEVELS.Warning, @@ -180,6 +192,25 @@ def ask_to_configure_network(): if not len(gateway := input('Enter your gateway (router) IP address or leave blank for none: ').strip()): gateway = None + # Assuming that gateway (router) IP address doesn't contain subnet, + # we can implement this check for it + # Implemented new check for correct IP input + + #gateway = input('Enter your gateway (router) IP address or leave blank for none: ').strip() + #while 1: + # try: + # if len(gateway) == 0: + # gateway = None + # else: + # ipaddress.ip_address(gateway) + # break + # except ValueError: + # log( + # "You need to enter a valid gateway (router) IP address.", + # level=LOG_LEVELS.Warning, + # fg='red' + # ) + dns = None if len(dns_input := input('Enter your DNS servers (space separated, blank for none): ').strip()): dns = dns_input.split(' ') @@ -199,7 +230,8 @@ def ask_for_disk_layout(): 'abort' : 'Abort the installation' } - value = generic_select(options, "Found partitions on the selected drive, (select by number) what you want to do: ", False) + value = generic_select(options, "Found partitions on the selected drive, (select by number) what you want to do: ", + allow_empty_input=False) return next((key for key, val in options.items() if val == value), None) def ask_for_main_filesystem_format(): @@ -210,7 +242,8 @@ def ask_for_main_filesystem_format(): 'f2fs' : 'f2fs' } - value = generic_select(options, "Select which filesystem your main partition should use (by number or name): ", False) + value = generic_select(options, "Select which filesystem your main partition should use (by number or name): ", + allow_empty_input=False) return next((key for key, val in options.items() if val == value), None) def generic_select(options, input_text="Select one of the above by index or absolute value: ", allow_empty_input=True, options_output=True): @@ -231,7 +264,7 @@ def generic_select(options, input_text="Select one of the above by index or abso if type(options) not in [list, dict]: log(" * It looks like there are something wrong with provided options. Maybe it's time to open an issue on GitHub! * ", fg='red') log(" * Here are the link: https://github.com/archlinux/archinstall/issues * ", fg='yellow') - raise RequirementError("generic_select() reqiures list or dictionary as options.") + raise RequirementError("generic_select() requires list or dictionary as options.") if type(options) == dict: options = sorted(list(options.values())) # To allow only `list` and `dict`, converting values of options and sorting them here. Therefore, now we can only provide the dictionary itself if len(options) == 0: log(" * It looks like there are no options to choose from. Maybe it's time to open an issue on GitHub! * ", fg='red') @@ -289,7 +322,8 @@ def select_disk(dict_o_disks): if len(drives) >= 1: for index, drive in enumerate(drives): print(f"{index}: {drive} ({dict_o_disks[drive]['size'], dict_o_disks[drive].device, dict_o_disks[drive]['label']})") - drive = generic_select(drives, 'Select one of the above disks (by number or full path) or leave blank to skip partitioning: ', True, False) + drive = generic_select(drives, 'Select one of the above disks (by number or full path) or leave blank to skip partitioning: ', + options_output=False) if not drive: return drive drive = dict_o_disks[drive] @@ -311,10 +345,15 @@ def select_profile(options): profiles = sorted(list(options)) if len(profiles) >= 1: - print(' -- The below list is a set of pre-programmed profiles. --') + for index, profile in enumerate(profiles): + print(f"{index}: {profile}") + + print(' -- The above list is a set of pre-programmed profiles. --') print(' -- They might make it easier to install things like desktop environments. --') + print(' -- (Leave blank and hit enter to skip this step and continue) --') - selected_profile = generic_select(profiles, 'Enter a pre-programmed profile name if you want to install one or leave blank to skip this step: ') + selected_profile = generic_select(profiles, 'Enter a pre-programmed profile name if you want to install one: ', + options_output=False) return Profile(None, selected_profile) raise RequirementError("Selecting profiles require a least one profile to be given as an option.") @@ -350,7 +389,8 @@ def select_language(options, show_only_country_codes=True): languages_length = len(languages) print(f' -- You can enter ? ({languages_length - 2}) or help ({languages_length - 1}) to search for more languages, or skip to use US layout --') - selected_language = generic_select(languages, 'Select one of the above keyboard languages (by number or full name): ', True, False) + selected_language = generic_select(languages, 'Select one of the above keyboard languages (by number or full name): ', + options_output=False) if not selected_language: return DEFAULT_KEYBOARD_LANGUAGE @@ -398,7 +438,8 @@ def select_mirror_regions(mirrors, show_top_mirrors=True): print_large_list(regions, margin_bottom=4) print(' -- You can skip this step by leaving the option blank --') - selected_mirror = generic_select(regions, 'Select one of the above regions to download packages from (by number or full name): ', True, False) + selected_mirror = generic_select(regions, 'Select one of the above regions to download packages from (by number or full name): ', + options_output=False) if not selected_mirror: # Returning back empty options which can be both used to # do "if x:" logic as well as do `x.get('mirror', {}).get('sub', None)` chaining diff --git a/profiles/desktop.py b/profiles/desktop.py index b46bf6fd..d350cd71 100644 --- a/profiles/desktop.py +++ b/profiles/desktop.py @@ -17,7 +17,8 @@ def _prep_function(*args, **kwargs): """ supported_desktops = ['gnome', 'kde', 'awesome', 'sway', 'cinnamon', 'xfce4', 'lxqt', 'i3', 'budgie', 'mate'] - desktop = archinstall.generic_select(supported_desktops, 'Select your desired desktop environment: ', False) + desktop = archinstall.generic_select(supported_desktops, 'Select your desired desktop environment: ', + allow_empty_input=False) # Temporarily store the selected desktop profile # in a session-safe location, since this module will get reloaded diff --git a/profiles/i3.py b/profiles/i3.py index fc427186..923b2f2b 100644 --- a/profiles/i3.py +++ b/profiles/i3.py @@ -17,7 +17,8 @@ def _prep_function(*args, **kwargs): """ supported_configurations = ['i3-wm', 'i3-gaps'] - desktop = archinstall.generic_select(supported_configurations, 'Select your desired configuration: ', False) + desktop = archinstall.generic_select(supported_configurations, 'Select your desired configuration: ', + allow_empty_input=False) # Temporarily store the selected desktop profile # in a session-safe location, since this module will get reloaded diff --git a/profiles/xorg.py b/profiles/xorg.py index 8c6f686d..130f3ed0 100644 --- a/profiles/xorg.py +++ b/profiles/xorg.py @@ -1,6 +1,7 @@ # A system with "xorg" installed -import archinstall, os +import os +from archinstall import generic_select, sys_command, RequirementError is_top_level_profile = True @@ -33,10 +34,13 @@ def select_driver(options): drivers = sorted(list(options)) if len(drivers) >= 1: - print(' -- The below list are supported graphic card drivers. --') + for index, driver in enumerate(drivers): + print(f"{index}: {driver}") + + print(' -- The above list are supported graphic card drivers. --') print(' -- You need to select (and read about) which one you need. --') - lspci = archinstall.sys_command(f'/usr/bin/lspci') + lspci = sys_command(f'/usr/bin/lspci') for line in lspci.trace_log.split(b'\r\n'): if b' vga ' in line.lower(): if b'nvidia' in line.lower(): @@ -44,7 +48,8 @@ def select_driver(options): elif b'amd' in line.lower(): print(' ** AMD card detected, suggested driver: AMD / ATI **') - selected_driver = archinstall.generic_select(drivers, 'Select your graphics card driver: ', False) + selected_driver = generic_select(drivers, 'Select your graphics card driver: ', + allow_empty_input=False, options_output=False) initial_option = selected_driver # Disabled search for now, only a few profiles exist anyway @@ -60,14 +65,15 @@ def select_driver(options): if type(selected_driver) == dict: driver_options = sorted(list(selected_driver)) - selected_driver_package_group = archinstall.generic_select(driver_options, f'Which driver-type do you want for {initial_option}: ', False) - selected_driver_package_group = selected_driver[selected_driver_package_group] + driver_package_group = generic_select(driver_options, f'Which driver-type do you want for {initial_option}: ', + allow_empty_input=False) + driver_package_group = selected_driver[driver_package_group] - return selected_driver_package_group + return driver_package_group return selected_driver - raise archinstall.RequirementError("Selecting drivers require a least one profile to be given as an option.") + raise RequirementError("Selecting drivers require a least one profile to be given as an option.") def _prep_function(*args, **kwargs): """ -- cgit v1.2.3-70-g09d2 From 1d04c9225873e21e92bc09290653450d161e067d Mon Sep 17 00:00:00 2001 From: SecondThundeR Date: Tue, 20 Apr 2021 14:45:54 +0300 Subject: Add sort parameter for generic_select Updated required features to support these change --- archinstall/lib/user_interaction.py | 9 ++++++--- profiles/desktop.py | 2 +- profiles/i3.py | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index b5c07f6a..f2eae530 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -231,7 +231,7 @@ def ask_for_disk_layout(): } value = generic_select(options, "Found partitions on the selected drive, (select by number) what you want to do: ", - allow_empty_input=False) + allow_empty_input=False, sort=True) return next((key for key, val in options.items() if val == value), None) def ask_for_main_filesystem_format(): @@ -246,7 +246,7 @@ def ask_for_main_filesystem_format(): allow_empty_input=False) return next((key for key, val in options.items() if val == value), None) -def generic_select(options, input_text="Select one of the above by index or absolute value: ", allow_empty_input=True, options_output=True): +def generic_select(options, input_text="Select one of the above by index or absolute value: ", allow_empty_input=True, options_output=True, sort=False): """ A generic select function that does not output anything other than the options and their indexes. As an example: @@ -265,7 +265,10 @@ def generic_select(options, input_text="Select one of the above by index or abso log(" * It looks like there are something wrong with provided options. Maybe it's time to open an issue on GitHub! * ", fg='red') log(" * Here are the link: https://github.com/archlinux/archinstall/issues * ", fg='yellow') raise RequirementError("generic_select() requires list or dictionary as options.") - if type(options) == dict: options = sorted(list(options.values())) # To allow only `list` and `dict`, converting values of options and sorting them here. Therefore, now we can only provide the dictionary itself + # To allow only `list` and `dict`, converting values of options here. + # Therefore, now we can only provide the dictionary itself + if type(options) == dict: options = list(options.values()) + if sort: options = sorted(options) # As we pass only list and dict (converted to list), we can skip converting to list if len(options) == 0: log(" * It looks like there are no options to choose from. Maybe it's time to open an issue on GitHub! * ", fg='red') log(" * Here are the link: https://github.com/archlinux/archinstall/issues * ", fg='yellow') diff --git a/profiles/desktop.py b/profiles/desktop.py index d350cd71..2aea6d30 100644 --- a/profiles/desktop.py +++ b/profiles/desktop.py @@ -18,7 +18,7 @@ def _prep_function(*args, **kwargs): supported_desktops = ['gnome', 'kde', 'awesome', 'sway', 'cinnamon', 'xfce4', 'lxqt', 'i3', 'budgie', 'mate'] desktop = archinstall.generic_select(supported_desktops, 'Select your desired desktop environment: ', - allow_empty_input=False) + allow_empty_input=False, sort=True) # Temporarily store the selected desktop profile # in a session-safe location, since this module will get reloaded diff --git a/profiles/i3.py b/profiles/i3.py index 923b2f2b..b82c03d6 100644 --- a/profiles/i3.py +++ b/profiles/i3.py @@ -18,7 +18,7 @@ def _prep_function(*args, **kwargs): supported_configurations = ['i3-wm', 'i3-gaps'] desktop = archinstall.generic_select(supported_configurations, 'Select your desired configuration: ', - allow_empty_input=False) + allow_empty_input=False, sort=True) # Temporarily store the selected desktop profile # in a session-safe location, since this module will get reloaded -- cgit v1.2.3-70-g09d2 From 1d37e5a49e094e0117ab52813bcaa374809bdc4b Mon Sep 17 00:00:00 2001 From: SecondThundeR Date: Tue, 20 Apr 2021 15:02:11 +0300 Subject: Uncomment new check for gateway IP address --- archinstall/lib/user_interaction.py | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index f2eae530..fcdeace1 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -189,27 +189,21 @@ def ask_to_configure_network(): fg='red' ) - if not len(gateway := input('Enter your gateway (router) IP address or leave blank for none: ').strip()): - gateway = None - - # Assuming that gateway (router) IP address doesn't contain subnet, - # we can implement this check for it - # Implemented new check for correct IP input - - #gateway = input('Enter your gateway (router) IP address or leave blank for none: ').strip() - #while 1: - # try: - # if len(gateway) == 0: - # gateway = None - # else: - # ipaddress.ip_address(gateway) - # break - # except ValueError: - # log( - # "You need to enter a valid gateway (router) IP address.", - # level=LOG_LEVELS.Warning, - # fg='red' - # ) + gateway = input('Enter your gateway (router) IP address or leave blank for none: ').strip() + # Implemented new check for correct gateway IP address + while 1: + try: + if len(gateway) == 0: + gateway = None + else: + ipaddress.ip_address(gateway) + break + except ValueError: + log( + "You need to enter a valid gateway (router) IP address.", + level=LOG_LEVELS.Warning, + fg='red' + ) dns = None if len(dns_input := input('Enter your DNS servers (space separated, blank for none): ').strip()): -- cgit v1.2.3-70-g09d2 From 06e846796d7fd18a0832b14291ef3fcf429dd214 Mon Sep 17 00:00:00 2001 From: SecondThundeR Date: Tue, 20 Apr 2021 15:07:52 +0300 Subject: Fix infinite loop on incorrect input of gateway IP --- archinstall/lib/user_interaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index fcdeace1..86d6e514 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -189,9 +189,9 @@ def ask_to_configure_network(): fg='red' ) - gateway = input('Enter your gateway (router) IP address or leave blank for none: ').strip() # Implemented new check for correct gateway IP address while 1: + gateway = input('Enter your gateway (router) IP address or leave blank for none: ').strip() try: if len(gateway) == 0: gateway = None -- cgit v1.2.3-70-g09d2 From 3facc2e58d6b430e47d0eba42dc6d11879c36017 Mon Sep 17 00:00:00 2001 From: SecondThundeR Date: Tue, 20 Apr 2021 15:25:46 +0300 Subject: Fix TabError issue after uncommenting --- archinstall/lib/user_interaction.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 86d6e514..546ee3ee 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -195,15 +195,15 @@ def ask_to_configure_network(): try: if len(gateway) == 0: gateway = None - else: - ipaddress.ip_address(gateway) - break - except ValueError: - log( - "You need to enter a valid gateway (router) IP address.", - level=LOG_LEVELS.Warning, - fg='red' - ) + else: + ipaddress.ip_address(gateway) + break + except ValueError: + log( + "You need to enter a valid gateway (router) IP address.", + level=LOG_LEVELS.Warning, + fg='red' + ) dns = None if len(dns_input := input('Enter your DNS servers (space separated, blank for none): ').strip()): -- cgit v1.2.3-70-g09d2 From 7afba65c261057808fae8e3cb22ddd89fed31f2b Mon Sep 17 00:00:00 2001 From: advaithm Date: Tue, 20 Apr 2021 18:43:19 +0530 Subject: rebase --- archinstall/lib/user_interaction.py | 14 ++++++++++++-- examples/guided.py | 7 +++++-- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 822f63be..9d09d8f1 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -7,7 +7,7 @@ from .output import log, LOG_LEVELS from .storage import storage from .networking import list_interfaces from .general import sys_command -from .hardware import AVAILABLE_GFX_DRIVERS +from .hardware import AVAILABLE_GFX_DRIVERS, hasUEFI ## TODO: Some inconsistencies between the selection processes. ## Some return the keys from the options, some the values? @@ -143,7 +143,17 @@ def ask_for_a_timezone(): level=LOG_LEVELS.Warning, fg='red' ) - + +def ask_for_bootloader() -> str: + bootloader = "systemd-bootctl" + if hasUEFI==False: + bootloader="grub-install" + else: + bootloader_choice = input("Would you like to use Grub as a bootloader over systemd-boot [y/N] ").lower() + if bootloader_choice == "y": + bootloader="grub-install" + return bootloader + def ask_for_audio_selection(): audio = "pulseaudio" # Default for most desktop environments pipewire_choice = input("Would you like to install pipewire instead of pulseaudio as the default audio server? [Y/n] ").lower() diff --git a/examples/guided.py b/examples/guided.py index c86f2b4b..643e2c94 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -64,6 +64,8 @@ def ask_user_questions(): partition_mountpoints[partition] = None except archinstall.UnknownFilesystemFormat as err: archinstall.log(f" {partition} (Filesystem not supported)", fg='red') + + archinstall.arguments["bootloader"] = archinstall.ask_for_bootloader() # We then ask what to do with the partitions. if (option := archinstall.ask_for_disk_layout()) == 'abort': @@ -190,7 +192,7 @@ def ask_user_questions(): # Additional packages (with some light weight error handling for invalid package names) while True: if not archinstall.arguments.get('packages', None): - print("Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.") + print("Only packages such as base, base-devel, linux, linux-firmware, efibootmgr(in uefi systems) 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.") archinstall.arguments['packages'] = [package for package in input('Write additional packages to install (space separated, leave blank to skip): ').split(' ') if len(package)] @@ -303,7 +305,8 @@ def perform_installation(mountpoint): # 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 hasUEFI()==False: + installation.base_packages.replace("efibootmgr","")# if we aren't on a uefi system why install efibootmgr if installation.minimal_installation(): installation.set_hostname(archinstall.arguments['hostname']) if archinstall.arguments['mirror-region'].get("mirrors",{})!= None: -- cgit v1.2.3-70-g09d2 From 8d9a542962f8c2f2d0a31c0035b4bf63ef1583cb Mon Sep 17 00:00:00 2001 From: SecondThundeR Date: Tue, 20 Apr 2021 17:10:28 +0300 Subject: Add clarifying log text for generic_select --- archinstall/lib/user_interaction.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 546ee3ee..1cfe5490 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -256,17 +256,17 @@ def generic_select(options, input_text="Select one of the above by index or abso # Checking if options are different from `list` or `dict` if type(options) not in [list, dict]: - log(" * It looks like there are something wrong with provided options. Maybe it's time to open an issue on GitHub! * ", fg='red') - log(" * Here are the link: https://github.com/archlinux/archinstall/issues * ", fg='yellow') + log(f" * Generic select doesn't support ({type(options)}) as type of options * ", fg='red') + log(" * If problem persists, please create an issue on https://github.com/archlinux/archinstall/issues * ", fg='yellow') raise RequirementError("generic_select() requires list or dictionary as options.") # To allow only `list` and `dict`, converting values of options here. # Therefore, now we can only provide the dictionary itself if type(options) == dict: options = list(options.values()) if sort: options = sorted(options) # As we pass only list and dict (converted to list), we can skip converting to list if len(options) == 0: - log(" * It looks like there are no options to choose from. Maybe it's time to open an issue on GitHub! * ", fg='red') - log(" * Here are the link: https://github.com/archlinux/archinstall/issues * ", fg='yellow') - raise RequirementError('generic_select() requires at least one option to operate.') + log(f" * Generic select didn't find any options to choose from * ", fg='red') + log(" * If problem persists, please create an issue on https://github.com/archlinux/archinstall/issues * ", fg='yellow') + raise RequirementError('generic_select() requires at least one option to proceed.') # Added ability to disable the output of options items, -- cgit v1.2.3-70-g09d2 From 264db25eefd09a99ecdfa5f095b49343fc143d82 Mon Sep 17 00:00:00 2001 From: advaithm Date: Tue, 20 Apr 2021 19:50:12 +0530 Subject: call hasUEFI --- archinstall/lib/user_interaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 9d09d8f1..818590dd 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -146,7 +146,7 @@ def ask_for_a_timezone(): def ask_for_bootloader() -> str: bootloader = "systemd-bootctl" - if hasUEFI==False: + if hasUEFI()==False: bootloader="grub-install" else: bootloader_choice = input("Would you like to use Grub as a bootloader over systemd-boot [y/N] ").lower() -- cgit v1.2.3-70-g09d2 From 8b723c25e2c487c508d2718b06381ca6c2b7d2a0 Mon Sep 17 00:00:00 2001 From: advaithm Date: Tue, 20 Apr 2021 20:36:26 +0530 Subject: changed some strings --- archinstall/lib/user_interaction.py | 2 +- examples/guided.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 818590dd..175079ee 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -149,7 +149,7 @@ def ask_for_bootloader() -> str: if hasUEFI()==False: bootloader="grub-install" else: - bootloader_choice = input("Would you like to use Grub as a bootloader over systemd-boot [y/N] ").lower() + bootloader_choice = input("Would you like to use GRUB as a bootloader instead off systemd-boot [y/N] ").lower() if bootloader_choice == "y": bootloader="grub-install" return bootloader diff --git a/examples/guided.py b/examples/guided.py index 8308d22c..3d357e2d 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -191,7 +191,7 @@ def ask_user_questions(): # Additional packages (with some light weight error handling for invalid package names) while True: if not archinstall.arguments.get('packages', None): - print("Only packages such as base, base-devel, linux, linux-firmware, efibootmgr(on uefi systems)/grub(on bios systems) and optional profile packages are installed.") + print("Only packages such as base, base-devel, linux, linux-firmware, efibootmgr (on UEFI systems)/GRUB (on BIOS systems) 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.") archinstall.arguments['packages'] = [package for package in input('Write additional packages to install (space separated, leave blank to skip): ').split(' ') if len(package)] -- cgit v1.2.3-70-g09d2 From 63a94a57708bdfb4cdfde0c24af2d324ba191beb Mon Sep 17 00:00:00 2001 From: advaithm Date: Tue, 20 Apr 2021 22:09:27 +0530 Subject: add efibootmgr only on uefi systems --- archinstall/lib/installer.py | 8 +++++--- archinstall/lib/user_interaction.py | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 10a9ad4b..f6c891a3 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -39,7 +39,7 @@ class Installer(): :type hostname: str, optional """ - def __init__(self, target, *, base_packages='base base-devel linux linux-firmware efibootmgr'): + def __init__(self, target, *, base_packages='base base-devel linux linux-firmware'): self.target = target self.init_time = time.strftime('%Y-%m-%d_%H-%M-%S') self.milliseconds = int(str(time.time()).split('.')[1]) @@ -50,8 +50,10 @@ class Installer(): } self.base_packages = base_packages.split(' ') if type(base_packages) is str else base_packages - if hasUEFI() == False: - self.base_packages.pop(self.base_packages.index("efibootmgr")) + if hasUEFI(): + self.base_packages.append("efibootmgr") + else: + self.base_packages.append("grub") self.post_base_install = [] storage['session'] = self diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 175079ee..70ff7a1e 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -149,7 +149,7 @@ def ask_for_bootloader() -> str: if hasUEFI()==False: bootloader="grub-install" else: - bootloader_choice = input("Would you like to use GRUB as a bootloader instead off systemd-boot [y/N] ").lower() + bootloader_choice = input("Would you like to use GRUB as a bootloader instead of systemd-boot? [y/N] ").lower() if bootloader_choice == "y": bootloader="grub-install" return bootloader -- cgit v1.2.3-70-g09d2 From a834bffeec337098a696832b2deef63344bb176c Mon Sep 17 00:00:00 2001 From: jtagcat Date: Wed, 21 Apr 2021 14:20:22 +0000 Subject: user_interaction: Explicitly ask for username on super-user creation. --- archinstall/lib/user_interaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 10df6755..dfc3553b 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -93,7 +93,7 @@ def print_large_list(options, padding=5, margin_bottom=0, separator=': '): print(f"{str(column): >{highest_index_number_length}}{separator}{options[column]}", end = spaces) print() -def ask_for_superuser_account(prompt='Create a required super-user with sudo privileges: ', forced=False): +def ask_for_superuser_account(prompt='Username for required super-user with sudo privileges: ', forced=False): while 1: new_user = input(prompt).strip(' ') -- cgit v1.2.3-70-g09d2 From 4d65639724e1402ef584221bbbb0e80dba827ab7 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Thu, 22 Apr 2021 10:31:32 +0200 Subject: Fixes the crash on empty profile choice. Since generic_select() returns None, we can't pipe that into Profile() (at least not yet) --- archinstall/lib/user_interaction.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 1cfe5490..e0761e07 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -351,9 +351,10 @@ def select_profile(options): selected_profile = generic_select(profiles, 'Enter a pre-programmed profile name if you want to install one: ', options_output=False) - return Profile(None, selected_profile) - - raise RequirementError("Selecting profiles require a least one profile to be given as an option.") + if selected_profile: + return Profile(None, selected_profile) + else: + raise RequirementError("Selecting profiles require a least one profile to be given as an option.") def select_language(options, show_only_country_codes=True): """ -- cgit v1.2.3-70-g09d2 From 4209074137e5c7ee820fe3811ada3a53afdf7c93 Mon Sep 17 00:00:00 2001 From: Владислав Date: Thu, 22 Apr 2021 12:05:24 +0300 Subject: Update language selection Reverted generic_select changes and added ability to choose any layout at first input --- archinstall/lib/user_interaction.py | 57 ++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 33 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 484205fb..fc62f44c 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -360,13 +360,10 @@ def select_language(options, show_only_country_codes=True): """ Asks the user to select a language from the `options` dictionary parameter. Usually this is combined with :ref:`archinstall.list_keyboard_languages`. - :param options: A `dict` where keys are the language name, value should be a dict containing language information. :type options: dict - :param show_only_country_codes: Filters out languages that are not len(lang) == 2. This to limit the number of results from stuff like dvorak and x-latin1 alternatives. :type show_only_country_codes: bool - :return: The language/dictionary key of the selected language :rtype: str """ @@ -381,37 +378,31 @@ def select_language(options, show_only_country_codes=True): for index, language in enumerate(languages): print(f"{index}: {language}") - # Current workaround for passing `generic_select`, - # if these values are provided as input - languages.extend(['?', 'help']) - languages_length = len(languages) - - print(f' -- You can enter ? ({languages_length - 2}) or help ({languages_length - 1}) to search for more languages, or skip to use US layout --') - selected_language = generic_select(languages, 'Select one of the above keyboard languages (by number or full name): ', - options_output=False) - - if not selected_language: - return DEFAULT_KEYBOARD_LANGUAGE - elif selected_language in ('?', 'help'): - while True: - filter_string = input('Search for layout containing (example: "sv-"): ') - new_options = list(search_keyboard_layout(filter_string)) - - if len(new_options) <= 0: - log(f"Search string '{filter_string}' yielded no results, please try another search or Ctrl+D to abort.", fg='yellow') + print(" -- You can choose a layout that isn't in this list, but whose name you know --") + print(" -- Also, you can enter '?' or 'help' to search for more languages, or skip to use US layout --") + + while True: + selected_language = input('Select one of the above keyboard languages (by name or full name): ') + if not selected_language: + return DEFAULT_KEYBOARD_LANGUAGE + elif selected_language.lower() in ('?', 'help'): + while True: + filter_string = input('Search for layout containing (example: "sv-"): ') + new_options = list(search_keyboard_layout(filter_string)) + if len(new_options) <= 0: + log(f"Search string '{filter_string}' yielded no results, please try another search or Ctrl+D to abort.", fg='yellow') + continue + return select_language(new_options, show_only_country_codes=False) + elif selected_language.isnumeric(): + selected_language = int(selected_language) + if selected_language >= len(languages): + log(' * Selected option is out of range * ', fg='red') continue - - return select_language(new_options, show_only_country_codes=False) - - # I'm leaving "options" on purpose here. - # Since languages possibly contains a filtered version of - # all possible language layouts, and we might want to write - # for instance sv-latin1 (if we know that exists) without having to - # go through the search step. - - return selected_language - - raise RequirementError("Selecting languages require a least one language to be given as an option.") + return languages[selected_language] + elif search_keyboard_layout(selected_language): + return selected_language + else: + log(" * Given language wasn't found * ", fg='red') def select_mirror_regions(mirrors, show_top_mirrors=True): """ -- cgit v1.2.3-70-g09d2 From eb1ff72f5ba627e09da820b1ce0970d361653eca Mon Sep 17 00:00:00 2001 From: advaithm Date: Thu, 22 Apr 2021 17:00:51 +0530 Subject: readded some commits that got removed --- archinstall/lib/user_interaction.py | 54 ++++++++++++++++++++++++++++ profiles/sway.py | 6 ++++ profiles/xorg.py | 70 ------------------------------------- 3 files changed, 60 insertions(+), 70 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index b0d8e138..d8d2df18 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -7,7 +7,11 @@ from .output import log, LOG_LEVELS from .storage import storage from .networking import list_interfaces from .general import sys_command +<<<<<<< HEAD from .hardware import AVAILABLE_GFX_DRIVERS, hasUEFI +======= +from .hardware import AVAILABLE_GFX_DRIVERS +>>>>>>> 1292c07... Fixed PR #273. Moved the graphic drivers into hardware since they are hardware specific, in the long run maybe we move them into 'drivers' or something. And moved the user interaction from gfx_drivers into user_interactions. And removed the import from installer.py to __init__.py since we don't want to import 'global functions' in extension imports. ## TODO: Some inconsistencies between the selection processes. ## Some return the keys from the options, some the values? @@ -465,7 +469,19 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS): (The template xorg is for beginner users, not advanced, and should there for appeal to the general public first and edge cases later) """ +<<<<<<< HEAD if len(options) >= 1: +======= + drivers = sorted(list(options)) + + if len(drivers) >= 1: + for index, driver in enumerate(drivers): + print(f"{index}: {driver}") + + print(' -- The above list are supported graphic card drivers. --') + print(' -- You need to select (and read about) which one you need. --') + +>>>>>>> 1292c07... Fixed PR #273. Moved the graphic drivers into hardware since they are hardware specific, in the long run maybe we move them into 'drivers' or something. And moved the user interaction from gfx_drivers into user_interactions. And removed the import from installer.py to __init__.py since we don't want to import 'global functions' in extension imports. lspci = sys_command(f'/usr/bin/lspci') for line in lspci.trace_log.split(b'\r\n'): if b' vga ' in line.lower(): @@ -474,6 +490,7 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS): elif b'amd' in line.lower(): print(' ** AMD card detected, suggested driver: AMD / ATI **') +<<<<<<< HEAD selected_driver = generic_select(options, input_text="Select your graphics card driver: ", sort=True) initial_option = selected_driver @@ -484,6 +501,39 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS): if selected_driver_package_group in options[initial_option].keys(): print(options[initial_option][selected_driver_package_group]) selected_driver = options[initial_option][selected_driver_package_group] +======= + selected_driver = input('Select your graphics card driver: ') + initial_option = selected_driver + + # Disabled search for now, only a few profiles exist anyway + # + #print(' -- You can enter ? or help to search for more drivers --') + #if selected_driver.lower() in ('?', 'help'): + # filter_string = input('Search for layout containing (example: "sv-"): ') + # new_options = search_keyboard_layout(filter_string) + # return select_language(new_options) + if selected_driver.isdigit() and (pos := int(selected_driver)) <= len(drivers)-1: + selected_driver = options[drivers[pos]] + elif selected_driver in options: + selected_driver = options[options.index(selected_driver)] + elif len(selected_driver) == 0: + raise RequirementError("At least one graphics driver is needed to support a graphical environment. Please restart the installer and try again.") + else: + raise RequirementError("Selected driver does not exist.") + + if type(selected_driver) == dict: + driver_options = sorted(list(selected_driver)) + for index, driver_package_group in enumerate(driver_options): + print(f"{index}: {driver_package_group}") + + selected_driver_package_group = input(f'Which driver-type do you want for {initial_option}: ') + if selected_driver_package_group.isdigit() and (pos := int(selected_driver_package_group)) <= len(driver_options)-1: + selected_driver_package_group = selected_driver[driver_options[pos]] + elif selected_driver_package_group in selected_driver: + selected_driver_package_group = selected_driver[selected_driver.index(selected_driver_package_group)] + elif len(selected_driver_package_group) == 0: + raise RequirementError(f"At least one driver package is required for a graphical environment using {selected_driver}. Please restart the installer and try again.") +>>>>>>> 1292c07... Fixed PR #273. Moved the graphic drivers into hardware since they are hardware specific, in the long run maybe we move them into 'drivers' or something. And moved the user interaction from gfx_drivers into user_interactions. And removed the import from installer.py to __init__.py since we don't want to import 'global functions' in extension imports. else: raise RequirementError(f"Selected driver-type does not exist for {initial_option}.") @@ -491,4 +541,8 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS): return selected_driver +<<<<<<< HEAD + raise RequirementError("Selecting drivers require a least one profile to be given as an option.") +======= raise RequirementError("Selecting drivers require a least one profile to be given as an option.") +>>>>>>> 1292c07... Fixed PR #273. Moved the graphic drivers into hardware since they are hardware specific, in the long run maybe we move them into 'drivers' or something. And moved the user interaction from gfx_drivers into user_interactions. And removed the import from installer.py to __init__.py since we don't want to import 'global functions' in extension imports. diff --git a/profiles/sway.py b/profiles/sway.py index 5633cce2..7fb64fd7 100644 --- a/profiles/sway.py +++ b/profiles/sway.py @@ -11,6 +11,12 @@ def _prep_function(*args, **kwargs): other code in this stage. So it's a safe way to ask the user for more input before any other installer steps start. """ +<<<<<<< HEAD +======= + + __builtins__['_gfx_driver_packages'] = archinstall.select_driver() + +>>>>>>> 1292c07... Fixed PR #273. Moved the graphic drivers into hardware since they are hardware specific, in the long run maybe we move them into 'drivers' or something. And moved the user interaction from gfx_drivers into user_interactions. And removed the import from installer.py to __init__.py since we don't want to import 'global functions' in extension imports. return True # Ensures that this code only gets executed if executed diff --git a/profiles/xorg.py b/profiles/xorg.py index 8e9779bc..4d54fe79 100644 --- a/profiles/xorg.py +++ b/profiles/xorg.py @@ -5,76 +5,6 @@ from archinstall import generic_select, sys_command, RequirementError is_top_level_profile = True -AVAILABLE_DRIVERS = { - # Sub-dicts are layer-2 options to be selected - # and lists are a list of packages to be installed - 'AMD / ATI' : { - 'amd' : ['xf86-video-amdgpu'], - 'ati' : ['xf86-video-ati'] - }, - 'intel' : ['xf86-video-intel'], - 'nvidia' : { - 'open source' : ['xf86-video-nouveau'], - 'proprietary' : ['nvidia'] - }, - 'mesa' : ['mesa'], - 'fbdev' : ['xf86-video-fbdev'], - 'vesa' : ['xf86-video-vesa'], - 'vmware' : ['xf86-video-vmware'] -} - -def select_driver(options): - """ - Some what convoluted function, which's 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) - """ - drivers = sorted(list(options)) - - if len(drivers) >= 1: - for index, driver in enumerate(drivers): - print(f"{index}: {driver}") - - print(' -- The above list are supported graphic card drivers. --') - print(' -- You need to select (and read about) which one you need. --') - - lspci = sys_command(f'/usr/bin/lspci') - for line in lspci.trace_log.split(b'\r\n'): - if b' vga ' in line.lower(): - if b'nvidia' in line.lower(): - print(' ** nvidia card detected, suggested driver: nvidia **') - elif b'amd' in line.lower(): - print(' ** AMD card detected, suggested driver: AMD / ATI **') - - selected_driver = generic_select(drivers, 'Select your graphics card driver: ', - allow_empty_input=False, options_output=False) - initial_option = selected_driver - - # Disabled search for now, only a few profiles exist anyway - # - #print(' -- You can enter ? or help to search for more drivers --') - #if selected_driver.lower() in ('?', 'help'): - # filter_string = input('Search for layout containing (example: "sv-"): ') - # new_options = search_keyboard_layout(filter_string) - # return select_language(new_options) - - selected_driver = options[selected_driver] - - if type(selected_driver) == dict: - driver_options = sorted(list(selected_driver)) - - driver_package_group = generic_select(driver_options, f'Which driver-type do you want for {initial_option}: ', - allow_empty_input=False) - driver_package_group = selected_driver[driver_package_group] - - return driver_package_group - - return selected_driver - - raise RequirementError("Selecting drivers require a least one profile to be given as an option.") - def _prep_function(*args, **kwargs): """ Magic function called by the importing installer -- cgit v1.2.3-70-g09d2 From e63eb26388eee05ba625b55ae1a1883e9ea340c1 Mon Sep 17 00:00:00 2001 From: advaithm Date: Thu, 22 Apr 2021 17:03:24 +0530 Subject: fixed merge conflicts --- archinstall/lib/user_interaction.py | 25 ------------------------- profiles/sway.py | 3 --- 2 files changed, 28 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index d8d2df18..befcb6d1 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -7,11 +7,7 @@ from .output import log, LOG_LEVELS from .storage import storage from .networking import list_interfaces from .general import sys_command -<<<<<<< HEAD -from .hardware import AVAILABLE_GFX_DRIVERS, hasUEFI -======= from .hardware import AVAILABLE_GFX_DRIVERS ->>>>>>> 1292c07... Fixed PR #273. Moved the graphic drivers into hardware since they are hardware specific, in the long run maybe we move them into 'drivers' or something. And moved the user interaction from gfx_drivers into user_interactions. And removed the import from installer.py to __init__.py since we don't want to import 'global functions' in extension imports. ## TODO: Some inconsistencies between the selection processes. ## Some return the keys from the options, some the values? @@ -469,9 +465,6 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS): (The template xorg is for beginner users, not advanced, and should there for appeal to the general public first and edge cases later) """ -<<<<<<< HEAD - if len(options) >= 1: -======= drivers = sorted(list(options)) if len(drivers) >= 1: @@ -481,7 +474,6 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS): print(' -- The above list are supported graphic card drivers. --') print(' -- You need to select (and read about) which one you need. --') ->>>>>>> 1292c07... Fixed PR #273. Moved the graphic drivers into hardware since they are hardware specific, in the long run maybe we move them into 'drivers' or something. And moved the user interaction from gfx_drivers into user_interactions. And removed the import from installer.py to __init__.py since we don't want to import 'global functions' in extension imports. lspci = sys_command(f'/usr/bin/lspci') for line in lspci.trace_log.split(b'\r\n'): if b' vga ' in line.lower(): @@ -490,18 +482,6 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS): elif b'amd' in line.lower(): print(' ** AMD card detected, suggested driver: AMD / ATI **') -<<<<<<< HEAD - selected_driver = generic_select(options, input_text="Select your graphics card driver: ", sort=True) - initial_option = selected_driver - - if type(options[initial_option]) == dict: - driver_options = sorted(options[initial_option].keys()) - - selected_driver_package_group = generic_select(driver_options, input_text=f"Which driver-type do you want for {initial_option}: ") - if selected_driver_package_group in options[initial_option].keys(): - print(options[initial_option][selected_driver_package_group]) - selected_driver = options[initial_option][selected_driver_package_group] -======= selected_driver = input('Select your graphics card driver: ') initial_option = selected_driver @@ -533,7 +513,6 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS): selected_driver_package_group = selected_driver[selected_driver.index(selected_driver_package_group)] elif len(selected_driver_package_group) == 0: raise RequirementError(f"At least one driver package is required for a graphical environment using {selected_driver}. Please restart the installer and try again.") ->>>>>>> 1292c07... Fixed PR #273. Moved the graphic drivers into hardware since they are hardware specific, in the long run maybe we move them into 'drivers' or something. And moved the user interaction from gfx_drivers into user_interactions. And removed the import from installer.py to __init__.py since we don't want to import 'global functions' in extension imports. else: raise RequirementError(f"Selected driver-type does not exist for {initial_option}.") @@ -541,8 +520,4 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS): return selected_driver -<<<<<<< HEAD - raise RequirementError("Selecting drivers require a least one profile to be given as an option.") -======= raise RequirementError("Selecting drivers require a least one profile to be given as an option.") ->>>>>>> 1292c07... Fixed PR #273. Moved the graphic drivers into hardware since they are hardware specific, in the long run maybe we move them into 'drivers' or something. And moved the user interaction from gfx_drivers into user_interactions. And removed the import from installer.py to __init__.py since we don't want to import 'global functions' in extension imports. diff --git a/profiles/sway.py b/profiles/sway.py index 7fb64fd7..53eb8c5a 100644 --- a/profiles/sway.py +++ b/profiles/sway.py @@ -11,12 +11,9 @@ def _prep_function(*args, **kwargs): other code in this stage. So it's a safe way to ask the user for more input before any other installer steps start. """ -<<<<<<< HEAD -======= __builtins__['_gfx_driver_packages'] = archinstall.select_driver() ->>>>>>> 1292c07... Fixed PR #273. Moved the graphic drivers into hardware since they are hardware specific, in the long run maybe we move them into 'drivers' or something. And moved the user interaction from gfx_drivers into user_interactions. And removed the import from installer.py to __init__.py since we don't want to import 'global functions' in extension imports. return True # Ensures that this code only gets executed if executed -- cgit v1.2.3-70-g09d2 From d1560d98ec566e2951841f193b79d107cca8daeb Mon Sep 17 00:00:00 2001 From: SecondThundeR Date: Thu, 22 Apr 2021 14:54:35 +0300 Subject: Fix other issues of language selection --- archinstall/lib/locale_helpers.py | 6 ++++++ archinstall/lib/user_interaction.py | 23 +++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/locale_helpers.py b/archinstall/lib/locale_helpers.py index 736bfc47..3c373bc6 100644 --- a/archinstall/lib/locale_helpers.py +++ b/archinstall/lib/locale_helpers.py @@ -16,6 +16,12 @@ def list_keyboard_languages(): if os.path.splitext(file)[1] == '.gz': yield file.strip('.gz').strip('.map') +def verify_keyboard_layout(layout): + for language in list_keyboard_languages(): + if layout.lower() == language.lower(): + return True + return False + def search_keyboard_layout(filter): for language in list_keyboard_languages(): if filter.lower() in language.lower(): diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index fc62f44c..77b3d771 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -2,7 +2,7 @@ import getpass, pathlib, os, shutil, re import sys, time, signal, ipaddress from .exceptions import * from .profiles import Profile -from .locale_helpers import search_keyboard_layout +from .locale_helpers import list_keyboard_languages, verify_keyboard_layout, search_keyboard_layout from .output import log, LOG_LEVELS from .storage import storage from .networking import list_interfaces @@ -360,10 +360,13 @@ def select_language(options, show_only_country_codes=True): """ Asks the user to select a language from the `options` dictionary parameter. Usually this is combined with :ref:`archinstall.list_keyboard_languages`. - :param options: A `dict` where keys are the language name, value should be a dict containing language information. - :type options: dict + + :param options: A `generator` or `list` where keys are the language name, value should be a dict containing language information. + :type options: generator or list + :param show_only_country_codes: Filters out languages that are not len(lang) == 2. This to limit the number of results from stuff like dvorak and x-latin1 alternatives. :type show_only_country_codes: bool + :return: The language/dictionary key of the selected language :rtype: str """ @@ -387,11 +390,17 @@ def select_language(options, show_only_country_codes=True): return DEFAULT_KEYBOARD_LANGUAGE elif selected_language.lower() in ('?', 'help'): while True: - filter_string = input('Search for layout containing (example: "sv-"): ') + filter_string = input("Search for layout containing (example: \"sv-\") or enter 'exit' to exit from search: ") + + if filter_string.lower() == 'exit': + return select_language(list_keyboard_languages()) + new_options = list(search_keyboard_layout(filter_string)) + if len(new_options) <= 0: - log(f"Search string '{filter_string}' yielded no results, please try another search or Ctrl+D to abort.", fg='yellow') + log(f"Search string '{filter_string}' yielded no results, please try another search.", fg='yellow') continue + return select_language(new_options, show_only_country_codes=False) elif selected_language.isnumeric(): selected_language = int(selected_language) @@ -399,11 +408,13 @@ def select_language(options, show_only_country_codes=True): log(' * Selected option is out of range * ', fg='red') continue return languages[selected_language] - elif search_keyboard_layout(selected_language): + elif verify_keyboard_layout(selected_language): return selected_language else: log(" * Given language wasn't found * ", fg='red') + raise RequirementError("Selecting languages require a least one language to be given as an option.") + def select_mirror_regions(mirrors, show_top_mirrors=True): """ Asks the user to select a mirror or region from the `mirrors` dictionary parameter. -- cgit v1.2.3-70-g09d2 From 1c0c1277624aae89bf9cba6940dcc69fb152be81 Mon Sep 17 00:00:00 2001 From: advaithm Date: Thu, 22 Apr 2021 19:13:57 +0530 Subject: has uefi was removed from user_interactions --- archinstall/lib/user_interaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 7e0a62ba..83c5e5cd 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -7,7 +7,7 @@ from .output import log, LOG_LEVELS from .storage import storage from .networking import list_interfaces from .general import sys_command -from .hardware import AVAILABLE_GFX_DRIVERS +from .hardware import AVAILABLE_GFX_DRIVERS, hasUEFI ## TODO: Some inconsistencies between the selection processes. ## Some return the keys from the options, some the values? -- cgit v1.2.3-70-g09d2 From bd9d2c9125aa7e08d38093dcf21fcef3d101f4d6 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Thu, 22 Apr 2021 15:18:59 -0400 Subject: Update user_interaction.py --- archinstall/lib/user_interaction.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 77b3d771..a481dac3 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -130,17 +130,19 @@ def ask_for_additional_users(prompt='Any additional users to install (leave blan return users, super_users def ask_for_a_timezone(): - timezone = input('Enter a valid timezone (examples: Europe/Stockholm, US/Eastern) or press enter to use UTC: ').strip() - if timezone == '': - timezone = 'UTC' - if (pathlib.Path("/usr")/"share"/"zoneinfo"/timezone).exists(): - return timezone - else: - log( - f"Time zone {timezone} does not exist, continuing with system default.", - level=LOG_LEVELS.Warning, - fg='red' - ) + exists = False + while not exists: + timezone = input('Enter a valid timezone (examples: Europe/Stockholm, US/Eastern) or press enter to use UTC: ').strip() + if timezone == '': + timezone = 'UTC' + if (pathlib.Path("/usr")/"share"/"zoneinfo"/timezone).exists(): + return timezone + else: + log( + f"Specified timezone {timezone} does not exist.", + level=LOG_LEVELS.Warning, + fg='red' + ) def ask_for_audio_selection(): audio = "pulseaudio" # Default for most desktop environments -- cgit v1.2.3-70-g09d2 From 74951b841c9906511731408bc5d3a70a7d6ae65f Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Thu, 22 Apr 2021 21:36:31 +0200 Subject: Restructured endless loop definition I prefer to easily spot an endless loop by not having external variables attached to the loop condition. Since we never updated `exists` it's a dummy variable that I expect to be updated some where in order to break it. When that's not the case, it's more clear that it's an endless loop if there's no conditions attached to the loop definition. --- archinstall/lib/user_interaction.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index a481dac3..dcb51e2a 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -130,8 +130,7 @@ def ask_for_additional_users(prompt='Any additional users to install (leave blan return users, super_users def ask_for_a_timezone(): - exists = False - while not exists: + while True: timezone = input('Enter a valid timezone (examples: Europe/Stockholm, US/Eastern) or press enter to use UTC: ').strip() if timezone == '': timezone = 'UTC' -- cgit v1.2.3-70-g09d2 From 2d3d3c54ef5ec5f4311afe75d87733a27b1d7509 Mon Sep 17 00:00:00 2001 From: SecondThundeR Date: Fri, 23 Apr 2021 01:08:38 +0300 Subject: Remove unnecessary else in try...except This change simplifies the try...except block in generic_select by adding a break to the item selection by index --- archinstall/lib/user_interaction.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index dcb51e2a..572acf4f 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -277,7 +277,7 @@ def generic_select(options, input_text="Select one of the above by index or abso print(f"{index}: {option}") # The new changes introduce a single while loop for all inputs processed by this function - # Now the try...except...else block handles validation for invalid input from the user + # Now the try...except block handles validation for invalid input from the user while True: try: selected_option = input(input_text) @@ -293,6 +293,7 @@ def generic_select(options, input_text="Select one of the above by index or abso if selected_option >= len(options): raise RequirementError(f'Selected option "{selected_option}" is out of range') selected_option = options[selected_option] + break elif selected_option in options: break # We gave a correct absolute value else: @@ -300,8 +301,6 @@ def generic_select(options, input_text="Select one of the above by index or abso except RequirementError as err: log(f" * {err} * ", fg='red') continue - else: - break return selected_option -- cgit v1.2.3-70-g09d2 From f5b6e7bafead1f604c27bfb31b84f3f560a682c8 Mon Sep 17 00:00:00 2001 From: SecondThundeR Date: Fri, 23 Apr 2021 01:55:53 +0300 Subject: Update logging for some functions - Unified view of warning (red) and info (yellow) logs - Fixed some PEP8 related issues, like removing redundant f-strings and replacing double quotes to single ones - Removed warning logging level for simple logs - Removed other background color settings for logs to fully close https://github.com/archlinux/archinstall/pull/171 --- archinstall/lib/disk.py | 4 ++-- archinstall/lib/installer.py | 10 +++++----- archinstall/lib/mirrors.py | 2 +- archinstall/lib/output.py | 4 ++-- archinstall/lib/user_interaction.py | 25 +++++++++--------------- examples/guided.py | 39 +++++++++++++++++++------------------ 6 files changed, 39 insertions(+), 45 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/disk.py b/archinstall/lib/disk.py index bada4076..de93556a 100644 --- a/archinstall/lib/disk.py +++ b/archinstall/lib/disk.py @@ -128,7 +128,7 @@ class BlockDevice(): @property def uuid(self): - log(f'BlockDevice().uuid is untested!', level=LOG_LEVELS.Warning, fg='yellow') + log('BlockDevice().uuid is untested!', level=LOG_LEVELS.Warning, fg='yellow') """ Returns the disk UUID as returned by lsblk. This is more reliable than relying on /dev/disk/by-partuuid as @@ -292,7 +292,7 @@ class Partition(): raise DiskError(f"Attempting to encrypt a partition that was not marked for encryption: {self}") if not self.safe_to_format(): - log(f"Partition {self} was marked as protected but encrypt() was called on it!", level=LOG_LEVELS.Error, fg="red") + log(f" * Partition {self} was marked as protected but encrypt() was called on it! * ", level=LOG_LEVELS.Error, fg='red') return False handle = luks2(self, None, None) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 758033a7..c64079f6 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -79,14 +79,14 @@ class Installer(): self.genfstab() if not (missing_steps := self.post_install_check()): - self.log('Installation completed without any errors. You may now reboot.', bg='black', fg='green', level=LOG_LEVELS.Info) + self.log('Installation completed without any errors. You may now reboot.', fg='green', level=LOG_LEVELS.Info) self.sync_log_to_install_medium() return True else: - self.log('Some required steps were not successfully installed/configured before leaving the installer:', bg='black', fg='red', level=LOG_LEVELS.Warning) + self.log('Some required steps were not successfully installed/configured before leaving the installer:', fg='red', level=LOG_LEVELS.Warning) for step in missing_steps: - self.log(f' - {step}', bg='black', fg='red', level=LOG_LEVELS.Warning) + self.log(f' - {step}', fg='red', level=LOG_LEVELS.Warning) self.log(f"Detailed error logs can be found at: {storage['LOG_PATH']}", level=LOG_LEVELS.Warning) self.log(f"Submit this zip file as an issue to https://github.com/archlinux/archinstall/issues", level=LOG_LEVELS.Warning) @@ -168,7 +168,7 @@ class Installer(): return True else: self.log( - f"Time zone {zone} does not exist, continuing with system default.", + f" * Timezone \"{zone}\" does not exist, continuing with system default * ", level=LOG_LEVELS.Warning, fg='red' ) @@ -460,5 +460,5 @@ class Installer(): vconsole.write(f'KEYMAP={language}\n') vconsole.write(f'FONT=lat9w-16\n') else: - self.log(f'Keyboard language was not changed from default (no language specified).', fg="yellow", level=LOG_LEVELS.Info) + self.log('Keyboard language was not changed from default (no language specified).', fg='yellow', level=LOG_LEVELS.Info) return True diff --git a/archinstall/lib/mirrors.py b/archinstall/lib/mirrors.py index 04f47c0d..57271bf8 100644 --- a/archinstall/lib/mirrors.py +++ b/archinstall/lib/mirrors.py @@ -79,7 +79,7 @@ def list_mirrors(): try: response = urllib.request.urlopen(url) except urllib.error.URLError as err: - log(f'Could not fetch an active mirror-list: {err}', level=LOG_LEVELS.Warning, fg="yellow") + log(f"Could not fetch an active mirror-list: {err}", level=LOG_LEVELS.Warning, fg='yellow') return regions diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py index 6b184b4b..eb23faf4 100644 --- a/archinstall/lib/output.py +++ b/archinstall/lib/output.py @@ -100,12 +100,12 @@ def log(*args, **kwargs): Path(absolute_logfile).parents[0].mkdir(exist_ok=True, parents=True) 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." + 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") + log(err_string, fg='red') Path(absolute_logfile).touch() # Overkill? diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 572acf4f..ec6b6d34 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -22,11 +22,7 @@ def get_longest_option(options): def check_for_correct_username(username): if re.match(r'^[a-z_][a-z0-9_-]*\$?$', username) and len(username) <= 32: return True - log( - "The username you entered is invalid. Try again", - level=LOG_LEVELS.Warning, - fg='red' - ) + log(' * The username you entered is invalid. Try again * ', fg='red') return False def do_countdown(): @@ -138,8 +134,7 @@ def ask_for_a_timezone(): return timezone else: log( - f"Specified timezone {timezone} does not exist.", - level=LOG_LEVELS.Warning, + f"* Specified timezone {timezone} does not exist * ", fg='red' ) @@ -185,8 +180,7 @@ def ask_to_configure_network(): break except ValueError: log( - "You need to enter a valid IP in IP-config mode.", - level=LOG_LEVELS.Warning, + 'You need to enter a valid IP in IP-config mode', fg='red' ) @@ -201,8 +195,7 @@ def ask_to_configure_network(): break except ValueError: log( - "You need to enter a valid gateway (router) IP address.", - level=LOG_LEVELS.Warning, + 'You need to enter a valid gateway (router) IP address', fg='red' ) @@ -258,15 +251,15 @@ def generic_select(options, input_text="Select one of the above by index or abso # Checking if options are different from `list` or `dict` if type(options) not in [list, dict]: log(f" * Generic select doesn't support ({type(options)}) as type of options * ", fg='red') - log(" * If problem persists, please create an issue on https://github.com/archlinux/archinstall/issues * ", fg='yellow') + log('If problem persists, please create an issue on https://github.com/archlinux/archinstall/issues', fg='yellow') raise RequirementError("generic_select() requires list or dictionary as options.") # To allow only `list` and `dict`, converting values of options here. # Therefore, now we can only provide the dictionary itself if type(options) == dict: options = list(options.values()) if sort: options = sorted(options) # As we pass only list and dict (converted to list), we can skip converting to list if len(options) == 0: - log(f" * Generic select didn't find any options to choose from * ", fg='red') - log(" * If problem persists, please create an issue on https://github.com/archlinux/archinstall/issues * ", fg='yellow') + log(' * Generic select didn\'t find any options to choose from * ', fg='red') + log('If problem persists, please create an issue on https://github.com/archlinux/archinstall/issues', fg='yellow') raise RequirementError('generic_select() requires at least one option to proceed.') @@ -398,7 +391,7 @@ def select_language(options, show_only_country_codes=True): new_options = list(search_keyboard_layout(filter_string)) if len(new_options) <= 0: - log(f"Search string '{filter_string}' yielded no results, please try another search.", fg='yellow') + log(f"Search string '{filter_string}' yielded no results, please try another search", fg='yellow') continue return select_language(new_options, show_only_country_codes=False) @@ -411,7 +404,7 @@ def select_language(options, show_only_country_codes=True): elif verify_keyboard_layout(selected_language): return selected_language else: - log(" * Given language wasn't found * ", fg='red') + log(' * Given language was not found * ', fg='red') raise RequirementError("Selecting languages require a least one language to be given as an option.") diff --git a/examples/guided.py b/examples/guided.py index cc9cf5fc..f89efa45 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -4,7 +4,7 @@ from archinstall.lib.hardware import hasUEFI from archinstall.lib.profiles import Profile if hasUEFI() is False: - archinstall.log("ArchInstall currently only supports machines booted with UEFI.\nMBR & GRUB support is coming in version 2.2.0!", fg="red", level=archinstall.LOG_LEVELS.Error) + archinstall.log(" * ArchInstall currently only supports machines booted with UEFI.\nMBR & GRUB support is coming in version 2.2.0! * ", fg='red', level=archinstall.LOG_LEVELS.Error) exit(1) def ask_user_questions(): @@ -18,8 +18,8 @@ def ask_user_questions(): try: archinstall.arguments['keyboard-language'] = archinstall.select_language(archinstall.list_keyboard_languages()).strip() break - except archinstall.RequirementError as err: - archinstall.log(err, fg="red") + except archinstall.RequirementError as e: + archinstall.log(f" * {e} * ", fg='red') # Before continuing, set the preferred keyboard layout/language in the current terminal. # This will just help the user with the next following questions. @@ -33,7 +33,7 @@ def ask_user_questions(): archinstall.arguments['mirror-region'] = archinstall.select_mirror_regions(archinstall.list_mirrors()) break except archinstall.RequirementError as e: - archinstall.log(e, fg="red") + archinstall.log(f" * {e} * ", fg='red') else: selected_region = archinstall.arguments['mirror-region'] archinstall.arguments['mirror-region'] = {selected_region : archinstall.list_mirrors()[selected_region]} @@ -67,13 +67,13 @@ def ask_user_questions(): # We then ask what to do with the partitions. if (option := archinstall.ask_for_disk_layout()) == 'abort': - archinstall.log(f"Safely aborting the installation. No changes to the disk or system has been made.") + archinstall.log('Safely aborting the installation. No changes to the disk or system has been made', fg='yellow') exit(1) elif option == 'keep-existing': archinstall.arguments['harddrive'].keep_partitions = True - archinstall.log(f" ** You will now select which partitions to use by selecting mount points (inside the installation). **") - archinstall.log(f" ** The root would be a simple / and the boot partition /boot (as all paths are relative inside the installation). **") + archinstall.log(' -- You will now select which partitions to use by selecting mount points (inside the installation) -- ') + archinstall.log(' -- The root would be a simple / and the boot partition /boot (as all paths are relative inside the installation) -- ') mountpoints_set = [] while True: # Select a partition @@ -103,8 +103,8 @@ def ask_user_questions(): if (autodetected_filesystem := partition.detect_inner_filesystem(old_password)): new_filesystem = autodetected_filesystem else: - archinstall.log(f"Could not auto-detect the filesystem inside the encrypted volume.", fg='red') - archinstall.log(f"A filesystem must be defined for the unlocked encrypted partition.") + archinstall.log(' * Could not auto-detect the filesystem inside the encrypted volume * ', fg='red') + archinstall.log('A filesystem must be defined for the unlocked encrypted partition') continue break @@ -114,8 +114,9 @@ def ask_user_questions(): try: partition.format(new_filesystem, path='/dev/null', log_formatting=False, allow_formatting=True) except archinstall.UnknownFilesystemFormat: - archinstall.log(f"Selected filesystem is not supported yet. If you want archinstall to support '{new_filesystem}', please create a issue-ticket suggesting it on github at https://github.com/archlinux/archinstall/issues.") - archinstall.log(f"Until then, please enter another supported filesystem.") + archinstall.log(' * Selected filesystem is not supported yet * ', fg='red') + archinstall.log(f"If you want archinstall to support '{new_filesystem}', please create a issue-ticket suggesting it on github at https://github.com/archlinux/archinstall/issues.") + archinstall.log('Until then, please enter another supported filesystem') continue except archinstall.SysCallError: pass # Expected exception since mkfs. can not format /dev/null. @@ -178,7 +179,7 @@ def ask_user_questions(): with archinstall.arguments['profile'].load_instructions(namespace=f"{archinstall.arguments['profile'].namespace}.py") as imported: if not imported._prep_function(): archinstall.log( - ' * Profile\'s preparation requirements was not fulfilled.', + ' * Profile\'s preparation requirements was not fulfilled * ', fg='red' ) exit(1) @@ -204,11 +205,11 @@ def ask_user_questions(): if len(archinstall.arguments['packages']): # Verify packages that were given try: - archinstall.log(f"Verifying that additional packages exist (this might take a few seconds)") + archinstall.log('Verifying that additional packages exist (this might take a few seconds)', fg='yellow') archinstall.validate_package_list(archinstall.arguments['packages']) break except archinstall.RequirementError as e: - archinstall.log(e, fg='red') + archinstall.log(f" * {e} * ", fg='red') archinstall.arguments['packages'] = None # Clear the packages to trigger a new input question else: # no additional packages were selected, which we'll allow @@ -218,7 +219,7 @@ def ask_user_questions(): if not archinstall.arguments.get('nic', None): archinstall.arguments['nic'] = archinstall.ask_to_configure_network() if not archinstall.arguments['nic']: - archinstall.log(f"No network configuration was selected. Network is going to be unavailable until configured manually!", fg="yellow") + archinstall.log('No network configuration was selected. Network is going to be unavailable until configured manually!', fg='yellow') if not archinstall.arguments.get('timezone', None): archinstall.arguments['timezone'] = archinstall.ask_for_a_timezone() @@ -299,7 +300,7 @@ def perform_installation(mountpoint): # 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(f'Waiting for automatic mirror selection (reflector) to complete.', level=archinstall.LOG_LEVELS.Info) + installation.log('Waiting for automatic mirror selection (reflector) to complete...', level=archinstall.LOG_LEVELS.Info) while archinstall.service_state('reflector') not in ('dead', 'failed'): time.sleep(1) @@ -356,9 +357,9 @@ def perform_installation(mountpoint): if (root_pw := archinstall.arguments.get('!root-password', None)) and len(root_pw): installation.user_set_pw('root', root_pw) - installation.log("For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation", fg="yellow") - choice = input("Would you like to chroot into the newly created installation and perform post-installation configuration? [Y/n] ") - if choice.lower() in ("y", ""): + installation.log('For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation', fg='yellow') + choice = input('Would you like to chroot into the newly created installation and perform post-installation configuration? [Y/n] ') + if choice.lower() in ('y', ''): try: installation.drop_to_shell() except: -- cgit v1.2.3-70-g09d2 From 95e73f65a7ea41c49ba2186124517d52dd962eb8 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Thu, 22 Apr 2021 19:36:32 -0400 Subject: Ignore dotfiles and globs --- archinstall/lib/user_interaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index dcb51e2a..b1437382 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -134,7 +134,7 @@ def ask_for_a_timezone(): timezone = input('Enter a valid timezone (examples: Europe/Stockholm, US/Eastern) or press enter to use UTC: ').strip() if timezone == '': timezone = 'UTC' - if (pathlib.Path("/usr")/"share"/"zoneinfo"/timezone).exists(): + if (pathlib.Path("/usr")/"share"/"zoneinfo"/timezone.strip('*.')).exists(): return timezone else: log( -- cgit v1.2.3-70-g09d2 From d21e31d4779593bd371ef0a301f4dc0a4d79cc1f Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Thu, 22 Apr 2021 19:38:43 -0400 Subject: Actually, putting it here makes more sense. --- archinstall/lib/user_interaction.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index b1437382..89c54f78 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -131,10 +131,10 @@ def ask_for_additional_users(prompt='Any additional users to install (leave blan def ask_for_a_timezone(): while True: - timezone = input('Enter a valid timezone (examples: Europe/Stockholm, US/Eastern) or press enter to use UTC: ').strip() + timezone = input('Enter a valid timezone (examples: Europe/Stockholm, US/Eastern) or press enter to use UTC: ').strip().strip('*.') if timezone == '': timezone = 'UTC' - if (pathlib.Path("/usr")/"share"/"zoneinfo"/timezone.strip('*.')).exists(): + if (pathlib.Path("/usr")/"share"/"zoneinfo"/timezone).exists(): return timezone else: log( -- cgit v1.2.3-70-g09d2 From 84dbbd0857253cd2e954c2f8951ce426b53a6037 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Fri, 23 Apr 2021 23:52:49 +0200 Subject: Added back the note about using /mnt as is Also added a "experimental" warning to it. --- archinstall/lib/user_interaction.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index dcb51e2a..5d857b93 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -320,10 +320,13 @@ def select_disk(dict_o_disks): if len(drives) >= 1: for index, drive in enumerate(drives): print(f"{index}: {drive} ({dict_o_disks[drive]['size'], dict_o_disks[drive].device, dict_o_disks[drive]['label']})") - drive = generic_select(drives, 'Select one of the above disks (by number or full path) or leave blank to skip partitioning: ', + + log(f"You can skip selecting a drive and partition it and use whatever drive-setup is mounted at /mnt (experimental)", fg="yellow") + drive = generic_select(drives, 'Select one of the above disks (by name or number) or leave blank to use /mnt: ', options_output=False) if not drive: return drive + drive = dict_o_disks[drive] return drive -- cgit v1.2.3-70-g09d2 From 5c9859d316b8064949370c03745cfd99e0d67670 Mon Sep 17 00:00:00 2001 From: Aggam Rahamim Date: Sun, 25 Apr 2021 14:55:39 +0300 Subject: having ability to use multiple kernels --- archinstall/lib/installer.py | 4 ++-- archinstall/lib/user_interaction.py | 4 +++- examples/guided.py | 19 +++++++++++-------- 3 files changed, 16 insertions(+), 11 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 7f6311c9..d952a6ed 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -34,8 +34,8 @@ class Installer(): :type hostname: str, optional """ - def __init__(self, target, *, base_packages=' base base-devel linux-firmware efibootmgr', kernel='linux'): - base_packages = kernel + base_packages + def __init__(self, target, *, base_packages='base base-devel linux-firmware efibootmgr ', kernels='linux'): + base_packages = base_packages + kernels.replace(',', ' ') self.target = target self.init_time = time.strftime('%Y-%m-%d_%H-%M-%S') self.milliseconds = int(str(time.time()).split('.')[1]) diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 99cf6274..3da24684 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -218,6 +218,8 @@ def generic_select(options, input_text="Select one of the above by index or abso 1: first 2: second 3: third option + + it will return the selected text. """ if type(options) == dict: options = list(options) @@ -263,7 +265,7 @@ def select_disk(dict_o_disks): elif drive.isdigit(): drive = int(drive) if drive >= len(drives): - raise DiskError(f'Selected option "{drive}" is out of range') + raise Dis1ror(f'Selected option "{drive}" is out of range') drive = dict_o_disks[drives[drive]] elif drive in dict_o_disks: drive = dict_o_disks[drive] diff --git a/examples/guided.py b/examples/guided.py index e6abe872..ee7b63d9 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -2,6 +2,7 @@ import getpass, time, json, os import archinstall from archinstall.lib.hardware import hasUEFI from archinstall.lib.profiles import Profile +from archinstall.lib.user_interaction import generic_select if hasUEFI() is False: log("ArchInstall currently only supports machines booted with UEFI. MBR & GRUB support is coming in version 2.2.0!", fg="red", level=archinstall.LOG_LEVELS.Error) @@ -168,7 +169,6 @@ def ask_user_questions(): # Ask about audio server selection if one is not already set if not archinstall.arguments.get('audio', None): - # only ask for audio server selection on a desktop profile if str(archinstall.arguments['profile']) == 'Profile(desktop)': archinstall.arguments['audio'] = archinstall.ask_for_audio_selection() @@ -178,12 +178,15 @@ def ask_user_questions(): archinstall.arguments['audio'] = None # Ask what kernel user wants: - kernel = input("1. linux\n2. linux-lts\n3. linux-zen\nchoose a kernel of the following: ") - try: - archinstall.arguments['kernel'] = ['linux', 'linux-lts', 'linux-zen'][int(kernel) - 1] - except: - archinstall.log('invalid kernel selected. defaulting to \'linux\'.') - archinstall.arguments['kernel'] = 'linux' + while True: + kernel = generic_select(["linux", "linux-lts", "linux-zen", "continue"], "choose a kernel:") + if (archinstall.arguments['kernels'] == None or archinstall.arguments['kernels'] == ""): + archinstall.arguments['kernels'] = kernel + else: + if (kernel == "continue"): + break + archinstall.arguments['kernels'] += "," + kernel + # Additional packages (with some light weight error handling for invalid package names) @@ -281,7 +284,7 @@ def perform_installation(mountpoint): Only requirement is that the block devices are formatted and setup prior to entering this function. """ - with archinstall.Installer(mountpoint, kernel=archinstall.arguments['kernel']) as installation: + with archinstall.Installer(mountpoint, kernels=archinstall.arguments['kernels']) as 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 -- cgit v1.2.3-70-g09d2 From ea9d34e8fcd549c2602ced2bdcf13b29e9221817 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Sun, 25 Apr 2021 22:18:29 +0200 Subject: Re-phrased the wording a bit on the exp. warning. --- archinstall/lib/user_interaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index f35298ee..5cab81f8 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -321,7 +321,7 @@ def select_disk(dict_o_disks): for index, drive in enumerate(drives): print(f"{index}: {drive} ({dict_o_disks[drive]['size'], dict_o_disks[drive].device, dict_o_disks[drive]['label']})") - log(f"You can skip selecting a drive and partition it and use whatever drive-setup is mounted at /mnt (experimental)", fg="yellow") + log(f"You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)", fg="yellow") drive = generic_select(drives, 'Select one of the above disks (by name or number) or leave blank to use /mnt: ', options_output=False) if not drive: -- cgit v1.2.3-70-g09d2 From 847cb3d032498e03f27b9bffede04faa5ac4361f Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Tue, 27 Apr 2021 12:27:49 +0200 Subject: Spelling error --- archinstall/lib/user_interaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index b93f88b3..f801ec83 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -267,7 +267,7 @@ def select_disk(dict_o_disks): elif drive.isdigit(): drive = int(drive) if drive >= len(drives): - raise Dis1ror(f'Selected option "{drive}" is out of range') + raise DiskError(f'Selected option "{drive}" is out of range') drive = dict_o_disks[drives[drive]] elif drive in dict_o_disks: drive = dict_o_disks[drive] -- cgit v1.2.3-70-g09d2 From 19c928df6ae7f6bf700817d5d709c8918bf3d411 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Tue, 27 Apr 2021 13:35:14 +0200 Subject: Forgot to hit save after merge conflict fixes. --- archinstall/lib/user_interaction.py | 8 -------- 1 file changed, 8 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 91ed5ed9..bd9d432d 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -259,20 +259,12 @@ def generic_select(options, input_text="Select one of the above by index or abso other than the options and their indexes. As an example: generic_select(["first", "second", "third option"]) -<<<<<<< HEAD 0: first 1: second 2: third option When the user has entered the option correctly, this function returns an item from list, a string, or None -======= - 1: first - 2: second - 3: third option - - it will return the selected text. ->>>>>>> 831f5e4eaf66f6f1a2aed04932af064ce9314dd4 """ # Checking if options are different from `list` or `dict` -- cgit v1.2.3-70-g09d2 From 985b7fac3ff400de9954d441480ab795222f4b24 Mon Sep 17 00:00:00 2001 From: SecondThundeR Date: Tue, 27 Apr 2021 15:14:28 +0300 Subject: Revert "Update logging for some functions" This reverts commit f5b6e7bafead1f604c27bfb31b84f3f560a682c8. Reverting commit due to currently redundant change and merge conflict --- archinstall/lib/disk.py | 4 ++-- archinstall/lib/installer.py | 10 +++++----- archinstall/lib/mirrors.py | 2 +- archinstall/lib/output.py | 4 ++-- archinstall/lib/user_interaction.py | 25 +++++++++++++++--------- examples/guided.py | 39 ++++++++++++++++++------------------- 6 files changed, 45 insertions(+), 39 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/disk.py b/archinstall/lib/disk.py index de93556a..bada4076 100644 --- a/archinstall/lib/disk.py +++ b/archinstall/lib/disk.py @@ -128,7 +128,7 @@ class BlockDevice(): @property def uuid(self): - log('BlockDevice().uuid is untested!', level=LOG_LEVELS.Warning, fg='yellow') + log(f'BlockDevice().uuid is untested!', level=LOG_LEVELS.Warning, fg='yellow') """ Returns the disk UUID as returned by lsblk. This is more reliable than relying on /dev/disk/by-partuuid as @@ -292,7 +292,7 @@ class Partition(): raise DiskError(f"Attempting to encrypt a partition that was not marked for encryption: {self}") if not self.safe_to_format(): - log(f" * Partition {self} was marked as protected but encrypt() was called on it! * ", level=LOG_LEVELS.Error, fg='red') + log(f"Partition {self} was marked as protected but encrypt() was called on it!", level=LOG_LEVELS.Error, fg="red") return False handle = luks2(self, None, None) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index c64079f6..758033a7 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -79,14 +79,14 @@ class Installer(): self.genfstab() if not (missing_steps := self.post_install_check()): - self.log('Installation completed without any errors. You may now reboot.', fg='green', level=LOG_LEVELS.Info) + self.log('Installation completed without any errors. You may now reboot.', bg='black', fg='green', level=LOG_LEVELS.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=LOG_LEVELS.Warning) + self.log('Some required steps were not successfully installed/configured before leaving the installer:', bg='black', fg='red', level=LOG_LEVELS.Warning) for step in missing_steps: - self.log(f' - {step}', fg='red', level=LOG_LEVELS.Warning) + self.log(f' - {step}', bg='black', fg='red', level=LOG_LEVELS.Warning) self.log(f"Detailed error logs can be found at: {storage['LOG_PATH']}", level=LOG_LEVELS.Warning) self.log(f"Submit this zip file as an issue to https://github.com/archlinux/archinstall/issues", level=LOG_LEVELS.Warning) @@ -168,7 +168,7 @@ class Installer(): return True else: self.log( - f" * Timezone \"{zone}\" does not exist, continuing with system default * ", + f"Time zone {zone} does not exist, continuing with system default.", level=LOG_LEVELS.Warning, fg='red' ) @@ -460,5 +460,5 @@ class Installer(): vconsole.write(f'KEYMAP={language}\n') vconsole.write(f'FONT=lat9w-16\n') else: - self.log('Keyboard language was not changed from default (no language specified).', fg='yellow', level=LOG_LEVELS.Info) + self.log(f'Keyboard language was not changed from default (no language specified).', fg="yellow", level=LOG_LEVELS.Info) return True diff --git a/archinstall/lib/mirrors.py b/archinstall/lib/mirrors.py index 57271bf8..04f47c0d 100644 --- a/archinstall/lib/mirrors.py +++ b/archinstall/lib/mirrors.py @@ -79,7 +79,7 @@ def list_mirrors(): try: response = urllib.request.urlopen(url) except urllib.error.URLError as err: - log(f"Could not fetch an active mirror-list: {err}", level=LOG_LEVELS.Warning, fg='yellow') + log(f'Could not fetch an active mirror-list: {err}', level=LOG_LEVELS.Warning, fg="yellow") return regions diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py index eb23faf4..6b184b4b 100644 --- a/archinstall/lib/output.py +++ b/archinstall/lib/output.py @@ -100,12 +100,12 @@ def log(*args, **kwargs): Path(absolute_logfile).parents[0].mkdir(exist_ok=True, parents=True) 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 * " + 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') + log(err_string, fg="red") Path(absolute_logfile).touch() # Overkill? diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index ec6b6d34..572acf4f 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -22,7 +22,11 @@ def get_longest_option(options): def check_for_correct_username(username): if re.match(r'^[a-z_][a-z0-9_-]*\$?$', username) and len(username) <= 32: return True - log(' * The username you entered is invalid. Try again * ', fg='red') + log( + "The username you entered is invalid. Try again", + level=LOG_LEVELS.Warning, + fg='red' + ) return False def do_countdown(): @@ -134,7 +138,8 @@ def ask_for_a_timezone(): return timezone else: log( - f"* Specified timezone {timezone} does not exist * ", + f"Specified timezone {timezone} does not exist.", + level=LOG_LEVELS.Warning, fg='red' ) @@ -180,7 +185,8 @@ def ask_to_configure_network(): break except ValueError: log( - 'You need to enter a valid IP in IP-config mode', + "You need to enter a valid IP in IP-config mode.", + level=LOG_LEVELS.Warning, fg='red' ) @@ -195,7 +201,8 @@ def ask_to_configure_network(): break except ValueError: log( - 'You need to enter a valid gateway (router) IP address', + "You need to enter a valid gateway (router) IP address.", + level=LOG_LEVELS.Warning, fg='red' ) @@ -251,15 +258,15 @@ def generic_select(options, input_text="Select one of the above by index or abso # Checking if options are different from `list` or `dict` if type(options) not in [list, dict]: log(f" * Generic select doesn't support ({type(options)}) as type of options * ", fg='red') - log('If problem persists, please create an issue on https://github.com/archlinux/archinstall/issues', fg='yellow') + log(" * If problem persists, please create an issue on https://github.com/archlinux/archinstall/issues * ", fg='yellow') raise RequirementError("generic_select() requires list or dictionary as options.") # To allow only `list` and `dict`, converting values of options here. # Therefore, now we can only provide the dictionary itself if type(options) == dict: options = list(options.values()) if sort: options = sorted(options) # As we pass only list and dict (converted to list), we can skip converting to list if len(options) == 0: - log(' * Generic select didn\'t find any options to choose from * ', fg='red') - log('If problem persists, please create an issue on https://github.com/archlinux/archinstall/issues', fg='yellow') + log(f" * Generic select didn't find any options to choose from * ", fg='red') + log(" * If problem persists, please create an issue on https://github.com/archlinux/archinstall/issues * ", fg='yellow') raise RequirementError('generic_select() requires at least one option to proceed.') @@ -391,7 +398,7 @@ def select_language(options, show_only_country_codes=True): new_options = list(search_keyboard_layout(filter_string)) if len(new_options) <= 0: - log(f"Search string '{filter_string}' yielded no results, please try another search", fg='yellow') + log(f"Search string '{filter_string}' yielded no results, please try another search.", fg='yellow') continue return select_language(new_options, show_only_country_codes=False) @@ -404,7 +411,7 @@ def select_language(options, show_only_country_codes=True): elif verify_keyboard_layout(selected_language): return selected_language else: - log(' * Given language was not found * ', fg='red') + log(" * Given language wasn't found * ", fg='red') raise RequirementError("Selecting languages require a least one language to be given as an option.") diff --git a/examples/guided.py b/examples/guided.py index f89efa45..cc9cf5fc 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -4,7 +4,7 @@ from archinstall.lib.hardware import hasUEFI from archinstall.lib.profiles import Profile if hasUEFI() is False: - archinstall.log(" * ArchInstall currently only supports machines booted with UEFI.\nMBR & GRUB support is coming in version 2.2.0! * ", fg='red', level=archinstall.LOG_LEVELS.Error) + archinstall.log("ArchInstall currently only supports machines booted with UEFI.\nMBR & GRUB support is coming in version 2.2.0!", fg="red", level=archinstall.LOG_LEVELS.Error) exit(1) def ask_user_questions(): @@ -18,8 +18,8 @@ def ask_user_questions(): try: archinstall.arguments['keyboard-language'] = archinstall.select_language(archinstall.list_keyboard_languages()).strip() break - except archinstall.RequirementError as e: - archinstall.log(f" * {e} * ", fg='red') + except archinstall.RequirementError as err: + archinstall.log(err, fg="red") # Before continuing, set the preferred keyboard layout/language in the current terminal. # This will just help the user with the next following questions. @@ -33,7 +33,7 @@ def ask_user_questions(): archinstall.arguments['mirror-region'] = archinstall.select_mirror_regions(archinstall.list_mirrors()) break except archinstall.RequirementError as e: - archinstall.log(f" * {e} * ", fg='red') + archinstall.log(e, fg="red") else: selected_region = archinstall.arguments['mirror-region'] archinstall.arguments['mirror-region'] = {selected_region : archinstall.list_mirrors()[selected_region]} @@ -67,13 +67,13 @@ def ask_user_questions(): # We then ask what to do with the partitions. if (option := archinstall.ask_for_disk_layout()) == 'abort': - archinstall.log('Safely aborting the installation. No changes to the disk or system has been made', fg='yellow') + archinstall.log(f"Safely aborting the installation. No changes to the disk or system has been made.") exit(1) elif option == 'keep-existing': archinstall.arguments['harddrive'].keep_partitions = True - archinstall.log(' -- You will now select which partitions to use by selecting mount points (inside the installation) -- ') - archinstall.log(' -- The root would be a simple / and the boot partition /boot (as all paths are relative inside the installation) -- ') + archinstall.log(f" ** You will now select which partitions to use by selecting mount points (inside the installation). **") + archinstall.log(f" ** The root would be a simple / and the boot partition /boot (as all paths are relative inside the installation). **") mountpoints_set = [] while True: # Select a partition @@ -103,8 +103,8 @@ def ask_user_questions(): if (autodetected_filesystem := partition.detect_inner_filesystem(old_password)): new_filesystem = autodetected_filesystem else: - archinstall.log(' * Could not auto-detect the filesystem inside the encrypted volume * ', fg='red') - archinstall.log('A filesystem must be defined for the unlocked encrypted partition') + archinstall.log(f"Could not auto-detect the filesystem inside the encrypted volume.", fg='red') + archinstall.log(f"A filesystem must be defined for the unlocked encrypted partition.") continue break @@ -114,9 +114,8 @@ def ask_user_questions(): try: partition.format(new_filesystem, path='/dev/null', log_formatting=False, allow_formatting=True) except archinstall.UnknownFilesystemFormat: - archinstall.log(' * Selected filesystem is not supported yet * ', fg='red') - archinstall.log(f"If you want archinstall to support '{new_filesystem}', please create a issue-ticket suggesting it on github at https://github.com/archlinux/archinstall/issues.") - archinstall.log('Until then, please enter another supported filesystem') + archinstall.log(f"Selected filesystem is not supported yet. If you want archinstall to support '{new_filesystem}', please create a issue-ticket suggesting it on github at https://github.com/archlinux/archinstall/issues.") + archinstall.log(f"Until then, please enter another supported filesystem.") continue except archinstall.SysCallError: pass # Expected exception since mkfs. can not format /dev/null. @@ -179,7 +178,7 @@ def ask_user_questions(): with archinstall.arguments['profile'].load_instructions(namespace=f"{archinstall.arguments['profile'].namespace}.py") as imported: if not imported._prep_function(): archinstall.log( - ' * Profile\'s preparation requirements was not fulfilled * ', + ' * Profile\'s preparation requirements was not fulfilled.', fg='red' ) exit(1) @@ -205,11 +204,11 @@ def ask_user_questions(): if len(archinstall.arguments['packages']): # Verify packages that were given try: - archinstall.log('Verifying that additional packages exist (this might take a few seconds)', fg='yellow') + archinstall.log(f"Verifying that additional packages exist (this might take a few seconds)") archinstall.validate_package_list(archinstall.arguments['packages']) break except archinstall.RequirementError as e: - archinstall.log(f" * {e} * ", fg='red') + archinstall.log(e, fg='red') archinstall.arguments['packages'] = None # Clear the packages to trigger a new input question else: # no additional packages were selected, which we'll allow @@ -219,7 +218,7 @@ def ask_user_questions(): if not archinstall.arguments.get('nic', None): archinstall.arguments['nic'] = archinstall.ask_to_configure_network() if not archinstall.arguments['nic']: - archinstall.log('No network configuration was selected. Network is going to be unavailable until configured manually!', fg='yellow') + archinstall.log(f"No network configuration was selected. Network is going to be unavailable until configured manually!", fg="yellow") if not archinstall.arguments.get('timezone', None): archinstall.arguments['timezone'] = archinstall.ask_for_a_timezone() @@ -300,7 +299,7 @@ def perform_installation(mountpoint): # 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=archinstall.LOG_LEVELS.Info) + installation.log(f'Waiting for automatic mirror selection (reflector) to complete.', level=archinstall.LOG_LEVELS.Info) while archinstall.service_state('reflector') not in ('dead', 'failed'): time.sleep(1) @@ -357,9 +356,9 @@ def perform_installation(mountpoint): if (root_pw := archinstall.arguments.get('!root-password', None)) and len(root_pw): installation.user_set_pw('root', root_pw) - installation.log('For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation', fg='yellow') - choice = input('Would you like to chroot into the newly created installation and perform post-installation configuration? [Y/n] ') - if choice.lower() in ('y', ''): + installation.log("For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation", fg="yellow") + choice = input("Would you like to chroot into the newly created installation and perform post-installation configuration? [Y/n] ") + if choice.lower() in ("y", ""): try: installation.drop_to_shell() except: -- cgit v1.2.3-70-g09d2 From 5f9720ab1c2e00c96f3e73b7b6d369046bd78fb8 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Tue, 27 Apr 2021 08:22:36 -0400 Subject: Use generic_select for driver selection --- archinstall/lib/user_interaction.py | 47 +++++++------------------------------ 1 file changed, 9 insertions(+), 38 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 71ba2538..d8640a81 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -480,15 +480,7 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS): (The template xorg is for beginner users, not advanced, and should there for appeal to the general public first and edge cases later) """ - drivers = sorted(list(options)) - - if len(drivers) >= 1: - for index, driver in enumerate(drivers): - print(f"{index}: {driver}") - - print(' -- The above list are supported graphic card drivers. --') - print(' -- You need to select (and read about) which one you need. --') - + if len(options) >= 1: lspci = sys_command(f'/usr/bin/lspci') for line in lspci.trace_log.split(b'\r\n'): if b' vga ' in line.lower(): @@ -497,37 +489,16 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS): elif b'amd' in line.lower(): print(' ** AMD card detected, suggested driver: AMD / ATI **') - selected_driver = input('Select your graphics card driver: ') + selected_driver = generic_select(options, input_text="Select your graphics card driver: ", sort=True) initial_option = selected_driver - # Disabled search for now, only a few profiles exist anyway - # - #print(' -- You can enter ? or help to search for more drivers --') - #if selected_driver.lower() in ('?', 'help'): - # filter_string = input('Search for layout containing (example: "sv-"): ') - # new_options = search_keyboard_layout(filter_string) - # return select_language(new_options) - if selected_driver.isdigit() and (pos := int(selected_driver)) <= len(drivers)-1: - selected_driver = options[drivers[pos]] - elif selected_driver in options: - selected_driver = options[options.index(selected_driver)] - elif len(selected_driver) == 0: - raise RequirementError("At least one graphics driver is needed to support a graphical environment. Please restart the installer and try again.") - else: - raise RequirementError("Selected driver does not exist.") - - if type(selected_driver) == dict: - driver_options = sorted(list(selected_driver)) - for index, driver_package_group in enumerate(driver_options): - print(f"{index}: {driver_package_group}") - - selected_driver_package_group = input(f'Which driver-type do you want for {initial_option}: ') - if selected_driver_package_group.isdigit() and (pos := int(selected_driver_package_group)) <= len(driver_options)-1: - selected_driver_package_group = selected_driver[driver_options[pos]] - elif selected_driver_package_group in selected_driver: - selected_driver_package_group = selected_driver[selected_driver.index(selected_driver_package_group)] - elif len(selected_driver_package_group) == 0: - raise RequirementError(f"At least one driver package is required for a graphical environment using {selected_driver}. Please restart the installer and try again.") + if type(options[initial_option]) == dict: + driver_options = sorted(options[initial_option].keys()) + + selected_driver_package_group = generic_select(driver_options, input_text=f"Which driver-type do you want for {initial_option}: ") + if selected_driver_package_group in options[initial_option].keys(): + print(options[initial_option][selected_driver_package_group]) + selected_driver = options[initial_option][selected_driver_package_group] else: raise RequirementError(f"Selected driver-type does not exist for {initial_option}.") -- cgit v1.2.3-70-g09d2 From 090b98b8307fd924882e78b69df9227b4621ec6b Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Tue, 27 Apr 2021 14:43:17 +0000 Subject: Moving away from custom log levels, to something that's well defined. (#360) * Moving away from custom log levels, to something that's well defined. * Added backward compability to log() as well. * Added an option to force log messages out on screen even if the level is below the log level threashold. * Added force log messages when wrong notation is used. * Added some more length to the deprecated message * Swapped all log levels to use logging. instead. Co-authored-by: Anton Hvornum --- archinstall/lib/disk.py | 42 +++++++++++------------ archinstall/lib/general.py | 30 ++++++++--------- archinstall/lib/installer.py | 55 +++++++++++++++--------------- archinstall/lib/luks.py | 11 +++--- archinstall/lib/mirrors.py | 6 ++-- archinstall/lib/output.py | 67 +++++++++++++++++++++++++++---------- archinstall/lib/profiles.py | 6 ++-- archinstall/lib/user_interaction.py | 12 +++---- examples/guided.py | 14 ++++---- 9 files changed, 139 insertions(+), 104 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/disk.py b/archinstall/lib/disk.py index 67c2bdcd..8c7c6818 100644 --- a/archinstall/lib/disk.py +++ b/archinstall/lib/disk.py @@ -1,9 +1,9 @@ import glob, re, os, json, time, hashlib -import pathlib, traceback +import pathlib, traceback, logging from collections import OrderedDict from .exceptions import DiskError from .general import * -from .output import log, LOG_LEVELS +from .output import log from .storage import storage from .hardware import hasUEFI @@ -88,7 +88,7 @@ class BlockDevice(): 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['type']}", level=LOG_LEVELS.Debug) + log(f"Unknown blockdevice type for {self.path}: {self.info['type']}", level=logging.DEBUG) # if not stat.S_ISBLK(os.stat(full_path).st_mode): # raise DiskError(f'Selected disk "{full_path}" is not a block device.') @@ -129,7 +129,7 @@ class BlockDevice(): @property def uuid(self): - log(f'BlockDevice().uuid is untested!', level=LOG_LEVELS.Warning, fg='yellow') + log(f'BlockDevice().uuid is untested!', level=logging.WARNING, fg='yellow') """ Returns the disk UUID as returned by lsblk. This is more reliable than relying on /dev/disk/by-partuuid as @@ -222,8 +222,8 @@ class Partition(): @encrypted.setter def encrypted(self, value :bool): if value: - log(f'Marking {self} as encrypted: {value}', level=LOG_LEVELS.Debug) - log(f"Callstrack when marking the partition: {''.join(traceback.format_stack())}", level=LOG_LEVELS.Debug) + log(f'Marking {self} as encrypted: {value}', level=logging.DEBUG) + log(f"Callstrack when marking the partition: {''.join(traceback.format_stack())}", level=logging.DEBUG) self._encrypted = value @@ -240,7 +240,7 @@ class Partition(): return self.path def detect_inner_filesystem(self, password): - log(f'Trying to detect inner filesystem format on {self} (This might take a while)', level=LOG_LEVELS.Info) + log(f'Trying to detect inner filesystem format on {self} (This might take a while)', level=logging.INFO) from .luks import luks2 try: @@ -269,16 +269,16 @@ class Partition(): def safe_to_format(self): if self.allow_formatting is False: - log(f"Partition {self} is not marked for formatting.", level=LOG_LEVELS.Debug) + log(f"Partition {self} is not marked for formatting.", level=logging.DEBUG) return False elif self.target_mountpoint == '/boot': try: if self.has_content(): - log(f"Partition {self} is a boot partition and has content inside.", level=LOG_LEVELS.Debug) + log(f"Partition {self} is a boot partition and has content inside.", level=logging.DEBUG) return False except SysCallError as err: - log(err.message, LOG_LEVELS.Debug) - log(f"Partition {self} was identified as /boot but we could not mount to check for content, continuing!", level=LOG_LEVELS.Debug) + log(err.message, logging.DEBUG) + log(f"Partition {self} was identified as /boot but we could not mount to check for content, continuing!", level=logging.DEBUG) pass return True @@ -293,7 +293,7 @@ class Partition(): raise DiskError(f"Attempting to encrypt a partition that was not marked for encryption: {self}") if not self.safe_to_format(): - log(f"Partition {self} was marked as protected but encrypt() was called on it!", level=LOG_LEVELS.Error, fg="red") + log(f"Partition {self} was marked as protected but encrypt() was called on it!", level=logging.ERROR, fg="red") return False handle = luks2(self, None, None) @@ -321,7 +321,7 @@ class Partition(): raise PermissionError(f"{self} is not formatable either because instance is locked ({self.allow_formatting}) or a blocking flag was given ({allow_formatting})") if log_formatting: - log(f'Formatting {path} -> {filesystem}', level=LOG_LEVELS.Info) + log(f'Formatting {path} -> {filesystem}', level=logging.INFO) if filesystem == 'btrfs': o = b''.join(sys_command(f'/usr/bin/mkfs.btrfs -f {path}')) @@ -376,7 +376,7 @@ class Partition(): def mount(self, target, fs=None, options=''): if not self.mountpoint: - log(f'Mounting {self} to {target}', level=LOG_LEVELS.Info) + log(f'Mounting {self} to {target}', level=logging.INFO) if not fs: if not self.filesystem: raise DiskError(f'Need to format (or define) the filesystem on {self} before mounting.') fs = self.filesystem @@ -434,7 +434,7 @@ class Filesystem(): def __enter__(self, *args, **kwargs): if self.blockdevice.keep_partitions is False: - log(f'Wiping {self.blockdevice} by using partition format {self.mode}', level=LOG_LEVELS.Debug) + log(f'Wiping {self.blockdevice} by using partition format {self.mode}', level=logging.DEBUG) if self.mode == GPT: if self.raw_parted(f'{self.blockdevice.device} mklabel gpt').exit_code == 0: self.blockdevice.flush_cache() @@ -451,7 +451,7 @@ class Filesystem(): # TODO: partition_table_type is hardcoded to GPT at the moment. This has to be changed. elif self.mode == self.blockdevice.partition_table_type: - log(f'Kept partition format {self.mode} for {self.blockdevice}', level=LOG_LEVELS.Debug) + log(f'Kept partition format {self.mode} for {self.blockdevice}', level=logging.DEBUG) else: raise DiskError(f'The selected partition table format {self.mode} does not match that of {self.blockdevice}.') @@ -474,7 +474,7 @@ class Filesystem(): def raw_parted(self, string:str): x = sys_command(f'/usr/bin/parted -s {string}') - log(f"'parted -s {string}' returned: {b''.join(x)}", level=LOG_LEVELS.Debug) + log(f"'parted -s {string}' returned: {b''.join(x)}", level=logging.DEBUG) return x def parted(self, string:str): @@ -487,7 +487,7 @@ class Filesystem(): return self.raw_parted(string).exit_code def use_entire_disk(self, root_filesystem_type='ext4'): - log(f"Using and formatting the entire {self.blockdevice}.", level=LOG_LEVELS.Debug) + log(f"Using and formatting the entire {self.blockdevice}.", level=logging.DEBUG) if hasUEFI(): self.add_partition('primary', start='1MiB', end='513MiB', format='fat32') self.set_name(0, 'EFI') @@ -499,7 +499,7 @@ class Filesystem(): self.blockdevice.partition[0].filesystem = 'vfat' self.blockdevice.partition[1].filesystem = root_filesystem_type - log(f"Set the root partition {self.blockdevice.partition[1]} to use filesystem {root_filesystem_type}.", level=LOG_LEVELS.Debug) + log(f"Set the root partition {self.blockdevice.partition[1]} to use filesystem {root_filesystem_type}.", level=logging.DEBUG) self.blockdevice.partition[0].target_mountpoint = '/boot' self.blockdevice.partition[1].target_mountpoint = '/' @@ -510,12 +510,12 @@ class Filesystem(): #we don't need a seprate boot partition it would be a waste of space self.add_partition('primary', start='1MB', end='100%') self.blockdevice.partition[0].filesystem=root_filesystem_type - log(f"Set the root partition {self.blockdevice.partition[0]} to use filesystem {root_filesystem_type}.", level=LOG_LEVELS.Debug) + log(f"Set the root partition {self.blockdevice.partition[0]} to use filesystem {root_filesystem_type}.", level=logging.DEBUG) self.blockdevice.partition[0].target_mountpoint = '/' self.blockdevice.partition[0].allow_formatting = True def add_partition(self, type, start, end, format=None): - log(f'Adding partition to {self.blockdevice}', level=LOG_LEVELS.Info) + log(f'Adding partition to {self.blockdevice}', level=logging.INFO) previous_partitions = self.blockdevice.partitions if self.mode == MBR: diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py index dc0f018a..eb0c5d14 100644 --- a/archinstall/lib/general.py +++ b/archinstall/lib/general.py @@ -1,10 +1,10 @@ import os, json, hashlib, shlex, sys -import time, pty +import time, pty, logging from datetime import datetime, date from subprocess import Popen, STDOUT, PIPE, check_output from select import epoll, EPOLLIN, EPOLLHUP from .exceptions import * -from .output import log, LOG_LEVELS +from .output import log def gen_uid(entropy_length=256): return hashlib.sha512(os.urandom(entropy_length)).hexdigest() @@ -84,7 +84,7 @@ class sys_command():#Thread): self.log = kwargs.get('log', log) if kwargs['emulate']: - self.log(f"Starting command '{cmd}' in emulation mode.", level=LOG_LEVELS.Debug) + self.log(f"Starting command '{cmd}' in emulation mode.", level=logging.DEBUG) if type(cmd) is list: # if we get a list of arguments @@ -204,7 +204,7 @@ class sys_command():#Thread): os.execve(self.cmd[0], self.cmd, {**os.environ, **self.environment_vars}) except FileNotFoundError: self.status = 'done' - self.log(f"{self.cmd[0]} does not exist.", level=LOG_LEVELS.Debug) + self.log(f"{self.cmd[0]} does not exist.", level=logging.DEBUG) self.exit_code = 1 return False @@ -214,8 +214,8 @@ class sys_command():#Thread): poller.register(child_fd, EPOLLIN | EPOLLHUP) if 'events' in self.kwargs and 'debug' in self.kwargs: - self.log(f'[D] Using triggers for command: {self.cmd}', level=LOG_LEVELS.Debug) - self.log(json.dumps(self.kwargs['events']), level=LOG_LEVELS.Debug) + self.log(f'[D] Using triggers for command: {self.cmd}', level=logging.DEBUG) + self.log(json.dumps(self.kwargs['events']), level=logging.DEBUG) alive = True last_trigger_pos = 0 @@ -230,7 +230,7 @@ class sys_command():#Thread): break if 'debug' in self.kwargs and self.kwargs['debug'] and len(output): - self.log(self.cmd, 'gave:', output.decode('UTF-8'), level=LOG_LEVELS.Debug) + self.log(self.cmd, 'gave:', output.decode('UTF-8'), level=logging.DEBUG) if 'on_output' in self.kwargs: self.kwargs['on_output'](self.kwargs['worker'], output) @@ -251,8 +251,8 @@ class sys_command():#Thread): trigger_pos = self.trace_log[last_trigger_pos:].lower().find(trigger.lower()) if 'debug' in self.kwargs and self.kwargs['debug']: - self.log(f"Writing to subprocess {self.cmd[0]}: {self.kwargs['events'][trigger].decode('UTF-8')}", level=LOG_LEVELS.Debug) - self.log(f"Writing to subprocess {self.cmd[0]}: {self.kwargs['events'][trigger].decode('UTF-8')}", level=LOG_LEVELS.Debug) + self.log(f"Writing to subprocess {self.cmd[0]}: {self.kwargs['events'][trigger].decode('UTF-8')}", level=logging.DEBUG) + self.log(f"Writing to subprocess {self.cmd[0]}: {self.kwargs['events'][trigger].decode('UTF-8')}", level=logging.DEBUG) last_trigger_pos = trigger_pos os.write(child_fd, self.kwargs['events'][trigger]) @@ -266,18 +266,18 @@ class sys_command():#Thread): ## Adding a exit trigger: if len(self.kwargs['events']) == 0: if 'debug' in self.kwargs and self.kwargs['debug']: - self.log(f"Waiting for last command {self.cmd[0]} to finish.", level=LOG_LEVELS.Debug) + self.log(f"Waiting for last command {self.cmd[0]} to finish.", level=logging.DEBUG) if bytes(f']$'.lower(), 'UTF-8') in self.trace_log[0-len(f']$')-5:].lower(): if 'debug' in self.kwargs and self.kwargs['debug']: - self.log(f"{self.cmd[0]} has finished.", level=LOG_LEVELS.Debug) + self.log(f"{self.cmd[0]} has finished.", level=logging.DEBUG) alive = False break self.status = 'done' if 'debug' in self.kwargs and self.kwargs['debug']: - self.log(f"{self.cmd[0]} waiting for exit code.", level=LOG_LEVELS.Debug) + self.log(f"{self.cmd[0]} waiting for exit code.", level=logging.DEBUG) if not self.kwargs['emulate']: try: @@ -291,14 +291,14 @@ class sys_command():#Thread): self.exit_code = 0 if 'debug' in self.kwargs and self.kwargs['debug']: - self.log(f"{self.cmd[0]} got exit code: {self.exit_code}", level=LOG_LEVELS.Debug) + self.log(f"{self.cmd[0]} got exit code: {self.exit_code}", level=logging.DEBUG) if 'ignore_errors' in self.kwargs: self.exit_code = 0 if self.exit_code != 0 and not self.kwargs['suppress_errors']: - #self.log(self.trace_log.decode('UTF-8'), level=LOG_LEVELS.Debug) - #self.log(f"'{self.raw_cmd}' did not exit gracefully, exit code {self.exit_code}.", level=LOG_LEVELS.Error) + #self.log(self.trace_log.decode('UTF-8'), level=logging.DEBUG) + #self.log(f"'{self.raw_cmd}' did not exit gracefully, exit code {self.exit_code}.", level=logging.ERROR) raise SysCallError(message=f"{self.trace_log.decode('UTF-8')}\n'{self.raw_cmd}' did not exit gracefully (trace log above), exit code: {self.exit_code}", exit_code=self.exit_code) self.ended = time.time() diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index e2762603..04fe44c8 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -1,4 +1,5 @@ -import os, stat, time, shutil, pathlib, subprocess +import os, stat, time, shutil, pathlib +import subprocess, logging from .exceptions import * from .disk import * @@ -7,7 +8,7 @@ from .user_interaction import * from .profiles import Profile from .mirrors import * from .systemd import Networkd -from .output import log, LOG_LEVELS +from .output import log from .storage import storage from .hardware import * @@ -60,7 +61,7 @@ class Installer(): storage['session'] = self self.partitions = get_partitions_in_use(self.target) - def log(self, *args, level=LOG_LEVELS.Debug, **kwargs): + def log(self, *args, level=logging.DEBUG, **kwargs): """ 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. @@ -75,8 +76,8 @@ class Installer(): # 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(self.trace_log.decode('UTF-8'), level=LOG_LEVELS.Debug) - self.log(args[1], level=LOG_LEVELS.Error, fg='red') + #self.log(self.trace_log.decode('UTF-8'), level=logging.DEBUG) + self.log(args[1], level=logging.ERROR, fg='red') self.sync_log_to_install_medium() @@ -89,17 +90,17 @@ class Installer(): self.genfstab() if not (missing_steps := self.post_install_check()): - self.log('Installation completed without any errors. You may now reboot.', bg='black', fg='green', level=LOG_LEVELS.Info) + self.log('Installation completed without any errors. You may now reboot.', bg='black', 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:', bg='black', fg='red', level=LOG_LEVELS.Warning) + self.log('Some required steps were not successfully installed/configured before leaving the installer:', bg='black', fg='red', level=logging.WARNING) for step in missing_steps: - self.log(f' - {step}', bg='black', fg='red', level=LOG_LEVELS.Warning) + self.log(f' - {step}', bg='black', fg='red', level=logging.WARNING) - self.log(f"Detailed error logs can be found at: {storage['LOG_PATH']}", level=LOG_LEVELS.Warning) - self.log(f"Submit this zip file as an issue to https://github.com/archlinux/archinstall/issues", level=LOG_LEVELS.Warning) + self.log(f"Detailed error logs can be found at: {storage['LOG_PATH']}", level=logging.WARNING) + self.log(f"Submit this zip file as an issue to https://github.com/archlinux/archinstall/issues", level=logging.WARNING) self.sync_log_to_install_medium() return False @@ -129,21 +130,21 @@ class Installer(): def pacstrap(self, *packages, **kwargs): if type(packages[0]) in (list, tuple): packages = packages[0] - self.log(f'Installing packages: {packages}', level=LOG_LEVELS.Info) + self.log(f'Installing packages: {packages}', level=logging.INFO) if (sync_mirrors := sys_command('/usr/bin/pacman -Syy')).exit_code == 0: if (pacstrap := sys_command(f'/usr/bin/pacstrap {self.target} {" ".join(packages)}', **kwargs)).exit_code == 0: return True else: - self.log(f'Could not strap in packages: {pacstrap.exit_code}', level=LOG_LEVELS.Info) + self.log(f'Could not strap in packages: {pacstrap.exit_code}', level=logging.INFO) else: - self.log(f'Could not sync mirrors: {sync_mirrors.exit_code}', level=LOG_LEVELS.Info) + self.log(f'Could not sync mirrors: {sync_mirrors.exit_code}', level=logging.INFO) def set_mirrors(self, mirrors): return use_mirrors(mirrors, destination=f'{self.target}/etc/pacman.d/mirrorlist') def genfstab(self, flags='-pU'): - self.log(f"Updating {self.target}/etc/fstab", level=LOG_LEVELS.Info) + self.log(f"Updating {self.target}/etc/fstab", level=logging.INFO) fstab = sys_command(f'/usr/bin/genfstab {flags} {self.target}').trace_log with open(f"{self.target}/etc/fstab", 'ab') as fstab_fh: @@ -179,19 +180,19 @@ class Installer(): else: self.log( f"Time zone {zone} does not exist, continuing with system default.", - level=LOG_LEVELS.Warning, + level=logging.WARNING, fg='red' ) def activate_ntp(self): - self.log(f'Installing and activating NTP.', level=LOG_LEVELS.Info) + self.log(f'Installing and activating NTP.', level=logging.INFO) if self.pacstrap('ntp'): if self.enable_service('ntpd'): return True def enable_service(self, *services): for service in services: - self.log(f'Enabling service {service}', level=LOG_LEVELS.Info) + self.log(f'Enabling service {service}', level=logging.INFO) if (output := self.arch_chroot(f'systemctl enable {service}')).exit_code != 0: raise ServiceException(f"Unable to start service {service}: {output}") @@ -349,7 +350,7 @@ class Installer(): # Run registered post-install hooks for function in self.post_base_install: - self.log(f"Running post-installation hook: {function}", level=LOG_LEVELS.Info) + self.log(f"Running post-installation hook: {function}", level=logging.INFO) function(self) return True @@ -363,7 +364,7 @@ class Installer(): elif partition.mountpoint == self.target: root_partition = partition - self.log(f'Adding bootloader {bootloader} to {boot_partition}', level=LOG_LEVELS.Info) + self.log(f'Adding bootloader {bootloader} to {boot_partition}', level=logging.INFO) if bootloader == 'systemd-bootctl': if not hasUEFI(): @@ -417,10 +418,10 @@ class Installer(): if (real_device := self.detect_encryption(root_partition)): # 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}'.", level=LOG_LEVELS.Debug) + log(f"Identifying root partition by PART-UUID on {real_device}: '{real_device.uuid}'.", level=logging.DEBUG) entry.write(f'options cryptdevice=PARTUUID={real_device.uuid}:luksdev root=/dev/mapper/luksdev rw intel_pstate=no_hwp\n') else: - log(f"Identifying root partition by PART-UUID on {root_partition}, looking for '{root_partition.uuid}'.", level=LOG_LEVELS.Debug) + log(f"Identifying root partition by PART-UUID on {root_partition}, looking for '{root_partition.uuid}'.", level=logging.DEBUG) entry.write(f'options root=PARTUUID={root_partition.uuid} rw intel_pstate=no_hwp\n') self.helper_flags['bootloader'] = bootloader @@ -458,17 +459,17 @@ class Installer(): if type(profile) == str: profile = Profile(self, profile) - self.log(f'Installing network profile {profile}', level=LOG_LEVELS.Info) + self.log(f'Installing network profile {profile}', level=logging.INFO) return profile.install() def enable_sudo(self, entity :str, group=False): - self.log(f'Enabling sudo permissions for {entity}.', level=LOG_LEVELS.Info) + self.log(f'Enabling sudo permissions for {entity}.', level=logging.INFO) with open(f'{self.target}/etc/sudoers', 'a') as sudoers: sudoers.write(f'{"%" if group else ""}{entity} ALL=(ALL) ALL\n') return True def user_create(self, user :str, password=None, groups=[], sudo=False): - self.log(f'Creating user {user}', level=LOG_LEVELS.Info) + self.log(f'Creating user {user}', level=logging.INFO) o = b''.join(sys_command(f'/usr/bin/arch-chroot {self.target} useradd -m -G wheel {user}')) if password: self.user_set_pw(user, password) @@ -481,7 +482,7 @@ class Installer(): self.helper_flags['user'] = True def user_set_pw(self, user, password): - self.log(f'Setting password for {user}', level=LOG_LEVELS.Info) + self.log(f'Setting password for {user}', level=logging.INFO) if user == 'root': # This means the root account isn't locked/disabled with * in /etc/passwd @@ -491,7 +492,7 @@ class Installer(): pass def user_set_shell(self, user, shell): - self.log(f'Setting shell for {user} to {shell}', level=LOG_LEVELS.Info) + self.log(f'Setting shell for {user} to {shell}', level=logging.INFO) o = b''.join(sys_command(f"/usr/bin/arch-chroot {self.target} sh -c \"chsh -s {shell} {user}\"")) pass @@ -502,5 +503,5 @@ class Installer(): vconsole.write(f'KEYMAP={language}\n') vconsole.write(f'FONT=lat9w-16\n') else: - self.log(f'Keyboard language was not changed from default (no language specified).', fg="yellow", level=LOG_LEVELS.Info) + self.log(f'Keyboard language was not changed from default (no language specified).', fg="yellow", level=logging.INFO) return True diff --git a/archinstall/lib/luks.py b/archinstall/lib/luks.py index 894be1c8..7f8485e6 100644 --- a/archinstall/lib/luks.py +++ b/archinstall/lib/luks.py @@ -2,10 +2,11 @@ import os import shlex import time import pathlib +import logging from .exceptions import * from .general import * from .disk import Partition -from .output import log, LOG_LEVELS +from .output import log from .storage import storage class luks2(): @@ -48,7 +49,7 @@ class luks2(): if not self.partition.allow_formatting: raise DiskError(f'Could not encrypt volume {self.partition} due to it having a formatting lock.') - log(f'Encrypting {partition} (This might take a while)', level=LOG_LEVELS.Info) + log(f'Encrypting {partition} (This might take a while)', level=logging.INFO) if not key_file: if self.key_file: @@ -84,7 +85,7 @@ class luks2(): cmd_handle = sys_command(cryptsetup_args) except SysCallError as err: if err.exit_code == 256: - log(f'{partition} is being used, trying to unmount and crypt-close the device and running one more attempt at encrypting the device.', level=LOG_LEVELS.Debug) + 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() @@ -97,11 +98,11 @@ class luks2(): for child in children: # Unmount the child location if child_mountpoint := child.get('mountpoint', None): - log(f'Unmounting {child_mountpoint}', level=LOG_LEVELS.Debug) + log(f'Unmounting {child_mountpoint}', level=logging.DEBUG) sys_command(f"umount -R {child_mountpoint}") # And close it if possible. - log(f"Closing crypt device {child['name']}", level=LOG_LEVELS.Debug) + log(f"Closing crypt device {child['name']}", level=logging.DEBUG) sys_command(f"cryptsetup close {child['name']}") # Then try again to set up the crypt-device diff --git a/archinstall/lib/mirrors.py b/archinstall/lib/mirrors.py index 04f47c0d..ae6c6422 100644 --- a/archinstall/lib/mirrors.py +++ b/archinstall/lib/mirrors.py @@ -1,4 +1,4 @@ -import urllib.request +import urllib.request, logging from .exceptions import * from .general import * @@ -59,7 +59,7 @@ def insert_mirrors(mirrors, *args, **kwargs): return True def use_mirrors(regions :dict, destination='/etc/pacman.d/mirrorlist'): - log(f'A new package mirror-list has been created: {destination}', level=LOG_LEVELS.Info) + log(f'A new package mirror-list has been created: {destination}', level=logging.INFO) for region, mirrors in regions.items(): with open(destination, 'w') as mirrorlist: for mirror in mirrors: @@ -79,7 +79,7 @@ def list_mirrors(): try: response = urllib.request.urlopen(url) except urllib.error.URLError as err: - log(f'Could not fetch an active mirror-list: {err}', level=LOG_LEVELS.Warning, fg="yellow") + log(f'Could not fetch an active mirror-list: {err}', level=logging.WARNING, fg="yellow") return regions diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py index 6b184b4b..73819422 100644 --- a/archinstall/lib/output.py +++ b/archinstall/lib/output.py @@ -17,8 +17,32 @@ class LOG_LEVELS: class journald(dict): @abc.abstractmethod - def log(message, level=LOG_LEVELS.Debug): - import systemd.journal + def log(message, level=logging.DEBUG): + try: + import systemd.journal + except ModuleNotFoundError: + return False + + # For backwards compability, convert old style log-levels + # to logging levels (and warn about deprecated usage) + # There's some code re-usage here but that should be fine. + # TODO: Remove these in a few versions: + if level == LOG_LEVELS.Critical: + log("Deprecated level detected in log message, please use new logging. instead for the following log message:", fg="red", level=logging.ERROR, force=True) + level = logging.CRITICAL + elif level == LOG_LEVELS.Error: + log("Deprecated level detected in log message, please use new logging. instead for the following log message:", fg="red", level=logging.ERROR, force=True) + level = logging.ERROR + elif level == LOG_LEVELS.Warning: + log("Deprecated level detected in log message, please use new logging. instead for the following log message:", fg="red", level=logging.ERROR, force=True) + level = logging.WARNING + elif level == LOG_LEVELS.Info: + log("Deprecated level detected in log message, please use new logging. instead for the following log message:", fg="red", level=logging.ERROR, force=True) + level = logging.INFO + elif level == LOG_LEVELS.Debug: + log("Deprecated level detected in log message, please use new logging. instead for the following log message:", fg="red", level=logging.ERROR, force=True) + level = logging.DEBUG + log_adapter = logging.getLogger('archinstall') log_fmt = logging.Formatter("[%(levelname)s]: %(message)s") log_ch = systemd.journal.JournalHandler() @@ -26,19 +50,7 @@ class journald(dict): log_adapter.addHandler(log_ch) log_adapter.setLevel(logging.DEBUG) - if level == LOG_LEVELS.Critical: - log_adapter.critical(message) - elif level == LOG_LEVELS.Error: - log_adapter.error(message) - elif level == LOG_LEVELS.Warning: - log_adapter.warning(message) - elif level == LOG_LEVELS.Info: - log_adapter.info(message) - elif level == LOG_LEVELS.Debug: - log_adapter.debug(message) - else: - # Fallback logger - log_adapter.debug(message) + log_adapter.log(level, message) # TODO: Replace log() for session based logging. class SessionLogging(): @@ -112,17 +124,38 @@ def log(*args, **kwargs): with open(absolute_logfile, 'a') as log_file: log_file.write(f"{orig_string}\n") + # If we assigned a level, try to log it to systemd's journald. # Unless the level is higher than we've decided to output interactively. # (Remember, log files still get *ALL* the output despite level restrictions) if 'level' in kwargs: - if kwargs['level'] > storage.get('LOG_LEVEL', LOG_LEVELS.Info): + # For backwards compability, convert old style log-levels + # to logging levels (and warn about deprecated usage) + # There's some code re-usage here but that should be fine. + # TODO: Remove these in a few versions: + if kwargs['level'] == LOG_LEVELS.Critical: + log("Deprecated level detected in log message, please use new logging. instead for the following log message:", fg="red", level=logging.ERROR, force=True) + kwargs['level'] = logging.CRITICAL + elif kwargs['level'] == LOG_LEVELS.Error: + log("Deprecated level detected in log message, please use new logging. instead for the following log message:", fg="red", level=logging.ERROR, force=True) + kwargs['level'] = logging.ERROR + elif kwargs['level'] == LOG_LEVELS.Warning: + log("Deprecated level detected in log message, please use new logging. instead for the following log message:", fg="red", level=logging.ERROR, force=True) + kwargs['level'] = logging.WARNING + elif kwargs['level'] == LOG_LEVELS.Info: + log("Deprecated level detected in log message, please use new logging. instead for the following log message:", fg="red", level=logging.ERROR, force=True) + kwargs['level'] = logging.INFO + elif kwargs['level'] == LOG_LEVELS.Debug: + log("Deprecated level detected in log message, please use new logging. instead for the following log message:", fg="red", level=logging.ERROR, force=True) + kwargs['level'] = logging.DEBUG + + if kwargs['level'] > storage.get('LOG_LEVEL', logging.INFO) and not 'force' in kwargs: # Level on log message was Debug, but output level is set to Info. # In that case, we'll drop it. return None try: - journald.log(string, level=kwargs.get('level', LOG_LEVELS.Info)) + journald.log(string, level=kwargs.get('level', logging.INFO)) except ModuleNotFoundError: pass # Ignore writing to journald diff --git a/archinstall/lib/profiles.py b/archinstall/lib/profiles.py index 265ca26a..4988e7ab 100644 --- a/archinstall/lib/profiles.py +++ b/archinstall/lib/profiles.py @@ -1,10 +1,10 @@ import os, urllib.request, urllib.parse, ssl, json, re -import importlib.util, sys, glob, hashlib +import importlib.util, sys, glob, hashlib, logging from collections import OrderedDict from .general import multisplit, sys_command from .exceptions import * from .networking import * -from .output import log, LOG_LEVELS +from .output import log from .storage import storage def grab_url_data(path): @@ -82,7 +82,7 @@ class Script(): self.examples = None self.namespace = os.path.splitext(os.path.basename(self.path))[0] self.original_namespace = self.namespace - log(f"Script {self} has been loaded with namespace '{self.namespace}'", level=LOG_LEVELS.Debug) + log(f"Script {self} has been loaded with namespace '{self.namespace}'", level=logging.DEBUG) def __enter__(self, *args, **kwargs): self.execute() diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index d8640a81..79b0e0b9 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -1,9 +1,9 @@ import getpass, pathlib, os, shutil, re -import sys, time, signal, ipaddress +import sys, time, signal, ipaddress, logging from .exceptions import * from .profiles import Profile from .locale_helpers import list_keyboard_languages, verify_keyboard_layout, search_keyboard_layout -from .output import log, LOG_LEVELS +from .output import log from .storage import storage from .networking import list_interfaces from .general import sys_command @@ -26,7 +26,7 @@ def check_for_correct_username(username): return True log( "The username you entered is invalid. Try again", - level=LOG_LEVELS.Warning, + level=logging.WARNING, fg='red' ) return False @@ -141,7 +141,7 @@ def ask_for_a_timezone(): else: log( f"Specified timezone {timezone} does not exist.", - level=LOG_LEVELS.Warning, + level=logging.WARNING, fg='red' ) @@ -198,7 +198,7 @@ def ask_to_configure_network(): except ValueError: log( "You need to enter a valid IP in IP-config mode.", - level=LOG_LEVELS.Warning, + level=logging.WARNING, fg='red' ) @@ -214,7 +214,7 @@ def ask_to_configure_network(): except ValueError: log( "You need to enter a valid gateway (router) IP address.", - level=LOG_LEVELS.Warning, + level=logging.WARNING, fg='red' ) diff --git a/examples/guided.py b/examples/guided.py index 43aaa788..0e6f2904 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -1,4 +1,4 @@ -import getpass, time, json, os +import getpass, time, json, os, logging import archinstall from archinstall.lib.hardware import hasUEFI from archinstall.lib.profiles import Profile @@ -237,8 +237,8 @@ def ask_user_questions(): def perform_installation_steps(): print() print('This is your chosen configuration:') - archinstall.log("-- Guided template chosen (with below config) --", level=archinstall.LOG_LEVELS.Debug) - archinstall.log(json.dumps(archinstall.arguments, indent=4, sort_keys=True, cls=archinstall.JSON), level=archinstall.LOG_LEVELS.Info) + archinstall.log("-- Guided template chosen (with below config) --", level=logging.DEBUG) + archinstall.log(json.dumps(archinstall.arguments, indent=4, sort_keys=True, cls=archinstall.JSON), level=logging.INFO) print() input('Press Enter to continue.') @@ -282,7 +282,7 @@ def perform_installation_steps(): else: partition.format() else: - archinstall.log(f"Did not format {partition} because .safe_to_format() returned False or .allow_formatting was False.", level=archinstall.LOG_LEVELS.Debug) + archinstall.log(f"Did not format {partition} because .safe_to_format() returned False or .allow_formatting was False.", level=logging.DEBUG) if hasUEFI(): fs.find_partition('/boot').format('vfat')# we don't have a boot partition in bios mode @@ -313,7 +313,7 @@ def perform_installation(mountpoint): # 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(f'Waiting for automatic mirror selection (reflector) to complete.', level=archinstall.LOG_LEVELS.Info) + installation.log(f'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) @@ -342,7 +342,7 @@ def perform_installation(mountpoint): installation.enable_service('systemd-resolved') if archinstall.arguments.get('audio', None) != None: - installation.log(f"This audio server will be used: {archinstall.arguments.get('audio', None)}", level=archinstall.LOG_LEVELS.Info) + installation.log(f"This audio server will be used: {archinstall.arguments.get('audio', None)}", level=logging.INFO) if archinstall.arguments.get('audio', None) == 'pipewire': print('Installing pipewire ...') @@ -351,7 +351,7 @@ def perform_installation(mountpoint): print('Installing pulseaudio ...') installation.add_additional_packages("pulseaudio") else: - installation.log("No audio server will be installed.", level=archinstall.LOG_LEVELS.Info) + 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)) -- cgit v1.2.3-70-g09d2 From bedd8a1241eab7773c160d164ae0a27d002998b0 Mon Sep 17 00:00:00 2001 From: Владислав Date: Tue, 27 Apr 2021 21:26:58 +0300 Subject: Fixed a crash before choosing a video card driver --- archinstall/lib/user_interaction.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 79b0e0b9..5cfb0a1b 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -480,7 +480,10 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS): (The template xorg is for beginner users, not advanced, and should there for appeal to the general public first and edge cases later) """ - if len(options) >= 1: + + drivers = sorted(list(options)) + + if drivers: lspci = sys_command(f'/usr/bin/lspci') for line in lspci.trace_log.split(b'\r\n'): if b' vga ' in line.lower(): @@ -489,20 +492,17 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS): elif b'amd' in line.lower(): print(' ** AMD card detected, suggested driver: AMD / ATI **') - selected_driver = generic_select(options, input_text="Select your graphics card driver: ", sort=True) - initial_option = selected_driver + initial_option = generic_select(drivers, input_text="Select your graphics card driver: ") + selected_driver = options[initial_option] - if type(options[initial_option]) == dict: - driver_options = sorted(options[initial_option].keys()) + if type(selected_driver) == dict: + driver_options = sorted(list(selected_driver)) - selected_driver_package_group = generic_select(driver_options, input_text=f"Which driver-type do you want for {initial_option}: ") - if selected_driver_package_group in options[initial_option].keys(): - print(options[initial_option][selected_driver_package_group]) - selected_driver = options[initial_option][selected_driver_package_group] - else: - raise RequirementError(f"Selected driver-type does not exist for {initial_option}.") + driver_package_group = generic_select(driver_options, f'Which driver-type do you want for {initial_option}: ', + allow_empty_input=False) + driver_package_group = selected_driver[driver_package_group] - return selected_driver_package_group + return driver_package_group return selected_driver -- cgit v1.2.3-70-g09d2 From 6cfaf30718c3c0281553a7afd890fcfca342f2bc Mon Sep 17 00:00:00 2001 From: SecondThundeR Date: Wed, 28 Apr 2021 09:41:47 +0300 Subject: Update kernel select Move select to separate function Remove 'continue' option Add hardened kernel as option --- archinstall/lib/user_interaction.py | 23 +++++++++++++++++++++++ examples/guided.py | 11 +++-------- 2 files changed, 26 insertions(+), 8 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index cd848136..8d5c9994 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -506,3 +506,26 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS): return selected_driver raise RequirementError("Selecting drivers require a least one profile to be given as an option.") + +def select_kernel(options): + """ + Asks the user to select a kernel for system. + + :param options: A `list` with kernel options + :type options: list + + :return: The string as a selected kernel + :rtype: string + """ + + DEFAULT_KERNEL = "linux" + + kernels = sorted(list(options)) + + if kernels: + selected_kernels = generic_select(kernels, "Choose a kernel: ") + if not selected_kernels: + return DEFAULT_KERNEL + return selected_kernels + + raise RequirementError("Selecting kernels require a least one kernel to be given as an option.") diff --git a/examples/guided.py b/examples/guided.py index 1ff84907..2b8a06f5 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -2,7 +2,6 @@ import getpass, time, json, os, logging import archinstall from archinstall.lib.hardware import hasUEFI from archinstall.lib.profiles import Profile -from archinstall.lib.user_interaction import generic_select if archinstall.arguments.get('help'): print("See `man archinstall` for help.") @@ -195,14 +194,10 @@ def ask_user_questions(): # we will not try to remove packages post-installation to not have audio, as that may cause multiple issues archinstall.arguments['audio'] = None - # Ask what kernel user wants: + # Ask for preferred kernel: if not archinstall.arguments.get("kernels", None): - archinstall.log(f"Here you can choose which kernel to use, leave blank for default which is 'linux'.") - - if (kernel := generic_select(["linux", "linux-lts", "linux-zen", "continue"], "choose a kernel:")): - archinstall.arguments['kernels'] = kernel - else: - archinstall.arguments['kernels'] = 'linux' + kernels = ["linux", "linux-lts", "linux-zen", "linux-hardened"] + archinstall.arguments['kernels'] = archinstall.select_kernel(kernels) # 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.") -- cgit v1.2.3-70-g09d2 From 66197f08de883bcedcb1b596e4591db82290c322 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Wed, 28 Apr 2021 09:10:29 +0200 Subject: Added a default description --- archinstall/lib/user_interaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 8d5c9994..d08d7e25 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -523,7 +523,7 @@ def select_kernel(options): kernels = sorted(list(options)) if kernels: - selected_kernels = generic_select(kernels, "Choose a kernel: ") + selected_kernels = generic_select(kernels, f"Choose which kernel to use (leave blank for default: {DEFAULT_KERNEL}): ") if not selected_kernels: return DEFAULT_KERNEL return selected_kernels -- cgit v1.2.3-70-g09d2 From 4079eebc70af3dff4c5f3071c397697283a6890c Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Wed, 28 Apr 2021 13:18:28 +0000 Subject: Added a mini curses class and generic-multi-select (#362) * Added a mini curses class. It can do some simple tricks to iterate over menu options and indicate which ones are chosen using generic_multi_select(). * Include the default parameter if set. * Modified 'select_kernel()' to use the new multi-select. * Sneaky character got in. * removed some debugging * removed some debugging * Spelling error * Adding error handling and loop support. * Enforce that 'default' is always selected if no other option is selected. * Fixed backspace issues and ghosting. Co-authored-by: Anton Hvornum --- archinstall/lib/disk.py | 3 - archinstall/lib/profiles.py | 1 - archinstall/lib/user_interaction.py | 175 ++++++++++++++++++++++++++++++++++-- 3 files changed, 170 insertions(+), 9 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/disk.py b/archinstall/lib/disk.py index 8c7c6818..ff924f62 100644 --- a/archinstall/lib/disk.py +++ b/archinstall/lib/disk.py @@ -221,9 +221,6 @@ class Partition(): @encrypted.setter def encrypted(self, value :bool): - if value: - log(f'Marking {self} as encrypted: {value}', level=logging.DEBUG) - log(f"Callstrack when marking the partition: {''.join(traceback.format_stack())}", level=logging.DEBUG) self._encrypted = value diff --git a/archinstall/lib/profiles.py b/archinstall/lib/profiles.py index 4988e7ab..ad5d3bac 100644 --- a/archinstall/lib/profiles.py +++ b/archinstall/lib/profiles.py @@ -82,7 +82,6 @@ class Script(): self.examples = None self.namespace = os.path.splitext(os.path.basename(self.path))[0] self.original_namespace = self.namespace - log(f"Script {self} has been loaded with namespace '{self.namespace}'", level=logging.DEBUG) def __enter__(self, *args, **kwargs): self.execute() diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index d08d7e25..451251cd 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -1,5 +1,6 @@ -import getpass, pathlib, os, shutil, re +import getpass, pathlib, os, shutil, re, time import sys, time, signal, ipaddress, logging +import termios, tty, select # Used for char by char polling of sys.stdin from .exceptions import * from .profiles import Profile from .locale_helpers import list_keyboard_languages, verify_keyboard_layout, search_keyboard_layout @@ -95,6 +96,173 @@ def print_large_list(options, padding=5, margin_bottom=0, separator=': '): print(f"{str(column): >{highest_index_number_length}}{separator}{options[column]}", end = spaces) print() + return column, row + + +def generic_multi_select(options, text="Select one or more of the options above (leave blank to continue): ", sort=True, default=None, allow_empty=False): + if sort: + options = sorted(options) + + section = MiniCurses(get_terminal_width(), len(options)) + + selected_options = [] + + while True: + if len(selected_options) <= 0 and default and default in options: + selected_options.append(default) + + printed_options = [] + for option in options: + if option in selected_options: + printed_options.append(f'>> {option}') + else: + printed_options.append(f'{option}') + + section.clear(0, get_terminal_height()-section._cursor_y-1) + x, y = print_large_list(printed_options, margin_bottom=2) + section._cursor_y = len(printed_options) + section._cursor_x = 0 + section.write_line(text) + section.input_pos = section._cursor_x + selected_option = section.get_keyboard_input(end=None) + + if selected_option is None: + if len(selected_options) <= 0 and default: + selected_options = [default] + + if len(selected_options) or allow_empty is True: + break + else: + log('* Need to select at least one option!', fg='red') + continue + + elif selected_option.isdigit(): + if (selected_option := int(selected_option)) >= len(options): + log('* Option is out of range, please select another one!', fg='red') + continue + selected_option = options[selected_option] + if selected_option in selected_options: + selected_options.remove(selected_option) + else: + selected_options.append(selected_option) + + return selected_options + + +class MiniCurses(): + def __init__(self, width, height): + self.width = width + self.height = height + + self._cursor_y = 0 + self._cursor_x = 0 + + self.input_pos = 0 + + def write_line(self, text, clear_line=True): + if clear_line: + sys.stdout.flush() + sys.stdout.write("\033[%dG" % 0) + sys.stdout.flush() + sys.stdout.write(" " * (get_terminal_width()-1)) + sys.stdout.flush() + sys.stdout.write("\033[%dG" % 0) + sys.stdout.flush() + sys.stdout.write(text) + sys.stdout.flush() + self._cursor_x += len(text) + + def clear(self, x, y): + if x < 0: x = 0 + if y < 0: y = 0 + + #import time + #sys.stdout.write(f"Clearing from: {x, y}") + #sys.stdout.flush() + #time.sleep(2) + + sys.stdout.flush() + sys.stdout.write('\033[%d;%df' % (y, x)) + for line in range(get_terminal_height()-y-1, y): + sys.stdout.write(" " * (get_terminal_width()-1)) + sys.stdout.flush() + sys.stdout.write('\033[%d;%df' % (y, x)) + sys.stdout.flush() + + def deal_with_control_characters(self, char): + mapper = { + '\x7f' : 'BACKSPACE', + '\r' : 'CR', + '\n' : 'NL' + } + + if (mapped_char := mapper.get(char, None)) == 'BACKSPACE': + if self._cursor_x <= self.input_pos: + # Don't backspace futher back than the cursor start position during input + return True + # Move back to the current known position (BACKSPACE doesn't updated x-pos) + sys.stdout.flush() + sys.stdout.write("\033[%dG" % (self._cursor_x)) + sys.stdout.flush() + + # Write a blank space + sys.stdout.flush() + sys.stdout.write(" ") + sys.stdout.flush() + + # And move back again + sys.stdout.flush() + sys.stdout.write("\033[%dG" % (self._cursor_x)) + sys.stdout.flush() + + self._cursor_x -= 1 + + return True + elif mapped_char in ('CR', 'NL'): + return True + + return None + + def get_keyboard_input(self, strip_rowbreaks=True, end='\n'): + assert end in ['\r', '\n', None] + + poller = select.epoll() + response = '' + + sys_fileno = sys.stdin.fileno() + old_settings = termios.tcgetattr(sys_fileno) + tty.setraw(sys_fileno) + + poller.register(sys.stdin.fileno(), select.EPOLLIN) + + EOF = False + while EOF is False: + for fileno, event in poller.poll(0.025): + char = sys.stdin.read(1) + + #sys.stdout.write(f"{[char]}") + #sys.stdout.flush() + + if (newline := (char in ('\n', '\r'))): + EOF = True + + if not newline or strip_rowbreaks is False: + response += char + + if self.deal_with_control_characters(char) is not True: + self.write_line(response[-1], clear_line=False) + + termios.tcsetattr(sys_fileno, termios.TCSADRAIN, old_settings) + + if end: + sys.stdout.write(end) + sys.stdout.flush() + self._cursor_x = 0 + self._cursor_y += 1 + + if response: + return response + def ask_for_superuser_account(prompt='Username for required super-user with sudo privileges: ', forced=False): while 1: new_user = input(prompt).strip(' ') @@ -523,9 +691,6 @@ def select_kernel(options): kernels = sorted(list(options)) if kernels: - selected_kernels = generic_select(kernels, f"Choose which kernel to use (leave blank for default: {DEFAULT_KERNEL}): ") - if not selected_kernels: - return DEFAULT_KERNEL - return selected_kernels + return generic_multi_select(kernels, f"Choose which kernel to use (leave blank for default: {DEFAULT_KERNEL}): ", default=DEFAULT_KERNEL) raise RequirementError("Selecting kernels require a least one kernel to be given as an option.") -- cgit v1.2.3-70-g09d2 From feae13ac84e10cdf624cb4b3b19ba4707a96dabd Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Fri, 30 Apr 2021 22:34:54 -0400 Subject: Tweak wording for superuser prompt a little bit --- archinstall/lib/user_interaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 451251cd..ac9ceefb 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -292,7 +292,7 @@ def ask_for_additional_users(prompt='Any additional users to install (leave blan continue password = get_password(prompt=f'Password for user {new_user}: ') - if input("Should this user be a sudo (super) user (y/N): ").strip(' ').lower() in ('y', 'yes'): + if input("Should this user be a superuser (sudoer) [y/N]: ").strip(' ').lower() in ('y', 'yes'): super_users[new_user] = {"!password" : password} else: users[new_user] = {"!password" : password} -- cgit v1.2.3-70-g09d2 From 4efa01c4fd1b14103f2690a92c7195bd6bb6fe42 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Fri, 30 Apr 2021 22:42:08 -0400 Subject: Make the style of the word superuser consistent --- archinstall/lib/user_interaction.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'archinstall/lib/user_interaction.py') diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index ac9ceefb..be01594e 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -263,14 +263,14 @@ class MiniCurses(): if response: return response -def ask_for_superuser_account(prompt='Username for required super-user with sudo privileges: ', forced=False): +def ask_for_superuser_account(prompt='Username for required superuser with sudo privileges: ', forced=False): while 1: new_user = input(prompt).strip(' ') if not new_user and forced: # TODO: make this text more generic? # It's only used to create the first sudo user when root is disabled in guided.py - log(' * Since root is disabled, you need to create a least one (super) user!', fg='red') + log(' * Since root is disabled, you need to create a least one superuser!', fg='red') continue elif not new_user and not forced: raise UserError("No superuser was created.") @@ -282,7 +282,7 @@ def ask_for_superuser_account(prompt='Username for required super-user with sudo def ask_for_additional_users(prompt='Any additional users to install (leave blank for no users): '): users = {} - super_users = {} + superusers = {} while 1: new_user = input(prompt).strip(' ') @@ -293,11 +293,11 @@ def ask_for_additional_users(prompt='Any additional users to install (leave blan password = get_password(prompt=f'Password for user {new_user}: ') if input("Should this user be a superuser (sudoer) [y/N]: ").strip(' ').lower() in ('y', 'yes'): - super_users[new_user] = {"!password" : password} + superusers[new_user] = {"!password" : password} else: users[new_user] = {"!password" : password} - return users, super_users + return users, superusers def ask_for_a_timezone(): while True: -- cgit v1.2.3-70-g09d2