]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Replace MkosiConfigParser with parse_config()
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Fri, 1 Sep 2023 09:41:29 +0000 (11:41 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Fri, 1 Sep 2023 09:41:29 +0000 (11:41 +0200)
The only reason that MkosiConfigParser is a class is to store the
lookup hashmaps required for configuration parsing. Yet we can
easily do that as well by just declaring the functions that need
those inline in a function. So let's simplify the configuration
parsing interface by replacing MkosiConfigParser with a function
parse_config().

mkosi/__init__.py
mkosi/__main__.py
mkosi/config.py
tests/test_parse_load_args.py

index 0a8951efe5e460305a13efccb84408143d941186..5686d8b27d7ad1cc12ea6f7199598a262c33ae2f 100644 (file)
@@ -31,11 +31,11 @@ from mkosi.config import (
     ManifestFormat,
     MkosiArgs,
     MkosiConfig,
-    MkosiConfigParser,
     OutputFormat,
     SecureBootSignTool,
     Verb,
     format_source_target,
+    parse_config,
     summary,
 )
 from mkosi.install import add_dropin_config_from_resource
@@ -946,7 +946,7 @@ def build_initrd(state: MkosiState) -> Path:
     ]
 
     with complete_step("Building initrd"):
-        args, presets = MkosiConfigParser().parse(cmdline)
+        args, presets = parse_config(cmdline)
         config = presets[0]
         unlink_output(args, config)
         build_image(args, config)
index 8442cecc0461e5f5dc659cd9f7ec26812169ff14..9b3e1f44690e7dd4555bec10b439933aa481d650 100644 (file)
@@ -9,7 +9,7 @@ import sys
 from collections.abc import Iterator
 
 from mkosi import run_verb
-from mkosi.config import MkosiConfigParser
+from mkosi.config import parse_config
 from mkosi.log import ARG_DEBUG, log_setup
 from mkosi.run import ensure_exc_info, run
 
@@ -42,7 +42,7 @@ def propagate_failed_return() -> Iterator[None]:
 @propagate_failed_return()
 def main() -> None:
     log_setup()
-    args, presets = MkosiConfigParser().parse()
+    args, presets = parse_config()
 
     try:
         run_verb(args, presets)
index 4fb7ae9086ac089a3dc1ccb79388f345905b62f3..9b45dc0d9107ae4098ed02d674c79f50741d7cd2 100644 (file)
@@ -871,776 +871,1005 @@ def parse_ini(path: Path, only_sections: Sequence[str] = ()) -> Iterator[tuple[s
         yield section, setting, value
 
 
-class MkosiConfigParser:
-    SETTINGS = (
-        MkosiConfigSetting(
-            dest="dependencies",
-            long="--dependency",
-            section="Preset",
-            parse=config_make_list_parser(delimiter=","),
-            help="Specify other presets that this preset depends on",
-        ),
-        MkosiConfigSetting(
-            dest="distribution",
-            short="-d",
-            section="Distribution",
-            parse=config_make_enum_parser(Distribution),
-            match=config_make_enum_matcher(Distribution),
-            default=detect_distribution()[0],
-            choices=Distribution.values(),
-            help="Distribution to install",
-        ),
-        MkosiConfigSetting(
-            dest="release",
-            short="-r",
-            section="Distribution",
-            parse=config_parse_string,
-            match=config_make_string_matcher(),
-            default_factory=config_default_release,
-            default_factory_depends=("distribution",),
-            help="Distribution release to install",
-        ),
-        MkosiConfigSetting(
-            dest="architecture",
-            section="Distribution",
-            parse=config_make_enum_parser(Architecture),
-            default=Architecture.native(),
-            choices=Architecture.values(),
-            help="Override the architecture of installation",
-        ),
-        MkosiConfigSetting(
-            dest="mirror",
-            short="-m",
-            section="Distribution",
-            default_factory=config_default_mirror,
-            default_factory_depends=("distribution", "release", "architecture"),
-            help="Distribution mirror to use",
-        ),
-        MkosiConfigSetting(
-            dest="local_mirror",
-            section="Distribution",
-            help="Use a single local, flat and plain mirror to build the image",
-        ),
-        MkosiConfigSetting(
-            dest="repository_key_check",
-            metavar="BOOL",
-            nargs="?",
-            section="Distribution",
-            default=True,
-            parse=config_parse_boolean,
-            help="Controls signature and key checks on repositories",
-        ),
-        MkosiConfigSetting(
-            dest="repositories",
-            metavar="REPOS",
-            section="Distribution",
-            parse=config_make_list_parser(delimiter=","),
-            help="Repositories to use",
-        ),
-        MkosiConfigSetting(
-            dest="cache_only",
-            metavar="BOOL",
-            section="Distribution",
-            parse=config_parse_boolean,
-            help="Only use the package cache when installing packages",
-        ),
-
-        MkosiConfigSetting(
-            dest="output_format",
-            short="-t",
-            long="--format",
-            metavar="FORMAT",
-            name="Format",
-            section="Output",
-            parse=config_make_enum_parser(OutputFormat),
-            default=OutputFormat.disk,
-            choices=OutputFormat.values(),
-            help="Output Format",
-        ),
-        MkosiConfigSetting(
-            dest="manifest_format",
-            metavar="FORMAT",
-            section="Output",
-            parse=config_make_list_parser(delimiter=",", parse=make_enum_parser(ManifestFormat)),
-            help="Manifest Format",
-        ),
-        MkosiConfigSetting(
-            dest="output",
-            short="-o",
-            metavar="NAME",
-            section="Output",
-            parse=config_parse_filename,
-            help="Output name",
-        ),
-        MkosiConfigSetting(
-            dest="compress_output",
-            metavar="ALG",
-            nargs="?",
-            section="Output",
-            parse=config_parse_compression,
-            default_factory=config_default_compression,
-            default_factory_depends=("distribution", "release", "output_format"),
-            help="Enable whole-output compression (with images or archives)",
-        ),
-        MkosiConfigSetting(
-            dest="output_dir",
-            short="-O",
-            metavar="DIR",
-            name="OutputDirectory",
-            section="Output",
-            parse=config_make_path_parser(required=False),
-            paths=("mkosi.output",),
-            default_factory=lambda _: Path.cwd(),
-            help="Output directory",
-        ),
-        MkosiConfigSetting(
-            dest="workspace_dir",
-            metavar="DIR",
-            name="WorkspaceDirectory",
-            section="Output",
-            parse=config_make_path_parser(required=False),
-            paths=("mkosi.workspace",),
-            default_factory=lambda _: Path.cwd(),
-            help="Workspace directory",
-        ),
-        MkosiConfigSetting(
-            dest="cache_dir",
-            metavar="PATH",
-            name="CacheDirectory",
-            section="Output",
-            parse=config_make_path_parser(required=False),
-            paths=("mkosi.cache",),
-            help="Package cache path",
-        ),
-        MkosiConfigSetting(
-            dest="build_dir",
-            metavar="PATH",
-            name="BuildDirectory",
-            section="Output",
-            parse=config_make_path_parser(required=False),
-            paths=("mkosi.builddir",),
-            help="Path to use as persistent build directory",
-        ),
-        MkosiConfigSetting(
-            dest="image_version",
-            match=config_match_image_version,
-            section="Output",
-            help="Set version for image",
-            paths=("mkosi.version",),
-            path_read_text=True,
-        ),
-        MkosiConfigSetting(
-            dest="image_id",
-            match=config_make_string_matcher(allow_globs=True),
-            section="Output",
-            help="Set ID for image",
-        ),
-        MkosiConfigSetting(
-            dest="split_artifacts",
-            metavar="BOOL",
-            nargs="?",
-            section="Output",
-            parse=config_parse_boolean,
-            help="Generate split partitions",
-        ),
-        MkosiConfigSetting(
-            dest="repart_dirs",
-            long="--repart-dir",
-            metavar="PATH",
-            name="RepartDirectories",
-            section="Output",
-            parse=config_make_list_parser(delimiter=",", parse=make_path_parser()),
-            paths=("mkosi.repart",),
-            path_default=False,
-            help="Directory containing systemd-repart partition definitions",
-        ),
-        MkosiConfigSetting(
-            dest="sector_size",
-            section="Output",
-            parse=config_parse_string,
-            help="Set the disk image sector size",
-        ),
-        MkosiConfigSetting(
-            dest="overlay",
-            metavar="BOOL",
-            nargs="?",
-            section="Output",
-            parse=config_parse_boolean,
-            help="Only output the additions on top of the given base trees",
-        ),
-        MkosiConfigSetting(
-            dest="use_subvolumes",
-            metavar="FEATURE",
-            nargs="?",
-            section="Output",
-            parse=config_parse_feature,
-            help="Use btrfs subvolumes for faster directory operations where possible",
-        ),
-        MkosiConfigSetting(
-            dest="seed",
-            metavar="UUID",
-            section="Output",
-            parse=config_parse_seed,
-            help="Set the seed for systemd-repart",
-        ),
-
-        MkosiConfigSetting(
-            dest="packages",
-            short="-p",
-            long="--package",
-            metavar="PACKAGE",
-            section="Content",
-            parse=config_make_list_parser(delimiter=","),
-            help="Add an additional package to the OS image",
-        ),
-        MkosiConfigSetting(
-            dest="build_packages",
-            long="--build-package",
-            metavar="PACKAGE",
-            section="Content",
-            parse=config_make_list_parser(delimiter=","),
-            help="Additional packages needed for build script",
-        ),
-        MkosiConfigSetting(
-            dest="with_docs",
-            metavar="BOOL",
-            nargs="?",
-            section="Content",
-            parse=config_parse_boolean,
-            help="Install documentation",
-        ),
-        MkosiConfigSetting(
-            dest="base_trees",
-            long='--base-tree',
-            metavar='PATH',
-            section="Content",
-            parse=config_make_list_parser(delimiter=",", parse=make_path_parser(required=False)),
-            help='Use the given tree as base tree (e.g. lower sysext layer)',
-        ),
-        MkosiConfigSetting(
-            dest="skeleton_trees",
-            long="--skeleton-tree",
-            metavar="PATH",
-            section="Content",
-            parse=config_make_list_parser(delimiter=",", parse=make_source_target_paths_parser()),
-            paths=("mkosi.skeleton", "mkosi.skeleton.tar"),
-            path_default=False,
-            help="Use a skeleton tree to bootstrap the image before installing anything",
-        ),
-        MkosiConfigSetting(
-            dest="package_manager_trees",
-            long="--package-manager-tree",
-            metavar="PATH",
-            section="Content",
-            parse=config_make_list_parser(delimiter=",", parse=make_source_target_paths_parser()),
-            default_factory=lambda ns: ns.skeleton_trees,
-            default_factory_depends=("skeleton_trees",),
-            help="Use a package manager tree to configure the package manager",
-        ),
-        MkosiConfigSetting(
-            dest="extra_trees",
-            long="--extra-tree",
-            metavar="PATH",
-            section="Content",
-            parse=config_make_list_parser(delimiter=",", parse=make_source_target_paths_parser()),
-            paths=("mkosi.extra", "mkosi.extra.tar"),
-            path_default=False,
-            help="Copy an extra tree on top of image",
-        ),
-        MkosiConfigSetting(
-            dest="remove_packages",
-            long="--remove-package",
-            metavar="PACKAGE",
-            section="Content",
-            parse=config_make_list_parser(delimiter=","),
-            help="Remove package from the image OS image after installation",
-        ),
-        MkosiConfigSetting(
-            dest="remove_files",
-            metavar="GLOB",
-            section="Content",
-            parse=config_make_list_parser(delimiter=","),
-            help="Remove files from built image",
-        ),
-        MkosiConfigSetting(
-            dest="clean_package_metadata",
-            metavar="FEATURE",
-            section="Content",
-            parse=config_parse_feature,
-            help="Remove package manager database and other files",
-        ),
-        MkosiConfigSetting(
-            dest="source_date_epoch",
-            metavar="TIMESTAMP",
-            section="Content",
-            parse=config_parse_source_date_epoch,
-            default_factory=config_default_source_date_epoch,
-            default_factory_depends=("environment",),
-            help="Set the $SOURCE_DATE_EPOCH timestamp",
-        ),
-        MkosiConfigSetting(
-            dest="prepare_script",
-            metavar="PATH",
-            section="Content",
-            parse=config_parse_script,
-            paths=("mkosi.prepare",),
-            help="Prepare script to run inside the image before it is cached",
-        ),
-        MkosiConfigSetting(
-            dest="build_script",
-            metavar="PATH",
-            section="Content",
-            parse=config_parse_script,
-            paths=("mkosi.build",),
-            help="Build script to run inside image",
-        ),
-        MkosiConfigSetting(
-            dest="postinst_script",
-            metavar="PATH",
-            name="PostInstallationScript",
-            section="Content",
-            parse=config_parse_script,
-            paths=("mkosi.postinst",),
-            help="Postinstall script to run inside image",
-        ),
-        MkosiConfigSetting(
-            dest="finalize_script",
-            metavar="PATH",
-            section="Content",
-            parse=config_parse_script,
-            paths=("mkosi.finalize",),
-            help="Postinstall script to run outside image",
-        ),
-        MkosiConfigSetting(
-            dest="build_sources",
-            metavar="PATH",
-            section="Content",
-            parse=config_make_list_parser(delimiter=",", parse=make_source_target_paths_parser(absolute=False)),
-            help="Path for sources to build",
-        ),
-        MkosiConfigSetting(
-            dest="environment",
-            short="-E",
-            metavar="NAME[=VALUE]",
-            section="Content",
-            parse=config_make_list_parser(delimiter=" ", unescape=True),
-            help="Set an environment variable when running scripts",
-        ),
-        MkosiConfigSetting(
-            dest="with_tests",
-            short="-T",
-            long="--without-tests",
-            nargs="?",
-            const="no",
-            section="Content",
-            parse=config_parse_boolean,
-            default=True,
-            help="Do not run tests as part of build script, if supported",
-        ),
-        MkosiConfigSetting(
-            dest="with_network",
-            metavar="BOOL",
-            nargs="?",
-            section="Content",
-            parse=config_parse_boolean,
-            help="Run build and postinst scripts with network access (instead of private network)",
-        ),
-        MkosiConfigSetting(
-            dest="bootable",
-            metavar="FEATURE",
-            nargs="?",
-            section="Content",
-            parse=config_parse_feature,
-            match=config_match_feature,
-            help="Generate ESP partition with systemd-boot and UKIs for installed kernels",
-        ),
-        MkosiConfigSetting(
-            dest="bootloader",
-            metavar="BOOTLOADER",
-            section="Content",
-            parse=config_make_enum_parser(Bootloader),
-            choices=Bootloader.values(),
-            default=Bootloader.systemd_boot,
-            help="Specify which UEFI bootloader to use",
-        ),
-        MkosiConfigSetting(
-            dest="bios_bootloader",
-            metavar="BOOTLOADER",
-            section="Content",
-            parse=config_make_enum_parser(BiosBootloader),
-            choices=BiosBootloader.values(),
-            default=BiosBootloader.none,
-            help="Specify which BIOS bootloader to use",
-        ),
-        MkosiConfigSetting(
-            dest="initrds",
-            long="--initrd",
-            metavar="PATH",
-            section="Content",
-            parse=config_make_list_parser(delimiter=",", parse=make_path_parser(required=False)),
-            help="Add a user-provided initrd to image",
-        ),
-        MkosiConfigSetting(
-            dest="kernel_command_line",
-            metavar="OPTIONS",
-            section="Content",
-            parse=config_make_list_parser(delimiter=" "),
-            default=["console=ttyS0"],
-            help="Set the kernel command line (only bootable images)",
-        ),
-        MkosiConfigSetting(
-            dest="kernel_modules_include",
-            metavar="REGEX",
-            section="Content",
-            parse=config_make_list_parser(delimiter=","),
-            help="Only include the specified kernel modules in the image",
-        ),
-        MkosiConfigSetting(
-            dest="kernel_modules_exclude",
-            metavar="REGEX",
-            section="Content",
-            parse=config_make_list_parser(delimiter=","),
-            help="Exclude the specified kernel modules from the image",
-        ),
-        MkosiConfigSetting(
-            dest="kernel_modules_initrd",
-            metavar="BOOL",
-            nargs="?",
-            section="Content",
-            parse=config_parse_boolean,
-            default=True,
-            help="When building a bootable image, add an extra initrd containing the kernel modules",
-        ),
-        MkosiConfigSetting(
-            dest="kernel_modules_initrd_include",
-            metavar="REGEX",
-            section="Content",
-            parse=config_make_list_parser(delimiter=","),
-            help="When building a kernel modules initrd, only include the specified kernel modules",
-        ),
-        MkosiConfigSetting(
-            dest="kernel_modules_initrd_exclude",
-            metavar="REGEX",
-            section="Content",
-            parse=config_make_list_parser(delimiter=","),
-            help="When building a kernel modules initrd, exclude the specified kernel modules",
-        ),
-        MkosiConfigSetting(
-            dest="locale",
-            section="Content",
-            parse=config_parse_string,
-            help="Set the system locale",
-        ),
-        MkosiConfigSetting(
-            dest="locale_messages",
-            metavar="LOCALE",
-            section="Content",
-            parse=config_parse_string,
-            help="Set the messages locale",
-        ),
-        MkosiConfigSetting(
-            dest="keymap",
-            metavar="KEYMAP",
-            section="Content",
-            parse=config_parse_string,
-            help="Set the system keymap",
-        ),
-        MkosiConfigSetting(
-            dest="timezone",
-            metavar="TIMEZONE",
-            section="Content",
-            parse=config_parse_string,
-            help="Set the system timezone",
-        ),
-        MkosiConfigSetting(
-            dest="hostname",
-            metavar="HOSTNAME",
-            section="Content",
-            parse=config_parse_string,
-            help="Set the system hostname",
-        ),
-        MkosiConfigSetting(
-            dest="root_password",
-            metavar="PASSWORD",
-            section="Content",
-            parse=config_parse_root_password,
-            paths=("mkosi.rootpw",),
-            path_read_text=True,
-            path_secret=True,
-            help="Set the password for root",
-        ),
-        MkosiConfigSetting(
-            dest="root_shell",
-            metavar="SHELL",
-            section="Content",
-            parse=config_parse_string,
-            help="Set the shell for root",
-        ),
-        MkosiConfigSetting(
-            dest="autologin",
-            metavar="BOOL",
-            nargs="?",
-            section="Content",
-            parse=config_parse_boolean,
-            help="Enable root autologin",
-        ),
-        MkosiConfigSetting(
-            dest="make_initrd",
-            metavar="BOOL",
-            nargs="?",
-            section="Content",
-            parse=config_parse_boolean,
-            help="Make sure the image can be used as an initramfs",
-        ),
-        MkosiConfigSetting(
-            dest="ssh",
-            metavar="BOOL",
-            nargs="?",
-            section="Content",
-            parse=config_parse_boolean,
-            help="Set up SSH access from the host to the final image via 'mkosi ssh'",
-        ),
-
-        MkosiConfigSetting(
-            dest="secure_boot",
-            metavar="BOOL",
-            nargs="?",
-            section="Validation",
-            parse=config_parse_boolean,
-            help="Sign the resulting kernel/initrd image for UEFI SecureBoot",
-        ),
-        MkosiConfigSetting(
-            dest="secure_boot_key",
-            metavar="PATH",
-            section="Validation",
-            parse=config_make_path_parser(),
-            paths=("mkosi.key",),
-            help="UEFI SecureBoot private key in PEM format",
-        ),
-        MkosiConfigSetting(
-            dest="secure_boot_certificate",
-            metavar="PATH",
-            section="Validation",
-            parse=config_make_path_parser(),
-            paths=("mkosi.crt",),
-            help="UEFI SecureBoot certificate in X509 format",
-        ),
-        MkosiConfigSetting(
-            dest="secure_boot_sign_tool",
-            metavar="TOOL",
-            section="Validation",
-            parse=config_make_enum_parser(SecureBootSignTool),
-            default=SecureBootSignTool.auto,
-            choices=SecureBootSignTool.values(),
-            help="Tool to use for signing PE binaries for secure boot",
-        ),
-        MkosiConfigSetting(
-            dest="verity_key",
-            metavar="PATH",
-            section="Validation",
-            parse=config_make_path_parser(),
-            paths=("mkosi.key",),
-            help="Private key for signing verity signature in PEM format",
-        ),
-        MkosiConfigSetting(
-            dest="verity_certificate",
-            metavar="PATH",
-            section="Validation",
-            parse=config_make_path_parser(),
-            paths=("mkosi.crt",),
-            help="Certificate for signing verity signature in X509 format",
-        ),
-        MkosiConfigSetting(
-            dest="sign_expected_pcr",
-            metavar="FEATURE",
-            section="Validation",
-            parse=config_parse_feature,
-            help="Measure the components of the unified kernel image (UKI) and embed the PCR signature into the UKI",
-        ),
-        MkosiConfigSetting(
-            dest="passphrase",
-            metavar="PATH",
-            section="Validation",
-            parse=config_make_path_parser(required=False),
-            paths=("mkosi.passphrase",),
-            help="Path to a file containing the passphrase to use when LUKS encryption is selected",
-        ),
-        MkosiConfigSetting(
-            dest="checksum",
-            metavar="BOOL",
-            nargs="?",
-            section="Validation",
-            parse=config_parse_boolean,
-            help="Write SHA256SUMS file",
-        ),
-        MkosiConfigSetting(
-            dest="sign",
-            metavar="BOOL",
-            nargs="?",
-            section="Validation",
-            parse=config_parse_boolean,
-            help="Write and sign SHA256SUMS file",
-        ),
-        MkosiConfigSetting(
-            dest="key",
-            section="Validation",
-            help="GPG key to use for signing",
-        ),
-
-        MkosiConfigSetting(
-            dest="incremental",
-            short="-i",
-            metavar="BOOL",
-            nargs="?",
-            section="Host",
-            parse=config_parse_boolean,
-            help="Make use of and generate intermediary cache images",
-        ),
-        MkosiConfigSetting(
-            dest="nspawn_settings",
-            name="NSpawnSettings",
-            long="--settings",
-            metavar="PATH",
-            section="Host",
-            parse=config_make_path_parser(),
-            paths=("mkosi.nspawn",),
-            help="Add in .nspawn settings file",
-        ),
-        MkosiConfigSetting(
-            dest="extra_search_paths",
-            long="--extra-search-path",
-            metavar="PATH",
-            section="Host",
-            parse=config_make_list_parser(delimiter=",", parse=make_path_parser()),
-            help="List of comma-separated paths to look for programs before looking in PATH",
-        ),
-        MkosiConfigSetting(
-            dest="qemu_gui",
-            metavar="BOOL",
-            nargs="?",
-            section="Host",
-            parse=config_parse_boolean,
-            help="Start QEMU in graphical mode",
-        ),
-        MkosiConfigSetting(
-            dest="qemu_smp",
-            metavar="SMP",
-            section="Host",
-            default="1",
-            help="Configure guest's SMP settings",
-        ),
-        MkosiConfigSetting(
-            dest="qemu_mem",
-            metavar="MEM",
-            section="Host",
-            default="2G",
-            help="Configure guest's RAM size",
-        ),
-        MkosiConfigSetting(
-            dest="qemu_kvm",
-            metavar="FEATURE",
-            nargs="?",
-            section="Host",
-            parse=config_parse_feature,
-            help="Configure whether to use KVM or not",
-        ),
-        MkosiConfigSetting(
-            dest="qemu_vsock",
-            metavar="FEATURE",
-            nargs="?",
-            section="Host",
-            parse=config_parse_feature,
-            help="Configure whether to use qemu with a vsock or not",
-        ),
-        MkosiConfigSetting(
-            dest="qemu_swtpm",
-            metavar="FEATURE",
-            nargs="?",
-            section="Host",
-            parse=config_parse_feature,
-            help="Configure whether to use qemu with swtpm or not",
-        ),
-        MkosiConfigSetting(
-            dest="qemu_cdrom",
-            metavar="BOOLEAN",
-            nargs="?",
-            section="Host",
-            parse=config_parse_boolean,
-            help="Attach the image as a CD-ROM to the virtual machine",
-        ),
-        MkosiConfigSetting(
-            dest="qemu_bios",
-            metavar="BOOLEAN",
-            nargs="?",
-            section="Host",
-            parse=config_parse_boolean,
-            help="Boot QEMU with SeaBIOS instead of EDK2",
-        ),
-        MkosiConfigSetting(
-            dest="qemu_args",
-            metavar="ARGS",
-            section="Host",
-            parse=config_make_list_parser(delimiter=" "),
-            # Suppress the command line option because it's already possible to pass qemu args as normal
-            # arguments.
-            help=argparse.SUPPRESS,
-        ),
-        MkosiConfigSetting(
-            dest="ephemeral",
-            metavar="BOOL",
-            section="Host",
-            parse=config_parse_boolean,
-            help=('If specified, the container/VM is run with a temporary snapshot of the output '
-                  'image that is removed immediately when the container/VM terminates'),
-            nargs="?",
-        ),
-        MkosiConfigSetting(
-            dest="credentials",
-            long="--credential",
-            metavar="NAME=VALUE",
-            section="Host",
-            parse=config_make_list_parser(delimiter=" "),
-            help="Pass a systemd credential to systemd-nspawn or qemu",
-        ),
-        MkosiConfigSetting(
-            dest="kernel_command_line_extra",
-            metavar="OPTIONS",
-            section="Host",
-            parse=config_make_list_parser(delimiter=" "),
-            help="Append extra entries to the kernel command line when booting the image",
-        ),
-        MkosiConfigSetting(
-            dest="acl",
-            metavar="BOOL",
-            nargs="?",
-            section="Host",
-            parse=config_parse_boolean,
-            help="Set ACLs on generated directories to permit the user running mkosi to remove them",
-        ),
-        MkosiConfigSetting(
-            dest="tools_tree",
-            long="--tools-tree",
-            metavar="PATH",
-            section="Host",
-            parse=config_make_path_parser(required=False, absolute=False),
-            paths=("mkosi.tools",),
-            help="Look up programs to execute inside the given tree",
-        ),
+SETTINGS = (
+    MkosiConfigSetting(
+        dest="dependencies",
+        long="--dependency",
+        section="Preset",
+        parse=config_make_list_parser(delimiter=","),
+        help="Specify other presets that this preset depends on",
+    ),
+    MkosiConfigSetting(
+        dest="distribution",
+        short="-d",
+        section="Distribution",
+        parse=config_make_enum_parser(Distribution),
+        match=config_make_enum_matcher(Distribution),
+        default=detect_distribution()[0],
+        choices=Distribution.values(),
+        help="Distribution to install",
+    ),
+    MkosiConfigSetting(
+        dest="release",
+        short="-r",
+        section="Distribution",
+        parse=config_parse_string,
+        match=config_make_string_matcher(),
+        default_factory=config_default_release,
+        default_factory_depends=("distribution",),
+        help="Distribution release to install",
+    ),
+    MkosiConfigSetting(
+        dest="architecture",
+        section="Distribution",
+        parse=config_make_enum_parser(Architecture),
+        default=Architecture.native(),
+        choices=Architecture.values(),
+        help="Override the architecture of installation",
+    ),
+    MkosiConfigSetting(
+        dest="mirror",
+        short="-m",
+        section="Distribution",
+        default_factory=config_default_mirror,
+        default_factory_depends=("distribution", "release", "architecture"),
+        help="Distribution mirror to use",
+    ),
+    MkosiConfigSetting(
+        dest="local_mirror",
+        section="Distribution",
+        help="Use a single local, flat and plain mirror to build the image",
+    ),
+    MkosiConfigSetting(
+        dest="repository_key_check",
+        metavar="BOOL",
+        nargs="?",
+        section="Distribution",
+        default=True,
+        parse=config_parse_boolean,
+        help="Controls signature and key checks on repositories",
+    ),
+    MkosiConfigSetting(
+        dest="repositories",
+        metavar="REPOS",
+        section="Distribution",
+        parse=config_make_list_parser(delimiter=","),
+        help="Repositories to use",
+    ),
+    MkosiConfigSetting(
+        dest="cache_only",
+        metavar="BOOL",
+        section="Distribution",
+        parse=config_parse_boolean,
+        help="Only use the package cache when installing packages",
+    ),
+
+    MkosiConfigSetting(
+        dest="output_format",
+        short="-t",
+        long="--format",
+        metavar="FORMAT",
+        name="Format",
+        section="Output",
+        parse=config_make_enum_parser(OutputFormat),
+        default=OutputFormat.disk,
+        choices=OutputFormat.values(),
+        help="Output Format",
+    ),
+    MkosiConfigSetting(
+        dest="manifest_format",
+        metavar="FORMAT",
+        section="Output",
+        parse=config_make_list_parser(delimiter=",", parse=make_enum_parser(ManifestFormat)),
+        help="Manifest Format",
+    ),
+    MkosiConfigSetting(
+        dest="output",
+        short="-o",
+        metavar="NAME",
+        section="Output",
+        parse=config_parse_filename,
+        help="Output name",
+    ),
+    MkosiConfigSetting(
+        dest="compress_output",
+        metavar="ALG",
+        nargs="?",
+        section="Output",
+        parse=config_parse_compression,
+        default_factory=config_default_compression,
+        default_factory_depends=("distribution", "release", "output_format"),
+        help="Enable whole-output compression (with images or archives)",
+    ),
+    MkosiConfigSetting(
+        dest="output_dir",
+        short="-O",
+        metavar="DIR",
+        name="OutputDirectory",
+        section="Output",
+        parse=config_make_path_parser(required=False),
+        paths=("mkosi.output",),
+        default_factory=lambda _: Path.cwd(),
+        help="Output directory",
+    ),
+    MkosiConfigSetting(
+        dest="workspace_dir",
+        metavar="DIR",
+        name="WorkspaceDirectory",
+        section="Output",
+        parse=config_make_path_parser(required=False),
+        paths=("mkosi.workspace",),
+        default_factory=lambda _: Path.cwd(),
+        help="Workspace directory",
+    ),
+    MkosiConfigSetting(
+        dest="cache_dir",
+        metavar="PATH",
+        name="CacheDirectory",
+        section="Output",
+        parse=config_make_path_parser(required=False),
+        paths=("mkosi.cache",),
+        help="Package cache path",
+    ),
+    MkosiConfigSetting(
+        dest="build_dir",
+        metavar="PATH",
+        name="BuildDirectory",
+        section="Output",
+        parse=config_make_path_parser(required=False),
+        paths=("mkosi.builddir",),
+        help="Path to use as persistent build directory",
+    ),
+    MkosiConfigSetting(
+        dest="image_version",
+        match=config_match_image_version,
+        section="Output",
+        help="Set version for image",
+        paths=("mkosi.version",),
+        path_read_text=True,
+    ),
+    MkosiConfigSetting(
+        dest="image_id",
+        match=config_make_string_matcher(allow_globs=True),
+        section="Output",
+        help="Set ID for image",
+    ),
+    MkosiConfigSetting(
+        dest="split_artifacts",
+        metavar="BOOL",
+        nargs="?",
+        section="Output",
+        parse=config_parse_boolean,
+        help="Generate split partitions",
+    ),
+    MkosiConfigSetting(
+        dest="repart_dirs",
+        long="--repart-dir",
+        metavar="PATH",
+        name="RepartDirectories",
+        section="Output",
+        parse=config_make_list_parser(delimiter=",", parse=make_path_parser()),
+        paths=("mkosi.repart",),
+        path_default=False,
+        help="Directory containing systemd-repart partition definitions",
+    ),
+    MkosiConfigSetting(
+        dest="sector_size",
+        section="Output",
+        parse=config_parse_string,
+        help="Set the disk image sector size",
+    ),
+    MkosiConfigSetting(
+        dest="overlay",
+        metavar="BOOL",
+        nargs="?",
+        section="Output",
+        parse=config_parse_boolean,
+        help="Only output the additions on top of the given base trees",
+    ),
+    MkosiConfigSetting(
+        dest="use_subvolumes",
+        metavar="FEATURE",
+        nargs="?",
+        section="Output",
+        parse=config_parse_feature,
+        help="Use btrfs subvolumes for faster directory operations where possible",
+    ),
+    MkosiConfigSetting(
+        dest="seed",
+        metavar="UUID",
+        section="Output",
+        parse=config_parse_seed,
+        help="Set the seed for systemd-repart",
+    ),
+
+    MkosiConfigSetting(
+        dest="packages",
+        short="-p",
+        long="--package",
+        metavar="PACKAGE",
+        section="Content",
+        parse=config_make_list_parser(delimiter=","),
+        help="Add an additional package to the OS image",
+    ),
+    MkosiConfigSetting(
+        dest="build_packages",
+        long="--build-package",
+        metavar="PACKAGE",
+        section="Content",
+        parse=config_make_list_parser(delimiter=","),
+        help="Additional packages needed for build script",
+    ),
+    MkosiConfigSetting(
+        dest="with_docs",
+        metavar="BOOL",
+        nargs="?",
+        section="Content",
+        parse=config_parse_boolean,
+        help="Install documentation",
+    ),
+    MkosiConfigSetting(
+        dest="base_trees",
+        long='--base-tree',
+        metavar='PATH',
+        section="Content",
+        parse=config_make_list_parser(delimiter=",", parse=make_path_parser(required=False)),
+        help='Use the given tree as base tree (e.g. lower sysext layer)',
+    ),
+    MkosiConfigSetting(
+        dest="skeleton_trees",
+        long="--skeleton-tree",
+        metavar="PATH",
+        section="Content",
+        parse=config_make_list_parser(delimiter=",", parse=make_source_target_paths_parser()),
+        paths=("mkosi.skeleton", "mkosi.skeleton.tar"),
+        path_default=False,
+        help="Use a skeleton tree to bootstrap the image before installing anything",
+    ),
+    MkosiConfigSetting(
+        dest="package_manager_trees",
+        long="--package-manager-tree",
+        metavar="PATH",
+        section="Content",
+        parse=config_make_list_parser(delimiter=",", parse=make_source_target_paths_parser()),
+        default_factory=lambda ns: ns.skeleton_trees,
+        default_factory_depends=("skeleton_trees",),
+        help="Use a package manager tree to configure the package manager",
+    ),
+    MkosiConfigSetting(
+        dest="extra_trees",
+        long="--extra-tree",
+        metavar="PATH",
+        section="Content",
+        parse=config_make_list_parser(delimiter=",", parse=make_source_target_paths_parser()),
+        paths=("mkosi.extra", "mkosi.extra.tar"),
+        path_default=False,
+        help="Copy an extra tree on top of image",
+    ),
+    MkosiConfigSetting(
+        dest="remove_packages",
+        long="--remove-package",
+        metavar="PACKAGE",
+        section="Content",
+        parse=config_make_list_parser(delimiter=","),
+        help="Remove package from the image OS image after installation",
+    ),
+    MkosiConfigSetting(
+        dest="remove_files",
+        metavar="GLOB",
+        section="Content",
+        parse=config_make_list_parser(delimiter=","),
+        help="Remove files from built image",
+    ),
+    MkosiConfigSetting(
+        dest="clean_package_metadata",
+        metavar="FEATURE",
+        section="Content",
+        parse=config_parse_feature,
+        help="Remove package manager database and other files",
+    ),
+    MkosiConfigSetting(
+        dest="source_date_epoch",
+        metavar="TIMESTAMP",
+        section="Content",
+        parse=config_parse_source_date_epoch,
+        default_factory=config_default_source_date_epoch,
+        default_factory_depends=("environment",),
+        help="Set the $SOURCE_DATE_EPOCH timestamp",
+    ),
+    MkosiConfigSetting(
+        dest="prepare_script",
+        metavar="PATH",
+        section="Content",
+        parse=config_parse_script,
+        paths=("mkosi.prepare",),
+        help="Prepare script to run inside the image before it is cached",
+    ),
+    MkosiConfigSetting(
+        dest="build_script",
+        metavar="PATH",
+        section="Content",
+        parse=config_parse_script,
+        paths=("mkosi.build",),
+        help="Build script to run inside image",
+    ),
+    MkosiConfigSetting(
+        dest="postinst_script",
+        metavar="PATH",
+        name="PostInstallationScript",
+        section="Content",
+        parse=config_parse_script,
+        paths=("mkosi.postinst",),
+        help="Postinstall script to run inside image",
+    ),
+    MkosiConfigSetting(
+        dest="finalize_script",
+        metavar="PATH",
+        section="Content",
+        parse=config_parse_script,
+        paths=("mkosi.finalize",),
+        help="Postinstall script to run outside image",
+    ),
+    MkosiConfigSetting(
+        dest="build_sources",
+        metavar="PATH",
+        section="Content",
+        parse=config_make_list_parser(delimiter=",", parse=make_source_target_paths_parser(absolute=False)),
+        help="Path for sources to build",
+    ),
+    MkosiConfigSetting(
+        dest="environment",
+        short="-E",
+        metavar="NAME[=VALUE]",
+        section="Content",
+        parse=config_make_list_parser(delimiter=" ", unescape=True),
+        help="Set an environment variable when running scripts",
+    ),
+    MkosiConfigSetting(
+        dest="with_tests",
+        short="-T",
+        long="--without-tests",
+        nargs="?",
+        const="no",
+        section="Content",
+        parse=config_parse_boolean,
+        default=True,
+        help="Do not run tests as part of build script, if supported",
+    ),
+    MkosiConfigSetting(
+        dest="with_network",
+        metavar="BOOL",
+        nargs="?",
+        section="Content",
+        parse=config_parse_boolean,
+        help="Run build and postinst scripts with network access (instead of private network)",
+    ),
+    MkosiConfigSetting(
+        dest="bootable",
+        metavar="FEATURE",
+        nargs="?",
+        section="Content",
+        parse=config_parse_feature,
+        match=config_match_feature,
+        help="Generate ESP partition with systemd-boot and UKIs for installed kernels",
+    ),
+    MkosiConfigSetting(
+        dest="bootloader",
+        metavar="BOOTLOADER",
+        section="Content",
+        parse=config_make_enum_parser(Bootloader),
+        choices=Bootloader.values(),
+        default=Bootloader.systemd_boot,
+        help="Specify which UEFI bootloader to use",
+    ),
+    MkosiConfigSetting(
+        dest="bios_bootloader",
+        metavar="BOOTLOADER",
+        section="Content",
+        parse=config_make_enum_parser(BiosBootloader),
+        choices=BiosBootloader.values(),
+        default=BiosBootloader.none,
+        help="Specify which BIOS bootloader to use",
+    ),
+    MkosiConfigSetting(
+        dest="initrds",
+        long="--initrd",
+        metavar="PATH",
+        section="Content",
+        parse=config_make_list_parser(delimiter=",", parse=make_path_parser(required=False)),
+        help="Add a user-provided initrd to image",
+    ),
+    MkosiConfigSetting(
+        dest="kernel_command_line",
+        metavar="OPTIONS",
+        section="Content",
+        parse=config_make_list_parser(delimiter=" "),
+        default=["console=ttyS0"],
+        help="Set the kernel command line (only bootable images)",
+    ),
+    MkosiConfigSetting(
+        dest="kernel_modules_include",
+        metavar="REGEX",
+        section="Content",
+        parse=config_make_list_parser(delimiter=","),
+        help="Only include the specified kernel modules in the image",
+    ),
+    MkosiConfigSetting(
+        dest="kernel_modules_exclude",
+        metavar="REGEX",
+        section="Content",
+        parse=config_make_list_parser(delimiter=","),
+        help="Exclude the specified kernel modules from the image",
+    ),
+    MkosiConfigSetting(
+        dest="kernel_modules_initrd",
+        metavar="BOOL",
+        nargs="?",
+        section="Content",
+        parse=config_parse_boolean,
+        default=True,
+        help="When building a bootable image, add an extra initrd containing the kernel modules",
+    ),
+    MkosiConfigSetting(
+        dest="kernel_modules_initrd_include",
+        metavar="REGEX",
+        section="Content",
+        parse=config_make_list_parser(delimiter=","),
+        help="When building a kernel modules initrd, only include the specified kernel modules",
+    ),
+    MkosiConfigSetting(
+        dest="kernel_modules_initrd_exclude",
+        metavar="REGEX",
+        section="Content",
+        parse=config_make_list_parser(delimiter=","),
+        help="When building a kernel modules initrd, exclude the specified kernel modules",
+    ),
+    MkosiConfigSetting(
+        dest="locale",
+        section="Content",
+        parse=config_parse_string,
+        help="Set the system locale",
+    ),
+    MkosiConfigSetting(
+        dest="locale_messages",
+        metavar="LOCALE",
+        section="Content",
+        parse=config_parse_string,
+        help="Set the messages locale",
+    ),
+    MkosiConfigSetting(
+        dest="keymap",
+        metavar="KEYMAP",
+        section="Content",
+        parse=config_parse_string,
+        help="Set the system keymap",
+    ),
+    MkosiConfigSetting(
+        dest="timezone",
+        metavar="TIMEZONE",
+        section="Content",
+        parse=config_parse_string,
+        help="Set the system timezone",
+    ),
+    MkosiConfigSetting(
+        dest="hostname",
+        metavar="HOSTNAME",
+        section="Content",
+        parse=config_parse_string,
+        help="Set the system hostname",
+    ),
+    MkosiConfigSetting(
+        dest="root_password",
+        metavar="PASSWORD",
+        section="Content",
+        parse=config_parse_root_password,
+        paths=("mkosi.rootpw",),
+        path_read_text=True,
+        path_secret=True,
+        help="Set the password for root",
+    ),
+    MkosiConfigSetting(
+        dest="root_shell",
+        metavar="SHELL",
+        section="Content",
+        parse=config_parse_string,
+        help="Set the shell for root",
+    ),
+    MkosiConfigSetting(
+        dest="autologin",
+        metavar="BOOL",
+        nargs="?",
+        section="Content",
+        parse=config_parse_boolean,
+        help="Enable root autologin",
+    ),
+    MkosiConfigSetting(
+        dest="make_initrd",
+        metavar="BOOL",
+        nargs="?",
+        section="Content",
+        parse=config_parse_boolean,
+        help="Make sure the image can be used as an initramfs",
+    ),
+    MkosiConfigSetting(
+        dest="ssh",
+        metavar="BOOL",
+        nargs="?",
+        section="Content",
+        parse=config_parse_boolean,
+        help="Set up SSH access from the host to the final image via 'mkosi ssh'",
+    ),
+
+    MkosiConfigSetting(
+        dest="secure_boot",
+        metavar="BOOL",
+        nargs="?",
+        section="Validation",
+        parse=config_parse_boolean,
+        help="Sign the resulting kernel/initrd image for UEFI SecureBoot",
+    ),
+    MkosiConfigSetting(
+        dest="secure_boot_key",
+        metavar="PATH",
+        section="Validation",
+        parse=config_make_path_parser(),
+        paths=("mkosi.key",),
+        help="UEFI SecureBoot private key in PEM format",
+    ),
+    MkosiConfigSetting(
+        dest="secure_boot_certificate",
+        metavar="PATH",
+        section="Validation",
+        parse=config_make_path_parser(),
+        paths=("mkosi.crt",),
+        help="UEFI SecureBoot certificate in X509 format",
+    ),
+    MkosiConfigSetting(
+        dest="secure_boot_sign_tool",
+        metavar="TOOL",
+        section="Validation",
+        parse=config_make_enum_parser(SecureBootSignTool),
+        default=SecureBootSignTool.auto,
+        choices=SecureBootSignTool.values(),
+        help="Tool to use for signing PE binaries for secure boot",
+    ),
+    MkosiConfigSetting(
+        dest="verity_key",
+        metavar="PATH",
+        section="Validation",
+        parse=config_make_path_parser(),
+        paths=("mkosi.key",),
+        help="Private key for signing verity signature in PEM format",
+    ),
+    MkosiConfigSetting(
+        dest="verity_certificate",
+        metavar="PATH",
+        section="Validation",
+        parse=config_make_path_parser(),
+        paths=("mkosi.crt",),
+        help="Certificate for signing verity signature in X509 format",
+    ),
+    MkosiConfigSetting(
+        dest="sign_expected_pcr",
+        metavar="FEATURE",
+        section="Validation",
+        parse=config_parse_feature,
+        help="Measure the components of the unified kernel image (UKI) and embed the PCR signature into the UKI",
+    ),
+    MkosiConfigSetting(
+        dest="passphrase",
+        metavar="PATH",
+        section="Validation",
+        parse=config_make_path_parser(required=False),
+        paths=("mkosi.passphrase",),
+        help="Path to a file containing the passphrase to use when LUKS encryption is selected",
+    ),
+    MkosiConfigSetting(
+        dest="checksum",
+        metavar="BOOL",
+        nargs="?",
+        section="Validation",
+        parse=config_parse_boolean,
+        help="Write SHA256SUMS file",
+    ),
+    MkosiConfigSetting(
+        dest="sign",
+        metavar="BOOL",
+        nargs="?",
+        section="Validation",
+        parse=config_parse_boolean,
+        help="Write and sign SHA256SUMS file",
+    ),
+    MkosiConfigSetting(
+        dest="key",
+        section="Validation",
+        help="GPG key to use for signing",
+    ),
+
+    MkosiConfigSetting(
+        dest="incremental",
+        short="-i",
+        metavar="BOOL",
+        nargs="?",
+        section="Host",
+        parse=config_parse_boolean,
+        help="Make use of and generate intermediary cache images",
+    ),
+    MkosiConfigSetting(
+        dest="nspawn_settings",
+        name="NSpawnSettings",
+        long="--settings",
+        metavar="PATH",
+        section="Host",
+        parse=config_make_path_parser(),
+        paths=("mkosi.nspawn",),
+        help="Add in .nspawn settings file",
+    ),
+    MkosiConfigSetting(
+        dest="extra_search_paths",
+        long="--extra-search-path",
+        metavar="PATH",
+        section="Host",
+        parse=config_make_list_parser(delimiter=",", parse=make_path_parser()),
+        help="List of comma-separated paths to look for programs before looking in PATH",
+    ),
+    MkosiConfigSetting(
+        dest="qemu_gui",
+        metavar="BOOL",
+        nargs="?",
+        section="Host",
+        parse=config_parse_boolean,
+        help="Start QEMU in graphical mode",
+    ),
+    MkosiConfigSetting(
+        dest="qemu_smp",
+        metavar="SMP",
+        section="Host",
+        default="1",
+        help="Configure guest's SMP settings",
+    ),
+    MkosiConfigSetting(
+        dest="qemu_mem",
+        metavar="MEM",
+        section="Host",
+        default="2G",
+        help="Configure guest's RAM size",
+    ),
+    MkosiConfigSetting(
+        dest="qemu_kvm",
+        metavar="FEATURE",
+        nargs="?",
+        section="Host",
+        parse=config_parse_feature,
+        help="Configure whether to use KVM or not",
+    ),
+    MkosiConfigSetting(
+        dest="qemu_vsock",
+        metavar="FEATURE",
+        nargs="?",
+        section="Host",
+        parse=config_parse_feature,
+        help="Configure whether to use qemu with a vsock or not",
+    ),
+    MkosiConfigSetting(
+        dest="qemu_swtpm",
+        metavar="FEATURE",
+        nargs="?",
+        section="Host",
+        parse=config_parse_feature,
+        help="Configure whether to use qemu with swtpm or not",
+    ),
+    MkosiConfigSetting(
+        dest="qemu_cdrom",
+        metavar="BOOLEAN",
+        nargs="?",
+        section="Host",
+        parse=config_parse_boolean,
+        help="Attach the image as a CD-ROM to the virtual machine",
+    ),
+    MkosiConfigSetting(
+        dest="qemu_bios",
+        metavar="BOOLEAN",
+        nargs="?",
+        section="Host",
+        parse=config_parse_boolean,
+        help="Boot QEMU with SeaBIOS instead of EDK2",
+    ),
+    MkosiConfigSetting(
+        dest="qemu_args",
+        metavar="ARGS",
+        section="Host",
+        parse=config_make_list_parser(delimiter=" "),
+        # Suppress the command line option because it's already possible to pass qemu args as normal
+        # arguments.
+        help=argparse.SUPPRESS,
+    ),
+    MkosiConfigSetting(
+        dest="ephemeral",
+        metavar="BOOL",
+        section="Host",
+        parse=config_parse_boolean,
+        help=('If specified, the container/VM is run with a temporary snapshot of the output '
+                'image that is removed immediately when the container/VM terminates'),
+        nargs="?",
+    ),
+    MkosiConfigSetting(
+        dest="credentials",
+        long="--credential",
+        metavar="NAME=VALUE",
+        section="Host",
+        parse=config_make_list_parser(delimiter=" "),
+        help="Pass a systemd credential to systemd-nspawn or qemu",
+    ),
+    MkosiConfigSetting(
+        dest="kernel_command_line_extra",
+        metavar="OPTIONS",
+        section="Host",
+        parse=config_make_list_parser(delimiter=" "),
+        help="Append extra entries to the kernel command line when booting the image",
+    ),
+    MkosiConfigSetting(
+        dest="acl",
+        metavar="BOOL",
+        nargs="?",
+        section="Host",
+        parse=config_parse_boolean,
+        help="Set ACLs on generated directories to permit the user running mkosi to remove them",
+    ),
+    MkosiConfigSetting(
+        dest="tools_tree",
+        long="--tools-tree",
+        metavar="PATH",
+        section="Host",
+        parse=config_make_path_parser(required=False, absolute=False),
+        paths=("mkosi.tools",),
+        help="Look up programs to execute inside the given tree",
+    ),
+)
+
+MATCHES = (
+    MkosiMatch(
+        name="PathExists",
+        match=match_path_exists,
+    ),
+)
+
+
+def create_argument_parser() -> argparse.ArgumentParser:
+    action = config_make_action(SETTINGS)
+
+    parser = argparse.ArgumentParser(
+        prog="mkosi",
+        description="Build Bespoke OS Images",
+        usage="\n  " + textwrap.dedent("""\
+                mkosi [options...] {b}summary{e}
+                mkosi [options...] {b}build{e} [script parameters...]
+                mkosi [options...] {b}shell{e} [command line...]
+                mkosi [options...] {b}boot{e}  [nspawn settings...]
+                mkosi [options...] {b}qemu{e}  [qemu parameters...]
+                mkosi [options...] {b}ssh{e}   [command line...]
+                mkosi [options...] {b}clean{e}
+                mkosi [options...] {b}serve{e}
+                mkosi [options...] {b}bump{e}
+                mkosi [options...] {b}genkey{e}
+                mkosi [options...] {b}documentation{e}
+                mkosi [options...] {b}help{e}
+                mkosi -h | --help
+                mkosi --version
+        """).format(b=Style.bold, e=Style.reset),
+        add_help=False,
+        allow_abbrev=False,
+        argument_default=argparse.SUPPRESS,
+        formatter_class=CustomHelpFormatter,
     )
 
-    MATCHES = (
-        MkosiMatch(
-            name="PathExists",
-            match=match_path_exists,
-        ),
+    parser.add_argument(
+        "verb",
+        type=Verb,
+        choices=list(Verb),
+        default=Verb.build,
+        help=argparse.SUPPRESS,
+    )
+    parser.add_argument(
+        "cmdline",
+        nargs=argparse.REMAINDER,
+        help=argparse.SUPPRESS,
+    )
+    parser.add_argument(
+        "-h", "--help",
+        action=PagerHelpAction,
+        help=argparse.SUPPRESS,
+    )
+    parser.add_argument(
+        "--version",
+        action="version",
+        version="%(prog)s " + __version__,
+        help=argparse.SUPPRESS,
+    )
+    parser.add_argument(
+        "-f", "--force",
+        action="count",
+        dest="force",
+        default=0,
+        help="Remove existing image file before operation",
+    )
+    parser.add_argument(
+        "-C", "--directory",
+        help="Change to specified directory before doing anything",
+        metavar="PATH",
+        default=None,
+    )
+    parser.add_argument(
+        "--debug",
+        help="Turn on debugging output",
+        action="store_true",
+        default=False,
+    )
+    parser.add_argument(
+        "--debug-shell",
+        help="Spawn an interactive shell in the image if a chroot command fails",
+        action="store_true",
+        default=False,
+    )
+    parser.add_argument(
+        "--no-pager",
+        action="store_false",
+        dest="pager",
+        default=True,
+        help="Enable paging for long output",
+    )
+    parser.add_argument(
+        "--genkey-valid-days",
+        metavar="DAYS",
+        help="Number of days keys should be valid when generating keys",
+        action=action,
+        default="730",
+    )
+    parser.add_argument(
+        "--genkey-common-name",
+        metavar="CN",
+        help="Template for the CN when generating keys",
+        action=action,
+        default="mkosi of %u",
+    )
+    parser.add_argument(
+        "-B", "--auto-bump",
+        help="Automatically bump image version after building",
+        action="store_true",
+        default=False,
+    )
+    parser.add_argument(
+        "--preset",
+        action="append",
+        dest="presets",
+        metavar="PRESET",
+        default=[],
+        help="Build the specified presets and their dependencies",
+    )
+    parser.add_argument(
+        "--doc-format",
+        help="The format to show documentation in",
+        default=DocFormat.auto,
+        type=DocFormat,
     )
+    parser.add_argument(
+        "--nspawn-keep-unit",
+        action="store_true",
+        help=argparse.SUPPRESS,
+    )
+    parser.add_argument(
+        "--default",
+        help=argparse.SUPPRESS,
+    )
+    parser.add_argument(
+        "--cache",
+        metavar="PATH",
+        help=argparse.SUPPRESS,
+    )
+
+    last_section = None
+
+    for s in SETTINGS:
+        if s.section != last_section:
+            group = parser.add_argument_group(f"{s.section} configuration options")
+            last_section = s.section
+
+        long = s.long if s.long else f"--{s.dest.replace('_', '-')}"
+        opts = [s.short, long] if s.short else [long]
+
+        group.add_argument(    # type: ignore
+            *opts,
+            dest=s.dest,
+            choices=s.choices,
+            metavar=s.metavar,
+            nargs=s.nargs,     # type: ignore
+            const=s.const,
+            help=s.help,
+            action=action,
+        )
+
+
+    try:
+        import argcomplete
+
+        argcomplete.autocomplete(parser)
+    except ImportError:
+        pass
+
+    return parser
+
+
+def backward_compat_stubs(namespace: argparse.Namespace) -> None:
+    # These can be removed once mkosi v15 is available in LTS distros and compatibility with <= v14
+    # is no longer needed in build infrastructure (e.g.: OBS).
+    if getattr(namespace, "nspawn_keep_unit", None):
+        delattr(namespace, "nspawn_keep_unit")
+        logging.warning("--nspawn-keep-unit is no longer supported")
+
+    if getattr(namespace, "default", None):
+        delattr(namespace, "default")
+        logging.warning("--default is no longer supported")
 
-    def __init__(self) -> None:
-        self.settings_lookup_by_name = {s.name: s for s in self.SETTINGS}
-        self.settings_lookup_by_dest = {s.dest: s for s in self.SETTINGS}
-        self.match_lookup = {m.name: m for m in self.MATCHES}
+    if getattr(namespace, "cache", None):
+        delattr(namespace, "cache")
+        logging.warning("--cache is no longer supported")
 
-    def match_config(self, path: Path, namespace: argparse.Namespace, defaults: argparse.Namespace) -> bool:
+
+def resolve_deps(args: MkosiArgs, presets: Sequence[MkosiConfig]) -> list[MkosiConfig]:
+    graph = {p.preset: p.dependencies for p in presets}
+
+    if args.presets:
+        if any((missing := p) not in graph for p in args.presets):
+            die(f"No preset found with name {missing}")
+
+        deps = set()
+        queue = [*args.presets]
+
+        while queue:
+            if (preset := queue.pop(0)) not in deps:
+                deps.add(preset)
+                queue.extend(graph[preset])
+
+        presets = [p for p in presets if p.preset in deps]
+
+    graph = {p.preset: p.dependencies for p in presets}
+
+    try:
+        order = list(graphlib.TopologicalSorter(graph).static_order())
+    except graphlib.CycleError as e:
+        die(f"Preset dependency cycle detected: {' => '.join(e.args[1])}")
+
+    return sorted(presets, key=lambda p: order.index(p.preset))
+
+
+def parse_config(argv: Optional[Sequence[str]] = None) -> tuple[MkosiArgs, tuple[MkosiConfig, ...]]:
+    settings_lookup_by_name = {s.name: s for s in SETTINGS}
+    settings_lookup_by_dest = {s.dest: s for s in SETTINGS}
+    match_lookup = {m.name: m for m in MATCHES}
+
+    def finalize_default(
+        setting: MkosiConfigSetting,
+        namespace: argparse.Namespace,
+        defaults: argparse.Namespace
+    ) -> None:
+        if setting.dest in namespace:
+            return
+
+        for d in setting.default_factory_depends:
+            finalize_default(settings_lookup_by_dest[d], namespace, defaults)
+
+        if setting.dest in defaults:
+            default = getattr(defaults, setting.dest)
+        elif setting.default_factory:
+            default = setting.default_factory(namespace)
+        elif setting.default is None:
+            default = setting.parse(None, None)
+        else:
+            default = setting.default
+
+        setattr(namespace, setting.dest, default)
+
+    def match_config(path: Path, namespace: argparse.Namespace, defaults: argparse.Namespace) -> bool:
         triggered = None
 
         # If the config file does not exist, we assume it matches so that we look at the other files in the
@@ -1657,18 +1886,18 @@ class MkosiConfigParser:
             if not v:
                 die("Match value cannot be empty")
 
-            if (s := self.settings_lookup_by_name.get(k)):
+            if (s := settings_lookup_by_name.get(k)):
                 if not s.match:
                     die(f"{k} cannot be used in [Match]")
 
                 # If we encounter a setting in [Match] that has not been explicitly configured yet,
                 # we assign the default value first so that we can [Match] on default values for
                 # settings.
-                self.finalize_default(s, namespace, defaults)
+                finalize_default(s, namespace, defaults)
 
                 result = s.match(v, getattr(namespace, s.dest))
 
-            elif (m := self.match_lookup.get(k)):
+            elif (m := match_lookup.get(k)):
                 result = m.match(v)
             else:
                 die(f"{k} cannot be used in [Match]")
@@ -1682,14 +1911,13 @@ class MkosiConfigParser:
 
         return triggered is not False
 
-
-    def parse_config(self, path: Path, namespace: argparse.Namespace, defaults: argparse.Namespace) -> bool:
+    def parse_config( path: Path, namespace: argparse.Namespace, defaults: argparse.Namespace) -> bool:
         extras = path.is_dir()
 
         if path.is_dir():
             path = path / "mkosi.conf"
 
-        if not self.match_config(path, namespace, defaults):
+        if not match_config(path, namespace, defaults):
             return False
 
         if path.exists():
@@ -1698,13 +1926,13 @@ class MkosiConfigParser:
             for _, k, v in parse_ini(path, only_sections=["Distribution", "Output", "Content", "Validation", "Host"]):
                 ns = defaults if k.startswith("@") else namespace
 
-                if not (s := self.settings_lookup_by_name.get(k.removeprefix("@"))):
+                if not (s := settings_lookup_by_name.get(k.removeprefix("@"))):
                     die(f"Unknown setting {k}")
 
                 setattr(ns, s.dest, s.parse(v, getattr(ns, s.dest, None)))
 
         if extras:
-            for s in self.SETTINGS:
+            for s in SETTINGS:
                 ns = defaults if s.path_default else namespace
                 for f in s.paths:
                     p = parse_path(
@@ -1724,321 +1952,93 @@ class MkosiConfigParser:
                 for p in sorted((path.parent / "mkosi.conf.d").iterdir()):
                     if p.is_dir() or p.suffix == ".conf":
                         with chdir(p if p.is_dir() else Path.cwd()):
-                            self.parse_config(p if p.is_file() else Path("."), namespace, defaults)
+                            parse_config(p if p.is_file() else Path("."), namespace, defaults)
 
         return True
 
-    def create_argument_parser(self) -> argparse.ArgumentParser:
-        action = config_make_action(self.SETTINGS)
-
-        parser = argparse.ArgumentParser(
-            prog="mkosi",
-            description="Build Bespoke OS Images",
-            usage="\n  " + textwrap.dedent("""\
-                  mkosi [options...] {b}summary{e}
-                    mkosi [options...] {b}build{e} [script parameters...]
-                    mkosi [options...] {b}shell{e} [command line...]
-                    mkosi [options...] {b}boot{e}  [nspawn settings...]
-                    mkosi [options...] {b}qemu{e}  [qemu parameters...]
-                    mkosi [options...] {b}ssh{e}   [command line...]
-                    mkosi [options...] {b}clean{e}
-                    mkosi [options...] {b}serve{e}
-                    mkosi [options...] {b}bump{e}
-                    mkosi [options...] {b}genkey{e}
-                    mkosi [options...] {b}documentation{e}
-                    mkosi [options...] {b}help{e}
-                    mkosi -h | --help
-                    mkosi --version
-            """).format(b=Style.bold, e=Style.reset),
-            add_help=False,
-            allow_abbrev=False,
-            argument_default=argparse.SUPPRESS,
-            formatter_class=CustomHelpFormatter,
-        )
-
-        parser.add_argument(
-            "verb",
-            type=Verb,
-            choices=list(Verb),
-            default=Verb.build,
-            help=argparse.SUPPRESS,
-        )
-        parser.add_argument(
-            "cmdline",
-            nargs=argparse.REMAINDER,
-            help=argparse.SUPPRESS,
-        )
-        parser.add_argument(
-            "-h", "--help",
-            action=PagerHelpAction,
-            help=argparse.SUPPRESS,
-        )
-        parser.add_argument(
-            "--version",
-            action="version",
-            version="%(prog)s " + __version__,
-            help=argparse.SUPPRESS,
-        )
-        parser.add_argument(
-            "-f", "--force",
-            action="count",
-            dest="force",
-            default=0,
-            help="Remove existing image file before operation",
-        )
-        parser.add_argument(
-            "-C", "--directory",
-            help="Change to specified directory before doing anything",
-            metavar="PATH",
-            default=None,
-        )
-        parser.add_argument(
-            "--debug",
-            help="Turn on debugging output",
-            action="store_true",
-            default=False,
-        )
-        parser.add_argument(
-            "--debug-shell",
-            help="Spawn an interactive shell in the image if a chroot command fails",
-            action="store_true",
-            default=False,
-        )
-        parser.add_argument(
-            "--no-pager",
-            action="store_false",
-            dest="pager",
-            default=True,
-            help="Enable paging for long output",
-        )
-        parser.add_argument(
-            "--genkey-valid-days",
-            metavar="DAYS",
-            help="Number of days keys should be valid when generating keys",
-            action=action,
-            default="730",
-        )
-        parser.add_argument(
-            "--genkey-common-name",
-            metavar="CN",
-            help="Template for the CN when generating keys",
-            action=action,
-            default="mkosi of %u",
-        )
-        parser.add_argument(
-            "-B", "--auto-bump",
-            help="Automatically bump image version after building",
-            action="store_true",
-            default=False,
-        )
-        parser.add_argument(
-            "--preset",
-            action="append",
-            dest="presets",
-            metavar="PRESET",
-            default=[],
-            help="Build the specified presets and their dependencies",
-        )
-        parser.add_argument(
-            "--doc-format",
-            help="The format to show documentation in",
-            default=DocFormat.auto,
-            type=DocFormat,
-        )
-        parser.add_argument(
-            "--nspawn-keep-unit",
-            action="store_true",
-            help=argparse.SUPPRESS,
-        )
-        parser.add_argument(
-            "--default",
-            help=argparse.SUPPRESS,
-        )
-        parser.add_argument(
-            "--cache",
-            metavar="PATH",
-            help=argparse.SUPPRESS,
-        )
-
-        last_section = None
-
-        for s in self.SETTINGS:
-            if s.section != last_section:
-                group = parser.add_argument_group(f"{s.section} configuration options")
-                last_section = s.section
-
-            long = s.long if s.long else f"--{s.dest.replace('_', '-')}"
-            opts = [s.short, long] if s.short else [long]
-
-            group.add_argument(    # type: ignore
-                *opts,
-                dest=s.dest,
-                choices=s.choices,
-                metavar=s.metavar,
-                nargs=s.nargs,     # type: ignore
-                const=s.const,
-                help=s.help,
-                action=action,
-            )
-
-
-        try:
-            import argcomplete
-
-            argcomplete.autocomplete(parser)
-        except ImportError:
-            pass
-
-        return parser
-
-    def backward_compat_stubs(self, namespace: argparse.Namespace) -> None:
-        # These can be removed once mkosi v15 is available in LTS distros and compatibility with <= v14
-        # is no longer needed in build infrastructure (e.g.: OBS).
-        if getattr(namespace, "nspawn_keep_unit", None):
-            delattr(namespace, "nspawn_keep_unit")
-            logging.warning("--nspawn-keep-unit is no longer supported")
-
-        if getattr(namespace, "default", None):
-            delattr(namespace, "default")
-            logging.warning("--default is no longer supported")
-
-        if getattr(namespace, "cache", None):
-            delattr(namespace, "cache")
-            logging.warning("--cache is no longer supported")
-
-    def resolve_deps(self, args: MkosiArgs, presets: Sequence[MkosiConfig]) -> list[MkosiConfig]:
-        graph = {p.preset: p.dependencies for p in presets}
-
-        if args.presets:
-            if any((missing := p) not in graph for p in args.presets):
-                die(f"No preset found with name {missing}")
-
-            deps = set()
-            queue = [*args.presets]
+    def finalize_defaults(namespace: argparse.Namespace, defaults: argparse.Namespace) -> None:
+        for s in SETTINGS:
+            finalize_default(s, namespace, defaults)
 
-            while queue:
-                if (preset := queue.pop(0)) not in deps:
-                    deps.add(preset)
-                    queue.extend(graph[preset])
+    presets = []
+    namespace = argparse.Namespace()
+    defaults = argparse.Namespace()
 
-            presets = [p for p in presets if p.preset in deps]
-
-        graph = {p.preset: p.dependencies for p in presets}
+    if argv is None:
+        argv = sys.argv[1:]
+    argv = list(argv)
 
+    # Make sure the verb command gets explicitly passed. Insert a -- before the positional verb argument
+    # otherwise it might be considered as an argument of a parameter with nargs='?'. For example mkosi -i
+    # summary would be treated as -i=summary.
+    for verb in Verb:
         try:
-            order = list(graphlib.TopologicalSorter(graph).static_order())
-        except graphlib.CycleError as e:
-            die(f"Preset dependency cycle detected: {' => '.join(e.args[1])}")
-
-        return sorted(presets, key=lambda p: order.index(p.preset))
-
-    def finalize_default(
-        self,
-        setting: MkosiConfigSetting,
-        namespace: argparse.Namespace,
-        defaults: argparse.Namespace
-    ) -> None:
-        if setting.dest in namespace:
-            return
-
-        for d in setting.default_factory_depends:
-            self.finalize_default(self.settings_lookup_by_dest[d], namespace, defaults)
-
-        if setting.dest in defaults:
-            default = getattr(defaults, setting.dest)
-        elif setting.default_factory:
-            default = setting.default_factory(namespace)
-        elif setting.default is None:
-            default = setting.parse(None, None)
-        else:
-            default = setting.default
-
-        setattr(namespace, setting.dest, default)
-
-    def finalize_defaults(self, namespace: argparse.Namespace, defaults: argparse.Namespace) -> None:
-        for s in self.SETTINGS:
-            self.finalize_default(s, namespace, defaults)
+            v_i = argv.index(verb.name)
+        except ValueError:
+            continue
 
-    def parse(self, argv: Optional[Sequence[str]] = None) -> tuple[MkosiArgs, tuple[MkosiConfig, ...]]:
-        presets = []
-        namespace = argparse.Namespace()
-        defaults = argparse.Namespace()
+        if v_i > 0 and argv[v_i - 1] != "--":
+            argv.insert(v_i, "--")
+        break
+    else:
+        argv += ["--", "build"]
 
-        if argv is None:
-            argv = sys.argv[1:]
-        argv = list(argv)
+    argparser = create_argument_parser()
+    argparser.parse_args(argv, namespace)
 
-        # Make sure the verb command gets explicitly passed. Insert a -- before the positional verb argument
-        # otherwise it might be considered as an argument of a parameter with nargs='?'. For example mkosi -i
-        # summary would be treated as -i=summary.
-        for verb in Verb:
-            try:
-                v_i = argv.index(verb.name)
-            except ValueError:
-                continue
+    args = load_args(namespace)
 
-            if v_i > 0 and argv[v_i - 1] != "--":
-                argv.insert(v_i, "--")
-            break
-        else:
-            argv += ["--", "build"]
+    if ARG_DEBUG.get():
+        logging.getLogger().setLevel(logging.DEBUG)
 
-        argparser = self.create_argument_parser()
-        argparser.parse_args(argv, namespace)
+    if args.verb == Verb.help:
+        PagerHelpAction.__call__(None, argparser, namespace)  # type: ignore
 
-        args = load_args(namespace)
+    if args.directory and not Path(args.directory).is_dir():
+        die(f"{args.directory} is not a directory!")
 
-        if ARG_DEBUG.get():
-            logging.getLogger().setLevel(logging.DEBUG)
+    if args.directory:
+        os.chdir(args.directory)
 
-        if args.verb == Verb.help:
-            PagerHelpAction.__call__(None, argparser, namespace)  # type: ignore
+    if args.directory != "":
+        parse_config(Path("."), namespace, defaults)
 
-        if args.directory and not Path(args.directory).is_dir():
-            die(f"{args.directory} is not a directory!")
+        if Path("mkosi.presets").exists():
+            for p in Path("mkosi.presets").iterdir():
+                if not p.is_dir() and not p.suffix == ".conf":
+                    continue
 
-        if args.directory:
-            os.chdir(args.directory)
+                name = p.name.removesuffix(".conf")
+                if not name:
+                    die(f"{p} is not a valid preset name")
 
-        if args.directory != "":
-            self.parse_config(Path("."), namespace, defaults)
+                ns_copy = copy.deepcopy(namespace)
+                defaults_copy = copy.deepcopy(defaults)
 
-            if Path("mkosi.presets").exists():
-                for p in Path("mkosi.presets").iterdir():
-                    if not p.is_dir() and not p.suffix == ".conf":
+                with chdir(p if p.is_dir() else Path.cwd()):
+                    if not parse_config(p if p.is_file() else Path("."), ns_copy, defaults_copy):
                         continue
 
-                    name = p.name.removesuffix(".conf")
-                    if not name:
-                        die(f"{p} is not a valid preset name")
-
-                    ns_copy = copy.deepcopy(namespace)
-                    defaults_copy = copy.deepcopy(defaults)
-
-                    with chdir(p if p.is_dir() else Path.cwd()):
-                        if not self.parse_config(p if p.is_file() else Path("."), ns_copy, defaults_copy):
-                            continue
-
-                    setattr(ns_copy, "preset", name)
-                    self.finalize_defaults(ns_copy, defaults_copy)
-                    presets += [ns_copy]
+                setattr(ns_copy, "preset", name)
+                finalize_defaults(ns_copy, defaults_copy)
+                presets += [ns_copy]
 
-        if not presets:
-            setattr(namespace, "preset", None)
-            self.finalize_defaults(namespace, defaults)
-            presets = [namespace]
+    if not presets:
+        setattr(namespace, "preset", None)
+        finalize_defaults(namespace, defaults)
+        presets = [namespace]
 
-        if not presets:
-            die("No presets defined in mkosi.presets/")
+    if not presets:
+        die("No presets defined in mkosi.presets/")
 
-        # Manipulate some old settings to make them work with the new settings, for those typically used in
-        # infrastructure scripts rather than image-specific configuration.
-        self.backward_compat_stubs(namespace)
+    # Manipulate some old settings to make them work with the new settings, for those typically used in
+    # infrastructure scripts rather than image-specific configuration.
+    backward_compat_stubs(namespace)
 
-        presets = [load_config(ns) for ns in presets]
-        presets = self.resolve_deps(args, presets)
+    presets = [load_config(ns) for ns in presets]
+    presets = resolve_deps(args, presets)
 
-        return args, tuple(presets)
+    return args, tuple(presets)
 
 
 def load_credentials(args: argparse.Namespace) -> dict[str, str]:
index 93c63a6057e539706f95f5ec3396c6894238f38e..2463d9744d15b36268e9e15f1c9ad81222861416 100644 (file)
@@ -6,50 +6,46 @@ import operator
 import tempfile
 from pathlib import Path
 import textwrap
-from typing import List, Optional
+from typing import Optional
 
 import pytest
 
-from mkosi.config import Compression, MkosiArgs, MkosiConfig, MkosiConfigParser, Verb
+from mkosi.config import Compression, Verb, parse_config
 from mkosi.distributions import Distribution
 from mkosi.util import chdir
 
 
-def parse(argv: Optional[List[str]] = None) -> tuple[MkosiArgs, tuple[MkosiConfig, ...]]:
-    return MkosiConfigParser().parse(argv)
-
-
 def test_parse_load_verb() -> None:
     with tempfile.TemporaryDirectory() as d, chdir(d):
-        assert parse(["build"])[0].verb == Verb.build
-        assert parse(["clean"])[0].verb == Verb.clean
+        assert parse_config(["build"])[0].verb == Verb.build
+        assert parse_config(["clean"])[0].verb == Verb.clean
         with pytest.raises(SystemExit):
-            parse(["help"])
-        assert parse(["genkey"])[0].verb == Verb.genkey
-        assert parse(["bump"])[0].verb == Verb.bump
-        assert parse(["serve"])[0].verb == Verb.serve
-        assert parse(["build"])[0].verb == Verb.build
-        assert parse(["shell"])[0].verb == Verb.shell
-        assert parse(["boot"])[0].verb == Verb.boot
-        assert parse(["qemu"])[0].verb == Verb.qemu
+            parse_config(["help"])
+        assert parse_config(["genkey"])[0].verb == Verb.genkey
+        assert parse_config(["bump"])[0].verb == Verb.bump
+        assert parse_config(["serve"])[0].verb == Verb.serve
+        assert parse_config(["build"])[0].verb == Verb.build
+        assert parse_config(["shell"])[0].verb == Verb.shell
+        assert parse_config(["boot"])[0].verb == Verb.boot
+        assert parse_config(["qemu"])[0].verb == Verb.qemu
         with pytest.raises(SystemExit):
-            parse(["invalid"])
+            parse_config(["invalid"])
 
 
 def test_os_distribution() -> None:
     with tempfile.TemporaryDirectory() as d, chdir(d):
         for dist in Distribution:
-            assert parse(["-d", dist.name])[1][0].distribution == dist
+            assert parse_config(["-d", dist.name])[1][0].distribution == dist
 
         with pytest.raises(tuple((argparse.ArgumentError, SystemExit))):
-            parse(["-d", "invalidDistro"])
+            parse_config(["-d", "invalidDistro"])
         with pytest.raises(tuple((argparse.ArgumentError, SystemExit))):
-            parse(["-d"])
+            parse_config(["-d"])
 
         for dist in Distribution:
             config = Path("mkosi.conf")
             config.write_text(f"[Distribution]\nDistribution={dist}")
-            assert parse([])[1][0].distribution == dist
+            assert parse_config([])[1][0].distribution == dist
 
 
 def test_parse_config_files_filter() -> None:
@@ -60,12 +56,12 @@ def test_parse_config_files_filter() -> None:
         (confd / "10-file.conf").write_text("[Content]\nPackages=yes")
         (confd / "20-file.noconf").write_text("[Content]\nPackages=nope")
 
-        assert parse([])[1][0].packages == ["yes"]
+        assert parse_config([])[1][0].packages == ["yes"]
 
 
 def test_compression() -> None:
     with tempfile.TemporaryDirectory() as d, chdir(d):
-        assert parse(["--format", "disk", "--compress-output", "False"])[1][0].compress_output == Compression.none
+        assert parse_config(["--format", "disk", "--compress-output", "False"])[1][0].compress_output == Compression.none
 
 
 @pytest.mark.parametrize("dist1,dist2", itertools.combinations_with_replacement(Distribution, 2))
@@ -121,7 +117,7 @@ def test_match_distribution(dist1: Distribution, dist2: Distribution) -> None:
             )
         )
 
-        conf = parse([])[1][0]
+        conf = parse_config([])[1][0]
         assert "testpkg1" in conf.packages
         if dist1 == dist2:
             assert "testpkg2" in conf.packages
@@ -186,7 +182,7 @@ def test_match_release(release1: int, release2: int) -> None:
             )
         )
 
-        conf = parse([])[1][0]
+        conf = parse_config([])[1][0]
         assert "testpkg1" in conf.packages
         if release1 == release2:
             assert "testpkg2" in conf.packages
@@ -265,7 +261,7 @@ def test_match_imageid(image1: str, image2: str) -> None:
             )
         )
 
-        conf = parse([])[1][0]
+        conf = parse_config([])[1][0]
         assert "testpkg1" in conf.packages
         if image1 == image2:
             assert "testpkg2" in conf.packages
@@ -343,7 +339,7 @@ def test_match_imageversion(op: str, version: str) -> None:
             )
         )
 
-        conf = parse([])[1][0]
+        conf = parse_config([])[1][0]
         assert ("testpkg1" in conf.packages) == opfunc(123, version)
         assert ("testpkg2" in conf.packages) == opfunc(123, version)
         assert "testpkg3" not in conf.packages
@@ -365,7 +361,7 @@ def test_package_manager_tree(skel: Optional[Path], pkgmngr: Optional[Path]) ->
             if pkgmngr is not None:
                 f.write(f"PackageManagerTrees={pkgmngr}\n")
 
-        conf = parse([])[1][0]
+        conf = parse_config([])[1][0]
 
         skel_expected = [(skel, None)] if skel is not None else []
         pkgmngr_expected = [(pkgmngr, None)] if pkgmngr is not None else skel_expected