Move audio configuration to application config (#3612)
This commit is contained in:
parent
63848f645f
commit
37b3985625
|
|
@ -0,0 +1,80 @@
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from archinstall.lib.hardware import SysInfo
|
||||||
|
from archinstall.lib.models.application import Audio, AudioConfiguration
|
||||||
|
from archinstall.lib.models.users import User
|
||||||
|
from archinstall.lib.output import debug
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from archinstall.lib.installer import Installer
|
||||||
|
|
||||||
|
|
||||||
|
class AudioApp:
|
||||||
|
@property
|
||||||
|
def pulseaudio_packages(self) -> list[str]:
|
||||||
|
return [
|
||||||
|
'pulseaudio',
|
||||||
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pipewire_packages(self) -> list[str]:
|
||||||
|
return [
|
||||||
|
'pipewire',
|
||||||
|
'pipewire-alsa',
|
||||||
|
'pipewire-jack',
|
||||||
|
'pipewire-pulse',
|
||||||
|
'gst-plugin-pipewire',
|
||||||
|
'libpulse',
|
||||||
|
'wireplumber',
|
||||||
|
]
|
||||||
|
|
||||||
|
def _enable_pipewire(
|
||||||
|
self,
|
||||||
|
install_session: 'Installer',
|
||||||
|
users: list['User'] | None = None,
|
||||||
|
) -> None:
|
||||||
|
if users is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
for user in users:
|
||||||
|
# Create the full path for enabling the pipewire systemd items
|
||||||
|
service_dir = install_session.target / 'home' / user.username / '.config' / 'systemd' / 'user' / 'default.target.wants'
|
||||||
|
service_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Set ownership of the entire user catalogue
|
||||||
|
install_session.arch_chroot(f'chown -R {user.username}:{user.username} /home/{user.username}')
|
||||||
|
|
||||||
|
# symlink in the correct pipewire systemd items
|
||||||
|
install_session.arch_chroot(
|
||||||
|
f'ln -sf /usr/lib/systemd/user/pipewire-pulse.service /home/{user.username}/.config/systemd/user/default.target.wants/pipewire-pulse.service',
|
||||||
|
run_as=user.username,
|
||||||
|
)
|
||||||
|
install_session.arch_chroot(
|
||||||
|
f'ln -sf /usr/lib/systemd/user/pipewire-pulse.socket /home/{user.username}/.config/systemd/user/default.target.wants/pipewire-pulse.socket',
|
||||||
|
run_as=user.username,
|
||||||
|
)
|
||||||
|
|
||||||
|
def install(
|
||||||
|
self,
|
||||||
|
install_session: 'Installer',
|
||||||
|
audio_config: AudioConfiguration,
|
||||||
|
users: list[User] | None = None,
|
||||||
|
) -> None:
|
||||||
|
debug(f'Installing audio server: {audio_config.audio.value}')
|
||||||
|
|
||||||
|
if audio_config.audio == Audio.NO_AUDIO:
|
||||||
|
debug('No audio server selected, skipping installation.')
|
||||||
|
return
|
||||||
|
|
||||||
|
if SysInfo.requires_sof_fw():
|
||||||
|
install_session.add_additional_packages('sof-firmware')
|
||||||
|
|
||||||
|
if SysInfo.requires_alsa_fw():
|
||||||
|
install_session.add_additional_packages('alsa-firmware')
|
||||||
|
|
||||||
|
match audio_config.audio:
|
||||||
|
case Audio.PIPEWIRE:
|
||||||
|
install_session.add_additional_packages(self.pipewire_packages)
|
||||||
|
self._enable_pipewire(install_session, users)
|
||||||
|
case Audio.PULSEAUDIO:
|
||||||
|
install_session.add_additional_packages(self.pulseaudio_packages)
|
||||||
|
|
@ -6,7 +6,7 @@ if TYPE_CHECKING:
|
||||||
from archinstall.lib.installer import Installer
|
from archinstall.lib.installer import Installer
|
||||||
|
|
||||||
|
|
||||||
class Bluetooth:
|
class BluetoothApp:
|
||||||
@property
|
@property
|
||||||
def packages(self) -> list[str]:
|
def packages(self) -> list[str]:
|
||||||
return [
|
return [
|
||||||
|
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
from typing import TYPE_CHECKING, override
|
|
||||||
|
|
||||||
from archinstall.default_profiles.profile import Profile, ProfileType
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from archinstall.lib.installer import Installer
|
|
||||||
from archinstall.lib.models.users import User
|
|
||||||
|
|
||||||
|
|
||||||
class PipewireProfile(Profile):
|
|
||||||
def __init__(self) -> None:
|
|
||||||
super().__init__('Pipewire', ProfileType.Application)
|
|
||||||
|
|
||||||
@property
|
|
||||||
@override
|
|
||||||
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') -> None:
|
|
||||||
from archinstall.lib.args import arch_config_handler
|
|
||||||
|
|
||||||
users: list[User] | None = arch_config_handler.config.users
|
|
||||||
|
|
||||||
if users is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
for user in users:
|
|
||||||
# Create the full path for enabling the pipewire systemd items
|
|
||||||
service_dir = install_session.target / 'home' / user.username / '.config' / 'systemd' / 'user' / 'default.target.wants'
|
|
||||||
service_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
# Set ownership of the entire user catalogue
|
|
||||||
install_session.arch_chroot(f'chown -R {user.username}:{user.username} /home/{user.username}')
|
|
||||||
|
|
||||||
# symlink in the correct pipewire systemd items
|
|
||||||
install_session.arch_chroot(
|
|
||||||
f'ln -sf /usr/lib/systemd/user/pipewire-pulse.service /home/{user.username}/.config/systemd/user/default.target.wants/pipewire-pulse.service',
|
|
||||||
run_as=user.username,
|
|
||||||
)
|
|
||||||
install_session.arch_chroot(
|
|
||||||
f'ln -sf /usr/lib/systemd/user/pipewire-pulse.socket /home/{user.username}/.config/systemd/user/default.target.wants/pipewire-pulse.socket',
|
|
||||||
run_as=user.username,
|
|
||||||
)
|
|
||||||
|
|
||||||
@override
|
|
||||||
def install(self, install_session: 'Installer') -> None:
|
|
||||||
super().install(install_session)
|
|
||||||
install_session.add_additional_packages(self.packages)
|
|
||||||
self._enable_pipewire_for_all(install_session)
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from archinstall.applications.bluetooth import Bluetooth
|
from archinstall.applications.audio import AudioApp
|
||||||
|
from archinstall.applications.bluetooth import BluetoothApp
|
||||||
from archinstall.lib.models.application import ApplicationConfiguration
|
from archinstall.lib.models.application import ApplicationConfiguration
|
||||||
|
from archinstall.lib.models.users import User
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from archinstall.lib.installer import Installer
|
from archinstall.lib.installer import Installer
|
||||||
|
|
@ -11,13 +13,16 @@ class ApplicationHandler:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def install_applications(
|
def install_applications(self, install_session: 'Installer', app_config: ApplicationConfiguration, users: list['User'] | None = None) -> None:
|
||||||
self,
|
|
||||||
install_session: 'Installer',
|
|
||||||
app_config: ApplicationConfiguration,
|
|
||||||
) -> None:
|
|
||||||
if app_config.bluetooth_config:
|
if app_config.bluetooth_config:
|
||||||
Bluetooth().install(install_session)
|
BluetoothApp().install(install_session)
|
||||||
|
|
||||||
|
if app_config.audio_config:
|
||||||
|
AudioApp().install(
|
||||||
|
install_session,
|
||||||
|
app_config.audio_config,
|
||||||
|
users,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
application_handler = ApplicationHandler()
|
application_handler = ApplicationHandler()
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
from typing import override
|
from typing import override
|
||||||
|
|
||||||
from archinstall.lib.menu.abstract_menu import AbstractSubMenu
|
from archinstall.lib.menu.abstract_menu import AbstractSubMenu
|
||||||
from archinstall.lib.models.application import ApplicationConfiguration, BluetoothConfiguration
|
from archinstall.lib.models.application import ApplicationConfiguration, Audio, AudioConfiguration, BluetoothConfiguration
|
||||||
from archinstall.lib.translationhandler import tr
|
from archinstall.lib.translationhandler import tr
|
||||||
from archinstall.tui.curses_menu import SelectMenu
|
from archinstall.tui.curses_menu import SelectMenu
|
||||||
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
|
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
|
||||||
from archinstall.tui.types import Alignment, Orientation
|
from archinstall.tui.result import ResultType
|
||||||
|
from archinstall.tui.types import Alignment, FrameProperties, Orientation
|
||||||
|
|
||||||
|
|
||||||
class ApplicationMenu(AbstractSubMenu[ApplicationConfiguration]):
|
class ApplicationMenu(AbstractSubMenu[ApplicationConfiguration]):
|
||||||
|
|
@ -41,6 +42,12 @@ class ApplicationMenu(AbstractSubMenu[ApplicationConfiguration]):
|
||||||
preview_action=self._prev_bluetooth,
|
preview_action=self._prev_bluetooth,
|
||||||
key='bluetooth_config',
|
key='bluetooth_config',
|
||||||
),
|
),
|
||||||
|
MenuItem(
|
||||||
|
text=tr('Audio'),
|
||||||
|
action=select_audio,
|
||||||
|
preview_action=self._prev_audio,
|
||||||
|
key='audio_config',
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
def _prev_bluetooth(self, item: MenuItem) -> str | None:
|
def _prev_bluetooth(self, item: MenuItem) -> str | None:
|
||||||
|
|
@ -52,6 +59,12 @@ class ApplicationMenu(AbstractSubMenu[ApplicationConfiguration]):
|
||||||
return output
|
return output
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _prev_audio(self, item: MenuItem) -> str | None:
|
||||||
|
if item.value is not None:
|
||||||
|
config: AudioConfiguration = item.value
|
||||||
|
return f'{tr("Audio")}: {config.audio.value}'
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def select_bluetooth(preset: BluetoothConfiguration | None) -> BluetoothConfiguration | None:
|
def select_bluetooth(preset: BluetoothConfiguration | None) -> BluetoothConfiguration | None:
|
||||||
group = MenuItemGroup.yes_no()
|
group = MenuItemGroup.yes_no()
|
||||||
|
|
@ -74,3 +87,26 @@ def select_bluetooth(preset: BluetoothConfiguration | None) -> BluetoothConfigur
|
||||||
enabled = result.item() == MenuItem.yes()
|
enabled = result.item() == MenuItem.yes()
|
||||||
|
|
||||||
return BluetoothConfiguration(enabled)
|
return BluetoothConfiguration(enabled)
|
||||||
|
|
||||||
|
|
||||||
|
def select_audio(preset: AudioConfiguration | None = None) -> AudioConfiguration | None:
|
||||||
|
items = [MenuItem(a.value, value=a) for a in Audio]
|
||||||
|
group = MenuItemGroup(items)
|
||||||
|
|
||||||
|
if preset:
|
||||||
|
group.set_focus_by_value(preset.audio)
|
||||||
|
|
||||||
|
result = SelectMenu[Audio](
|
||||||
|
group,
|
||||||
|
allow_skip=True,
|
||||||
|
alignment=Alignment.CENTER,
|
||||||
|
frame=FrameProperties.min(tr('Audio')),
|
||||||
|
).run()
|
||||||
|
|
||||||
|
match result.type_:
|
||||||
|
case ResultType.Skip:
|
||||||
|
return preset
|
||||||
|
case ResultType.Selection:
|
||||||
|
return AudioConfiguration(audio=result.get_value())
|
||||||
|
case ResultType.Reset:
|
||||||
|
raise ValueError('Unhandled result type')
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ from pydantic.dataclasses import dataclass as p_dataclass
|
||||||
|
|
||||||
from archinstall.lib.crypt import decrypt
|
from archinstall.lib.crypt import decrypt
|
||||||
from archinstall.lib.models.application import ApplicationConfiguration
|
from archinstall.lib.models.application import ApplicationConfiguration
|
||||||
from archinstall.lib.models.audio_configuration import AudioConfiguration
|
|
||||||
from archinstall.lib.models.bootloader import Bootloader
|
from archinstall.lib.models.bootloader import Bootloader
|
||||||
from archinstall.lib.models.device_model import DiskEncryption, DiskLayoutConfiguration
|
from archinstall.lib.models.device_model import DiskEncryption, DiskLayoutConfiguration
|
||||||
from archinstall.lib.models.locale import LocaleConfiguration
|
from archinstall.lib.models.locale import LocaleConfiguration
|
||||||
|
|
@ -63,8 +62,7 @@ class ArchConfig:
|
||||||
network_config: NetworkConfiguration | None = None
|
network_config: NetworkConfiguration | None = None
|
||||||
bootloader: Bootloader = field(default=Bootloader.get_default())
|
bootloader: Bootloader = field(default=Bootloader.get_default())
|
||||||
uki: bool = False
|
uki: bool = False
|
||||||
audio_config: AudioConfiguration | None = None
|
app_config: ApplicationConfiguration | None = None
|
||||||
application_config: ApplicationConfiguration | None = None
|
|
||||||
hostname: str = 'archlinux'
|
hostname: str = 'archlinux'
|
||||||
kernels: list[str] = field(default_factory=lambda: ['linux'])
|
kernels: list[str] = field(default_factory=lambda: ['linux'])
|
||||||
ntp: bool = True
|
ntp: bool = True
|
||||||
|
|
@ -106,8 +104,7 @@ class ArchConfig:
|
||||||
'services': self.services,
|
'services': self.services,
|
||||||
'custom_commands': self.custom_commands,
|
'custom_commands': self.custom_commands,
|
||||||
'bootloader': self.bootloader.json(),
|
'bootloader': self.bootloader.json(),
|
||||||
'audio_config': self.audio_config.json() if self.audio_config else None,
|
'app_config': self.app_config.json() if self.app_config else None,
|
||||||
'app_config': self.application_config.json() if self.application_config else None,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.locale_config:
|
if self.locale_config:
|
||||||
|
|
@ -184,11 +181,12 @@ class ArchConfig:
|
||||||
if args_config.get('uki') and not arch_config.bootloader.has_uki_support():
|
if args_config.get('uki') and not arch_config.bootloader.has_uki_support():
|
||||||
arch_config.uki = False
|
arch_config.uki = False
|
||||||
|
|
||||||
if audio_config := args_config.get('audio_config', None):
|
# deprecated: backwards compatibility
|
||||||
arch_config.audio_config = AudioConfiguration.parse_arg(audio_config)
|
audio_config_args = args_config.get('audio_config', None)
|
||||||
|
app_config_args = args_config.get('app_config', None)
|
||||||
|
|
||||||
if app_config := args_config.get('app_config', None):
|
if audio_config_args is not None or app_config_args is not None:
|
||||||
arch_config.application_config = ApplicationConfiguration.parse_arg(app_config)
|
arch_config.app_config = ApplicationConfiguration.parse_arg(app_config_args, audio_config_args)
|
||||||
|
|
||||||
if hostname := args_config.get('hostname', ''):
|
if hostname := args_config.get('hostname', ''):
|
||||||
arch_config.hostname = hostname
|
arch_config.hostname = hostname
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ from .interactions.general_conf import (
|
||||||
add_number_of_parallel_downloads,
|
add_number_of_parallel_downloads,
|
||||||
ask_additional_packages_to_install,
|
ask_additional_packages_to_install,
|
||||||
ask_for_a_timezone,
|
ask_for_a_timezone,
|
||||||
ask_for_audio_selection,
|
|
||||||
ask_hostname,
|
ask_hostname,
|
||||||
ask_ntp,
|
ask_ntp,
|
||||||
)
|
)
|
||||||
|
|
@ -26,7 +25,6 @@ from .interactions.system_conf import ask_for_bootloader, ask_for_swap, ask_for_
|
||||||
from .locale.locale_menu import LocaleMenu
|
from .locale.locale_menu import LocaleMenu
|
||||||
from .menu.abstract_menu import CONFIG_KEY, AbstractMenu
|
from .menu.abstract_menu import CONFIG_KEY, AbstractMenu
|
||||||
from .mirrors import MirrorMenu
|
from .mirrors import MirrorMenu
|
||||||
from .models.audio_configuration import AudioConfiguration
|
|
||||||
from .models.bootloader import Bootloader
|
from .models.bootloader import Bootloader
|
||||||
from .models.locale import LocaleConfiguration
|
from .models.locale import LocaleConfiguration
|
||||||
from .models.mirrors import MirrorConfiguration
|
from .models.mirrors import MirrorConfiguration
|
||||||
|
|
@ -128,12 +126,6 @@ class GlobalMenu(AbstractMenu[None]):
|
||||||
preview_action=self._prev_profile,
|
preview_action=self._prev_profile,
|
||||||
key='profile_config',
|
key='profile_config',
|
||||||
),
|
),
|
||||||
MenuItem(
|
|
||||||
text=tr('Audio'),
|
|
||||||
action=ask_for_audio_selection,
|
|
||||||
preview_action=self._prev_audio,
|
|
||||||
key='audio_config',
|
|
||||||
),
|
|
||||||
MenuItem(
|
MenuItem(
|
||||||
text=tr('Applications'),
|
text=tr('Applications'),
|
||||||
action=self._select_applications,
|
action=self._select_applications,
|
||||||
|
|
@ -311,7 +303,13 @@ class GlobalMenu(AbstractMenu[None]):
|
||||||
|
|
||||||
if app_config.bluetooth_config:
|
if app_config.bluetooth_config:
|
||||||
output += f'{tr("Bluetooth")}: '
|
output += f'{tr("Bluetooth")}: '
|
||||||
output += tr('Enabled') if app_config.bluetooth_config.enabled else tr('Disabled') + '\n'
|
output += tr('Enabled') if app_config.bluetooth_config.enabled else tr('Disabled')
|
||||||
|
output += '\n'
|
||||||
|
|
||||||
|
if app_config.audio_config:
|
||||||
|
audio_config = app_config.audio_config
|
||||||
|
output += f'{tr("Audio")}: {audio_config.audio.value}'
|
||||||
|
output += '\n'
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
@ -378,12 +376,6 @@ class GlobalMenu(AbstractMenu[None]):
|
||||||
return f'{tr("Root password")}: {password.hidden()}'
|
return f'{tr("Root password")}: {password.hidden()}'
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _prev_audio(self, item: MenuItem) -> str | None:
|
|
||||||
if item.value is not None:
|
|
||||||
config: AudioConfiguration = item.value
|
|
||||||
return f'{tr("Audio")}: {config.audio.value}'
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _prev_parallel_dw(self, item: MenuItem) -> str | None:
|
def _prev_parallel_dw(self, item: MenuItem) -> str | None:
|
||||||
if item.value is not None:
|
if item.value is not None:
|
||||||
return f'{tr("Parallel Downloads")}: {item.value}'
|
return f'{tr("Parallel Downloads")}: {item.value}'
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ from .general_conf import (
|
||||||
add_number_of_parallel_downloads,
|
add_number_of_parallel_downloads,
|
||||||
ask_additional_packages_to_install,
|
ask_additional_packages_to_install,
|
||||||
ask_for_a_timezone,
|
ask_for_a_timezone,
|
||||||
ask_for_audio_selection,
|
|
||||||
ask_hostname,
|
ask_hostname,
|
||||||
ask_ntp,
|
ask_ntp,
|
||||||
select_archinstall_language,
|
select_archinstall_language,
|
||||||
|
|
@ -26,7 +25,6 @@ __all__ = [
|
||||||
'ask_additional_packages_to_install',
|
'ask_additional_packages_to_install',
|
||||||
'ask_for_a_timezone',
|
'ask_for_a_timezone',
|
||||||
'ask_for_additional_users',
|
'ask_for_additional_users',
|
||||||
'ask_for_audio_selection',
|
|
||||||
'ask_for_bootloader',
|
'ask_for_bootloader',
|
||||||
'ask_for_swap',
|
'ask_for_swap',
|
||||||
'ask_for_uki',
|
'ask_for_uki',
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ from archinstall.tui.result import ResultType
|
||||||
from archinstall.tui.types import Alignment, FrameProperties, Orientation, PreviewStyle
|
from archinstall.tui.types import Alignment, FrameProperties, Orientation, PreviewStyle
|
||||||
|
|
||||||
from ..locale.utils import list_timezones
|
from ..locale.utils import list_timezones
|
||||||
from ..models.audio_configuration import Audio, AudioConfiguration
|
|
||||||
from ..models.packages import AvailablePackage, PackageGroup
|
from ..models.packages import AvailablePackage, PackageGroup
|
||||||
from ..output import warn
|
from ..output import warn
|
||||||
from ..translationhandler import Language
|
from ..translationhandler import Language
|
||||||
|
|
@ -102,29 +101,6 @@ def ask_for_a_timezone(preset: str | None = None) -> str | None:
|
||||||
return result.get_value()
|
return result.get_value()
|
||||||
|
|
||||||
|
|
||||||
def ask_for_audio_selection(preset: AudioConfiguration | None = None) -> AudioConfiguration | None:
|
|
||||||
items = [MenuItem(a.value, value=a) for a in Audio]
|
|
||||||
group = MenuItemGroup(items)
|
|
||||||
|
|
||||||
if preset:
|
|
||||||
group.set_focus_by_value(preset.audio)
|
|
||||||
|
|
||||||
result = SelectMenu[Audio](
|
|
||||||
group,
|
|
||||||
allow_skip=True,
|
|
||||||
alignment=Alignment.CENTER,
|
|
||||||
frame=FrameProperties.min(tr('Audio')),
|
|
||||||
).run()
|
|
||||||
|
|
||||||
match result.type_:
|
|
||||||
case ResultType.Skip:
|
|
||||||
return preset
|
|
||||||
case ResultType.Selection:
|
|
||||||
return AudioConfiguration(audio=result.get_value())
|
|
||||||
case ResultType.Reset:
|
|
||||||
raise ValueError('Unhandled result type')
|
|
||||||
|
|
||||||
|
|
||||||
def select_language(preset: str | None = None) -> str | None:
|
def select_language(preset: str | None = None) -> str | None:
|
||||||
from ..locale.locale_menu import select_kb_layout
|
from ..locale.locale_menu import select_kb_layout
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
from .audio_configuration import Audio, AudioConfiguration
|
from .application import (
|
||||||
|
ApplicationConfiguration,
|
||||||
|
Audio,
|
||||||
|
AudioConfiguration,
|
||||||
|
BluetoothConfiguration,
|
||||||
|
)
|
||||||
from .bootloader import Bootloader
|
from .bootloader import Bootloader
|
||||||
from .device_model import (
|
from .device_model import (
|
||||||
BDevice,
|
BDevice,
|
||||||
|
|
@ -35,9 +40,11 @@ from .profile_model import ProfileConfiguration
|
||||||
from .users import PasswordStrength, User
|
from .users import PasswordStrength, User
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
'ApplicationConfiguration',
|
||||||
'Audio',
|
'Audio',
|
||||||
'AudioConfiguration',
|
'AudioConfiguration',
|
||||||
'BDevice',
|
'BDevice',
|
||||||
|
'BluetoothConfiguration',
|
||||||
'Bootloader',
|
'Bootloader',
|
||||||
'CustomRepository',
|
'CustomRepository',
|
||||||
'DeviceGeometry',
|
'DeviceGeometry',
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,41 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import NotRequired, TypedDict
|
from enum import StrEnum, auto
|
||||||
|
from typing import Any, NotRequired, TypedDict
|
||||||
|
|
||||||
|
|
||||||
class BluetoothConfigSerialization(TypedDict):
|
class BluetoothConfigSerialization(TypedDict):
|
||||||
enabled: bool
|
enabled: bool
|
||||||
|
|
||||||
|
|
||||||
|
class Audio(StrEnum):
|
||||||
|
NO_AUDIO = 'No audio server'
|
||||||
|
PIPEWIRE = auto()
|
||||||
|
PULSEAUDIO = auto()
|
||||||
|
|
||||||
|
|
||||||
|
class AudioConfigSerialization(TypedDict):
|
||||||
|
audio: str
|
||||||
|
|
||||||
|
|
||||||
class ApplicationSerialization(TypedDict):
|
class ApplicationSerialization(TypedDict):
|
||||||
bluetooth_config: NotRequired[BluetoothConfigSerialization]
|
bluetooth_config: NotRequired[BluetoothConfigSerialization]
|
||||||
|
audio_config: NotRequired[AudioConfigSerialization]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AudioConfiguration:
|
||||||
|
audio: Audio
|
||||||
|
|
||||||
|
def json(self) -> AudioConfigSerialization:
|
||||||
|
return {
|
||||||
|
'audio': self.audio.value,
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_arg(arg: dict[str, Any]) -> 'AudioConfiguration':
|
||||||
|
return AudioConfiguration(
|
||||||
|
Audio(arg['audio']),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|
@ -18,23 +46,33 @@ class BluetoothConfiguration:
|
||||||
return {'enabled': self.enabled}
|
return {'enabled': self.enabled}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_arg(arg: BluetoothConfigSerialization) -> 'BluetoothConfiguration':
|
def parse_arg(arg: dict[str, Any]) -> 'BluetoothConfiguration':
|
||||||
return BluetoothConfiguration(arg['enabled'])
|
return BluetoothConfiguration(arg['enabled'])
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ApplicationConfiguration:
|
class ApplicationConfiguration:
|
||||||
bluetooth_config: BluetoothConfiguration | None = None
|
bluetooth_config: BluetoothConfiguration | None = None
|
||||||
|
audio_config: AudioConfiguration | None = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_arg(args: ApplicationSerialization) -> 'ApplicationConfiguration':
|
def parse_arg(
|
||||||
bluetooth_config: BluetoothConfiguration | None = None
|
args: dict[str, Any] | None = None,
|
||||||
if 'bluetooth_config' in args:
|
old_audio_config: dict[str, Any] | None = None,
|
||||||
bluetooth_config = BluetoothConfiguration.parse_arg(args['bluetooth_config'])
|
) -> 'ApplicationConfiguration':
|
||||||
|
app_config = ApplicationConfiguration()
|
||||||
|
|
||||||
return ApplicationConfiguration(
|
if args and (bluetooth_config := args.get('bluetooth_config')) is not None:
|
||||||
bluetooth_config=bluetooth_config,
|
app_config.bluetooth_config = BluetoothConfiguration.parse_arg(bluetooth_config)
|
||||||
)
|
|
||||||
|
# deprecated: backwards compatibility
|
||||||
|
if old_audio_config is not None:
|
||||||
|
app_config.audio_config = AudioConfiguration.parse_arg(old_audio_config)
|
||||||
|
|
||||||
|
if args and (audio_config := args.get('audio_config')) is not None:
|
||||||
|
app_config.audio_config = AudioConfiguration.parse_arg(audio_config)
|
||||||
|
|
||||||
|
return app_config
|
||||||
|
|
||||||
def json(self) -> ApplicationSerialization:
|
def json(self) -> ApplicationSerialization:
|
||||||
config: ApplicationSerialization = {}
|
config: ApplicationSerialization = {}
|
||||||
|
|
@ -42,4 +80,7 @@ class ApplicationConfiguration:
|
||||||
if self.bluetooth_config:
|
if self.bluetooth_config:
|
||||||
config['bluetooth_config'] = self.bluetooth_config.json()
|
config['bluetooth_config'] = self.bluetooth_config.json()
|
||||||
|
|
||||||
|
if self.audio_config:
|
||||||
|
config['audio_config'] = self.audio_config.json()
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
from dataclasses import dataclass
|
|
||||||
from enum import StrEnum, auto
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from ..hardware import SysInfo
|
|
||||||
from ..output import info
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from archinstall.lib.installer import Installer
|
|
||||||
|
|
||||||
|
|
||||||
class Audio(StrEnum):
|
|
||||||
NO_AUDIO = 'No audio server'
|
|
||||||
PIPEWIRE = auto()
|
|
||||||
PULSEAUDIO = auto()
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class AudioConfiguration:
|
|
||||||
audio: Audio
|
|
||||||
|
|
||||||
def json(self) -> dict[str, str]:
|
|
||||||
return {
|
|
||||||
'audio': self.audio.value,
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def parse_arg(arg: dict[str, str]) -> 'AudioConfiguration':
|
|
||||||
return AudioConfiguration(
|
|
||||||
Audio(arg['audio']),
|
|
||||||
)
|
|
||||||
|
|
||||||
def install_audio_config(
|
|
||||||
self,
|
|
||||||
installation: 'Installer',
|
|
||||||
) -> None:
|
|
||||||
info(f'Installing audio server: {self.audio.name}')
|
|
||||||
|
|
||||||
from ...default_profiles.applications.pipewire import PipewireProfile
|
|
||||||
|
|
||||||
match self.audio:
|
|
||||||
case Audio.PIPEWIRE:
|
|
||||||
PipewireProfile().install(installation)
|
|
||||||
case Audio.PULSEAUDIO:
|
|
||||||
installation.add_additional_packages('pulseaudio')
|
|
||||||
|
|
||||||
if self.audio != Audio.NO_AUDIO:
|
|
||||||
if SysInfo.requires_sof_fw():
|
|
||||||
installation.add_additional_packages('sof-firmware')
|
|
||||||
|
|
||||||
if SysInfo.requires_alsa_fw():
|
|
||||||
installation.add_additional_packages('alsa-firmware')
|
|
||||||
|
|
@ -116,19 +116,13 @@ def perform_installation(mountpoint: Path) -> None:
|
||||||
if users := config.users:
|
if users := config.users:
|
||||||
installation.create_users(users)
|
installation.create_users(users)
|
||||||
|
|
||||||
audio_config = config.audio_config
|
|
||||||
if audio_config:
|
|
||||||
audio_config.install_audio_config(installation)
|
|
||||||
else:
|
|
||||||
info('No audio server will be installed')
|
|
||||||
|
|
||||||
if config.packages and config.packages[0] != '':
|
if config.packages and config.packages[0] != '':
|
||||||
installation.add_additional_packages(config.packages)
|
installation.add_additional_packages(config.packages)
|
||||||
|
|
||||||
if profile_config := config.profile_config:
|
if profile_config := config.profile_config:
|
||||||
profile_handler.install_profile_config(installation, profile_config)
|
profile_handler.install_profile_config(installation, profile_config)
|
||||||
|
|
||||||
if app_config := config.application_config:
|
if app_config := config.app_config:
|
||||||
application_handler.install_applications(installation, app_config)
|
application_handler.install_applications(installation, app_config)
|
||||||
|
|
||||||
if timezone := config.timezone:
|
if timezone := config.timezone:
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,11 @@ def deprecated_mirror_config() -> Path:
|
||||||
return Path(__file__).parent / 'data' / 'test_deprecated_mirror_config.json'
|
return Path(__file__).parent / 'data' / 'test_deprecated_mirror_config.json'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='session')
|
||||||
|
def deprecated_audio_config() -> Path:
|
||||||
|
return Path(__file__).parent / 'data' / 'test_deprecated_audio_config.json'
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='session')
|
@pytest.fixture(scope='session')
|
||||||
def mirrorlist_no_country_fixture() -> Path:
|
def mirrorlist_no_country_fixture() -> Path:
|
||||||
return Path(__file__).parent / 'data' / 'mirrorlists' / 'test_no_country'
|
return Path(__file__).parent / 'data' / 'mirrorlists' / 'test_no_country'
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@
|
||||||
"app_config": {
|
"app_config": {
|
||||||
"bluetooth_config": {
|
"bluetooth_config": {
|
||||||
"enabled": true
|
"enabled": true
|
||||||
|
},
|
||||||
|
"audio_config": {
|
||||||
|
"audio": "pipewire"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"audio_config": {
|
"audio_config": {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"audio_config": {
|
||||||
|
"audio": "pipewire"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,8 +6,7 @@ from pytest import MonkeyPatch
|
||||||
from archinstall.default_profiles.profile import GreeterType
|
from archinstall.default_profiles.profile import GreeterType
|
||||||
from archinstall.lib.args import ArchConfig, ArchConfigHandler, Arguments
|
from archinstall.lib.args import ArchConfig, ArchConfigHandler, Arguments
|
||||||
from archinstall.lib.hardware import GfxDriver
|
from archinstall.lib.hardware import GfxDriver
|
||||||
from archinstall.lib.models.application import ApplicationConfiguration, BluetoothConfiguration
|
from archinstall.lib.models.application import ApplicationConfiguration, Audio, AudioConfiguration, BluetoothConfiguration
|
||||||
from archinstall.lib.models.audio_configuration import Audio, AudioConfiguration
|
|
||||||
from archinstall.lib.models.bootloader import Bootloader
|
from archinstall.lib.models.bootloader import Bootloader
|
||||||
from archinstall.lib.models.device_model import DiskLayoutConfiguration, DiskLayoutType
|
from archinstall.lib.models.device_model import DiskLayoutConfiguration, DiskLayoutType
|
||||||
from archinstall.lib.models.locale import LocaleConfiguration
|
from archinstall.lib.models.locale import LocaleConfiguration
|
||||||
|
|
@ -128,8 +127,9 @@ def test_config_file_parsing(
|
||||||
|
|
||||||
assert arch_config == ArchConfig(
|
assert arch_config == ArchConfig(
|
||||||
version='3.0.2',
|
version='3.0.2',
|
||||||
application_config=ApplicationConfiguration(
|
app_config=ApplicationConfiguration(
|
||||||
bluetooth_config=BluetoothConfiguration(enabled=True),
|
bluetooth_config=BluetoothConfiguration(enabled=True),
|
||||||
|
audio_config=AudioConfiguration(audio=Audio.PIPEWIRE),
|
||||||
),
|
),
|
||||||
locale_config=LocaleConfiguration(
|
locale_config=LocaleConfiguration(
|
||||||
kb_layout='us',
|
kb_layout='us',
|
||||||
|
|
@ -199,7 +199,6 @@ def test_config_file_parsing(
|
||||||
),
|
),
|
||||||
bootloader=Bootloader.Systemd,
|
bootloader=Bootloader.Systemd,
|
||||||
uki=False,
|
uki=False,
|
||||||
audio_config=AudioConfiguration(Audio.PIPEWIRE),
|
|
||||||
hostname='archy',
|
hostname='archy',
|
||||||
kernels=['linux-zen'],
|
kernels=['linux-zen'],
|
||||||
ntp=True,
|
ntp=True,
|
||||||
|
|
@ -285,6 +284,27 @@ def test_deprecated_creds_config_parsing(
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_deprecated_audio_config_parsing(
|
||||||
|
monkeypatch: MonkeyPatch,
|
||||||
|
deprecated_audio_config: Path,
|
||||||
|
) -> None:
|
||||||
|
monkeypatch.setattr(
|
||||||
|
'sys.argv',
|
||||||
|
[
|
||||||
|
'archinstall',
|
||||||
|
'--config',
|
||||||
|
str(deprecated_audio_config),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
handler = ArchConfigHandler()
|
||||||
|
arch_config = handler.config
|
||||||
|
|
||||||
|
assert arch_config.app_config == ApplicationConfiguration(
|
||||||
|
audio_config=AudioConfiguration(audio=Audio.PIPEWIRE),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_encrypted_creds_with_arg(
|
def test_encrypted_creds_with_arg(
|
||||||
monkeypatch: MonkeyPatch,
|
monkeypatch: MonkeyPatch,
|
||||||
encrypted_creds_fixture: Path,
|
encrypted_creds_fixture: Path,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue