]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Apply path expansion when parsing 1472/head
authorGeorges Discry <georges@discry.be>
Thu, 20 Apr 2023 20:09:58 +0000 (22:09 +0200)
committerGeorges Discry <georges@discry.be>
Fri, 21 Apr 2023 12:31:36 +0000 (14:31 +0200)
When the `extra_search_paths` configuration was introduced, it would
perform path expansions with the environment variables (using $) and the
home directories (starting with ~). Home directories would not be
expanded when mkosi was run with sudo or pkexec. Instead, `$SUDO_HOME`
could be used but only when run with sudo or pkexec.

However, those expansions were not applied to other paths in the
configuration and were broken with the rewrite of the configuration
loading, as the parser would check if the paths exist before expanding.

Those expansions are now handled directly in the parser and are
performed before validation. Environment variables expansion is enabled
for all paths and home directory expansion is enabled for all paths on
the host.

Furthermore, home directory expansion is always available, with `~`
expanding to the user's home directory when sudo or pkexec are used (and
not `/root`), replacing the need for `$SUDO_HOME`.

mkosi/__init__.py
mkosi/config.py

index 36f3635bb3f6179a93dd378b96bc6c2bc80bf168..e9bdf0998ca26b52860d2e2f7c447ab72a2b6d6a 100644 (file)
@@ -15,7 +15,6 @@ import os
 import platform
 import resource
 import shutil
-import string
 import subprocess
 import sys
 import tempfile
@@ -1105,8 +1104,6 @@ def load_args(args: argparse.Namespace) -> MkosiConfig:
 
     find_image_version(args)
 
-    args.extra_search_paths = expand_paths(args.extra_search_paths)
-
     if args.cmdline and args.verb not in MKOSI_COMMANDS_CMDLINE:
         die(f"Parameters after verb are only accepted for {list_to_string(verb.name for verb in MKOSI_COMMANDS_CMDLINE)}.")
 
@@ -2335,30 +2332,6 @@ def bump_image_version(config: MkosiConfig) -> None:
     Path("mkosi.version").write_text(new_version + "\n")
 
 
-def expand_paths(paths: Sequence[str]) -> list[Path]:
-    if not paths:
-        return []
-
-    environ = os.environ.copy()
-    # Add a fake SUDO_HOME variable to allow non-root users specify
-    # paths in their home when using mkosi via sudo.
-    sudo_user = os.getenv("SUDO_USER")
-    if sudo_user and "SUDO_HOME" not in environ:
-        environ["SUDO_HOME"] = os.path.expanduser(f"~{sudo_user}")
-
-    # No os.path.expandvars because it treats unset variables as empty.
-    expanded = []
-    for path in paths:
-        if not sudo_user:
-            path = os.path.expanduser(path)
-        try:
-            expanded += [Path(string.Template(str(path)).substitute(environ))]
-        except KeyError:
-            # Skip path if it uses a variable not defined.
-            pass
-    return expanded
-
-
 @contextlib.contextmanager
 def prepend_to_environ_path(paths: Sequence[Path]) -> Iterator[None]:
     if not paths:
index 9be1f54c9f127a3aa23c427220122c4f540b0de9..8b059cfc4b85a13294231fa531b43fdf5d6eee08 100644 (file)
@@ -4,7 +4,7 @@ import dataclasses
 import enum
 import fnmatch
 import functools
-import os
+import os.path
 import platform
 import sys
 import textwrap
@@ -19,6 +19,7 @@ from mkosi.backend import (
     OutputFormat,
     Verb,
     chdir,
+    current_user,
     detect_distribution,
     flatten,
 )
@@ -45,9 +46,19 @@ def parse_boolean(s: str) -> bool:
     die(f"Invalid boolean literal: {s!r}")
 
 
-def parse_path(value: str, *, required: bool, absolute: bool = True) -> Path:
+def parse_path(value: str, *, required: bool, absolute: bool = True, expanduser: bool = True, expandvars: bool = True) -> Path:
+    if expandvars:
+        value = os.path.expandvars(value)
+
     path = Path(value)
 
+    if expanduser:
+        user = current_user()
+        if path.is_relative_to("~") and not user.is_running_user():
+            path = user.home / path.relative_to("~")
+        else:
+            path = path.expanduser()
+
     if required and not path.exists():
         die(f"{value} does not exist")
 
@@ -61,7 +72,7 @@ def parse_source_target_paths(value: str) -> tuple[Path, Optional[Path]]:
     src, _, target = value.partition(':')
     src_path = parse_path(src, required=True)
     if target:
-        target_path = parse_path(target, required=False, absolute=False)
+        target_path = parse_path(target, required=False, absolute=False, expanduser=False)
         if not target_path.is_absolute():
             die("Target path must be absolute")
     else:
@@ -213,15 +224,17 @@ def config_make_list_parser(delimiter: str, parse: Callable[[str], Any] = str) -
     return config_parse_list
 
 
-def make_path_parser(*, required: bool, absolute: bool = True) -> Callable[[str], Path]:
+def make_path_parser(*, required: bool, absolute: bool = True, expanduser: bool = True, expandvars: bool = True) -> Callable[[str], Path]:
     return functools.partial(
         parse_path,
         required=required,
         absolute=absolute,
+        expanduser=expanduser,
+        expandvars=expandvars,
     )
 
 
-def config_make_path_parser(*, required: bool, absolute: bool = True) -> ConfigParseCallback:
+def config_make_path_parser(*, required: bool, absolute: bool = True, expanduser: bool = True, expandvars: bool = True) -> ConfigParseCallback:
     def config_parse_path(dest: str, value: Optional[str], namespace: argparse.Namespace) -> Optional[Path]:
         if dest in namespace:
             return getattr(namespace, dest) # type: ignore
@@ -231,6 +244,8 @@ def config_make_path_parser(*, required: bool, absolute: bool = True) -> ConfigP
                 value,
                 required=required,
                 absolute=absolute,
+                expanduser=expanduser,
+                expandvars=expandvars,
             )
 
         return None