]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
manifest: when recording packages, ignore packages from the base image
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 9 Sep 2021 11:04:39 +0000 (13:04 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 12 Oct 2021 10:46:37 +0000 (12:46 +0200)
This solves the problem that the generated package list included all packages
visible in the combined overlay when building something on top of a base image.
Instead, we want just the stuff that was added in the overlay.

I considered some other approaches:
- use 'dnf history info' to query what the last transacation was.
  The output is human-readable tabular text, and would have to be parsed.
  This could be done, but there's a bigger problem: we don't necessarilly
  know that the last transaction is all that matters. And in fact, as
  raised by mdomonko in #rpm-ecosystem, dnf is not the only way to install
  rpm packages. Using rpm directly also covers direct rpm invocations,
  which could be done from the build scripts.
- look at rpm transaction id. This still has the problem that we don't
  know if the last transaction is all that matters.

So overall, the simple time-based approach should be no worse than the other
ones, and is trivially easy to implement.

mkosi/manifest.py

index aedc7bacdd0e7ffa8908c9c02236f81651c7dd06..76f71c70028b400c2e54e11e045a8d8b88ce83bc 100644 (file)
@@ -2,6 +2,7 @@
 
 import dataclasses
 import json
+from datetime import datetime
 from pathlib import Path
 from subprocess import DEVNULL, PIPE
 from textwrap import dedent
@@ -61,6 +62,8 @@ class Manifest:
     packages: List[PackageManifest] = dataclasses.field(default_factory=list)
     source_packages: Dict[str, SourcePackageManifest] = dataclasses.field(default_factory=dict)
 
+    _init_timestamp: datetime = dataclasses.field(init=False, default_factory=datetime.now)
+
     def need_source_info(self) -> bool:
         return ManifestFormat.changelog in self.args.manifest_format
 
@@ -71,7 +74,8 @@ class Manifest:
 
     def record_rpm_packages(self, root: Path) -> None:
         c = run(
-            ["rpm", f"--root={root}", "-qa", "--qf", r"%{NEVRA}\t%{SOURCERPM}\t%{NAME}\t%{SIZE}\n"],
+            ["rpm", f"--root={root}", "-qa", "--qf",
+             r"%{NEVRA}\t%{SOURCERPM}\t%{NAME}\t%{SIZE}\t%{INSTALLTIME}\n"],
             stdout=PIPE,
             stderr=DEVNULL,
             text=True,
@@ -80,12 +84,19 @@ class Manifest:
         packages = sorted(c.stdout.splitlines())
 
         for package in packages:
-            nevra, srpm, name, size = package.split("\t")
+            nevra, srpm, name, size, installtime = package.split("\t")
 
             assert nevra.startswith(f"{name}-")
             evra = nevra[len(name) + 1 :]
 
             size = int(size)
+            installtime = datetime.fromtimestamp(int(installtime))
+
+            # If we are creating a layer based on a BaseImage=, e.g. a sysext, filter by
+            # packages that were installed in this execution of mkosi. We assume that the
+            # upper layer is put together in one go, which currently is always true.
+            if self.args.base_image and installtime < self._init_timestamp:
+                continue
 
             package = PackageManifest("rpm", name, evra, size)
             self.packages.append(package)