From: Daan De Meyer Date: Sat, 2 Dec 2023 08:34:05 +0000 (+0100) Subject: Allow configuring rpm in package manager trees X-Git-Tag: v20~114^2~27 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5b7d86c4a6d62eaeb0d74bfd51ce033d270bb625;p=thirdparty%2Fmkosi.git Allow configuring rpm in package manager trees 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. --- diff --git a/mkosi/__init__.py b/mkosi/__init__.py index 96ee413bf..bf23e18b4 100644 --- a/mkosi/__init__.py +++ b/mkosi/__init__.py @@ -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 diff --git a/mkosi/installer/dnf.py b/mkosi/installer/dnf.py index 1ba0c2e89..08bfea87e 100644 --- a/mkosi/installer/dnf.py +++ b/mkosi/installer/dnf.py @@ -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] diff --git a/mkosi/resources/mkosi.md b/mkosi/resources/mkosi.md index c54cd0602..9e750491c 100644 --- a/mkosi/resources/mkosi.md +++ b/mkosi/resources/mkosi.md @@ -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