Add AUR helper installation support
Adds an "Enable AUR" entry to the global menu allowing the user to choose between paru and yay. The selected helper is built from source inside the chroot, running as the first sudo user under a temporary NOPASSWD-pacman sudoers entry that is removed in a finally block. Source builds avoid the libalpm soname mismatch that can occur with prebuilt -bin packages. The feature gracefully no-ops when no AUR helper is configured. The menu item is gated behind the presence of a regular user account; attempting --silent installs without one raises RequirementError. Signed-off-by: pentago <876756+pentago@users.noreply.github.com>
This commit is contained in:
parent
b5d323762b
commit
817a1adef3
|
|
@ -0,0 +1,69 @@
|
|||
import shlex
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from archinstall.lib.exceptions import PackageError, SysCallError
|
||||
from archinstall.lib.models.aur import AURHelperConfiguration
|
||||
from archinstall.lib.models.users import User
|
||||
from archinstall.lib.output import debug, info
|
||||
from archinstall.lib.translationhandler import tr
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from archinstall.lib.installer import Installer
|
||||
|
||||
|
||||
class AURHelperApp:
|
||||
"""Bootstraps an AUR helper (paru / yay) inside the chroot.
|
||||
|
||||
The helper package name comes from a controlled ``AURHelper`` enum value,
|
||||
and the build username is validated upstream by archinstall's user-creation
|
||||
path, so shell interpolation of these values is safe. Dynamic file system
|
||||
paths are still passed through ``shlex.quote``.
|
||||
"""
|
||||
|
||||
_SUDOERS_FILE = 'etc/sudoers.d/99_aur_build'
|
||||
|
||||
def install(
|
||||
self,
|
||||
install_session: Installer,
|
||||
helper_config: AURHelperConfiguration,
|
||||
build_user: User,
|
||||
) -> None:
|
||||
install_session.add_additional_packages(['base-devel', 'git'])
|
||||
|
||||
sudoers_path = install_session.target / self._SUDOERS_FILE
|
||||
sudoers_path.write_text(f'{build_user.username} ALL=(ALL) NOPASSWD: /usr/bin/pacman\n')
|
||||
sudoers_path.chmod(0o440)
|
||||
|
||||
helper_pkg = helper_config.helper.value
|
||||
# Build inside the user's $HOME rather than /tmp: arch-chroot -S spawns a
|
||||
# transient systemd-run unit and the chroot's /tmp is the on-disk
|
||||
# directory (mode 0755, root-owned) since no boot-time tmpfs mount has
|
||||
# happened, so non-root writes to /tmp fail. Build tools like Go also
|
||||
# default TMPDIR to /tmp, so we redirect TMPDIR for the same reason.
|
||||
build_subdir = f'.cache/aur-build/{helper_pkg}'
|
||||
quoted_build = shlex.quote(build_subdir)
|
||||
tmp_env = 'TMPDIR="$HOME/.cache/aur-build/tmp"'
|
||||
|
||||
try:
|
||||
info(tr('Installing AUR helper {}').format(helper_config.helper.value))
|
||||
install_session.arch_chroot(
|
||||
f'rm -rf -- {quoted_build} && mkdir -p -- "$HOME/.cache/aur-build/tmp" "$(dirname -- {quoted_build})"',
|
||||
run_as=build_user.username,
|
||||
)
|
||||
install_session.arch_chroot(
|
||||
f'git clone https://aur.archlinux.org/{helper_pkg}.git {quoted_build}',
|
||||
run_as=build_user.username,
|
||||
)
|
||||
install_session.arch_chroot(
|
||||
f'cd {quoted_build} && {tmp_env} makepkg -si --noconfirm',
|
||||
run_as=build_user.username,
|
||||
)
|
||||
install_session.arch_chroot(
|
||||
f'rm -rf -- {quoted_build} "$HOME/.cache/aur-build/tmp"',
|
||||
run_as=build_user.username,
|
||||
)
|
||||
except SysCallError as e:
|
||||
debug(f'AUR helper install failed: {e}')
|
||||
raise PackageError(tr('Failed to install AUR helper: {}').format(e)) from e
|
||||
finally:
|
||||
sudoers_path.unlink(missing_ok=True)
|
||||
|
|
@ -16,6 +16,7 @@ from pydantic.dataclasses import dataclass as p_dataclass
|
|||
from archinstall.lib.crypt import decrypt
|
||||
from archinstall.lib.menu.util import get_password
|
||||
from archinstall.lib.models.application import ApplicationConfiguration, ZramConfiguration
|
||||
from archinstall.lib.models.aur import AURConfiguration
|
||||
from archinstall.lib.models.authentication import AuthenticationConfiguration
|
||||
from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration
|
||||
from archinstall.lib.models.config import SubConfig
|
||||
|
|
@ -76,6 +77,7 @@ class ArchConfigType(StrEnum):
|
|||
NETWORK_CONFIG = 'network_config'
|
||||
BOOTLOADER_CONFIG = 'bootloader_config'
|
||||
APP_CONFIG = 'app_config'
|
||||
AUR_CONFIG = 'aur_config'
|
||||
AUTH_CONFIG = 'auth_config'
|
||||
SWAP = 'swap'
|
||||
USERS = 'users'
|
||||
|
|
@ -112,6 +114,8 @@ class ArchConfigType(StrEnum):
|
|||
return tr('Bootloader')
|
||||
case ArchConfigType.APP_CONFIG:
|
||||
return tr('Application')
|
||||
case ArchConfigType.AUR_CONFIG:
|
||||
return tr('Enable AUR')
|
||||
case ArchConfigType.AUTH_CONFIG:
|
||||
return tr('Authentication')
|
||||
case ArchConfigType.SWAP:
|
||||
|
|
@ -152,6 +156,7 @@ class ArchConfig:
|
|||
network_config: NetworkConfiguration | None = None
|
||||
bootloader_config: BootloaderConfiguration | None = None
|
||||
app_config: ApplicationConfiguration | None = None
|
||||
aur_config: AURConfiguration | None = None
|
||||
auth_config: AuthenticationConfiguration | None = None
|
||||
swap: ZramConfiguration | None = None
|
||||
hostname: str = 'archlinux'
|
||||
|
|
@ -240,6 +245,9 @@ class ArchConfig:
|
|||
if self.app_config:
|
||||
cfg[ArchConfigType.APP_CONFIG] = self.app_config
|
||||
|
||||
if self.aur_config:
|
||||
cfg[ArchConfigType.AUR_CONFIG] = self.aur_config
|
||||
|
||||
return cfg
|
||||
|
||||
@classmethod
|
||||
|
|
@ -307,6 +315,9 @@ class ArchConfig:
|
|||
if audio_config_args is not None or app_config_args is not None:
|
||||
arch_config.app_config = ApplicationConfiguration.parse_arg(app_config_args, audio_config_args)
|
||||
|
||||
if aur_config_args := args_config.get('aur_config', None):
|
||||
arch_config.aur_config = AURConfiguration.parse_arg(aur_config_args)
|
||||
|
||||
if auth_config_args := args_config.get('auth_config', None):
|
||||
arch_config.auth_config = AuthenticationConfiguration.parse_arg(auth_config_args)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
from archinstall.applications.aur_helper import AURHelperApp
|
||||
from archinstall.lib.exceptions import RequirementError
|
||||
from archinstall.lib.models.aur import AURConfiguration
|
||||
from archinstall.lib.models.users import User
|
||||
from archinstall.lib.output import debug
|
||||
from archinstall.lib.translationhandler import tr
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from archinstall.lib.installer import Installer
|
||||
|
||||
|
||||
class AURHandler:
|
||||
"""Coordinates AUR helper installation as part of a guided install.
|
||||
|
||||
The build user is resolved deterministically: first user with ``sudo=True``,
|
||||
falling back to the first user in ``users``. If no users are configured the
|
||||
handler raises ``RequirementError`` so ``--silent`` runs surface the missing
|
||||
dependency immediately.
|
||||
"""
|
||||
|
||||
def install_aur(
|
||||
self,
|
||||
install_session: Installer,
|
||||
aur_config: AURConfiguration | None,
|
||||
users: list[User],
|
||||
) -> None:
|
||||
if aur_config is None or aur_config.helper_config is None:
|
||||
debug('AUR: no helper configured, skipping')
|
||||
return
|
||||
|
||||
build_user = next((u for u in users if u.sudo), None) or (users[0] if users else None)
|
||||
|
||||
if build_user is None:
|
||||
raise RequirementError(
|
||||
tr('AUR helper requires a non-root user account. Configure one under Authentication.'),
|
||||
)
|
||||
|
||||
AURHelperApp().install(install_session, aur_config.helper_config, build_user)
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
from typing import override
|
||||
|
||||
from archinstall.lib.menu.abstract_menu import AbstractSubMenu
|
||||
from archinstall.lib.menu.helpers import Selection
|
||||
from archinstall.lib.models.aur import (
|
||||
AURConfiguration,
|
||||
AURHelper,
|
||||
AURHelperConfiguration,
|
||||
)
|
||||
from archinstall.lib.translationhandler import tr
|
||||
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
|
||||
from archinstall.tui.result import ResultType
|
||||
|
||||
|
||||
class AURMenu(AbstractSubMenu[AURConfiguration]):
|
||||
def __init__(
|
||||
self,
|
||||
preset: AURConfiguration | None = None,
|
||||
):
|
||||
if preset:
|
||||
self._aur_config = preset
|
||||
else:
|
||||
self._aur_config = AURConfiguration()
|
||||
|
||||
menu_options = self._define_menu_options()
|
||||
self._item_group = MenuItemGroup(menu_options, checkmarks=True)
|
||||
|
||||
super().__init__(
|
||||
self._item_group,
|
||||
config=self._aur_config,
|
||||
allow_reset=True,
|
||||
)
|
||||
|
||||
@override
|
||||
async def show(self) -> AURConfiguration | None:
|
||||
_ = await super().show()
|
||||
return self._aur_config
|
||||
|
||||
def _define_menu_options(self) -> list[MenuItem]:
|
||||
return [
|
||||
MenuItem(
|
||||
text=tr('Enable AUR Helper'),
|
||||
action=select_aur_helper,
|
||||
value=self._aur_config.helper_config,
|
||||
preview_action=self._prev_helper,
|
||||
key='helper_config',
|
||||
),
|
||||
]
|
||||
|
||||
def _prev_helper(self, item: MenuItem) -> str | None:
|
||||
if item.value is not None:
|
||||
config: AURHelperConfiguration = item.value
|
||||
return f'{tr("AUR helper")}: {config.helper.value}'
|
||||
return None
|
||||
|
||||
|
||||
async def select_aur_helper(preset: AURHelperConfiguration | None = None) -> AURHelperConfiguration | None:
|
||||
items = [MenuItem(h.value, value=h) for h in AURHelper]
|
||||
group = MenuItemGroup(items)
|
||||
|
||||
if preset:
|
||||
group.set_focus_by_value(preset.helper)
|
||||
|
||||
result = await Selection[AURHelper](
|
||||
group,
|
||||
header=tr('Enable AUR Helper'),
|
||||
allow_skip=True,
|
||||
allow_reset=True,
|
||||
).show()
|
||||
|
||||
match result.type_:
|
||||
case ResultType.Skip:
|
||||
return preset
|
||||
case ResultType.Selection:
|
||||
return AURHelperConfiguration(helper=result.get_value())
|
||||
case ResultType.Reset:
|
||||
return None
|
||||
|
|
@ -3,6 +3,7 @@ from typing import override
|
|||
from archinstall.default_profiles.profile import GreeterType
|
||||
from archinstall.lib.applications.application_menu import ApplicationMenu
|
||||
from archinstall.lib.args import ArchConfig
|
||||
from archinstall.lib.aur.aur_menu import AURMenu
|
||||
from archinstall.lib.authentication.authentication_menu import AuthenticationMenu
|
||||
from archinstall.lib.bootloader.bootloader_menu import BootloaderMenu
|
||||
from archinstall.lib.bootloader.utils import validate_bootloader_layout
|
||||
|
|
@ -16,6 +17,7 @@ from archinstall.lib.menu.abstract_menu import AbstractMenu, SpecialMenuKey
|
|||
from archinstall.lib.mirror.mirror_handler import MirrorListHandler
|
||||
from archinstall.lib.mirror.mirror_menu import MirrorMenu
|
||||
from archinstall.lib.models.application import ApplicationConfiguration, ZramConfiguration
|
||||
from archinstall.lib.models.aur import AURConfiguration
|
||||
from archinstall.lib.models.authentication import AuthenticationConfiguration
|
||||
from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration
|
||||
from archinstall.lib.models.device import DiskLayoutConfiguration, DiskLayoutType, PartitionModification
|
||||
|
|
@ -136,6 +138,12 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
preview_action=self._prev_applications,
|
||||
key='app_config',
|
||||
),
|
||||
MenuItem(
|
||||
text=tr('Enable AUR'),
|
||||
action=self._select_aur,
|
||||
preview_action=self._prev_aur,
|
||||
key='aur_config',
|
||||
),
|
||||
MenuItem(
|
||||
text=tr('Network configuration'),
|
||||
action=select_network,
|
||||
|
|
@ -235,6 +243,13 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
if not check(item.key):
|
||||
missing.add(item.text)
|
||||
|
||||
aur_item: MenuItem = self._item_group.find_by_key('aur_config')
|
||||
aur_config: AURConfiguration | None = aur_item.value
|
||||
if aur_config and aur_config.helper_config and not (auth_config and auth_config.has_regular_user()):
|
||||
missing.add(
|
||||
tr('AUR helper requires a non-root user account. Configure one under Authentication.'),
|
||||
)
|
||||
|
||||
return list(missing)
|
||||
|
||||
@override
|
||||
|
|
@ -267,6 +282,17 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
app_config = await ApplicationMenu(preset).show()
|
||||
return app_config
|
||||
|
||||
async def _select_aur(self, preset: AURConfiguration | None) -> AURConfiguration | None:
|
||||
aur_config = await AURMenu(preset).show()
|
||||
return aur_config
|
||||
|
||||
def _prev_aur(self, item: MenuItem) -> str | None:
|
||||
if item.value is not None:
|
||||
aur_config: AURConfiguration = item.value
|
||||
if aur_config.helper_config:
|
||||
return f'{tr("AUR helper")}: {aur_config.helper_config.helper.value}'
|
||||
return None
|
||||
|
||||
async def _select_authentication(self, preset: AuthenticationConfiguration | None) -> AuthenticationConfiguration | None:
|
||||
auth_config = await AuthenticationMenu(preset).show()
|
||||
return auth_config
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
from dataclasses import dataclass
|
||||
from enum import StrEnum
|
||||
from typing import Any, NotRequired, Self, TypedDict, override
|
||||
|
||||
from archinstall.lib.models.config import SubConfig
|
||||
from archinstall.lib.translationhandler import tr
|
||||
|
||||
|
||||
class AURHelper(StrEnum):
|
||||
PARU = 'paru'
|
||||
YAY = 'yay'
|
||||
|
||||
|
||||
class AURHelperConfigSerialization(TypedDict):
|
||||
helper: str
|
||||
|
||||
|
||||
class AURConfigSerialization(TypedDict, total=False):
|
||||
helper_config: NotRequired[AURHelperConfigSerialization]
|
||||
|
||||
|
||||
@dataclass
|
||||
class AURHelperConfiguration:
|
||||
helper: AURHelper
|
||||
|
||||
def json(self) -> AURHelperConfigSerialization:
|
||||
return {'helper': self.helper.value}
|
||||
|
||||
@classmethod
|
||||
def parse_arg(cls, arg: dict[str, Any]) -> Self:
|
||||
return cls(helper=AURHelper(arg['helper']))
|
||||
|
||||
|
||||
@dataclass
|
||||
class AURConfiguration(SubConfig):
|
||||
helper_config: AURHelperConfiguration | None = None
|
||||
|
||||
@classmethod
|
||||
def parse_arg(cls, args: dict[str, Any] | None = None) -> Self:
|
||||
cfg = cls()
|
||||
if args and (helper_config := args.get('helper_config')) is not None:
|
||||
cfg.helper_config = AURHelperConfiguration.parse_arg(helper_config)
|
||||
return cfg
|
||||
|
||||
@override
|
||||
def json(self) -> AURConfigSerialization:
|
||||
config: AURConfigSerialization = {}
|
||||
if self.helper_config:
|
||||
config['helper_config'] = self.helper_config.json()
|
||||
return config
|
||||
|
||||
@override
|
||||
def summary(self) -> list[str]:
|
||||
out: list[str] = []
|
||||
if self.helper_config:
|
||||
out.append(tr('AUR helper "{}"').format(self.helper_config.helper.value))
|
||||
return out
|
||||
|
|
@ -2463,3 +2463,27 @@ msgstr ""
|
|||
|
||||
msgid "Enter a repository name"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable AUR"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable AUR Helper"
|
||||
msgstr ""
|
||||
|
||||
msgid "AUR helper"
|
||||
msgstr ""
|
||||
|
||||
msgid "AUR helper requires a non-root user account. Configure one under Authentication."
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Installing AUR helper {}"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Failed to install AUR helper: {}"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "AUR helper \"{}\""
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import time
|
|||
|
||||
from archinstall.lib.applications.application_handler import ApplicationHandler
|
||||
from archinstall.lib.args import ArchConfig, ArchConfigHandler
|
||||
from archinstall.lib.aur.aur_handler import AURHandler
|
||||
from archinstall.lib.authentication.authentication_handler import AuthenticationHandler
|
||||
from archinstall.lib.bootloader.utils import validate_bootloader_layout
|
||||
from archinstall.lib.configuration import ConfigurationOutput
|
||||
|
|
@ -138,6 +139,8 @@ def perform_installation(
|
|||
if app_config := config.app_config:
|
||||
application_handler.install_applications(installation, app_config)
|
||||
|
||||
AURHandler().install_aur(installation, config.aur_config, users or [])
|
||||
|
||||
if profile_config := config.profile_config:
|
||||
profile_handler.install_profile_config(installation, profile_config)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,11 @@ def config_fixture() -> Path:
|
|||
return Path(__file__).parent / 'data' / 'test_config.json'
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def aur_config_fixture() -> Path:
|
||||
return Path(__file__).parent / 'data' / 'test_config_aur.json'
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def btrfs_config_fixture() -> Path:
|
||||
return Path(__file__).parent / 'data' / 'test_config_btrfs.json'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,214 @@
|
|||
{
|
||||
"archinstall-language": "English",
|
||||
"script": "test_script",
|
||||
"app_config": {
|
||||
"bluetooth_config": {
|
||||
"enabled": true
|
||||
},
|
||||
"audio_config": {
|
||||
"audio": "pipewire"
|
||||
},
|
||||
"print_service_config": {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
"auth_config": {
|
||||
"u2f_config": {
|
||||
"passwordless_sudo": true,
|
||||
"u2f_login_method": "passwordless"
|
||||
}
|
||||
},
|
||||
"audio_config": {
|
||||
"audio": "pipewire"
|
||||
},
|
||||
"bootloader_config": {
|
||||
"bootloader": "Systemd-boot",
|
||||
"uki": false,
|
||||
"removable": false
|
||||
},
|
||||
"services": [
|
||||
"service_1",
|
||||
"service_2"
|
||||
],
|
||||
"disk_config": {
|
||||
"config_type": "default_layout",
|
||||
"btrfs_options": {
|
||||
"snapshot_config": {
|
||||
"type": "Snapper"
|
||||
}
|
||||
},
|
||||
"device_modifications": [
|
||||
{
|
||||
"device": "/dev/sda",
|
||||
"partitions": [
|
||||
{
|
||||
"btrfs": [],
|
||||
"dev_path": null,
|
||||
"flags": [
|
||||
"boot"
|
||||
],
|
||||
"fs_type": "fat32",
|
||||
"size": {
|
||||
"sector_size": {
|
||||
"unit": "B",
|
||||
"value": 512
|
||||
},
|
||||
"unit": "MiB",
|
||||
"value": 512
|
||||
},
|
||||
"mount_options": [],
|
||||
"mountpoint": "/boot",
|
||||
"obj_id": "2c3fa2d5-2c79-4fab-86ec-22d0ea1543c0",
|
||||
"start": {
|
||||
"sector_size": {
|
||||
"unit": "B",
|
||||
"value": 512
|
||||
},
|
||||
"unit": "MiB",
|
||||
"value": 1
|
||||
},
|
||||
"status": "create",
|
||||
"type": "primary"
|
||||
},
|
||||
{
|
||||
"btrfs": [],
|
||||
"dev_path": null,
|
||||
"flags": [],
|
||||
"fs_type": "ext4",
|
||||
"size": {
|
||||
"sector_size": {
|
||||
"unit": "B",
|
||||
"value": 512
|
||||
},
|
||||
"unit": "GiB",
|
||||
"value": 32
|
||||
},
|
||||
"mount_options": [],
|
||||
"mountpoint": "/",
|
||||
"obj_id": "3e7018a0-363b-4d05-ab83-8e82d13db208",
|
||||
"start": {
|
||||
"sector_size": {
|
||||
"unit": "B",
|
||||
"value": 512
|
||||
},
|
||||
"unit": "MiB",
|
||||
"value": 513
|
||||
},
|
||||
"status": "create",
|
||||
"type": "primary"
|
||||
},
|
||||
{
|
||||
"btrfs": [],
|
||||
"dev_path": null,
|
||||
"flags": [],
|
||||
"fs_type": "ext4",
|
||||
"size": {
|
||||
"sector_size": {
|
||||
"unit": "B",
|
||||
"value": 512
|
||||
},
|
||||
"unit": "GiB",
|
||||
"value": 32
|
||||
},
|
||||
"mount_options": [],
|
||||
"mountpoint": "/home",
|
||||
"obj_id": "ce58b139-f041-4a06-94da-1f8bad775d3f",
|
||||
"start": {
|
||||
"sector_size": {
|
||||
"unit": "B",
|
||||
"value": 512
|
||||
},
|
||||
"unit": "MiB",
|
||||
"value": 33281
|
||||
},
|
||||
"status": "create",
|
||||
"type": "primary"
|
||||
}
|
||||
],
|
||||
"wipe": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostname": "archy",
|
||||
"kernels": [
|
||||
"linux-zen"
|
||||
],
|
||||
"locale_config": {
|
||||
"kb_layout": "us",
|
||||
"sys_enc": "UTF-8",
|
||||
"sys_lang": "en_US"
|
||||
},
|
||||
"mirror_config": {
|
||||
"custom_servers": [
|
||||
{
|
||||
"url": "https://mymirror.com/$repo/os/$arch"
|
||||
}
|
||||
],
|
||||
"mirror_regions": {
|
||||
"Australia": [
|
||||
"http://archlinux.mirror.digitalpacific.com.au/$repo/os/$arch"
|
||||
]
|
||||
},
|
||||
"optional_repositories": [
|
||||
"testing"
|
||||
],
|
||||
"custom_repositories": [
|
||||
{
|
||||
"name": "myrepo",
|
||||
"url": "https://myrepo.com/$repo/os/$arch",
|
||||
"sign_check": "Required",
|
||||
"sign_option": "TrustAll"
|
||||
}
|
||||
]
|
||||
},
|
||||
"network_config": {
|
||||
"type": "manual",
|
||||
"nics": [
|
||||
{
|
||||
"iface": "eno1",
|
||||
"ip": "192.168.1.15/24",
|
||||
"dhcp": true,
|
||||
"gateway": "192.168.1.1",
|
||||
"dns": [
|
||||
"192.168.1.1",
|
||||
"9.9.9.9"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ntp": true,
|
||||
"packages": [
|
||||
"firefox"
|
||||
],
|
||||
"parallel_downloads": 66,
|
||||
"profile_config": {
|
||||
"gfx_driver": "All open-source",
|
||||
"greeter": "lightdm-gtk-greeter",
|
||||
"profile": {
|
||||
"custom_settings": {
|
||||
"Hyprland": {
|
||||
"seat_access": "polkit"
|
||||
},
|
||||
"Sway": {
|
||||
"seat_access": "seatd"
|
||||
}
|
||||
},
|
||||
"details": [
|
||||
"Sway",
|
||||
"Hyprland"
|
||||
],
|
||||
"main": "Desktop"
|
||||
}
|
||||
},
|
||||
"custom_commands": [
|
||||
"echo 'Hello, World!'"
|
||||
],
|
||||
"swap": false,
|
||||
"timezone": "UTC",
|
||||
"version": "3.0.2",
|
||||
"aur_config": {
|
||||
"helper_config": {
|
||||
"helper": "paru"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -66,3 +66,28 @@ def test_creds_roundtrip(
|
|||
expected = json.loads(creds_fixture.read_text())
|
||||
|
||||
assert sorted(result.items()) == sorted(expected.items())
|
||||
|
||||
|
||||
def test_aur_config_roundtrip(
|
||||
monkeypatch: MonkeyPatch,
|
||||
aur_config_fixture: Path,
|
||||
) -> None:
|
||||
monkeypatch.setattr('sys.argv', ['archinstall', '--config', str(aur_config_fixture)])
|
||||
|
||||
handler = ArchConfigHandler()
|
||||
arch_config = handler.config
|
||||
arch_config.version = '3.0.2'
|
||||
|
||||
assert arch_config.aur_config is not None
|
||||
assert arch_config.aur_config.helper_config is not None
|
||||
assert arch_config.aur_config.helper_config.helper.value == 'paru'
|
||||
|
||||
config_output = ConfigurationOutput(arch_config)
|
||||
test_out_dir = Path('/tmp/')
|
||||
test_out_file = test_out_dir / config_output.user_configuration_file
|
||||
config_output.save(test_out_dir)
|
||||
|
||||
result = json.loads(test_out_file.read_text())
|
||||
expected = json.loads(aur_config_fixture.read_text())
|
||||
|
||||
assert result['aur_config'] == expected['aur_config']
|
||||
|
|
|
|||
Loading…
Reference in New Issue