]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Move package installation and removal to PackageManager interface 3582/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Wed, 5 Mar 2025 19:32:58 +0000 (20:32 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 6 Mar 2025 10:08:39 +0000 (11:08 +0100)
There's no need for these to be implemented by the Distribution
interface as they don't need distribution specific knowledge so let's
move them to the PackageManager interface instead.

16 files changed:
mkosi/__init__.py
mkosi/distributions/__init__.py
mkosi/distributions/arch.py
mkosi/distributions/azure.py
mkosi/distributions/centos.py
mkosi/distributions/custom.py
mkosi/distributions/debian.py
mkosi/distributions/fedora.py
mkosi/distributions/mageia.py
mkosi/distributions/openmandriva.py
mkosi/distributions/opensuse.py
mkosi/installer/__init__.py
mkosi/installer/apt.py
mkosi/installer/dnf.py
mkosi/installer/pacman.py
mkosi/installer/zypper.py

index ecef2275941457e293fcdbbbb81bca83c6b294fa..6ab11017ae4d783f159aedc6f04e5837dd990e0d 100644 (file)
@@ -239,7 +239,9 @@ def install_distribution(context: Context) -> None:
             return
 
         with complete_step(f"Installing extra packages for {context.config.distribution.pretty_name()}"):
-            context.config.distribution.install_packages(context, context.config.packages)
+            context.config.distribution.package_manager(context.config).install(
+                context, context.config.packages
+            )
     else:
         if context.config.overlay or context.config.output_format.is_extension_image():
             if context.config.packages:
@@ -278,7 +280,9 @@ def install_distribution(context: Context) -> None:
                 (context.root / "boot/loader/entries.srel").write_text("type1\n")
 
             if context.config.packages:
-                context.config.distribution.install_packages(context, context.config.packages)
+                context.config.distribution.package_manager(context.config).install(
+                    context, context.config.packages
+                )
 
     for f in (
         "var/lib/systemd/random-seed",
@@ -300,7 +304,9 @@ def install_build_packages(context: Context) -> None:
         complete_step(f"Installing build packages for {context.config.distribution.pretty_name()}"),
         setup_build_overlay(context),
     ):
-        context.config.distribution.install_packages(context, context.config.build_packages)
+        context.config.distribution.package_manager(context.config).install(
+            context, context.config.build_packages
+        )
 
 
 def install_volatile_packages(context: Context) -> None:
@@ -308,7 +314,9 @@ def install_volatile_packages(context: Context) -> None:
         return
 
     with complete_step(f"Installing volatile packages for {context.config.distribution.pretty_name()}"):
-        context.config.distribution.install_packages(context, context.config.volatile_packages)
+        context.config.distribution.package_manager(context.config).install(
+            context, context.config.volatile_packages
+        )
 
 
 def remove_packages(context: Context) -> None:
@@ -319,7 +327,9 @@ def remove_packages(context: Context) -> None:
 
     with complete_step(f"Removing {len(context.config.remove_packages)} packages…"):
         try:
-            context.config.distribution.remove_packages(context, context.config.remove_packages)
+            context.config.distribution.package_manager(context.config).remove(
+                context, context.config.remove_packages
+            )
         except NotImplementedError:
             die(f"Removing packages is not supported for {context.config.distribution}")
 
index 0800bafc0e80c26b1180eeea56d3430050b315d8..381becc2937c47b98d5465c013951246f6dfaa6f 100644 (file)
@@ -3,7 +3,6 @@
 import enum
 import importlib
 import urllib.parse
-from collections.abc import Sequence
 from pathlib import Path
 from typing import TYPE_CHECKING, Optional, cast
 
@@ -43,14 +42,6 @@ class DistributionInstaller:
     def install(cls, context: "Context") -> None:
         raise NotImplementedError
 
-    @classmethod
-    def install_packages(cls, context: "Context", packages: Sequence[str]) -> None:
-        raise NotImplementedError
-
-    @classmethod
-    def remove_packages(cls, context: "Context", packages: Sequence[str]) -> None:
-        raise NotImplementedError
-
     @classmethod
     def filesystem(cls) -> str:
         return "ext4"
@@ -136,12 +127,6 @@ class Distribution(StrEnum):
     def install(self, context: "Context") -> None:
         return self.installer().install(context)
 
-    def install_packages(self, context: "Context", packages: Sequence[str]) -> None:
-        return self.installer().install_packages(context, packages)
-
-    def remove_packages(self, context: "Context", packages: Sequence[str]) -> None:
-        return self.installer().remove_packages(context, packages)
-
     def filesystem(self) -> str:
         return self.installer().filesystem()
 
index 382742c2571e5f6de8626fb763bae4f01f2d9671..65910801eaaaa08b3ea4b3958db7401360e02d7f 100644 (file)
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
 import tempfile
-from collections.abc import Iterable, Sequence
+from collections.abc import Iterable
 from pathlib import Path
 
 from mkosi.archive import extract_tar
@@ -62,20 +62,7 @@ class Installer(DistributionInstaller):
 
     @classmethod
     def install(cls, context: Context) -> None:
-        cls.install_packages(context, ["filesystem"], apivfs=False)
-
-    @classmethod
-    def install_packages(cls, context: Context, packages: Sequence[str], apivfs: bool = True) -> None:
-        Pacman.invoke(
-            context,
-            "--sync",
-            ["--needed", "--assume-installed", "initramfs", *packages],
-            apivfs=apivfs,
-        )
-
-    @classmethod
-    def remove_packages(cls, context: Context, packages: Sequence[str]) -> None:
-        Pacman.invoke(context, "--remove", ["--nosave", "--recursive", *packages], apivfs=True)
+        Pacman.install(context, ["filesystem"], apivfs=False)
 
     @classmethod
     def repositories(cls, context: Context) -> Iterable[PacmanRepository]:
index 84df21e1c027bbe4559a618c9c913220f9148177..f66d8a450809eee1408c9e0ceb0672fb5e758903 100644 (file)
@@ -33,7 +33,7 @@ class Installer(fedora.Installer):
 
     @classmethod
     def install(cls, context: Context) -> None:
-        cls.install_packages(context, ["filesystem", "azurelinux-release"], apivfs=False)
+        Dnf.install(context, ["filesystem", "azurelinux-release"], apivfs=False)
 
     @classmethod
     def repositories(cls, context: Context) -> Iterable[RpmRepository]:
index c57b8945c04eb0473271b224a8cf356933af50e8..29f03c15c08b41db4a9fa6227d6ee5a35514ee4f 100644 (file)
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
-from collections.abc import Iterable, Sequence
+from collections.abc import Iterable
 
 from mkosi.config import Architecture, Config
 from mkosi.context import Context
@@ -78,15 +78,7 @@ class Installer(DistributionInstaller):
 
     @classmethod
     def install(cls, context: Context) -> None:
-        cls.install_packages(context, ["basesystem"], apivfs=False)
-
-    @classmethod
-    def install_packages(cls, context: Context, packages: Sequence[str], apivfs: bool = True) -> None:
-        Dnf.invoke(context, "install", packages, apivfs=apivfs)
-
-    @classmethod
-    def remove_packages(cls, context: Context, packages: Sequence[str]) -> None:
-        Dnf.invoke(context, "remove", packages, apivfs=True)
+        Dnf.install(context, ["basesystem"], apivfs=False)
 
     @classmethod
     def architecture(cls, arch: Architecture) -> str:
index fd31f5c0830d53fe5757e1c60ba8d113d0a46431..8c36cfdec4a5a8caf6cea8a27cfda45175d0e499 100644 (file)
@@ -1,12 +1,10 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
-from collections.abc import Sequence
 
 from mkosi.config import Architecture, Config
 from mkosi.context import Context
 from mkosi.distributions import DistributionInstaller
 from mkosi.installer import PackageManager
-from mkosi.log import die
 
 
 class Installer(DistributionInstaller):
@@ -29,13 +27,3 @@ class Installer(DistributionInstaller):
     @classmethod
     def install(cls, context: Context) -> None:
         pass
-
-    @classmethod
-    def install_packages(cls, context: Context, packages: Sequence[str]) -> None:
-        if packages:
-            die("Installing packages is not supported for custom distributions'")
-
-    @classmethod
-    def remove_packages(cls, context: Context, packages: Sequence[str]) -> None:
-        if packages:
-            die("Removing packages is not supported for custom distributions")
index b6730d3f674a39f3040f8ab3b0ad7f01e84dfd26..dfa4413eba6f7ecff60987e5b2300977ea6cdfb9 100644 (file)
@@ -1,7 +1,8 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
+import itertools
 import tempfile
-from collections.abc import Iterable, Sequence
+from collections.abc import Iterable
 from pathlib import Path
 
 from mkosi.archive import extract_tar
@@ -192,25 +193,12 @@ class Installer(DistributionInstaller):
         # Finally, run apt to properly install packages in the chroot without having to worry that maintainer
         # scripts won't find basic tools that they depend on.
 
-        cls.install_packages(
-            context, [Path(deb).name.partition("_")[0].removesuffix(".deb") for deb in essential]
-        )
+        Apt.install(context, [Path(deb).name.partition("_")[0].removesuffix(".deb") for deb in essential])
 
         fixup_os_release(context)
 
-    @classmethod
-    def install_packages(cls, context: Context, packages: Sequence[str], apivfs: bool = True) -> None:
-        Apt.invoke(context, "install", packages, apivfs=apivfs)
-        install_apt_sources(context, cls.repositories(context, local=False))
-
-        # systemd-gpt-auto-generator is disabled by default in Ubuntu:
-        # https://git.launchpad.net/ubuntu/+source/systemd/tree/debian/systemd.links?h=ubuntu/noble-proposed.
-        # Let's make sure it is enabled by default in our images.
-        (context.root / "etc/systemd/system-generators/systemd-gpt-auto-generator").unlink(missing_ok=True)
-
-    @classmethod
-    def remove_packages(cls, context: Context, packages: Sequence[str]) -> None:
-        Apt.invoke(context, "purge", packages, apivfs=True)
+        if "apt" in itertools.chain(context.config.packages, context.config.volatile_packages):
+            install_apt_sources(context, cls.repositories(context, local=False))
 
     @classmethod
     def architecture(cls, arch: Architecture) -> str:
index 5e8af9c5fcabdca1ce8dd5f62c7de2c152807841..88b94c9f7c3472258a64f167ee63bb1107feb981 100644 (file)
@@ -3,7 +3,7 @@
 import re
 import subprocess
 import tempfile
-from collections.abc import Iterable, Sequence
+from collections.abc import Iterable
 from pathlib import Path
 
 from mkosi.config import Architecture, Config
@@ -129,15 +129,7 @@ class Installer(DistributionInstaller):
 
     @classmethod
     def install(cls, context: Context) -> None:
-        cls.install_packages(context, ["basesystem"], apivfs=False)
-
-    @classmethod
-    def install_packages(cls, context: Context, packages: Sequence[str], apivfs: bool = True) -> None:
-        Dnf.invoke(context, "install", packages, apivfs=apivfs)
-
-    @classmethod
-    def remove_packages(cls, context: Context, packages: Sequence[str]) -> None:
-        Dnf.invoke(context, "remove", packages, apivfs=True)
+        Dnf.install(context, ["basesystem"], apivfs=False)
 
     @classmethod
     def repositories(cls, context: Context) -> Iterable[RpmRepository]:
index ce8317dc6dd92ac98df9b9108c92b11ad780c55f..65224f69e963c5cbad60176b4dcae544b81fa7fd 100644 (file)
@@ -5,6 +5,7 @@ from collections.abc import Iterable
 from mkosi.config import Architecture
 from mkosi.context import Context
 from mkosi.distributions import fedora, join_mirror
+from mkosi.installer.dnf import Dnf
 from mkosi.installer.rpm import RpmRepository, find_rpm_gpgkey
 from mkosi.log import die
 
@@ -24,7 +25,7 @@ class Installer(fedora.Installer):
 
     @classmethod
     def install(cls, context: Context) -> None:
-        cls.install_packages(context, ["filesystem"], apivfs=False)
+        Dnf.install(context, ["filesystem"], apivfs=False)
 
     @classmethod
     def repositories(cls, context: Context) -> Iterable[RpmRepository]:
index 1e0de8b54a1bd4a83b6841f7119f15002d7ec848..2bfc2a225de7e259a07d57dd882af04ed48cba81 100644 (file)
@@ -5,6 +5,7 @@ from collections.abc import Iterable
 from mkosi.config import Architecture
 from mkosi.context import Context
 from mkosi.distributions import fedora, join_mirror
+from mkosi.installer.dnf import Dnf
 from mkosi.installer.rpm import RpmRepository, find_rpm_gpgkey
 from mkosi.log import die
 
@@ -24,7 +25,7 @@ class Installer(fedora.Installer):
 
     @classmethod
     def install(cls, context: Context) -> None:
-        cls.install_packages(context, ["filesystem"], apivfs=False)
+        Dnf.install(context, ["filesystem"], apivfs=False)
 
     @classmethod
     def repositories(cls, context: Context) -> Iterable[RpmRepository]:
index 7ecc380209925ee0b0e04468475057d017e5104d..ff9360c0c281a42ad0952162db538dce180a3a87 100644 (file)
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
 import tempfile
-from collections.abc import Iterable, Sequence
+from collections.abc import Iterable
 from pathlib import Path
 from xml.etree import ElementTree
 
@@ -58,30 +58,7 @@ class Installer(DistributionInstaller):
 
     @classmethod
     def install(cls, context: Context) -> None:
-        cls.install_packages(context, ["filesystem"], apivfs=False)
-
-    @classmethod
-    def install_packages(cls, context: Context, packages: Sequence[str], apivfs: bool = True) -> None:
-        if context.config.find_binary("zypper"):
-            Zypper.invoke(
-                context,
-                "install",
-                [
-                    "--download", "in-advance",
-                    "--recommends" if context.config.with_recommends else "--no-recommends",
-                    *packages,
-                ],
-                apivfs=apivfs,
-            )  # fmt: skip
-        else:
-            Dnf.invoke(context, "install", packages, apivfs=apivfs)
-
-    @classmethod
-    def remove_packages(cls, context: Context, packages: Sequence[str]) -> None:
-        if context.config.find_binary("zypper"):
-            Zypper.invoke(context, "remove", ["--clean-deps", *packages], apivfs=True)
-        else:
-            Dnf.invoke(context, "remove", packages, apivfs=True)
+        cls.package_manager(context.config).install(context, ["filesystem"], apivfs=False)
 
     @classmethod
     def repositories(cls, context: Context) -> Iterable[RpmRepository]:
index b9277396640443747639219c2dbfbafc85b0b7f0..5a49da680cc0f68814dcf6542d569e406a96dee9 100644 (file)
@@ -145,6 +145,20 @@ class PackageManager:
             ],
         )  # fmt: skip
 
+    @classmethod
+    def install(
+        cls,
+        context: Context,
+        packages: Sequence[str],
+        *,
+        apivfs: bool = True,
+    ) -> None:
+        pass
+
+    @classmethod
+    def remove(cls, context: Context, packages: Sequence[str]) -> None:
+        pass
+
     @classmethod
     def sync(cls, context: Context, force: bool) -> None:
         pass
index ec89a4d82951ec3955f3b064480bd6dc0982e939..d53bb83bf27e025826f0acf0d383e577f3d3460d 100644 (file)
@@ -229,6 +229,25 @@ class Apt(PackageManager):
             stdout=stdout,
         )
 
+    @classmethod
+    def install(
+        cls,
+        context: Context,
+        packages: Sequence[str],
+        *,
+        apivfs: bool = True,
+    ) -> None:
+        cls.invoke(context, "install", packages, apivfs=apivfs)
+
+        # systemd-gpt-auto-generator is disabled by default in Ubuntu:
+        # https://git.launchpad.net/ubuntu/+source/systemd/tree/debian/systemd.links?h=ubuntu/noble-proposed.
+        # Let's make sure it is enabled by default in our images.
+        (context.root / "etc/systemd/system-generators/systemd-gpt-auto-generator").unlink(missing_ok=True)
+
+    @classmethod
+    def remove(cls, context: Context, packages: Sequence[str]) -> None:
+        cls.invoke(context, "purge", packages, apivfs=True)
+
     @classmethod
     def sync(cls, context: Context, force: bool) -> None:
         cls.invoke(context, "update")
index 13b97edff539a7034f0ab358669289be87825b63..dec412932a559f3a7cc190fd6a0c6bb200252060 100644 (file)
@@ -228,6 +228,20 @@ class Dnf(PackageManager):
                     if any(p.name.startswith(prefix) for prefix in ("dnf", "hawkey", "yum")):
                         p.unlink()
 
+    @classmethod
+    def install(
+        cls,
+        context: Context,
+        packages: Sequence[str],
+        *,
+        apivfs: bool = True,
+    ) -> None:
+        cls.invoke(context, "install", packages, apivfs=apivfs)
+
+    @classmethod
+    def remove(cls, context: Context, packages: Sequence[str]) -> None:
+        cls.invoke(context, "remove", packages, apivfs=True)
+
     @classmethod
     def sync(cls, context: Context, force: bool, arguments: Sequence[str] = ()) -> None:
         cls.invoke(
index 115c5b0cbba54f14b5dd69325211520e953b8edf..a11589b5205acaa13172cd00b12e3a0aeb1c6dd1 100644 (file)
@@ -180,6 +180,21 @@ class Pacman(PackageManager):
             stdout=stdout,
         )
 
+    @classmethod
+    def install(
+        cls,
+        context: Context,
+        packages: Sequence[str],
+        *,
+        apivfs: bool = True,
+    ) -> None:
+        arguments = ["--needed", "--assume-installed", "initramfs", *packages]
+        cls.invoke(context, "--sync", arguments, apivfs=apivfs)
+
+    @classmethod
+    def remove(cls, context: Context, packages: Sequence[str]) -> None:
+        cls.invoke(context, "--remove", ["--nosave", "--recursive", *packages], apivfs=True)
+
     @classmethod
     def keyring(cls, context: Context) -> None:
         def sandbox() -> AbstractContextManager[list[PathString]]:
index b39377e28d24538d4ed45e06f9d48fecfb958c57..8d78fc1fceadef2fd1063b1892db4ae88074e40b 100644 (file)
@@ -132,6 +132,26 @@ class Zypper(PackageManager):
             stdout=stdout,
         )
 
+    @classmethod
+    def install(
+        cls,
+        context: Context,
+        packages: Sequence[str],
+        *,
+        apivfs: bool = True,
+    ) -> None:
+        arguments = [
+            "--download", "in-advance",
+            "--recommends" if context.config.with_recommends else "--no-recommends",
+            *packages,
+        ]  # fmt: skip
+
+        cls.invoke(context, "install", arguments, apivfs=apivfs)
+
+    @classmethod
+    def remove(cls, context: Context, packages: Sequence[str]) -> None:
+        cls.invoke(context, "remove", ["--clean-deps", *packages], apivfs=True)
+
     @classmethod
     def sync(cls, context: Context, force: bool, arguments: Sequence[str] = ()) -> None:
         cls.invoke(context, "refresh", [*(["--force"] if force else []), *arguments])