]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Support glob expansion for path list config options
authoralexhulbert <alex@alexhulbert.com>
Tue, 17 Feb 2026 19:48:37 +0000 (14:48 -0500)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Tue, 17 Feb 2026 20:26:02 +0000 (21:26 +0100)
mkosi/config.py
mkosi/resources/man/mkosi.1.md
tests/test_config.py

index ed0f9d25cd5f203eb8f7e7806e652ed66353e4bc..0a846db3e34b5139bd025c2e7c825399726d93e2 100644 (file)
@@ -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",
index bdaa7c3edd3f1b76283bb39d524c9da60cb100d8..e3f95170e86e40fd0de34dea3ab59b0886d61fc6 100644 (file)
@@ -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
index cee25cfa9632b7bc4638fd3ce7ce7472f4728ccc..e3b3a3cac27d244032dcbf2c2e2e9b31bb71afd2 100644 (file)
@@ -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",
     [