Repository: systemd/mkosi

Issue:
[Match] sections ignored in mkosi.conf.d directories nested in mkosi.images
The `[Match]` section of a `mkosi.conf` file in a directory like `mkosi.images/system` or `mkosi.images/system/mkosi.conf.d/30-centos-fedora` shall apply to the entire directory, for example `mkosi.images/system/mkosi.conf.d` or `mkosi.images/system/mkosi.conf.d/30-centos-fedora/mkosi.conf.d`. However, inheritance of the `[Match]` section to nested directories breaks in the second case `mkosi.images/system/mkosi.conf.d/30-centos-fedora`.

To better illustrate the problem I have uploaded a reproducer at [JM1/mkosi-issue-match-inheritance](https://github.com/JM1/mkosi-issue-match-inheritance):

It produces two images `base` and `system` of type `directory` for distributions `centos`, `debian`, `fedora` and `ubuntu`. The `system` image will install package `shim` on `centos` and `fedora` which is not available on `debian` (but on `ubuntu` it is). It will install package
`shim-signed` on `debian` and `ubuntu` which is not available on `centos` and `fedora`.

In practice though, `mkosi` will try to install `shim` when building the `system` image for `debian`. The `[Match]` section in [mkosi.images/system/mkosi.conf.d/30-centos-fedora/mkosi.conf] is not applied to [mkosi.images/system/mkosi.conf.d/30-centos-fedora/mkosi.conf.d/example.conf] properly. The `system` image build fails 😱

Is this behavior expected? 🤔 Can mkosi be changed to include nested dirs in `mkosi.images` when evaluating `[Match]` sections? 🥺

[mkosi.images/system/mkosi.conf.d/30-centos-fedora/mkosi.conf]: https://github.com/JM1/mkosi-issue-match-inheritance/blob/main/mkosi.images/system/mkosi.conf.d/30-centos-fedora/mkosi.conf
[mkosi.images/system/mkosi.conf.d/30-centos-fedora/mkosi.conf.d/example.conf]: https://github.com/JM1/mkosi-issue-match-inheritance/blob/main/mkosi.images/system/mkosi.conf.d/30-centos-fedora/mkosi.conf.d/example.conf



Discussion:

Resolving patch:
diff --git a/mkosi/config.py b/mkosi/config.py
--- a/mkosi/config.py
+++ b/mkosi/config.py
@@ -1674,6 +1674,9 @@ def parse_ini(path: Path, only_sections: Collection[str] = ()) -> Iterator[tuple
     if section and setting and value is not None:
         yield section, setting, value
 
+    if section:
+        yield section, "", ""
+
 
 SETTINGS = (
     ConfigSetting(


Test patch:
diff --git a/tests/test_config.py b/tests/test_config.py
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -307,8 +307,6 @@ def test_parse_load_verb(tmp_path: Path) -> None:
     with chdir(tmp_path):
         assert parse_config(["build"])[0].verb == Verb.build
         assert parse_config(["clean"])[0].verb == Verb.clean
-        with pytest.raises(SystemExit):
-            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
@@ -357,6 +355,28 @@ def test_compression(tmp_path: Path) -> None:
         assert config.compress_output == Compression.none
 
 
+def test_match_only(tmp_path: Path) -> None:
+    with chdir(tmp_path):
+        Path("mkosi.conf").write_text(
+            """\
+            [Match]
+            Format=|directory
+            Format=|disk
+            """
+        )
+
+        Path("mkosi.conf.d").mkdir()
+        Path("mkosi.conf.d/10-abc.conf").write_text(
+            """\
+            [Output]
+            ImageId=abcde
+            """
+        )
+
+        _, [config] = parse_config(["--format", "tar"])
+        assert config.image_id != "abcde"
+
+
 def test_match_multiple(tmp_path: Path) -> None:
     with chdir(tmp_path):
         Path("mkosi.conf").write_text(