]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Allow configuring rpm in package manager trees
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Sat, 2 Dec 2023 08:34:05 +0000 (09:34 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 4 Dec 2023 13:21:36 +0000 (14:21 +0100)
Up until now, we've been unconditionally using the rpm configuration
from the host system. Unfortunately we can't entirely get rid of this
as rpm doesn't operate with an empty configuration and the configuration
is tightly coupled to the rpm version.

However, let's at least make sure we don't use any rpm configuration from
/etc and let's allow users to add extra rpm configuration themselves using
package manager trees.

By using $RPM_CONFIGDIR, we can configure where rpm looks for its main
configuration. We allow users to configure various rpm configuration in their
package manager trees and then copy over any missing files from the host
afterwards.

Because the /etc config directories are hardcoded in rpm, we have to resort
to mounting an empty directory on top of /etc/rpm if it exists.

mkosi/__init__.py
mkosi/installer/dnf.py
mkosi/resources/mkosi.md

index 96ee413bfd367fd79061b7cf29ef58fb691f574b..bf23e18b492c5791492a99fa4e83d5225d24a49c 100644 (file)
@@ -2731,6 +2731,24 @@ def mount_tools(tree: Optional[Path]) -> Iterator[None]:
         yield
 
 
+@contextlib.contextmanager
+def hide_host_directories() -> Iterator[None]:
+    with contextlib.ExitStack() as stack:
+
+        # We want to limit the effect of host specific admin configuration on image builds, so we hide various config
+        # directories that we can't override using CLI options or environment variables so that they don't affect our
+        # builds.
+
+        for d in ("/etc/rpm",):
+            if not Path(d).exists():
+                continue
+
+            tmp = stack.enter_context(tempfile.TemporaryDirectory())
+            stack.enter_context(mount(what=tmp, where=Path(d), operation="--bind"))
+
+        yield
+
+
 def check_workspace_directory(config: MkosiConfig) -> None:
     wd = config.workspace_dir_or_default()
 
@@ -2850,6 +2868,7 @@ def run_verb(args: MkosiArgs, images: Sequence[MkosiConfig]) -> None:
         with (
             complete_step(f"Building {config.image or 'default'} image"),
             mount_tools(config.tools_tree),
+            hide_host_directories(),
             prepend_to_environ_path(config),
         ):
             # After tools have been mounted, check if we have what we need
index 1ba0c2e898689b8fc4623bd0b9cb1738f6378cfa..08bfea87e184df1f3c8df534717f6107ca90fe61 100644 (file)
@@ -1,14 +1,15 @@
 # SPDX-License-Identifier: LGPL-2.1+
 import os
 import shutil
+import subprocess
 import textwrap
 from collections.abc import Iterable
 from pathlib import Path
 from typing import NamedTuple, Optional
 
-from mkosi.run import apivfs_cmd, bwrap
+from mkosi.run import apivfs_cmd, bwrap, run
 from mkosi.state import MkosiState
-from mkosi.tree import rmtree
+from mkosi.tree import copy_tree, rmtree
 from mkosi.types import PathString
 from mkosi.util import sort_packages
 
@@ -94,11 +95,18 @@ def setup_dnf(state: MkosiState, repos: Iterable[Repo], filelists: bool = True)
 
                 f.write("\n")
 
+    rpmconfigdir = Path(run(["rpm", "--eval", "%{_rpmconfigdir}"], stdout=subprocess.PIPE).stdout.strip())
+    (state.pkgmngr / "usr/lib").mkdir(parents=True, exist_ok=True)
+    copy_tree(state.config, rpmconfigdir, state.pkgmngr / "usr/lib/rpm", clobber=False)
+
 
 def dnf_cmd(state: MkosiState) -> list[PathString]:
     dnf = dnf_executable(state)
 
     cmdline: list[PathString] = [
+        "env",
+        "HOME=/", # Make sure rpm doesn't pick up ~/.rpmmacros and ~/.rpmrc.
+        f"RPM_CONFIGDIR={state.pkgmngr / 'usr/lib/rpm'}",
         dnf,
         "--assumeyes",
         f"--config={state.pkgmngr / 'etc/dnf/dnf.conf'}",
@@ -167,4 +175,4 @@ def fixup_rpmdb_location(root: Path) -> None:
 
 
 def rpm_cmd(state: MkosiState) -> list[PathString]:
-    return ["rpm", "--root", state.root]
+    return ["env", "HOME=/", f"RPM_CONFIGDIR={state.pkgmngr / 'usr/lib/rpm'}", "rpm", "--root", state.root]
index c54cd06026c46a8710d6a9413b6ee4a09d793887..9e750491c24acaf838686fd48890129779df66d4 100644 (file)
@@ -556,6 +556,19 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
   subdirectory of the workspace directory instead of the OS tree. This
   subdirectory of the workspace is used to configure the package manager.
 
+: `mkosi` will look for the package manager configuration and related
+  files in the configured package manager trees. Unless specified
+  otherwise, it will use the configuration file from its canonical
+  location in the package manager trees. For example, it will look for
+  `etc/dnf/dnf.conf` in the package manager trees if `dnf` is used to
+  install packages.
+
+: Extra rpm configuration should be put in `usr/lib/rpm` in the package
+  manager trees. Any configuration in `etc/rpm` will be ignored. The
+  extra configuration in `usr/lib/rpm` should mimick the layout of
+  `usr/lib/rpm` on the host. For example, extra macro files should go in
+  `usr/lib/rpm/macros.d` in the package manager trees.
+
 : `SkeletonTrees=` and `PackageManagerTrees=` fulfill similar roles. Use
   `SkeletonTrees=` if you want the files to be present in the final image. Use
   `PackageManagerTrees=` if you don't want the files to be present in the final