From 057f9cd0281e7dd7fcda0c6d7b1927b23d33b0ce Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Fri, 9 Apr 2021 09:52:02 -0400 Subject: Delete 52-54-00-12-34-56.py --- profiles/52-54-00-12-34-56.py | 56 ------------------------------------------- 1 file changed, 56 deletions(-) delete mode 100644 profiles/52-54-00-12-34-56.py diff --git a/profiles/52-54-00-12-34-56.py b/profiles/52-54-00-12-34-56.py deleted file mode 100644 index 679c6721..00000000 --- a/profiles/52-54-00-12-34-56.py +++ /dev/null @@ -1,56 +0,0 @@ -import archinstall -import json -import urllib.request -import git - -# Unmount and close previous runs (Mainly only used for re-runs, but won't hurt.) -archinstall.sys_command(f'umount -R /mnt', suppress_errors=True) -archinstall.sys_command(f'cryptsetup close /dev/mapper/luksloop', suppress_errors=True) - -# Select a harddrive and a disk password -harddrive = archinstall.all_disks()['/dev/sda'] -disk_password = '1234' - -with archinstall.Filesystem(harddrive, archinstall.GPT) as fs: - # Use the entire disk instead of setting up partitions on your own - fs.use_entire_disk('luks2') - - if harddrive.partition[1].size == '512M': - raise OSError('Trying to encrypt the boot partition for petes sake..') - harddrive.partition[0].format('fat32') - - with archinstall.luks2(harddrive.partition[1], 'luksloop', disk_password) as unlocked_device: - unlocked_device.format('btrfs') - - with archinstall.Installer( - unlocked_device, - boot_partition=harddrive.partition[0], - hostname="testmachine" - ) as installation: - if installation.minimal_installation(): - installation.add_bootloader() - - installation.add_additional_packages(['nano', 'wget', 'git']) - installation.install_profile('awesome') - - installation.user_create('anton', 'test') - installation.user_set_pw('root', 'toor') - - repo = git.Repo('./') - commit = repo.head.commit.hexsha[:7] - - print(f'Submitting {commit}: success') - - conditions = { - "project": "archinstall", - "profile": "52-54-00-12-34-56", - "status": "success", - "commit": commit - } - req = urllib.request.Request("https://api.archlinux.life/build/success", - data=json.dumps(conditions).encode('utf8'), - headers={'content-type': 'application/json'}) - try: - urllib.request.urlopen(req, timeout=5) - except: - pass \ No newline at end of file -- cgit v1.2.3-54-g00ecf From 8f48e1ac697b00eff4d2d6e3cbcd7686ecc3bd3f Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Fri, 9 Apr 2021 09:55:16 -0400 Subject: Update README.md --- README.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/README.md b/README.md index 2f3881e1..8285d7e3 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,69 @@ This installer will perform the following: > **Creating your own ISO with this script on it:** Follow [ArchISO](https://wiki.archlinux.org/index.php/archiso)'s guide on how to create your own ISO or use a pre-built [guided ISO](https://hvornum.se/archiso/) to skip the python installation step, or to create auto-installing ISO templates. Further down are examples and cheat sheets on how to create different live ISO's. +## Unattended installation based on MAC address + +It is possible to add a file in the profiles directory that will automatically run when launching archinstall using the unattended mode if the MAC address of the system matches the profile name. In the following example, this profile would go into a file called `profiles/52-54-00-12-34-56.py`: + +``` +import archinstall +import json +import urllib.request +import git + +# Unmount and close previous runs (Mainly only used for re-runs, but won't hurt.) +archinstall.sys_command(f'umount -R /mnt', suppress_errors=True) +archinstall.sys_command(f'cryptsetup close /dev/mapper/luksloop', suppress_errors=True) + +# Select a harddrive and a disk password +harddrive = archinstall.all_disks()['/dev/sda'] +disk_password = '1234' + +with archinstall.Filesystem(harddrive, archinstall.GPT) as fs: + # Use the entire disk instead of setting up partitions on your own + fs.use_entire_disk('luks2') + + if harddrive.partition[1].size == '512M': + raise OSError('Trying to encrypt the boot partition for petes sake..') + harddrive.partition[0].format('fat32') + + with archinstall.luks2(harddrive.partition[1], 'luksloop', disk_password) as unlocked_device: + unlocked_device.format('btrfs') + + with archinstall.Installer( + unlocked_device, + boot_partition=harddrive.partition[0], + hostname="testmachine" + ) as installation: + if installation.minimal_installation(): + installation.add_bootloader() + + installation.add_additional_packages(['nano', 'wget', 'git']) + installation.install_profile('minimum') + + installation.user_create('devel', 'devel') + installation.user_set_pw('root', 'toor') + + repo = git.Repo('./') + commit = repo.head.commit.hexsha[:7] + + print(f'Submitting {commit}: success') + + conditions = { + "project": "archinstall", + "profile": "52-54-00-12-34-56", + "status": "success", + "commit": commit + } + req = urllib.request.Request("https://api.archlinux.life/build/success", + data=json.dumps(conditions).encode('utf8'), + headers={'content-type': 'application/json'}) + try: + urllib.request.urlopen(req, timeout=5) + except: + pass +``` + # Help Submit an issue on Github, or submit a post in the discord help channel.
-- cgit v1.2.3-54-g00ecf From 4904b70db69f3c4f155524e7ad1c95a15337ddca Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Fri, 9 Apr 2021 10:18:34 -0400 Subject: Update README.md --- README.md | 61 +------------------------------------------------------------ 1 file changed, 1 insertion(+), 60 deletions(-) diff --git a/README.md b/README.md index 8285d7e3..b569b8eb 100644 --- a/README.md +++ b/README.md @@ -68,66 +68,7 @@ This installer will perform the following: ## Unattended installation based on MAC address -It is possible to add a file in the profiles directory that will automatically run when launching archinstall using the unattended mode if the MAC address of the system matches the profile name. In the following example, this profile would go into a file called `profiles/52-54-00-12-34-56.py`: - -``` -import archinstall -import json -import urllib.request -import git - -# Unmount and close previous runs (Mainly only used for re-runs, but won't hurt.) -archinstall.sys_command(f'umount -R /mnt', suppress_errors=True) -archinstall.sys_command(f'cryptsetup close /dev/mapper/luksloop', suppress_errors=True) - -# Select a harddrive and a disk password -harddrive = archinstall.all_disks()['/dev/sda'] -disk_password = '1234' - -with archinstall.Filesystem(harddrive, archinstall.GPT) as fs: - # Use the entire disk instead of setting up partitions on your own - fs.use_entire_disk('luks2') - - if harddrive.partition[1].size == '512M': - raise OSError('Trying to encrypt the boot partition for petes sake..') - harddrive.partition[0].format('fat32') - - with archinstall.luks2(harddrive.partition[1], 'luksloop', disk_password) as unlocked_device: - unlocked_device.format('btrfs') - - with archinstall.Installer( - unlocked_device, - boot_partition=harddrive.partition[0], - hostname="testmachine" - ) as installation: - if installation.minimal_installation(): - installation.add_bootloader() - - installation.add_additional_packages(['nano', 'wget', 'git']) - installation.install_profile('minimum') - - installation.user_create('devel', 'devel') - installation.user_set_pw('root', 'toor') - - repo = git.Repo('./') - commit = repo.head.commit.hexsha[:7] - - print(f'Submitting {commit}: success') - - conditions = { - "project": "archinstall", - "profile": "52-54-00-12-34-56", - "status": "success", - "commit": commit - } - req = urllib.request.Request("https://api.archlinux.life/build/success", - data=json.dumps(conditions).encode('utf8'), - headers={'content-type': 'application/json'}) - try: - urllib.request.urlopen(req, timeout=5) - except: - pass -``` +It is possible to automatically run a custom profile when launching archinstall in unattended mode based on the MAC address of the system. There is an example in the docs which would go into a file called `profiles/52-54-00-12-34-56.py`, and would be run on a sytem where the MAC address matched this profile name. # Help -- cgit v1.2.3-54-g00ecf From 1f1f269e933628233c1b25787ae449c51b7d66bd Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Fri, 9 Apr 2021 16:23:06 +0200 Subject: Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b569b8eb..1d96886f 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ This installer will perform the following: ## Unattended installation based on MAC address -It is possible to automatically run a custom profile when launching archinstall in unattended mode based on the MAC address of the system. There is an example in the docs which would go into a file called `profiles/52-54-00-12-34-56.py`, and would be run on a sytem where the MAC address matched this profile name. +Archinstall comes with a [unattended](examples/unattended.py) example which will look for a matching profile for the machine it is being run on, based on any local MAC address. For instance, if the machine that [unattended](examples/unattended.py) is run on has the MAC address `52:54:00:12:34:56` it will look for a profile called [profiles/52-54-00-12-34-56.py](profiles/52-54-00-12-34-56.py). If it's found, the unattended installation will commence and source that profile as it's installation proceedure. # Help -- cgit v1.2.3-54-g00ecf From 49ca26479095117f202bd7a3f95e8b93da02760e Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Sun, 11 Apr 2021 20:38:24 -0400 Subject: Change from argon2i to argon2id Closes #269 --- archinstall/lib/luks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archinstall/lib/luks.py b/archinstall/lib/luks.py index ca077b3d..894be1c8 100644 --- a/archinstall/lib/luks.py +++ b/archinstall/lib/luks.py @@ -70,7 +70,7 @@ class luks2(): '--batch-mode', '--verbose', '--type', 'luks2', - '--pbkdf', 'argon2i', + '--pbkdf', 'argon2id', '--hash', hash_type, '--key-size', str(key_size), '--iter-time', str(iter_time), -- cgit v1.2.3-54-g00ecf 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(-) 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-54-g00ecf 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(-) 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-54-g00ecf From 3fba7873968fff347e5304748c370bb5d8fc6662 Mon Sep 17 00:00:00 2001 From: Владислав Date: Sat, 17 Apr 2021 15:49:58 +0300 Subject: Fix typo in partition.format() --- examples/guided.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/guided.py b/examples/guided.py index c0d22023..7bf088ca 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -106,7 +106,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.") -- cgit v1.2.3-54-g00ecf 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(-) 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-54-g00ecf 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(-) 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-54-g00ecf 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(-) 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-54-g00ecf 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(-) 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-54-g00ecf 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(-) 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-54-g00ecf 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(-) 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-54-g00ecf 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(-) 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-54-g00ecf From 1d1d8ea2e09c7fe8e6d6fc80ae98d3cb36c292ae Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Wed, 21 Apr 2021 13:20:41 +0200 Subject: Reverting deletion on mac-template example. --- profiles/52-54-00-12-34-56.py | 56 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 profiles/52-54-00-12-34-56.py diff --git a/profiles/52-54-00-12-34-56.py b/profiles/52-54-00-12-34-56.py new file mode 100644 index 00000000..679c6721 --- /dev/null +++ b/profiles/52-54-00-12-34-56.py @@ -0,0 +1,56 @@ +import archinstall +import json +import urllib.request +import git + +# Unmount and close previous runs (Mainly only used for re-runs, but won't hurt.) +archinstall.sys_command(f'umount -R /mnt', suppress_errors=True) +archinstall.sys_command(f'cryptsetup close /dev/mapper/luksloop', suppress_errors=True) + +# Select a harddrive and a disk password +harddrive = archinstall.all_disks()['/dev/sda'] +disk_password = '1234' + +with archinstall.Filesystem(harddrive, archinstall.GPT) as fs: + # Use the entire disk instead of setting up partitions on your own + fs.use_entire_disk('luks2') + + if harddrive.partition[1].size == '512M': + raise OSError('Trying to encrypt the boot partition for petes sake..') + harddrive.partition[0].format('fat32') + + with archinstall.luks2(harddrive.partition[1], 'luksloop', disk_password) as unlocked_device: + unlocked_device.format('btrfs') + + with archinstall.Installer( + unlocked_device, + boot_partition=harddrive.partition[0], + hostname="testmachine" + ) as installation: + if installation.minimal_installation(): + installation.add_bootloader() + + installation.add_additional_packages(['nano', 'wget', 'git']) + installation.install_profile('awesome') + + installation.user_create('anton', 'test') + installation.user_set_pw('root', 'toor') + + repo = git.Repo('./') + commit = repo.head.commit.hexsha[:7] + + print(f'Submitting {commit}: success') + + conditions = { + "project": "archinstall", + "profile": "52-54-00-12-34-56", + "status": "success", + "commit": commit + } + req = urllib.request.Request("https://api.archlinux.life/build/success", + data=json.dumps(conditions).encode('utf8'), + headers={'content-type': 'application/json'}) + try: + urllib.request.urlopen(req, timeout=5) + except: + pass \ No newline at end of file -- cgit v1.2.3-54-g00ecf From 46b872aa61e0f07fd561a4e14083a30a3d039d38 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Wed, 21 Apr 2021 14:37:23 +0200 Subject: Re-working top-level-profile lambda to be a parameter to list_profiles() instead. When skipping a profile, None is returned from somewhere and it causes a glitch. So trying to figure out where and what by cleaning up a bit --- archinstall/lib/profiles.py | 7 ++++++- examples/guided.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/archinstall/lib/profiles.py b/archinstall/lib/profiles.py index 21ec5f6f..95962fd6 100644 --- a/archinstall/lib/profiles.py +++ b/archinstall/lib/profiles.py @@ -15,7 +15,7 @@ def grab_url_data(path): response = urllib.request.urlopen(safe_path, context=ssl_context) return response.read() -def list_profiles(filter_irrelevant_macs=True, subpath=''): +def list_profiles(filter_irrelevant_macs=True, subpath='', filter_top_level_profiles=False): # TODO: Grab from github page as well, not just local static files if filter_irrelevant_macs: local_macs = list_interfaces() @@ -63,6 +63,11 @@ def list_profiles(filter_irrelevant_macs=True, subpath=''): cache[profile[:-3]] = {'path' : os.path.join(storage["UPSTREAM_URL"]+subpath, profile), 'description' : profile_list[profile], 'tailored' : tailored} + if filter_top_level_profiles: + for profile in list(cache.keys()): + if Profile(None, profile).is_top_level_profile() is False: + del(cache[profile]) + return cache class Script(): diff --git a/examples/guided.py b/examples/guided.py index 89868148..ef447abb 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -164,7 +164,7 @@ def ask_user_questions(): # Ask for archinstall-specific profiles (such as desktop environments etc) if not archinstall.arguments.get('profile', None): - archinstall.arguments['profile'] = archinstall.select_profile(filter(lambda profile: (Profile(None, profile).is_top_level_profile()), archinstall.list_profiles())) + archinstall.arguments['profile'] = archinstall.select_profile(archinstall.list_profiles(filter_top_level_profiles=True)) else: archinstall.arguments['profile'] = archinstall.list_profiles()[archinstall.arguments['profile']] -- cgit v1.2.3-54-g00ecf 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(-) 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-54-g00ecf 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(-) 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-54-g00ecf 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(-) 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-54-g00ecf