pungi/pungi/dnf_wrapper.py
Lubomír Sedlář 004ef31917 gather: Ignore comps in lookaside repo
This fixes a case where extra packages are pulled in.

The scenario is:

 * there's a lookaside repo which contains group G which has package P
 * we want to pull group G into the compose, but our definition of G
   does not contain P
 * lookaside repo does not contain package P
 * current package set has P

DNF depsolver will then merge the two definitions and try to get all the
packages. For most cases this is not a problem, since the package is in
the lookaside repo and will not be pulled into the compose. But in the
example above since P is not in lookaside, Pungi will put it into
current compose.

This is also ugly in the depsolving log says it includes package P
because it was in input. But checking comps file does not show it.

The result of this change is that some packages might disappear from
current composes. This can only happen if there is a lookaside which
defines groups with similar IDs, which should be very rare. In cases
where this would be a problem the fix is to explicitly add wanted
packages either to comps or to additional packages.

Fixes: https://pagure.io/pungi/issue/978
Signed-off-by: Lubomír Sedlář <lsedlar@redhat.com>
2018-06-08 13:32:24 +02:00

150 lines
4.7 KiB
Python

# -*- coding: utf-8 -*-
# This program 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; version 2 of the License.
#
# 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 Library 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 <https://gnu.org/licenses/>.
# TODO: remove all DNF hacks, possibly this whole file
from distutils.version import LooseVersion
import os
import dnf
import dnf.conf
import dnf.repo
import dnf.sack
import pungi.arch
try:
import dnf.rpm as dnf_arch
except ImportError:
import dnf.arch as dnf_arch
class Conf(dnf.conf.Conf):
# This is only modified to get our custom Substitutions class in.
def __init__(self, arch, *args, **kwargs):
super(Conf, self).__init__(*args, **kwargs)
self.substitutions = Substitutions(arch)
class Substitutions(dict):
# DNF version of Substitutions detects host arch. We don't want that.
def __init__(self, arch):
super(Substitutions, self).__init__()
self['arch'] = arch
self['basearch'] = dnf_arch.basearch(arch)
class DnfWrapper(dnf.Base):
def __init__(self, *args, **kwargs):
super(DnfWrapper, self).__init__(*args, **kwargs)
self.arch_wrapper = ArchWrapper(self.conf.substitutions["arch"])
self.comps_wrapper = CompsWrapper(self)
def add_repo(
self, repoid, baseurl=None, mirrorlist=None, enablegroups=True, lookaside=False
):
if "://" not in baseurl:
baseurl = "file://%s" % os.path.abspath(baseurl)
if LooseVersion(dnf.__version__) < LooseVersion("2.0.0"):
repo = dnf.repo.Repo(repoid, self.conf.cachedir)
else:
repo = dnf.repo.Repo(repoid, self.conf)
repo.baseurl = baseurl
repo.mirrorlist = mirrorlist
repo.enablegroups = enablegroups
repo.enable()
self.repos.add(repo)
repo.priority = 10 if lookaside else 20
class CompsWrapper(object):
def __init__(self, dnf_obj):
self.dnf = dnf_obj
def __getitem__(self, name):
return self.groups[name]
@property
def comps(self):
return self.dnf.comps
@property
def groups(self):
result = {}
for i in self.comps.groups:
result[i.id] = i
return result
def get_packages_from_group(self, group_id, include_default=True, include_optional=True, include_conditional=True):
packages = []
conditional = []
group = self.groups[group_id]
# add mandatory packages
packages.extend([i.name for i in group.mandatory_packages])
# add default packages
if include_default:
packages.extend([i.name for i in group.default_packages])
# add optional packages
if include_optional:
packages.extend([i.name for i in group.optional_packages])
for package in group.conditional_packages:
conditional.append({"name": package.requires, "install": package.name})
return packages, conditional
def get_comps_packages(self, groups, exclude_groups):
packages = set()
conditional = []
if isinstance(groups, list):
groups = dict([(i, 1) for i in groups])
for group_id, group_include in sorted(groups.items()):
if group_id in exclude_groups:
continue
include_default = group_include in (1, 2)
include_optional = group_include in (2, )
include_conditional = True
pkgs, cond = self.get_packages_from_group(group_id, include_default, include_optional, include_conditional)
packages.update(pkgs)
for i in cond:
if i not in conditional:
conditional.append(i)
return list(packages), conditional
def get_langpacks(self):
result = []
for name, install in self.comps._i.langpacks.items():
result.append({"name": name, "install": install})
return result
class ArchWrapper(object):
def __init__(self, arch):
self.base_arch = dnf_arch.basearch(arch)
self.all_arches = pungi.arch.get_valid_arches(self.base_arch, multilib=True, add_noarch=True)
self.native_arches = pungi.arch.get_valid_arches(self.base_arch, multilib=False, add_noarch=True)
self.multilib_arches = pungi.arch.get_valid_multilib_arches(self.base_arch)
self.source_arches = ["src", "nosrc"]