From: alexhulbert Date: Tue, 17 Feb 2026 19:48:37 +0000 (-0500) Subject: Support glob expansion for path list config options X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=df51194bc2d890d4c267af644a1832d2d53339ac;p=thirdparty%2Fmkosi.git Support glob expansion for path list config options --- diff --git a/mkosi/config.py b/mkosi/config.py index ed0f9d25c..0a846db3e 100644 --- a/mkosi/config.py +++ b/mkosi/config.py @@ -8,6 +8,7 @@ import enum import fnmatch import functools import getpass +import glob import graphlib import io import itertools @@ -1204,6 +1205,7 @@ def config_make_list_parser( unescape: bool = False, reset: bool = True, key: Optional[Callable[[T], Any]] = None, + expand_globs: bool = False, ) -> ConfigParseCallback[list[T]]: def config_parse_list(value: Optional[str], old: Optional[list[T]]) -> Optional[list[T]]: new = old.copy() if old else [] @@ -1228,6 +1230,9 @@ def config_make_list_parser( if reset and len(values) == 1 and values[0] == "": return None + if expand_globs: + values = flatten(sorted(glob.glob(v)) for v in values) + new += [parse(v) for v in values if v] if key: @@ -2694,7 +2699,7 @@ SETTINGS: list[ConfigSetting[Any]] = [ long="--configure-script", metavar="PATH", section="Config", - parse=config_make_list_parser(delimiter=",", parse=make_path_parser()), + parse=config_make_list_parser(delimiter=",", parse=make_path_parser(), expand_globs=True), path_suffixes=("configure",), help="Configure script to run before doing anything", ), @@ -2968,7 +2973,7 @@ SETTINGS: list[ConfigSetting[Any]] = [ long="--clean-script", metavar="PATH", section="Output", - parse=config_make_list_parser(delimiter=",", parse=make_path_parser()), + parse=config_make_list_parser(delimiter=",", parse=make_path_parser(), expand_globs=True), path_suffixes=("clean",), recursive_path_suffixes=("clean.d/*",), help="Clean script to run after cleanup", @@ -3098,7 +3103,7 @@ SETTINGS: list[ConfigSetting[Any]] = [ long="--sync-script", metavar="PATH", section="Content", - parse=config_make_list_parser(delimiter=",", parse=make_path_parser()), + parse=config_make_list_parser(delimiter=",", parse=make_path_parser(), expand_globs=True), path_suffixes=("sync",), recursive_path_suffixes=("sync.d/*",), help="Sync script to run before starting the build", @@ -3109,7 +3114,7 @@ SETTINGS: list[ConfigSetting[Any]] = [ long="--prepare-script", metavar="PATH", section="Content", - parse=config_make_list_parser(delimiter=",", parse=make_path_parser()), + parse=config_make_list_parser(delimiter=",", parse=make_path_parser(), expand_globs=True), path_suffixes=("prepare", "prepare.chroot"), recursive_path_suffixes=("prepare.d/*",), help="Prepare script to run inside the image before it is cached", @@ -3121,7 +3126,7 @@ SETTINGS: list[ConfigSetting[Any]] = [ long="--build-script", metavar="PATH", section="Content", - parse=config_make_list_parser(delimiter=",", parse=make_path_parser()), + parse=config_make_list_parser(delimiter=",", parse=make_path_parser(), expand_globs=True), path_suffixes=("build", "build.chroot"), recursive_path_suffixes=("build.d/*",), help="Build script to run inside image", @@ -3133,7 +3138,7 @@ SETTINGS: list[ConfigSetting[Any]] = [ metavar="PATH", name="PostInstallationScripts", section="Content", - parse=config_make_list_parser(delimiter=",", parse=make_path_parser()), + parse=config_make_list_parser(delimiter=",", parse=make_path_parser(), expand_globs=True), path_suffixes=("postinst", "postinst.chroot"), recursive_path_suffixes=("postinst.d/*",), help="Postinstall script to run inside image", @@ -3144,7 +3149,7 @@ SETTINGS: list[ConfigSetting[Any]] = [ long="--finalize-script", metavar="PATH", section="Content", - parse=config_make_list_parser(delimiter=",", parse=make_path_parser()), + parse=config_make_list_parser(delimiter=",", parse=make_path_parser(), expand_globs=True), path_suffixes=("finalize", "finalize.chroot"), recursive_path_suffixes=("finalize.d/*",), help="Postinstall script to run outside image", @@ -3156,7 +3161,7 @@ SETTINGS: list[ConfigSetting[Any]] = [ metavar="PATH", name="PostOutputScripts", section="Content", - parse=config_make_list_parser(delimiter=",", parse=make_path_parser()), + parse=config_make_list_parser(delimiter=",", parse=make_path_parser(), expand_globs=True), path_suffixes=("postoutput",), recursive_path_suffixes=("postoutput.d/*",), help="Output postprocessing script to run outside image", diff --git a/mkosi/resources/man/mkosi.1.md b/mkosi/resources/man/mkosi.1.md index bdaa7c3ed..e3f95170e 100644 --- a/mkosi/resources/man/mkosi.1.md +++ b/mkosi/resources/man/mkosi.1.md @@ -786,8 +786,9 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`, `CleanScripts=`, `--clean-script=` : Takes a comma-separated list of paths to executables that are used as - the clean scripts for this image. See the **SCRIPTS** section for - more information. + the clean scripts for this image. Glob patterns are supported and + matching paths are sorted alphabetically. See the **SCRIPTS** section + for more information. ### [Content] Section @@ -965,33 +966,39 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`, `SyncScripts=`, `--sync-script=` : Takes a comma-separated list of paths to executables that are used as - the sync scripts for this image. See the **SCRIPTS** section for - more information. + the sync scripts for this image. Glob patterns are supported and + matching paths are sorted alphabetically. See the **SCRIPTS** section + for more information. `PrepareScripts=`, `--prepare-script=` : Takes a comma-separated list of paths to executables that are used as - the prepare scripts for this image. See the **SCRIPTS** section for - more information. + the prepare scripts for this image. Glob patterns are supported and + matching paths are sorted alphabetically. See the **SCRIPTS** section + for more information. `BuildScripts=`, `--build-script=` : Takes a comma-separated list of paths to executables that are used as - the build scripts for this image. See the **SCRIPTS** section for more - information. + the build scripts for this image. Glob patterns are supported and + matching paths are sorted alphabetically. See the **SCRIPTS** section + for more information. `PostInstallationScripts=`, `--postinst-script=` : Takes a comma-separated list of paths to executables that are used as - the post-installation scripts for this image. See the **SCRIPTS** section + the post-installation scripts for this image. Glob patterns are supported + and matching paths are sorted alphabetically. See the **SCRIPTS** section for more information. `FinalizeScripts=`, `--finalize-script=` : Takes a comma-separated list of paths to executables that are used as - the finalize scripts for this image. See the **SCRIPTS** section for more - information. + the finalize scripts for this image. Glob patterns are supported and + matching paths are sorted alphabetically. See the **SCRIPTS** section + for more information. `PostOutputScripts=`, `--postoutput-script=` : Takes a comma-separated list of paths to executables that are used as - the post output scripts for this image. See the **SCRIPTS** section for more - information. + the post output scripts for this image. Glob patterns are supported and + matching paths are sorted alphabetically. See the **SCRIPTS** section + for more information. `Bootable=`, `--bootable=` : Takes a boolean or `auto`. Enables or disables generation of a @@ -2278,8 +2285,9 @@ config file is read: `ConfigureScripts=`, `--configure-script=` : Takes a comma-separated list of paths to executables that are used as - the configure scripts for this image. See the **SCRIPTS** section for - more information. + the configure scripts for this image. Glob patterns are supported and + matching paths are sorted alphabetically. See the **SCRIPTS** section + for more information. `PassEnvironment=`, `--pass-environment=` : Takes a list of environment variable names separated by spaces. When diff --git a/tests/test_config.py b/tests/test_config.py index cee25cfa9..e3b3a3cac 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1113,6 +1113,72 @@ def test_paths_with_default_factory(tmp_path: Path) -> None: ] +def test_glob_expansion(tmp_path: Path) -> None: + d = tmp_path + + (d / "script_a.sh").touch() + (d / "script_b.sh").touch() + (d / "script_c.sh").touch() + (d / "other.py").touch() + + # Glob patterns should be expanded and results should be sorted. + (d / "mkosi.conf").write_text( + f"""\ + [Content] + PrepareScripts={d}/script_*.sh + """ + ) + + with chdir(d): + _, _, [config] = parse_config() + + assert config.prepare_scripts == [d / "script_a.sh", d / "script_b.sh", d / "script_c.sh"] + + # Glob patterns that match nothing should result in an empty list. + (d / "mkosi.conf").write_text( + f"""\ + [Content] + PrepareScripts={d}/nonexistent_*.sh + """ + ) + + with chdir(d): + _, _, [config] = parse_config() + + assert config.prepare_scripts == [] + + # Non-glob paths should be ordered before glob results when listed first. + (d / "mkosi.conf").write_text( + f"""\ + [Content] + PrepareScripts={d}/other.py,{d}/script_*.sh + """ + ) + + with chdir(d): + _, _, [config] = parse_config() + + assert config.prepare_scripts == [ + d / "other.py", + d / "script_a.sh", + d / "script_b.sh", + d / "script_c.sh", + ] + + # Glob expansion should work with other script options too. + (d / "mkosi.conf").write_text( + f"""\ + [Content] + BuildScripts={d}/script_*.sh + """ + ) + + with chdir(d): + _, _, [config] = parse_config() + + assert config.build_scripts == [d / "script_a.sh", d / "script_b.sh", d / "script_c.sh"] + + @pytest.mark.parametrize( "sections,args,warning_count", [