index : archinstall32 | |
Archlinux32 installer | gitolite user |
summaryrefslogtreecommitdiff |
author | Andreas Baumann <mail@andreasbaumann.cc> | 2024-05-10 15:56:28 +0200 |
---|---|---|
committer | Andreas Baumann <mail@andreasbaumann.cc> | 2024-05-10 15:56:28 +0200 |
commit | 683da22298abbd90f51d4dd38a7ec4b0dfb04555 (patch) | |
tree | ec2ac04967f9277df038edc362201937b331abe5 /archinstall/default_profiles | |
parent | af7ab9833c9f9944874f0162ae0975175ddc628d (diff) | |
parent | 3381cd55673e5105697d354cf4a1be9a7bcef062 (diff) |
diff --git a/archinstall/default_profiles/__init__.py b/archinstall/default_profiles/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/archinstall/default_profiles/__init__.py diff --git a/archinstall/default_profiles/applications/__init__.py b/archinstall/default_profiles/applications/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/archinstall/default_profiles/applications/__init__.py diff --git a/archinstall/default_profiles/applications/pipewire.py b/archinstall/default_profiles/applications/pipewire.py new file mode 100644 index 00000000..4cb75968 --- /dev/null +++ b/archinstall/default_profiles/applications/pipewire.py @@ -0,0 +1,40 @@ +from typing import List, Union, Any, TYPE_CHECKING + +import archinstall + +from archinstall.default_profiles.profile import Profile, ProfileType +from archinstall.lib.models import User + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +class PipewireProfile(Profile): + def __init__(self): + super().__init__('Pipewire', ProfileType.Application) + + @property + def packages(self) -> List[str]: + return [ + 'pipewire', + 'pipewire-alsa', + 'pipewire-jack', + 'pipewire-pulse', + 'gst-plugin-pipewire', + 'libpulse', + 'wireplumber' + ] + + def _enable_pipewire_for_all(self, install_session: 'Installer'): + users: Union[User, List[User]] = archinstall.arguments.get('!users', []) + if not isinstance(users, list): + users = [users] + + for user in users: + install_session.arch_chroot('systemctl enable --user pipewire-pulse.service', run_as=user.username) + + def install(self, install_session: 'Installer'): + super().install(install_session) + install_session.add_additional_packages(self.packages) + self._enable_pipewire_for_all(install_session) diff --git a/archinstall/default_profiles/custom.py b/archinstall/default_profiles/custom.py new file mode 100644 index 00000000..5f9db620 --- /dev/null +++ b/archinstall/default_profiles/custom.py @@ -0,0 +1,218 @@ +# from typing import List, Dict, Optional, TYPE_CHECKING, Any +# +# from ..lib import menu +# from archinstall.lib.output import log, FormattedOutput +# from archinstall.lib.profile.profiles_handler import profile_handler +# from archinstall.default_profiles.profile import Profile, ProfileType, SelectResult, ProfileInfo, TProfile +# +# if TYPE_CHECKING: +# from archinstall.lib.installer import Installer +# _: Any +# +# +# class CustomProfileList(menu.ListManager): +# def __init__(self, prompt: str, profiles: List[TProfile]): +# self._actions = [ +# str(_('Add profile')), +# str(_('Edit profile')), +# str(_('Delete profile')) +# ] +# super().__init__(prompt, profiles, [self._actions[0]], self._actions[1:]) +# +# def reformat(self, data: List[TProfile]) -> Dict[str, Optional[TProfile]]: +# table = FormattedOutput.as_table(data) +# rows = table.split('\n') +# +# # these are the header rows of the table and do not map to any profile obviously +# # we're adding 2 spaces as prefix because the menu selector '> ' will be put before +# # the selectable rows so the header has to be aligned +# display_data: Dict[str, Optional[TProfile]] = {f' {rows[0]}': None, f' {rows[1]}': None} +# +# for row, profile in zip(rows[2:], data): +# row = row.replace('|', '\\|') +# display_data[row] = profile +# +# return display_data +# +# def selected_action_display(self, profile: TProfile) -> str: +# return profile.name +# +# def handle_action( +# self, +# action: str, +# entry: Optional['CustomTypeProfile'], +# data: List['CustomTypeProfile'] +# ) -> List['CustomTypeProfile']: +# if action == self._actions[0]: # add +# new_profile = self._add_profile() +# if new_profile is not None: +# # in case a profile with the same name as an existing profile +# # was created we'll replace the existing one +# data = [d for d in data if d.name != new_profile.name] +# data += [new_profile] +# elif entry is not None: +# if action == self._actions[1]: # edit +# new_profile = self._add_profile(entry) +# if new_profile is not None: +# # we'll remove the original profile and add the modified version +# data = [d for d in data if d.name != entry.name and d.name != new_profile.name] +# data += [new_profile] +# elif action == self._actions[2]: # delete +# data = [d for d in data if d != entry] +# +# return data +# +# def _is_new_profile_name(self, name: str) -> bool: +# existing_profile = profile_handler.get_profile_by_name(name) +# if existing_profile is not None and existing_profile.profile_type != ProfileType.CustomType: +# return False +# return True +# +# def _add_profile(self, editing: Optional['CustomTypeProfile'] = None) -> Optional['CustomTypeProfile']: +# name_prompt = '\n\n' + str(_('Profile name: ')) +# +# while True: +# profile_name = menu.TextInput(name_prompt, editing.name if editing else '').run().strip() +# +# if not profile_name: +# return None +# +# if not self._is_new_profile_name(profile_name): +# error_prompt = str(_("The profile name you entered is already in use. Try again")) +# print(error_prompt) +# else: +# break +# +# packages_prompt = str(_('Packages to be install with this profile (space separated, leave blank to skip): ')) +# edit_packages = ' '.join(editing.packages) if editing else '' +# packages = menu.TextInput(packages_prompt, edit_packages).run().strip() +# +# services_prompt = str(_('Services to be enabled with this profile (space separated, leave blank to skip): ')) +# edit_services = ' '.join(editing.services) if editing else '' +# services = menu.TextInput(services_prompt, edit_services).run().strip() +# +# choice = menu.Menu( +# str(_('Should this profile be enabled for installation?')), +# menu.Menu.yes_no(), +# skip=False, +# default_option=menu.Menu.no(), +# clear_screen=False, +# show_search_hint=False +# ).run() +# +# enable_profile = True if choice.value == menu.Menu.yes() else False +# +# profile = CustomTypeProfile( +# profile_name, +# enabled=enable_profile, +# packages=packages.split(' '), +# services=services.split(' ') +# ) +# +# return profile +# +# +# # TODO +# # Still needs some ironing out +# class CustomProfile(): +# def __init__(self): +# super().__init__( +# 'Custom', +# ProfileType.Custom, +# description=str(_('Create your own')) +# ) +# +# def json(self) -> Dict[str, Any]: +# data: Dict[str, Any] = {'main': self.name, 'gfx_driver': self.gfx_driver, 'custom': []} +# +# for profile in self._current_selection: +# data['custom'].append({ +# 'name': profile.name, +# 'packages': profile.packages, +# 'services': profile.services, +# 'enabled': profile.custom_enabled +# }) +# +# return data +# +# def do_on_select(self) -> SelectResult: +# custom_profile_list = CustomProfileList('', profile_handler.get_custom_profiles()) +# custom_profiles = custom_profile_list.run() +# +# # we'll first remove existing custom default_profiles with +# # the same name and then add the new ones this +# # will avoid errors of default_profiles with duplicate naming +# profile_handler.remove_custom_profiles(custom_profiles) +# profile_handler.add_custom_profiles(custom_profiles) +# +# self.set_current_selection(custom_profiles) +# +# if custom_profile_list.is_last_choice_cancel(): +# return SelectResult.SameSelection +# +# enabled_profiles = [p for p in self._current_selection if p.custom_enabled] +# # in case we only created inactive default_profiles we wanna store them but +# # we want to reset the original setting +# if not enabled_profiles: +# return SelectResult.ResetCurrent +# +# return SelectResult.NewSelection +# +# def post_install(self, install_session: 'Installer'): +# for profile in self._current_selection: +# profile.post_install(install_session) +# +# def install(self, install_session: 'Installer'): +# driver_packages = self.gfx_driver_packages() +# install_session.add_additional_packages(driver_packages) +# +# for profile in self._current_selection: +# if profile.custom_enabled: +# log(f'Installing custom profile {profile.name}...') +# +# install_session.add_additional_packages(profile.packages) +# install_session.enable_service(profile.services) +# +# profile.install(install_session) +# +# def info(self) -> Optional[ProfileInfo]: +# enabled_profiles = [p for p in self._current_selection if p.custom_enabled] +# if enabled_profiles: +# details = ', '.join([p.name for p in enabled_profiles]) +# gfx_driver = self.gfx_driver +# return ProfileInfo(self.name, details, gfx_driver) +# +# return None +# +# def reset(self): +# for profile in self._current_selection: +# profile.set_enabled(False) +# +# self.gfx_driver = None +# +# +# class CustomTypeProfile(Profile): +# def __init__( +# self, +# name: str, +# enabled: bool = False, +# packages: List[str] = [], +# services: List[str] = [] +# ): +# super().__init__( +# name, +# ProfileType.CustomType, +# packages=packages, +# services=services, +# support_gfx_driver=True +# ) +# +# self.custom_enabled = enabled +# +# def json(self) -> Dict[str, Any]: +# return { +# 'name': self.name, +# 'packages': self.packages, +# 'services': self.services, +# 'enabled': self.custom_enabled +# } diff --git a/archinstall/default_profiles/desktop.py b/archinstall/default_profiles/desktop.py new file mode 100644 index 00000000..417d86d6 --- /dev/null +++ b/archinstall/default_profiles/desktop.py @@ -0,0 +1,87 @@ +from typing import Any, TYPE_CHECKING, List, Optional, Dict + +from archinstall.lib import menu +from archinstall.lib.output import info +from archinstall.lib.profile.profiles_handler import profile_handler +from archinstall.default_profiles.profile import Profile, ProfileType, SelectResult, GreeterType + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +class DesktopProfile(Profile): + def __init__(self, current_selection: List[Profile] = []): + super().__init__( + 'Desktop', + ProfileType.Desktop, + description=str(_('Provides a selection of desktop environments and tiling window managers, e.g. GNOME, KDE Plasma, Sway')), + current_selection=current_selection, + support_greeter=True + ) + + @property + def packages(self) -> List[str]: + return [ + 'nano', + 'vim', + 'openssh', + 'htop', + 'wget', + 'iwd', + 'wireless_tools', + 'wpa_supplicant', + 'smartmontools', + 'xdg-utils' + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + combined_greeters: Dict[GreeterType, int] = {} + for profile in self.current_selection: + if profile.default_greeter_type: + combined_greeters.setdefault(profile.default_greeter_type, 0) + combined_greeters[profile.default_greeter_type] += 1 + + if len(combined_greeters) >= 1: + return list(combined_greeters)[0] + + return None + + def _do_on_select_profiles(self): + for profile in self.current_selection: + profile.do_on_select() + + def do_on_select(self) -> SelectResult: + choice = profile_handler.select_profile( + profile_handler.get_desktop_profiles(), + self._current_selection, + title=str(_('Select your desired desktop environment')), + multi=True + ) + + match choice.type_: + case menu.MenuSelectionType.Selection: + self.set_current_selection(choice.value) # type: ignore + self._do_on_select_profiles() + return SelectResult.NewSelection + case menu.MenuSelectionType.Skip: + return SelectResult.SameSelection + case menu.MenuSelectionType.Reset: + return SelectResult.ResetCurrent + + def post_install(self, install_session: 'Installer'): + for profile in self._current_selection: + profile.post_install(install_session) + + def install(self, install_session: 'Installer'): + # Install common packages for all desktop environments + install_session.add_additional_packages(self.packages) + + for profile in self._current_selection: + info(f'Installing profile {profile.name}...') + + install_session.add_additional_packages(profile.packages) + install_session.enable_service(profile.services) + + profile.install(install_session) diff --git a/archinstall/default_profiles/desktops/__init__.py b/archinstall/default_profiles/desktops/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/archinstall/default_profiles/desktops/__init__.py diff --git a/archinstall/default_profiles/desktops/awesome.py b/archinstall/default_profiles/desktops/awesome.py new file mode 100644 index 00000000..3833ce71 --- /dev/null +++ b/archinstall/default_profiles/desktops/awesome.py @@ -0,0 +1,63 @@ +from typing import List, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +class AwesomeProfile(XorgProfile): + def __init__(self): + super().__init__('Awesome', ProfileType.WindowMgr, description='') + + @property + def packages(self) -> List[str]: + return super().packages + [ + 'awesome', + 'alacritty', + 'xorg-xinit', + 'xorg-xrandr', + 'xterm', + 'feh', + 'slock', + 'terminus-font', + 'gnu-free-fonts', + 'ttf-liberation', + 'xsel', + ] + + def install(self, install_session: 'Installer'): + super().install(install_session) + + # TODO: Copy a full configuration to ~/.config/awesome/rc.lua instead. + with open(f"{install_session.target}/etc/xdg/awesome/rc.lua", 'r') as fh: + awesome_lua = fh.read() + + # Replace xterm with alacritty for a smoother experience. + awesome_lua = awesome_lua.replace('"xterm"', '"alacritty"') + + with open(f"{install_session.target}/etc/xdg/awesome/rc.lua", 'w') as fh: + fh.write(awesome_lua) + + # TODO: Configure the right-click-menu to contain the above packages that were installed. (as a user config) + + # TODO: check if we selected a greeter, + # but for now, awesome is intended to run without one. + with open(f"{install_session.target}/etc/X11/xinit/xinitrc", 'r') as xinitrc: + xinitrc_data = xinitrc.read() + + for line in xinitrc_data.split('\n'): + if "twm &" in line: + xinitrc_data = xinitrc_data.replace(line, f"# {line}") + if "xclock" in line: + xinitrc_data = xinitrc_data.replace(line, f"# {line}") + if "xterm" in line: + xinitrc_data = xinitrc_data.replace(line, f"# {line}") + + xinitrc_data += '\n' + xinitrc_data += 'exec awesome\n' + + with open(f"{install_session.target}/etc/X11/xinit/xinitrc", 'w') as xinitrc: + xinitrc.write(xinitrc_data)
\ No newline at end of file diff --git a/archinstall/default_profiles/desktops/bspwm.py b/archinstall/default_profiles/desktops/bspwm.py new file mode 100644 index 00000000..61eeba43 --- /dev/null +++ b/archinstall/default_profiles/desktops/bspwm.py @@ -0,0 +1,27 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class BspwmProfile(XorgProfile): + def __init__(self): + super().__init__('Bspwm', ProfileType.WindowMgr, description='') + + @property + def packages(self) -> List[str]: + # return super().packages + [ + return [ + 'bspwm', + 'sxhkd', + 'dmenu', + 'xdo', + 'rxvt-unicode' + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm diff --git a/archinstall/default_profiles/desktops/budgie.py b/archinstall/default_profiles/desktops/budgie.py new file mode 100644 index 00000000..28c05f45 --- /dev/null +++ b/archinstall/default_profiles/desktops/budgie.py @@ -0,0 +1,26 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class BudgieProfile(XorgProfile): + def __init__(self): + super().__init__('Budgie', ProfileType.DesktopEnv, description='') + + @property + def packages(self) -> List[str]: + return [ + "arc-gtk-theme", + "budgie", + "mate-terminal", + "nemo", + "papirus-icon-theme" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.LightdmSlick diff --git a/archinstall/default_profiles/desktops/cinnamon.py b/archinstall/default_profiles/desktops/cinnamon.py new file mode 100644 index 00000000..a819b4d1 --- /dev/null +++ b/archinstall/default_profiles/desktops/cinnamon.py @@ -0,0 +1,27 @@ +from typing import Optional, List, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class CinnamonProfile(XorgProfile): + def __init__(self): + super().__init__('Cinnamon', ProfileType.DesktopEnv, description='') + + @property + def packages(self) -> List[str]: + return [ + "cinnamon", + "system-config-printer", + "gnome-keyring", + "gnome-terminal", + "blueberry", + "metacity" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm diff --git a/archinstall/default_profiles/desktops/cutefish.py b/archinstall/default_profiles/desktops/cutefish.py new file mode 100644 index 00000000..c4202920 --- /dev/null +++ b/archinstall/default_profiles/desktops/cutefish.py @@ -0,0 +1,27 @@ +from typing import Optional, List, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +class CutefishProfile(XorgProfile): + def __init__(self): + super().__init__('Cutefish', ProfileType.DesktopEnv, description='') + + @property + def packages(self) -> List[str]: + return [ + "cutefish", + "noto-fonts" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Sddm + + def install(self, install_session: 'Installer'): + super().install(install_session) diff --git a/archinstall/default_profiles/desktops/deepin.py b/archinstall/default_profiles/desktops/deepin.py new file mode 100644 index 00000000..e6a9f6b5 --- /dev/null +++ b/archinstall/default_profiles/desktops/deepin.py @@ -0,0 +1,24 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class DeepinProfile(XorgProfile): + def __init__(self): + super().__init__('Deepin', ProfileType.DesktopEnv, description='') + + @property + def packages(self) -> List[str]: + return [ + "deepin", + "deepin-terminal", + "deepin-editor" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm diff --git a/archinstall/default_profiles/desktops/enlightenment.py b/archinstall/default_profiles/desktops/enlightenment.py new file mode 100644 index 00000000..7dd7822a --- /dev/null +++ b/archinstall/default_profiles/desktops/enlightenment.py @@ -0,0 +1,23 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class EnlighenmentProfile(XorgProfile): + def __init__(self): + super().__init__('Enlightenment', ProfileType.WindowMgr, description='') + + @property + def packages(self) -> List[str]: + return [ + "enlightenment", + "terminology" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm diff --git a/archinstall/default_profiles/desktops/gnome.py b/archinstall/default_profiles/desktops/gnome.py new file mode 100644 index 00000000..24ade437 --- /dev/null +++ b/archinstall/default_profiles/desktops/gnome.py @@ -0,0 +1,23 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class GnomeProfile(XorgProfile): + def __init__(self): + super().__init__('Gnome', ProfileType.DesktopEnv, description='') + + @property + def packages(self) -> List[str]: + return [ + 'gnome', + 'gnome-tweaks' + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Gdm diff --git a/archinstall/default_profiles/desktops/hyprland.py b/archinstall/default_profiles/desktops/hyprland.py new file mode 100644 index 00000000..0c5452eb --- /dev/null +++ b/archinstall/default_profiles/desktops/hyprland.py @@ -0,0 +1,68 @@ +from enum import Enum +from typing import List, Optional, TYPE_CHECKING, Any + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile +from archinstall.lib.menu import Menu + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +class SeatAccess(Enum): + seatd = 'seatd' + polkit = 'polkit' + + +class HyprlandProfile(XorgProfile): + def __init__(self): + super().__init__('Hyprland', ProfileType.DesktopEnv, description='') + + self.custom_settings = {'seat_access': None} + + @property + def packages(self) -> List[str]: + return [ + "hyprland", + "dunst", + "kitty", + "dolphin", + "wofi", + "xdg-desktop-portal-hyprland", + "qt5-wayland", + "qt6-wayland", + "polkit-kde-agent", + "grim", + "slurp" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Sddm + + @property + def services(self) -> List[str]: + if pref := self.custom_settings.get('seat_access', None): + return [pref] + return [] + + def _ask_seat_access(self): + # need to activate seat service and add to seat group + title = str(_('Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)')) + title += str(_('\n\nChoose an option to give Hyprland access to your hardware')) + + options = [e.value for e in SeatAccess] + default = None + + if seat := self.custom_settings.get('seat_access', None): + default = seat + + choice = Menu(title, options, skip=False, preset_values=default).run() + self.custom_settings['seat_access'] = choice.single_value + + def do_on_select(self): + self._ask_seat_access() + + def install(self, install_session: 'Installer'): + super().install(install_session) diff --git a/archinstall/default_profiles/desktops/i3.py b/archinstall/default_profiles/desktops/i3.py new file mode 100644 index 00000000..9c2994de --- /dev/null +++ b/archinstall/default_profiles/desktops/i3.py @@ -0,0 +1,29 @@ +from typing import Optional, List, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class I3wmProfile(XorgProfile): + def __init__(self): + super().__init__('i3-wm', ProfileType.WindowMgr, description='') + + @property + def packages(self) -> List[str]: + return [ + 'i3-wm', + 'i3lock', + 'i3status', + 'i3blocks', + 'xterm', + 'lightdm-gtk-greeter', + 'lightdm', + 'dmenu' + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm diff --git a/archinstall/default_profiles/desktops/lxqt.py b/archinstall/default_profiles/desktops/lxqt.py new file mode 100644 index 00000000..5d75e08d --- /dev/null +++ b/archinstall/default_profiles/desktops/lxqt.py @@ -0,0 +1,31 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class LxqtProfile(XorgProfile): + def __init__(self): + super().__init__('Lxqt', ProfileType.DesktopEnv, description='') + + # NOTE: SDDM is the only officially supported greeter for LXQt, so unlike other DEs, lightdm is not used here. + # LXQt works with lightdm, but since this is not supported, we will not default to this. + # https://github.com/lxqt/lxqt/issues/795 + @property + def packages(self) -> List[str]: + return [ + "lxqt", + "breeze-icons", + "oxygen-icons", + "xdg-utils", + "ttf-freefont", + "leafpad", + "slock" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Sddm diff --git a/archinstall/default_profiles/desktops/mate.py b/archinstall/default_profiles/desktops/mate.py new file mode 100644 index 00000000..d3c4a6e1 --- /dev/null +++ b/archinstall/default_profiles/desktops/mate.py @@ -0,0 +1,23 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class MateProfile(XorgProfile): + def __init__(self): + super().__init__('Mate', ProfileType.DesktopEnv, description='') + + @property + def packages(self) -> List[str]: + return [ + "mate", + "mate-extra" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm diff --git a/archinstall/default_profiles/desktops/plasma.py b/archinstall/default_profiles/desktops/plasma.py new file mode 100644 index 00000000..bcc1ea1b --- /dev/null +++ b/archinstall/default_profiles/desktops/plasma.py @@ -0,0 +1,27 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + +class PlasmaProfile(XorgProfile): + def __init__(self): + super().__init__('KDE Plasma', ProfileType.DesktopEnv, description='') + + @property + def packages(self) -> List[str]: + return [ + "plasma-meta", + "konsole", + "kwrite", + "dolphin", + "ark", + "plasma-workspace", + "egl-wayland" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Sddm diff --git a/archinstall/default_profiles/desktops/qtile.py b/archinstall/default_profiles/desktops/qtile.py new file mode 100644 index 00000000..96e93b1d --- /dev/null +++ b/archinstall/default_profiles/desktops/qtile.py @@ -0,0 +1,23 @@ +from typing import Optional, List, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class QtileProfile(XorgProfile): + def __init__(self): + super().__init__('Qtile', ProfileType.WindowMgr, description='') + + @property + def packages(self) -> List[str]: + return [ + 'qtile', + 'alacritty' + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm diff --git a/archinstall/default_profiles/desktops/sway.py b/archinstall/default_profiles/desktops/sway.py new file mode 100644 index 00000000..c757797d --- /dev/null +++ b/archinstall/default_profiles/desktops/sway.py @@ -0,0 +1,77 @@ +from enum import Enum +from typing import List, Optional, TYPE_CHECKING, Any + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile +from archinstall.lib.menu import Menu + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +class SeatAccess(Enum): + seatd = 'seatd' + polkit = 'polkit' + + +class SwayProfile(XorgProfile): + def __init__(self): + super().__init__( + 'Sway', + ProfileType.WindowMgr, + description='' + ) + + self.custom_settings = {'seat_access': None} + + @property + def packages(self) -> List[str]: + additional = [] + if seat := self.custom_settings.get('seat_access', None): + additional = [seat] + + return [ + "sway", + "swaybg", + "swaylock", + "swayidle", + "waybar", + "dmenu", + "brightnessctl", + "grim", + "slurp", + "pavucontrol", + "foot", + "xorg-xwayland" + ] + additional + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm + + @property + def services(self) -> List[str]: + if pref := self.custom_settings.get('seat_access', None): + return [pref] + return [] + + def _ask_seat_access(self): + # need to activate seat service and add to seat group + title = str(_('Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)')) + title += str(_('\n\nChoose an option to give Sway access to your hardware')) + + options = [e.value for e in SeatAccess] + default = None + + if seat := self.custom_settings.get('seat_access', None): + default = seat + + choice = Menu(title, options, skip=False, preset_values=default).run() + self.custom_settings['seat_access'] = choice.single_value + + def do_on_select(self): + self._ask_seat_access() + + def install(self, install_session: 'Installer'): + super().install(install_session) diff --git a/archinstall/default_profiles/desktops/xfce4.py b/archinstall/default_profiles/desktops/xfce4.py new file mode 100644 index 00000000..a7f0a7e6 --- /dev/null +++ b/archinstall/default_profiles/desktops/xfce4.py @@ -0,0 +1,26 @@ +from typing import List, Optional, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType, GreeterType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + _: Any + + +class Xfce4Profile(XorgProfile): + def __init__(self): + super().__init__('Xfce4', ProfileType.DesktopEnv, description='') + + @property + def packages(self) -> List[str]: + return [ + "xfce4", + "xfce4-goodies", + "pavucontrol", + "gvfs", + "xarchiver" + ] + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + return GreeterType.Lightdm diff --git a/archinstall/default_profiles/minimal.py b/archinstall/default_profiles/minimal.py new file mode 100644 index 00000000..f78708e9 --- /dev/null +++ b/archinstall/default_profiles/minimal.py @@ -0,0 +1,15 @@ +from typing import Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import Profile, ProfileType + +if TYPE_CHECKING: + _: Any + + +class MinimalProfile(Profile): + def __init__(self): + super().__init__( + 'Minimal', + ProfileType.Minimal, + description=str(_('A very basic installation that allows you to customize Arch Linux as you see fit.')) + ) diff --git a/archinstall/default_profiles/profile.py b/archinstall/default_profiles/profile.py new file mode 100644 index 00000000..4c85b0c7 --- /dev/null +++ b/archinstall/default_profiles/profile.py @@ -0,0 +1,205 @@ +from __future__ import annotations + +from enum import Enum, auto +from typing import List, Optional, Any, Dict, TYPE_CHECKING, TypeVar + +from archinstall.lib.utils.util import format_cols + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +TProfile = TypeVar('TProfile', bound='Profile') + + +class ProfileType(Enum): + # top level default_profiles + Server = 'Server' + Desktop = 'Desktop' + Xorg = 'Xorg' + Minimal = 'Minimal' + Custom = 'Custom' + # detailed selection default_profiles + ServerType = 'ServerType' + WindowMgr = 'Window Manager' + DesktopEnv = 'Desktop Environment' + CustomType = 'CustomType' + # special things + Tailored = 'Tailored' + Application = 'Application' + + +class GreeterType(Enum): + Lightdm = 'lightdm-gtk-greeter' + LightdmSlick = 'lightdm-slick-greeter' + Sddm = 'sddm' + Gdm = 'gdm' + Ly = 'ly' + + +class SelectResult(Enum): + NewSelection = auto() + SameSelection = auto() + ResetCurrent = auto() + + +class Profile: + def __init__( + self, + name: str, + profile_type: ProfileType, + description: str = '', + current_selection: List[TProfile] = [], + packages: List[str] = [], + services: List[str] = [], + support_gfx_driver: bool = False, + support_greeter: bool = False + ): + self.name = name + self.description = description + self.profile_type = profile_type + self.custom_settings: Dict[str, Any] = {} + + self._support_gfx_driver = support_gfx_driver + self._support_greeter = support_greeter + + # self.gfx_driver: Optional[str] = None + + self._current_selection = current_selection + self._packages = packages + self._services = services + + # Only used for custom default_profiles + self.custom_enabled = False + + @property + def current_selection(self) -> List[TProfile]: + return self._current_selection + + @property + def packages(self) -> List[str]: + """ + Returns a list of packages that should be installed when + this profile is among the chosen ones + """ + return self._packages + + @property + def services(self) -> List[str]: + """ + Returns a list of services that should be enabled when + this profile is among the chosen ones + """ + return self._services + + @property + def default_greeter_type(self) -> Optional[GreeterType]: + """ + Setting a default greeter type for a desktop profile + """ + return None + + def install(self, install_session: 'Installer'): + """ + Performs installation steps when this profile was selected + """ + + def post_install(self, install_session: 'Installer'): + """ + Hook that will be called when the installation process is + finished and custom installation steps for specific default_profiles + are needed + """ + + def json(self) -> Dict: + """ + Returns a json representation of the profile + """ + return {} + + def do_on_select(self) -> SelectResult: + """ + Hook that will be called when a profile is selected + """ + return SelectResult.NewSelection + + def set_custom_settings(self, settings: Dict[str, Any]): + """ + Set the custom settings for the profile. + This is also called when the settings are parsed from the config + and can be overridden to perform further actions based on the profile + """ + self.custom_settings = settings + + def current_selection_names(self) -> List[str]: + if self._current_selection: + return [s.name for s in self._current_selection] + return [] + + def reset(self): + self.set_current_selection([]) + + def set_current_selection(self, current_selection: List[TProfile]): + self._current_selection = current_selection + + def is_top_level_profile(self) -> bool: + top_levels = [ProfileType.Desktop, ProfileType.Server, ProfileType.Xorg, ProfileType.Minimal, ProfileType.Custom] + return self.profile_type in top_levels + + def is_desktop_profile(self) -> bool: + return self.profile_type == ProfileType.Desktop + + def is_server_type_profile(self) -> bool: + return self.profile_type == ProfileType.ServerType + + def is_desktop_type_profile(self) -> bool: + return self.profile_type == ProfileType.DesktopEnv or self.profile_type == ProfileType.WindowMgr + + def is_xorg_type_profile(self) -> bool: + return self.profile_type == ProfileType.Xorg + + def is_tailored(self) -> bool: + return self.profile_type == ProfileType.Tailored + + def is_custom_type_profile(self) -> bool: + return self.profile_type == ProfileType.CustomType + + def is_graphic_driver_supported(self) -> bool: + if not self._current_selection: + return self._support_gfx_driver + else: + if any([p._support_gfx_driver for p in self._current_selection]): + return True + return False + + def is_greeter_supported(self) -> bool: + return self._support_greeter + + def preview_text(self) -> Optional[str]: + """ + Override this method to provide a preview text for the profile + """ + return self.packages_text() + + def packages_text(self, include_sub_packages: bool = False) -> Optional[str]: + header = str(_('Installed packages')) + + text = '' + packages = [] + + if self.packages: + packages = self.packages + + if include_sub_packages: + for p in self.current_selection: + if p.packages: + packages += p.packages + + text += format_cols(sorted(set(packages))) + + if text: + text = f'{header}: \n{text}' + return text + + return None diff --git a/archinstall/default_profiles/server.py b/archinstall/default_profiles/server.py new file mode 100644 index 00000000..ab758975 --- /dev/null +++ b/archinstall/default_profiles/server.py @@ -0,0 +1,56 @@ +from typing import Any, TYPE_CHECKING, List + +from archinstall.lib.output import info +from archinstall.lib.menu import MenuSelectionType +from archinstall.lib.profile.profiles_handler import profile_handler +from archinstall.default_profiles.profile import ProfileType, Profile, SelectResult, TProfile + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +class ServerProfile(Profile): + def __init__(self, current_value: List[TProfile] = []): + super().__init__( + 'Server', + ProfileType.Server, + description=str(_('Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb')), + current_selection=current_value + ) + + def do_on_select(self) -> SelectResult: + available_servers = profile_handler.get_server_profiles() + + choice = profile_handler.select_profile( + available_servers, + self._current_selection, + title=str(_('Choose which servers to install, if none then a minimal installation will be done')), + multi=True + ) + + match choice.type_: + case MenuSelectionType.Selection: + self.set_current_selection(choice.value) # type: ignore + return SelectResult.NewSelection + case MenuSelectionType.Skip: + return SelectResult.SameSelection + case MenuSelectionType.Reset: + return SelectResult.ResetCurrent + + def post_install(self, install_session: 'Installer'): + for profile in self._current_selection: + profile.post_install(install_session) + + def install(self, install_session: 'Installer'): + server_info = self.current_selection_names() + details = ', '.join(server_info) + info(f'Now installing the selected servers: {details}') + + for server in self._current_selection: + info(f'Installing {server.name}...') + install_session.add_additional_packages(server.packages) + install_session.enable_service(server.services) + server.install(install_session) + + info('If your selections included multiple servers with the same port, you may have to reconfigure them.') diff --git a/archinstall/default_profiles/servers/__init__.py b/archinstall/default_profiles/servers/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/archinstall/default_profiles/servers/__init__.py diff --git a/archinstall/default_profiles/servers/cockpit.py b/archinstall/default_profiles/servers/cockpit.py new file mode 100644 index 00000000..8cac0976 --- /dev/null +++ b/archinstall/default_profiles/servers/cockpit.py @@ -0,0 +1,19 @@ +from typing import List + +from archinstall.default_profiles.profile import Profile, ProfileType + + +class CockpitProfile(Profile): + def __init__(self): + super().__init__( + 'Cockpit', + ProfileType.ServerType + ) + + @property + def packages(self) -> List[str]: + return ['cockpit', 'udisks2', 'packagekit'] + + @property + def services(self) -> List[str]: + return ['cockpit.socket'] diff --git a/archinstall/default_profiles/servers/docker.py b/archinstall/default_profiles/servers/docker.py new file mode 100644 index 00000000..f4800916 --- /dev/null +++ b/archinstall/default_profiles/servers/docker.py @@ -0,0 +1,33 @@ +from typing import List, Union, TYPE_CHECKING + +import archinstall + +from archinstall.default_profiles.profile import Profile, ProfileType +from archinstall.lib.models import User + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + + +class DockerProfile(Profile): + def __init__(self): + super().__init__( + 'Docker', + ProfileType.ServerType + ) + + @property + def packages(self) -> List[str]: + return ['docker'] + + @property + def services(self) -> List[str]: + return ['docker'] + + def post_install(self, install_session: 'Installer'): + users: Union[User, List[User]] = archinstall.arguments.get('!users', []) + if not isinstance(users, list): + users = [users] + + for user in users: + install_session.arch_chroot(f'usermod -a -G docker {user.username}') diff --git a/archinstall/default_profiles/servers/httpd.py b/archinstall/default_profiles/servers/httpd.py new file mode 100644 index 00000000..595ce84f --- /dev/null +++ b/archinstall/default_profiles/servers/httpd.py @@ -0,0 +1,19 @@ +from typing import List + +from archinstall.default_profiles.profile import Profile, ProfileType + + +class HttpdProfile(Profile): + def __init__(self): + super().__init__( + 'httpd', + ProfileType.ServerType + ) + + @property + def packages(self) -> List[str]: + return ['apache'] + + @property + def services(self) -> List[str]: + return ['httpd'] diff --git a/archinstall/default_profiles/servers/lighttpd.py b/archinstall/default_profiles/servers/lighttpd.py new file mode 100644 index 00000000..00aa5564 --- /dev/null +++ b/archinstall/default_profiles/servers/lighttpd.py @@ -0,0 +1,19 @@ +from typing import List + +from archinstall.default_profiles.profile import Profile, ProfileType + + +class LighttpdProfile(Profile): + def __init__(self): + super().__init__( + 'Lighttpd', + ProfileType.ServerType + ) + + @property + def packages(self) -> List[str]: + return ['lighttpd'] + + @property + def services(self) -> List[str]: + return ['lighttpd'] diff --git a/archinstall/default_profiles/servers/mariadb.py b/archinstall/default_profiles/servers/mariadb.py new file mode 100644 index 00000000..4506f1bc --- /dev/null +++ b/archinstall/default_profiles/servers/mariadb.py @@ -0,0 +1,25 @@ +from typing import List, TYPE_CHECKING + +from archinstall.default_profiles.profile import Profile, ProfileType + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + + +class MariadbProfile(Profile): + def __init__(self): + super().__init__( + 'Mariadb', + ProfileType.ServerType + ) + + @property + def packages(self) -> List[str]: + return ['mariadb'] + + @property + def services(self) -> List[str]: + return ['mariadb'] + + def post_install(self, install_session: 'Installer'): + install_session.arch_chroot('mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql') diff --git a/archinstall/default_profiles/servers/nginx.py b/archinstall/default_profiles/servers/nginx.py new file mode 100644 index 00000000..6038616c --- /dev/null +++ b/archinstall/default_profiles/servers/nginx.py @@ -0,0 +1,19 @@ +from typing import List + +from archinstall.default_profiles.profile import Profile, ProfileType + + +class NginxProfile(Profile): + def __init__(self): + super().__init__( + 'Nginx', + ProfileType.ServerType + ) + + @property + def packages(self) -> List[str]: + return ['nginx'] + + @property + def services(self) -> List[str]: + return ['nginx'] diff --git a/archinstall/default_profiles/servers/postgresql.py b/archinstall/default_profiles/servers/postgresql.py new file mode 100644 index 00000000..dba722ce --- /dev/null +++ b/archinstall/default_profiles/servers/postgresql.py @@ -0,0 +1,26 @@ +from typing import List, TYPE_CHECKING + +from archinstall.default_profiles.profile import Profile, ProfileType + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + + +class PostgresqlProfile(Profile): + def __init__(self): + super().__init__( + 'Postgresql', + ProfileType.ServerType, + '' + ) + + @property + def packages(self) -> List[str]: + return ['postgresql'] + + @property + def services(self) -> List[str]: + return ['postgresql'] + + def post_install(self, install_session: 'Installer'): + install_session.arch_chroot("initdb -D /var/lib/postgres/data", run_as='postgres') diff --git a/archinstall/default_profiles/servers/sshd.py b/archinstall/default_profiles/servers/sshd.py new file mode 100644 index 00000000..7f855b1a --- /dev/null +++ b/archinstall/default_profiles/servers/sshd.py @@ -0,0 +1,19 @@ +from typing import List + +from archinstall.default_profiles.profile import Profile, ProfileType + + +class SshdProfile(Profile): + def __init__(self): + super().__init__( + 'sshd', + ProfileType.ServerType + ) + + @property + def packages(self) -> List[str]: + return ['openssh'] + + @property + def services(self) -> List[str]: + return ['sshd'] diff --git a/archinstall/default_profiles/servers/tomcat.py b/archinstall/default_profiles/servers/tomcat.py new file mode 100644 index 00000000..9bd8837b --- /dev/null +++ b/archinstall/default_profiles/servers/tomcat.py @@ -0,0 +1,19 @@ +from typing import List + +from archinstall.default_profiles.profile import Profile, ProfileType + + +class TomcatProfile(Profile): + def __init__(self): + super().__init__( + 'Tomcat', + ProfileType.ServerType + ) + + @property + def packages(self) -> List[str]: + return ['tomcat10'] + + @property + def services(self) -> List[str]: + return ['tomcat10'] diff --git a/archinstall/default_profiles/tailored.py b/archinstall/default_profiles/tailored.py new file mode 100644 index 00000000..62666249 --- /dev/null +++ b/archinstall/default_profiles/tailored.py @@ -0,0 +1,21 @@ +from typing import List, Any, TYPE_CHECKING + +from archinstall.default_profiles.profile import ProfileType +from archinstall.default_profiles.xorg import XorgProfile + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + _: Any + + +class TailoredProfile(XorgProfile): + def __init__(self): + super().__init__('52-54-00-12-34-56', ProfileType.Tailored, description='') + + @property + def packages(self) -> List[str]: + return ['nano', 'wget', 'git'] + + def install(self, install_session: 'Installer'): + super().install(install_session) + # do whatever you like here :) diff --git a/archinstall/default_profiles/xorg.py b/archinstall/default_profiles/xorg.py new file mode 100644 index 00000000..88ba55a6 --- /dev/null +++ b/archinstall/default_profiles/xorg.py @@ -0,0 +1,34 @@ +from typing import Any, Optional, TYPE_CHECKING, List + +from archinstall.default_profiles.profile import Profile, ProfileType + +if TYPE_CHECKING: + _: Any + + +class XorgProfile(Profile): + def __init__( + self, + name: str = 'Xorg', + profile_type: ProfileType = ProfileType.Xorg, + description: str = str(_('Installs a minimal system as well as xorg and graphics drivers.')), + ): + super().__init__( + name, + profile_type, + description=description, + support_gfx_driver=True + ) + + def preview_text(self) -> Optional[str]: + text = str(_('Environment type: {}')).format(self.profile_type.value) + if packages := self.packages_text(): + text += f'\n{packages}' + + return text + + @property + def packages(self) -> List[str]: + return [ + 'xorg-server' + ] |