]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Add new 'cat-config' verb
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Sat, 31 Aug 2024 07:16:41 +0000 (10:16 +0300)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Sat, 31 Aug 2024 13:22:17 +0000 (16:22 +0300)
This is similar to 'systemd-analyze cat-config'. With many config
files, it can be a bit difficult to figure out where some setting is
set.

mkosi/__init__.py
mkosi/config.py
mkosi/log.py
tests/test_json.py

index eaf59b0f67994678d1943dde60f74f96be3ad202..0d850bd4327928323d8b7ba65b49a73425225944 100644 (file)
@@ -48,6 +48,7 @@ from mkosi.config import (
     ShimBootloader,
     Verb,
     Vmm,
+    cat_config,
     format_bytes,
     parse_boolean,
     parse_config,
@@ -4750,6 +4751,11 @@ def run_verb(args: Args, images: Sequence[Config], *, resources: Path) -> None:
         page(text, args.pager)
         return
 
+    if args.verb == Verb.cat_config:
+        text = cat_config(images)
+        page(text, args.pager)
+        return
+
     last = images[-1]
 
     if (minversion := last.minimum_version) and minversion > __version__:
index 461eaedf77ba94c960e976e82a8ef3996810fa70..e5b99ee1945cbf5e1878bece2a7787c9ad06d114 100644 (file)
@@ -10,6 +10,7 @@ import fnmatch
 import functools
 import graphlib
 import inspect
+import io
 import json
 import logging
 import math
@@ -60,6 +61,7 @@ class Verb(StrEnum):
     build         = enum.auto()
     clean         = enum.auto()
     summary       = enum.auto()
+    cat_config    = enum.auto()
     shell         = enum.auto()
     boot          = enum.auto()
     qemu          = enum.auto()
@@ -1411,6 +1413,7 @@ class Config:
     """
 
     profile: Optional[str]
+    files: list[Path]
     include: list[Path]
     initrd_include: list[Path]
     dependencies: list[str]
@@ -3215,6 +3218,7 @@ def create_argument_parser(chdir: bool = True) -> argparse.ArgumentParser:
         # the synopsis below is supposed to be indented by two spaces
         usage="\n  " + textwrap.dedent("""\
               mkosi [options...] {b}summary{e}
+                mkosi [options...] {b}cat-config{e}
                 mkosi [options...] {b}build{e}       [command line...]
                 mkosi [options...] {b}shell{e}       [command line...]
                 mkosi [options...] {b}boot{e}        [nspawn settings...]
@@ -3433,7 +3437,9 @@ class ParseContext:
         # in configuration files. This is required to implement both [Match] support and the behavior where settings
         # specified on the CLI always override settings specified in configuration files.
         self.cli = argparse.Namespace()
-        self.config = argparse.Namespace()
+        self.config = argparse.Namespace(
+            files = [],
+        )
         self.defaults = argparse.Namespace()
         # Compare inodes instead of paths so we can't get tricked by bind mounts and such.
         self.includes: set[tuple[int, int]] = set()
@@ -3701,7 +3707,10 @@ class ParseContext:
                         )
 
         if path.exists():
-            logging.debug(f"Including configuration file {Path.cwd() / path}")
+            abs_path = Path.cwd() / path
+            logging.debug(f"Loading configuration file {abs_path}")
+            files = getattr(self.config, 'files')
+            files += [abs_path]
 
             for section, k, v in parse_ini(path, only_sections={s.section for s in SETTINGS}):
                 if not k and not v:
@@ -3807,6 +3816,7 @@ def parse_config(argv: Sequence[str] = (), *, resources: Path = Path("/")) -> tu
     # One of the specifiers needs access to the directory so let's make sure it
     # is available.
     setattr(context.config, "directory", args.directory)
+    setattr(context.config, "files", [])
 
     # Parse the global configuration unless the user explicitly asked us not to.
     if args.directory is not None:
@@ -3867,6 +3877,7 @@ def parse_config(argv: Sequence[str] = (), *, resources: Path = Path("/")) -> tu
             context.config = argparse.Namespace()
             setattr(context.config, "image", name)
             setattr(context.config, "directory", args.directory)
+            setattr(context.config, "files", [])
 
             # Settings that are marked as "inherit" are passed down to subimages but can
             # be overridden, so we copy these to the config namespace so that they'll be
@@ -4111,10 +4122,29 @@ def format_bytes_or_none(num_bytes: Optional[int]) -> str:
     return format_bytes(num_bytes) if num_bytes is not None else "none"
 
 
-def summary(config: Config) -> str:
-    def bold(s: Any) -> str:
-        return f"{Style.bold}{s}{Style.reset}"
+def bold(s: Any) -> str:
+    return f"{Style.bold}{s}{Style.reset}"
+
+
+def cat_config(images: Sequence[Config]) -> str:
+    c = io.StringIO()
+    for n, config in enumerate(images):
+        if n > 0:
+            print(file=c)
+
+        print(bold(f"### IMAGE: {config.image or 'default'}"), file=c)
 
+        for path in config.files:
+            # Display the paths as relative to ., if underneath.
+            if path.is_relative_to(Path.cwd()):
+                path = path.relative_to(Path.cwd())
+            print(f'{Style.blue}# {path}{Style.reset}', file=c)
+            print(path.read_text(), file=c)
+
+    return c.getvalue()
+
+
+def summary(config: Config) -> str:
     maniformats = (" ".join(i.name for i in config.manifest_format)) or "(none)"
     env = [f"{k}={v}" for k, v in config.environment.items()]
 
index e3612707c62ad1124ff398f1174e39f69199238d..e264c4decb1e2056d82f69099ebb6197dbe4415a 100644 (file)
@@ -16,6 +16,7 @@ LEVEL = 0
 
 class Style:
     bold = "\033[0;1;39m" if sys.stderr.isatty() else ""
+    blue = "\033[0;1;34m" if sys.stderr.isatty() else ""
     gray = "\033[0;38;5;245m" if sys.stderr.isatty() else ""
     red = "\033[31;1m" if sys.stderr.isatty() else ""
     yellow = "\033[33;1m" if sys.stderr.isatty() else ""
index ae2f121fcfef5d9757024a737a3f48d0f1e1b9e2..7adf73f1497ad2e50513997b7fae25ece19cb404 100644 (file)
@@ -137,6 +137,7 @@ def test_config() -> None:
             "Ephemeral": true,
             "ExtraSearchPaths": [],
             "ExtraTrees": [],
+            "Files": [],
             "FinalizeScripts": [],
             "Format": "uki",
             "ForwardJournal": "/mkosi.journal",
@@ -390,6 +391,7 @@ def test_config() -> None:
         ephemeral=True,
         extra_search_paths=[],
         extra_trees=[],
+        files=[],
         finalize_scripts=[],
         forward_journal=Path("/mkosi.journal"),
         hostname=None,