Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/archinstall/lib/menu/global_menu.py
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2022-05-28 10:36:38 +0200
committerAndreas Baumann <mail@andreasbaumann.cc>2022-05-28 10:36:38 +0200
commitfaf925de1882be722d2994d697a802918282e509 (patch)
tree4856c76b10b36e94875ce3c9add961960bb23bf0 /archinstall/lib/menu/global_menu.py
parent3801bee921d22e23435c781c469d9ec0adfa00bd (diff)
parent78449f75bc44f0e2b03cb9d909b9b78e4f7ca4c8 (diff)
Merge branch 'upstreamMaster'
Diffstat (limited to 'archinstall/lib/menu/global_menu.py')
-rw-r--r--archinstall/lib/menu/global_menu.py264
1 files changed, 174 insertions, 90 deletions
diff --git a/archinstall/lib/menu/global_menu.py b/archinstall/lib/menu/global_menu.py
index afccd45b..5cb27cab 100644
--- a/archinstall/lib/menu/global_menu.py
+++ b/archinstall/lib/menu/global_menu.py
@@ -1,6 +1,8 @@
from __future__ import annotations
-from typing import Any, List, Optional, Union
+from typing import Any, List, Optional, Union, Dict, TYPE_CHECKING
+
+import archinstall
from ..menu import Menu
from ..menu.selection_menu import Selector, GeneralMenu
@@ -8,8 +10,7 @@ from ..general import SysCommand, secret
from ..hardware import has_uefi
from ..models import NetworkConfiguration
from ..storage import storage
-from ..output import log
-from ..profiles import is_desktop_profile
+from ..profiles import is_desktop_profile, Profile
from ..disk import encrypted_partitions
from ..user_interaction import get_password, ask_for_a_timezone, save_config
@@ -20,7 +21,6 @@ from ..user_interaction import ask_hostname
from ..user_interaction import ask_for_audio_selection
from ..user_interaction import ask_additional_packages_to_install
from ..user_interaction import ask_to_configure_network
-from ..user_interaction import ask_for_superuser_account
from ..user_interaction import ask_for_additional_users
from ..user_interaction import select_language
from ..user_interaction import select_mirror_regions
@@ -32,126 +32,147 @@ from ..user_interaction import select_encrypted_partitions
from ..user_interaction import select_harddrives
from ..user_interaction import select_profile
from ..user_interaction import select_additional_repositories
+from ..models.users import User
+from ..user_interaction.partitioning_conf import current_partition_layout
+from ..output import FormattedOutput
+
+if TYPE_CHECKING:
+ _: Any
+
class GlobalMenu(GeneralMenu):
def __init__(self,data_store):
- super().__init__(data_store=data_store, auto_cursor=True)
+ super().__init__(data_store=data_store, auto_cursor=True, preview_size=0.3)
def _setup_selection_menu_options(self):
# archinstall.Language will not use preset values
self._menu_options['archinstall-language'] = \
Selector(
- _('Select Archinstall language'),
- lambda x: self._select_archinstall_language('English'),
+ _('Archinstall language'),
+ lambda x: self._select_archinstall_language(x),
default='English')
self._menu_options['keyboard-layout'] = \
- Selector(_('Select keyboard layout'), lambda preset: select_language('us',preset), default='us')
+ Selector(
+ _('Keyboard layout'),
+ lambda preset: select_language(preset),
+ default='us')
self._menu_options['mirror-region'] = \
Selector(
- _('Select mirror region'),
- select_mirror_regions,
+ _('Mirror region'),
+ lambda preset: select_mirror_regions(preset),
display_func=lambda x: list(x.keys()) if x else '[]',
default={})
self._menu_options['sys-language'] = \
- Selector(_('Select locale language'), lambda preset: select_locale_lang('en_US',preset), default='en_US')
+ Selector(
+ _('Locale language'),
+ lambda preset: select_locale_lang(preset),
+ default='en_US')
self._menu_options['sys-encoding'] = \
- Selector(_('Select locale encoding'), lambda preset: select_locale_enc('utf-8',preset), default='utf-8')
+ Selector(
+ _('Locale encoding'),
+ lambda preset: select_locale_enc(preset),
+ default='UTF-8')
self._menu_options['harddrives'] = \
Selector(
- _('Select harddrives'),
- self._select_harddrives)
+ _('Drive(s)'),
+ lambda preset: self._select_harddrives(preset),
+ display_func=lambda x: f'{len(x)} ' + str(_('Drive(s)')) if x is not None and len(x) > 0 else '',
+ preview_func=self._prev_harddrives,
+ )
self._menu_options['disk_layouts'] = \
Selector(
- _('Select disk layout'),
- lambda x: select_disk_layout(
+ _('Disk layout'),
+ lambda preset: select_disk_layout(
+ preset,
storage['arguments'].get('harddrives', []),
storage['arguments'].get('advanced', False)
),
+ preview_func=self._prev_disk_layouts,
+ display_func=lambda x: self._display_disk_layout(x),
dependencies=['harddrives'])
self._menu_options['!encryption-password'] = \
Selector(
- _('Set encryption password'),
+ _('Encryption password'),
lambda x: self._select_encrypted_password(),
display_func=lambda x: secret(x) if x else 'None',
dependencies=['harddrives'])
+ self._menu_options['HSM'] = Selector(
+ description=_('Use HSM to unlock encrypted drive'),
+ func=lambda preset: self._select_hsm(preset),
+ dependencies=['!encryption-password'],
+ default=None
+ )
self._menu_options['swap'] = \
Selector(
- _('Use swap'),
+ _('Swap'),
lambda preset: ask_for_swap(preset),
default=True)
self._menu_options['bootloader'] = \
Selector(
- _('Select bootloader'),
+ _('Bootloader'),
lambda preset: ask_for_bootloader(storage['arguments'].get('advanced', False),preset),
default="systemd-bootctl" if has_uefi() else "grub-install")
self._menu_options['hostname'] = \
Selector(
- _('Specify hostname'),
+ _('Hostname'),
ask_hostname,
default='archlinux')
# root password won't have preset value
self._menu_options['!root-password'] = \
Selector(
- _('Set root password'),
+ _('Root password'),
lambda preset:self._set_root_password(),
display_func=lambda x: secret(x) if x else 'None')
- self._menu_options['!superusers'] = \
- Selector(
- _('Specify superuser account'),
- lambda preset: self._create_superuser_account(),
- default={},
- exec_func=lambda n,v:self._users_resynch(),
- dependencies_not=['!root-password'],
- display_func=lambda x: self._display_superusers())
self._menu_options['!users'] = \
Selector(
- _('Specify user account'),
- lambda x: self._create_user_account(),
+ _('User account'),
+ lambda x: self._create_user_account(x),
default={},
- exec_func=lambda n,v:self._users_resynch(),
- display_func=lambda x: list(x.keys()) if x else '[]')
+ display_func=lambda x: f'{len(x)} {_("User(s)")}' if len(x) > 0 else None,
+ preview_func=self._prev_users)
self._menu_options['profile'] = \
Selector(
- _('Specify profile'),
- lambda x: self._select_profile(),
- display_func=lambda x: x if x else 'None')
+ _('Profile'),
+ lambda preset: self._select_profile(preset),
+ display_func=lambda x: x if x else 'None'
+ )
self._menu_options['audio'] = \
Selector(
- _('Select audio'),
+ _('Audio'),
lambda preset: ask_for_audio_selection(is_desktop_profile(storage['arguments'].get('profile', None)),preset),
display_func=lambda x: x if x else 'None',
default=None
)
self._menu_options['kernels'] = \
Selector(
- _('Select kernels'),
+ _('Kernels'),
lambda preset: select_kernel(preset),
default=['linux'])
self._menu_options['packages'] = \
Selector(
- _('Additional packages to install'),
+ _('Additional packages'),
# lambda x: ask_additional_packages_to_install(storage['arguments'].get('packages', None)),
ask_additional_packages_to_install,
default=[])
self._menu_options['additional-repositories'] = \
Selector(
- _('Additional repositories to enable'),
+ _('Optional repositories'),
select_additional_repositories,
default=[])
self._menu_options['nic'] = \
Selector(
- _('Configure network'),
+ _('Network configuration'),
ask_to_configure_network,
display_func=lambda x: self._prev_network_configuration(x),
default={})
self._menu_options['timezone'] = \
Selector(
- _('Select timezone'),
+ _('Timezone'),
lambda preset: ask_for_a_timezone(preset),
default='UTC')
self._menu_options['ntp'] = \
Selector(
- _('Set automatic time sync (NTP)'),
+ _('Automatic time sync (NTP)'),
lambda preset: self._select_ntp(preset),
default=True)
self._menu_options['__separator__'] = \
@@ -172,7 +193,7 @@ class GlobalMenu(GeneralMenu):
def _update_install_text(self, name :str = None, result :Any = None):
text = self._install_text()
- self._menu_options.get('install').update_description(text)
+ self._menu_options['install'].update_description(text)
def post_callback(self,name :str = None ,result :Any = None):
self._update_install_text(name, result)
@@ -182,14 +203,21 @@ class GlobalMenu(GeneralMenu):
# If no partitions was marked as encrypted, but a password was supplied and we have some disks to format..
# Then we need to identify which partitions to encrypt. This will default to / (root).
if len(list(encrypted_partitions(storage['arguments'].get('disk_layouts', [])))) == 0:
- storage['arguments']['disk_layouts'] = select_encrypted_partitions(
- storage['arguments']['disk_layouts'], storage['arguments']['!encryption-password'])
+ for blockdevice in storage['arguments']['disk_layouts']:
+ for partition_index in select_encrypted_partitions(
+ title="Select which partitions to encrypt:",
+ partitions=storage['arguments']['disk_layouts'][blockdevice]['partitions']
+ ):
+
+ partition = storage['arguments']['disk_layouts'][blockdevice]['partitions'][partition_index]
+ partition['encrypted'] = True
+ partition['!password'] = storage['arguments']['!encryption-password']
def _install_text(self):
missing = len(self._missing_configs())
if missing > 0:
return _('Install ({} config(s) missing)').format(missing)
- return 'Install'
+ return _('Install')
def _prev_network_configuration(self, cur_value: Union[NetworkConfiguration, List[NetworkConfiguration]]) -> str:
if not cur_value:
@@ -201,6 +229,35 @@ class GlobalMenu(GeneralMenu):
else:
return str(cur_value)
+ def _prev_harddrives(self) -> Optional[str]:
+ selector = self._menu_options['harddrives']
+ if selector.has_selection():
+ drives = selector.current_selection
+ return '\n\n'.join([d.display_info for d in drives])
+ return None
+
+ def _prev_disk_layouts(self) -> Optional[str]:
+ selector = self._menu_options['disk_layouts']
+ if selector.has_selection():
+ layouts: Dict[str, Dict[str, Any]] = selector.current_selection
+
+ output = ''
+ for device, layout in layouts.items():
+ output += f'{_("Device")}: {device}\n\n'
+ output += current_partition_layout(layout['partitions'], with_title=False)
+ output += '\n\n'
+
+ return output.rstrip()
+
+ return None
+
+ def _display_disk_layout(self, current_value: Optional[Dict[str, Any]]) -> str:
+ if current_value:
+ total_partitions = [entry['partitions'] for entry in current_value.values()]
+ total_nr = sum([len(p) for p in total_partitions])
+ return f'{total_nr} {_("Partitions")}'
+ return ''
+
def _prev_install_missing_config(self) -> Optional[str]:
if missing := self._missing_configs():
text = str(_('Missing configurations:\n'))
@@ -209,31 +266,42 @@ class GlobalMenu(GeneralMenu):
return text[:-1] # remove last new line
return None
+ def _prev_users(self) -> Optional[str]:
+ selector = self._menu_options['!users']
+ if selector.has_selection():
+ users: List[User] = selector.current_selection
+ return FormattedOutput.as_table(users)
+ return None
+
def _missing_configs(self) -> List[str]:
def check(s):
return self._menu_options.get(s).has_selection()
+ def has_superuser() -> bool:
+ users = self._menu_options['!users'].current_selection
+ return any([u.sudo for u in users])
+
missing = []
if not check('bootloader'):
missing += ['Bootloader']
if not check('hostname'):
missing += ['Hostname']
- if not check('!root-password') and not check('!superusers'):
- missing += [str(_('Either root-password or at least 1 superuser must be specified'))]
+ if not check('!root-password') and not has_superuser():
+ missing += [str(_('Either root-password or at least 1 user with sudo privileges must be specified'))]
if not check('harddrives'):
missing += ['Hard drives']
if check('harddrives'):
- if not self._menu_options.get('harddrives').is_empty() and not check('disk_layouts'):
+ if not self._menu_options['harddrives'].is_empty() and not check('disk_layouts'):
missing += ['Disk layout']
return missing
- def _set_root_password(self):
+ def _set_root_password(self) -> Optional[str]:
prompt = str(_('Enter root password (leave blank to disable root): '))
password = get_password(prompt=prompt)
return password
- def _select_encrypted_password(self):
+ def _select_encrypted_password(self) -> Optional[str]:
if passwd := get_password(prompt=str(_('Enter disk encryption password (leave blank for no encryption): '))):
return passwd
else:
@@ -247,59 +315,75 @@ class GlobalMenu(GeneralMenu):
return ntp
- def _select_harddrives(self, old_harddrives : list) -> list:
- # old_haddrives = storage['arguments'].get('harddrives', [])
+ def _select_harddrives(self, old_harddrives : list) -> List:
harddrives = select_harddrives(old_harddrives)
- # in case the harddrives got changed we have to reset the disk layout as well
- if old_harddrives != harddrives:
- self._menu_options.get('disk_layouts').set_current_selection(None)
- storage['arguments']['disk_layouts'] = {}
-
- if not harddrives:
+ if len(harddrives) == 0:
prompt = _(
"You decided to skip harddrive selection\nand will use whatever drive-setup is mounted at {} (experimental)\n"
"WARNING: Archinstall won't check the suitability of this setup\n"
"Do you wish to continue?"
).format(storage['MOUNT_POINT'])
- choice = Menu(prompt, ['yes', 'no'], default_option='yes').run()
+ choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes(), skip=False).run()
- if choice == 'no':
- exit(1)
+ if choice.value == Menu.no():
+ return self._select_harddrives(old_harddrives)
+
+ # in case the harddrives got changed we have to reset the disk layout as well
+ if old_harddrives != harddrives:
+ self._menu_options['disk_layouts'].set_current_selection(None)
+ storage['arguments']['disk_layouts'] = {}
return harddrives
- def _select_profile(self):
- profile = select_profile()
+ def _select_profile(self, preset):
+ profile = select_profile(preset)
+ ret = None
+
+ if profile is None:
+ if any([
+ archinstall.storage.get('profile_minimal', False),
+ archinstall.storage.get('_selected_servers', None),
+ archinstall.storage.get('_desktop_profile', None),
+ archinstall.arguments.get('desktop-environment', None),
+ archinstall.arguments.get('gfx_driver_packages', None)
+ ]):
+ return preset
+ else: # ctrl+c was actioned and all profile settings have been reset
+ return None
+
+ servers = archinstall.storage.get('_selected_servers', [])
+ desktop = archinstall.storage.get('_desktop_profile', None)
+ desktop_env = archinstall.arguments.get('desktop-environment', None)
+ gfx_driver = archinstall.arguments.get('gfx_driver_packages', None)
# Check the potentially selected profiles preparations to get early checks if some additional questions are needed.
if profile and profile.has_prep_function():
namespace = f'{profile.namespace}.py'
with profile.load_instructions(namespace=namespace) as imported:
- if not imported._prep_function():
- log(' * Profile\'s preparation requirements was not fulfilled.', fg='red')
- exit(1)
-
- return profile
-
- def _create_superuser_account(self):
- superusers = ask_for_superuser_account(str(_('Manage superuser accounts: ')))
- return superusers if superusers else None
-
- def _create_user_account(self):
- users = ask_for_additional_users(str(_('Manage ordinary user accounts: ')))
+ if imported._prep_function(servers=servers, desktop=desktop, desktop_env=desktop_env, gfx_driver=gfx_driver):
+ ret: Profile = profile
+
+ match ret.name:
+ case 'minimal':
+ reset = ['_selected_servers', '_desktop_profile', 'desktop-environment', 'gfx_driver_packages']
+ case 'server':
+ reset = ['_desktop_profile', 'desktop-environment']
+ case 'desktop':
+ reset = ['_selected_servers']
+ case 'xorg':
+ reset = ['_selected_servers', '_desktop_profile', 'desktop-environment']
+
+ for r in reset:
+ archinstall.storage[r] = None
+ else:
+ return self._select_profile(preset)
+ elif profile:
+ ret = profile
+
+ return ret
+
+ def _create_user_account(self, defined_users: List[User]) -> List[User]:
+ users = ask_for_additional_users(defined_users=defined_users)
return users
-
- def _display_superusers(self):
- superusers = self._data_store.get('!superusers', {})
-
- if self._menu_options.get('!root-password').has_selection():
- return list(superusers.keys()) if superusers else '[]'
- else:
- return list(superusers.keys()) if superusers else ''
-
- def _users_resynch(self):
- self.synch('!superusers')
- self.synch('!users')
- return False