diff options
Diffstat (limited to 'depver_test.py')
-rw-r--r-- | depver_test.py | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/depver_test.py b/depver_test.py new file mode 100644 index 0000000..3dcf596 --- /dev/null +++ b/depver_test.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 +# Adélie Linux architecture package tester +# Ensure all packages depend on latest versions of each other +# +# Copyright © 2019-2020 Adélie Linux team. All rights reserved. +# NCSA license. +# +import argparse +import collections +import functools +import os +import sys + +from apkkit.base.index import Index + +from version import is_older, is_same, verkey, ver_is, APK_OPS +from output import FORMATTERS, No, Partial + +def atomize(spec): + if not any(i in spec for i in APK_OPS): + return spec, None + + for op in APK_OPS: + try: + name, ver = spec.split(op, maxsplit=1) + except ValueError: + continue + + return name, functools.partial(ver_is, op=op, b=ver) + + # Not reached + assert False + +def analyze(url, repos, arch): + pkgs = collections.defaultdict(dict) + newest = {} + providers = collections.defaultdict(list) + + print("Loading " + arch + "...", file=sys.stderr) + + index = [] + for repo in repos: + index.extend(Index(url=url + f"/{repo}/{arch}/APKINDEX.tar.gz").packages) + + for pkg in index: + new = pkg.version + pkgs[pkg.name][new] = pkg + + curr = newest.get(pkg.name, None) + if curr is None: + newest[pkg.name] = new + elif is_older(curr, new): + newest[pkg.name] = new + + providers[pkg.name].append((pkg.name, new)) + for i in pkg.provides: + i = i.split("=", maxsplit=1) + if len(i) == 2: + i, pver = i + else: + i, pver = i[0], new + + curr = newest.get(i, None) + if curr is None: + newest[i] = pver + elif is_older(curr, pver): + newest[i] = pver + + providers[i].append((pkg.name, pver)) + + yield ["arch", "package", "version", "issue"] + + for name in sorted(pkgs.keys()): + # DON'T use newest[] here. It is possible that a package + # provides= another package's name with a newer version + ver = sorted(pkgs[name].keys(), key=verkey)[-1] + pkg = pkgs[name][ver] + + for dep in pkg.depends: + if dep.startswith("!"): + continue + + spec = dep + dep, constraint = atomize(dep) + + if dep in pkg.provides: + continue + if dep not in providers: + yield [arch, name, ver, No(f"Missing {spec}")] + continue + + if constraint: + for provider, pver in providers[dep]: + if constraint(pver): + break + else: + yield [arch, name, ver, No(f"Missing {spec}")] + + for _, pver in providers[dep]: + if is_same(pver, newest[dep]): + break + else: + yield [arch, name, ver, Partial(f"Old {spec}")] + +if __name__ == "__main__": + opts = argparse.ArgumentParser( + description="""Scan the REPO/ARCH repositories under URL and + look for outdated or missing dependencies as required by the + most recent version of each package. This script examines both + main packages and subpackages. + + Be sure to include any repositories on which the repository of + interest depends, otherwise a lot of dependencies will be + incorrectly marked as missing.""", + ) + opts.add_argument( + "-f", "--format", choices=FORMATTERS.keys(), + default="pretty" if os.isatty(sys.stdout.fileno()) else "tab", + help="display format", + ) + opts.add_argument( + "url", metavar="URL", + help="base URL (no repository or arch)", + ) + opts.add_argument( + "repos", metavar="REPOS", + help="repositories (comma separated)", + ) + opts.add_argument( + "arches", metavar="ARCHES", + help="architectures (comma separated)", + ) + opts = opts.parse_args() + opts.repos = opts.repos.split(",") + opts.arches = opts.arches.split(",") + + for arch in opts.arches: + # FIXME: with html, wrapping will repeat for > 1 arch + FORMATTERS[opts.format]( + opts, + analyze(opts.url, opts.repos, arch), + ) |