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:
Diffstat (limited to 'archinstall/lib/menu/global_menu.py')
-rw-r--r--archinstall/lib/menu/global_menu.py293
1 files changed, 293 insertions, 0 deletions
diff --git a/archinstall/lib/menu/global_menu.py b/archinstall/lib/menu/global_menu.py
new file mode 100644
index 00000000..3f57941a
--- /dev/null
+++ b/archinstall/lib/menu/global_menu.py
@@ -0,0 +1,293 @@
+from __future__ import annotations
+
+from typing import Any, List, Optional
+
+from ..menu import Menu
+from ..menu.selection_menu import Selector, GeneralMenu
+from ..general import SysCommand, secret
+from ..hardware import has_uefi
+from ..storage import storage
+from ..output import log
+from ..profiles import is_desktop_profile
+from ..disk import encrypted_partitions
+
+from ..user_interaction import get_password, ask_for_a_timezone, save_config
+from ..user_interaction import ask_ntp
+from ..user_interaction import ask_for_swap
+from ..user_interaction import ask_for_bootloader
+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
+from ..user_interaction import select_locale_lang
+from ..user_interaction import select_locale_enc
+from ..user_interaction import select_disk_layout
+from ..user_interaction import select_kernel
+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
+
+class GlobalMenu(GeneralMenu):
+ def __init__(self,data_store):
+ super().__init__(data_store=data_store, auto_cursor=True)
+
+ 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'),
+ default='English',
+ enabled=True)
+ self._menu_options['keyboard-layout'] = \
+ Selector(_('Select keyboard layout'), lambda preset: select_language('us',preset), default='us')
+ self._menu_options['mirror-region'] = \
+ Selector(
+ _('Select mirror region'),
+ select_mirror_regions,
+ 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')
+ self._menu_options['sys-encoding'] = \
+ Selector(_('Select locale encoding'), lambda preset: select_locale_enc('utf-8',preset), default='utf-8')
+ self._menu_options['harddrives'] = \
+ Selector(
+ _('Select harddrives'),
+ self._select_harddrives)
+ self._menu_options['disk_layouts'] = \
+ Selector(
+ _('Select disk layout'),
+ lambda x: select_disk_layout(
+ storage['arguments'].get('harddrives', []),
+ storage['arguments'].get('advanced', False)
+ ),
+ dependencies=['harddrives'])
+ self._menu_options['!encryption-password'] = \
+ Selector(
+ _('Set encryption password'),
+ lambda x: self._select_encrypted_password(),
+ display_func=lambda x: secret(x) if x else 'None',
+ dependencies=['harddrives'])
+ self._menu_options['swap'] = \
+ Selector(
+ _('Use swap'),
+ lambda preset: ask_for_swap(preset),
+ default=True)
+ self._menu_options['bootloader'] = \
+ Selector(
+ _('Select 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'),
+ ask_hostname,
+ default='archlinux')
+ # root password won't have preset value
+ self._menu_options['!root-password'] = \
+ Selector(
+ _('Set 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(),
+ 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(),
+ default={},
+ exec_func=lambda n,v:self._users_resynch(),
+ display_func=lambda x: list(x.keys()) if x else '[]')
+ self._menu_options['profile'] = \
+ Selector(
+ _('Specify profile'),
+ lambda x: self._select_profile(),
+ display_func=lambda x: x if x else 'None')
+ self._menu_options['audio'] = \
+ Selector(
+ _('Select audio'),
+ lambda preset: ask_for_audio_selection(is_desktop_profile(storage['arguments'].get('profile', None)),preset))
+ self._menu_options['kernels'] = \
+ Selector(
+ _('Select kernels'),
+ lambda preset: select_kernel(preset),
+ default=['linux'])
+ self._menu_options['packages'] = \
+ Selector(
+ _('Additional packages to install'),
+ # 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'),
+ select_additional_repositories,
+ default=[])
+ self._menu_options['nic'] = \
+ Selector(
+ _('Configure network'),
+ ask_to_configure_network,
+ display_func=lambda x: x if x else _('Not configured, unavailable unless setup manually'),
+ default={})
+ self._menu_options['timezone'] = \
+ Selector(
+ _('Select timezone'),
+ lambda preset: ask_for_a_timezone(preset),
+ default='UTC')
+ self._menu_options['ntp'] = \
+ Selector(
+ _('Set automatic time sync (NTP)'),
+ lambda preset: self._select_ntp(preset),
+ default=True)
+ self._menu_options['save_config'] = \
+ Selector(
+ _('Save configuration'),
+ lambda preset: save_config(self._data_store),
+ enabled=True,
+ no_store=True)
+ self._menu_options['install'] = \
+ Selector(
+ self._install_text(),
+ exec_func=lambda n,v: True if len(self._missing_configs()) == 0 else False,
+ preview_func=self._prev_install_missing_config,
+ enabled=True,
+ no_store=True)
+
+ self._menu_options['abort'] = Selector(_('Abort'), exec_func=lambda n,v:exit(1), enabled=True)
+
+ def _update_install_text(self, name :str = None, result :Any = None):
+ text = self._install_text()
+ self._menu_options.get('install').update_description(text)
+
+ def post_callback(self,name :str = None ,result :Any = None):
+ self._update_install_text(name, result)
+
+ def exit_callback(self):
+ if self._data_store.get('harddrives', None) and self._data_store.get('!encryption-password', None):
+ # 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'])
+
+ def _install_text(self):
+ missing = len(self._missing_configs())
+ if missing > 0:
+ return _('Install ({} config(s) missing)').format(missing)
+ return 'Install'
+
+ def _prev_install_missing_config(self) -> Optional[str]:
+ if missing := self._missing_configs():
+ text = str(_('Missing configurations:\n'))
+ for m in missing:
+ text += f'- {m}\n'
+ return text[:-1] # remove last new line
+ return None
+
+ def _missing_configs(self) -> List[str]:
+ def check(s):
+ return self._menu_options.get(s).has_selection()
+
+ missing = []
+ if not check('bootloader'):
+ missing += ['Bootloader']
+ if not check('hostname'):
+ missing += ['Hostname']
+ if not check('audio'):
+ missing += ['Audio']
+ 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('harddrives'):
+ missing += ['Hard drives']
+ if check('harddrives'):
+ if not self._menu_options.get('harddrives').is_empty() and not check('disk_layouts'):
+ missing += ['Disk layout']
+
+ return missing
+
+ def _set_root_password(self):
+ prompt = str(_('Enter root password (leave blank to disable root): '))
+ password = get_password(prompt=prompt)
+ return password
+
+ def _select_encrypted_password(self):
+ if passwd := get_password(prompt=str(_('Enter disk encryption password (leave blank for no encryption): '))):
+ return passwd
+ else:
+ return None
+
+ def _select_ntp(self, preset :bool = True) -> bool:
+ ntp = ask_ntp(preset)
+
+ value = str(ntp).lower()
+ SysCommand(f'timedatectl set-ntp {value}')
+
+ return ntp
+
+ def _select_harddrives(self, old_harddrives : list) -> list:
+ # old_haddrives = storage['arguments'].get('harddrives', [])
+ harddrives = select_harddrives(old_harddrives)
+
+ # in case the harddrives got changed we have to reset the disk layout as well
+ if old_harddrives != harddrives:
+ self._menu_options.get('disk_layouts').set_current_selection(None)
+ storage['arguments']['disk_layouts'] = {}
+
+ if not harddrives:
+ 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()
+
+ if choice == 'no':
+ return self._select_harddrives(old_harddrives)
+
+ return harddrives
+
+ def _select_profile(self):
+ profile = select_profile()
+
+ # 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: ')))
+ 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