feat: add plymouth config

This commit is contained in:
Mário Victor Ribeiro Silva 2026-05-24 23:10:33 -03:00
parent d92a98d3bf
commit 1f66848b22
8 changed files with 206 additions and 0 deletions

View File

@ -16,6 +16,7 @@ from pydantic.dataclasses import dataclass as p_dataclass
from archinstall.lib.crypt import decrypt
from archinstall.lib.log import debug, error, logger, warn
from archinstall.lib.menu.util import get_password
from archinstall.lib.models import PlymouthConfiguration
from archinstall.lib.models.application import ApplicationConfiguration, ZramConfiguration
from archinstall.lib.models.authentication import AuthenticationConfiguration
from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration
@ -88,6 +89,7 @@ class ArchConfigType(StrEnum):
SERVICES = 'services'
PACKAGES = 'packages'
PACMAN_CONFIG = 'pacman_config'
PLYMOUTH_CONFIG = 'plymouth_config'
CUSTOM_COMMANDS = 'custom_commands'
def text(self) -> str:
@ -130,6 +132,8 @@ class ArchConfigType(StrEnum):
return tr('Additional packages')
case ArchConfigType.PACMAN_CONFIG:
return tr('Pacman')
case ArchConfigType.PLYMOUTH_CONFIG:
return tr('Plymouth')
case ArchConfigType.CUSTOM_COMMANDS:
return tr('Custom commands')
case ArchConfigType.USERS:
@ -159,6 +163,7 @@ class ArchConfig:
ntp: bool = True
packages: list[str] = field(default_factory=list)
pacman_config: PacmanConfiguration = field(default_factory=PacmanConfiguration.default)
plymouth_config: PlymouthConfiguration | None = None
timezone: str = 'UTC'
services: list[str] = field(default_factory=list)
custom_commands: list[str] = field(default_factory=list)
@ -240,6 +245,9 @@ class ArchConfig:
if self.app_config:
cfg[ArchConfigType.APP_CONFIG] = self.app_config
if self.plymouth_config:
cfg[ArchConfigType.PLYMOUTH_CONFIG] = self.plymouth_config
return cfg
@classmethod
@ -336,6 +344,9 @@ class ArchConfig:
if services := args_config.get('services', []):
arch_config.services = services
if plymouth_config := args_config.get('plymouth_config', None):
arch_config.plymouth_config = PlymouthConfiguration.parse_arg(plymouth_config)
# DEPRECATED: backwards compatibility
root_password = None
if root_password := args_config.get('!root-password', None):

View File

@ -25,11 +25,13 @@ from archinstall.lib.models.network import NetworkConfiguration, NicType
from archinstall.lib.models.package_types import DEFAULT_KERNEL
from archinstall.lib.models.packages import Repository
from archinstall.lib.models.pacman import PacmanConfiguration
from archinstall.lib.models.plymouth import PlymouthConfiguration
from archinstall.lib.models.profile import ProfileConfiguration
from archinstall.lib.network.network_menu import select_network
from archinstall.lib.packages.packages import list_available_packages, select_additional_packages
from archinstall.lib.pacman.config import PacmanConfig
from archinstall.lib.pacman.pacman_menu import PacmanMenu
from archinstall.lib.plymouth.plymouth_menu import select_plymouth_theme
from archinstall.lib.translationhandler import Language, tr, translation_handler
from archinstall.lib.utils.format import as_table
from archinstall.tui.components import tui
@ -150,6 +152,13 @@ class GlobalMenu(AbstractMenu[None]):
preview_action=self._prev_pacman_config,
key='pacman_config',
),
MenuItem(
text=tr('Plymouth'),
action=self._plymouth_configuration,
value=None,
preview_action=self._prev_plymouth_config,
key='plymouth_config',
),
MenuItem(
text=tr('Additional packages'),
action=self._select_additional_packages,
@ -432,6 +441,15 @@ class GlobalMenu(AbstractMenu[None]):
output += '{}: {}'.format(tr('Color'), config.color)
return output
async def _plymouth_configuration(self, preset: PlymouthConfiguration | None) -> PlymouthConfiguration | None:
return await select_plymouth_theme(preset)
def _prev_plymouth_config(self, item: MenuItem) -> str | None:
if not item.value:
return None
config: PlymouthConfiguration = item.value
return config.preview()
def _prev_kernel(self, item: MenuItem) -> str | None:
if item.value:
kernel = ', '.join(item.value)

View File

@ -30,6 +30,7 @@ from archinstall.lib.models.locale import LocaleConfiguration
from archinstall.lib.models.mirrors import CustomRepository, MirrorConfiguration, MirrorRegion
from archinstall.lib.models.network import NetworkConfiguration, Nic, NicType
from archinstall.lib.models.packages import LocalPackage, PackageSearch, PackageSearchResult, Repository
from archinstall.lib.models.plymouth import PlymouthConfiguration, PlymouthTheme
from archinstall.lib.models.profile import ProfileConfiguration
from archinstall.lib.models.users import PasswordStrength, User
@ -69,6 +70,8 @@ __all__ = [
'PartitionTable',
'PartitionType',
'PasswordStrength',
'PlymouthConfiguration',
'PlymouthTheme',
'PrintServiceConfiguration',
'ProfileConfiguration',
'Repository',

View File

@ -0,0 +1,57 @@
from dataclasses import dataclass
from enum import Enum
from typing import Self, TypedDict, override
from archinstall.lib.models.config import SubConfig
from archinstall.lib.translationhandler import tr
class PlymouthConfigSerialization(TypedDict):
plymouth: str
class PlymouthTheme(Enum):
DISABLED = 'Disabled'
BGRT = 'bgrt'
FADE_IN = 'fade-in'
GLOW = 'glow'
SCRIPT = 'script'
SOLAR = 'solar'
SPINNER = 'spinner'
SPINFINITY = 'spinfinity'
TRIBAR = 'tribar'
TEXT = 'text'
DETAILS = 'details'
@dataclass
class PlymouthConfiguration(SubConfig):
plymouth: PlymouthTheme = PlymouthTheme.DISABLED
@override
def json(self) -> PlymouthConfigSerialization:
return {
'plymouth': self.plymouth.value,
}
@classmethod
def default(cls) -> Self:
return cls()
@classmethod
def parse_arg(cls, arg: PlymouthConfigSerialization) -> Self:
config = cls.default()
if 'plymouth' in arg:
config.plymouth = PlymouthTheme(arg['plymouth'])
return config
@override
def summary(self) -> str:
if self.plymouth == PlymouthTheme.DISABLED:
return tr('Disabled')
return tr('{} Selected').format(self.plymouth.value)
def preview(self) -> str:
return f'Plymouth: {tr(self.plymouth.value)}'

View File

View File

@ -0,0 +1,87 @@
from typing import TYPE_CHECKING
from archinstall.lib.log import debug
from archinstall.lib.models.plymouth import PlymouthConfiguration, PlymouthTheme
if TYPE_CHECKING:
from archinstall.lib.installer import Installer
class PlymouthHandler:
@property
def plymouth_packages(self) -> list[str]:
return [
'plymouth',
]
@property
def kernel_params(self) -> list[str]:
return [
'quiet',
'splash',
]
@property
def plymouth_hook(self) -> str:
return 'plymouth'
@property
def _hook_anchors(self) -> list[tuple[str, bool]]:
return [
# hook_name, insert_after
('encrypt', False),
('sd-encrypt', False),
('systemd', True),
('systemd', True),
('filesystems', False),
('keyboard', True),
]
def install(self, install_session: Installer, plymouth_config: PlymouthConfiguration) -> None:
debug(f'Installing plymouth with theme: {plymouth_config.plymouth.value}')
if plymouth_config.plymouth == PlymouthTheme.DISABLED:
debug('No plymouth theme selected, skipping installation.')
return
install_session.add_additional_packages(self.plymouth_packages)
self._add_kernel_params(install_session)
self._add_hooks(install_session)
self._set_theme(install_session, plymouth_config.plymouth.value)
self._regenerate_mkinitcpio(install_session)
debug('Plymouth install completed')
def _add_kernel_params(self, install_session: Installer) -> None:
debug(f'Adding kernel params for plymouth {self.kernel_params}')
for param in self.kernel_params:
if param not in install_session._kernel_params:
install_session._kernel_params.append(param)
def _add_hooks(self, install_session: Installer) -> None:
debug('Adding Plymouth hook')
if self.plymouth_hook in install_session._hooks:
debug(f'{self.plymouth_hook} hook already present')
return
for hook, insert_after in self._hook_anchors:
try:
index = install_session._hooks.index(hook)
position = index + (1 if insert_after else 0)
install_session._hooks.insert(position, self.plymouth_hook)
debug(f'Plymouth hook inserted {["before", "after"][insert_after]} {hook!r}')
return
except ValueError:
continue
install_session._hooks.append(self.plymouth_hook)
debug('Plymouth hook appended at end (no anchor hook found)')
def _set_theme(self, install_session: Installer, theme: str) -> None:
debug(f'Set plymouth theme to {theme}')
install_session.arch_chroot(f'plymouth-set-default-theme {theme}')
def _regenerate_mkinitcpio(self, install_session: Installer) -> None:
install_session.mkinitcpio(['-P'])

View File

@ -0,0 +1,26 @@
from archinstall.lib.menu.helpers import Selection
from archinstall.lib.models.plymouth import PlymouthConfiguration, PlymouthTheme
from archinstall.lib.translationhandler import tr
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
async def select_plymouth_theme(preset: PlymouthConfiguration | None = None) -> PlymouthConfiguration | None:
items = [MenuItem(t.value, value=t) for t in PlymouthTheme]
group = MenuItemGroup(items, sort_items=False)
group.set_selected_by_value(preset.plymouth if preset else PlymouthTheme.DISABLED)
result = await Selection[PlymouthTheme](
group,
header=tr('Select Plymouth theme'),
allow_reset=True,
allow_skip=True,
).show()
match result.type_:
case ResultType.Skip:
return preset
case ResultType.Reset:
return None
case ResultType.Selection:
return PlymouthConfiguration(result.get_value())

View File

@ -20,6 +20,7 @@ from archinstall.lib.models.device import DiskLayoutType, EncryptionType
from archinstall.lib.models.users import User
from archinstall.lib.network.network_handler import install_network_config
from archinstall.lib.packages.util import check_version_upgrade
from archinstall.lib.plymouth.plymouth_handler import PlymouthHandler
from archinstall.lib.profile.profiles_handler import profile_handler
from archinstall.lib.translationhandler import tr
from archinstall.tui.components import tui
@ -114,6 +115,9 @@ def perform_installation(
if config.swap and config.swap.enabled:
installation.setup_swap(algo=config.swap.algorithm)
if config.plymouth_config:
PlymouthHandler().install(installation, config.plymouth_config)
if config.bootloader_config and config.bootloader_config.bootloader != Bootloader.NO_BOOTLOADER:
installation.add_bootloader(
config.bootloader_config.bootloader,