Also get/modify filelists and modules, improve parser efficiency (#23)
All checks were successful
CI via Tox / tox (pull_request) Successful in 1m39s
AI Code Review / ai-review (pull_request_target) Successful in 31s

The main goal here is to also download and modify filelists and
modules metadata to fix some problems I saw in EPEL 9 update
tests. We need the filelists metadata to get correct results if
a package has a dependency on a file that is not present in the
primary metadata; if we don't also download filelists, we'll get
an incorrect "new" broken dependency because repoclosure on the
modified repository will not be able to find the package that
contains the file.

Similarly, we need modules metadata (if present) to ensure that
dnf knows module packages are module packages. Without the modules
metadata it treats all module packages as non-module packages,
so repoclosure on the modified repo might incorrectly say a dep
of a non-module package is fixed because it can be satisfied by a
module package.

However, there was a big trap lurking here: the filelists metadata
is even larger than the primary metadata, and both are kinda huge.
Previously we were using ElementTree.parse(), which loads the
entire XML tree into memory; this was already using >6GB of RAM
for a typical primary metadata file. If you tried to load both
primary and filelists into RAM at once (as my initial attempt did)
it uses a huge amount of RAM and probably gets OOM killed.

So, we'll parse the XML as a chunked bytestring. As we go along,
we split out package elements, one by one. Everything that is
not part of a package element gets passed straight through to the
output file.

When we encounter a package element, we parse it with lxml (which
gives us a nice ~2x speedup over ElementTree). We decide whether
to drop it. We do this the same way as before for the primary
metadata, but record the pkgid (which is usually the checksum).
When parsing the filelists metadata, we take the list of pkgids
removed from the primary metadata as input, and remove all
package elements with the same pkgid.

If we decide to drop the package, we move to the end of it in
the current input chunk. Otherwise, we write the chunk through
to the output file, then move ahead and continue.

We use the `open()` methods of various compression libraries to
achieve transparent decompression and recompression, and track
uncompressed checksums and sizes along the way. This also adds
support for various other compression formats; previously we
assumed zstd. Supporting at least gzip is important as there are
still extant RHEL releases with only gzip-compressed metadata.

We switch the other uses of ElementTree to lxml for consistency.

Signed-off-by: Adam Williamson <awilliam@redhat.com>
This commit is contained in:
Adam Williamson 2026-04-11 17:25:46 -07:00
commit f209553299
29 changed files with 841 additions and 138 deletions

View file

@ -0,0 +1,2 @@
backports.zstd ; python_version<'3.14'
lxml

View file

@ -56,5 +56,8 @@ show_missing = true
# don't @ me, Hynek
line-length = 100
[tool.mypy]
plugins = ["mypy_plugin_lxml.main"]
[tool.setuptools.dynamic]
dependencies = { file = ["install.requires"] }

View file

@ -19,6 +19,8 @@
#
# Author(s): Adam Williamson <awilliam@redhat.com>
# pylint: disable=c-extension-no-member
"""RPM package installability and reverse-dependency checks using a
repository modification strategy (hence 'rm').
"""
@ -26,24 +28,34 @@ repository modification strategy (hence 'rm').
# Standard libraries
import argparse
import gzip
import hashlib
import json
import lzma
import os
import platform
import subprocess
import sys
import tempfile
import xml.etree.ElementTree as et
from contextlib import contextmanager
from functools import partial
from typing import Iterable
from typing import Any, Generator, Iterable
from urllib.parse import urlparse
import lxml.etree as et
if sys.version_info >= (3, 14):
from compression import zstd
else:
from backports import zstd # pragma: no cover
# type alias for the tuples produced by parse_repoclosure
# can't properly declare this because type statement was only added in
# 3.12, and TypeAlias is deprecated since 3.12 and wasn't in 3.9
DepTuple = tuple[str, str, str]
# Mainly for tests to override to exercise the parser
CHUNKSIZE = 1 << 20
CURLARGS = ("curl", "-s", "-f", "-L", "--retry-delay", "10", "--max-time", "300", "--retry", "5")
# use a fresh temporary cache for each run to avoid collisions between
# runs and polluting the 'real' cache
@ -59,6 +71,7 @@ SUBPCAPTURE = partial(subprocess.run, capture_output=True, text=True, check=Fals
SUBPCAPTCHECK = partial(subprocess.run, capture_output=True, text=True, check=True)
SUBPCHECK = partial(subprocess.run, check=True)
REPOHASHES = {}
SAFEPARSER = et.XMLParser(resolve_entities=False)
def mfind(element: et.Element, string: str, ns: dict) -> et.Element:
@ -122,62 +135,236 @@ def get_file(src: str, dest: str) -> None:
SUBPCHECK(CURLARGS + ("-o", dest, src))
def get_primary(repomdroot: et.Element) -> et.Element:
"""Given a repomd.xml root element, return the element for the
primary data file.
"""
return mfind(repomdroot, "repo:data[@type='primary']", XMLNS)
def download_primary(primary: et.Element, repourl: str, mrepodir: str) -> str:
def download_element(element: et.Element, repourl: str, mrepodir: str) -> str:
"""Given the ET element with information about it, and the URL of
the repo to download from and the local directory to download to,
download and uncompress the primary data file, returning
the filename. Note mrepodir/repodata is assumed to exist.
download the specified data file, returning the filename. Note
mrepodir/repodata is assumed to exist.
"""
primloc = mfind(primary, "repo:location", XMLNS).attrib["href"]
encprimfn = f"{mrepodir}/{primloc}"
get_file(f"{repourl}/{primloc}", encprimfn)
SUBPCHECK(("unzstd", "-q", encprimfn))
os.remove(encprimfn)
return encprimfn.replace(".zst", "")
elemloc = mfind(element, "repo:location", XMLNS).attrib["href"]
encelemfn = f"{mrepodir}/{elemloc}"
get_file(f"{repourl}/{elemloc}", encelemfn)
return encelemfn
def replace_primary(primfn: str, removes: Iterable[str]) -> tuple[str, int, str, int]:
"""Parse the primary data file, remove any packages whose source
package name matches one in removes, and write out a new file
with the correct name (containing its own sha256sum). Return the
checksums and sizes of the new uncompressed and compressed files,
for writing back into the repomd.
# we can stop using Any and use io.Reader / io.Writer once we stop
# caring about Python < 3.14
@contextmanager
def _open_read(path: str) -> Generator[Any, None, None]:
"""Open a repo metadata file for reading, decompressing transparently.
Written by Cursor 2.6.11 + Claude 4.6 Opus.
"""
rddir = os.path.dirname(primfn)
et.register_namespace("", "http://linux.duke.edu/metadata/common")
et.register_namespace("rpm", "http://linux.duke.edu/metadata/rpm")
primtree = et.parse(primfn)
primroot = primtree.getroot()
for pkg in primroot.findall("common:package", XMLNS):
if mfind(pkg, "common:arch", XMLNS).text == "src":
spkg = mfind(pkg, "common:name", XMLNS).text
if path.endswith(".gz"):
with gzip.open(path, "rb") as f:
yield f
elif path.endswith(".xz"):
with lzma.open(path, "rb") as f:
yield f
elif path.endswith((".zst", ".zstd")):
with zstd.open(path, "rb") as f:
yield f
else:
with open(path, "rb") as f:
yield f
@contextmanager
def _open_write(path: str) -> Generator[Any, None, None]:
"""Open a repo metadata file for writing, compressing transparently.
Written by Cursor 2.6.11 + Claude 4.6 Opus.
"""
if path.endswith(".gz"):
with gzip.open(path, "wb", compresslevel=6) as f:
yield f
elif path.endswith(".xz"):
with lzma.open(path, "wb", preset=6) as f:
yield f
elif path.endswith((".zst", ".zstd")):
with zstd.open(path, "wb", level=3) as f:
yield f
else:
with open(path, "wb") as f:
yield f
def _next_chunk(infh: Any, size: int = 0) -> bytes:
"""Read an arbitrarily-sized chunk of data from infh, ensuring it
ends with a > for ease of parsing. Defaults to quite a large size.
Note that using size=1 acts as 'read to end of next tag'.
"""
if not size:
size = CHUNKSIZE
chunk = infh.read(size)
while chunk and not chunk.endswith(b">"):
char = infh.read(1)
if char:
chunk += char
else:
spkg = mfind(mfind(pkg, "common:format", XMLNS), "rpm:sourcerpm", XMLNS).text
if spkg:
spkg = spkg.rsplit("-", 2)[0]
if spkg and spkg in removes:
primroot.remove(pkg)
break
return chunk
tempfn = f"{rddir}/primtemp.xml"
primtree.write(tempfn)
with open(tempfn, "rb") as tempfh:
opensum = hashlib.sha256(tempfh.read()).hexdigest()
opensize = os.path.getsize(tempfn)
SUBPCHECK(("zstd", "-q", tempfn))
with open(f"{tempfn}.zst", "rb") as tempfhz:
csum = hashlib.sha256(tempfhz.read()).hexdigest()
size = os.path.getsize(f"{tempfn}.zst")
os.rename(f"{tempfn}.zst", f"{rddir}/{csum}-primary.xml.zst")
os.remove(tempfn)
os.remove(primfn)
return (csum, size, opensum, opensize)
def _maybe_remove(currpkg: bytes, removes: Iterable[str], flmode: bool) -> str:
"""Given a bytestring representing a single XML package element
starting "<package" and ending "</package>", parse it to find
the (source) package name and compare against removes to decide
whether it should be removed. Returns the pkgid of the package if
it should be removed, empty string if it should not.
"""
# force in namespace definition, ugh. we have to do this or else
# lxml will refuse to parse the fragment. we know it starts with
# '<package', so replace that
nspkg = b'<package xmlns:rpm="http://linux.duke.edu/metadata/rpm"' + currpkg[8:]
parsed = et.fromstring(nspkg, parser=SAFEPARSER)
if flmode:
pkg = parsed.attrib.get("pkgid", "")
# remove package if name matches removes
if pkg in removes:
return pkg
return ""
# primary mode
# find first child element with a pkgid attribute, usually
# checksum. the text of this element is the pkgid
pkgid = mfind(parsed, "./*[@pkgid]", {}).text or ""
# remove package if source package names matches
if mfind(parsed, "arch", {}).text == "src":
# we are a source package, this is our name
spkg = mfind(parsed, "name", {}).text or ""
else:
# we're binary, this is our srpm
spkg = mfind(mfind(parsed, "format", {}), "rpm:sourcerpm", XMLNS).text or ""
if spkg:
# get name from srpm
spkg = spkg.rsplit("-", 2)[0]
if spkg and spkg in removes:
return pkgid
return ""
def parse_xml(
infh: Any, outfh: Any, removes: Iterable[str], flmode: bool
) -> tuple[set[str], str, int]:
"""Parse primary or filelists XML metadata, remove packages
in removes. Works slightly differently in each mode.
"""
removed: set = set()
# parse infh in chunks, pass through non-package content to outfh.
# parse package content one-by-one, using _maybe_remove to decide
# whether to keep (pass through) or drop each package text blob
currpkg = b""
chunk = _next_chunk(infh)
sha = hashlib.sha256()
size = 0
while chunk:
if currpkg:
# look for end of package text
endpos = chunk.find(b"</package>")
if endpos != -1:
# decide whether to remove package
currpkg += chunk[: endpos + 10]
nextpos = endpos + 10
# if we're at the end of the chunk, read in another
# tag and add it, to simplify the next bit
if nextpos >= len(chunk):
chunk += _next_chunk(infh, size=1)
# if next char is \n, include it in the package block
nextchar = chunk[endpos + 10 : endpos + 11]
if nextchar == b"\n":
currpkg += b"\n"
nextpos = endpos + 11
pkgid = _maybe_remove(currpkg, removes, flmode)
if pkgid:
# we should remove it
removed.add(pkgid)
else:
# pass it through, update csum and size
sha.update(currpkg)
size += len(currpkg)
outfh.write(currpkg)
# reset current package text buffer
currpkg = b""
# move to appropriate position in chunk
chunk = chunk[nextpos:]
continue
# package does not end in current chunk, add whole
# chunk to buffer and read next
currpkg += chunk
chunk = _next_chunk(infh)
continue
# we're not in a package block, so find the next package tag
# there is a potential issue with packager tags, but we should
# always encounter a package tag before we encounter a
# packager tag
startpos = chunk.find(b"<package")
if startpos != -1:
# pass through all content before the tag, updating
# csum and size
sha.update(chunk[:startpos])
size += len(chunk[:startpos])
outfh.write(chunk[:startpos])
# initialize the current package buffer with the tag
currpkg = b"<package"
# move to appropriate position in the chunk, note we
# cannot be at end of chunk as it must end with >
chunk = chunk[startpos + 8 :]
continue
# no package tag found in chunk, pass through whole
# chunk, updating csum and size
sha.update(chunk)
size += len(chunk)
outfh.write(chunk)
# read next chunk
chunk = _next_chunk(infh)
continue
return (removed, sha.hexdigest(), size)
# pylint: disable-next=too-many-locals
def replace_packages(
primfn: str, flfn: str, removes: Iterable[str]
) -> tuple[tuple[str, int, str, int], ...]:
"""Parse the primary and filelists data files, remove any packages
whose source package name matches one in removes, and write out new
files with the correct names (containing their own sha256sum).
Return the checksums and sizes of the new uncompressed and
compressed files, for writing back into the repomd. We use raw
line by line text parsing to do this, because these files are huge
and parsing them with ElementTree.parse uses a huge amount of RAM.
Using iterparse would be messier and harder than doing this.
"""
ret = []
primremoved: Iterable[str] = set()
# find the repodata directory
rddir = os.path.dirname(primfn)
# parse both primary and filelists metadata
for fn, typ, flmode in ((primfn, "primary", False), (flfn, "filelists", True)):
if flmode:
# remove the same packages we removed in the prior iteration
toremove = primremoved
else:
# remove packages built from the srpm names in 'removes'
toremove = removes
# get the file extensions
exts = os.path.basename(fn).split(".")[1:]
# construct a temporary filename with the same extensions
tempfn = ".".join([f"{rddir}/{typ}temp"] + exts)
with _open_read(fn) as infh, _open_write(tempfn) as outfh:
# parse from input file to temporary file with inline
# compression, checksumming and size discovery
removed, opensum, opensize = parse_xml(infh, outfh, toremove, flmode=flmode)
if not flmode:
# populate the to-remove set for the next iteration
primremoved = removed
# get compressed checksum and size
with open(tempfn, "rb") as outfh:
csum = hashlib.sha256(outfh.read()).hexdigest()
size = os.path.getsize(tempfn)
# rename output file to expected name with checksum and type
os.rename(tempfn, ".".join([f"{rddir}/{csum}-{typ}"] + exts))
# return compressed and uncompressed sums and sizes
ret.append((csum, size, opensum, opensize))
return tuple(ret)
def get_base_repoclosure(baserepos: Iterable[str], nmbaserepos: Iterable[str]) -> str:
@ -213,26 +400,45 @@ def get_modified_repoclosure(
os.makedirs(f"{mrepodir}/repodata")
repomdfn = f"{mrepodir}/repodata/repomd.xml"
get_file(f"{mrepo}/repodata/repomd.xml", repomdfn)
et.register_namespace("", "http://linux.duke.edu/metadata/repo")
repomdtree = et.parse(repomdfn)
repomdtree = et.parse(repomdfn, parser=SAFEPARSER)
repomdroot = repomdtree.getroot()
primary = get_primary(repomdroot)
primfn = download_primary(primary, mrepo, mrepodir)
csum, size, opensum, opensize = replace_primary(primfn, removes)
# we need to also download and modify filelists, for file
# dependencies that aren't included in primary
filelists = mfind(repomdroot, "repo:data[@type='filelists']", XMLNS)
flfn = download_element(filelists, mrepo, mrepodir)
flexts = os.path.basename(flfn).split(".")[1:]
primary = mfind(repomdroot, "repo:data[@type='primary']", XMLNS)
primfn = download_element(primary, mrepo, mrepodir)
primexts = os.path.basename(primfn).split(".")[1:]
# https://github.com/pylint-dev/pylint/issues/5671#issuecomment-4239834783
primdata, fldata = replace_packages(primfn, flfn, removes) # pylint:disable=W0632
# we also need the module metadata if present, or else
# module packages will be treated as non-module and cause
# false results
modules = repomdroot.find("repo:data[@type='modules']", XMLNS)
if modules is not None:
# setting up a test repo with module metadata is a huge pain
download_element(modules, mrepo, mrepodir) # pragma: no cover
# modify the repomd
mfind(primary, "repo:checksum", XMLNS).text = csum
mfind(primary, "repo:size", XMLNS).text = str(size)
mfind(primary, "repo:open-checksum", XMLNS).text = opensum
mfind(primary, "repo:open-size", XMLNS).text = str(opensize)
mfind(primary, "repo:location", XMLNS).attrib["href"] = f"repodata/{csum}-primary.xml.zst"
# requires Python 3.10:
# notprimary = repomdroot.findall("repo:data[@type]", XMLNS)
for typ, data in ((primary, primdata), (filelists, fldata)):
csum, size, opensum, opensize = data
mfind(typ, "repo:checksum", XMLNS).text = csum
mfind(typ, "repo:size", XMLNS).text = str(size)
mfind(typ, "repo:open-checksum", XMLNS).text = opensum
mfind(typ, "repo:open-size", XMLNS).text = str(opensize)
if typ == primary:
mfind(primary, "repo:location", XMLNS).attrib["href"] = ".".join(
[f"repodata/{csum}-primary"] + primexts
)
else:
mfind(filelists, "repo:location", XMLNS).attrib["href"] = ".".join(
[f"repodata/{csum}-filelists"] + flexts
)
alldata = repomdroot.findall("repo:data[@type]", XMLNS)
notprimary = [data for data in alldata if data is not primary]
for item in notprimary:
notused = [data for data in alldata if data not in [primary, filelists, modules]]
for item in notused:
repomdroot.remove(item)
et.register_namespace("", "http://linux.duke.edu/metadata/repo")
repomdtree.write(repomdfn)
# add the modified repo to the repoclosure command
args.extend(["--repofrompath", f"{hash_repo(mrepo)},{mrepodir}"])
@ -442,7 +648,7 @@ def parse_args() -> argparse.Namespace:
def check_utils() -> None:
"""Check required utilities are installed."""
missing = []
for prog in (("zstd", "-V"), ("dnf", "--version"), ("curl", "-V")):
for prog in (("dnf", "--version"), ("curl", "-V")):
try:
subprocess.run(prog, stdout=subprocess.DEVNULL, check=True)
except FileNotFoundError:

View file

@ -22,6 +22,10 @@
"""Tests for rmdepcheck."""
import glob
import gzip
import io
import lzma
import os
import shutil
import sys
@ -29,6 +33,11 @@ import tempfile
import xml.etree.ElementTree as et
from unittest import mock
if sys.version_info >= (3, 14):
from compression import zstd
else:
from backports import zstd
import pytest
import rmdepcheck
@ -79,61 +88,137 @@ def test_get_file():
assert "This file is" in testfh.read()
def test_download_primary():
def test_download_element():
repomdtree = et.parse(f"{REPOS}/base/repodata/repomd.xml")
repomdroot = repomdtree.getroot()
primary = rmdepcheck.get_primary(repomdroot)
primary = repomdroot.find("repo:data[@type='primary']", rmdepcheck.XMLNS)
assert isinstance(primary, et.Element)
assert primary.attrib == {"type": "primary"}
with tempfile.TemporaryDirectory() as tempdir:
os.makedirs(f"{tempdir}/repodata")
rmdepcheck.download_primary(primary, f"file://{REPOS}/base", tempdir)
rmdepcheck.download_element(primary, f"file://{REPOS}/base", tempdir)
# NOTE: this filename changes any time mkrepos.py is run
assert os.path.exists(
# pylint: disable-next=line-too-long
f"{tempdir}/repodata/54942fbb3415a66f4ca49463d36439407c816b3eed8e30fc43016bd70ac9d456-primary.xml"
f"{tempdir}/repodata/54942fbb3415a66f4ca49463d36439407c816b3eed8e30fc43016bd70ac9d456-primary.xml.zst"
)
def test_parse_xml_empty():
"""Test parse_xml can handle an empty repo. We mostly test it
implicitly, but easiest to test this explicitly.
"""
empty = b"""<?xml version="1.0" encoding="UTF-8"?>
<metadata xmlns="http://linux.duke.edu/metadata/common" xmlns:rpm="http://linux.duke.edu/metadata/rpm" packages="0">
</metadata>
"""
infh = io.BytesIO(empty)
outfh = io.BytesIO()
assert rmdepcheck.parse_xml(infh, outfh, ["somepackage"], False) == (
set(),
"0f9fd176ae380f60833f432e009774b256c2e77fc50740be60eaaf06d49ee004",
168,
)
outfh.seek(0)
assert outfh.read() == empty
@pytest.mark.parametrize("extension", (".xml", ".xml.zst", ".xml.gz", ".xml.xz"))
@pytest.mark.parametrize(
"repotup",
(
(
"binary",
(
"42daebf05f6c3cabe9d029acb3a8056f7fc533cde9820b7388d90acb1f6e7dbe",
1067,
"562981a96ba946cd0f993d180108c4dd107d1a5f24210c5768b19cdd93e8f33a",
7531,
(
"8cba35dd9233f535f90d2c1a6f7c00a8ae8bfcc9b0854f50ef593ab8f685cd7c",
9596,
),
(
"03254e576554e240d9d2434ac9e6f06e71d50adf5e5699acdecd28d0ceafaa37",
1562,
),
),
),
(
"source",
(
"3378e32450503892f65c6ff2a77a451f069146c4284a69f21752e113918da64f",
566,
"7ac52d6a0f3c0314a58f78eff39f4a7b5721127816b1b59d8ef30f4a29e091d9",
1045,
(
"c55ba80f7615e041c9d84b8237a6dd4afb9e56c9d7e1776324a164d4e0b921a1",
1082,
),
(
"97c9e867dca2f9f1d0c1f45e77c5ffb9e6b91e5a76b73240bc162aa4d8b8bd9b",
282,
),
),
),
),
)
def test_replace_primary(repotup):
def test_replace_packages(extension, repotup):
# this exercises some rarely-encountered edge case parser paths
if extension == ".xml":
rmdepcheck.CHUNKSIZE = 1
repo, expected = repotup
with tempfile.TemporaryDirectory() as tempdir:
shutil.copy2(
# binary.xml is an old version of base's primary file
# source.xml is a primary file from a repo with just
# ccc.src and ddd.src packages
f"{TESTDATA}/test_replace_primary_{repo}.xml",
f"{tempdir}/test.xml",
f"{TESTDATA}/test_replace_primary_{repo}{extension}",
f"{tempdir}/testprim{extension}",
)
ret = rmdepcheck.replace_primary(f"{tempdir}/test.xml", "ccc")
shutil.copy2(
f"{TESTDATA}/test_replace_fl_{repo}{extension}",
f"{tempdir}/testfl{extension}",
)
ret = rmdepcheck.replace_packages(
f"{tempdir}/testprim{extension}", f"{tempdir}/testfl{extension}", "ccc"
)
# skip the compressed sum and size as they may change with
# python version
ret = ((ret[0][2], ret[0][3]), (ret[1][2], ret[1][3]))
assert ret == expected
assert os.path.exists(
# pylint: disable-next=line-too-long
f"{tempdir}/{expected[0]}-primary.xml.zst"
gotprim = glob.glob(f"{tempdir}/*primary.xml*")[0]
gotfl = glob.glob(f"{tempdir}/*filelists.xml*")[0]
funcmap = {
".xml": open,
".xml.zst": zstd.open,
".xml.gz": gzip.open,
".xml.xz": lzma.open,
}
with funcmap[extension](gotprim, "rb") as gotpfh:
with open(f"{TESTDATA}/test_replace_primary_{repo}_expected.xml", "rb") as exppfh:
assert gotpfh.read() == exppfh.read()
with funcmap[extension](gotfl, "rb") as gotffh:
with open(f"{TESTDATA}/test_replace_fl_{repo}_expected.xml", "rb") as expffh:
assert gotffh.read() == expffh.read()
def test_replace_packages_minified():
rmdepcheck.CHUNKSIZE = 1
with tempfile.TemporaryDirectory() as tempdir:
shutil.copy2(
# same as source but with all newlines removed
f"{TESTDATA}/test_replace_primary_source_minified.xml",
f"{tempdir}/testprim.xml",
)
shutil.copy2(
# same as source but with all newlines removed
f"{TESTDATA}/test_replace_fl_source_minified.xml",
f"{tempdir}/testfl.xml",
)
rmdepcheck.replace_packages(f"{tempdir}/testprim.xml", f"{tempdir}/testfl.xml", "ccc")
gotprim = glob.glob(f"{tempdir}/*primary.xml*")[0]
gotfl = glob.glob(f"{tempdir}/*filelists.xml*")[0]
with open(gotprim, "rb") as gotpfh:
with open(
f"{TESTDATA}/test_replace_primary_source_minified_expected.xml", "rb"
) as exppfh:
assert gotpfh.read() == exppfh.read()
with open(gotfl, "rb") as gotffh:
with open(f"{TESTDATA}/test_replace_fl_source_minified_expected.xml", "rb") as expffh:
assert gotffh.read() == expffh.read()
def test_get_base_repoclosure():
@ -256,10 +341,10 @@ def test_check_arch(_):
@mock.patch("subprocess.run", autospec=True)
def test_check_utils(mock_run):
rmdepcheck.check_utils()
mock_run.side_effect = [FileNotFoundError, None, FileNotFoundError]
mock_run.side_effect = [None, FileNotFoundError]
with pytest.raises(SystemExit) as excinfo:
rmdepcheck.check_utils()
assert excinfo.value.code == "Please install missing required utilities: zstd curl"
assert excinfo.value.code == "Please install missing required utilities: curl"
@mock.patch("rmdepcheck.check_utils", side_effect=KeyboardInterrupt)

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<filelists xmlns="http://linux.duke.edu/metadata/filelists" packages="10">
<package pkgid="a3d06ac0d14a113c2c40f407b6d9089aab53f2cade404a285d70eef03d54a4fb" name="aaa" arch="x86_64">
<version epoch="0" ver="1.0" rel="1"/>
</package>
<package pkgid="8af0eb8f054b804671a0815bffb22f49a4e8171b54e9610438ae07463bf812ae" name="bbb" arch="x86_64">
<version epoch="0" ver="1.0" rel="1"/>
</package>
<package pkgid="4fc7facc674ab82600e38cc9a3bfe46f5593de27c4acfe27585622c6893820f8" name="ccc" arch="x86_64">
<version epoch="0" ver="1.0" rel="1"/>
</package>
<package pkgid="8299adf0ecb62a00e1b4f3fa1ab8d2288a30f38602642470d61c56b9f2e7dabc" name="ddd" arch="x86_64">
<version epoch="0" ver="1.0" rel="1"/>
</package>
<package pkgid="95ab3d70bdf8b1c2d202c4e04cb7960153fa4b6460a300c838a62fd6b0840632" name="eee" arch="x86_64">
<version epoch="0" ver="1.0" rel="1"/>
</package>
<package pkgid="e26065fed581c695ba8cd3647609b6c2aa6d5bd9faffbb8ad1c2c28e533c87d6" name="fff" arch="x86_64">
<version epoch="0" ver="1.0" rel="1"/>
</package>
<package pkgid="2141e54e9285d94ef2bb53db4e1ae0b9086886a5a07a5f1930dd678682e95ef8" name="ggg" arch="i686">
<version epoch="0" ver="1.0" rel="1"/>
</package>
<package pkgid="7b6f760e650902d0a94ef5fcd480f77a1f6818e9f15cea54a0318be34d364c83" name="ggg" arch="x86_64">
<version epoch="0" ver="1.0" rel="1"/>
</package>
<package pkgid="8280ab6b2707f63add64ad79ee4ff01faf343d6317cadd6f18f4aa745abf145c" name="hhh" arch="i686">
<version epoch="0" ver="1.0" rel="1"/>
</package>
<package pkgid="f0aa917356d9c3cba05c2b346d5a73e5ab919b3df9d3417731c2a25cf3d5ae80" name="hhh" arch="x86_64">
<version epoch="0" ver="1.0" rel="1"/>
</package>
</filelists>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<filelists xmlns="http://linux.duke.edu/metadata/filelists" packages="10">
<package pkgid="a3d06ac0d14a113c2c40f407b6d9089aab53f2cade404a285d70eef03d54a4fb" name="aaa" arch="x86_64">
<version epoch="0" ver="1.0" rel="1"/>
</package>
<package pkgid="8af0eb8f054b804671a0815bffb22f49a4e8171b54e9610438ae07463bf812ae" name="bbb" arch="x86_64">
<version epoch="0" ver="1.0" rel="1"/>
</package>
<package pkgid="8299adf0ecb62a00e1b4f3fa1ab8d2288a30f38602642470d61c56b9f2e7dabc" name="ddd" arch="x86_64">
<version epoch="0" ver="1.0" rel="1"/>
</package>
<package pkgid="95ab3d70bdf8b1c2d202c4e04cb7960153fa4b6460a300c838a62fd6b0840632" name="eee" arch="x86_64">
<version epoch="0" ver="1.0" rel="1"/>
</package>
<package pkgid="e26065fed581c695ba8cd3647609b6c2aa6d5bd9faffbb8ad1c2c28e533c87d6" name="fff" arch="x86_64">
<version epoch="0" ver="1.0" rel="1"/>
</package>
<package pkgid="2141e54e9285d94ef2bb53db4e1ae0b9086886a5a07a5f1930dd678682e95ef8" name="ggg" arch="i686">
<version epoch="0" ver="1.0" rel="1"/>
</package>
<package pkgid="7b6f760e650902d0a94ef5fcd480f77a1f6818e9f15cea54a0318be34d364c83" name="ggg" arch="x86_64">
<version epoch="0" ver="1.0" rel="1"/>
</package>
<package pkgid="8280ab6b2707f63add64ad79ee4ff01faf343d6317cadd6f18f4aa745abf145c" name="hhh" arch="i686">
<version epoch="0" ver="1.0" rel="1"/>
</package>
<package pkgid="f0aa917356d9c3cba05c2b346d5a73e5ab919b3df9d3417731c2a25cf3d5ae80" name="hhh" arch="x86_64">
<version epoch="0" ver="1.0" rel="1"/>
</package>
</filelists>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<filelists xmlns="http://linux.duke.edu/metadata/filelists" packages="2">
<package pkgid="04f2d544eba701ba69cd064a6b35ca4854497674abfc76b5acd05cc1b597b73a" name="ccc" arch="src">
<version epoch="0" ver="1.0" rel="1"/>
</package>
<package pkgid="8e0fa37fb664f3160595c10bae5031a07894681d35556813adc087f1bcdb5b03" name="ddd" arch="src">
<version epoch="0" ver="1.0" rel="1"/>
</package>
</filelists>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<filelists xmlns="http://linux.duke.edu/metadata/filelists" packages="2">
<package pkgid="8e0fa37fb664f3160595c10bae5031a07894681d35556813adc087f1bcdb5b03" name="ddd" arch="src">
<version epoch="0" ver="1.0" rel="1"/>
</package>
</filelists>

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><filelists xmlns="http://linux.duke.edu/metadata/filelists" packages="2"><package pkgid="04f2d544eba701ba69cd064a6b35ca4854497674abfc76b5acd05cc1b597b73a" name="ccc" arch="src"> <version epoch="0" ver="1.0" rel="1"/></package><package pkgid="8e0fa37fb664f3160595c10bae5031a07894681d35556813adc087f1bcdb5b03" name="ddd" arch="src"> <version epoch="0" ver="1.0" rel="1"/></package></filelists>

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><filelists xmlns="http://linux.duke.edu/metadata/filelists" packages="2"><package pkgid="8e0fa37fb664f3160595c10bae5031a07894681d35556813adc087f1bcdb5b03" name="ddd" arch="src"> <version epoch="0" ver="1.0" rel="1"/></package></filelists>

View file

@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata xmlns="http://linux.duke.edu/metadata/common" xmlns:rpm="http://linux.duke.edu/metadata/rpm" packages="8">
<metadata xmlns="http://linux.duke.edu/metadata/common" xmlns:rpm="http://linux.duke.edu/metadata/rpm" packages="10">
<package type="rpm">
<name>aaa</name>
<arch>x86_64</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">7188c68d3407d04571b70e3a8d3c8e6329c34d2dd7af8a6c7364a0b29c991cec</checksum>
<checksum type="sha256" pkgid="YES">a3d06ac0d14a113c2c40f407b6d9089aab53f2cade404a285d70eef03d54a4fb</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1749998096" build="1749998095"/>
<time file="1774488262" build="1774488261"/>
<size package="6329" installed="0" archive="124"/>
<location href="aaa-1.0-1.x86_64.rpm"/>
<format>
@ -29,12 +29,12 @@
<name>bbb</name>
<arch>x86_64</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">3e5425656c6bbfda7b1d707ea39340db1220e9c0618458c4e583a49d060f789b</checksum>
<checksum type="sha256" pkgid="YES">8af0eb8f054b804671a0815bffb22f49a4e8171b54e9610438ae07463bf812ae</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1749998096" build="1749998095"/>
<time file="1774488262" build="1774488261"/>
<size package="6341" installed="0" archive="124"/>
<location href="bbb-1.0-1.x86_64.rpm"/>
<format>
@ -49,7 +49,7 @@
<rpm:entry name="bbb(x86-64)" flags="EQ" epoch="0" ver="1.0" rel="1"/>
</rpm:provides>
<rpm:requires>
<rpm:entry name="aaa" flags="EQ" epoch="0" ver="1.0"/>
<rpm:entry name="aaa" flags="LT" epoch="0" ver="3.0"/>
</rpm:requires>
</format>
</package>
@ -57,12 +57,12 @@
<name>ccc</name>
<arch>x86_64</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">3c810c039112c6d626a9dd628b38af6cbcb387c710b702d51028f6c215c34ad6</checksum>
<checksum type="sha256" pkgid="YES">4fc7facc674ab82600e38cc9a3bfe46f5593de27c4acfe27585622c6893820f8</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1749998096" build="1749998095"/>
<time file="1774488262" build="1774488261"/>
<size package="6329" installed="0" archive="124"/>
<location href="ccc-1.0-1.x86_64.rpm"/>
<format>
@ -82,12 +82,12 @@
<name>ddd</name>
<arch>x86_64</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">7694e747a24fa368cfea4f54b074f9504b3d0cc3fed6b040fd8932d7bb9ac42e</checksum>
<checksum type="sha256" pkgid="YES">8299adf0ecb62a00e1b4f3fa1ab8d2288a30f38602642470d61c56b9f2e7dabc</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1749998096" build="1749998096"/>
<time file="1774488262" build="1774488261"/>
<size package="6341" installed="0" archive="124"/>
<location href="ddd-1.0-1.x86_64.rpm"/>
<format>
@ -102,45 +102,20 @@
<rpm:entry name="ddd(x86-64)" flags="EQ" epoch="0" ver="1.0" rel="1"/>
</rpm:provides>
<rpm:requires>
<rpm:entry name="ccc" flags="EQ" epoch="0" ver="2.0"/>
<rpm:entry name="ccc" flags="EQ" epoch="0" ver="3.0"/>
</rpm:requires>
</format>
</package>
<package type="rpm">
<name>eee</name>
<arch>i686</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">2f12f8ac088158a42338af348f15cdd27dc9564822602af3ff9ea6c3bf9b5acf</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1749998096" build="1749998096"/>
<size package="6273" installed="0" archive="124"/>
<location href="eee-1.0-1.i686.rpm"/>
<format>
<rpm:license>GPL</rpm:license>
<rpm:vendor>Fedora Project</rpm:vendor>
<rpm:group>Applications/Productivity</rpm:group>
<rpm:buildhost>toolbx</rpm:buildhost>
<rpm:sourcerpm>eee-1.0-1.src.rpm</rpm:sourcerpm>
<rpm:header-range start="4504" end="6229"/>
<rpm:provides>
<rpm:entry name="eee" flags="EQ" epoch="0" ver="1.0" rel="1"/>
<rpm:entry name="eee(x86-32)" flags="EQ" epoch="0" ver="1.0" rel="1"/>
</rpm:provides>
</format>
</package>
<package type="rpm">
<name>eee</name>
<arch>x86_64</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">ad3f753e2debe9e0837515fdcf1cf80ced733bbf5fb430f4dc4b0496bc3d69c8</checksum>
<checksum type="sha256" pkgid="YES">95ab3d70bdf8b1c2d202c4e04cb7960153fa4b6460a300c838a62fd6b0840632</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1749998096" build="1749998096"/>
<time file="1774488262" build="1774488262"/>
<size package="6329" installed="0" archive="124"/>
<location href="eee-1.0-1.x86_64.rpm"/>
<format>
@ -156,44 +131,16 @@
</rpm:provides>
</format>
</package>
<package type="rpm">
<name>fff</name>
<arch>i686</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">fbd2c3c44712e41447f9119811df6313be5b57286bc0679f069667b9865f8dc8</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1749998096" build="1749998096"/>
<size package="6285" installed="0" archive="124"/>
<location href="fff-1.0-1.i686.rpm"/>
<format>
<rpm:license>GPL</rpm:license>
<rpm:vendor>Fedora Project</rpm:vendor>
<rpm:group>Applications/Productivity</rpm:group>
<rpm:buildhost>toolbx</rpm:buildhost>
<rpm:sourcerpm>fff-1.0-1.src.rpm</rpm:sourcerpm>
<rpm:header-range start="4504" end="6241"/>
<rpm:provides>
<rpm:entry name="fff" flags="EQ" epoch="0" ver="1.0" rel="1"/>
<rpm:entry name="fff(x86-32)" flags="EQ" epoch="0" ver="1.0" rel="1"/>
</rpm:provides>
<rpm:requires>
<rpm:entry name="eee" flags="EQ" epoch="0" ver="1.0"/>
</rpm:requires>
</format>
</package>
<package type="rpm">
<name>fff</name>
<arch>x86_64</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">47e7d437c69d68b5f4b8790b20854ad01847d48ab01019011a72d99341498f86</checksum>
<checksum type="sha256" pkgid="YES">e26065fed581c695ba8cd3647609b6c2aa6d5bd9faffbb8ad1c2c28e533c87d6</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1749998096" build="1749998096"/>
<time file="1774488262" build="1774488262"/>
<size package="6341" installed="0" archive="124"/>
<location href="fff-1.0-1.x86_64.rpm"/>
<format>
@ -212,4 +159,110 @@
</rpm:requires>
</format>
</package>
<package type="rpm">
<name>ggg</name>
<arch>i686</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">2141e54e9285d94ef2bb53db4e1ae0b9086886a5a07a5f1930dd678682e95ef8</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1774488262" build="1774488262"/>
<size package="6273" installed="0" archive="124"/>
<location href="ggg-1.0-1.i686.rpm"/>
<format>
<rpm:license>GPL</rpm:license>
<rpm:vendor>Fedora Project</rpm:vendor>
<rpm:group>Applications/Productivity</rpm:group>
<rpm:buildhost>toolbx</rpm:buildhost>
<rpm:sourcerpm>ggg-1.0-1.src.rpm</rpm:sourcerpm>
<rpm:header-range start="4504" end="6229"/>
<rpm:provides>
<rpm:entry name="ggg" flags="EQ" epoch="0" ver="1.0" rel="1"/>
<rpm:entry name="ggg(x86-32)" flags="EQ" epoch="0" ver="1.0" rel="1"/>
</rpm:provides>
</format>
</package>
<package type="rpm">
<name>ggg</name>
<arch>x86_64</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">7b6f760e650902d0a94ef5fcd480f77a1f6818e9f15cea54a0318be34d364c83</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1774488262" build="1774488262"/>
<size package="6329" installed="0" archive="124"/>
<location href="ggg-1.0-1.x86_64.rpm"/>
<format>
<rpm:license>GPL</rpm:license>
<rpm:vendor>Fedora Project</rpm:vendor>
<rpm:group>Applications/Productivity</rpm:group>
<rpm:buildhost>toolbx</rpm:buildhost>
<rpm:sourcerpm>ggg-1.0-1.src.rpm</rpm:sourcerpm>
<rpm:header-range start="4504" end="6285"/>
<rpm:provides>
<rpm:entry name="ggg" flags="EQ" epoch="0" ver="1.0" rel="1"/>
<rpm:entry name="ggg(x86-64)" flags="EQ" epoch="0" ver="1.0" rel="1"/>
</rpm:provides>
</format>
</package>
<package type="rpm">
<name>hhh</name>
<arch>i686</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">8280ab6b2707f63add64ad79ee4ff01faf343d6317cadd6f18f4aa745abf145c</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1774488262" build="1774488262"/>
<size package="6289" installed="0" archive="124"/>
<location href="hhh-1.0-1.i686.rpm"/>
<format>
<rpm:license>GPL</rpm:license>
<rpm:vendor>Fedora Project</rpm:vendor>
<rpm:group>Applications/Productivity</rpm:group>
<rpm:buildhost>toolbx</rpm:buildhost>
<rpm:sourcerpm>hhh-1.0-1.src.rpm</rpm:sourcerpm>
<rpm:header-range start="4504" end="6245"/>
<rpm:provides>
<rpm:entry name="hhh" flags="EQ" epoch="0" ver="1.0" rel="1"/>
<rpm:entry name="hhh(x86-32)" flags="EQ" epoch="0" ver="1.0" rel="1"/>
</rpm:provides>
<rpm:requires>
<rpm:entry name="ggg(x86-32)"/>
</rpm:requires>
</format>
</package>
<package type="rpm">
<name>hhh</name>
<arch>x86_64</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">f0aa917356d9c3cba05c2b346d5a73e5ab919b3df9d3417731c2a25cf3d5ae80</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1774488262" build="1774488262"/>
<size package="6345" installed="0" archive="124"/>
<location href="hhh-1.0-1.x86_64.rpm"/>
<format>
<rpm:license>GPL</rpm:license>
<rpm:vendor>Fedora Project</rpm:vendor>
<rpm:group>Applications/Productivity</rpm:group>
<rpm:buildhost>toolbx</rpm:buildhost>
<rpm:sourcerpm>hhh-1.0-1.src.rpm</rpm:sourcerpm>
<rpm:header-range start="4504" end="6301"/>
<rpm:provides>
<rpm:entry name="hhh" flags="EQ" epoch="0" ver="1.0" rel="1"/>
<rpm:entry name="hhh(x86-64)" flags="EQ" epoch="0" ver="1.0" rel="1"/>
</rpm:provides>
<rpm:requires>
<rpm:entry name="ggg(x86-64)"/>
</rpm:requires>
</format>
</package>
</metadata>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,243 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata xmlns="http://linux.duke.edu/metadata/common" xmlns:rpm="http://linux.duke.edu/metadata/rpm" packages="10">
<package type="rpm">
<name>aaa</name>
<arch>x86_64</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">a3d06ac0d14a113c2c40f407b6d9089aab53f2cade404a285d70eef03d54a4fb</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1774488262" build="1774488261"/>
<size package="6329" installed="0" archive="124"/>
<location href="aaa-1.0-1.x86_64.rpm"/>
<format>
<rpm:license>GPL</rpm:license>
<rpm:vendor>Fedora Project</rpm:vendor>
<rpm:group>Applications/Productivity</rpm:group>
<rpm:buildhost>toolbx</rpm:buildhost>
<rpm:sourcerpm>aaa-1.0-1.src.rpm</rpm:sourcerpm>
<rpm:header-range start="4504" end="6285"/>
<rpm:provides>
<rpm:entry name="aaa" flags="EQ" epoch="0" ver="1.0" rel="1"/>
<rpm:entry name="aaa(x86-64)" flags="EQ" epoch="0" ver="1.0" rel="1"/>
</rpm:provides>
</format>
</package>
<package type="rpm">
<name>bbb</name>
<arch>x86_64</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">8af0eb8f054b804671a0815bffb22f49a4e8171b54e9610438ae07463bf812ae</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1774488262" build="1774488261"/>
<size package="6341" installed="0" archive="124"/>
<location href="bbb-1.0-1.x86_64.rpm"/>
<format>
<rpm:license>GPL</rpm:license>
<rpm:vendor>Fedora Project</rpm:vendor>
<rpm:group>Applications/Productivity</rpm:group>
<rpm:buildhost>toolbx</rpm:buildhost>
<rpm:sourcerpm>bbb-1.0-1.src.rpm</rpm:sourcerpm>
<rpm:header-range start="4504" end="6297"/>
<rpm:provides>
<rpm:entry name="bbb" flags="EQ" epoch="0" ver="1.0" rel="1"/>
<rpm:entry name="bbb(x86-64)" flags="EQ" epoch="0" ver="1.0" rel="1"/>
</rpm:provides>
<rpm:requires>
<rpm:entry name="aaa" flags="LT" epoch="0" ver="3.0"/>
</rpm:requires>
</format>
</package>
<package type="rpm">
<name>ddd</name>
<arch>x86_64</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">8299adf0ecb62a00e1b4f3fa1ab8d2288a30f38602642470d61c56b9f2e7dabc</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1774488262" build="1774488261"/>
<size package="6341" installed="0" archive="124"/>
<location href="ddd-1.0-1.x86_64.rpm"/>
<format>
<rpm:license>GPL</rpm:license>
<rpm:vendor>Fedora Project</rpm:vendor>
<rpm:group>Applications/Productivity</rpm:group>
<rpm:buildhost>toolbx</rpm:buildhost>
<rpm:sourcerpm>ddd-1.0-1.src.rpm</rpm:sourcerpm>
<rpm:header-range start="4504" end="6297"/>
<rpm:provides>
<rpm:entry name="ddd" flags="EQ" epoch="0" ver="1.0" rel="1"/>
<rpm:entry name="ddd(x86-64)" flags="EQ" epoch="0" ver="1.0" rel="1"/>
</rpm:provides>
<rpm:requires>
<rpm:entry name="ccc" flags="EQ" epoch="0" ver="3.0"/>
</rpm:requires>
</format>
</package>
<package type="rpm">
<name>eee</name>
<arch>x86_64</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">95ab3d70bdf8b1c2d202c4e04cb7960153fa4b6460a300c838a62fd6b0840632</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1774488262" build="1774488262"/>
<size package="6329" installed="0" archive="124"/>
<location href="eee-1.0-1.x86_64.rpm"/>
<format>
<rpm:license>GPL</rpm:license>
<rpm:vendor>Fedora Project</rpm:vendor>
<rpm:group>Applications/Productivity</rpm:group>
<rpm:buildhost>toolbx</rpm:buildhost>
<rpm:sourcerpm>eee-1.0-1.src.rpm</rpm:sourcerpm>
<rpm:header-range start="4504" end="6285"/>
<rpm:provides>
<rpm:entry name="eee" flags="EQ" epoch="0" ver="1.0" rel="1"/>
<rpm:entry name="eee(x86-64)" flags="EQ" epoch="0" ver="1.0" rel="1"/>
</rpm:provides>
</format>
</package>
<package type="rpm">
<name>fff</name>
<arch>x86_64</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">e26065fed581c695ba8cd3647609b6c2aa6d5bd9faffbb8ad1c2c28e533c87d6</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1774488262" build="1774488262"/>
<size package="6341" installed="0" archive="124"/>
<location href="fff-1.0-1.x86_64.rpm"/>
<format>
<rpm:license>GPL</rpm:license>
<rpm:vendor>Fedora Project</rpm:vendor>
<rpm:group>Applications/Productivity</rpm:group>
<rpm:buildhost>toolbx</rpm:buildhost>
<rpm:sourcerpm>fff-1.0-1.src.rpm</rpm:sourcerpm>
<rpm:header-range start="4504" end="6297"/>
<rpm:provides>
<rpm:entry name="fff" flags="EQ" epoch="0" ver="1.0" rel="1"/>
<rpm:entry name="fff(x86-64)" flags="EQ" epoch="0" ver="1.0" rel="1"/>
</rpm:provides>
<rpm:requires>
<rpm:entry name="eee" flags="EQ" epoch="0" ver="1.0"/>
</rpm:requires>
</format>
</package>
<package type="rpm">
<name>ggg</name>
<arch>i686</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">2141e54e9285d94ef2bb53db4e1ae0b9086886a5a07a5f1930dd678682e95ef8</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1774488262" build="1774488262"/>
<size package="6273" installed="0" archive="124"/>
<location href="ggg-1.0-1.i686.rpm"/>
<format>
<rpm:license>GPL</rpm:license>
<rpm:vendor>Fedora Project</rpm:vendor>
<rpm:group>Applications/Productivity</rpm:group>
<rpm:buildhost>toolbx</rpm:buildhost>
<rpm:sourcerpm>ggg-1.0-1.src.rpm</rpm:sourcerpm>
<rpm:header-range start="4504" end="6229"/>
<rpm:provides>
<rpm:entry name="ggg" flags="EQ" epoch="0" ver="1.0" rel="1"/>
<rpm:entry name="ggg(x86-32)" flags="EQ" epoch="0" ver="1.0" rel="1"/>
</rpm:provides>
</format>
</package>
<package type="rpm">
<name>ggg</name>
<arch>x86_64</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">7b6f760e650902d0a94ef5fcd480f77a1f6818e9f15cea54a0318be34d364c83</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1774488262" build="1774488262"/>
<size package="6329" installed="0" archive="124"/>
<location href="ggg-1.0-1.x86_64.rpm"/>
<format>
<rpm:license>GPL</rpm:license>
<rpm:vendor>Fedora Project</rpm:vendor>
<rpm:group>Applications/Productivity</rpm:group>
<rpm:buildhost>toolbx</rpm:buildhost>
<rpm:sourcerpm>ggg-1.0-1.src.rpm</rpm:sourcerpm>
<rpm:header-range start="4504" end="6285"/>
<rpm:provides>
<rpm:entry name="ggg" flags="EQ" epoch="0" ver="1.0" rel="1"/>
<rpm:entry name="ggg(x86-64)" flags="EQ" epoch="0" ver="1.0" rel="1"/>
</rpm:provides>
</format>
</package>
<package type="rpm">
<name>hhh</name>
<arch>i686</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">8280ab6b2707f63add64ad79ee4ff01faf343d6317cadd6f18f4aa745abf145c</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1774488262" build="1774488262"/>
<size package="6289" installed="0" archive="124"/>
<location href="hhh-1.0-1.i686.rpm"/>
<format>
<rpm:license>GPL</rpm:license>
<rpm:vendor>Fedora Project</rpm:vendor>
<rpm:group>Applications/Productivity</rpm:group>
<rpm:buildhost>toolbx</rpm:buildhost>
<rpm:sourcerpm>hhh-1.0-1.src.rpm</rpm:sourcerpm>
<rpm:header-range start="4504" end="6245"/>
<rpm:provides>
<rpm:entry name="hhh" flags="EQ" epoch="0" ver="1.0" rel="1"/>
<rpm:entry name="hhh(x86-32)" flags="EQ" epoch="0" ver="1.0" rel="1"/>
</rpm:provides>
<rpm:requires>
<rpm:entry name="ggg(x86-32)"/>
</rpm:requires>
</format>
</package>
<package type="rpm">
<name>hhh</name>
<arch>x86_64</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">f0aa917356d9c3cba05c2b346d5a73e5ab919b3df9d3417731c2a25cf3d5ae80</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1774488262" build="1774488262"/>
<size package="6345" installed="0" archive="124"/>
<location href="hhh-1.0-1.x86_64.rpm"/>
<format>
<rpm:license>GPL</rpm:license>
<rpm:vendor>Fedora Project</rpm:vendor>
<rpm:group>Applications/Productivity</rpm:group>
<rpm:buildhost>toolbx</rpm:buildhost>
<rpm:sourcerpm>hhh-1.0-1.src.rpm</rpm:sourcerpm>
<rpm:header-range start="4504" end="6301"/>
<rpm:provides>
<rpm:entry name="hhh" flags="EQ" epoch="0" ver="1.0" rel="1"/>
<rpm:entry name="hhh(x86-64)" flags="EQ" epoch="0" ver="1.0" rel="1"/>
</rpm:provides>
<rpm:requires>
<rpm:entry name="ggg(x86-64)"/>
</rpm:requires>
</format>
</package>
</metadata>

View file

@ -4,12 +4,12 @@
<name>ccc</name>
<arch>src</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">81b6e8246bcf98be9d18b0848c70e1614d522939231f08559c80970a6dd367ae</checksum>
<checksum type="sha256" pkgid="YES">04f2d544eba701ba69cd064a6b35ca4854497674abfc76b5acd05cc1b597b73a</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1773779954" build="1773779954"/>
<time file="1775952837" build="1775952837"/>
<size package="6297" installed="0" archive="124"/>
<location href="ccc-1.0-1.src.rpm"/>
<format>
@ -28,12 +28,12 @@
<name>ddd</name>
<arch>src</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">8961c521f38804fd4910310bd4a9bb075cbbde6bbd7fa52523dcd57d2953a4a2</checksum>
<checksum type="sha256" pkgid="YES">8e0fa37fb664f3160595c10bae5031a07894681d35556813adc087f1bcdb5b03</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1773779954" build="1773779954"/>
<time file="1775952837" build="1775952837"/>
<size package="6297" installed="0" archive="124"/>
<location href="ddd-1.0-1.src.rpm"/>
<format>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata xmlns="http://linux.duke.edu/metadata/common" xmlns:rpm="http://linux.duke.edu/metadata/rpm" packages="2">
<package type="rpm">
<name>ddd</name>
<arch>src</arch>
<version epoch="0" ver="1.0" rel="1"/>
<checksum type="sha256" pkgid="YES">8e0fa37fb664f3160595c10bae5031a07894681d35556813adc087f1bcdb5b03</checksum>
<summary>Dummy summary</summary>
<description>This is a dummy description.</description>
<packager>Fedora Project</packager>
<url></url>
<time file="1775952837" build="1775952837"/>
<size package="6297" installed="0" archive="124"/>
<location href="ddd-1.0-1.src.rpm"/>
<format>
<rpm:license>GPL</rpm:license>
<rpm:vendor>Fedora Project</rpm:vendor>
<rpm:group>Applications/Productivity</rpm:group>
<rpm:buildhost>toolbx</rpm:buildhost>
<rpm:sourcerpm>ddd-1.0-1.src.rpm</rpm:sourcerpm>
<rpm:header-range start="4504" end="6253"/>
<rpm:provides>
<rpm:entry name="ddd" flags="EQ" epoch="0" ver="1.0" rel="1"/>
</rpm:provides>
</format>
</package>
</metadata>

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><metadata xmlns="http://linux.duke.edu/metadata/common" xmlns:rpm="http://linux.duke.edu/metadata/rpm" packages="2"><package type="rpm"> <name>ccc</name> <arch>src</arch> <version epoch="0" ver="1.0" rel="1"/> <checksum type="sha256" pkgid="YES">04f2d544eba701ba69cd064a6b35ca4854497674abfc76b5acd05cc1b597b73a</checksum> <summary>Dummy summary</summary> <description>This is a dummy description.</description> <packager>Fedora Project</packager> <url></url> <time file="1775952837" build="1775952837"/> <size package="6297" installed="0" archive="124"/> <location href="ccc-1.0-1.src.rpm"/> <format> <rpm:license>GPL</rpm:license> <rpm:vendor>Fedora Project</rpm:vendor> <rpm:group>Applications/Productivity</rpm:group> <rpm:buildhost>toolbx</rpm:buildhost> <rpm:sourcerpm>ccc-1.0-1.src.rpm</rpm:sourcerpm> <rpm:header-range start="4504" end="6253"/> <rpm:provides> <rpm:entry name="ccc" flags="EQ" epoch="0" ver="1.0" rel="1"/> </rpm:provides> </format></package><package type="rpm"> <name>ddd</name> <arch>src</arch> <version epoch="0" ver="1.0" rel="1"/> <checksum type="sha256" pkgid="YES">8e0fa37fb664f3160595c10bae5031a07894681d35556813adc087f1bcdb5b03</checksum> <summary>Dummy summary</summary> <description>This is a dummy description.</description> <packager>Fedora Project</packager> <url></url> <time file="1775952837" build="1775952837"/> <size package="6297" installed="0" archive="124"/> <location href="ddd-1.0-1.src.rpm"/> <format> <rpm:license>GPL</rpm:license> <rpm:vendor>Fedora Project</rpm:vendor> <rpm:group>Applications/Productivity</rpm:group> <rpm:buildhost>toolbx</rpm:buildhost> <rpm:sourcerpm>ddd-1.0-1.src.rpm</rpm:sourcerpm> <rpm:header-range start="4504" end="6253"/> <rpm:provides> <rpm:entry name="ddd" flags="EQ" epoch="0" ver="1.0" rel="1"/> </rpm:provides> </format></package></metadata>

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><metadata xmlns="http://linux.duke.edu/metadata/common" xmlns:rpm="http://linux.duke.edu/metadata/rpm" packages="2"><package type="rpm"> <name>ddd</name> <arch>src</arch> <version epoch="0" ver="1.0" rel="1"/> <checksum type="sha256" pkgid="YES">8e0fa37fb664f3160595c10bae5031a07894681d35556813adc087f1bcdb5b03</checksum> <summary>Dummy summary</summary> <description>This is a dummy description.</description> <packager>Fedora Project</packager> <url></url> <time file="1775952837" build="1775952837"/> <size package="6297" installed="0" archive="124"/> <location href="ddd-1.0-1.src.rpm"/> <format> <rpm:license>GPL</rpm:license> <rpm:vendor>Fedora Project</rpm:vendor> <rpm:group>Applications/Productivity</rpm:group> <rpm:buildhost>toolbx</rpm:buildhost> <rpm:sourcerpm>ddd-1.0-1.src.rpm</rpm:sourcerpm> <rpm:header-range start="4504" end="6253"/> <rpm:provides> <rpm:entry name="ddd" flags="EQ" epoch="0" ver="1.0" rel="1"/> </rpm:provides> </format></package></metadata>

View file

@ -1,6 +1,8 @@
black
coverage
diff-cover
lxml
mypy
pylint
pytest-cov
types-lxml