Blame plugins/repoclosure.py

Packit Service 27f74b
# repoclosure.py
Packit Service 27f74b
# DNF plugin adding a command to display a list of unresolved dependencies
Packit Service 27f74b
# for repositories.
Packit Service 27f74b
#
Packit Service 27f74b
# Copyright (C) 2015 Igor Gnatenko
Packit Service 27f74b
#
Packit Service 27f74b
# This copyrighted material is made available to anyone wishing to use,
Packit Service 27f74b
# modify, copy, or redistribute it subject to the terms and conditions of
Packit Service 27f74b
# the GNU General Public License v.2, or (at your option) any later version.
Packit Service 27f74b
# This program is distributed in the hope that it will be useful, but WITHOUT
Packit Service 27f74b
# ANY WARRANTY expressed or implied, including the implied warranties of
Packit Service 27f74b
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
Packit Service 27f74b
# Public License for more details.  You should have received a copy of the
Packit Service 27f74b
# GNU General Public License along with this program; if not, write to the
Packit Service 27f74b
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service 27f74b
# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
Packit Service 27f74b
# source code or documentation are not subject to the GNU General Public
Packit Service 27f74b
# License and may only be used or replicated with the express permission of
Packit Service 27f74b
# Red Hat, Inc.
Packit Service 27f74b
#
Packit Service 27f74b
Packit Service 27f74b
from __future__ import absolute_import
Packit Service 27f74b
from __future__ import unicode_literals
Packit Service 27f74b
from dnfpluginscore import _
Packit Service 27f74b
Packit Service 27f74b
import dnf.cli
Packit Service 27f74b
Packit Service 27f74b
Packit Service 27f74b
class RepoClosure(dnf.Plugin):
Packit Service 27f74b
Packit Service 27f74b
    name = "repoclosure"
Packit Service 27f74b
Packit Service 27f74b
    def __init__(self, base, cli):
Packit Service 27f74b
        super(RepoClosure, self).__init__(base, cli)
Packit Service 27f74b
        if cli is None:
Packit Service 27f74b
            return
Packit Service 27f74b
        cli.register_command(RepoClosureCommand)
Packit Service 27f74b
Packit Service 27f74b
Packit Service 27f74b
class RepoClosureCommand(dnf.cli.Command):
Packit Service 27f74b
    aliases = ("repoclosure",)
Packit Service 27f74b
    summary = _("Display a list of unresolved dependencies for repositories")
Packit Service 27f74b
Packit Service 27f74b
    def configure(self):
Packit Service 27f74b
        demands = self.cli.demands
Packit Service 27f74b
        demands.sack_activation = True
Packit Service 27f74b
        demands.available_repos = True
Packit Service 27f74b
        if self.opts.repo:
Packit Service 27f74b
            for repo in self.base.repos.all():
Packit Service 27f74b
                if repo.id not in self.opts.repo and repo.id not in self.opts.check:
Packit Service 27f74b
                    repo.disable()
Packit Service 27f74b
                else:
Packit Service 27f74b
                    repo.enable()
Packit Service 27f74b
Packit Service 27f74b
    def run(self):
Packit Service 27f74b
        if self.opts.arches:
Packit Service 27f74b
            unresolved = self._get_unresolved(self.opts.arches)
Packit Service 27f74b
        else:
Packit Service 27f74b
            unresolved = self._get_unresolved()
Packit Service 27f74b
        for pkg in sorted(unresolved.keys()):
Packit Service 27f74b
            print("package: {} from {}".format(str(pkg), pkg.reponame))
Packit Service 27f74b
            print("  unresolved deps:")
Packit Service 27f74b
            for dep in unresolved[pkg]:
Packit Service 27f74b
                print("    {}".format(dep))
Packit Service 27f74b
        if len(unresolved) > 0:
Packit Service 27f74b
            msg = _("Repoclosure ended with unresolved dependencies.")
Packit Service 27f74b
            raise dnf.exceptions.Error(msg)
Packit Service 27f74b
Packit Service 27f74b
    def _get_unresolved(self, arch=None):
Packit Service 27f74b
        unresolved = {}
Packit Service 27f74b
        deps = set()
Packit Service 27f74b
Packit Service 27f74b
        # We have two sets of packages, available and to_check:
Packit Service 27f74b
        # * available is the set of packages used to satisfy dependencies
Packit Service 27f74b
        # * to_check is the set of packages we are checking the dependencies of
Packit Service 27f74b
        #
Packit Service 27f74b
        # to_check can be a subset of available if the --arch, --best, --check,
Packit Service 27f74b
        # --newest, or --pkg options are used
Packit Service 27f74b
        #
Packit Service 27f74b
        # --arch:   only packages matching arch are checked
Packit Service 27f74b
        # --best:   available only contains the latest packages per arch across all repos
Packit Service 27f74b
        # --check:  only check packages in the specified repo(s)
Packit Service 27f74b
        # --newest: only consider the latest versions of a package from each repo
Packit Service 27f74b
        # --pkg:    only check the specified packages
Packit Service 27f74b
        #
Packit Service 27f74b
        # Relationship of --best and --newest:
Packit Service 27f74b
        #
Packit Service 27f74b
        # Pkg Set   | Neither |  --best             | --newest        | --best and --newest |
Packit Service 27f74b
        # available | all     | latest in all repos | latest per repo | latest in all repos |
Packit Service 27f74b
        # to_check  | all     | all                 | latest per repo | latest per repo     |
Packit Service 27f74b
Packit Service 27f74b
        if self.opts.newest:
Packit Service 27f74b
            available = self.base.sack.query().filter(empty=True)
Packit Service 27f74b
            to_check = self.base.sack.query().filter(empty=True)
Packit Service 27f74b
            for repo in self.base.repos.iter_enabled():
Packit Service 27f74b
                available = \
Packit Service 27f74b
                    available.union(self.base.sack.query().filter(reponame=repo.id).latest())
Packit Service 27f74b
                to_check = \
Packit Service 27f74b
                    to_check.union(self.base.sack.query().filter(reponame=repo.id).latest())
Packit Service 27f74b
        else:
Packit Service 27f74b
            available = self.base.sack.query().available()
Packit Service 27f74b
            to_check = self.base.sack.query().available()
Packit Service 27f74b
Packit Service 27f74b
        if self.opts.pkglist:
Packit Service 27f74b
            pkglist_q = self.base.sack.query().filter(empty=True)
Packit Service 27f74b
            errors = []
Packit Service 27f74b
            for pkg in self.opts.pkglist:
Packit Service 27f74b
                subj = dnf.subject.Subject(pkg)
Packit Service 27f74b
                pkg_q = to_check.intersection(
Packit Service 27f74b
                    subj.get_best_query(self.base.sack, with_nevra=True,
Packit Service 27f74b
                                        with_provides=False, with_filenames=False))
Packit Service 27f74b
                if pkg_q:
Packit Service 27f74b
                    pkglist_q = pkglist_q.union(pkg_q)
Packit Service 27f74b
                else:
Packit Service 27f74b
                    errors.append(pkg)
Packit Service 27f74b
            if errors:
Packit Service 27f74b
                raise dnf.exceptions.Error(
Packit Service 27f74b
                    _('no package matched: %s') % ', '.join(errors))
Packit Service 27f74b
            to_check = pkglist_q
Packit Service 27f74b
Packit Service 27f74b
        if self.opts.check:
Packit Service 27f74b
            to_check.filterm(reponame=self.opts.check)
Packit Service 27f74b
Packit Service 27f74b
        if arch is not None:
Packit Service 27f74b
            to_check.filterm(arch=arch)
Packit Service 27f74b
Packit Service 27f74b
        if self.base.conf.best:
Packit Service 27f74b
            available.filterm(latest_per_arch=True)
Packit Service 27f74b
Packit Service 27f74b
        available.apply()
Packit Service 27f74b
        to_check.apply()
Packit Service 27f74b
Packit Service 27f74b
        for pkg in to_check:
Packit Service 27f74b
            unresolved[pkg] = set()
Packit Service 27f74b
            for req in pkg.requires:
Packit Service 27f74b
                reqname = str(req)
Packit Service 27f74b
                # XXX: https://bugzilla.redhat.com/show_bug.cgi?id=1186721
Packit Service 27f74b
                if reqname.startswith("solvable:") or \
Packit Service 27f74b
                        reqname.startswith("rpmlib("):
Packit Service 27f74b
                    continue
Packit Service 27f74b
                deps.add(req)
Packit Service 27f74b
                unresolved[pkg].add(req)
Packit Service 27f74b
Packit Service 27f74b
        unresolved_deps = set(x for x in deps if not available.filter(provides=x))
Packit Service 27f74b
Packit Service 27f74b
        unresolved_transition = {k: set(x for x in v if x in unresolved_deps)
Packit Service 27f74b
                                 for k, v in unresolved.items()}
Packit Service 27f74b
        return {k: v for k, v in unresolved_transition.items() if v}
Packit Service 27f74b
Packit Service 27f74b
    @staticmethod
Packit Service 27f74b
    def set_argparser(parser):
Packit Service 27f74b
        parser.add_argument("--arch", default=[], action="append", dest='arches',
Packit Service 27f74b
                            help=_("check packages of the given archs, can be "
Packit Service 27f74b
                                   "specified multiple times"))
Packit Service 27f74b
        parser.add_argument("--check", default=[], action="append",
Packit Service 27f74b
                            help=_("Specify repositories to check"))
Packit Service 27f74b
        parser.add_argument("-n", "--newest", action="store_true",
Packit Service 27f74b
                            help=_("Check only the newest packages in the "
Packit Service 27f74b
                                   "repos"))
Packit Service 27f74b
        parser.add_argument("--pkg", default=[], action="append",
Packit Service 27f74b
                            help=_("Check closure for this package only"),
Packit Service 27f74b
                            dest="pkglist")