import dnf-plugins-core-4.0.18-4.el8

This commit is contained in:
CentOS Sources 2021-05-18 02:39:10 -04:00 committed by Andrew Lukoshko
parent cd6f9b2bb5
commit 5acc81ebfe
14 changed files with 3780 additions and 7806 deletions

View File

@ -1 +1 @@
f938708df18862c3e31e2b9d49e5c9b322d79897 SOURCES/dnf-plugins-core-4.0.17.tar.gz
3b8638dec2cb91a13241106b9a57114ed037d2ca SOURCES/dnf-plugins-core-4.0.18.tar.gz

2
.gitignore vendored
View File

@ -1 +1 @@
SOURCES/dnf-plugins-core-4.0.17.tar.gz
SOURCES/dnf-plugins-core-4.0.18.tar.gz

View File

@ -0,0 +1,653 @@
From 40f08d7a22907e6292c314462c01de94584c0854 Mon Sep 17 00:00:00 2001
From: Marek Blaha <mblaha@redhat.com>
Date: Tue, 27 Oct 2020 15:46:03 +0100
Subject: [PATCH 1/2] [groups-manager] Re-introduce yum-groups-manager
functionality (RhBug:1826016)
Implements 'dnf groups-manager' command with features:
- read, merge, print and write groups metadata files
- edit group attributes name (with translated variants),
description (with translated variants), uservisible, displayorder
- add packgages to group
- remove packages from group
= changelog =
msg: Re-introduce yum-groups-manager functionality
type: enhancement
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1826016
---
dnf-plugins-core.spec | 22 ++-
doc/CMakeLists.txt | 2 +
doc/conf.py | 2 +
doc/groups-manager.rst | 94 ++++++++++++
doc/index.rst | 1 +
libexec/dnf-utils.in | 1 +
plugins/CMakeLists.txt | 1 +
plugins/groups_manager.py | 314 ++++++++++++++++++++++++++++++++++++++
8 files changed, 432 insertions(+), 5 deletions(-)
create mode 100644 doc/groups-manager.rst
create mode 100644 plugins/groups_manager.py
diff --git a/dnf-plugins-core.spec b/dnf-plugins-core.spec
index d13a996..42d0884 100644
--- a/dnf-plugins-core.spec
+++ b/dnf-plugins-core.spec
@@ -58,6 +58,7 @@ Provides: dnf-command(debug-dump)
Provides: dnf-command(debug-restore)
Provides: dnf-command(debuginfo-install)
Provides: dnf-command(download)
+Provides: dnf-command(groups-manager)
Provides: dnf-command(repoclosure)
Provides: dnf-command(repograph)
Provides: dnf-command(repomanage)
@@ -73,6 +74,7 @@ Provides: dnf-plugin-debuginfo-install = %{version}-%{release}
Provides: dnf-plugin-download = %{version}-%{release}
Provides: dnf-plugin-generate_completion_cache = %{version}-%{release}
Provides: dnf-plugin-needs_restarting = %{version}-%{release}
+Provides: dnf-plugin-groups-manager = %{version}-%{release}
Provides: dnf-plugin-repoclosure = %{version}-%{release}
Provides: dnf-plugin-repodiff = %{version}-%{release}
Provides: dnf-plugin-repograph = %{version}-%{release}
@@ -87,7 +89,7 @@ Conflicts: dnf-plugins-extras-common-data < %{dnf_plugins_extra}
%description
Core Plugins for DNF. This package enhances DNF with builddep, config-manager,
-copr, debug, debuginfo-install, download, needs-restarting, repoclosure,
+copr, debug, debuginfo-install, download, needs-restarting, groups-manager, repoclosure,
repograph, repomanage, reposync, changelog and repodiff commands. Additionally
provides generate_completion_cache passive plugin.
@@ -129,7 +131,8 @@ Conflicts: python-%{name} < %{version}-%{release}
%description -n python2-%{name}
Core Plugins for DNF, Python 2 interface. This package enhances DNF with builddep,
config-manager, copr, degug, debuginfo-install, download, needs-restarting,
-repoclosure, repograph, repomanage, reposync, changelog and repodiff commands.
+groups-manager, repoclosure, repograph, repomanage, reposync, changelog
+and repodiff commands.
Additionally provides generate_completion_cache passive plugin.
%endif
@@ -163,7 +166,8 @@ Conflicts: python-%{name} < %{version}-%{release}
%description -n python3-%{name}
Core Plugins for DNF, Python 3 interface. This package enhances DNF with builddep,
config-manager, copr, debug, debuginfo-install, download, needs-restarting,
-repoclosure, repograph, repomanage, reposync, changelog and repodiff commands.
+groups-manager, repoclosure, repograph, repomanage, reposync, changelog
+and repodiff commands.
Additionally provides generate_completion_cache passive plugin.
%endif
@@ -190,8 +194,8 @@ Summary: Yum-utils CLI compatibility layer
%description -n %{yum_utils_subpackage_name}
As a Yum-utils CLI compatibility layer, supplies in CLI shims for
debuginfo-install, repograph, package-cleanup, repoclosure, repomanage,
-repoquery, reposync, repotrack, repodiff, builddep, config-manager, debug
-and download that use new implementations using DNF.
+repoquery, reposync, repotrack, repodiff, builddep, config-manager, debug,
+download and yum-groups-manager that use new implementations using DNF.
%endif
%if 0%{?rhel} == 0 && %{with python2}
@@ -458,6 +462,7 @@ ln -sf %{_libexecdir}/dnf-utils %{buildroot}%{_bindir}/yum-builddep
ln -sf %{_libexecdir}/dnf-utils %{buildroot}%{_bindir}/yum-config-manager
ln -sf %{_libexecdir}/dnf-utils %{buildroot}%{_bindir}/yum-debug-dump
ln -sf %{_libexecdir}/dnf-utils %{buildroot}%{_bindir}/yum-debug-restore
+ln -sf %{_libexecdir}/dnf-utils %{buildroot}%{_bindir}/yum-groups-manager
ln -sf %{_libexecdir}/dnf-utils %{buildroot}%{_bindir}/yumdownloader
# These commands don't have a dedicated man page, so let's just point them
# to the utils page which contains their descriptions.
@@ -483,6 +488,7 @@ PYTHONPATH=./plugins nosetests-%{python3_version} -s tests/
%{_mandir}/man8/dnf-debuginfo-install.*
%{_mandir}/man8/dnf-download.*
%{_mandir}/man8/dnf-generate_completion_cache.*
+%{_mandir}/man8/dnf-groups-manager.*
%{_mandir}/man8/dnf-needs-restarting.*
%{_mandir}/man8/dnf-repoclosure.*
%{_mandir}/man8/dnf-repodiff.*
@@ -513,6 +519,7 @@ PYTHONPATH=./plugins nosetests-%{python3_version} -s tests/
%{python2_sitelib}/dnf-plugins/debuginfo-install.*
%{python2_sitelib}/dnf-plugins/download.*
%{python2_sitelib}/dnf-plugins/generate_completion_cache.*
+%{python2_sitelib}/dnf-plugins/groups_manager.*
%{python2_sitelib}/dnf-plugins/needs_restarting.*
%{python2_sitelib}/dnf-plugins/repoclosure.*
%{python2_sitelib}/dnf-plugins/repodiff.*
@@ -538,6 +545,7 @@ PYTHONPATH=./plugins nosetests-%{python3_version} -s tests/
%{python3_sitelib}/dnf-plugins/debuginfo-install.py
%{python3_sitelib}/dnf-plugins/download.py
%{python3_sitelib}/dnf-plugins/generate_completion_cache.py
+%{python3_sitelib}/dnf-plugins/groups_manager.py
%{python3_sitelib}/dnf-plugins/needs_restarting.py
%{python3_sitelib}/dnf-plugins/repoclosure.py
%{python3_sitelib}/dnf-plugins/repodiff.py
@@ -552,6 +560,7 @@ PYTHONPATH=./plugins nosetests-%{python3_version} -s tests/
%{python3_sitelib}/dnf-plugins/__pycache__/debuginfo-install.*
%{python3_sitelib}/dnf-plugins/__pycache__/download.*
%{python3_sitelib}/dnf-plugins/__pycache__/generate_completion_cache.*
+%{python3_sitelib}/dnf-plugins/__pycache__/groups_manager.*
%{python3_sitelib}/dnf-plugins/__pycache__/needs_restarting.*
%{python3_sitelib}/dnf-plugins/__pycache__/repoclosure.*
%{python3_sitelib}/dnf-plugins/__pycache__/repodiff.*
@@ -579,6 +588,7 @@ PYTHONPATH=./plugins nosetests-%{python3_version} -s tests/
%{_bindir}/yum-config-manager
%{_bindir}/yum-debug-dump
%{_bindir}/yum-debug-restore
+%{_bindir}/yum-groups-manager
%{_bindir}/yumdownloader
%{_mandir}/man1/debuginfo-install.*
%{_mandir}/man1/needs-restarting.*
@@ -591,6 +601,7 @@ PYTHONPATH=./plugins nosetests-%{python3_version} -s tests/
%{_mandir}/man1/yum-config-manager.*
%{_mandir}/man1/yum-debug-dump.*
%{_mandir}/man1/yum-debug-restore.*
+%{_mandir}/man1/yum-groups-manager.*
%{_mandir}/man1/yumdownloader.*
%{_mandir}/man1/package-cleanup.*
%{_mandir}/man1/dnf-utils.*
@@ -612,6 +623,7 @@ PYTHONPATH=./plugins nosetests-%{python3_version} -s tests/
%exclude %{_mandir}/man1/yum-config-manager.*
%exclude %{_mandir}/man1/yum-debug-dump.*
%exclude %{_mandir}/man1/yum-debug-restore.*
+%exclude %{_mandir}/man1/yum-groups-manager.*
%exclude %{_mandir}/man1/yumdownloader.*
%exclude %{_mandir}/man1/package-cleanup.*
%exclude %{_mandir}/man1/dnf-utils.*
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
index dd97eb2..3fb665d 100644
--- a/doc/CMakeLists.txt
+++ b/doc/CMakeLists.txt
@@ -26,6 +26,7 @@ INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/dnf-builddep.8
${CMAKE_CURRENT_BINARY_DIR}/dnf-debuginfo-install.8
${CMAKE_CURRENT_BINARY_DIR}/dnf-download.8
${CMAKE_CURRENT_BINARY_DIR}/dnf-generate_completion_cache.8
+ ${CMAKE_CURRENT_BINARY_DIR}/dnf-groups-manager.8
${CMAKE_CURRENT_BINARY_DIR}/dnf-leaves.8
${CMAKE_CURRENT_BINARY_DIR}/dnf-needs-restarting.8
${CMAKE_CURRENT_BINARY_DIR}/dnf-repoclosure.8
@@ -61,6 +62,7 @@ INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/debuginfo-install.1
${CMAKE_CURRENT_BINARY_DIR}/yum-config-manager.1
${CMAKE_CURRENT_BINARY_DIR}/yum-debug-dump.1
${CMAKE_CURRENT_BINARY_DIR}/yum-debug-restore.1
+ ${CMAKE_CURRENT_BINARY_DIR}/yum-groups-manager.1
${CMAKE_CURRENT_BINARY_DIR}/yumdownloader.1
${CMAKE_CURRENT_BINARY_DIR}/package-cleanup.1
${CMAKE_CURRENT_BINARY_DIR}/dnf-utils.1
diff --git a/doc/conf.py b/doc/conf.py
index d760ef3..645185a 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -251,6 +251,7 @@ man_pages = [
('download', 'dnf-download', u'DNF download Plugin', AUTHORS, 8),
('generate_completion_cache', 'dnf-generate_completion_cache',
u'DNF generate_completion_cache Plugin', AUTHORS, 8),
+ ('groups-manager', 'dnf-groups-manager', u'DNF groups-manager Plugin', AUTHORS, 8),
('leaves', 'dnf-leaves', u'DNF leaves Plugin', AUTHORS, 8),
('local', 'dnf-local', u'DNF local Plugin', AUTHORS, 8),
('needs_restarting', 'dnf-needs-restarting', u'DNF needs_restarting Plugin', AUTHORS, 8),
@@ -268,6 +269,7 @@ man_pages = [
('copr', 'yum-copr', u'redirecting to DNF copr Plugin', AUTHORS, 8),
('debuginfo-install', 'debuginfo-install', u'redirecting to DNF debuginfo-install Plugin',
AUTHORS, 1),
+ ('groups-manager', 'yum-groups-manager', u'redirecting to DNF groups-manager Plugin', AUTHORS, 1),
('needs_restarting', 'needs-restarting', u'redirecting to DNF needs-restarting Plugin',
AUTHORS, 1),
('repoclosure', 'repoclosure', u'redirecting to DNF repoclosure Plugin', AUTHORS, 1),
diff --git a/doc/groups-manager.rst b/doc/groups-manager.rst
new file mode 100644
index 0000000..f8f76a1
--- /dev/null
+++ b/doc/groups-manager.rst
@@ -0,0 +1,94 @@
+..
+ Copyright (C) 2020 Red Hat, Inc.
+
+ This copyrighted material is made available to anyone wishing to use,
+ modify, copy, or redistribute it subject to the terms and conditions of
+ the GNU General Public License v.2, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY expressed or implied, including the implied warranties 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, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
+ source code or documentation are not subject to the GNU General Public
+ License and may only be used or replicated with the express permission of
+ Red Hat, Inc.
+
+=========================
+DNF groups-manager Plugin
+=========================
+
+Create and edit groups repository metadata files.
+
+--------
+Synopsis
+--------
+
+``dnf groups-manager [options] [package-name-spec [package-name-spec ...]]``
+
+-----------
+Description
+-----------
+groups-manager plugin is used to create or edit a group metadata file for a repository. This is often much easier than writing/editing the XML by hand. The groups-manager can load an entire file of groups metadata and either create a new group or edit an existing group and then write all of the groups metadata back out.
+
+---------
+Arguments
+---------
+
+``<package-name-spec>``
+ Package to add to a group or remove from a group.
+
+-------
+Options
+-------
+
+All general DNF options are accepted, see `Options` in :manpage:`dnf(8)` for details.
+
+``--load=<path_to_comps.xml>``
+ Load the groups metadata information from the specified file before performing any operations. Metadata from all files are merged together if the option is specified multiple times.
+
+``--save=<path_to_comps.xml>``
+ Save the result to this file. You can specify the name of a file you are loading from as the data will only be saved when all the operations have been performed. This option can also be specified multiple times.
+
+``--merge=<path_to_comps.xml>``
+ This is the same as loading and saving a file, however the "merge" file is loaded before any others and saved last.
+
+``--print``
+ Also print the result to stdout.
+
+``--id=<id>``
+ The id to lookup/use for the group. If you don't specify an ``<id>``, but do specify a name that doesn't refer to an existing group, then an id for the group is generated based on the name.
+
+``-n <name>, --name=<name>``
+ The name to lookup/use for the group. If you specify an existing group id, then the group with that id will have it's name changed to this value.
+
+``--description=<description>``
+ The description to use for the group.
+
+``--display-order=<display_order>``
+ Change the integer which controls the order groups are presented in, for example in ``dnf grouplist``.
+
+``--translated-name=<lang:text>``
+ A translation of the group name in the given language. The syntax is ``lang:text``. Eg. ``en:my-group-name-in-english``
+
+``--translated-description=<lang:text>``
+ A translation of the group description in the given language. The syntax is ``lang:text``. Eg. ``en:my-group-description-in-english``.
+
+``--user-visible``
+ Make the group visible in ``dnf grouplist`` (this is the default).
+
+``--not-user-visible``
+ Make the group not visible in ``dnf grouplist``.
+
+``--mandatory``
+ Store the package names specified within the mandatory section of the specified group, the default is to use the default section.
+
+``--optional``
+ Store the package names specified within the optional section of the specified group, the default is to use the default section.
+
+``--remove``
+ Instead of adding packages remove them. Note that the packages are removed from all sections (default, mandatory and optional).
+
+``--dependencies``
+ Also include the names of the direct dependencies for each package specified.
diff --git a/doc/index.rst b/doc/index.rst
index 91bb36e..7213253 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -33,6 +33,7 @@ This documents core plugins of DNF:
debuginfo-install
download
generate_completion_cache
+ groups-manager
leaves
local
migrate
diff --git a/libexec/dnf-utils.in b/libexec/dnf-utils.in
index 667ce13..af1e893 100644
--- a/libexec/dnf-utils.in
+++ b/libexec/dnf-utils.in
@@ -37,6 +37,7 @@ MAPPING = {'debuginfo-install': ['debuginfo-install'],
'yum-config-manager': ['config-manager'],
'yum-debug-dump': ['debug-dump'],
'yum-debug-restore': ['debug-restore'],
+ 'yum-groups-manager': ['groups-manager'],
'yumdownloader': ['download']
}
diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt
index 7465e53..f66d3df 100644
--- a/plugins/CMakeLists.txt
+++ b/plugins/CMakeLists.txt
@@ -6,6 +6,7 @@ INSTALL (FILES config_manager.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
INSTALL (FILES copr.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
INSTALL (FILES download.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
INSTALL (FILES generate_completion_cache.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
+INSTALL (FILES groups_manager.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
INSTALL (FILES leaves.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
if (${WITHOUT_LOCAL} STREQUAL "0")
INSTALL (FILES local.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
diff --git a/plugins/groups_manager.py b/plugins/groups_manager.py
new file mode 100644
index 0000000..382df37
--- /dev/null
+++ b/plugins/groups_manager.py
@@ -0,0 +1,314 @@
+# groups_manager.py
+# DNF plugin for managing comps groups metadata files
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties 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, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+
+from __future__ import absolute_import
+from __future__ import unicode_literals
+
+import argparse
+import gzip
+import libcomps
+import os
+import re
+import shutil
+import tempfile
+
+from dnfpluginscore import _, logger
+import dnf
+import dnf.cli
+
+
+RE_GROUP_ID_VALID = '-a-z0-9_.:'
+RE_GROUP_ID = re.compile(r'^[{}]+$'.format(RE_GROUP_ID_VALID))
+RE_LANG = re.compile(r'^[-a-zA-Z0-9_.@]+$')
+COMPS_XML_OPTIONS = {
+ 'default_explicit': True,
+ 'uservisible_explicit': True,
+ 'empty_groups': True}
+
+
+def group_id_type(value):
+ '''group id validator'''
+ if not RE_GROUP_ID.match(value):
+ raise argparse.ArgumentTypeError(_('Invalid group id'))
+ return value
+
+
+def translation_type(value):
+ '''translated texts validator'''
+ data = value.split(':', 2)
+ if len(data) != 2:
+ raise argparse.ArgumentTypeError(
+ _("Invalid translated data, should be in form 'lang:text'"))
+ lang, text = data
+ if not RE_LANG.match(lang):
+ raise argparse.ArgumentTypeError(_('Invalid/empty language for translated data'))
+ return lang, text
+
+
+def text_to_id(text):
+ '''generate group id based on its name'''
+ group_id = text.lower()
+ group_id = re.sub('[^{}]'.format(RE_GROUP_ID_VALID), '', group_id)
+ if not group_id:
+ raise dnf.cli.CliError(
+ _("Can't generate group id from '{}'. Please specify group id using --id.").format(
+ text))
+ return group_id
+
+
+@dnf.plugin.register_command
+class GroupsManagerCommand(dnf.cli.Command):
+ aliases = ('groups-manager',)
+ summary = _('create and edit groups metadata file')
+
+ def __init__(self, cli):
+ super(GroupsManagerCommand, self).__init__(cli)
+ self.comps = libcomps.Comps()
+
+ @staticmethod
+ def set_argparser(parser):
+ # input / output options
+ parser.add_argument('--load', action='append', default=[],
+ metavar='COMPS.XML',
+ help=_('load groups metadata from file'))
+ parser.add_argument('--save', action='append', default=[],
+ metavar='COMPS.XML',
+ help=_('save groups metadata to file'))
+ parser.add_argument('--merge', metavar='COMPS.XML',
+ help=_('load and save groups metadata to file'))
+ parser.add_argument('--print', action='store_true', default=False,
+ help=_('print the result metadata to stdout'))
+ # group options
+ parser.add_argument('--id', type=group_id_type,
+ help=_('group id'))
+ parser.add_argument('-n', '--name', help=_('group name'))
+ parser.add_argument('--description',
+ help=_('group description'))
+ parser.add_argument('--display-order', type=int,
+ help=_('group display order'))
+ parser.add_argument('--translated-name', action='append', default=[],
+ metavar='LANG:TEXT', type=translation_type,
+ help=_('translated name for the group'))
+ parser.add_argument('--translated-description', action='append', default=[],
+ metavar='LANG:TEXT', type=translation_type,
+ help=_('translated description for the group'))
+ visible = parser.add_mutually_exclusive_group()
+ visible.add_argument('--user-visible', dest='user_visible', action='store_true',
+ default=None,
+ help=_('make the group user visible (default)'))
+ visible.add_argument('--not-user-visible', dest='user_visible', action='store_false',
+ default=None,
+ help=_('make the group user invisible'))
+
+ # package list options
+ section = parser.add_mutually_exclusive_group()
+ section.add_argument('--mandatory', action='store_true',
+ help=_('add packages to the mandatory section'))
+ section.add_argument('--optional', action='store_true',
+ help=_('add packages to the optional section'))
+ section.add_argument('--remove', action='store_true', default=False,
+ help=_('remove packages from the group instead of adding them'))
+ parser.add_argument('--dependencies', action='store_true',
+ help=_('include also direct dependencies for packages'))
+
+ parser.add_argument("packages", nargs='*', metavar='PACKAGE',
+ help=_('package specification'))
+
+ def configure(self):
+ demands = self.cli.demands
+
+ if self.opts.packages:
+ demands.sack_activation = True
+ demands.available_repos = True
+ demands.load_system_repo = False
+
+ # handle --merge option (shortcut to --load and --save the same file)
+ if self.opts.merge:
+ self.opts.load.insert(0, self.opts.merge)
+ self.opts.save.append(self.opts.merge)
+
+ # check that group is specified when editing is attempted
+ if (self.opts.description
+ or self.opts.display_order
+ or self.opts.translated_name
+ or self.opts.translated_description
+ or self.opts.user_visible is not None
+ or self.opts.packages):
+ if not self.opts.id and not self.opts.name:
+ raise dnf.cli.CliError(
+ _("Can't edit group without specifying it (use --id or --name)"))
+
+ def load_input_files(self):
+ """
+ Loads all input xml files.
+ Returns True if at least one file was successfuly loaded
+ """
+ for file_name in self.opts.load:
+ file_comps = libcomps.Comps()
+ try:
+ if file_name.endswith('.gz'):
+ # libcomps does not support gzipped files - decompress to temporary
+ # location
+ with gzip.open(file_name) as gz_file:
+ temp_file = tempfile.NamedTemporaryFile(delete=False)
+ try:
+ shutil.copyfileobj(gz_file, temp_file)
+ # close temp_file to ensure the content is flushed to disk
+ temp_file.close()
+ file_comps.fromxml_f(temp_file.name)
+ finally:
+ os.unlink(temp_file.name)
+ else:
+ file_comps.fromxml_f(file_name)
+ except (IOError, OSError, libcomps.ParserError) as err:
+ # gzip module raises OSError on reading from malformed gz file
+ # get_last_errors() output often contains duplicit lines, remove them
+ seen = set()
+ for error in file_comps.get_last_errors():
+ if error in seen:
+ continue
+ logger.error(error.strip())
+ seen.add(error)
+ raise dnf.exceptions.Error(
+ _("Can't load file \"{}\": {}").format(file_name, err))
+ else:
+ self.comps += file_comps
+
+ def save_output_files(self):
+ for file_name in self.opts.save:
+ try:
+ # xml_f returns a list of errors / log entries
+ errors = self.comps.xml_f(file_name, xml_options=COMPS_XML_OPTIONS)
+ except libcomps.XMLGenError as err:
+ errors = [err]
+ if errors:
+ # xml_f() method could return more than one error. In this case
+ # raise the latest of them and log the others.
+ for err in errors[:-1]:
+ logger.error(err.strip())
+ raise dnf.exceptions.Error(_("Can't save file \"{}\": {}").format(
+ file_name, errors[-1].strip()))
+
+
+ def find_group(self, group_id, name):
+ '''
+ Try to find group according to command line parameters - first by id
+ then by name.
+ '''
+ group = None
+ if group_id:
+ for grp in self.comps.groups:
+ if grp.id == group_id:
+ group = grp
+ break
+ if group is None and name:
+ for grp in self.comps.groups:
+ if grp.name == name:
+ group = grp
+ break
+ return group
+
+ def edit_group(self, group):
+ '''
+ Set attributes and package lists for selected group
+ '''
+ def langlist_to_strdict(lst):
+ str_dict = libcomps.StrDict()
+ for lang, text in lst:
+ str_dict[lang] = text
+ return str_dict
+
+ # set group attributes
+ if self.opts.name:
+ group.name = self.opts.name
+ if self.opts.description:
+ group.desc = self.opts.description
+ if self.opts.display_order:
+ group.display_order = self.opts.display_order
+ if self.opts.user_visible is not None:
+ group.uservisible = self.opts.user_visible
+ if self.opts.translated_name:
+ group.name_by_lang = langlist_to_strdict(self.opts.translated_name)
+ if self.opts.translated_description:
+ group.desc_by_lang = langlist_to_strdict(self.opts.translated_description)
+
+ # edit packages list
+ if self.opts.packages:
+ # find packages according to specifications from command line
+ packages = set()
+ for pkg_spec in self.opts.packages:
+ q = self.base.sack.query().filterm(name__glob=pkg_spec).latest()
+ if not q:
+ logger.warning(_("No match for argument: {}").format(pkg_spec))
+ continue
+ packages.update(q)
+ if self.opts.dependencies:
+ # add packages that provide requirements
+ requirements = set()
+ for pkg in packages:
+ requirements.update(pkg.requires)
+ packages.update(self.base.sack.query().filterm(provides=requirements))
+
+ pkg_names = {pkg.name for pkg in packages}
+
+ if self.opts.remove:
+ for pkg_name in pkg_names:
+ for pkg in group.packages_match(name=pkg_name,
+ type=libcomps.PACKAGE_TYPE_UNKNOWN):
+ group.packages.remove(pkg)
+ else:
+ if self.opts.mandatory:
+ pkg_type = libcomps.PACKAGE_TYPE_MANDATORY
+ elif self.opts.optional:
+ pkg_type = libcomps.PACKAGE_TYPE_OPTIONAL
+ else:
+ pkg_type = libcomps.PACKAGE_TYPE_DEFAULT
+ for pkg_name in sorted(pkg_names):
+ if not group.packages_match(name=pkg_name, type=pkg_type):
+ group.packages.append(libcomps.Package(name=pkg_name, type=pkg_type))
+
+ def run(self):
+ self.load_input_files()
+
+ if self.opts.id or self.opts.name:
+ # we are adding / editing a group
+ group = self.find_group(group_id=self.opts.id, name=self.opts.name)
+ if group is None:
+ # create a new group
+ if self.opts.remove:
+ raise dnf.exceptions.Error(_("Can't remove packages from non-existent group"))
+ group = libcomps.Group()
+ if self.opts.id:
+ group.id = self.opts.id
+ group.name = self.opts.id
+ elif self.opts.name:
+ group_id = text_to_id(self.opts.name)
+ if self.find_group(group_id=group_id, name=None):
+ raise dnf.cli.CliError(
+ _("Group id '{}' generated from '{}' is duplicit. "
+ "Please specify group id using --id.").format(
+ group_id, self.opts.name))
+ group.id = group_id
+ self.comps.groups.append(group)
+ self.edit_group(group)
+
+ self.save_output_files()
+ if self.opts.print or (not self.opts.save):
+ print(self.comps.xml_str(xml_options=COMPS_XML_OPTIONS))
--
2.26.2

View File

@ -1,62 +0,0 @@
From aa1f12be109a2d997eeb1c1cce22beb09dd21d04 Mon Sep 17 00:00:00 2001
From: Nicola Sella <nsella@redhat.com>
Date: Thu, 11 Jun 2020 09:32:17 +0200
Subject: [PATCH 1/2] [needs-restarting] Fix plugin fail if needs-restarting.d
does not exist
includes pep8 warning fix and string formatting space missing
---
plugins/needs_restarting.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/plugins/needs_restarting.py b/plugins/needs_restarting.py
index 91f7e116..6b7dacb6 100644
--- a/plugins/needs_restarting.py
+++ b/plugins/needs_restarting.py
@@ -46,6 +46,8 @@ def get_options_from_dir(filepath, base):
Return set of package names contained in files under filepath
"""
+ if not os.path.exists(filepath):
+ return set()
options = set()
for file in os.listdir(filepath):
if os.path.isdir(file) or not file.endswith('.conf'):
@@ -58,9 +60,9 @@ def get_options_from_dir(filepath, base):
packages = set()
for pkg in base.sack.query().installed().filter(name={x[0] for x in options}):
packages.add(pkg.name)
- for name, file in {x for x in options if x[0] not in packages }:
+ for name, file in {x for x in options if x[0] not in packages}:
logger.warning(
- _('No installed package found for package name "{pkg}"'
+ _('No installed package found for package name "{pkg}" '
'specified in needs-restarting file "{file}".'.format(pkg=name, file=file)))
return packages
From 57955d299f751cb9927fe501fa086d9153092532 Mon Sep 17 00:00:00 2001
From: Nicola Sella <nsella@redhat.com>
Date: Thu, 11 Jun 2020 10:53:54 +0200
Subject: [PATCH 2/2] [needs-restarting] add kernel-rt to reboot list
BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1806060
---
plugins/needs_restarting.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/plugins/needs_restarting.py b/plugins/needs_restarting.py
index 6b7dacb6..69203f4d 100644
--- a/plugins/needs_restarting.py
+++ b/plugins/needs_restarting.py
@@ -37,8 +37,8 @@
# For which package updates we should recommend a reboot
# Mostly taken from https://access.redhat.com/solutions/27943
-NEED_REBOOT = ['kernel', 'glibc', 'linux-firmware', 'systemd', 'dbus',
- 'dbus-broker', 'dbus-daemon']
+NEED_REBOOT = ['kernel', 'kernel-rt', 'glibc', 'linux-firmware',
+ 'systemd', 'dbus', 'dbus-broker', 'dbus-daemon']
def get_options_from_dir(filepath, base):
"""

View File

@ -1,159 +0,0 @@
From b94763c7f52dbbcc9920b4216d53fd8109e434c9 Mon Sep 17 00:00:00 2001
From: Marek Blaha <mblaha@redhat.com>
Date: Wed, 17 Jun 2020 15:49:50 +0200
Subject: [PATCH] Fix debug-restore command
- correctly work with install-only packages (BZ#1844533)
- do not remove current versions of packages that are supposed to be
replaced (downgraded / upgraded)
---
plugins/debug.py | 108 ++++++++++++++++++++++++-----------------------
1 file changed, 56 insertions(+), 52 deletions(-)
diff --git a/plugins/debug.py b/plugins/debug.py
index 6d00613d..29c5bf78 100644
--- a/plugins/debug.py
+++ b/plugins/debug.py
@@ -201,10 +201,9 @@ def run(self):
self.opts.filter_types = set(
self.opts.filter_types.replace(",", " ").split())
- installed = self.base.sack.query().installed()
dump_pkgs = self.read_dump_file(self.opts.filename[0])
- self.process_installed(installed, dump_pkgs, self.opts)
+ self.process_installed(dump_pkgs, self.opts)
self.process_dump(dump_pkgs, self.opts)
@@ -212,56 +211,63 @@ def run(self):
self.base.resolve()
self.base.do_transaction()
- def process_installed(self, installed, dump_pkgs, opts):
- for pkg in sorted(installed):
- filtered = False
+ def process_installed(self, dump_pkgs, opts):
+ installed = self.base.sack.query().installed()
+ installonly_pkgs = self.base._get_installonly_query(installed)
+ for pkg in installed:
+ pkg_remove = False
spec = pkgspec(pkg)
- action, dn, da, de, dv, dr = dump_pkgs.get((pkg.name, pkg.arch),
- [None, None, None,
- None, None, None])
- dump_naevr = (dn, da, de, dv, dr)
- if pkg.pkgtup == dump_naevr:
- # package unchanged
- del dump_pkgs[(pkg.name, pkg.arch)]
- else:
- if action == "install":
- # already have some version
- dump_pkgs[(pkg.name, pkg.arch)][0] = "replace"
- if "replace" not in opts.filter_types:
- filtered = True
+ dumped_versions = dump_pkgs.get((pkg.name, pkg.arch), None)
+ if dumped_versions is not None:
+ evr = (pkg.epoch, pkg.version, pkg.release)
+ if evr in dumped_versions:
+ # the correct version is already installed
+ dumped_versions[evr] = 'skip'
else:
- if "remove" not in opts.filter_types:
- filtered = True
- if not filtered:
- if opts.output:
- print("remove %s" % spec)
+ # other version is currently installed
+ if pkg in installonly_pkgs:
+ # package is install-only, should be removed
+ pkg_remove = True
else:
- self.base.package_remove(pkg)
-
- def process_dump(self, dump_pkgs, opts):
- for (action, n, a, e, v, r) in sorted(dump_pkgs.values()):
- filtered = False
- if opts.ignore_arch:
- arch = ""
- else:
- arch = "." + a
- if opts.install_latest and action == "install":
- pkg_spec = "%s%s" % (n, arch)
- if "install" not in opts.filter_types:
- filtered = True
+ # package should be upgraded / downgraded
+ if "replace" in opts.filter_types:
+ action = 'replace'
+ else:
+ action = 'skip'
+ for d_evr in dumped_versions.keys():
+ dumped_versions[d_evr] = action
else:
- pkg_spec = pkgtup2spec(n, arch, e, v, r)
- if (action == "replace" and
- "replace" not in opts.filter_types):
- filtered = True
- if not filtered:
+ # package should not be installed
+ pkg_remove = True
+ if pkg_remove and "remove" in opts.filter_types:
if opts.output:
- print("install %s" % pkg_spec)
+ print("remove %s" % spec)
else:
- try:
- self.base.install(pkg_spec)
- except dnf.exceptions.MarkingError:
- logger.error(_("Package %s is not available"), pkg_spec)
+ self.base.package_remove(pkg)
+
+ def process_dump(self, dump_pkgs, opts):
+ for (n, a) in sorted(dump_pkgs.keys()):
+ dumped_versions = dump_pkgs[(n, a)]
+ for (e, v, r) in sorted(dumped_versions.keys()):
+ action = dumped_versions[(e, v, r)]
+ if action == 'skip':
+ continue
+ if opts.ignore_arch:
+ arch = ""
+ else:
+ arch = "." + a
+ if opts.install_latest and action == "install":
+ pkg_spec = "%s%s" % (n, arch)
+ else:
+ pkg_spec = pkgtup2spec(n, arch, e, v, r)
+ if action in opts.filter_types:
+ if opts.output:
+ print("%s %s" % (action, pkg_spec))
+ else:
+ try:
+ self.base.install(pkg_spec)
+ except dnf.exceptions.MarkingError:
+ logger.error(_("Package %s is not available"), pkg_spec)
@staticmethod
def read_dump_file(filename):
@@ -288,11 +294,9 @@ def read_dump_file(filename):
pkg_spec = line.strip()
nevra = hawkey.split_nevra(pkg_spec)
- pkgs[(nevra.name, nevra.arch)] = ["install", ucd(nevra.name),
- ucd(nevra.arch),
- ucd(nevra.epoch),
- ucd(nevra.version),
- ucd(nevra.release)]
+ # {(name, arch): {(epoch, version, release): action}}
+ pkgs.setdefault((nevra.name, nevra.arch), {})[
+ (nevra.epoch, nevra.version, nevra.release)] = "install"
return pkgs
@@ -321,6 +325,6 @@ def pkgspec(pkg):
def pkgtup2spec(name, arch, epoch, version, release):
- a = "" if not arch else ".%s" % arch
+ a = "" if not arch else ".%s" % arch.lstrip('.')
e = "" if epoch in (None, "") else "%s:" % epoch
return "%s-%s%s-%s%s" % (name, e, version, release, a)

View File

@ -0,0 +1,138 @@
From b2a912724d737ca7ac4350885b54117f5e043046 Mon Sep 17 00:00:00 2001
From: Nicola Sella <nsella@redhat.com>
Date: Thu, 5 Mar 2020 12:45:39 +0100
Subject: [PATCH 2/2] [needs-restarting] add -s to list services
(RhBug:1772939)
= changelog =
msg: [needs-restarting] add -s to list services (RhBug:1772939)
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1772939
Closes: #395
Approved by: kontura
---
dnf-plugins-core.spec | 6 ++++++
doc/needs_restarting.rst | 3 +++
plugins/needs_restarting.py | 33 +++++++++++++++++++++++++++++++++
3 files changed, 42 insertions(+)
diff --git a/dnf-plugins-core.spec b/dnf-plugins-core.spec
index 42d0884..012dde8 100644
--- a/dnf-plugins-core.spec
+++ b/dnf-plugins-core.spec
@@ -99,8 +99,10 @@ Summary: Core Plugins for DNF
%{?python_provide:%python_provide python2-%{name}}
BuildRequires: python2-dnf >= %{dnf_lowest_compatible}
%if 0%{?rhel} && 0%{?rhel} <= 7
+BuildRequires: dbus-python
BuildRequires: python-nose
%else
+BuildRequires: python2-dbus
BuildRequires: python2-nose
%endif
BuildRequires: python2-devel
@@ -110,8 +112,10 @@ Requires: python2-distro
Requires: python2-dnf >= %{dnf_lowest_compatible}
Requires: python2-hawkey >= %{hawkey_version}
%if 0%{?rhel} && 0%{?rhel} <= 7
+Requires: dbus-python
Requires: python-dateutil
%else
+Requires: python2-dbus
Requires: python2-dateutil
%endif
Provides: python2-dnf-plugins-extras-debug = %{version}-%{release}
@@ -140,12 +144,14 @@ Additionally provides generate_completion_cache passive plugin.
%package -n python3-%{name}
Summary: Core Plugins for DNF
%{?python_provide:%python_provide python3-%{name}}
+BuildRequires: python3-dbus
BuildRequires: python3-devel
BuildRequires: python3-dnf >= %{dnf_lowest_compatible}
BuildRequires: python3-nose
%if 0%{?fedora}
Requires: python3-distro
%endif
+Requires: python3-dbus
Requires: python3-dnf >= %{dnf_lowest_compatible}
Requires: python3-hawkey >= %{hawkey_version}
Requires: python3-dateutil
diff --git a/doc/needs_restarting.rst b/doc/needs_restarting.rst
index e79b43f..1a3fbbe 100644
--- a/doc/needs_restarting.rst
+++ b/doc/needs_restarting.rst
@@ -48,3 +48,6 @@ All general DNF options are accepted, see `Options` in :manpage:`dnf(8)` for det
``-r, --reboothint``
Only report whether a reboot is required (exit code 1) or not (exit code 0).
+
+``-s, --services``
+ Only list the affected systemd services.
diff --git a/plugins/needs_restarting.py b/plugins/needs_restarting.py
index 69203f4..f6bf525 100644
--- a/plugins/needs_restarting.py
+++ b/plugins/needs_restarting.py
@@ -29,6 +29,7 @@ from dnfpluginscore import logger, _
import dnf
import dnf.cli
+import dbus
import functools
import os
import re
@@ -126,6 +127,30 @@ def print_cmd(pid):
print('%d : %s' % (pid, command))
+def get_service_dbus(pid):
+ bus = dbus.SystemBus()
+ systemd_manager_object = bus.get_object(
+ 'org.freedesktop.systemd1',
+ '/org/freedesktop/systemd1'
+ )
+ systemd_manager_interface = dbus.Interface(
+ systemd_manager_object,
+ 'org.freedesktop.systemd1.Manager'
+ )
+ service_proxy = bus.get_object(
+ 'org.freedesktop.systemd1',
+ systemd_manager_interface.GetUnitByPID(pid)
+ )
+ service_properties = dbus.Interface(
+ service_proxy, dbus_interface="org.freedesktop.DBus.Properties")
+ name = service_properties.Get(
+ "org.freedesktop.systemd1.Unit",
+ 'Id'
+ )
+ if name.endswith(".service"):
+ return name
+ return
+
def smap2opened_file(pid, line):
slash = line.find('/')
if slash < 0:
@@ -205,6 +230,8 @@ class NeedsRestartingCommand(dnf.cli.Command):
parser.add_argument('-r', '--reboothint', action='store_true',
help=_("only report whether a reboot is required "
"(exit code 1) or not (exit code 0)"))
+ parser.add_argument('-s', '--services', action='store_true',
+ help=_("only report affected systemd services"))
def configure(self):
demands = self.cli.demands
@@ -251,5 +278,11 @@ class NeedsRestartingCommand(dnf.cli.Command):
if pkg.installtime > process_start(ofile.pid):
stale_pids.add(ofile.pid)
+ if self.opts.services:
+ names = set([get_service_dbus(pid) for pid in sorted(stale_pids)])
+ for name in names:
+ if name is not None:
+ print(name)
+ return 0
for pid in sorted(stale_pids):
print_cmd(pid)
--
2.26.2

View File

@ -1,53 +0,0 @@
From 01f5570bb74aa923870e253007b76e8ed266a27f Mon Sep 17 00:00:00 2001
From: Marek Blaha <mblaha@redhat.com>
Date: Wed, 1 Jul 2020 08:52:19 +0200
Subject: [PATCH 3/5] [debug] Use standard demands.resolving for transaction
handling
Do not handle the transaction in plugin, use standard demands.resolving
instead. This ensures that transaction errors are correctly presented to
the user.
Before:
$ dnf debug-restore running-kernel-remove.txt.gz
$ echo $?
1
After the patch:
$ dnf debug-restore running-kernel-remove.txt.gz
Error:
Problem: The operation would result in removing the following protected packages: kernel-core
(try to add '--skip-broken' to skip uninstallable packages)
$ echo $?
1
---
plugins/debug.py | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/plugins/debug.py b/plugins/debug.py
index 29c5bf7..efe6bea 100644
--- a/plugins/debug.py
+++ b/plugins/debug.py
@@ -175,6 +175,8 @@ class DebugRestoreCommand(dnf.cli.Command):
self.cli.demands.sack_activation = True
self.cli.demands.available_repos = True
self.cli.demands.root_user = True
+ if not self.opts.output:
+ self.cli.demands.resolving = True
@staticmethod
def set_argparser(parser):
@@ -207,10 +209,6 @@ class DebugRestoreCommand(dnf.cli.Command):
self.process_dump(dump_pkgs, self.opts)
- if not self.opts.output:
- self.base.resolve()
- self.base.do_transaction()
-
def process_installed(self, dump_pkgs, opts):
installed = self.base.sack.query().installed()
installonly_pkgs = self.base._get_installonly_query(installed)
--
2.25.4

View File

@ -0,0 +1,194 @@
From a4f21266a6dab9e77913d56c04aba1e579f0e0c1 Mon Sep 17 00:00:00 2001
From: Marek Blaha <mblaha@redhat.com>
Date: Fri, 23 Oct 2020 09:06:35 +0200
Subject: [PATCH 1/2] [reposync] Reorder options alphabetically
---
doc/reposync.rst | 30 +++++++++++++++---------------
plugins/reposync.py | 18 +++++++++---------
2 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/doc/reposync.rst b/doc/reposync.rst
index 71a435dc..3b820f33 100644
--- a/doc/reposync.rst
+++ b/doc/reposync.rst
@@ -39,36 +39,36 @@ Options
All general DNF options are accepted. Namely, the ``--repoid`` option can be used to specify the repositories to synchronize. See `Options` in :manpage:`dnf(8)` for details.
-``-p <download-path>, --download-path=<download-path>``
- Root path under which the downloaded repositories are stored, relative to the current working directory. Defaults to the current working directory. Every downloaded repository has a subdirectory named after its ID under this path.
-
-``--norepopath``
- Don't add the reponame to the download path. Can only be used when syncing a single repository (default is to add the reponame).
-
-``--download-metadata``
- Download all repository metadata. Downloaded copy is instantly usable as a repository, no need to run createrepo_c on it.
-
``-a <architecture>, --arch=<architecture>``
Download only packages of given architectures (default is all architectures). Can be used multiple times.
-``--source``
- Operate on source packages.
+``--delete``
+ Delete local packages no longer present in repository.
+
+``--download-metadata``
+ Download all repository metadata. Downloaded copy is instantly usable as a repository, no need to run createrepo_c on it.
``-m, --downloadcomps``
Also download and uncompress comps.xml. Consider using ``--download-metadata`` option which will download all available repository metadata.
+``--metadata-path``
+ Root path under which the downloaded metadata are stored. It defaults to ``--download-path`` value if not given.
+
``-n, --newest-only``
Download only newest packages per-repo.
-``--delete``
- Delete local packages no longer present in repository.
+``--norepopath``
+ Don't add the reponame to the download path. Can only be used when syncing a single repository (default is to add the reponame).
-``--metadata-path``
- Root path under which the downloaded metadata are stored. It defaults to ``--download-path`` value if not given.
+``-p <download-path>, --download-path=<download-path>``
+ Root path under which the downloaded repositories are stored, relative to the current working directory. Defaults to the current working directory. Every downloaded repository has a subdirectory named after its ID under this path.
``--remote-time``
Try to set the timestamps of the downloaded files to those on the remote side.
+``--source``
+ Operate on source packages.
+
``-u, --urls``
Just print urls of what would be downloaded, don't download.
diff --git a/plugins/reposync.py b/plugins/reposync.py
index 7556e7eb..6f572cac 100644
--- a/plugins/reposync.py
+++ b/plugins/reposync.py
@@ -63,24 +63,24 @@ def set_argparser(parser):
help=_('download only packages for this ARCH'))
parser.add_argument('--delete', default=False, action='store_true',
help=_('delete local packages no longer present in repository'))
- parser.add_argument('-m', '--downloadcomps', default=False, action='store_true',
- help=_('also download and uncompress comps.xml'))
parser.add_argument('--download-metadata', default=False, action='store_true',
help=_('download all the metadata.'))
+ parser.add_argument('-m', '--downloadcomps', default=False, action='store_true',
+ help=_('also download and uncompress comps.xml'))
+ parser.add_argument('--metadata-path',
+ help=_('where to store downloaded repository metadata. '
+ 'Defaults to the value of --download-path.'))
parser.add_argument('-n', '--newest-only', default=False, action='store_true',
help=_('download only newest packages per-repo'))
- parser.add_argument('-p', '--download-path', default='./',
- help=_('where to store downloaded repositories'))
parser.add_argument('--norepopath', default=False, action='store_true',
help=_("Don't add the reponame to the download path."))
- parser.add_argument('--metadata-path',
- help=_('where to store downloaded repository metadata. '
- 'Defaults to the value of --download-path.'))
- parser.add_argument('--source', default=False, action='store_true',
- help=_('operate on source packages'))
+ parser.add_argument('-p', '--download-path', default='./',
+ help=_('where to store downloaded repositories'))
parser.add_argument('--remote-time', default=False, action='store_true',
help=_('try to set local timestamps of local files by '
'the one on the server'))
+ parser.add_argument('--source', default=False, action='store_true',
+ help=_('operate on source packages'))
parser.add_argument('-u', '--urls', default=False, action='store_true',
help=_("Just list urls of what would be downloaded, "
"don't download"))
From 978b7f2b1c654fed7b1b4cf45cb607143226804c Mon Sep 17 00:00:00 2001
From: Marek Blaha <mblaha@redhat.com>
Date: Fri, 23 Oct 2020 09:14:02 +0200
Subject: [PATCH 2/2] [reposync] Check GPG signatures of downloaded packages
(RhBug:1856818)
YUMv3 reposync used to have --gpgcheck option to remove packages that fail GPG
signature checking after downloading.
This patch implements the option for DNF.
= changelog =
msg: Add --gpgcheck option to reposync (RhBug:1856818)
type: enhancement
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1856818
---
doc/reposync.rst | 4 ++++
plugins/reposync.py | 21 +++++++++++++++++++++
2 files changed, 25 insertions(+)
diff --git a/doc/reposync.rst b/doc/reposync.rst
index 3b820f33..de40957f 100644
--- a/doc/reposync.rst
+++ b/doc/reposync.rst
@@ -48,6 +48,10 @@ All general DNF options are accepted. Namely, the ``--repoid`` option can be use
``--download-metadata``
Download all repository metadata. Downloaded copy is instantly usable as a repository, no need to run createrepo_c on it.
+``-g, --gpgcheck``
+ Remove packages that fail GPG signature checking after downloading. Exit code is ``1`` if at least one package was removed.
+ Note that for repositories with ``gpgcheck=0`` set in their configuration the GPG signature is not checked even with this option used.
+
``-m, --downloadcomps``
Also download and uncompress comps.xml. Consider using ``--download-metadata`` option which will download all available repository metadata.
diff --git a/plugins/reposync.py b/plugins/reposync.py
index 6f572cac..c891bfa2 100644
--- a/plugins/reposync.py
+++ b/plugins/reposync.py
@@ -24,6 +24,7 @@
import hawkey
import os
import shutil
+import types
from dnfpluginscore import _, logger
from dnf.cli.option_parser import OptionParser
@@ -65,6 +66,9 @@ def set_argparser(parser):
help=_('delete local packages no longer present in repository'))
parser.add_argument('--download-metadata', default=False, action='store_true',
help=_('download all the metadata.'))
+ parser.add_argument('-g', '--gpgcheck', default=False, action='store_true',
+ help=_('Remove packages that fail GPG signature checking '
+ 'after downloading'))
parser.add_argument('-m', '--downloadcomps', default=False, action='store_true',
help=_('also download and uncompress comps.xml'))
parser.add_argument('--metadata-path',
@@ -114,6 +118,7 @@ def configure(self):
def run(self):
self.base.conf.keepcache = True
+ gpgcheck_ok = True
for repo in self.base.repos.iter_enabled():
if self.opts.remote_time:
repo._repo.setPreserveRemoteTime(True)
@@ -150,8 +155,24 @@ def run(self):
self.print_urls(pkglist)
else:
self.download_packages(pkglist)
+ if self.opts.gpgcheck:
+ for pkg in pkglist:
+ local_path = self.pkg_download_path(pkg)
+ # base.package_signature_check uses pkg.localPkg() to determine
+ # the location of the package rpm file on the disk.
+ # Set it to the correct download path.
+ pkg.localPkg = types.MethodType(
+ lambda s, local_path=local_path: local_path, pkg)
+ result, error = self.base.package_signature_check(pkg)
+ if result != 0:
+ logger.warning(_("Removing {}: {}").format(
+ os.path.basename(local_path), error))
+ os.unlink(local_path)
+ gpgcheck_ok = False
if self.opts.delete:
self.delete_old_local_packages(repo, pkglist)
+ if not gpgcheck_ok:
+ raise dnf.exceptions.Error(_("GPG signature check failed."))
def repo_target(self, repo):
return _pkgdir(self.opts.destdir or self.opts.download_path,

View File

@ -1,41 +0,0 @@
From 9fc9615a07cb314edca953ab71caec27b53fac6d Mon Sep 17 00:00:00 2001
From: Marek Blaha <mblaha@redhat.com>
Date: Thu, 2 Jul 2020 14:29:18 +0200
Subject: [PATCH 4/5] Reorder options in dnf-debug man page alphabetically
---
doc/debug.rst | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/doc/debug.rst b/doc/debug.rst
index 13ac071..ee9860f 100644
--- a/doc/debug.rst
+++ b/doc/debug.rst
@@ -57,16 +57,16 @@ All general DNF options are accepted, see `Options` in :manpage:`dnf(8)` for det
``dnf debug-restore``
-``--output``
- Only output list of packages which will be installed or removed.
- No actuall changes are done.
-
-``--install-latest``
- When installing use the latest package of the same name and architecture.
+``--filter-types=[install,remove,replace]``
+ Limit package changes to specified type.
``--ignore-arch``
When installing package ignore architecture and install missing packages
matching the name, epoch, version and release.
-``--filter-types=[install,remove,replace]``
- Limit package changes to specified type.
+``--install-latest``
+ When installing use the latest package of the same name and architecture.
+
+``--output``
+ Only output list of packages which will be installed or removed.
+ No actuall changes are done.
--
2.25.4

File diff suppressed because it is too large Load Diff

View File

@ -1,90 +0,0 @@
From 5a05773dfcfbd317e082a8a58edc391d53bed845 Mon Sep 17 00:00:00 2001
From: Marek Blaha <mblaha@redhat.com>
Date: Thu, 2 Jul 2020 14:30:34 +0200
Subject: [PATCH 5/5] [debug] Do not remove install-only packages
(RhBug:1844533)
Running debug-restore command may result in an attempt to remove the
running kernel (in case the running kernel package is not present in the
dump file).
Newly the install-only packages are not removed, only the versions
mentioned in the dump file are marked for installation (the decision on
what versions to keep on the system is done according to
installonly_limit config option).
New option `--remove-installonly` to force removal of those versions of
install-only packages that are not present in the dump file is
introduced.
https://bugzilla.redhat.com/show_bug.cgi?id=1844533
---
doc/debug.rst | 18 +++++++++++++++++-
plugins/debug.py | 13 +++++++++----
2 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/doc/debug.rst b/doc/debug.rst
index ee9860f..2f8418f 100644
--- a/doc/debug.rst
+++ b/doc/debug.rst
@@ -23,7 +23,18 @@ DNF debug Plugin
Description
-----------
-Writes system RPM configuration to a dump file and restore it.
+The plugin provides two dnf commands:
+
+``debug-dump``
+ Writes system RPM configuration to a dump file
+
+``debug-restore``
+ Restore the installed packages to the versions written in the dump file. By
+ default, it does not remove already installed versions of install-only
+ packages and only marks those versions that are mentioned in the dump file
+ for installation. The final decision on which versions to keep on the
+ system is left to dnf and can be fine-tuned using the `installonly_limit`
+ (see :manpage:`dnf.conf(5)`) configuration option.
.. note:: DNF and Yum debug files are not compatible and thus can't be used
by the other program.
@@ -70,3 +81,8 @@ All general DNF options are accepted, see `Options` in :manpage:`dnf(8)` for det
``--output``
Only output list of packages which will be installed or removed.
No actuall changes are done.
+
+``--remove-installonly``
+ Allow removal of install-only packages. Using this option may result in an
+ attempt to remove the running kernel version (in situations when the currently
+ running kernel version is not part of the dump file).
diff --git a/plugins/debug.py b/plugins/debug.py
index efe6bea..ad136a9 100644
--- a/plugins/debug.py
+++ b/plugins/debug.py
@@ -194,6 +194,10 @@ class DebugRestoreCommand(dnf.cli.Command):
"--filter-types", metavar="[install, remove, replace]",
default="install, remove, replace",
help=_("limit to specified type"))
+ parser.add_argument(
+ "--remove-installonly", action="store_true",
+ help=_('Allow removing of install-only packages. Using this option may '
+ 'result in an attempt to remove the running kernel.'))
parser.add_argument(
"filename", nargs=1, help=_("name of dump file"))
@@ -238,10 +242,11 @@ class DebugRestoreCommand(dnf.cli.Command):
# package should not be installed
pkg_remove = True
if pkg_remove and "remove" in opts.filter_types:
- if opts.output:
- print("remove %s" % spec)
- else:
- self.base.package_remove(pkg)
+ if pkg not in installonly_pkgs or opts.remove_installonly:
+ if opts.output:
+ print("remove %s" % spec)
+ else:
+ self.base.package_remove(pkg)
def process_dump(self, dump_pkgs, opts):
for (n, a) in sorted(dump_pkgs.keys()):
--
2.25.4

File diff suppressed because it is too large Load Diff

View File

@ -1,96 +0,0 @@
From 37d60b626fcb3e3f68b02c2c24e4ae5149cf223f Mon Sep 17 00:00:00 2001
From: Marek Blaha <mblaha@redhat.com>
Date: Thu, 23 Jul 2020 16:27:22 +0200
Subject: [PATCH] [reposync] Add latest NEVRAs per stream to download (RhBug:
1833074)
This covers situation when package with the newest NEVRA is part of
an older version of a stream and reposync was used with --newest-only
switch.
With this patch these package versions are going to be downloaded:
- the latest NEVRAs from non-modular packages
- all packages from stream version with the latest package NEVRA (in
case the latest NEVRA is part of multiple stream versions only the
highest is downloaded)
- all packages from the latest stream version
https://bugzilla.redhat.com/show_bug.cgi?id=1833074
---
plugins/reposync.py | 53 ++++++++++++++++++++++++++++++++++++---------
1 file changed, 43 insertions(+), 10 deletions(-)
diff --git a/plugins/reposync.py b/plugins/reposync.py
index 548a05b4..7556e7eb 100644
--- a/plugins/reposync.py
+++ b/plugins/reposync.py
@@ -207,27 +207,60 @@ def download_metadata(self, repo):
def _get_latest(self, query):
"""
- return query with latest nonmodular package and all packages from latest version per stream
+ return union of these queries:
+ - the latest NEVRAs from non-modular packages
+ - all packages from stream version with the latest package NEVRA
+ (this should not be needed but the latest package NEVRAs might be
+ part of an older module version)
+ - all packages from the latest stream version
"""
if not dnf.base.WITH_MODULES:
return query.latest()
+
query.apply()
module_packages = self.base._moduleContainer.getModulePackages()
all_artifacts = set()
module_dict = {} # {NameStream: {Version: [modules]}}
+ artifact_version = {} # {artifact: {NameStream: [Version]}}
for module_package in module_packages:
- all_artifacts.update(module_package.getArtifacts())
+ artifacts = module_package.getArtifacts()
+ all_artifacts.update(artifacts)
module_dict.setdefault(module_package.getNameStream(), {}).setdefault(
module_package.getVersionNum(), []).append(module_package)
- non_modular_latest = query.filter(
+ for artifact in artifacts:
+ artifact_version.setdefault(artifact, {}).setdefault(
+ module_package.getNameStream(), []).append(module_package.getVersionNum())
+
+ # the latest NEVRAs from non-modular packages
+ latest_query = query.filter(
pkg__neq=query.filter(nevra_strict=all_artifacts)).latest()
- latest_artifacts = set()
- for version_dict in module_dict.values():
- keys = sorted(version_dict.keys(), reverse=True)
- for module in version_dict[keys[0]]:
- latest_artifacts.update(module.getArtifacts())
- latest_modular_query = query.filter(nevra_strict=latest_artifacts)
- return latest_modular_query.union(non_modular_latest)
+
+ # artifacts from the newest version and those versions that contain an artifact
+ # with the highest NEVRA
+ latest_stream_artifacts = set()
+ for namestream, version_dict in module_dict.items():
+ # versions that will be synchronized
+ versions = set()
+ # add the newest stream version
+ versions.add(sorted(version_dict.keys(), reverse=True)[0])
+ # collect all artifacts in all stream versions
+ stream_artifacts = set()
+ for modules in version_dict.values():
+ for module in modules:
+ stream_artifacts.update(module.getArtifacts())
+ # find versions to which the packages with the highest NEVRAs belong
+ for latest_pkg in query.filter(nevra_strict=stream_artifacts).latest():
+ # here we depend on modules.yaml allways containing full NEVRA (including epoch)
+ nevra = "{0.name}-{0.epoch}:{0.version}-{0.release}.{0.arch}".format(latest_pkg)
+ # download only highest version containing the latest artifact
+ versions.add(max(artifact_version[nevra][namestream]))
+ # add all artifacts from selected versions for synchronization
+ for version in versions:
+ for module in version_dict[version]:
+ latest_stream_artifacts.update(module.getArtifacts())
+ latest_query = latest_query.union(query.filter(nevra_strict=latest_stream_artifacts))
+
+ return latest_query
def get_pkglist(self, repo):
query = self.base.sack.query(flags=hawkey.IGNORE_MODULAR_EXCLUDES).available().filterm(

View File

@ -31,19 +31,16 @@
%endif
Name: dnf-plugins-core
Version: 4.0.17
Release: 5%{?dist}
Version: 4.0.18
Release: 4%{?dist}
Summary: Core Plugins for DNF
License: GPLv2+
URL: https://github.com/rpm-software-management/dnf-plugins-core
Source0: %{url}/archive/%{version}/%{name}-%{version}.tar.gz
Patch1: 0001-test-plugin-crash-if-needs-restarting-d-does-not-exist.patch
Patch2: 0002-Fix-debug-restore-command-RhBug-1844533.patch
Patch3: 0003-debug-Use-standard-demands.resolving-for-transaction.patch
Patch4: 0004-Reorder-options-in-dnf-debug-man-page-alphabetically.patch
Patch5: 0005-debug-Do-not-remove-install-only-packages-RhBug-1844.patch
Patch6: 0006-Update-translations-RhBug-1820546.patch
Patch7: 0007-reposync-Add-latest-NEVRAs-per-stream-to-download-RhBug-1833074.patch
Patch1: 0001-groups-manager-Re-introduce-yum-groups-manager-funct.patch
Patch2: 0002-needs-restarting-add-s-to-list-services-RhBug-177293.patch
Patch3: 0003-reposync-Check-GPG-signatures-of-downloaded-packages-RhBug-1856818.patch
Patch4: 0004-Update-translations.patch
BuildArch: noarch
BuildRequires: cmake
BuildRequires: gettext
@ -63,6 +60,7 @@ Provides: dnf-command(debug-dump)
Provides: dnf-command(debug-restore)
Provides: dnf-command(debuginfo-install)
Provides: dnf-command(download)
Provides: dnf-command(groups-manager)
Provides: dnf-command(repoclosure)
Provides: dnf-command(repograph)
Provides: dnf-command(repomanage)
@ -78,6 +76,7 @@ Provides: dnf-plugin-debuginfo-install = %{version}-%{release}
Provides: dnf-plugin-download = %{version}-%{release}
Provides: dnf-plugin-generate_completion_cache = %{version}-%{release}
Provides: dnf-plugin-needs_restarting = %{version}-%{release}
Provides: dnf-plugin-groups-manager = %{version}-%{release}
Provides: dnf-plugin-repoclosure = %{version}-%{release}
Provides: dnf-plugin-repodiff = %{version}-%{release}
Provides: dnf-plugin-repograph = %{version}-%{release}
@ -92,7 +91,7 @@ Conflicts: dnf-plugins-extras-common-data < %{dnf_plugins_extra}
%description
Core Plugins for DNF. This package enhances DNF with builddep, config-manager,
copr, debug, debuginfo-install, download, needs-restarting, repoclosure,
copr, debug, debuginfo-install, download, groups-manager, needs-restarting, repoclosure,
repograph, repomanage, reposync, changelog and repodiff commands. Additionally
provides generate_completion_cache passive plugin.
@ -102,8 +101,10 @@ Summary: Core Plugins for DNF
%{?python_provide:%python_provide python2-%{name}}
BuildRequires: python2-dnf >= %{dnf_lowest_compatible}
%if 0%{?rhel} && 0%{?rhel} <= 7
BuildRequires: dbus-python
BuildRequires: python-nose
%else
BuildRequires: python2-dbus
BuildRequires: python2-nose
%endif
BuildRequires: python2-devel
@ -113,8 +114,10 @@ Requires: python2-distro
Requires: python2-dnf >= %{dnf_lowest_compatible}
Requires: python2-hawkey >= %{hawkey_version}
%if 0%{?rhel} && 0%{?rhel} <= 7
Requires: dbus-python
Requires: python-dateutil
%else
Requires: python2-dbus
Requires: python2-dateutil
%endif
Provides: python2-dnf-plugins-extras-debug = %{version}-%{release}
@ -133,7 +136,7 @@ Conflicts: python-%{name} < %{version}-%{release}
%description -n python2-%{name}
Core Plugins for DNF, Python 2 interface. This package enhances DNF with builddep,
config-manager, copr, degug, debuginfo-install, download, needs-restarting,
config-manager, copr, degug, debuginfo-install, download, groups-manager, needs-restarting,
repoclosure, repograph, repomanage, reposync, changelog and repodiff commands.
Additionally provides generate_completion_cache passive plugin.
%endif
@ -142,12 +145,14 @@ Additionally provides generate_completion_cache passive plugin.
%package -n python3-%{name}
Summary: Core Plugins for DNF
%{?python_provide:%python_provide python3-%{name}}
BuildRequires: python3-dbus
BuildRequires: python3-devel
BuildRequires: python3-dnf >= %{dnf_lowest_compatible}
BuildRequires: python3-nose
%if 0%{?fedora}
Requires: python3-distro
%endif
Requires: python3-dbus
Requires: python3-dnf >= %{dnf_lowest_compatible}
Requires: python3-hawkey >= %{hawkey_version}
Requires: python3-dateutil
@ -167,7 +172,7 @@ Conflicts: python-%{name} < %{version}-%{release}
%description -n python3-%{name}
Core Plugins for DNF, Python 3 interface. This package enhances DNF with builddep,
config-manager, copr, debug, debuginfo-install, download, needs-restarting,
config-manager, copr, debug, debuginfo-install, download, groups-manager, needs-restarting,
repoclosure, repograph, repomanage, reposync, changelog and repodiff commands.
Additionally provides generate_completion_cache passive plugin.
%endif
@ -195,8 +200,8 @@ Summary: Yum-utils CLI compatibility layer
%description -n %{yum_utils_subpackage_name}
As a Yum-utils CLI compatibility layer, supplies in CLI shims for
debuginfo-install, repograph, package-cleanup, repoclosure, repomanage,
repoquery, reposync, repotrack, repodiff, builddep, config-manager, debug
and download that use new implementations using DNF.
repoquery, reposync, repotrack, repodiff, builddep, config-manager, debug,
download and yum-groups-manager that use new implementations using DNF.
%endif
%if 0%{?rhel} == 0 && %{with python2}
@ -463,6 +468,7 @@ ln -sf %{_libexecdir}/dnf-utils %{buildroot}%{_bindir}/yum-builddep
ln -sf %{_libexecdir}/dnf-utils %{buildroot}%{_bindir}/yum-config-manager
ln -sf %{_libexecdir}/dnf-utils %{buildroot}%{_bindir}/yum-debug-dump
ln -sf %{_libexecdir}/dnf-utils %{buildroot}%{_bindir}/yum-debug-restore
ln -sf %{_libexecdir}/dnf-utils %{buildroot}%{_bindir}/yum-groups-manager
ln -sf %{_libexecdir}/dnf-utils %{buildroot}%{_bindir}/yumdownloader
# These commands don't have a dedicated man page, so let's just point them
# to the utils page which contains their descriptions.
@ -488,6 +494,7 @@ PYTHONPATH=./plugins nosetests-%{python3_version} -s tests/
%{_mandir}/man8/dnf-debuginfo-install.*
%{_mandir}/man8/dnf-download.*
%{_mandir}/man8/dnf-generate_completion_cache.*
%{_mandir}/man8/dnf-groups-manager.*
%{_mandir}/man8/dnf-needs-restarting.*
%{_mandir}/man8/dnf-repoclosure.*
%{_mandir}/man8/dnf-repodiff.*
@ -518,6 +525,7 @@ PYTHONPATH=./plugins nosetests-%{python3_version} -s tests/
%{python2_sitelib}/dnf-plugins/debuginfo-install.*
%{python2_sitelib}/dnf-plugins/download.*
%{python2_sitelib}/dnf-plugins/generate_completion_cache.*
%{python2_sitelib}/dnf-plugins/groups_manager.*
%{python2_sitelib}/dnf-plugins/needs_restarting.*
%{python2_sitelib}/dnf-plugins/repoclosure.*
%{python2_sitelib}/dnf-plugins/repodiff.*
@ -543,6 +551,7 @@ PYTHONPATH=./plugins nosetests-%{python3_version} -s tests/
%{python3_sitelib}/dnf-plugins/debuginfo-install.py
%{python3_sitelib}/dnf-plugins/download.py
%{python3_sitelib}/dnf-plugins/generate_completion_cache.py
%{python3_sitelib}/dnf-plugins/groups_manager.py
%{python3_sitelib}/dnf-plugins/needs_restarting.py
%{python3_sitelib}/dnf-plugins/repoclosure.py
%{python3_sitelib}/dnf-plugins/repodiff.py
@ -557,6 +566,7 @@ PYTHONPATH=./plugins nosetests-%{python3_version} -s tests/
%{python3_sitelib}/dnf-plugins/__pycache__/debuginfo-install.*
%{python3_sitelib}/dnf-plugins/__pycache__/download.*
%{python3_sitelib}/dnf-plugins/__pycache__/generate_completion_cache.*
%{python3_sitelib}/dnf-plugins/__pycache__/groups_manager.*
%{python3_sitelib}/dnf-plugins/__pycache__/needs_restarting.*
%{python3_sitelib}/dnf-plugins/__pycache__/repoclosure.*
%{python3_sitelib}/dnf-plugins/__pycache__/repodiff.*
@ -584,6 +594,7 @@ PYTHONPATH=./plugins nosetests-%{python3_version} -s tests/
%{_bindir}/yum-config-manager
%{_bindir}/yum-debug-dump
%{_bindir}/yum-debug-restore
%{_bindir}/yum-groups-manager
%{_bindir}/yumdownloader
%{_mandir}/man1/debuginfo-install.*
%{_mandir}/man1/needs-restarting.*
@ -596,6 +607,7 @@ PYTHONPATH=./plugins nosetests-%{python3_version} -s tests/
%{_mandir}/man1/yum-config-manager.*
%{_mandir}/man1/yum-debug-dump.*
%{_mandir}/man1/yum-debug-restore.*
%{_mandir}/man1/yum-groups-manager.*
%{_mandir}/man1/yumdownloader.*
%{_mandir}/man1/package-cleanup.*
%{_mandir}/man1/dnf-utils.*
@ -617,6 +629,7 @@ PYTHONPATH=./plugins nosetests-%{python3_version} -s tests/
%exclude %{_mandir}/man1/yum-config-manager.*
%exclude %{_mandir}/man1/yum-debug-dump.*
%exclude %{_mandir}/man1/yum-debug-restore.*
%exclude %{_mandir}/man1/yum-groups-manager.*
%exclude %{_mandir}/man1/yumdownloader.*
%exclude %{_mandir}/man1/package-cleanup.*
%exclude %{_mandir}/man1/dnf-utils.*
@ -749,6 +762,29 @@ PYTHONPATH=./plugins nosetests-%{python3_version} -s tests/
%endif
%changelog
* Mon Mar 8 2021 Marek Blaha <mblaha@redhat.com> - 4.0.18-4
- Update translations (RhBug:1899687)
* Fri Jan 15 2021 Nicola Sella <nsella@redhat.com> - 4.0.18-3
- [reposync] Check GPG signatures of downloaded packages (RhBug:1856818)
* Tue Dec 8 2020 Marek Blaha <mblaha@redhat.com> - 4.0.18-2
- Introduce groups-manager plugin (RhBug:1826016)
- [needs-restarting] add -s to list services (RhBug:1772939)
* Tue Nov 10 2020 Nicola Sella <nsella@redhat.com> - 4.0.18-1
- Update to 4.0.18
- [needs-restarting] Fix plugin fail if needs-restarting.d does not exist
- [needs-restarting] add kernel-rt to reboot list
- Fix debug-restore command
- [config-manager] enable/disable comma separated pkgs (RhBug:1830530)
- [debug] Use standard demands.resolving for transaction handling
- [debug] Do not remove install-only packages (RhBug:1844533)
- return error when dnf download failed
- README: Reference Fedora Weblate instead of Zanata
- [reposync] Add latest NEVRAs per stream to download (RhBug: 1833074)
- copr: don't try to list runtime dependencies
* Wed Aug 05 2020 Nicola Sella <nsella@redhat.com> - 4.0.17-5
- [reposync] Add latest NEVRAs per stream to download (RhBug: 1833074)