Initial version of rmdepcheck
Signed-off-by: Adam Williamson <awilliam@redhat.com>
This commit is contained in:
commit
ec12468a08
69 changed files with 1295 additions and 0 deletions
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
**/*.pyc
|
||||
**/*.pyo
|
||||
__pycache__/
|
||||
build/
|
||||
dist/
|
||||
rmdepcheck.egg-info/
|
||||
.cache/
|
||||
.eggs/
|
||||
.tox/
|
||||
.coverage
|
||||
coverage.xml
|
||||
87
README.md
Normal file
87
README.md
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
# rmdepcheck
|
||||
|
||||
rmdepcheck is an RPM dependency check tool based on a repository metadata modification approach.
|
||||
It works by comparing a checked repository to one or more base repositories. First, checks are run
|
||||
on the base repositories as-is. Next, modified copies of the base repositories' metadata is
|
||||
created, with all packages from the same source RPM(s) as the package(s) in the checked
|
||||
repositories removed. Finally, checks are run again on the modified base repositories, with the
|
||||
checked repositories available to the dependency solver. The results of the two runs are compared.
|
||||
New failures should indicate problems introduced by the checked repositories. Also, some relevant
|
||||
checks are run on the checked repositories with reference to the modified base repositories.
|
||||
|
||||
Optionally, additional base repositories can be specified which will not be modified, and
|
||||
additional new repositories can be specified which will not be checked directly. The former is
|
||||
intended for testing scenarios like stable Fedora releases, which have a frozen release repository
|
||||
which is never modified, and an updates repository which is updated. The latter is intended for
|
||||
multilib scenarios; it may be desirable to use such an additional repository for packages for
|
||||
the multilib arch(es), if e.g. installability of these should not be tested directly.
|
||||
|
||||
## Requirements
|
||||
|
||||
rmdepcheck has no run-time Python dependencies outside the standard library. However, it requires
|
||||
several command-line utilities:
|
||||
|
||||
* dnf
|
||||
* zstd
|
||||
* curl
|
||||
|
||||
It checks for these, and will exit early with an error if any of them is not found. rmdepcheck
|
||||
is written primarily for Red Hat-family distributions, but should in theory be usable anywhere
|
||||
these utilities can be installed (and forward slashes act as directory separators).
|
||||
|
||||
## Installation
|
||||
|
||||
Installation of rmdepcheck is entirely optional, it can be run just as well directly from the
|
||||
repository. Otherwise, rmdepcheck uses setuptools for installation and is PEP 518-compliant. You
|
||||
can build and install with e.g. the `build` module and `pip`. rmdepcheck can also be installed
|
||||
directly from PyPI with pip and other tools.
|
||||
|
||||
## Usage
|
||||
|
||||
Simple usage looks like this:
|
||||
```
|
||||
rmdepcheck https://a.base.repo.example/repo,file:///another/baserepo file:///the/testedrepo
|
||||
```
|
||||
|
||||
The to-be-modified base repositories are specified as a comma-separated list. Repositories are
|
||||
always specified as URLs. Only file:// , http:// and https:// URLs are accepted.
|
||||
|
||||
For more complex usage, see `rmdepcheck --help`.
|
||||
|
||||
## License
|
||||
|
||||
rmdepcheck is released under the [GPL](https://www.gnu.org/licenses/gpl.txt), version 3 or later.
|
||||
See `COPYING` and the header of `rmdepcheck.py` itself.
|
||||
|
||||
## Contributing
|
||||
|
||||
Issues and pull requests can be filed in [Codeberg](https://codeberg.org/AdamWill/rmdepcheck).
|
||||
Pull requests must be signed off (use the `-s` git argument). By signing off
|
||||
your pull request you are agreeing to the
|
||||
[Developer's Certificate of Origin](http://developercertificate.org/):
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
0
install.requires
Normal file
0
install.requires
Normal file
56
pyproject.toml
Normal file
56
pyproject.toml
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
[project]
|
||||
name = "rmdepcheck"
|
||||
dynamic = ["dependencies"]
|
||||
version = "1.0.0"
|
||||
description = "RPM installability and repoclosure checks based on repomd.xml modification"
|
||||
readme = "README.md"
|
||||
license = { file="COPYING" }
|
||||
requires-python = ">=3.9"
|
||||
authors = [
|
||||
{ name = "Adam Williamson", email = "awilliam@redhat.com" },
|
||||
]
|
||||
keywords = ["rpm", "dependency", "test", "check", "reverse"]
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
|
||||
"Operating System :: POSIX :: Linux",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14",
|
||||
"Topic :: Internet :: WWW/HTTP",
|
||||
"Topic :: System :: Archiving :: Packaging",
|
||||
"Topic :: Software Development :: Testing",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
rmdepcheck = "rmdepcheck:main"
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=40.6.0", "setuptools-git", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.coverage.run]
|
||||
parallel = true
|
||||
branch = true
|
||||
source_pkgs = ["rmdepcheck"]
|
||||
|
||||
[tool.coverage.paths]
|
||||
source = [".", ".tox/**/site-packages"]
|
||||
|
||||
[tool.coverage.report]
|
||||
show_missing = true
|
||||
|
||||
[tool.black]
|
||||
# don't @ me, Hynek
|
||||
line-length = 100
|
||||
|
||||
[tool.setuptools.dynamic]
|
||||
dependencies = { file = ["install.requires"] }
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
"rmdepcheck" = ["py.typed"]
|
||||
27
release.sh
Executable file
27
release.sh
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
#!/bin/bash
|
||||
|
||||
baddeps=""
|
||||
# check deps
|
||||
python3 -m build.__init__ || baddeps="python3-build"
|
||||
rpm -q twine || baddeps="${baddeps} twine"
|
||||
if [ -n "${baddeps}" ]; then
|
||||
echo "${baddeps} must be installed!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$#" != "1" ]; then
|
||||
echo "Must pass release version!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version=$1
|
||||
name=rmdepcheck
|
||||
sed -i -e "s,version = \".*\",version = \"${version}\", g" pyproject.toml
|
||||
sed -i -e "s,__version__ = \".*\",__version__ = \"${version}\", g" ${name}.py
|
||||
git add pyproject.toml ${name}.py
|
||||
git commit -s -m "Release ${version}"
|
||||
git push
|
||||
git tag -a -m "Release ${version}" ${version}
|
||||
git push origin ${version}
|
||||
python3 -m build .
|
||||
twine upload -r pypi dist/${name}-${version}*
|
||||
347
rmdepcheck.py
Executable file
347
rmdepcheck.py
Executable file
|
|
@ -0,0 +1,347 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
# Copyright Red Hat
|
||||
#
|
||||
# This file is part of rmdepcheck.
|
||||
#
|
||||
# rmdepcheck is free software; you can redistribute it
|
||||
# and/or modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation, either version 3 of
|
||||
# the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Author(s): Adam Williamson <awilliam@redhat.com>
|
||||
|
||||
"""RPM package installability and reverse-dependency checks using a
|
||||
repository modification strategy (hence 'rm').
|
||||
"""
|
||||
|
||||
# Standard libraries
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import xml.etree.ElementTree as et
|
||||
|
||||
from functools import partial
|
||||
from typing import Sequence
|
||||
from urllib.parse import urlparse
|
||||
|
||||
CURLARGS = ("curl", "-s", "--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
|
||||
# pylint: disable-next=consider-using-with
|
||||
DNFTEMP = tempfile.TemporaryDirectory(prefix="rmdepcheck", dir="/var/tmp")
|
||||
DNFARGS = ["dnf", "--setopt", f"cachedir={DNFTEMP.name}", "-q", "--disablerepo=*"]
|
||||
XMLNS = {
|
||||
"repo": "http://linux.duke.edu/metadata/repo",
|
||||
"common": "http://linux.duke.edu/metadata/common",
|
||||
"rpm": "http://linux.duke.edu/metadata/rpm",
|
||||
}
|
||||
SUBPCAPTURE = partial(subprocess.run, capture_output=True, text=True, check=False)
|
||||
SUBPCAPTCHECK = partial(subprocess.run, capture_output=True, text=True, check=True)
|
||||
SUBPCHECK = partial(subprocess.run, check=True)
|
||||
REPOHASHES = {}
|
||||
|
||||
|
||||
def hash_repo(repo: str) -> str:
|
||||
gothash = hashlib.sha256(repo.encode(encoding="utf-8")).hexdigest()[:8]
|
||||
REPOHASHES[gothash] = repo
|
||||
return gothash
|
||||
|
||||
|
||||
def parse_repoclosure(rc: str) -> list[tuple[str, str, str]]:
|
||||
out = []
|
||||
pkg = None
|
||||
for line in rc.splitlines():
|
||||
if line.strip().startswith("package:"):
|
||||
# package, repo
|
||||
elems = line.split()
|
||||
pkg = (elems[1], REPOHASHES.get(elems[3], elems[3]))
|
||||
continue
|
||||
if line.strip().startswith("unresolved deps (") or line.strip().startswith("Error:"):
|
||||
continue
|
||||
# anything else is an unresolved dep
|
||||
# repoclosure doesn't handle rich deps with 'if' conditions
|
||||
if line.strip().startswith("(") and " if " in line:
|
||||
continue
|
||||
if pkg:
|
||||
out.append(pkg + (line.strip(),))
|
||||
return out
|
||||
|
||||
|
||||
def format_rc_errors(errors: list[tuple[str, str, str]]) -> None:
|
||||
pkg = ("", "")
|
||||
for error in errors:
|
||||
if error[:2] != pkg:
|
||||
pkg = error[:2]
|
||||
print(f"package: {error[0]} from {error[1]}")
|
||||
print(f" {error[2]}")
|
||||
|
||||
|
||||
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 repomdroot.find("repo:data[@type='primary']", XMLNS)
|
||||
|
||||
|
||||
def download_primary(primary: 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.
|
||||
"""
|
||||
primloc = primary.find("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", "")
|
||||
|
||||
|
||||
def replace_primary(primfn: str, removes: Sequence[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.
|
||||
"""
|
||||
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):
|
||||
srpm = pkg.find("common:format", XMLNS).find("rpm:sourcerpm", XMLNS).text
|
||||
if srpm.rsplit("-", 2)[0] in removes:
|
||||
primroot.remove(pkg)
|
||||
|
||||
tempfn = f"{rddir}/primtemp.xml"
|
||||
primtree.write(tempfn)
|
||||
# use hashlib? meh
|
||||
opensum = SUBPCAPTCHECK(("sha256sum", tempfn)).stdout.split()[0]
|
||||
opensize = os.path.getsize(tempfn)
|
||||
SUBPCHECK(("zstd", "-q", tempfn))
|
||||
csum = SUBPCAPTCHECK(("sha256sum", f"{tempfn}.zst")).stdout.split()[0]
|
||||
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 get_base_repoclosure(baserepos: Sequence[str], nmbaserepos: Sequence[str]) -> str:
|
||||
cmdargs = DNFARGS + ["repoclosure"]
|
||||
for repo in list(baserepos) + list(nmbaserepos):
|
||||
cmdargs.extend(["--repofrompath", f"{hash_repo(repo)},{repo}"])
|
||||
cmdargs.append("--check")
|
||||
# only check the repos that will be modified
|
||||
cmdargs.append(",".join([hash_repo(baserepo) for baserepo in baserepos]))
|
||||
return SUBPCAPTURE(cmdargs).stdout
|
||||
|
||||
|
||||
# pylint: disable-next=too-many-locals
|
||||
def get_modified_repoclosure(
|
||||
mrepos: Sequence[str], nmrepos: Sequence[str], nrepos: Sequence[str], removes: Sequence[str]
|
||||
) -> str:
|
||||
args = DNFARGS + ["repoclosure"]
|
||||
# place to stash the modified repos
|
||||
with tempfile.TemporaryDirectory() as mreposdir:
|
||||
for mrepo in mrepos:
|
||||
mrepodir = f"{mreposdir}/{hash_repo(mrepo)}"
|
||||
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)
|
||||
repomdroot = repomdtree.getroot()
|
||||
primary = get_primary(repomdroot)
|
||||
primfn = download_primary(primary, mrepo, mrepodir)
|
||||
(csum, size, opensum, opensize) = replace_primary(primfn, removes)
|
||||
|
||||
# modify the repomd
|
||||
primary.find("repo:checksum", XMLNS).text = csum
|
||||
primary.find("repo:size", XMLNS).text = str(size)
|
||||
primary.find("repo:open-checksum", XMLNS).text = opensum
|
||||
primary.find("repo:open-size", XMLNS).text = str(opensize)
|
||||
primary.find("repo:location", XMLNS).attrib["href"] = f"repodata/{csum}-primary.xml.zst"
|
||||
# requires Python 3.10:
|
||||
# notprimary = repomdroot.findall("repo:data[@type]", XMLNS)
|
||||
alldata = repomdroot.findall("repo:data[@type]", XMLNS)
|
||||
notprimary = [data for data in alldata if data is not primary]
|
||||
for item in notprimary:
|
||||
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}"])
|
||||
|
||||
# now add the non-modified base repos
|
||||
for nmrepo in nmrepos:
|
||||
args.extend(["--repofrompath", f"{hash_repo(nmrepo)},{nmrepo}"])
|
||||
|
||||
# now add the new package repos
|
||||
for nrepo in nrepos:
|
||||
args.extend(["--repofrompath", f"{hash_repo(nrepo)},{nrepo}"])
|
||||
|
||||
# finally, add the check arg
|
||||
args.append("--check")
|
||||
args.append(",".join([hash_repo(mrepo) for mrepo in mrepos]))
|
||||
|
||||
ret = SUBPCAPTURE(args).stdout
|
||||
return ret
|
||||
|
||||
|
||||
def get_new_repoclosure(baserepos: Sequence[str], nrepo: str) -> str:
|
||||
cmdargs = DNFARGS + ["repoclosure"]
|
||||
for repo in baserepos:
|
||||
cmdargs.extend(["--repofrompath", f"{hash_repo(repo)},{repo}"])
|
||||
cmdargs.extend(["--repofrompath", f"{hash_repo(nrepo)},{nrepo}", "--check", hash_repo(nrepo)])
|
||||
return SUBPCAPTURE(cmdargs).stdout
|
||||
|
||||
|
||||
def get_source_packages(repos: Sequence[str]) -> set[str]:
|
||||
args = list(DNFARGS)
|
||||
for repo in repos:
|
||||
args.extend(["--repofrompath", f"{hash_repo(repo)},{repo}"])
|
||||
args.extend(["repoquery", "--qf", "%{sourcerpm} "])
|
||||
srpms = SUBPCAPTCHECK(args).stdout.split()
|
||||
return {srpm.rsplit("-", 2)[0] for srpm in srpms}
|
||||
|
||||
|
||||
def url_check(arg: str) -> str:
|
||||
parsed = urlparse(arg)
|
||||
if parsed.scheme in ("http", "https", "file"):
|
||||
return arg
|
||||
raise ValueError(f"Unsupported URL scheme {parsed.scheme} in {arg}")
|
||||
|
||||
|
||||
def comma_url(arg: str) -> list[str]:
|
||||
if arg == "":
|
||||
return []
|
||||
split = arg.split(",")
|
||||
for item in split:
|
||||
try:
|
||||
url_check(item)
|
||||
except ValueError as err:
|
||||
newerr = str(err) + f"from {arg}"
|
||||
raise ValueError(newerr) from err
|
||||
return split
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
"""Parse arguments with argparse."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description=("Reverse dependency check implemented as a repoclosure diff.")
|
||||
)
|
||||
parser.add_argument(
|
||||
"--addrepos",
|
||||
type=comma_url,
|
||||
default="",
|
||||
help="The URL(s) of additional repositories containing new packages to be tested "
|
||||
"(comma-separated). This is mainly intended for multilib cases: i.e. for testing "
|
||||
"x86_64 package sets it should contain the matching i686 packages. It will be "
|
||||
"available to the repoclosure check, but will be ignored by the installability check",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--nmbaserepos",
|
||||
type=comma_url,
|
||||
default="",
|
||||
help="The URL(s) of non-modified base repositories to compare against (comma-separated). "
|
||||
"These repositories *will not* be modified as part of testing. They should be repositories "
|
||||
"whose packages would *not* be replaced with those from the new package repo(s) - e.g. "
|
||||
"the frozen release repository for a stable release, which is never changed",
|
||||
)
|
||||
parser.add_argument(
|
||||
"baserepos",
|
||||
type=comma_url,
|
||||
help="The URL(s) of base repositories to compare against (comma-separated). "
|
||||
"These repositories *will* be modified as part of testing. They should be "
|
||||
"repositories whose packages would be replaced with those from the new package "
|
||||
"repo(s) - e.g. the main repository for a development release, or the updates repository "
|
||||
"for a stable release",
|
||||
)
|
||||
parser.add_argument(
|
||||
"repo",
|
||||
type=url_check,
|
||||
help="The URL of the repo containing the main set of new packages to be tested",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def check_utils() -> None:
|
||||
"""Check required utilities are installed."""
|
||||
missing = []
|
||||
for prog in (("zstd", "-V"), ("dnf", "--version"), ("curl", "-V"), ("sha256sum", "--version")):
|
||||
try:
|
||||
subprocess.run(prog, stdout=subprocess.DEVNULL, check=True)
|
||||
except FileNotFoundError:
|
||||
missing.append(prog[0])
|
||||
if missing:
|
||||
sys.exit("Please install missing required utilities: " + " ".join(missing))
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Main loop."""
|
||||
try:
|
||||
check_utils()
|
||||
exitcode = 0
|
||||
args = parse_args()
|
||||
nrepos = [args.repo] + args.addrepos
|
||||
baserc = parse_repoclosure(get_base_repoclosure(args.baserepos, args.nmbaserepos))
|
||||
|
||||
# find source package(s) of our tested repo(s)
|
||||
# whether to include addrepos is arguable, but should usually be moot
|
||||
sources = get_source_packages(nrepos)
|
||||
|
||||
# get the modified rpmclosure output
|
||||
modrc = parse_repoclosure(
|
||||
get_modified_repoclosure(args.baserepos, args.nmbaserepos, nrepos, sources)
|
||||
)
|
||||
|
||||
# figure out the diffs
|
||||
newerrors = [dep for dep in modrc if dep not in baserc]
|
||||
fixederrors = [dep for dep in baserc if dep not in modrc]
|
||||
|
||||
if fixederrors:
|
||||
print("Fixed dependencies:")
|
||||
format_rc_errors(fixederrors)
|
||||
print("")
|
||||
if newerrors:
|
||||
print("New broken dependencies:")
|
||||
format_rc_errors(newerrors)
|
||||
exitcode += 1
|
||||
|
||||
# get repoclosure on new repo - this is an installability test
|
||||
newrc = parse_repoclosure(get_new_repoclosure(args.baserepos + args.nmbaserepos, args.repo))
|
||||
if newrc:
|
||||
print("Installability problems:")
|
||||
format_rc_errors(newrc)
|
||||
exitcode += 2
|
||||
|
||||
sys.exit(exitcode)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
sys.stderr.write("Interrupted, exiting...\n")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
main()
|
||||
|
||||
# vim: set textwidth=100 ts=8 et sw=4:
|
||||
220
test_rmdepcheck.py
Normal file
220
test_rmdepcheck.py
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
# Copyright Red Hat
|
||||
#
|
||||
# This file is part of rmdepcheck.
|
||||
#
|
||||
# rmdepcheck is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Author: Adam Williamson <awilliam@redhat.com>
|
||||
|
||||
# Unit tests don't always need docstrings.
|
||||
# pylint: disable=missing-function-docstring
|
||||
|
||||
"""Tests for rmdepcheck."""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import xml.etree.ElementTree as et
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
import rmdepcheck
|
||||
|
||||
HERE = os.path.abspath(os.path.dirname(__file__))
|
||||
TESTDATA = f"{HERE}/testdata"
|
||||
REPOS = f"{TESTDATA}/repos"
|
||||
|
||||
|
||||
def test_parse_repoclosure():
|
||||
with open(f"{TESTDATA}/test_parse_repoclosure.txt", "r", encoding="utf-8") as fh:
|
||||
rctext = fh.read()
|
||||
assert rmdepcheck.parse_repoclosure(rctext) == [
|
||||
("python3-wxnatpy-0.4.0-13.fc42.noarch", "baserepo0", "python(abi) = 3.13"),
|
||||
("python3-wxnatpy-0.4.0-13.fc42.noarch", "baserepo0", "python3.13dist(wxpython) >= 4"),
|
||||
("python3-wxnatpy-0.4.0-13.fc42.noarch", "baserepo0", "python3.13dist(xnat) >= 0.3.3"),
|
||||
("python3-x3dh-1.0.4-3.fc43.noarch", "baserepo1", "python3.14dist(pydantic) >= 1.7.4"),
|
||||
]
|
||||
|
||||
|
||||
def test_format_rc_errors(capsys):
|
||||
errs = [
|
||||
("python3-wxnatpy-0.4.0-13.fc42.noarch", "baserepo0", "python(abi) = 3.13"),
|
||||
("python3-wxnatpy-0.4.0-13.fc42.noarch", "baserepo0", "python3.13dist(wxpython) >= 4"),
|
||||
("python3-wxnatpy-0.4.0-13.fc42.noarch", "baserepo0", "python3.13dist(xnat) >= 0.3.3"),
|
||||
("python3-x3dh-1.0.4-3.fc43.noarch", "baserepo1", "python3.14dist(pydantic) >= 1.7.4"),
|
||||
]
|
||||
rmdepcheck.format_rc_errors(errs)
|
||||
captured = capsys.readouterr()
|
||||
with open(f"{TESTDATA}/test_format_rc_errors.txt", "r", encoding="utf-8") as fh:
|
||||
exptext = fh.read()
|
||||
assert captured.out == exptext
|
||||
|
||||
|
||||
def test_get_file():
|
||||
with tempfile.TemporaryDirectory() as tempdir:
|
||||
testfile = f"file://{HERE}/pyproject.toml"
|
||||
rmdepcheck.get_file(testfile, f"{tempdir}/pyproject.toml")
|
||||
with open(f"{tempdir}/pyproject.toml", "r", encoding="utf-8") as testfh:
|
||||
assert "rmdepcheck" in testfh.read()
|
||||
|
||||
|
||||
def test_get_download_primary():
|
||||
repomdtree = et.parse(f"{REPOS}/base/repodata/repomd.xml")
|
||||
repomdroot = repomdtree.getroot()
|
||||
primary = rmdepcheck.get_primary(repomdroot)
|
||||
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)
|
||||
# NOTE: this filename changes any time mkrepos.py is run
|
||||
assert os.path.exists(
|
||||
# pylint: disable-next=line-too-long
|
||||
f"{tempdir}/repodata/0e38861f6ebd9d0808917661ecdb6db855222b294611209cf801664f900c9779-primary.xml"
|
||||
)
|
||||
|
||||
|
||||
def test_replace_primary():
|
||||
with tempfile.TemporaryDirectory() as tempdir:
|
||||
shutil.copy2(
|
||||
# this is an old version of base's primary file
|
||||
f"{TESTDATA}/test_replace_primary.xml",
|
||||
f"{tempdir}/test.xml",
|
||||
)
|
||||
ret = rmdepcheck.replace_primary(f"{tempdir}/test.xml", "ccc")
|
||||
print(tempdir)
|
||||
assert ret == (
|
||||
"42daebf05f6c3cabe9d029acb3a8056f7fc533cde9820b7388d90acb1f6e7dbe",
|
||||
1067,
|
||||
"562981a96ba946cd0f993d180108c4dd107d1a5f24210c5768b19cdd93e8f33a",
|
||||
7531,
|
||||
)
|
||||
assert os.path.exists(
|
||||
# pylint: disable-next=line-too-long
|
||||
f"{tempdir}/42daebf05f6c3cabe9d029acb3a8056f7fc533cde9820b7388d90acb1f6e7dbe-primary.xml.zst"
|
||||
)
|
||||
|
||||
|
||||
def test_get_base_repoclosure():
|
||||
with open(f"{TESTDATA}/test_get_base_repoclosure.txt", "r", encoding="utf-8") as testfh:
|
||||
expected = testfh.read()
|
||||
ret = rmdepcheck.get_base_repoclosure([f"file://{REPOS}/base"], [])
|
||||
assert ret == expected
|
||||
|
||||
|
||||
def test_get_modified_repoclosure():
|
||||
with open(f"{TESTDATA}/test_get_modified_repoclosure.txt", "r", encoding="utf-8") as testfh:
|
||||
expected = testfh.read()
|
||||
ret = rmdepcheck.get_modified_repoclosure(
|
||||
[f"file://{REPOS}/base"], [], [f"file://{REPOS}/new"], ["aaa", "ccc", "eee", "fff", "ggg"]
|
||||
)
|
||||
assert ret == expected
|
||||
|
||||
|
||||
def test_get_new_repoclosure():
|
||||
with open(f"{TESTDATA}/test_get_new_repoclosure.txt", "r", encoding="utf-8") as testfh:
|
||||
expected = testfh.read()
|
||||
ret = rmdepcheck.get_new_repoclosure([f"file://{REPOS}/base"], f"file://{REPOS}/new")
|
||||
assert ret == expected
|
||||
|
||||
|
||||
def test_get_source_packages():
|
||||
sources = rmdepcheck.get_source_packages([f"file://{REPOS}/new"])
|
||||
assert sources == {"111", "aaa", "ccc", "eee", "fff", "ggg"}
|
||||
|
||||
|
||||
def test_url_check():
|
||||
rmdepcheck.url_check("file:///foo/bar")
|
||||
rmdepcheck.url_check("https://www.some.where")
|
||||
rmdepcheck.url_check("http://some.where.insecure")
|
||||
with pytest.raises(ValueError):
|
||||
rmdepcheck.url_check("ftp://1997.called")
|
||||
with pytest.raises(ValueError):
|
||||
rmdepcheck.url_check("whatisthis")
|
||||
|
||||
|
||||
def test_comma_url():
|
||||
rmdepcheck.comma_url("https://www.some.where")
|
||||
rmdepcheck.comma_url("https://www.some.where,file:///foo/bar")
|
||||
with pytest.raises(ValueError):
|
||||
rmdepcheck.comma_url("https://www.some.where,ftp://1997.called")
|
||||
with pytest.raises(ValueError):
|
||||
rmdepcheck.comma_url("ftp://1997.called")
|
||||
with pytest.raises(ValueError):
|
||||
rmdepcheck.comma_url("https://www.some.where,whatisthis")
|
||||
|
||||
|
||||
@mock.patch("subprocess.run", autospec=True)
|
||||
def test_check_utils(mock_run):
|
||||
rmdepcheck.check_utils()
|
||||
mock_run.side_effect = [None, FileNotFoundError, None, FileNotFoundError]
|
||||
with pytest.raises(SystemExit) as excinfo:
|
||||
rmdepcheck.check_utils()
|
||||
assert excinfo.value.code == "Please install missing required utilities: dnf sha256sum"
|
||||
|
||||
|
||||
@mock.patch("rmdepcheck.check_utils", side_effect=KeyboardInterrupt)
|
||||
def test_ctrl_c(_, capsys):
|
||||
with pytest.raises(SystemExit) as excinfo:
|
||||
rmdepcheck.main()
|
||||
assert excinfo.value.code == 1
|
||||
captured = capsys.readouterr()
|
||||
assert captured.err == "Interrupted, exiting...\n"
|
||||
|
||||
|
||||
def test_e2e_null(capsys):
|
||||
"""End-to-end test using an empty repository, to test what happens
|
||||
when no changes are found.
|
||||
"""
|
||||
sys.argv = ["rmdepcheck.py", f"file://{REPOS}/base", f"file://{REPOS}/empty"]
|
||||
with pytest.raises(SystemExit) as excinfo:
|
||||
rmdepcheck.main()
|
||||
assert excinfo.value.code == 0
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == ""
|
||||
|
||||
|
||||
def test_e2e_devel(capsys):
|
||||
"""End-to-end test similar to a Rawhide or Branched case, with
|
||||
a single modified base repo and a single check repo.
|
||||
"""
|
||||
sys.argv = ["rmdepcheck.py", f"file://{REPOS}/base", f"file://{REPOS}/new"]
|
||||
with pytest.raises(SystemExit) as excinfo:
|
||||
rmdepcheck.main()
|
||||
assert excinfo.value.code == 3
|
||||
captured = capsys.readouterr()
|
||||
with open(f"{TESTDATA}/test_e2e_devel.txt", "r", encoding="utf-8") as fh:
|
||||
exptext = fh.read()
|
||||
assert captured.out == exptext
|
||||
|
||||
|
||||
def test_e2e_updates(capsys):
|
||||
"""End-to-end test similar to a stable Fedora release, with one
|
||||
non-modified 'frozen' base repo and one modified updates repo.
|
||||
"""
|
||||
sys.argv = [
|
||||
"rmdepcheck.py",
|
||||
"--nmbaserepos",
|
||||
f"file://{REPOS}/base",
|
||||
f"file://{REPOS}/updates",
|
||||
f"file://{REPOS}/new",
|
||||
]
|
||||
with pytest.raises(SystemExit) as excinfo:
|
||||
rmdepcheck.main()
|
||||
assert excinfo.value.code == 3
|
||||
captured = capsys.readouterr()
|
||||
with open(f"{TESTDATA}/test_e2e_updates.txt", "r", encoding="utf-8") as fh:
|
||||
exptext = fh.read()
|
||||
assert captured.out == exptext
|
||||
BIN
testdata/repos/base/aaa-1.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/base/aaa-1.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/base/bbb-1.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/base/bbb-1.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/base/ccc-1.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/base/ccc-1.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/base/ddd-1.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/base/ddd-1.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/base/eee-1.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/base/eee-1.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/base/fff-1.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/base/fff-1.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/base/ggg-1.0-1.i686.rpm
vendored
Normal file
BIN
testdata/repos/base/ggg-1.0-1.i686.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/base/ggg-1.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/base/ggg-1.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/base/hhh-1.0-1.i686.rpm
vendored
Normal file
BIN
testdata/repos/base/hhh-1.0-1.i686.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/base/hhh-1.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/base/hhh-1.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
28
testdata/repos/base/repodata/repomd.xml
vendored
Normal file
28
testdata/repos/base/repodata/repomd.xml
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<repomd xmlns="http://linux.duke.edu/metadata/repo" xmlns:rpm="http://linux.duke.edu/metadata/rpm">
|
||||
<revision>1750233629</revision>
|
||||
<data type="primary">
|
||||
<checksum type="sha256">0e38861f6ebd9d0808917661ecdb6db855222b294611209cf801664f900c9779</checksum>
|
||||
<open-checksum type="sha256">a6a5fbcca35c3e7ff2172e589ac775726ae086c5273b5d256592bd3aa2a76378</open-checksum>
|
||||
<location href="repodata/0e38861f6ebd9d0808917661ecdb6db855222b294611209cf801664f900c9779-primary.xml.zst"/>
|
||||
<timestamp>1750233629</timestamp>
|
||||
<size>1283</size>
|
||||
<open-size>10594</open-size>
|
||||
</data>
|
||||
<data type="filelists">
|
||||
<checksum type="sha256">3a921e54a99aeb0fa147735c75c8bb6a5da6c9950a6af8e7b7ff295aadd2e5b5</checksum>
|
||||
<open-checksum type="sha256">28ead1ee812ab2c7ede944b76b03f21a4b0595414cb3f87fca3f4038cca373f2</open-checksum>
|
||||
<location href="repodata/3a921e54a99aeb0fa147735c75c8bb6a5da6c9950a6af8e7b7ff295aadd2e5b5-filelists.xml.zst"/>
|
||||
<timestamp>1750233629</timestamp>
|
||||
<size>622</size>
|
||||
<open-size>1722</open-size>
|
||||
</data>
|
||||
<data type="other">
|
||||
<checksum type="sha256">356035bc36bc5c932e218a655fa7e0ff7fde5caf0ccf38bd74fe17e02c58cc7a</checksum>
|
||||
<open-checksum type="sha256">b3df717e78f7972b789c8579c7e91d0a85656d6e40c93d034fe23d6fc82a3d11</open-checksum>
|
||||
<location href="repodata/356035bc36bc5c932e218a655fa7e0ff7fde5caf0ccf38bd74fe17e02c58cc7a-other.xml.zst"/>
|
||||
<timestamp>1750233629</timestamp>
|
||||
<size>708</size>
|
||||
<open-size>2838</open-size>
|
||||
</data>
|
||||
</repomd>
|
||||
BIN
testdata/repos/devel/aaa-1.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/devel/aaa-1.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/devel/bbb-1.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/devel/bbb-1.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/devel/ccc-1.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/devel/ccc-1.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/devel/ddd-1.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/devel/ddd-1.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/devel/eee-1.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/devel/eee-1.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/devel/fff-1.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/devel/fff-1.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/devel/ggg-1.0-1.i686.rpm
vendored
Normal file
BIN
testdata/repos/devel/ggg-1.0-1.i686.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/devel/ggg-1.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/devel/ggg-1.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/devel/hhh-1.0-1.i686.rpm
vendored
Normal file
BIN
testdata/repos/devel/hhh-1.0-1.i686.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/devel/hhh-1.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/devel/hhh-1.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
28
testdata/repos/devel/repodata/repomd.xml
vendored
Normal file
28
testdata/repos/devel/repodata/repomd.xml
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<repomd xmlns="http://linux.duke.edu/metadata/repo" xmlns:rpm="http://linux.duke.edu/metadata/rpm">
|
||||
<revision>1750149328</revision>
|
||||
<data type="primary">
|
||||
<checksum type="sha256">67ab12ab35b8b305f8527ba6116facfd39bb8101176950fba6082a1b74778a0f</checksum>
|
||||
<open-checksum type="sha256">8690a2b2c7d950f4ce564be461441d9117a1da9140b0135b070b864f97895c3a</open-checksum>
|
||||
<location href="repodata/67ab12ab35b8b305f8527ba6116facfd39bb8101176950fba6082a1b74778a0f-primary.xml.zst"/>
|
||||
<timestamp>1750149328</timestamp>
|
||||
<size>1271</size>
|
||||
<open-size>10594</open-size>
|
||||
</data>
|
||||
<data type="filelists">
|
||||
<checksum type="sha256">96baf42ea837d6c2a89cf7a2c52c3e6a665902a92090a94bb6bc86a219617ea0</checksum>
|
||||
<open-checksum type="sha256">02b311e71aa480a771e6a158d55fc13a142e5c2f0191c814fb29009fe1030a45</open-checksum>
|
||||
<location href="repodata/96baf42ea837d6c2a89cf7a2c52c3e6a665902a92090a94bb6bc86a219617ea0-filelists.xml.zst"/>
|
||||
<timestamp>1750149328</timestamp>
|
||||
<size>621</size>
|
||||
<open-size>1722</open-size>
|
||||
</data>
|
||||
<data type="other">
|
||||
<checksum type="sha256">26914f49a900cd21e77aa071ebe224918d47e1cfabf604b80bbd16b351fcea10</checksum>
|
||||
<open-checksum type="sha256">3e7f6535f0fe733e91847525e1cae8b39e3f5cff733d51d112e21eec6c3db5e1</open-checksum>
|
||||
<location href="repodata/26914f49a900cd21e77aa071ebe224918d47e1cfabf604b80bbd16b351fcea10-other.xml.zst"/>
|
||||
<timestamp>1750149328</timestamp>
|
||||
<size>709</size>
|
||||
<open-size>2838</open-size>
|
||||
</data>
|
||||
</repomd>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
28
testdata/repos/empty/repodata/repomd.xml
vendored
Normal file
28
testdata/repos/empty/repodata/repomd.xml
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<repomd xmlns="http://linux.duke.edu/metadata/repo" xmlns:rpm="http://linux.duke.edu/metadata/rpm">
|
||||
<revision>1750233631</revision>
|
||||
<data type="primary">
|
||||
<checksum type="sha256">69a3730a283b85a4b3cff7d04bfde3b2b234f0607ebc17319d7a8d143a8e066e</checksum>
|
||||
<open-checksum type="sha256">e1e2ffd2fb1ee76f87b70750d00ca5677a252b397ab6c2389137a0c33e7b359f</open-checksum>
|
||||
<location href="repodata/69a3730a283b85a4b3cff7d04bfde3b2b234f0607ebc17319d7a8d143a8e066e-primary.xml.zst"/>
|
||||
<timestamp>1750233631</timestamp>
|
||||
<size>123</size>
|
||||
<open-size>167</open-size>
|
||||
</data>
|
||||
<data type="filelists">
|
||||
<checksum type="sha256">9b07d97dc6ececed89aac0650b67bfb292647fe9fbaca48f629465be5f53f82b</checksum>
|
||||
<open-checksum type="sha256">bf9808b81cb2dbc54b4b8e35adc584ddcaa73bd81f7088d73bf7dbbada961310</open-checksum>
|
||||
<location href="repodata/9b07d97dc6ececed89aac0650b67bfb292647fe9fbaca48f629465be5f53f82b-filelists.xml.zst"/>
|
||||
<timestamp>1750233631</timestamp>
|
||||
<size>118</size>
|
||||
<open-size>125</open-size>
|
||||
</data>
|
||||
<data type="other">
|
||||
<checksum type="sha256">6b37cc67608a24beaa81e1191d218f2ffd6b1191dceb5c100bac2e66249d518d</checksum>
|
||||
<open-checksum type="sha256">e0ed5e0054194df036cf09c1a911e15bf2a4e7f26f2a788b6f47d53e80717ccc</open-checksum>
|
||||
<location href="repodata/6b37cc67608a24beaa81e1191d218f2ffd6b1191dceb5c100bac2e66249d518d-other.xml.zst"/>
|
||||
<timestamp>1750233631</timestamp>
|
||||
<size>117</size>
|
||||
<open-size>121</open-size>
|
||||
</data>
|
||||
</repomd>
|
||||
113
testdata/repos/mkrepos.py
vendored
Executable file
113
testdata/repos/mkrepos.py
vendored
Executable file
|
|
@ -0,0 +1,113 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright Red Hat
|
||||
#
|
||||
# This script is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Author: Adam Williamson <awilliam@redhat.com>
|
||||
|
||||
"""Script to create test packages and repos for rpdepcheck testing."""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
import rpmfluff
|
||||
import rpmfluff.yumrepobuild
|
||||
|
||||
# base packages, used in both the 'base' and 'stable' repos
|
||||
pkgaaa = rpmfluff.SimpleRpmBuild("aaa", "1.0", "1", ["x86_64"])
|
||||
pkgbbb = rpmfluff.SimpleRpmBuild("bbb", "1.0", "1", ["x86_64"])
|
||||
pkgccc = rpmfluff.SimpleRpmBuild("ccc", "1.0", "1", ["x86_64"])
|
||||
pkgddd = rpmfluff.SimpleRpmBuild("ddd", "1.0", "1", ["x86_64"])
|
||||
pkgeee = rpmfluff.SimpleRpmBuild("eee", "1.0", "1", ["x86_64"])
|
||||
pkgfff = rpmfluff.SimpleRpmBuild("fff", "1.0", "1", ["x86_64"])
|
||||
pkgggg = rpmfluff.SimpleRpmBuild("ggg", "1.0", "1", ["x86_64", "i686"])
|
||||
pkghhx = rpmfluff.SimpleRpmBuild("hhh", "1.0", "1", ["x86_64"])
|
||||
pkghhi = rpmfluff.SimpleRpmBuild("hhh", "1.0", "1", ["i686"])
|
||||
allbase = (pkgaaa, pkgbbb, pkgccc, pkgddd, pkgeee, pkgfff, pkgggg, pkghhx, pkghhi)
|
||||
# currently works
|
||||
pkgbbb.add_requires("aaa < 3.0")
|
||||
# currently broken
|
||||
pkgddd.add_requires("ccc = 3.0")
|
||||
# currently works, both sides replaced in new repo
|
||||
pkgfff.add_requires("eee = 1.0")
|
||||
# multilib
|
||||
pkghhx.add_requires("ggg(x86-64)")
|
||||
pkghhi.add_requires("ggg(x86-32)")
|
||||
|
||||
# existing stable update packages
|
||||
updaaa = rpmfluff.SimpleRpmBuild("aaa", "2.0", "1", ["x86_64"])
|
||||
updbbb = rpmfluff.SimpleRpmBuild("bbb", "2.0", "1", ["x86_64"])
|
||||
updccc = rpmfluff.SimpleRpmBuild("ccc", "2.0", "1", ["x86_64"])
|
||||
updddd = rpmfluff.SimpleRpmBuild("ddd", "2.0", "1", ["x86_64"])
|
||||
allupd = (updaaa, updbbb, updccc, updddd)
|
||||
# should be broken by the new packages
|
||||
updbbb.add_requires("aaa == 2.0")
|
||||
# should be OK
|
||||
updddd.add_requires("ccc >= 2.0")
|
||||
|
||||
# replaces aaa-1.0-1, breaks bbb
|
||||
newaaa = rpmfluff.SimpleRpmBuild("aaa", "3.0", "1", ["x86_64"])
|
||||
# replaces ccc-1.0-1, fixes ddd
|
||||
newccc = rpmfluff.SimpleRpmBuild("ccc", "3.0", "1", ["x86_64"])
|
||||
# replaces eee, would break fff but we also replace fff below
|
||||
neweee = rpmfluff.SimpleRpmBuild("eee", "3.0", "1", ["x86_64"])
|
||||
newfff = rpmfluff.SimpleRpmBuild("fff", "3.0", "1", ["x86_64"])
|
||||
# replaces ggg-1.0-1, breaks hhh.i686 as there's no i686 here
|
||||
newggg = rpmfluff.SimpleRpmBuild("ggg", "3.0", "1", ["x86_64"])
|
||||
# intentionally broken to check installability
|
||||
new111 = rpmfluff.SimpleRpmBuild("111", "3.0", "1", ["x86_64"])
|
||||
allnew = (newaaa, newccc, neweee, newfff, newggg, new111)
|
||||
newfff.add_requires("eee = 3.0")
|
||||
new111.add_requires("nonexistent")
|
||||
|
||||
for pkg in allbase + allupd + allnew:
|
||||
pkg.addVendor("Fedora Project")
|
||||
pkg.addPackager("Fedora Project")
|
||||
|
||||
base = rpmfluff.yumrepobuild.YumRepoBuild(allbase)
|
||||
basedir = "base"
|
||||
new = rpmfluff.yumrepobuild.YumRepoBuild(allnew)
|
||||
newdir = "new"
|
||||
updates = rpmfluff.yumrepobuild.YumRepoBuild(allupd)
|
||||
upddir = "updates"
|
||||
empty = rpmfluff.yumrepobuild.YumRepoBuild([])
|
||||
empdir = "empty"
|
||||
|
||||
|
||||
def cleanup(repos=True):
|
||||
dirs = [pkgaaa.get_base_dir()]
|
||||
if repos:
|
||||
dirs.extend((basedir, upddir, newdir, empdir))
|
||||
for _dir in dirs:
|
||||
if os.path.isdir(_dir):
|
||||
shutil.rmtree(_dir)
|
||||
|
||||
|
||||
cleanup()
|
||||
|
||||
os.mkdir(basedir)
|
||||
os.mkdir(newdir)
|
||||
os.mkdir(upddir)
|
||||
os.mkdir(empdir)
|
||||
base.repoDir = basedir
|
||||
new.repoDir = newdir
|
||||
updates.repoDir = upddir
|
||||
empty.repoDir = empdir
|
||||
base.make("x86_64", "i686")
|
||||
new.make("x86_64")
|
||||
updates.make("x86_64")
|
||||
empty.make()
|
||||
|
||||
cleanup(repos=False)
|
||||
BIN
testdata/repos/new/111-3.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/new/111-3.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/new/aaa-3.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/new/aaa-3.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/new/ccc-3.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/new/ccc-3.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/new/eee-3.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/new/eee-3.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/new/fff-3.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/new/fff-3.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/new/ggg-3.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/new/ggg-3.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
28
testdata/repos/new/repodata/repomd.xml
vendored
Normal file
28
testdata/repos/new/repodata/repomd.xml
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<repomd xmlns="http://linux.duke.edu/metadata/repo" xmlns:rpm="http://linux.duke.edu/metadata/rpm">
|
||||
<revision>1750233630</revision>
|
||||
<data type="primary">
|
||||
<checksum type="sha256">5ecd0d192609426e7cc61b37bbb51667ddcc125041fa76980002047b732d8652</checksum>
|
||||
<open-checksum type="sha256">25b8466bc077d2eae67c8fef9d2d00917958bb3e3e358a3fa7c24e0c3b4aa30b</open-checksum>
|
||||
<location href="repodata/5ecd0d192609426e7cc61b37bbb51667ddcc125041fa76980002047b732d8652-primary.xml.zst"/>
|
||||
<timestamp>1750233630</timestamp>
|
||||
<size>996</size>
|
||||
<open-size>6332</open-size>
|
||||
</data>
|
||||
<data type="filelists">
|
||||
<checksum type="sha256">840d94ef3a14d1f0b5a51f376cac9a5d26a780bb2aeb260d4906269adb9e5262</checksum>
|
||||
<open-checksum type="sha256">99882a419bc00a1d777393148c848c9e04086ebb881ccd645fc5ce4e45e8871a</open-checksum>
|
||||
<location href="repodata/840d94ef3a14d1f0b5a51f376cac9a5d26a780bb2aeb260d4906269adb9e5262-filelists.xml.zst"/>
|
||||
<timestamp>1750233630</timestamp>
|
||||
<size>458</size>
|
||||
<open-size>1085</open-size>
|
||||
</data>
|
||||
<data type="other">
|
||||
<checksum type="sha256">68cd0412bbf5516d48e81f2030960c3c6783fb587848cdd77f3f71a7ed8f89f5</checksum>
|
||||
<open-checksum type="sha256">1c4247850bc335618dc84ab32ce1e3489e1ff512596480a92344d07406f67a09</open-checksum>
|
||||
<location href="repodata/68cd0412bbf5516d48e81f2030960c3c6783fb587848cdd77f3f71a7ed8f89f5-other.xml.zst"/>
|
||||
<timestamp>1750233630</timestamp>
|
||||
<size>537</size>
|
||||
<open-size>1753</open-size>
|
||||
</data>
|
||||
</repomd>
|
||||
BIN
testdata/repos/updates/aaa-2.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/updates/aaa-2.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/updates/bbb-2.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/updates/bbb-2.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/updates/ccc-2.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/updates/ccc-2.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
BIN
testdata/repos/updates/ddd-2.0-1.x86_64.rpm
vendored
Normal file
BIN
testdata/repos/updates/ddd-2.0-1.x86_64.rpm
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
28
testdata/repos/updates/repodata/repomd.xml
vendored
Normal file
28
testdata/repos/updates/repodata/repomd.xml
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<repomd xmlns="http://linux.duke.edu/metadata/repo" xmlns:rpm="http://linux.duke.edu/metadata/rpm">
|
||||
<revision>1750233631</revision>
|
||||
<data type="primary">
|
||||
<checksum type="sha256">13fd527aa9f63b0e1c421d94a01a212e0a6bf8a8843a40bfd0bb53895272025f</checksum>
|
||||
<open-checksum type="sha256">837008ebe4332da3469b9323f97f48de808ea3ca7c3d651e9547c9ed0e17eeef</open-checksum>
|
||||
<location href="repodata/13fd527aa9f63b0e1c421d94a01a212e0a6bf8a8843a40bfd0bb53895272025f-primary.xml.zst"/>
|
||||
<timestamp>1750233631</timestamp>
|
||||
<size>859</size>
|
||||
<open-size>4359</open-size>
|
||||
</data>
|
||||
<data type="filelists">
|
||||
<checksum type="sha256">9986ef24ccf5a9814ae624cfeb2acb37fa489f75a1bd16ed3000df222b0998e9</checksum>
|
||||
<open-checksum type="sha256">4a8729de01249c4062fc841bf30a23c577ef13a29a2cd0c7e34015b52e48cf8c</open-checksum>
|
||||
<location href="repodata/9986ef24ccf5a9814ae624cfeb2acb37fa489f75a1bd16ed3000df222b0998e9-filelists.xml.zst"/>
|
||||
<timestamp>1750233631</timestamp>
|
||||
<size>374</size>
|
||||
<open-size>765</open-size>
|
||||
</data>
|
||||
<data type="other">
|
||||
<checksum type="sha256">0c7503e45ca77efa17c271e1c4b2177c574f1e9e53ce19a5498e1f3bb566a084</checksum>
|
||||
<open-checksum type="sha256">4c5fe999a1337dc80cf3433aa70cf6f64f3f0f746873f07b22f7eb84e24d8ea6</open-checksum>
|
||||
<location href="repodata/0c7503e45ca77efa17c271e1c4b2177c574f1e9e53ce19a5498e1f3bb566a084-other.xml.zst"/>
|
||||
<timestamp>1750233631</timestamp>
|
||||
<size>450</size>
|
||||
<open-size>1209</open-size>
|
||||
</data>
|
||||
</repomd>
|
||||
12
testdata/test_e2e_devel.txt
vendored
Normal file
12
testdata/test_e2e_devel.txt
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
Fixed dependencies:
|
||||
package: ddd-1.0-1.x86_64 from file:///var/home/adamw/local/rmdepcheck/testdata/repos/base
|
||||
ccc = 3.0
|
||||
|
||||
New broken dependencies:
|
||||
package: bbb-1.0-1.x86_64 from file:///var/home/adamw/local/rmdepcheck/testdata/repos/base
|
||||
aaa < 3.0
|
||||
package: hhh-1.0-1.i686 from file:///var/home/adamw/local/rmdepcheck/testdata/repos/base
|
||||
ggg(x86-32)
|
||||
Installability problems:
|
||||
package: 111-3.0-1.x86_64 from file:///var/home/adamw/local/rmdepcheck/testdata/repos/new
|
||||
nonexistent
|
||||
6
testdata/test_e2e_updates.txt
vendored
Normal file
6
testdata/test_e2e_updates.txt
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
New broken dependencies:
|
||||
package: bbb-2.0-1.x86_64 from file:///var/home/adamw/local/rmdepcheck/testdata/repos/updates
|
||||
aaa = 2.0
|
||||
Installability problems:
|
||||
package: 111-3.0-1.x86_64 from file:///var/home/adamw/local/rmdepcheck/testdata/repos/new
|
||||
nonexistent
|
||||
6
testdata/test_format_rc_errors.txt
vendored
Normal file
6
testdata/test_format_rc_errors.txt
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package: python3-wxnatpy-0.4.0-13.fc42.noarch from baserepo0
|
||||
python(abi) = 3.13
|
||||
python3.13dist(wxpython) >= 4
|
||||
python3.13dist(xnat) >= 0.3.3
|
||||
package: python3-x3dh-1.0.4-3.fc43.noarch from baserepo1
|
||||
python3.14dist(pydantic) >= 1.7.4
|
||||
3
testdata/test_get_base_repoclosure.txt
vendored
Normal file
3
testdata/test_get_base_repoclosure.txt
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
package: ddd-1.0-1.x86_64 from a59f6db1
|
||||
unresolved deps (1):
|
||||
ccc = 3.0
|
||||
6
testdata/test_get_modified_repoclosure.txt
vendored
Normal file
6
testdata/test_get_modified_repoclosure.txt
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package: bbb-1.0-1.x86_64 from a59f6db1
|
||||
unresolved deps (1):
|
||||
aaa < 3.0
|
||||
package: hhh-1.0-1.i686 from a59f6db1
|
||||
unresolved deps (1):
|
||||
ggg(x86-32)
|
||||
3
testdata/test_get_new_repoclosure.txt
vendored
Normal file
3
testdata/test_get_new_repoclosure.txt
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
package: 111-3.0-1.x86_64 from deb30c7b
|
||||
unresolved deps (1):
|
||||
nonexistent
|
||||
11
testdata/test_parse_repoclosure.txt
vendored
Normal file
11
testdata/test_parse_repoclosure.txt
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
haha this is a sneaky evil line for testing muahaha
|
||||
package: python3-wxnatpy-0.4.0-13.fc42.noarch from baserepo0
|
||||
unresolved deps (3):
|
||||
python(abi) = 3.13
|
||||
python3.13dist(wxpython) >= 4
|
||||
python3.13dist(xnat) >= 0.3.3
|
||||
(foo if bar)
|
||||
package: python3-x3dh-1.0.4-3.fc43.noarch from baserepo1
|
||||
unresolved deps (1):
|
||||
python3.14dist(pydantic) >= 1.7.4
|
||||
Error: Repoclosure ended with unresolved dependencies (3148) across 832 packages.
|
||||
215
testdata/test_replace_primary.xml
vendored
Normal file
215
testdata/test_replace_primary.xml
vendored
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
<?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">
|
||||
<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>
|
||||
<summary>Dummy summary</summary>
|
||||
<description>This is a dummy description.</description>
|
||||
<packager>Fedora Project</packager>
|
||||
<url></url>
|
||||
<time file="1749998096" build="1749998095"/>
|
||||
<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">3e5425656c6bbfda7b1d707ea39340db1220e9c0618458c4e583a49d060f789b</checksum>
|
||||
<summary>Dummy summary</summary>
|
||||
<description>This is a dummy description.</description>
|
||||
<packager>Fedora Project</packager>
|
||||
<url></url>
|
||||
<time file="1749998096" build="1749998095"/>
|
||||
<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="EQ" epoch="0" ver="1.0"/>
|
||||
</rpm:requires>
|
||||
</format>
|
||||
</package>
|
||||
<package type="rpm">
|
||||
<name>ccc</name>
|
||||
<arch>x86_64</arch>
|
||||
<version epoch="0" ver="1.0" rel="1"/>
|
||||
<checksum type="sha256" pkgid="YES">3c810c039112c6d626a9dd628b38af6cbcb387c710b702d51028f6c215c34ad6</checksum>
|
||||
<summary>Dummy summary</summary>
|
||||
<description>This is a dummy description.</description>
|
||||
<packager>Fedora Project</packager>
|
||||
<url></url>
|
||||
<time file="1749998096" build="1749998095"/>
|
||||
<size package="6329" installed="0" archive="124"/>
|
||||
<location href="ccc-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>ccc-1.0-1.src.rpm</rpm:sourcerpm>
|
||||
<rpm:header-range start="4504" end="6285"/>
|
||||
<rpm:provides>
|
||||
<rpm:entry name="ccc" flags="EQ" epoch="0" ver="1.0" rel="1"/>
|
||||
<rpm:entry name="ccc(x86-64)" flags="EQ" epoch="0" ver="1.0" rel="1"/>
|
||||
</rpm:provides>
|
||||
</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">7694e747a24fa368cfea4f54b074f9504b3d0cc3fed6b040fd8932d7bb9ac42e</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="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="2.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>
|
||||
<summary>Dummy summary</summary>
|
||||
<description>This is a dummy description.</description>
|
||||
<packager>Fedora Project</packager>
|
||||
<url></url>
|
||||
<time file="1749998096" build="1749998096"/>
|
||||
<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>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>
|
||||
<summary>Dummy summary</summary>
|
||||
<description>This is a dummy description.</description>
|
||||
<packager>Fedora Project</packager>
|
||||
<url></url>
|
||||
<time file="1749998096" build="1749998096"/>
|
||||
<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>
|
||||
</metadata>
|
||||
2
tests.requires
Normal file
2
tests.requires
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
coverage[toml]
|
||||
pytest
|
||||
25
tox.ini
Normal file
25
tox.ini
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
[tox]
|
||||
envlist = py39,py310,py311,py312,py313,py314,ci
|
||||
skip_missing_interpreters = true
|
||||
[testenv]
|
||||
deps =
|
||||
-r{toxinidir}/install.requires
|
||||
-r{toxinidir}/tests.requires
|
||||
commands =
|
||||
coverage run -m pytest {posargs}
|
||||
setenv =
|
||||
PYTHONPATH = {toxinidir}
|
||||
[testenv:ci]
|
||||
sitepackages = false
|
||||
deps =
|
||||
-r{toxinidir}/tox.requires
|
||||
skip_install = true
|
||||
ignore_errors = true
|
||||
commands =
|
||||
black rmdepcheck.py test_rmdepcheck.py testdata/repos/mkrepos.py --check
|
||||
coverage combine
|
||||
coverage report
|
||||
coverage xml
|
||||
diff-cover coverage.xml --fail-under=100
|
||||
diff-quality --violations=pylint --fail-under=100
|
||||
|
||||
5
tox.requires
Normal file
5
tox.requires
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
black
|
||||
coverage
|
||||
diff-cover
|
||||
pylint
|
||||
pytest-cov
|
||||
Loading…
Add table
Add a link
Reference in a new issue