dnf4.mark: mark packages in DNF state database

This adjustment allows the definition of the mark with the RPMs and runs
DNF after installing the RPMs to put the proper markings in the DNF
state database. See #455.

This ensures that packages don't get removed during `autoremove` leading
to broken systems.
This commit is contained in:
Simon de Vlieger 2023-06-27 14:42:09 +02:00 committed by Ondřej Budai
parent 4ddfe5ed0f
commit 161fe789af
4 changed files with 1244 additions and 0 deletions

81
stages/org.osbuild.dnf4.mark Executable file
View file

@ -0,0 +1,81 @@
#!/usr/bin/python3
"""
Mark packages in the DNF state database.
"""
import shutil
import subprocess
import sys
from osbuild import api
SCHEMA_2 = """
"options": {
"additionalProperties": false,
"properties": {
"packages": {
"type": "array",
"minItems": 1,
"description": "Packages and their marks.",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["name", "mark"],
"properties": {
"name": {
"type": "string",
"description": "Package name."
},
"mark": {
"type": "string",
"enum": ["install", "group"],
"description": "Package mark."
}
}
}
}
}
}
"""
def mark(tree, packages):
dnf_bin = shutil.which("dnf-3")
if not dnf_bin:
print("dnf not found")
return 1
markings = {}
for package in packages:
if package["mark"] not in markings:
markings[package["mark"]] = []
markings[package["mark"]] += [package]
if "install" in markings:
subprocess.run(
[dnf_bin, "--installroot", tree, "mark", "-y", "install"]
+ [package["name"] for package in markings["install"]],
check=True,
)
if "group" in markings:
subprocess.run(
[dnf_bin, "--installroot", tree, "mark", "-y", "group"]
+ [package["name"] for package in markings["group"]],
check=True,
)
return 0
def main(tree, options):
return mark(tree, options["packages"])
if __name__ == "__main__":
args = api.arguments()
r = main(args["tree"], args["options"])
sys.exit(r)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,30 @@
version: '2'
pipelines:
- mpp-import-pipelines:
path: ../manifests/fedora-vars.ipp.yaml
- mpp-import-pipeline:
path: ../manifests/fedora-build-v2.ipp.yaml
id: build
runner:
mpp-format-string: org.osbuild.fedora{release}
- name: tree
build: name:build
stages:
- type: org.osbuild.rpm
inputs:
packages:
type: org.osbuild.files
origin: org.osbuild.source
mpp-depsolve:
architecture: $arch
module-platform-id: $module_platform_id
repos:
mpp-eval: repos
packages:
- dnf
- fedora-release
- type: org.osbuild.dnf4.mark
options:
packages:
- name: dnf
mark: install

View file

@ -521,3 +521,36 @@ class TestStages(test.TestBase):
assert capacity == str(json.loads(res.stdout)["virtual-size"])
pop_size = ovf_tree_disk.attrib["{http://schemas.dmtf.org/ovf/envelope/1}populatedSize"]
assert pop_size == str(os.stat(vmdk).st_size)
def test_dnf4_mark(self):
datadir = self.locate_test_data()
testdir = os.path.join(datadir, "stages", "dnf4.mark")
with self.osbuild as osb, tempfile.TemporaryDirectory(dir="/var/tmp") as outdir:
osb.compile_file(os.path.join(testdir, "tree.json"), exports=["tree"], output_dir=outdir)
tree = os.path.join(outdir, "tree")
assert os.path.isdir(tree)
# we're going to verify that packages in the tree are marked according to
r = subprocess.run(
[
"dnf",
"--installroot", tree,
"repoquery", "--installed",
"--qf", "%{name},%{reason}"
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf8",
check=True
)
for line in r.stdout.splitlines():
package, mark = line.strip().split(",")
if package == "dnf":
assert mark == "user"
else:
assert mark == "unknown"