Dataclasses for args and config (#2936)
* Introduce dataclass for arguments and configuration * Update * Update
This commit is contained in:
parent
f2bc5ff280
commit
e51f7adf21
|
|
@ -32,7 +32,7 @@ from .lib.output import FormattedOutput, debug, error, info, log, warn
|
||||||
from .lib.pacman import Pacman
|
from .lib.pacman import Pacman
|
||||||
from .lib.plugins import load_plugin, plugins
|
from .lib.plugins import load_plugin, plugins
|
||||||
from .lib.storage import storage
|
from .lib.storage import storage
|
||||||
from .lib.translationhandler import DeferredTranslation, Language, TranslationHandler
|
from .lib.translationhandler import DeferredTranslation, Language, translation_handler
|
||||||
from .tui import Tui
|
from .tui import Tui
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
@ -224,7 +224,7 @@ def load_config() -> None:
|
||||||
arguments['locale_config'] = locale.LocaleConfiguration.parse_arg(arguments)
|
arguments['locale_config'] = locale.LocaleConfiguration.parse_arg(arguments)
|
||||||
|
|
||||||
if (archinstall_lang := arguments.get('archinstall-language', None)) is not None:
|
if (archinstall_lang := arguments.get('archinstall-language', None)) is not None:
|
||||||
arguments['archinstall-language'] = TranslationHandler().get_language_by_name(archinstall_lang)
|
arguments['archinstall-language'] = translation_handler.get_language_by_name(archinstall_lang)
|
||||||
|
|
||||||
if disk_config := arguments.get('disk_config', {}):
|
if disk_config := arguments.get('disk_config', {}):
|
||||||
arguments['disk_config'] = disk.DiskLayoutConfiguration.parse_arg(disk_config)
|
arguments['disk_config'] = disk.DiskLayoutConfiguration.parse_arg(disk_config)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,341 @@
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import urllib.error
|
||||||
|
import urllib.parse
|
||||||
|
from argparse import ArgumentParser, Namespace
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
from urllib.request import Request, urlopen
|
||||||
|
|
||||||
|
from pydantic.dataclasses import dataclass as p_dataclass
|
||||||
|
|
||||||
|
from .disk import DiskEncryption, DiskLayoutConfiguration
|
||||||
|
from .locale import LocaleConfiguration
|
||||||
|
from .mirrors import MirrorConfiguration
|
||||||
|
from .models import AudioConfiguration, Bootloader, NetworkConfiguration, User
|
||||||
|
from .output import error, warn
|
||||||
|
from .plugins import load_plugin
|
||||||
|
from .profile import ProfileConfiguration
|
||||||
|
from .storage import storage
|
||||||
|
from .translationhandler import Language, translation_handler
|
||||||
|
|
||||||
|
|
||||||
|
@p_dataclass
|
||||||
|
class Arguments:
|
||||||
|
config: Path | None = None
|
||||||
|
config_url: str | None = None
|
||||||
|
creds: Path | None = None
|
||||||
|
creds_url: str | None = None
|
||||||
|
silent: bool = False
|
||||||
|
dry_run: bool = False
|
||||||
|
script: str = 'guided'
|
||||||
|
mount_point: Path | None = Path('/mnt')
|
||||||
|
skip_ntp: bool = False
|
||||||
|
debug: bool = False
|
||||||
|
offline: bool = False
|
||||||
|
no_pkg_lookups: bool = False
|
||||||
|
plugin: str | None = None
|
||||||
|
skip_version_check: bool = False
|
||||||
|
advanced: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ArchConfig:
|
||||||
|
version: str = field(default_factory=lambda: storage['__version__'])
|
||||||
|
locale_config: LocaleConfiguration | None = None
|
||||||
|
archinstall_language: Language = field(default_factory=lambda: translation_handler.get_language_by_abbr('en'))
|
||||||
|
disk_config: DiskLayoutConfiguration | None = None
|
||||||
|
profile_config: ProfileConfiguration | None = None
|
||||||
|
mirror_config: MirrorConfiguration | None = None
|
||||||
|
network_config: NetworkConfiguration | None = None
|
||||||
|
bootloader: Bootloader = field(default=Bootloader.get_default())
|
||||||
|
uki: bool = False
|
||||||
|
audio_config: AudioConfiguration | None = None
|
||||||
|
hostname: str = 'archlinux'
|
||||||
|
kernels: list[str] = field(default_factory=lambda: ['linux'])
|
||||||
|
ntp: bool = False
|
||||||
|
packages: list[str] = field(default_factory=list)
|
||||||
|
parallel_downloads: int = 0
|
||||||
|
swap: bool = True
|
||||||
|
timezone: str = 'UTC'
|
||||||
|
additional_repositories: list[str] = field(default_factory=list)
|
||||||
|
|
||||||
|
# Special fields that should be handle with care due to security implications
|
||||||
|
_users: list[User] = field(default_factory=list)
|
||||||
|
_disk_encryption: DiskEncryption | None = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_config(cls, args_config: dict[str, Any]) -> 'ArchConfig':
|
||||||
|
arch_config = ArchConfig()
|
||||||
|
|
||||||
|
arch_config.locale_config = LocaleConfiguration.parse_arg(args_config)
|
||||||
|
|
||||||
|
if archinstall_lang := args_config.get('archinstall-language', None):
|
||||||
|
arch_config.archinstall_language = translation_handler.get_language_by_name(archinstall_lang)
|
||||||
|
|
||||||
|
if disk_config := args_config.get('disk_config', {}):
|
||||||
|
arch_config.disk_config = DiskLayoutConfiguration.parse_arg(disk_config)
|
||||||
|
|
||||||
|
if profile_config := args_config.get('profile_config', None):
|
||||||
|
arch_config.profile_config = ProfileConfiguration.parse_arg(profile_config)
|
||||||
|
|
||||||
|
if mirror_config := args_config.get('mirror_config', None):
|
||||||
|
arch_config.mirror_config = MirrorConfiguration.parse_args(mirror_config)
|
||||||
|
|
||||||
|
if net_config := args_config.get('network_config', None):
|
||||||
|
arch_config.network_config = NetworkConfiguration.parse_arg(net_config)
|
||||||
|
|
||||||
|
users = args_config.get('!users', None)
|
||||||
|
superusers = args_config.get('!superusers', None)
|
||||||
|
if users is not None or superusers is not None:
|
||||||
|
arch_config._users = User.parse_arguments(users, superusers)
|
||||||
|
|
||||||
|
if bootloader_config := args_config.get('bootloader', None):
|
||||||
|
arch_config.bootloader = Bootloader.from_arg(bootloader_config)
|
||||||
|
|
||||||
|
if args_config.get('uki', False) and not arch_config.bootloader.has_uki_support():
|
||||||
|
arch_config.uki = False
|
||||||
|
|
||||||
|
if audio_config := args_config.get('audio_config', None):
|
||||||
|
arch_config.audio_config = AudioConfiguration.parse_arg(audio_config)
|
||||||
|
|
||||||
|
if args_config.get('disk_encryption', None) is not None and arch_config.disk_config is not None:
|
||||||
|
arch_config._disk_encryption = DiskEncryption.parse_arg(
|
||||||
|
arch_config.disk_config,
|
||||||
|
args_config['disk_encryption'],
|
||||||
|
args_config.get('encryption_password', '')
|
||||||
|
)
|
||||||
|
|
||||||
|
if hostname := args_config.get('hostname', ''):
|
||||||
|
arch_config.hostname = hostname
|
||||||
|
|
||||||
|
if kernels := args_config.get('kernels', []):
|
||||||
|
arch_config.kernels = kernels
|
||||||
|
|
||||||
|
if ntp := args_config.get('ntp', False):
|
||||||
|
arch_config.ntp = ntp
|
||||||
|
|
||||||
|
if packages := args_config.get('packages', []):
|
||||||
|
arch_config.packages = packages
|
||||||
|
|
||||||
|
if parallel_downloads := args_config.get('parallel_downloads', 0):
|
||||||
|
arch_config.parallel_downloads = parallel_downloads
|
||||||
|
|
||||||
|
arch_config.swap = args_config.get('swap', True)
|
||||||
|
|
||||||
|
if timezone := args_config.get('timezone', 'UTC'):
|
||||||
|
arch_config.timezone = timezone
|
||||||
|
|
||||||
|
if additional_repositories := args_config.get('additional-repositories', []):
|
||||||
|
arch_config.additional_repositories = additional_repositories
|
||||||
|
|
||||||
|
return arch_config
|
||||||
|
|
||||||
|
|
||||||
|
class ArchConfigHandler:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self._parser: ArgumentParser = self._define_arguments()
|
||||||
|
self._args: Arguments = self._parse_args()
|
||||||
|
|
||||||
|
config = self._parse_config()
|
||||||
|
self._arch_config = ArchConfig.from_config(config)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def arch_config(self) -> ArchConfig:
|
||||||
|
return self._arch_config
|
||||||
|
|
||||||
|
@property
|
||||||
|
def args(self) -> Arguments:
|
||||||
|
return self._args
|
||||||
|
|
||||||
|
def print_help(self) -> None:
|
||||||
|
self._parser.print_help()
|
||||||
|
|
||||||
|
def _define_arguments(self) -> ArgumentParser:
|
||||||
|
parser = ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||||
|
parser.add_argument(
|
||||||
|
"-v",
|
||||||
|
"--version",
|
||||||
|
action="version",
|
||||||
|
default=False,
|
||||||
|
version="%(prog)s " + storage['__version__']
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--config",
|
||||||
|
type=Path,
|
||||||
|
nargs="?",
|
||||||
|
default=None,
|
||||||
|
help="JSON configuration file"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--config-url",
|
||||||
|
type=str,
|
||||||
|
nargs="?",
|
||||||
|
default=None,
|
||||||
|
help="Url to a JSON configuration file"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--creds",
|
||||||
|
type=Path,
|
||||||
|
nargs="?",
|
||||||
|
default=None,
|
||||||
|
help="JSON credentials configuration file"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--creds-url",
|
||||||
|
type=str,
|
||||||
|
nargs="?",
|
||||||
|
default=None,
|
||||||
|
help="Url to a JSON credentials configuration file"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--silent",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="WARNING: Disables all prompts for input and confirmation. If no configuration is provided, this is ignored"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--dry-run",
|
||||||
|
"--dry_run",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Generates a configuration file and then exits instead of performing an installation"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--script",
|
||||||
|
default="guided",
|
||||||
|
nargs="?",
|
||||||
|
help="Script to run for installation",
|
||||||
|
type=str
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--mount-point",
|
||||||
|
"--mount_point",
|
||||||
|
type=Path,
|
||||||
|
nargs="?",
|
||||||
|
default=Path('/mnt'),
|
||||||
|
help="Define an alternate mount point for installation"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--skip-ntp",
|
||||||
|
action="store_true",
|
||||||
|
help="Disables NTP checks during installation",
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--debug",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Adds debug info into the log"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--offline",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Disabled online upstream services such as package search and key-ring auto update."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-pkg-lookups",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Disabled package validation specifically prior to starting installation."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--plugin",
|
||||||
|
nargs="?",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help='File path to a plugin to load'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--skip-version-check",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Skip the version check when running archinstall"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--advanced",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Enabled advanced options"
|
||||||
|
)
|
||||||
|
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def _parse_args(self) -> Arguments:
|
||||||
|
argparse_args = vars(self._parser.parse_args())
|
||||||
|
args: Arguments = Arguments(**argparse_args)
|
||||||
|
|
||||||
|
# amend the parameters (check internal consistency)
|
||||||
|
# Installation can't be silent if config is not passed
|
||||||
|
if args.config is None:
|
||||||
|
args.silent = False
|
||||||
|
|
||||||
|
if args.mount_point is not None:
|
||||||
|
storage['MOUNT_POINT'] = Path(args.mount_point)
|
||||||
|
|
||||||
|
if args.debug:
|
||||||
|
warn(f"Warning: --debug mode will write certain credentials to {storage['LOG_PATH']}/{storage['LOG_FILE']}!")
|
||||||
|
|
||||||
|
if args.plugin:
|
||||||
|
plugin_path = Path(args.plugin)
|
||||||
|
load_plugin(plugin_path)
|
||||||
|
|
||||||
|
return args
|
||||||
|
|
||||||
|
def _parse_config(self) -> dict[str, Any]:
|
||||||
|
config: dict[str, Any] = {}
|
||||||
|
config_data: str | None = None
|
||||||
|
creds_data: str | None = None
|
||||||
|
|
||||||
|
if self._args.config is not None:
|
||||||
|
config_data = self._read_file(self._args.config)
|
||||||
|
elif self._args.config_url is not None:
|
||||||
|
config_data = self._fetch_from_url(self._args.config_url)
|
||||||
|
|
||||||
|
if config_data is not None:
|
||||||
|
config.update(json.loads(config_data))
|
||||||
|
|
||||||
|
if self._args.creds is not None:
|
||||||
|
creds_data = self._read_file(self._args.creds)
|
||||||
|
elif self._args.creds_url is not None:
|
||||||
|
creds_data = self._fetch_from_url(self._args.creds_url)
|
||||||
|
|
||||||
|
if creds_data is not None:
|
||||||
|
config.update(json.loads(creds_data))
|
||||||
|
|
||||||
|
config = self._cleanup_config(config)
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
|
def _fetch_from_url(self, url: str) -> str:
|
||||||
|
if urllib.parse.urlparse(url).scheme:
|
||||||
|
try:
|
||||||
|
req = Request(url, headers={'User-Agent': 'ArchInstall'})
|
||||||
|
with urlopen(req) as resp:
|
||||||
|
return resp.read()
|
||||||
|
except urllib.error.HTTPError as err:
|
||||||
|
error(f"Could not fetch JSON from {url}: {err}")
|
||||||
|
else:
|
||||||
|
error('Not a valid url')
|
||||||
|
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
def _read_file(self, path: Path) -> str:
|
||||||
|
if not path.exists():
|
||||||
|
error(f"Could not find file {path}")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
return path.read_text()
|
||||||
|
|
||||||
|
def _cleanup_config(self, config: Namespace | dict) -> dict[str, Any]:
|
||||||
|
clean_args = {}
|
||||||
|
for key, val in config.items():
|
||||||
|
if isinstance(val, dict):
|
||||||
|
val = self._cleanup_config(val)
|
||||||
|
|
||||||
|
if val is not None:
|
||||||
|
clean_args[key] = val
|
||||||
|
|
||||||
|
return clean_args
|
||||||
|
|
@ -32,7 +32,7 @@ from .models.bootloader import Bootloader
|
||||||
from .models.users import User
|
from .models.users import User
|
||||||
from .output import FormattedOutput
|
from .output import FormattedOutput
|
||||||
from .profile.profile_menu import ProfileConfiguration
|
from .profile.profile_menu import ProfileConfiguration
|
||||||
from .translationhandler import Language, TranslationHandler
|
from .translationhandler import Language, translation_handler
|
||||||
from .utils.util import format_cols, get_password
|
from .utils.util import format_cols, get_password
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
@ -46,10 +46,9 @@ if TYPE_CHECKING:
|
||||||
class GlobalMenu(AbstractMenu):
|
class GlobalMenu(AbstractMenu):
|
||||||
def __init__(self, data_store: dict[str, Any]):
|
def __init__(self, data_store: dict[str, Any]):
|
||||||
self._data_store = data_store
|
self._data_store = data_store
|
||||||
self._translation_handler = TranslationHandler()
|
|
||||||
|
|
||||||
if 'archinstall-language' not in data_store:
|
if 'archinstall-language' not in data_store:
|
||||||
data_store['archinstall-language'] = self._translation_handler.get_language_by_abbr('en')
|
data_store['archinstall-language'] = translation_handler.get_language_by_abbr('en')
|
||||||
|
|
||||||
menu_optioons = self._get_menu_options(data_store)
|
menu_optioons = self._get_menu_options(data_store)
|
||||||
self._item_group = MenuItemGroup(
|
self._item_group = MenuItemGroup(
|
||||||
|
|
@ -263,8 +262,8 @@ class GlobalMenu(AbstractMenu):
|
||||||
|
|
||||||
def _select_archinstall_language(self, preset: Language) -> Language:
|
def _select_archinstall_language(self, preset: Language) -> Language:
|
||||||
from .interactions.general_conf import select_archinstall_language
|
from .interactions.general_conf import select_archinstall_language
|
||||||
language = select_archinstall_language(self._translation_handler.translated_languages, preset)
|
language = select_archinstall_language(translation_handler.translated_languages, preset)
|
||||||
self._translation_handler.activate(language)
|
translation_handler.activate(language)
|
||||||
|
|
||||||
self._upate_lang_text()
|
self._upate_lang_text()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -206,3 +206,6 @@ class DeferredTranslation:
|
||||||
def install(cls) -> None:
|
def install(cls) -> None:
|
||||||
import builtins
|
import builtins
|
||||||
builtins._ = cls # type: ignore
|
builtins._ = cls # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
translation_handler = TranslationHandler()
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ dev = [
|
||||||
"ruff==0.8.0",
|
"ruff==0.8.0",
|
||||||
"pylint==3.3.1",
|
"pylint==3.3.1",
|
||||||
"pylint-pydantic==0.3.2",
|
"pylint-pydantic==0.3.2",
|
||||||
|
"pytest==6.2.5",
|
||||||
]
|
]
|
||||||
doc = ["sphinx"]
|
doc = ["sphinx"]
|
||||||
|
|
||||||
|
|
@ -114,6 +115,10 @@ warn_unreachable = false
|
||||||
module = "archinstall.tui.*"
|
module = "archinstall.tui.*"
|
||||||
warn_unreachable = false
|
warn_unreachable = false
|
||||||
|
|
||||||
|
[[tool.mypy.overrides]]
|
||||||
|
module = "tests.*"
|
||||||
|
ignore_errors = true
|
||||||
|
|
||||||
[[tool.mypy.overrides]]
|
[[tool.mypy.overrides]]
|
||||||
module = [
|
module = [
|
||||||
"parted",
|
"parted",
|
||||||
|
|
@ -124,6 +129,11 @@ ignore_missing_imports = true
|
||||||
targets = ["archinstall"]
|
targets = ["archinstall"]
|
||||||
exclude = ["/tests"]
|
exclude = ["/tests"]
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
pythonpath = ["."]
|
||||||
|
addopts = "-s"
|
||||||
|
testpaths = ["tests"]
|
||||||
|
|
||||||
[tool.pylint.main]
|
[tool.pylint.main]
|
||||||
ignore-paths = [
|
ignore-paths = [
|
||||||
"^build/",
|
"^build/",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='session')
|
||||||
|
def config_fixture() -> Path:
|
||||||
|
return Path(__file__).parent / 'data' / 'test_config.json'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='session')
|
||||||
|
def creds_fixture() -> Path:
|
||||||
|
return Path(__file__).parent / 'data' / 'test_creds.json'
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
{
|
||||||
|
"additional-repositories": ["testing"],
|
||||||
|
"archinstall-language": "English",
|
||||||
|
"audio_config": {"audio": "pipewire"},
|
||||||
|
"bootloader": "Systemd-boot",
|
||||||
|
"disk_config": {
|
||||||
|
"config_type": "default_layout",
|
||||||
|
"device_modifications": [
|
||||||
|
{
|
||||||
|
"device": "/dev/sda",
|
||||||
|
"partitions": [
|
||||||
|
{
|
||||||
|
"btrfs": [],
|
||||||
|
"flags": [
|
||||||
|
"boot"
|
||||||
|
],
|
||||||
|
"fs_type": "fat32",
|
||||||
|
"size": {
|
||||||
|
"sector_size": null,
|
||||||
|
"unit": "MiB",
|
||||||
|
"value": 512
|
||||||
|
},
|
||||||
|
"mount_options": [],
|
||||||
|
"mountpoint": "/boot",
|
||||||
|
"obj_id": "2c3fa2d5-2c79-4fab-86ec-22d0ea1543c0",
|
||||||
|
"start": {
|
||||||
|
"sector_size": null,
|
||||||
|
"unit": "MiB",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"status": "create",
|
||||||
|
"type": "primary"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"btrfs": [],
|
||||||
|
"flags": [],
|
||||||
|
"fs_type": "ext4",
|
||||||
|
"size": {
|
||||||
|
"sector_size": null,
|
||||||
|
"unit": "GiB",
|
||||||
|
"value": 20
|
||||||
|
},
|
||||||
|
"mount_options": [],
|
||||||
|
"mountpoint": "/",
|
||||||
|
"obj_id": "3e7018a0-363b-4d05-ab83-8e82d13db208",
|
||||||
|
"start": {
|
||||||
|
"sector_size": null,
|
||||||
|
"unit": "MiB",
|
||||||
|
"value": 513
|
||||||
|
},
|
||||||
|
"status": "create",
|
||||||
|
"type": "primary"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"btrfs": [],
|
||||||
|
"flags": [],
|
||||||
|
"fs_type": "ext4",
|
||||||
|
"size": {
|
||||||
|
"sector_size": null,
|
||||||
|
"unit": "Percent",
|
||||||
|
"value": 100
|
||||||
|
},
|
||||||
|
"mount_options": [],
|
||||||
|
"mountpoint": "/home",
|
||||||
|
"obj_id": "ce58b139-f041-4a06-94da-1f8bad775d3f",
|
||||||
|
"start": {
|
||||||
|
"sector_size": null,
|
||||||
|
"unit": "GiB",
|
||||||
|
"value": 20
|
||||||
|
},
|
||||||
|
"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": {
|
||||||
|
"mirror-regions": {
|
||||||
|
"Australia": [
|
||||||
|
"http://archlinux.mirror.digitalpacific.com.au/$repo/os/$arch"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"swap": false,
|
||||||
|
"timezone": "UTC",
|
||||||
|
"version": "2.8.6"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"!root-password": "super_pwd",
|
||||||
|
"!users": [
|
||||||
|
{
|
||||||
|
"!password": "user_pwd",
|
||||||
|
"sudo": true,
|
||||||
|
"username": "user_name"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,171 @@
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from pytest import MonkeyPatch
|
||||||
|
|
||||||
|
from archinstall.default_profiles.profile import GreeterType
|
||||||
|
from archinstall.lib.args import ArchConfig, ArchConfigHandler, Arguments
|
||||||
|
from archinstall.lib.disk import DiskLayoutConfiguration, DiskLayoutType
|
||||||
|
from archinstall.lib.hardware import GfxDriver
|
||||||
|
from archinstall.lib.locale import LocaleConfiguration
|
||||||
|
from archinstall.lib.mirrors import MirrorConfiguration
|
||||||
|
from archinstall.lib.models import Audio, AudioConfiguration, Bootloader, NetworkConfiguration, User
|
||||||
|
from archinstall.lib.models.network_configuration import Nic, NicType
|
||||||
|
from archinstall.lib.profile.profile_model import ProfileConfiguration
|
||||||
|
from archinstall.lib.profile.profiles_handler import profile_handler
|
||||||
|
from archinstall.lib.translationhandler import translation_handler
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_args(monkeypatch):
|
||||||
|
monkeypatch.setattr('sys.argv', ['archinstall'])
|
||||||
|
handler = ArchConfigHandler()
|
||||||
|
args = handler.args
|
||||||
|
assert args == Arguments(
|
||||||
|
config=None,
|
||||||
|
config_url=None,
|
||||||
|
creds=None,
|
||||||
|
silent=False,
|
||||||
|
dry_run=False,
|
||||||
|
script='guided',
|
||||||
|
mount_point=Path('/mnt'),
|
||||||
|
skip_ntp=False,
|
||||||
|
debug=False,
|
||||||
|
offline=False,
|
||||||
|
no_pkg_lookups=False,
|
||||||
|
plugin=None,
|
||||||
|
skip_version_check=False,
|
||||||
|
advanced=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_correct_parsing_args(
|
||||||
|
monkeypatch: MonkeyPatch,
|
||||||
|
config_fixture: Path,
|
||||||
|
creds_fixture: Path
|
||||||
|
) -> None:
|
||||||
|
monkeypatch.setattr('sys.argv', [
|
||||||
|
'archinstall',
|
||||||
|
'--config',
|
||||||
|
str(config_fixture),
|
||||||
|
'--config-url',
|
||||||
|
'https://example.com',
|
||||||
|
'--creds',
|
||||||
|
str(creds_fixture),
|
||||||
|
'--script',
|
||||||
|
'execution_script',
|
||||||
|
'--mount-point',
|
||||||
|
'/tmp',
|
||||||
|
'--skip-ntp',
|
||||||
|
'--debug',
|
||||||
|
'--offline',
|
||||||
|
'--no-pkg-lookups',
|
||||||
|
'--plugin',
|
||||||
|
'pytest_plugin.py',
|
||||||
|
'--skip-version-check',
|
||||||
|
'--advanced',
|
||||||
|
'--dry-run',
|
||||||
|
'--silent'
|
||||||
|
])
|
||||||
|
|
||||||
|
handler = ArchConfigHandler()
|
||||||
|
args = handler.args
|
||||||
|
|
||||||
|
assert args == Arguments(
|
||||||
|
config=config_fixture,
|
||||||
|
config_url='https://example.com',
|
||||||
|
creds=creds_fixture,
|
||||||
|
silent=True,
|
||||||
|
dry_run=True,
|
||||||
|
script='execution_script',
|
||||||
|
mount_point=Path('/tmp'),
|
||||||
|
skip_ntp=True,
|
||||||
|
debug=True,
|
||||||
|
offline=True,
|
||||||
|
no_pkg_lookups=True,
|
||||||
|
plugin='pytest_plugin.py',
|
||||||
|
skip_version_check=True,
|
||||||
|
advanced=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_config_file_parsing(
|
||||||
|
monkeypatch: MonkeyPatch,
|
||||||
|
config_fixture: Path,
|
||||||
|
creds_fixture: Path
|
||||||
|
) -> None:
|
||||||
|
monkeypatch.setattr('sys.argv', [
|
||||||
|
'archinstall',
|
||||||
|
'--config',
|
||||||
|
str(config_fixture),
|
||||||
|
'--creds',
|
||||||
|
str(creds_fixture),
|
||||||
|
])
|
||||||
|
|
||||||
|
handler = ArchConfigHandler()
|
||||||
|
arch_config = handler.arch_config
|
||||||
|
|
||||||
|
assert arch_config == ArchConfig(
|
||||||
|
version='3.0.0',
|
||||||
|
locale_config=LocaleConfiguration(
|
||||||
|
kb_layout='us',
|
||||||
|
sys_lang='en_US',
|
||||||
|
sys_enc='UTF-8'
|
||||||
|
),
|
||||||
|
archinstall_language=translation_handler.get_language_by_abbr('en'),
|
||||||
|
disk_config=DiskLayoutConfiguration(
|
||||||
|
config_type=DiskLayoutType.Default,
|
||||||
|
device_modifications=[],
|
||||||
|
lvm_config=None,
|
||||||
|
mountpoint=None
|
||||||
|
),
|
||||||
|
profile_config=ProfileConfiguration(
|
||||||
|
profile=profile_handler.parse_profile_config({
|
||||||
|
"custom_settings": {
|
||||||
|
"Hyprland": {
|
||||||
|
"seat_access": "polkit"
|
||||||
|
},
|
||||||
|
"Sway": {
|
||||||
|
"seat_access": "seatd"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"details": [
|
||||||
|
"Sway",
|
||||||
|
"Hyprland"
|
||||||
|
],
|
||||||
|
"main": "Desktop"
|
||||||
|
}),
|
||||||
|
gfx_driver=GfxDriver.AllOpenSource,
|
||||||
|
greeter=GreeterType.Lightdm
|
||||||
|
),
|
||||||
|
mirror_config=MirrorConfiguration(
|
||||||
|
mirror_regions={},
|
||||||
|
custom_mirrors=[]
|
||||||
|
),
|
||||||
|
network_config=NetworkConfiguration(
|
||||||
|
type=NicType.MANUAL,
|
||||||
|
nics=[
|
||||||
|
Nic(
|
||||||
|
iface='eno1',
|
||||||
|
ip='192.168.1.15/24',
|
||||||
|
dhcp=True,
|
||||||
|
gateway='192.168.1.1',
|
||||||
|
dns=[
|
||||||
|
'192.168.1.1',
|
||||||
|
'9.9.9.9'
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
bootloader=Bootloader.Systemd,
|
||||||
|
uki=False,
|
||||||
|
audio_config=AudioConfiguration(Audio.Pipewire),
|
||||||
|
hostname='archy',
|
||||||
|
kernels=['linux-zen'],
|
||||||
|
ntp=True,
|
||||||
|
packages=["firefox"],
|
||||||
|
parallel_downloads=66,
|
||||||
|
swap=False,
|
||||||
|
timezone='UTC',
|
||||||
|
additional_repositories=["testing"],
|
||||||
|
_users=[User(username='user_name', password='user_pwd', sudo=True)],
|
||||||
|
_disk_encryption=None
|
||||||
|
)
|
||||||
Loading…
Reference in New Issue