Skip to content

Instantly share code, notes, and snippets.

@gicmo
Created March 5, 2021 14:35
Show Gist options
  • Save gicmo/5450b6641ffa504aab1932fdfdcec359 to your computer and use it in GitHub Desktop.
Save gicmo/5450b6641ffa504aab1932fdfdcec359 to your computer and use it in GitHub Desktop.
#!/usr/bin/python3
import argparse
import json
import os
import sys
import tempfile
import dnf
import dnf.conf
import dnf.conf.read
import osbuild.util.osrelease as ostrelease
class DepSolver:
def __init__(self, arch, relver, dirs):
self.base = dnf.Base()
self.arch = arch
self.basearch = dnf.rpm.basearch(arch)
conf = self.base.conf
conf.config_file_path = "/dev/null"
conf.persistdir = dirs["persistdir"]
conf.cachedir = dirs["cachedir"]
conf.substitutions["arch"] = arch
conf.substitutions["basearch"] = self.basearch
conf.substitutions["releasever"] = relver
conf.reposdir = [dirs["repodir"]]
self.repos = self.read_repos()
def read_repos(self):
conf = self.base.conf
reader = dnf.conf.read.RepoReader(conf, {})
return {r.id: r for r in reader}
def reset(self):
base = self.base
base.reset(goal=True, repos=True, sack=True)
for repo in self.repos.values():
base.repos.add(repo)
base.fill_sack(load_system_repo=False)
def filter(self, pkg):
sack = self.base.sack
return dnf.subject.Subject(pkg).get_best_query(sack).filter(latest=True)
def install(self, packages, excludes=None, optional=False):
self.base.install_specs(packages, exclude=(excludes or []))
def resolve(self):
self.base.resolve()
dependencies = []
for tsi in self.base.transaction:
# Avoid using the install_set() helper, as it does not guarantee
# a stable order
if tsi.action not in dnf.transaction.FORWARD_ACTIONS:
continue
package = tsi.pkg
dependencies.append({
"name": package.name,
"epoch": package.epoch,
"version": package.version,
"release": package.release,
"arch": package.arch,
"repo_id": package.reponame,
"path": package.relativepath,
"remote_location": package.remote_location()
})
return dependencies
def list_packages(text, solver):
parser = argparse.ArgumentParser()
parser.add_argument("--optional", action="store_true", default=False)
parser.add_argument("--except", dest="excludes", action="append")
parser.add_argument("packages", help="The template to process", nargs="*")
packages = []
for line in text:
cmd, args = line[0], parser.parse_args(line[1:])
if cmd != "installpkg":
print(f"{cmd} ignored", file=sys.stderr)
continue
pkgs = solver.install(args.packages, None, args.optional)
packages += pkgs
return packages
INSTALL = [
"redhat-release",
"glibc", "glibc-minimal-langpack", "nss-altfiles",
"dracut-config-generic", "dracut-network",
"basesystem", "bash", "platform-python",
"shadow-utils", "chrony", "setup", "shadow-utils",
"sudo", "systemd", "coreutils", "util-linux",
"curl", "vim-minimal",
"rpm", "rpm-ostree", "polkit",
"lvm2", "cryptsetup", "pinentry",
"e2fsprogs", "dosfstools",
"keyutils", "gnupg2",
"attr", "xz", "gzip",
"firewalld", "iptables",
"NetworkManager", "NetworkManager-wifi", "NetworkManager-wwan",
"wpa_supplicant",
"dnsmasq", "traceroute",
"hostname", "iproute", "iputils",
"openssh-clients", "procps-ng", "rootfiles",
"openssh-server", "passwd",
"policycoreutils", "policycoreutils-python-utils",
"selinux-policy-targeted", "setools-console",
"less", "tar", "rsync",
"fwupd", "usbguard",
"bash-completion", "tmux",
"ima-evm-utils",
"audit",
"podman", "container-selinux", "skopeo", "criu",
"slirp4netns", "fuse-overlayfs",
"clevis", "clevis-dracut", "clevis-luks",
"greenboot", "greenboot-grub2", "greenboot-rpm-ostree-grub2", "greenboot-reboot", "greenboot-status",
"grub2", "grub2-efi-x64", "efibootmgr", "shim-x64", "microcode_ctl",
"iwl1000-firmware", "iwl100-firmware", "iwl105-firmware", "iwl135-firmware",
"iwl2000-firmware", "iwl2030-firmware", "iwl3160-firmware", "iwl5000-firmware",
"iwl5150-firmware", "iwl6000-firmware", "iwl6050-firmware", "iwl7260-firmware",
]
EXCLUDES = ["rng-tools", "subscription-manager"]
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--basearch", help="Set the `basearch` variable", default="x86_64")
parser.add_argument("--product", help="Set the `product` variable", default="fedora")
parser.add_argument("--dnf-cache", metavar="PATH", type=os.path.abspath, default=None,
help="Path to DNF cache-directory to use")
parser.add_argument("--repo-dir", metavar="PATH", type=os.path.abspath,
default="/etc/yum.repos.d",
help="Path to DNF repositories directory")
parser.add_argument("--os-version", metavar="PATH", default=None,
help="OS version to use for dnf")
args = parser.parse_args()
variables = {
"basearch": args.basearch,
"product": args.product
}
packages = []
os_version = args.os_version
if not os_version:
release = ostrelease.parse_files(*ostrelease.DEFAULT_PATHS)
os_version = release["VERSION_ID"]
with tempfile.TemporaryDirectory(dir="/var/tmp") as tmp:
persistdir = os.path.join(tmp, "dnf-persist")
cachedir = args.dnf_cache or os.path.join(tmp, "dnf-cache")
dirs = {
"persistdir": persistdir,
"cachedir": cachedir,
"repodir": args.repo_dir
}
solver = DepSolver(args.basearch, os_version, dirs)
solver.reset()
solver.install(INSTALL, EXCLUDES)
solver.install(["tuned"])
packages = solver.resolve()
json.dump(packages, sys.stdout, indent=2)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment