commit 3c4e6168868a0c37c582bdc9a09f455ad04178db Author: CentOS Sources Date: Sat Feb 20 06:10:51 2021 +0000 import dnf-4.4.2-10.el8 diff --git a/.dnf.metadata b/.dnf.metadata new file mode 100644 index 0000000..9125cc8 --- /dev/null +++ b/.dnf.metadata @@ -0,0 +1 @@ +5941a49cfd466aeed4ec882a33647912c2a89245 SOURCES/dnf-4.4.2.tar.gz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378380e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/dnf-4.4.2.tar.gz diff --git a/SOURCES/0001-tests-SQL-write-a-readonly-folder.patch b/SOURCES/0001-tests-SQL-write-a-readonly-folder.patch new file mode 100644 index 0000000..39780f4 --- /dev/null +++ b/SOURCES/0001-tests-SQL-write-a-readonly-folder.patch @@ -0,0 +1,45 @@ +From 66e08009b8254462cb2c454ff2320355633c20d6 Mon Sep 17 00:00:00 2001 +From: Nicola Sella +Date: Tue, 10 Nov 2020 12:11:17 +0100 +Subject: [PATCH 1/1] [tests] SQL write a readonly folder + +fixes on rhel8.4 for test_dnf_base and test_dnf_db_group +libdnf._error.Error: SQLite error on "/var/lib/dnf/history.sqlite": +Executing an SQL statement failed: attempt to write a readonly +database + +=changelog= +msg: fixes SQL write a readonly folder +type: bugfix +--- + tests/api/test_dnf_base.py | 1 + + tests/api/test_dnf_db_group.py | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/tests/api/test_dnf_base.py b/tests/api/test_dnf_base.py +index b1cf49fb..ca71b75c 100644 +--- a/tests/api/test_dnf_base.py ++++ b/tests/api/test_dnf_base.py +@@ -14,6 +14,7 @@ from .common import TOUR_4_4 + class DnfBaseApiTest(TestCase): + def setUp(self): + self.base = dnf.Base(dnf.conf.Conf()) ++ self.base.conf.persistdir = "/tmp/tests" + + def tearDown(self): + self.base.close() +diff --git a/tests/api/test_dnf_db_group.py b/tests/api/test_dnf_db_group.py +index 447fd121..e1828cb4 100644 +--- a/tests/api/test_dnf_db_group.py ++++ b/tests/api/test_dnf_db_group.py +@@ -12,6 +12,7 @@ from .common import TestCase + class DnfRPMTransactionApiTest(TestCase): + def setUp(self): + self.base = dnf.Base(dnf.conf.Conf()) ++ self.base.conf.persistdir = "/tmp/tests" + self.base.fill_sack(False, False) + self.base.resolve() + self.rpmTrans = self.base.transaction +-- +2.26.2 + diff --git a/SOURCES/0002-Revert-Fix-setopt-cachedir-writing-outside-of-installroot.patch b/SOURCES/0002-Revert-Fix-setopt-cachedir-writing-outside-of-installroot.patch new file mode 100644 index 0000000..a844311 --- /dev/null +++ b/SOURCES/0002-Revert-Fix-setopt-cachedir-writing-outside-of-installroot.patch @@ -0,0 +1,26 @@ +From c2e4901cec947e5be2e5ff5afa22691841d00bdc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hr=C3=A1zk=C3=BD?= +Date: Tue, 10 Nov 2020 13:46:57 +0100 +Subject: [PATCH] Revert "Fix --setopt=cachedir writing outside of installroot" + +This reverts commit 70fffff61f7a006d406b46adc592d21a685c12a8. + +The commit breaks resetting excludes with an empty --exclude= CLI switch +if the excludes were set in the config file. +--- + dnf/cli/cli.py | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py +index b5f7bca07b..5878c2aa15 100644 +--- a/dnf/cli/cli.py ++++ b/dnf/cli/cli.py +@@ -974,6 +974,8 @@ def configure(self, args, option_parser=None): + + self.base.configure_plugins() + ++ self.base.conf._configure_from_options(opts) ++ + self.command.configure() + + if self.base.conf.destdir: diff --git a/SOURCES/0003-Post-transaction-summary-is-logged-for-API-users-RhBug-1855158.patch b/SOURCES/0003-Post-transaction-summary-is-logged-for-API-users-RhBug-1855158.patch new file mode 100644 index 0000000..d34928f --- /dev/null +++ b/SOURCES/0003-Post-transaction-summary-is-logged-for-API-users-RhBug-1855158.patch @@ -0,0 +1,567 @@ +From 9ed390d08a9f2b66f4e352532fa526fc64e329d4 Mon Sep 17 00:00:00 2001 +From: Marek Blaha +Date: Tue, 28 Jul 2020 09:50:10 +0200 +Subject: [PATCH 1/3] Remove unused loops attribute from + DepSolveProgressCallBack + +--- + dnf/cli/output.py | 5 ----- + 1 file changed, 5 deletions(-) + +diff --git a/dnf/cli/output.py b/dnf/cli/output.py +index de188ffbd1..44d5f8b89f 100644 +--- a/dnf/cli/output.py ++++ b/dnf/cli/output.py +@@ -1987,10 +1987,6 @@ def historyInfoCmdPkgsAltered(self, old, pats=[]): + class DepSolveProgressCallBack(dnf.callback.Depsolve): + """Provides text output callback functions for Dependency Solver callback.""" + +- def __init__(self): +- """requires yum-cli log and errorlog functions as arguments""" +- self.loops = 0 +- + def pkg_added(self, pkg, mode): + """Print information about a package being added to the + transaction set. +@@ -2037,7 +2033,6 @@ def start(self): + process. + """ + logger.debug(_('--> Starting dependency resolution')) +- self.loops += 1 + + def end(self): + """Output a message stating that dependency resolution has finished.""" + +From 0ee646f4965c597f2832ed3df9d9f0e6546dcc54 Mon Sep 17 00:00:00 2001 +From: Marek Blaha +Date: Wed, 21 Oct 2020 11:47:43 +0200 +Subject: [PATCH 2/3] Remove unused parameter of _make_lists() + +--- + dnf/cli/output.py | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/dnf/cli/output.py b/dnf/cli/output.py +index 44d5f8b89f..af8a968770 100644 +--- a/dnf/cli/output.py ++++ b/dnf/cli/output.py +@@ -52,7 +52,8 @@ + + logger = logging.getLogger('dnf') + +-def _make_lists(transaction, goal): ++ ++def _make_lists(transaction): + b = dnf.util.Bunch({ + 'downgraded': [], + 'erased': [], +@@ -1101,7 +1102,7 @@ def list_transaction(self, transaction, total_width=None): + # in order to display module changes when RPM transaction is empty + transaction = [] + +- list_bunch = _make_lists(transaction, self.base._goal) ++ list_bunch = _make_lists(transaction) + pkglist_lines = [] + data = {'n' : {}, 'v' : {}, 'r' : {}} + a_wid = 0 # Arch can't get "that big" ... so always use the max. +@@ -1488,7 +1489,7 @@ def _tsi_or_pkg_nevra_cmp(item1, item2): + return (item1.arch > item2.arch) - (item1.arch < item2.arch) + + out = '' +- list_bunch = _make_lists(transaction, self.base._goal) ++ list_bunch = _make_lists(transaction) + + skipped_conflicts, skipped_broken = self._skipped_packages( + report_problems=False, transaction=transaction) + +From 865b7183453684de6a25e77fddf5a2d11fbffba8 Mon Sep 17 00:00:00 2001 +From: Marek Blaha +Date: Wed, 21 Oct 2020 17:59:46 +0200 +Subject: [PATCH 3/3] Post transaction summary is logged for API users + (RhBug:1855158) + +Post transaction summary is always logged into /var/log/dnf.log. +When transaction is called from cli, the summary is also printed to +stdout in columns (as previously). + += changelog = +msg: Packages installed/removed via DNF API are logged into dnf.log +type: enhancement +resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1855158 +--- + dnf/base.py | 46 ++++++++++++- + dnf/cli/cli.py | 8 ++- + dnf/cli/output.py | 167 ++++++++-------------------------------------- + dnf/util.py | 102 +++++++++++++++++++++++++++- + 4 files changed, 177 insertions(+), 146 deletions(-) + +diff --git a/dnf/base.py b/dnf/base.py +index 075e74265a..c0d7712605 100644 +--- a/dnf/base.py ++++ b/dnf/base.py +@@ -28,12 +28,12 @@ + import dnf + import libdnf.transaction + ++from copy import deepcopy + from dnf.comps import CompsQuery + from dnf.i18n import _, P_, ucd + from dnf.util import _parse_specs + from dnf.db.history import SwdbInterface + from dnf.yum import misc +-from functools import reduce + try: + from collections.abc import Sequence + except ImportError: +@@ -549,7 +549,7 @@ def _ts(self): + if self.conf.ignorearch: + self._rpm_probfilter.add(rpm.RPMPROB_FILTER_IGNOREARCH) + +- probfilter = reduce(operator.or_, self._rpm_probfilter, 0) ++ probfilter = functools.reduce(operator.or_, self._rpm_probfilter, 0) + self._priv_ts.setProbFilter(probfilter) + return self._priv_ts + +@@ -890,6 +890,15 @@ def do_transaction(self, display=()): + self._plugins.unload_removed_plugins(self.transaction) + self._plugins.run_transaction() + ++ # log post transaction summary ++ def _pto_callback(action, tsis): ++ msgs = [] ++ for tsi in tsis: ++ msgs.append('{}: {}'.format(action, str(tsi))) ++ return msgs ++ for msg in dnf.util._post_transaction_output(self, self.transaction, _pto_callback): ++ logger.debug(msg) ++ + return tid + + def _trans_error_summary(self, errstring): +@@ -1311,7 +1320,7 @@ def _do_package_lists(self, pkgnarrow='all', patterns=None, showdups=None, + if patterns is None or len(patterns) == 0: + return list_fn(None) + yghs = map(list_fn, patterns) +- return reduce(lambda a, b: a.merge_lists(b), yghs) ++ return functools.reduce(lambda a, b: a.merge_lists(b), yghs) + + def _list_pattern(self, pkgnarrow, pattern, showdups, ignore_case, + reponame=None): +@@ -2579,6 +2588,37 @@ def setup_loggers(self): + """ + self._logging._setup_from_dnf_conf(self.conf, file_loggers_only=True) + ++ def _skipped_packages(self, report_problems, transaction): ++ """returns set of conflicting packages and set of packages with broken dependency that would ++ be additionally installed when --best and --allowerasing""" ++ if self._goal.actions & (hawkey.INSTALL | hawkey.UPGRADE | hawkey.UPGRADE_ALL): ++ best = True ++ else: ++ best = False ++ ng = deepcopy(self._goal) ++ params = {"allow_uninstall": self._allow_erasing, ++ "force_best": best, ++ "ignore_weak": True} ++ ret = ng.run(**params) ++ if not ret and report_problems: ++ msg = dnf.util._format_resolve_problems(ng.problem_rules()) ++ logger.warning(msg) ++ problem_conflicts = set(ng.problem_conflicts(available=True)) ++ problem_dependency = set(ng.problem_broken_dependency(available=True)) - problem_conflicts ++ ++ def _nevra(item): ++ return hawkey.NEVRA(name=item.name, epoch=item.epoch, version=item.version, ++ release=item.release, arch=item.arch) ++ ++ # Sometimes, pkg is not in transaction item, therefore, comparing by nevra ++ transaction_nevras = [_nevra(tsi) for tsi in transaction] ++ skipped_conflicts = set( ++ [pkg for pkg in problem_conflicts if _nevra(pkg) not in transaction_nevras]) ++ skipped_dependency = set( ++ [pkg for pkg in problem_dependency if _nevra(pkg) not in transaction_nevras]) ++ ++ return skipped_conflicts, skipped_dependency ++ + + def _msg_installed(pkg): + name = ucd(pkg) +diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py +index 0bc2c119d0..334000362c 100644 +--- a/dnf/cli/cli.py ++++ b/dnf/cli/cli.py +@@ -252,8 +252,12 @@ def do_transaction(self, display=()): + trans = None + + if trans: +- msg = self.output.post_transaction_output(trans) +- logger.info(msg) ++ # the post transaction summary is already written to log during ++ # Base.do_transaction() so here only print the messages to the ++ # user arranged in columns ++ print() ++ print('\n'.join(self.output.post_transaction_output(trans))) ++ print() + for tsi in trans: + if tsi.state == libdnf.transaction.TransactionItemState_ERROR: + raise dnf.exceptions.Error(_('Transaction failed')) +diff --git a/dnf/cli/output.py b/dnf/cli/output.py +index af8a968770..6d729b63ba 100644 +--- a/dnf/cli/output.py ++++ b/dnf/cli/output.py +@@ -21,9 +21,7 @@ + from __future__ import print_function + from __future__ import unicode_literals + +-from copy import deepcopy + import fnmatch +-import functools + import hawkey + import itertools + import libdnf.transaction +@@ -53,51 +51,6 @@ + logger = logging.getLogger('dnf') + + +-def _make_lists(transaction): +- b = dnf.util.Bunch({ +- 'downgraded': [], +- 'erased': [], +- 'erased_clean': [], +- 'erased_dep': [], +- 'installed': [], +- 'installed_group': [], +- 'installed_dep': [], +- 'installed_weak': [], +- 'reinstalled': [], +- 'upgraded': [], +- 'failed': [], +- }) +- +- for tsi in transaction: +- if tsi.state == libdnf.transaction.TransactionItemState_ERROR: +- b.failed.append(tsi) +- elif tsi.action == libdnf.transaction.TransactionItemAction_DOWNGRADE: +- b.downgraded.append(tsi) +- elif tsi.action == libdnf.transaction.TransactionItemAction_INSTALL: +- if tsi.reason == libdnf.transaction.TransactionItemReason_GROUP: +- b.installed_group.append(tsi) +- elif tsi.reason == libdnf.transaction.TransactionItemReason_DEPENDENCY: +- b.installed_dep.append(tsi) +- elif tsi.reason == libdnf.transaction.TransactionItemReason_WEAK_DEPENDENCY: +- b.installed_weak.append(tsi) +- else: +- # TransactionItemReason_USER +- b.installed.append(tsi) +- elif tsi.action == libdnf.transaction.TransactionItemAction_REINSTALL: +- b.reinstalled.append(tsi) +- elif tsi.action == libdnf.transaction.TransactionItemAction_REMOVE: +- if tsi.reason == libdnf.transaction.TransactionItemReason_CLEAN: +- b.erased_clean.append(tsi) +- elif tsi.reason == libdnf.transaction.TransactionItemReason_DEPENDENCY: +- b.erased_dep.append(tsi) +- else: +- b.erased.append(tsi) +- elif tsi.action == libdnf.transaction.TransactionItemAction_UPGRADE: +- b.upgraded.append(tsi) +- +- return b +- +- + def _spread_in_columns(cols_count, label, lst): + left = itertools.chain((label,), itertools.repeat('')) + lst_length = len(lst) +@@ -1057,37 +1010,6 @@ def list_group_transaction(self, comps, history, diff): + out[0:0] = self._banner(col_data, (_('Group'), _('Packages'), '', '')) + return '\n'.join(out) + +- def _skipped_packages(self, report_problems, transaction): +- """returns set of conflicting packages and set of packages with broken dependency that would +- be additionally installed when --best and --allowerasing""" +- if self.base._goal.actions & (hawkey.INSTALL | hawkey.UPGRADE | hawkey.UPGRADE_ALL): +- best = True +- else: +- best = False +- ng = deepcopy(self.base._goal) +- params = {"allow_uninstall": self.base._allow_erasing, +- "force_best": best, +- "ignore_weak": True} +- ret = ng.run(**params) +- if not ret and report_problems: +- msg = dnf.util._format_resolve_problems(ng.problem_rules()) +- logger.warning(msg) +- problem_conflicts = set(ng.problem_conflicts(available=True)) +- problem_dependency = set(ng.problem_broken_dependency(available=True)) - problem_conflicts +- +- def _nevra(item): +- return hawkey.NEVRA(name=item.name, epoch=item.epoch, version=item.version, +- release=item.release, arch=item.arch) +- +- # Sometimes, pkg is not in transaction item, therefore, comparing by nevra +- transaction_nevras = [_nevra(tsi) for tsi in transaction] +- skipped_conflicts = set( +- [pkg for pkg in problem_conflicts if _nevra(pkg) not in transaction_nevras]) +- skipped_dependency = set( +- [pkg for pkg in problem_dependency if _nevra(pkg) not in transaction_nevras]) +- +- return skipped_conflicts, skipped_dependency +- + def list_transaction(self, transaction, total_width=None): + """Return a string representation of the transaction in an + easy-to-read format. +@@ -1102,7 +1024,7 @@ def list_transaction(self, transaction, total_width=None): + # in order to display module changes when RPM transaction is empty + transaction = [] + +- list_bunch = _make_lists(transaction) ++ list_bunch = dnf.util._make_lists(transaction) + pkglist_lines = [] + data = {'n' : {}, 'v' : {}, 'r' : {}} + a_wid = 0 # Arch can't get "that big" ... so always use the max. +@@ -1271,7 +1193,7 @@ def format_line(group): + # show skipped conflicting packages + if not self.conf.best and self.base._goal.actions & forward_actions: + lines = [] +- skipped_conflicts, skipped_broken = self._skipped_packages( ++ skipped_conflicts, skipped_broken = self.base._skipped_packages( + report_problems=True, transaction=transaction) + skipped_broken = dict((str(pkg), pkg) for pkg in skipped_broken) + for pkg in sorted(skipped_conflicts): +@@ -1436,13 +1358,8 @@ def format_line(group): + max_msg_count, count, msg_pkgs)) + return ''.join(out) + +- def post_transaction_output(self, transaction): +- """Returns a human-readable summary of the results of the +- transaction. + +- :return: a string containing a human-readable summary of the +- results of the transaction +- """ ++ def _pto_callback(self, action, tsis): + # Works a bit like calcColumns, but we never overflow a column we just + # have a dynamic number of columns. + def _fits_in_cols(msgs, num): +@@ -1472,61 +1389,33 @@ def _fits_in_cols(msgs, num): + col_lens[col] *= -1 + return col_lens + +- def _tsi_or_pkg_nevra_cmp(item1, item2): +- """Compares two transaction items or packages by nevra. +- Used as a fallback when tsi does not contain package object. +- """ +- ret = (item1.name > item2.name) - (item1.name < item2.name) +- if ret != 0: +- return ret +- nevra1 = hawkey.NEVRA(name=item1.name, epoch=item1.epoch, version=item1.version, +- release=item1.release, arch=item1.arch) +- nevra2 = hawkey.NEVRA(name=item2.name, epoch=item2.epoch, version=item2.version, +- release=item2.release, arch=item2.arch) +- ret = nevra1.evr_cmp(nevra2, self.sack) +- if ret != 0: +- return ret +- return (item1.arch > item2.arch) - (item1.arch < item2.arch) +- +- out = '' +- list_bunch = _make_lists(transaction) +- +- skipped_conflicts, skipped_broken = self._skipped_packages( +- report_problems=False, transaction=transaction) +- skipped = skipped_conflicts.union(skipped_broken) +- +- for (action, tsis) in [(_('Upgraded'), list_bunch.upgraded), +- (_('Downgraded'), list_bunch.downgraded), +- (_('Installed'), list_bunch.installed + +- list_bunch.installed_group + +- list_bunch.installed_weak + +- list_bunch.installed_dep), +- (_('Reinstalled'), list_bunch.reinstalled), +- (_('Skipped'), skipped), +- (_('Removed'), list_bunch.erased + +- list_bunch.erased_dep + +- list_bunch.erased_clean), +- (_('Failed'), list_bunch.failed)]: +- if not tsis: +- continue +- msgs = [] +- out += '\n%s:\n' % action +- for tsi in sorted(tsis, key=functools.cmp_to_key(_tsi_or_pkg_nevra_cmp)): +- msgs.append(str(tsi)) +- for num in (8, 7, 6, 5, 4, 3, 2): +- cols = _fits_in_cols(msgs, num) +- if cols: +- break +- if not cols: +- cols = [-(self.term.columns - 2)] +- while msgs: +- current_msgs = msgs[:len(cols)] +- out += ' ' +- out += self.fmtColumns(zip(current_msgs, cols), end=u'\n') +- msgs = msgs[len(cols):] +- ++ if not tsis: ++ return '' ++ out = [] ++ msgs = [] ++ out.append('{}:'.format(action)) ++ for tsi in tsis: ++ msgs.append(str(tsi)) ++ for num in (8, 7, 6, 5, 4, 3, 2): ++ cols = _fits_in_cols(msgs, num) ++ if cols: ++ break ++ if not cols: ++ cols = [-(self.term.columns - 2)] ++ while msgs: ++ current_msgs = msgs[:len(cols)] ++ out.append(' {}'.format(self.fmtColumns(zip(current_msgs, cols)))) ++ msgs = msgs[len(cols):] + return out + ++ ++ def post_transaction_output(self, transaction): ++ """ ++ Return a human-readable summary of the transaction. Packages in sections ++ are arranged to columns. ++ """ ++ return dnf.util._post_transaction_output(self.base, transaction, self._pto_callback) ++ + def setup_progress_callbacks(self): + """Set up the progress callbacks and various + output bars based on debug level. +diff --git a/dnf/util.py b/dnf/util.py +index 8cf362706d..0beb04424d 100644 +--- a/dnf/util.py ++++ b/dnf/util.py +@@ -24,13 +24,14 @@ + + from .pycomp import PY3, basestring + from dnf.i18n import _, ucd +-from functools import reduce + import argparse + import dnf + import dnf.callback + import dnf.const + import dnf.pycomp + import errno ++import functools ++import hawkey + import itertools + import locale + import logging +@@ -41,6 +42,7 @@ + import tempfile + import time + import libdnf.repo ++import libdnf.transaction + + logger = logging.getLogger('dnf') + +@@ -195,7 +197,7 @@ def group_by_filter(fn, iterable): + def splitter(acc, item): + acc[not bool(fn(item))].append(item) + return acc +- return reduce(splitter, iterable, ([], [])) ++ return functools.reduce(splitter, iterable, ([], [])) + + def insert_if(item, iterable, condition): + """Insert an item into an iterable by a condition.""" +@@ -504,3 +506,99 @@ def __setattr__(self, what, val): + def setter(item): + setattr(item, what, val) + return list(map(setter, self)) ++ ++ ++def _make_lists(transaction): ++ b = Bunch({ ++ 'downgraded': [], ++ 'erased': [], ++ 'erased_clean': [], ++ 'erased_dep': [], ++ 'installed': [], ++ 'installed_group': [], ++ 'installed_dep': [], ++ 'installed_weak': [], ++ 'reinstalled': [], ++ 'upgraded': [], ++ 'failed': [], ++ }) ++ ++ for tsi in transaction: ++ if tsi.state == libdnf.transaction.TransactionItemState_ERROR: ++ b.failed.append(tsi) ++ elif tsi.action == libdnf.transaction.TransactionItemAction_DOWNGRADE: ++ b.downgraded.append(tsi) ++ elif tsi.action == libdnf.transaction.TransactionItemAction_INSTALL: ++ if tsi.reason == libdnf.transaction.TransactionItemReason_GROUP: ++ b.installed_group.append(tsi) ++ elif tsi.reason == libdnf.transaction.TransactionItemReason_DEPENDENCY: ++ b.installed_dep.append(tsi) ++ elif tsi.reason == libdnf.transaction.TransactionItemReason_WEAK_DEPENDENCY: ++ b.installed_weak.append(tsi) ++ else: ++ # TransactionItemReason_USER ++ b.installed.append(tsi) ++ elif tsi.action == libdnf.transaction.TransactionItemAction_REINSTALL: ++ b.reinstalled.append(tsi) ++ elif tsi.action == libdnf.transaction.TransactionItemAction_REMOVE: ++ if tsi.reason == libdnf.transaction.TransactionItemReason_CLEAN: ++ b.erased_clean.append(tsi) ++ elif tsi.reason == libdnf.transaction.TransactionItemReason_DEPENDENCY: ++ b.erased_dep.append(tsi) ++ else: ++ b.erased.append(tsi) ++ elif tsi.action == libdnf.transaction.TransactionItemAction_UPGRADE: ++ b.upgraded.append(tsi) ++ ++ return b ++ ++ ++def _post_transaction_output(base, transaction, action_callback): ++ """Returns a human-readable summary of the results of the ++ transaction. ++ ++ :param action_callback: function generating output for specific action. It ++ takes two parameters - action as a string and list of affected packages for ++ this action ++ :return: a list of lines containing a human-readable summary of the ++ results of the transaction ++ """ ++ def _tsi_or_pkg_nevra_cmp(item1, item2): ++ """Compares two transaction items or packages by nevra. ++ Used as a fallback when tsi does not contain package object. ++ """ ++ ret = (item1.name > item2.name) - (item1.name < item2.name) ++ if ret != 0: ++ return ret ++ nevra1 = hawkey.NEVRA(name=item1.name, epoch=item1.epoch, version=item1.version, ++ release=item1.release, arch=item1.arch) ++ nevra2 = hawkey.NEVRA(name=item2.name, epoch=item2.epoch, version=item2.version, ++ release=item2.release, arch=item2.arch) ++ ret = nevra1.evr_cmp(nevra2, base.sack) ++ if ret != 0: ++ return ret ++ return (item1.arch > item2.arch) - (item1.arch < item2.arch) ++ ++ list_bunch = dnf.util._make_lists(transaction) ++ ++ skipped_conflicts, skipped_broken = base._skipped_packages( ++ report_problems=False, transaction=transaction) ++ skipped = skipped_conflicts.union(skipped_broken) ++ ++ out = [] ++ for (action, tsis) in [(_('Upgraded'), list_bunch.upgraded), ++ (_('Downgraded'), list_bunch.downgraded), ++ (_('Installed'), list_bunch.installed + ++ list_bunch.installed_group + ++ list_bunch.installed_weak + ++ list_bunch.installed_dep), ++ (_('Reinstalled'), list_bunch.reinstalled), ++ (_('Skipped'), skipped), ++ (_('Removed'), list_bunch.erased + ++ list_bunch.erased_dep + ++ list_bunch.erased_clean), ++ (_('Failed'), list_bunch.failed)]: ++ out.extend(action_callback( ++ action, sorted(tsis, key=functools.cmp_to_key(_tsi_or_pkg_nevra_cmp)))) ++ ++ return out diff --git a/SOURCES/0004-Log-scriptlets-output-also-for-API-users-RhBug-1847340.patch b/SOURCES/0004-Log-scriptlets-output-also-for-API-users-RhBug-1847340.patch new file mode 100644 index 0000000..8447353 --- /dev/null +++ b/SOURCES/0004-Log-scriptlets-output-also-for-API-users-RhBug-1847340.patch @@ -0,0 +1,130 @@ +From df64fd36d7fefe39a96fea3f41e35785bebd37ec Mon Sep 17 00:00:00 2001 +From: Marek Blaha +Date: Wed, 2 Dec 2020 16:33:26 +0100 +Subject: [PATCH 1/2] Log scriptlets output also for API users (RhBug:1847340) + +Messages logged into /var/log/dnf.rpm.log are now the same for both +command line and API usage. + +https://bugzilla.redhat.com/show_bug.cgi?id=1847340 +--- + dnf/cli/output.py | 7 +------ + dnf/yum/rpmtrans.py | 9 ++++++++- + 2 files changed, 9 insertions(+), 7 deletions(-) + +diff --git a/dnf/cli/output.py b/dnf/cli/output.py +index 51d6829ca6..86260661fc 100644 +--- a/dnf/cli/output.py ++++ b/dnf/cli/output.py +@@ -2151,12 +2151,7 @@ def error(self, message): + pass + + def scriptout(self, msgs): +- """Print messages originating from a package script. +- +- :param msgs: the messages coming from the script +- """ +- if msgs: +- self.rpm_logger.info(ucd(msgs)) ++ pass + + def _makefmt(self, percent, ts_done, ts_total, progress=True, + pkgname=None, wid1=15): +diff --git a/dnf/yum/rpmtrans.py b/dnf/yum/rpmtrans.py +index 447639a476..d6c549d2ed 100644 +--- a/dnf/yum/rpmtrans.py ++++ b/dnf/yum/rpmtrans.py +@@ -113,7 +113,10 @@ def progress(self, package, action, ti_done, ti_total, ts_done, ts_total): + pass + + def scriptout(self, msgs): +- """msgs is the messages that were output (if any).""" ++ """Hook for reporting an rpm scriptlet output. ++ ++ :param msgs: the scriptlet output ++ """ + pass + + def error(self, message): +@@ -156,6 +159,10 @@ def filelog(self, package, action): + msg = '%s: %s' % (action_str, package) + self.rpm_logger.log(dnf.logging.SUBDEBUG, msg) + ++ def scriptout(self, msgs): ++ if msgs: ++ self.rpm_logger.info(ucd(msgs)) ++ + + class RPMTransaction(object): + def __init__(self, base, test=False, displays=()): + +From ee6ffcf640180b2b08d2db50b4b81d2bdefb1f2f Mon Sep 17 00:00:00 2001 +From: Marek Blaha +Date: Thu, 3 Dec 2020 10:08:09 +0100 +Subject: [PATCH 2/2] Straighten inheritance of *Display classes + +--- + dnf/cli/output.py | 15 +++------------ + dnf/yum/rpmtrans.py | 2 +- + 2 files changed, 4 insertions(+), 13 deletions(-) + +diff --git a/dnf/cli/output.py b/dnf/cli/output.py +index 86260661fc..de188ffbd1 100644 +--- a/dnf/cli/output.py ++++ b/dnf/cli/output.py +@@ -37,7 +37,7 @@ + from dnf.cli.format import format_number, format_time + from dnf.i18n import _, C_, P_, ucd, fill_exact_width, textwrap_fill, exact_width, select_short_long + from dnf.pycomp import xrange, basestring, long, unicode, sys_maxsize +-from dnf.yum.rpmtrans import LoggingTransactionDisplay ++from dnf.yum.rpmtrans import TransactionDisplay + from dnf.db.history import MergedTransactionWrapper + import dnf.base + import dnf.callback +@@ -2071,7 +2071,7 @@ def short_id(id): + return self.output.userconfirm() + + +-class CliTransactionDisplay(LoggingTransactionDisplay): ++class CliTransactionDisplay(TransactionDisplay): + """A YUM specific callback class for RPM operations.""" + + width = property(lambda self: dnf.cli.term._term_width()) +@@ -2093,7 +2093,7 @@ def progress(self, package, action, ti_done, ti_total, ts_done, ts_total): + :param package: the package involved in the event + :param action: the type of action that is taking place. Valid + values are given by +- :func:`rpmtrans.LoggingTransactionDisplay.action.keys()` ++ :func:`rpmtrans.TransactionDisplay.action.keys()` + :param ti_done: a number representing the amount of work + already done in the current transaction + :param ti_total: a number representing the total amount of work +@@ -2144,15 +2144,6 @@ def _out_progress(self, ti_done, ti_total, ts_done, ts_total, + if ti_done == ti_total: + print(" ") + +- def filelog(self, package, action): +- pass +- +- def error(self, message): +- pass +- +- def scriptout(self, msgs): +- pass +- + def _makefmt(self, percent, ts_done, ts_total, progress=True, + pkgname=None, wid1=15): + l = len(str(ts_total)) +diff --git a/dnf/yum/rpmtrans.py b/dnf/yum/rpmtrans.py +index d6c549d2ed..51fa921d3e 100644 +--- a/dnf/yum/rpmtrans.py ++++ b/dnf/yum/rpmtrans.py +@@ -143,7 +143,7 @@ def error(self, message): + dnf.util._terminal_messenger('print', message, sys.stderr) + + +-class LoggingTransactionDisplay(ErrorTransactionDisplay): ++class LoggingTransactionDisplay(TransactionDisplay): + ''' + Base class for a RPMTransaction display callback class + ''' diff --git a/SOURCES/0005-dnf-history-operations-that-work-with-comps-correctly.patch b/SOURCES/0005-dnf-history-operations-that-work-with-comps-correctly.patch new file mode 100644 index 0000000..233a2fc --- /dev/null +++ b/SOURCES/0005-dnf-history-operations-that-work-with-comps-correctly.patch @@ -0,0 +1,1411 @@ +From b9a8226185f3ab58e3551b315af2b11a8b2f2ebe Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hr=C3=A1zk=C3=BD?= +Date: Tue, 8 Sep 2020 17:02:59 +0200 +Subject: [PATCH 01/17] Add a get_current() method to SwdbInterface + +The method returns the transaction that is currently being created in +Swdb, before it is stored to sqlite. +--- + VERSION.cmake | 2 +- + dnf.spec | 2 +- + dnf/db/history.py | 3 +++ + 3 files changed, 5 insertions(+), 2 deletions(-) + +diff --git a/dnf/db/history.py b/dnf/db/history.py +index 4d355f95..994cdb01 100644 +--- a/dnf/db/history.py ++++ b/dnf/db/history.py +@@ -381,6 +381,9 @@ class SwdbInterface(object): + prev_trans.altered_gt_rpmdb = True + return result[::-1] + ++ def get_current(self): ++ return TransactionWrapper(self.swdb.getCurrent()) ++ + def set_reason(self, pkg, reason): + """Set reason for package""" + rpm_item = self.rpm._pkg_to_swdb_rpm_item(pkg) +-- +2.26.2 + + +From 3bcf90aadfea98da1397b570fcb3ecc20a89c15d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hr=C3=A1zk=C3=BD?= +Date: Fri, 2 Oct 2020 15:52:19 +0200 +Subject: [PATCH 02/17] transaction-sr: Prefer installing from the original + transaction repository + +In case a package exists in the same repo_id as from which it was +originally installed, prefer the package from that repo when replaying +the transaction. + +Makes a difference in e.g. the system-upgrade plugin, where it ensures +the package is installed from the same repo from which it was downloaded +during the download step. +--- + dnf/transaction_sr.py | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/dnf/transaction_sr.py b/dnf/transaction_sr.py +index 9b9b0749..45ca2ef7 100644 +--- a/dnf/transaction_sr.py ++++ b/dnf/transaction_sr.py +@@ -257,6 +257,7 @@ class TransactionReplay(object): + try: + action = pkg_data["action"] + nevra = pkg_data["nevra"] ++ repo_id = pkg_data["repo_id"] + reason = libdnf.transaction.StringToTransactionItemReason(pkg_data["reason"]) + except KeyError as e: + raise TransactionError( +@@ -282,6 +283,18 @@ class TransactionReplay(object): + epoch = parsed_nevra.epoch if parsed_nevra.epoch is not None else 0 + query = query_na.filter(epoch=epoch, version=parsed_nevra.version, release=parsed_nevra.release) + ++ # In case the package is found in the same repo as in the original ++ # transaction, limit the query to that plus installed packages. IOW ++ # remove packages with the same NEVRA in case they are found in ++ # multiple repos and the repo the package came from originally is one ++ # of them. ++ # This can e.g. make a difference in the system-upgrade plugin, in case ++ # the same NEVRA is in two repos, this makes sure the same repo is used ++ # for both download and upgrade steps of the plugin. ++ query_repo = query.filter(reponame=repo_id) ++ if query_repo: ++ query = query_repo.union(query.installed()) ++ + if not query: + self._raise_or_warn(self._skip_unavailable, _('Cannot find rpm nevra "{nevra}".').format(nevra=nevra)) + return +-- +2.26.2 + + +From acfd6310131769f33165c8de1d064889a80fc259 Mon Sep 17 00:00:00 2001 +From: Daniel Mach +Date: Tue, 24 Nov 2020 10:57:21 +0100 +Subject: [PATCH 03/17] transaction_sr: Enable loading transactions from dict + +--- + dnf/cli/commands/history.py | 2 +- + dnf/transaction_sr.py | 42 +++++++++++++++++++++++++------------ + 2 files changed, 30 insertions(+), 14 deletions(-) + +diff --git a/dnf/cli/commands/history.py b/dnf/cli/commands/history.py +index e381f902..0a6dad9b 100644 +--- a/dnf/cli/commands/history.py ++++ b/dnf/cli/commands/history.py +@@ -270,7 +270,7 @@ class HistoryCommand(commands.Command): + + self.replay = TransactionReplay( + self.base, +- self.opts.transaction_filename, ++ filename=self.opts.transaction_filename, + ignore_installed = self.opts.ignore_installed, + ignore_extras = self.opts.ignore_extras, + skip_unavailable = self.opts.skip_unavailable +diff --git a/dnf/transaction_sr.py b/dnf/transaction_sr.py +index 45ca2ef7..e6b06665 100644 +--- a/dnf/transaction_sr.py ++++ b/dnf/transaction_sr.py +@@ -187,21 +187,23 @@ class TransactionReplay(object): + def __init__( + self, + base, +- fn, ++ filename="", ++ data=None, + ignore_extras=False, + ignore_installed=False, + skip_unavailable=False + ): + """ + :param base: the dnf base +- :param fn: the filename to load the transaction from ++ :param filename: the filename to load the transaction from (conflicts with the 'data' argument) ++ :param data: the dictionary to load the transaction from (conflicts with the 'filename' argument) + :param ignore_extras: whether to ignore extra package pulled into the transaction + :param ignore_installed: whether to ignore installed versions of packages + :param skip_unavailable: whether to skip transaction packages that aren't available + """ + + self._base = base +- self._filename = fn ++ self._filename = filename + self._ignore_installed = ignore_installed + self._ignore_extras = ignore_extras + self._skip_unavailable = skip_unavailable +@@ -213,25 +215,39 @@ class TransactionReplay(object): + self._nevra_reason_cache = {} + self._warnings = [] + ++ if filename and data: ++ raise ValueError(_("Conflicting TransactionReplay arguments have been specified: filename, data")) ++ elif filename: ++ self._load_from_file(filename) ++ else: ++ self._load_from_data(data) ++ ++ ++ def _load_from_file(self, fn): ++ self._filename = fn + with open(fn, "r") as f: + try: +- self._replay_data = json.load(f) ++ replay_data = json.load(f) + except json.decoder.JSONDecodeError as e: + raise TransactionFileError(fn, str(e) + ".") + + try: +- self._verify_toplevel_json(self._replay_data) ++ self._load_from_data(replay_data) ++ except TransactionError as e: ++ raise TransactionFileError(fn, e) + +- self._rpms = self._replay_data.get("rpms", []) +- self._assert_type(self._rpms, list, "rpms", "array") ++ def _load_from_data(self, data): ++ self._replay_data = data ++ self._verify_toplevel_json(self._replay_data) + +- self._groups = self._replay_data.get("groups", []) +- self._assert_type(self._groups, list, "groups", "array") ++ self._rpms = self._replay_data.get("rpms", []) ++ self._assert_type(self._rpms, list, "rpms", "array") + +- self._environments = self._replay_data.get("environments", []) +- self._assert_type(self._environments, list, "environments", "array") +- except TransactionError as e: +- raise TransactionFileError(fn, e) ++ self._groups = self._replay_data.get("groups", []) ++ self._assert_type(self._groups, list, "groups", "array") ++ ++ self._environments = self._replay_data.get("environments", []) ++ self._assert_type(self._environments, list, "environments", "array") + + def _raise_or_warn(self, warn_only, msg): + if warn_only: +-- +2.26.2 + + +From 90d4a2fd72b30b295adcb6da66b8043a70561b33 Mon Sep 17 00:00:00 2001 +From: Daniel Mach +Date: Fri, 20 Nov 2020 19:36:49 +0100 +Subject: [PATCH 04/17] transaction_sr: Store exception attributes for future + use + +--- + dnf/transaction_sr.py | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/dnf/transaction_sr.py b/dnf/transaction_sr.py +index e6b06665..36787de4 100644 +--- a/dnf/transaction_sr.py ++++ b/dnf/transaction_sr.py +@@ -55,6 +55,10 @@ class TransactionFileError(dnf.exceptions.Error): + :param errors: a list of error classes or a string with an error description + """ + ++ # store args in case someone wants to read them from a caught exception ++ self.filename = filename ++ self.errors = errors ++ + if isinstance(errors, (list, tuple)): + if len(errors) > 1: + msg = _('Errors in "{filename}":').format(filename=filename) +-- +2.26.2 + + +From 0ffa7ed9ea73035acaec2c4f916d967701fddda2 Mon Sep 17 00:00:00 2001 +From: Daniel Mach +Date: Fri, 20 Nov 2020 19:04:59 +0100 +Subject: [PATCH 05/17] transaction_sr: Handle serialize_transaction(None) + +--- + dnf/transaction_sr.py | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/dnf/transaction_sr.py b/dnf/transaction_sr.py +index 36787de4..41ddee1f 100644 +--- a/dnf/transaction_sr.py ++++ b/dnf/transaction_sr.py +@@ -120,6 +120,9 @@ def serialize_transaction(transaction): + groups = [] + environments = [] + ++ if transaction is None: ++ return data ++ + for tsi in transaction.packages(): + if tsi.is_package(): + rpms.append({ +-- +2.26.2 + + +From c4bae459caef1d5128bd7ed43fcbb749608449f4 Mon Sep 17 00:00:00 2001 +From: Daniel Mach +Date: Mon, 23 Nov 2020 16:23:53 +0100 +Subject: [PATCH 06/17] transaction_sr: Skip preferred repo lookup if repoid is + empty + +--- + dnf/transaction_sr.py | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/dnf/transaction_sr.py b/dnf/transaction_sr.py +index 41ddee1f..9926bebd 100644 +--- a/dnf/transaction_sr.py ++++ b/dnf/transaction_sr.py +@@ -314,9 +314,10 @@ class TransactionReplay(object): + # This can e.g. make a difference in the system-upgrade plugin, in case + # the same NEVRA is in two repos, this makes sure the same repo is used + # for both download and upgrade steps of the plugin. +- query_repo = query.filter(reponame=repo_id) +- if query_repo: +- query = query_repo.union(query.installed()) ++ if repo_id: ++ query_repo = query.filter(reponame=repo_id) ++ if query_repo: ++ query = query_repo.union(query.installed()) + + if not query: + self._raise_or_warn(self._skip_unavailable, _('Cannot find rpm nevra "{nevra}".').format(nevra=nevra)) +-- +2.26.2 + + +From 3f82f871170be871ce8ec9d509306d751890ac9e Mon Sep 17 00:00:00 2001 +From: Daniel Mach +Date: Fri, 20 Nov 2020 17:44:28 +0100 +Subject: [PATCH 07/17] history: Refactor redo code to use transaction + store/replay + += changelog = +msg: Support comps groups in history redo +type: enhancement +resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1657123 +resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1809565 +resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1809639 +--- + dnf/cli/commands/history.py | 40 +++++++++++++++---------------------- + 1 file changed, 16 insertions(+), 24 deletions(-) + +diff --git a/dnf/cli/commands/history.py b/dnf/cli/commands/history.py +index 0a6dad9b..c28a136a 100644 +--- a/dnf/cli/commands/history.py ++++ b/dnf/cli/commands/history.py +@@ -120,6 +120,10 @@ class HistoryCommand(commands.Command): + if not self.opts.transactions: + raise dnf.cli.CliError(_('No transaction ID or package name given.')) + elif self.opts.transactions_action in ['redo', 'undo', 'rollback']: ++ demands.available_repos = True ++ demands.resolving = True ++ demands.root_user = True ++ + self._require_one_transaction_id = True + if not self.opts.transactions: + msg = _('No transaction ID or package name given.') +@@ -157,28 +161,16 @@ class HistoryCommand(commands.Command): + old = self.base.history_get_transaction(extcmds) + if old is None: + return 1, ['Failed history redo'] +- tm = dnf.util.normalize_time(old.beg_timestamp) +- print('Repeating transaction %u, from %s' % (old.tid, tm)) +- self.output.historyInfoCmdPkgsAltered(old) +- +- for i in old.packages(): +- pkgs = list(self.base.sack.query().filter(nevra=str(i), reponame=i.from_repo)) +- if i.action in dnf.transaction.FORWARD_ACTIONS: +- if not pkgs: +- logger.info(_('No package %s available.'), +- self.output.term.bold(ucd(str(i)))) +- return 1, ['An operation cannot be redone'] +- pkg = pkgs[0] +- self.base.install(str(pkg)) +- elif i.action == libdnf.transaction.TransactionItemAction_REMOVE: +- if not pkgs: +- # package was removed already, we can skip removing it again +- continue +- pkg = pkgs[0] +- self.base.remove(str(pkg)) +- +- self.base.resolve() +- self.base.do_transaction() ++ ++ data = serialize_transaction(old) ++ self.replay = TransactionReplay( ++ self.base, ++ data=data, ++ ignore_installed=True, ++ ignore_extras=True, ++ skip_unavailable=self.opts.skip_unavailable ++ ) ++ self.replay.run() + + def _hcmd_undo(self, extcmds): + try: +@@ -326,13 +318,13 @@ class HistoryCommand(commands.Command): + raise dnf.exceptions.Error(strs[0]) + + def run_resolved(self): +- if self.opts.transactions_action != "replay": ++ if self.opts.transactions_action not in ("replay", "redo"): + return + + self.replay.post_transaction() + + def run_transaction(self): +- if self.opts.transactions_action != "replay": ++ if self.opts.transactions_action not in ("replay", "redo"): + return + + warnings = self.replay.get_warnings() +-- +2.26.2 + + +From d1b78ba8449b319121b5208c5b39609b1c6b61de Mon Sep 17 00:00:00 2001 +From: Daniel Mach +Date: Fri, 20 Nov 2020 19:07:50 +0100 +Subject: [PATCH 08/17] history: Refactor rollback code to use transaction + store/replay + += changelog = +msg: Support comps groups in history rollback +type: enhancement +resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1657123 +resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1809565 +resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1809639 +--- + dnf/cli/cli.py | 56 ----------------------------- + dnf/cli/commands/history.py | 72 ++++++++++++++++++++++++++++++++++--- + 2 files changed, 67 insertions(+), 61 deletions(-) + +diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py +index cd720a97..36671fd8 100644 +--- a/dnf/cli/cli.py ++++ b/dnf/cli/cli.py +@@ -627,62 +627,6 @@ class BaseCli(dnf.Base): + logger.critical(_('Found more than one transaction ID!')) + return old[0] + +- def history_rollback_transaction(self, extcmd): +- """Rollback given transaction.""" +- old = self.history_get_transaction((extcmd,)) +- if old is None: +- return 1, ['Failed history rollback, no transaction'] +- last = self.history.last() +- if last is None: +- return 1, ['Failed history rollback, no last?'] +- if old.tid == last.tid: +- return 0, ['Rollback to current, nothing to do'] +- +- mobj = None +- for trans in self.history.old(list(range(old.tid + 1, last.tid + 1))): +- if trans.altered_lt_rpmdb: +- logger.warning(_('Transaction history is incomplete, before %u.'), trans.tid) +- elif trans.altered_gt_rpmdb: +- logger.warning(_('Transaction history is incomplete, after %u.'), trans.tid) +- +- if mobj is None: +- mobj = dnf.db.history.MergedTransactionWrapper(trans) +- else: +- mobj.merge(trans) +- +- tm = dnf.util.normalize_time(old.beg_timestamp) +- print("Rollback to transaction %u, from %s" % (old.tid, tm)) +- print(self.output.fmtKeyValFill(" Undoing the following transactions: ", +- ", ".join((str(x) for x in mobj.tids())))) +- self.output.historyInfoCmdPkgsAltered(mobj) # :todo +- +-# history = dnf.history.open_history(self.history) # :todo +-# m = libdnf.transaction.MergedTransaction() +- +-# return +- +-# operations = dnf.history.NEVRAOperations() +-# for id_ in range(old.tid + 1, last.tid + 1): +-# operations += history.transaction_nevra_ops(id_) +- +- try: +- self._history_undo_operations(mobj, old.tid + 1, True, strict=self.conf.strict) +- except dnf.exceptions.PackagesNotInstalledError as err: +- raise +- logger.info(_('No package %s installed.'), +- self.output.term.bold(ucd(err.pkg_spec))) +- return 1, ['A transaction cannot be undone'] +- except dnf.exceptions.PackagesNotAvailableError as err: +- raise +- logger.info(_('No package %s available.'), +- self.output.term.bold(ucd(err.pkg_spec))) +- return 1, ['A transaction cannot be undone'] +- except dnf.exceptions.MarkingError: +- raise +- assert False +- else: +- return 2, ["Rollback to transaction %u" % (old.tid,)] +- + def history_undo_transaction(self, extcmd): + """Undo given transaction.""" + old = self.history_get_transaction((extcmd,)) +diff --git a/dnf/cli/commands/history.py b/dnf/cli/commands/history.py +index c28a136a..a450aaab 100644 +--- a/dnf/cli/commands/history.py ++++ b/dnf/cli/commands/history.py +@@ -20,6 +20,7 @@ from __future__ import print_function + from __future__ import unicode_literals + + import libdnf ++import hawkey + + from dnf.i18n import _, ucd + from dnf.cli import commands +@@ -33,6 +34,7 @@ import dnf.util + import json + import logging + import os ++import sys + + + logger = logging.getLogger('dnf') +@@ -179,10 +181,70 @@ class HistoryCommand(commands.Command): + return 1, [str(err)] + + def _hcmd_rollback(self, extcmds): ++ old = self.base.history_get_transaction(extcmds) ++ if old is None: ++ return 1, ['Failed history rollback'] ++ last = self.base.history.last() ++ ++ merged_trans = None ++ if old.tid != last.tid: ++ # history.old([]) returns all transactions and we don't want that ++ # so skip merging the transactions when trying to rollback to the last transaction ++ # which is the current system state and rollback is not applicable ++ for trans in self.base.history.old(list(range(old.tid + 1, last.tid + 1))): ++ if trans.altered_lt_rpmdb: ++ logger.warning(_('Transaction history is incomplete, before %u.'), trans.tid) ++ elif trans.altered_gt_rpmdb: ++ logger.warning(_('Transaction history is incomplete, after %u.'), trans.tid) ++ ++ if merged_trans is None: ++ merged_trans = dnf.db.history.MergedTransactionWrapper(trans) ++ else: ++ merged_trans.merge(trans) ++ ++ return self._revert_transaction(merged_trans) ++ ++ def _revert_transaction(self, trans): ++ action_map = { ++ "Install": "Removed", ++ "Removed": "Install", ++ "Upgrade": "Downgraded", ++ "Upgraded": "Downgrade", ++ "Downgrade": "Upgraded", ++ "Downgraded": "Upgrade", ++ "Reinstalled": "Reinstall", ++ "Reinstall": "Reinstalled", ++ "Obsoleted": "Install", ++ "Obsolete": "Obsoleted", ++ } ++ ++ data = serialize_transaction(trans) ++ ++ # revert actions in the serialized transaction data to perform rollback/undo ++ for content_type in ("rpms", "groups", "environments"): ++ for ti in data.get(content_type, []): ++ ti["action"] = action_map[ti["action"]] ++ ++ if ti["action"] == "Install" and ti.get("reason", None) == "clean": ++ ti["reason"] = "dependency" ++ ++ if ti.get("repo_id") == hawkey.SYSTEM_REPO_NAME: ++ # erase repo_id, because it's not possible to perform forward actions from the @System repo ++ ti["repo_id"] = None ++ ++ self.replay = TransactionReplay( ++ self.base, ++ data=data, ++ ignore_installed=True, ++ ignore_extras=True, ++ skip_unavailable=self.opts.skip_unavailable ++ ) + try: +- return self.base.history_rollback_transaction(extcmds[0]) +- except dnf.exceptions.Error as err: +- return 1, [str(err)] ++ self.replay.run() ++ except dnf.transaction_sr.TransactionFileError as ex: ++ for error in ex.errors: ++ print(str(error), file=sys.stderr) ++ raise dnf.exceptions.PackageNotFoundError(_('no package matched')) + + def _hcmd_userinstalled(self): + """Execute history userinstalled command.""" +@@ -318,13 +380,13 @@ class HistoryCommand(commands.Command): + raise dnf.exceptions.Error(strs[0]) + + def run_resolved(self): +- if self.opts.transactions_action not in ("replay", "redo"): ++ if self.opts.transactions_action not in ("replay", "redo", "rollback"): + return + + self.replay.post_transaction() + + def run_transaction(self): +- if self.opts.transactions_action not in ("replay", "redo"): ++ if self.opts.transactions_action not in ("replay", "redo", "rollback"): + return + + warnings = self.replay.get_warnings() +-- +2.26.2 + + +From a59a57ce456682e85e86ee362aab4eecc19dbc81 Mon Sep 17 00:00:00 2001 +From: Daniel Mach +Date: Thu, 3 Dec 2020 15:56:52 +0100 +Subject: [PATCH 09/17] history: Refactor undo code to use transaction + store/replay + += changelog = +msg: Support comps groups in history undo +type: enhancement +resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1657123 +resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1809565 +resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1809639 +--- + dnf/cli/cli.py | 28 ---------------------------- + dnf/cli/commands/history.py | 12 ++++++------ + 2 files changed, 6 insertions(+), 34 deletions(-) + +diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py +index 36671fd8..e4fd39c6 100644 +--- a/dnf/cli/cli.py ++++ b/dnf/cli/cli.py +@@ -627,34 +627,6 @@ class BaseCli(dnf.Base): + logger.critical(_('Found more than one transaction ID!')) + return old[0] + +- def history_undo_transaction(self, extcmd): +- """Undo given transaction.""" +- old = self.history_get_transaction((extcmd,)) +- if old is None: +- return 1, ['Failed history undo'] +- +- tm = dnf.util.normalize_time(old.beg_timestamp) +- msg = _("Undoing transaction {}, from {}").format(old.tid, ucd(tm)) +- logger.info(msg) +- self.output.historyInfoCmdPkgsAltered(old) # :todo +- +- +- mobj = dnf.db.history.MergedTransactionWrapper(old) +- +- try: +- self._history_undo_operations(mobj, old.tid, strict=self.conf.strict) +- except dnf.exceptions.PackagesNotInstalledError as err: +- logger.info(_('No package %s installed.'), +- self.output.term.bold(ucd(err.pkg_spec))) +- return 1, ['An operation cannot be undone'] +- except dnf.exceptions.PackagesNotAvailableError as err: +- logger.info(_('No package %s available.'), +- self.output.term.bold(ucd(err.pkg_spec))) +- return 1, ['An operation cannot be undone'] +- except dnf.exceptions.MarkingError: +- raise +- else: +- return 2, ["Undoing transaction %u" % (old.tid,)] + + class Cli(object): + def __init__(self, base): +diff --git a/dnf/cli/commands/history.py b/dnf/cli/commands/history.py +index a450aaab..d60d3f25 100644 +--- a/dnf/cli/commands/history.py ++++ b/dnf/cli/commands/history.py +@@ -175,10 +175,10 @@ class HistoryCommand(commands.Command): + self.replay.run() + + def _hcmd_undo(self, extcmds): +- try: +- return self.base.history_undo_transaction(extcmds[0]) +- except dnf.exceptions.Error as err: +- return 1, [str(err)] ++ old = self.base.history_get_transaction(extcmds) ++ if old is None: ++ return 1, ['Failed history undo'] ++ return self._revert_transaction(old) + + def _hcmd_rollback(self, extcmds): + old = self.base.history_get_transaction(extcmds) +@@ -380,13 +380,13 @@ class HistoryCommand(commands.Command): + raise dnf.exceptions.Error(strs[0]) + + def run_resolved(self): +- if self.opts.transactions_action not in ("replay", "redo", "rollback"): ++ if self.opts.transactions_action not in ("replay", "redo", "rollback", "undo"): + return + + self.replay.post_transaction() + + def run_transaction(self): +- if self.opts.transactions_action not in ("replay", "redo", "rollback"): ++ if self.opts.transactions_action not in ("replay", "redo", "rollback", "undo"): + return + + warnings = self.replay.get_warnings() +-- +2.26.2 + + +From 5a0b6cc00420fd6559a1fd611de1417ea90b1bfc Mon Sep 17 00:00:00 2001 +From: Daniel Mach +Date: Fri, 20 Nov 2020 19:54:54 +0100 +Subject: [PATCH 10/17] Remove Base._history_undo_operations() as it was + replaced with transaction_sr code + +--- + dnf/base.py | 59 ----------------------------------------------------- + 1 file changed, 59 deletions(-) + +diff --git a/dnf/base.py b/dnf/base.py +index ec41ab01..a2955051 100644 +--- a/dnf/base.py ++++ b/dnf/base.py +@@ -2218,65 +2218,6 @@ class Base(object): + for prefix in ['/bin/', '/sbin/', '/usr/bin/', '/usr/sbin/']] + return self.sack.query().filterm(file__glob=binary_provides), binary_provides + +- def _history_undo_operations(self, operations, first_trans, rollback=False, strict=True): +- """Undo the operations on packages by their NEVRAs. +- +- :param operations: a NEVRAOperations to be undone +- :param first_trans: first transaction id being undone +- :param rollback: True if transaction is performing a rollback +- :param strict: if True, raise an exception on any errors +- """ +- +- # map actions to their opposites +- action_map = { +- libdnf.transaction.TransactionItemAction_DOWNGRADE: None, +- libdnf.transaction.TransactionItemAction_DOWNGRADED: libdnf.transaction.TransactionItemAction_UPGRADE, +- libdnf.transaction.TransactionItemAction_INSTALL: libdnf.transaction.TransactionItemAction_REMOVE, +- libdnf.transaction.TransactionItemAction_OBSOLETE: None, +- libdnf.transaction.TransactionItemAction_OBSOLETED: libdnf.transaction.TransactionItemAction_INSTALL, +- libdnf.transaction.TransactionItemAction_REINSTALL: None, +- # reinstalls are skipped as they are considered as no-operation from history perspective +- libdnf.transaction.TransactionItemAction_REINSTALLED: None, +- libdnf.transaction.TransactionItemAction_REMOVE: libdnf.transaction.TransactionItemAction_INSTALL, +- libdnf.transaction.TransactionItemAction_UPGRADE: None, +- libdnf.transaction.TransactionItemAction_UPGRADED: libdnf.transaction.TransactionItemAction_DOWNGRADE, +- libdnf.transaction.TransactionItemAction_REASON_CHANGE: None, +- } +- +- failed = False +- for ti in operations.packages(): +- try: +- action = action_map[ti.action] +- except KeyError: +- raise RuntimeError(_("Action not handled: {}".format(action))) +- +- if action is None: +- continue +- +- if action == libdnf.transaction.TransactionItemAction_REMOVE: +- query = self.sack.query().installed().filterm(nevra_strict=str(ti)) +- if not query: +- logger.error(_('No package %s installed.'), ucd(str(ti))) +- failed = True +- continue +- else: +- query = self.sack.query().filterm(nevra_strict=str(ti)) +- if not query: +- logger.error(_('No package %s available.'), ucd(str(ti))) +- failed = True +- continue +- +- if action == libdnf.transaction.TransactionItemAction_REMOVE: +- for pkg in query: +- self._goal.erase(pkg) +- else: +- selector = dnf.selector.Selector(self.sack) +- selector.set(pkg=query) +- self._goal.install(select=selector, optional=(not strict)) +- +- if strict and failed: +- raise dnf.exceptions.PackageNotFoundError(_('no package matched')) +- + def _merge_update_filters(self, q, pkg_spec=None, warning=True): + """ + Merge Queries in _update_filters and return intersection with q Query +-- +2.26.2 + + +From c5a02f21d1a7b3be9ace78364ce234d853118574 Mon Sep 17 00:00:00 2001 +From: Daniel Mach +Date: Wed, 2 Dec 2020 08:57:15 +0100 +Subject: [PATCH 11/17] history: Move history methods from BaseCli to + HistoryCommand + +--- + dnf/cli/cli.py | 19 ------------- + dnf/cli/commands/history.py | 53 +++++++++++++++---------------------- + 2 files changed, 22 insertions(+), 50 deletions(-) + +diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py +index e4fd39c6..3080ae64 100644 +--- a/dnf/cli/cli.py ++++ b/dnf/cli/cli.py +@@ -608,25 +608,6 @@ class BaseCli(dnf.Base): + return False + return True + +- def _history_get_transactions(self, extcmds): +- if not extcmds: +- logger.critical(_('No transaction ID given')) +- return None +- +- old = self.history.old(extcmds) +- if not old: +- logger.critical(_('Not found given transaction ID')) +- return None +- return old +- +- def history_get_transaction(self, extcmds): +- old = self._history_get_transactions(extcmds) +- if old is None: +- return None +- if len(old) > 1: +- logger.critical(_('Found more than one transaction ID!')) +- return old[0] +- + + class Cli(object): + def __init__(self, base): +diff --git a/dnf/cli/commands/history.py b/dnf/cli/commands/history.py +index d60d3f25..dfd954ee 100644 +--- a/dnf/cli/commands/history.py ++++ b/dnf/cli/commands/history.py +@@ -34,7 +34,6 @@ import dnf.util + import json + import logging + import os +-import sys + + + logger = logging.getLogger('dnf') +@@ -160,10 +159,7 @@ class HistoryCommand(commands.Command): + return dnf.cli.commands.Command.get_error_output(self, error) + + def _hcmd_redo(self, extcmds): +- old = self.base.history_get_transaction(extcmds) +- if old is None: +- return 1, ['Failed history redo'] +- ++ old = self._history_get_transaction(extcmds) + data = serialize_transaction(old) + self.replay = TransactionReplay( + self.base, +@@ -174,16 +170,27 @@ class HistoryCommand(commands.Command): + ) + self.replay.run() + ++ def _history_get_transactions(self, extcmds): ++ if not extcmds: ++ raise dnf.cli.CliError(_('No transaction ID given')) ++ ++ old = self.base.history.old(extcmds) ++ if not old: ++ raise dnf.cli.CliError(_('Transaction ID "{0}" not found.').format(extcmds[0])) ++ return old ++ ++ def _history_get_transaction(self, extcmds): ++ old = self._history_get_transactions(extcmds) ++ if len(old) > 1: ++ raise dnf.cli.CliError(_('Found more than one transaction ID!')) ++ return old[0] ++ + def _hcmd_undo(self, extcmds): +- old = self.base.history_get_transaction(extcmds) +- if old is None: +- return 1, ['Failed history undo'] ++ old = self._history_get_transaction(extcmds) + return self._revert_transaction(old) + + def _hcmd_rollback(self, extcmds): +- old = self.base.history_get_transaction(extcmds) +- if old is None: +- return 1, ['Failed history rollback'] ++ old = self._history_get_transaction(extcmds) + last = self.base.history.last() + + merged_trans = None +@@ -239,12 +246,7 @@ class HistoryCommand(commands.Command): + ignore_extras=True, + skip_unavailable=self.opts.skip_unavailable + ) +- try: +- self.replay.run() +- except dnf.transaction_sr.TransactionFileError as ex: +- for error in ex.errors: +- print(str(error), file=sys.stderr) +- raise dnf.exceptions.PackageNotFoundError(_('no package matched')) ++ self.replay.run() + + def _hcmd_userinstalled(self): + """Execute history userinstalled command.""" +@@ -346,11 +348,8 @@ class HistoryCommand(commands.Command): + elif vcmd == 'userinstalled': + ret = self._hcmd_userinstalled() + elif vcmd == 'store': +- transactions = self.output.history.old(tids) +- if not transactions: +- raise dnf.cli.CliError(_('Transaction ID "{id}" not found.').format(id=tids[0])) +- +- data = serialize_transaction(transactions[0]) ++ tid = self._history_get_transaction(tids) ++ data = serialize_transaction(tid) + try: + filename = self.opts.output if self.opts.output is not None else "transaction.json" + +@@ -371,14 +370,6 @@ class HistoryCommand(commands.Command): + except OSError as e: + raise dnf.cli.CliError(_('Error storing transaction: {}').format(str(e))) + +- if ret is None: +- return +- (code, strs) = ret +- if code == 2: +- self.cli.demands.resolving = True +- elif code != 0: +- raise dnf.exceptions.Error(strs[0]) +- + def run_resolved(self): + if self.opts.transactions_action not in ("replay", "redo", "rollback", "undo"): + return +@@ -393,7 +384,7 @@ class HistoryCommand(commands.Command): + if warnings: + logger.log( + dnf.logging.WARNING, +- _("Warning, the following problems occurred while replaying the transaction:") ++ _("Warning, the following problems occurred while running a transaction:") + ) + for w in warnings: + logger.log(dnf.logging.WARNING, " " + w) +-- +2.26.2 + + +From 917f9f3b0fc418492293e08fa7db053b0c490d8f Mon Sep 17 00:00:00 2001 +From: Daniel Mach +Date: Thu, 10 Dec 2020 13:36:52 +0100 +Subject: [PATCH 12/17] transaction_sr: Simplify error reporting, unify with + history + +--- + dnf/transaction_sr.py | 20 +++++++++----------- + 1 file changed, 9 insertions(+), 11 deletions(-) + +diff --git a/dnf/transaction_sr.py b/dnf/transaction_sr.py +index 9926bebd..2122aba4 100644 +--- a/dnf/transaction_sr.py ++++ b/dnf/transaction_sr.py +@@ -57,21 +57,19 @@ class TransactionFileError(dnf.exceptions.Error): + + # store args in case someone wants to read them from a caught exception + self.filename = filename +- self.errors = errors +- + if isinstance(errors, (list, tuple)): +- if len(errors) > 1: +- msg = _('Errors in "{filename}":').format(filename=filename) +- for error in errors: +- msg += "\n " + str(error) ++ self.errors = errors ++ else: ++ self.errors = [errors] + +- super(TransactionFileError, self).__init__(msg) +- return ++ if filename: ++ msg = _('The following problems occurred while replaying the transaction from file "{filename}":').format(filename=filename) ++ else: ++ msg = _('The following problems occurred while running a transaction:') + +- else: +- errors = str(errors[0]) ++ for error in self.errors: ++ msg += "\n " + str(error) + +- msg = _('Error in "{filename}": {error}').format(filename=filename, error=errors) + super(TransactionFileError, self).__init__(msg) + + +-- +2.26.2 + + +From d2fb741829445efee3187553cf7960f7bc2f643e Mon Sep 17 00:00:00 2001 +From: Daniel Mach +Date: Thu, 17 Dec 2020 16:37:01 +0100 +Subject: [PATCH 13/17] transaction_sr: TransactionFileError exception to + TransactionReplayError + +--- + dnf/transaction_sr.py | 20 ++++++++++---------- + 1 file changed, 10 insertions(+), 10 deletions(-) + +diff --git a/dnf/transaction_sr.py b/dnf/transaction_sr.py +index 2122aba4..e4974eb9 100644 +--- a/dnf/transaction_sr.py ++++ b/dnf/transaction_sr.py +@@ -48,7 +48,7 @@ class TransactionError(dnf.exceptions.Error): + super(TransactionError, self).__init__(msg) + + +-class TransactionFileError(dnf.exceptions.Error): ++class TransactionReplayError(dnf.exceptions.Error): + def __init__(self, filename, errors): + """ + :param filename: The name of the transaction file being replayed +@@ -70,10 +70,10 @@ class TransactionFileError(dnf.exceptions.Error): + for error in self.errors: + msg += "\n " + str(error) + +- super(TransactionFileError, self).__init__(msg) ++ super(TransactionReplayError, self).__init__(msg) + + +-class IncompatibleTransactionVersionError(TransactionFileError): ++class IncompatibleTransactionVersionError(TransactionReplayError): + def __init__(self, filename, msg): + super(IncompatibleTransactionVersionError, self).__init__(filename, msg) + +@@ -84,7 +84,7 @@ def _check_version(version, filename): + try: + major = int(major) + except ValueError as e: +- raise TransactionFileError( ++ raise TransactionReplayError( + filename, + _('Invalid major version "{major}", number expected.').format(major=major) + ) +@@ -92,7 +92,7 @@ def _check_version(version, filename): + try: + int(minor) # minor is unused, just check it's a number + except ValueError as e: +- raise TransactionFileError( ++ raise TransactionReplayError( + filename, + _('Invalid minor version "{minor}", number expected.').format(minor=minor) + ) +@@ -234,12 +234,12 @@ class TransactionReplay(object): + try: + replay_data = json.load(f) + except json.decoder.JSONDecodeError as e: +- raise TransactionFileError(fn, str(e) + ".") ++ raise TransactionReplayError(fn, str(e) + ".") + + try: + self._load_from_data(replay_data) + except TransactionError as e: +- raise TransactionFileError(fn, e) ++ raise TransactionReplayError(fn, e) + + def _load_from_data(self, data): + self._replay_data = data +@@ -268,7 +268,7 @@ class TransactionReplay(object): + fn = self._filename + + if "version" not in replay_data: +- raise TransactionFileError(fn, _('Missing key "{key}".'.format(key="version"))) ++ raise TransactionReplayError(fn, _('Missing key "{key}".'.format(key="version"))) + + self._assert_type(replay_data["version"], str, "version", "string") + +@@ -580,7 +580,7 @@ class TransactionReplay(object): + errors.append(e) + + if errors: +- raise TransactionFileError(fn, errors) ++ raise TransactionReplayError(fn, errors) + + def post_transaction(self): + """ +@@ -635,4 +635,4 @@ class TransactionReplay(object): + pass + + if errors: +- raise TransactionFileError(self._filename, errors) ++ raise TransactionReplayError(self._filename, errors) +-- +2.26.2 + + +From 1182143e58d4fda530d5dfd19f0d9c9406e8eff3 Mon Sep 17 00:00:00 2001 +From: Daniel Mach +Date: Thu, 17 Dec 2020 16:55:39 +0100 +Subject: [PATCH 14/17] transaction_sr: Don't return if there's a mismatch in + actions + +When _ignore_installed == True, then an exception is raised anyway. +When _ignore_installed == False, get the requested package to the system +regardless the action. +--- + dnf/transaction_sr.py | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/dnf/transaction_sr.py b/dnf/transaction_sr.py +index e4974eb9..dae8d300 100644 +--- a/dnf/transaction_sr.py ++++ b/dnf/transaction_sr.py +@@ -334,7 +334,6 @@ class TransactionReplay(object): + if action == "Install" and query_na.installed() and not self._base._get_installonly_query(query_na): + self._raise_or_warn(self._ignore_installed, + _('Package "{na}" is already installed for action "{action}".').format(na=na, action=action)) +- return + + sltr = dnf.selector.Selector(self._base.sack).set(pkg=query) + self._base.goal.install(select=sltr, optional=not self._base.conf.strict) +-- +2.26.2 + + +From ff32a3c68fa853b53084a1a4947f345062056f23 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hr=C3=A1zk=C3=BD?= +Date: Fri, 8 Jan 2021 13:37:45 +0100 +Subject: [PATCH 15/17] cli/output: Return number of listed packages from + listPkgs() + +Instead of an error status and message. +--- + dnf/cli/cli.py | 5 ++--- + dnf/cli/commands/history.py | 4 +++- + dnf/cli/output.py | 14 ++------------ + 3 files changed, 7 insertions(+), 16 deletions(-) + +diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py +index 3080ae64..be737ed3 100644 +--- a/dnf/cli/cli.py ++++ b/dnf/cli/cli.py +@@ -505,7 +505,7 @@ class BaseCli(dnf.Base): + # XXX put this into the ListCommand at some point + if len(ypl.obsoletes) > 0 and basecmd == 'list': + # if we've looked up obsolete lists and it's a list request +- rop = [0, ''] ++ rop = len(ypl.obsoletes) + print(_('Obsoleting Packages')) + for obtup in sorted(ypl.obsoletesTuples, + key=operator.itemgetter(0)): +@@ -517,8 +517,7 @@ class BaseCli(dnf.Base): + rrap = self.output.listPkgs(ypl.recent, _('Recently Added Packages'), + basecmd, columns=columns) + if len(patterns) and \ +- rrap[0] and rop[0] and rup[0] and rep[0] and rap[0] and \ +- raep[0] and rip[0]: ++ rrap == 0 and rop == 0 and rup == 0 and rep == 0 and rap == 0 and raep == 0 and rip == 0: + raise dnf.exceptions.Error(_('No matching Packages to list')) + + def returnPkgLists(self, pkgnarrow='all', patterns=None, +diff --git a/dnf/cli/commands/history.py b/dnf/cli/commands/history.py +index dfd954ee..e9b91d0f 100644 +--- a/dnf/cli/commands/history.py ++++ b/dnf/cli/commands/history.py +@@ -251,7 +251,9 @@ class HistoryCommand(commands.Command): + def _hcmd_userinstalled(self): + """Execute history userinstalled command.""" + pkgs = tuple(self.base.iter_userinstalled()) +- return self.output.listPkgs(pkgs, 'Packages installed by user', 'nevra') ++ n_listed = self.output.listPkgs(pkgs, 'Packages installed by user', 'nevra') ++ if n_listed == 0: ++ raise dnf.cli.CliError(_('No packages to list')) + + def _args2transaction_ids(self): + """Convert commandline arguments to transaction ids""" +diff --git a/dnf/cli/output.py b/dnf/cli/output.py +index 6d729b63..6cfc9e22 100644 +--- a/dnf/cli/output.py ++++ b/dnf/cli/output.py +@@ -597,18 +597,10 @@ class Output(object): + number + '>' - highlighting used when the package has a higher version + number +- :return: (exit_code, [errors]) +- +- exit_code is:: +- +- 0 = we're done, exit +- 1 = we've errored, exit with error string +- ++ :return: number of packages listed + """ + if outputType in ['list', 'info', 'name', 'nevra']: +- thingslisted = 0 + if len(lst) > 0: +- thingslisted = 1 + print('%s' % description) + info_set = set() + if outputType == 'list': +@@ -645,9 +637,7 @@ class Output(object): + if info_set: + print("\n".join(sorted(info_set))) + +- if thingslisted == 0: +- return 1, [_('No packages to list')] +- return 0, [] ++ return len(lst) + + def userconfirm(self, msg=None, defaultyes_msg=None): + """Get a yes or no from the user, and default to No +-- +2.26.2 + + +From 0226da7351eb97cd9c4c6739725b1f77d445764e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hr=C3=A1zk=C3=BD?= +Date: Fri, 8 Jan 2021 13:44:27 +0100 +Subject: [PATCH 16/17] Clean up history command error handling + +The removal of `ret` value error handling which was removed previously was not +complete. Most of it is was no-op as no errors were really propagated through +it, but the `history userinstalled` command was still relying on it. + +The commit removes the last bit and replaces it with raising an exception. +--- + dnf/cli/commands/history.py | 17 ++++++++--------- + 1 file changed, 8 insertions(+), 9 deletions(-) + +diff --git a/dnf/cli/commands/history.py b/dnf/cli/commands/history.py +index e9b91d0f..7b38cb60 100644 +--- a/dnf/cli/commands/history.py ++++ b/dnf/cli/commands/history.py +@@ -187,7 +187,7 @@ class HistoryCommand(commands.Command): + + def _hcmd_undo(self, extcmds): + old = self._history_get_transaction(extcmds) +- return self._revert_transaction(old) ++ self._revert_transaction(old) + + def _hcmd_rollback(self, extcmds): + old = self._history_get_transaction(extcmds) +@@ -209,7 +209,7 @@ class HistoryCommand(commands.Command): + else: + merged_trans.merge(trans) + +- return self._revert_transaction(merged_trans) ++ self._revert_transaction(merged_trans) + + def _revert_transaction(self, trans): + action_map = { +@@ -321,7 +321,6 @@ class HistoryCommand(commands.Command): + + def run(self): + vcmd = self.opts.transactions_action +- ret = None + + if vcmd == 'replay': + self.base.read_comps(arch_filter=True) +@@ -338,17 +337,17 @@ class HistoryCommand(commands.Command): + tids, merged_tids = self._args2transaction_ids() + + if vcmd == 'list' and (tids or not self.opts.transactions): +- ret = self.output.historyListCmd(tids, reverse=self.opts.reverse) ++ self.output.historyListCmd(tids, reverse=self.opts.reverse) + elif vcmd == 'info' and (tids or not self.opts.transactions): +- ret = self.output.historyInfoCmd(tids, self.opts.transactions, merged_tids) ++ self.output.historyInfoCmd(tids, self.opts.transactions, merged_tids) + elif vcmd == 'undo': +- ret = self._hcmd_undo(tids) ++ self._hcmd_undo(tids) + elif vcmd == 'redo': +- ret = self._hcmd_redo(tids) ++ self._hcmd_redo(tids) + elif vcmd == 'rollback': +- ret = self._hcmd_rollback(tids) ++ self._hcmd_rollback(tids) + elif vcmd == 'userinstalled': +- ret = self._hcmd_userinstalled() ++ self._hcmd_userinstalled() + elif vcmd == 'store': + tid = self._history_get_transaction(tids) + data = serialize_transaction(tid) +-- +2.26.2 + + +From 7e862711b3d7b9b444d966594630b49bf3761faf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hr=C3=A1zk=C3=BD?= +Date: Mon, 23 Nov 2020 16:32:16 +0100 +Subject: [PATCH 17/17] Lazy-load base.comps instead of explicitly + +Loading base.comps was done by calling a method at arbitrary places in +the code, this is hard to maintain and get right. The method could be +inadvertedly called multiple times per dnf run too. + +Instead load the comps data lazily on first access. In case of the +shell, using "repo enable/disable" can cause the comps data to change +mid-run. Instead of explicitly reloading, clear the comps attribute and +let it be lazy-loaded again when needed. + +Closes: #1690 +Approved by: j-mracek +--- + dnf/base.py | 4 ++-- + dnf/cli/commands/group.py | 5 ----- + dnf/cli/commands/history.py | 2 -- + dnf/cli/commands/install.py | 1 - + dnf/cli/commands/remove.py | 1 - + dnf/cli/commands/repoquery.py | 1 - + dnf/cli/commands/shell.py | 3 +++ + dnf/cli/commands/upgrade.py | 1 - + tests/api/test_dnf_base.py | 4 +--- + 9 files changed, 6 insertions(+), 16 deletions(-) + +diff --git a/dnf/base.py b/dnf/base.py +index a2955051..39c21c33 100644 +--- a/dnf/base.py ++++ b/dnf/base.py +@@ -242,6 +242,8 @@ class Base(object): + @property + def comps(self): + # :api ++ if self._comps is None: ++ self.read_comps(arch_filter=True) + return self._comps + + @property +@@ -1881,7 +1883,6 @@ class Base(object): + no_match_module_specs = install_specs.grp_specs + + if no_match_module_specs: +- self.read_comps(arch_filter=True) + exclude_specs.grp_specs = self._expand_groups(exclude_specs.grp_specs) + self._install_groups(no_match_module_specs, exclude_specs, no_match_group_specs, strict) + +@@ -2084,7 +2085,6 @@ class Base(object): + msg = _('Not a valid form: %s') + logger.warning(msg, grp_spec) + elif grp_specs: +- self.read_comps(arch_filter=True) + if self.env_group_remove(grp_specs): + done = True + +diff --git a/dnf/cli/commands/group.py b/dnf/cli/commands/group.py +index bd17f80f..cf542799 100644 +--- a/dnf/cli/commands/group.py ++++ b/dnf/cli/commands/group.py +@@ -110,9 +110,6 @@ class GroupCommand(commands.Command): + + return installed, available + +- def _grp_setup(self): +- self.base.read_comps(arch_filter=True) +- + def _info(self, userlist): + for strng in userlist: + group_matched = False +@@ -370,8 +367,6 @@ class GroupCommand(commands.Command): + cmd = self.opts.subcmd + extcmds = self.opts.args + +- self._grp_setup() +- + if cmd == 'summary': + return self._summary(extcmds) + if cmd == 'list': +diff --git a/dnf/cli/commands/history.py b/dnf/cli/commands/history.py +index 7b38cb60..293d93fc 100644 +--- a/dnf/cli/commands/history.py ++++ b/dnf/cli/commands/history.py +@@ -323,8 +323,6 @@ class HistoryCommand(commands.Command): + vcmd = self.opts.transactions_action + + if vcmd == 'replay': +- self.base.read_comps(arch_filter=True) +- + self.replay = TransactionReplay( + self.base, + filename=self.opts.transaction_filename, +diff --git a/dnf/cli/commands/install.py b/dnf/cli/commands/install.py +index 38a90b61..b637af0b 100644 +--- a/dnf/cli/commands/install.py ++++ b/dnf/cli/commands/install.py +@@ -151,7 +151,6 @@ class InstallCommand(commands.Command): + return err_pkgs + + def _install_groups(self, grp_specs): +- self.base.read_comps(arch_filter=True) + try: + self.base.env_group_install(grp_specs, + tuple(self.base.conf.group_package_types), +diff --git a/dnf/cli/commands/remove.py b/dnf/cli/commands/remove.py +index f50dbd91..e455ba6e 100644 +--- a/dnf/cli/commands/remove.py ++++ b/dnf/cli/commands/remove.py +@@ -142,7 +142,6 @@ class RemoveCommand(commands.Command): + skipped_grps = self.opts.grp_specs + + if skipped_grps: +- self.base.read_comps(arch_filter=True) + for group in skipped_grps: + try: + if self.base.env_group_remove([group]): +diff --git a/dnf/cli/commands/repoquery.py b/dnf/cli/commands/repoquery.py +index 099a9312..b0d06a90 100644 +--- a/dnf/cli/commands/repoquery.py ++++ b/dnf/cli/commands/repoquery.py +@@ -632,7 +632,6 @@ class RepoQueryCommand(commands.Command): + print("\n".join(sorted(pkgs))) + + def _group_member_report(self, query): +- self.base.read_comps(arch_filter=True) + package_conf_dict = {} + for group in self.base.comps.groups: + package_conf_dict[group.id] = set([pkg.name for pkg in group.packages_iter()]) +diff --git a/dnf/cli/commands/shell.py b/dnf/cli/commands/shell.py +index 431fe502..18c886ff 100644 +--- a/dnf/cli/commands/shell.py ++++ b/dnf/cli/commands/shell.py +@@ -239,6 +239,9 @@ exit (or quit) exit the shell""") + if fill_sack: + self.base.fill_sack() + ++ # reset base._comps, as it has changed due to changing the repos ++ self.base._comps = None ++ + else: + self._help('repo') + +diff --git a/dnf/cli/commands/upgrade.py b/dnf/cli/commands/upgrade.py +index 44789c9a..f62cfcc1 100644 +--- a/dnf/cli/commands/upgrade.py ++++ b/dnf/cli/commands/upgrade.py +@@ -124,7 +124,6 @@ class UpgradeCommand(commands.Command): + + def _update_groups(self): + if self.skipped_grp_specs: +- self.base.read_comps(arch_filter=True) + self.base.env_group_upgrade(self.skipped_grp_specs) + return True + return False +diff --git a/tests/api/test_dnf_base.py b/tests/api/test_dnf_base.py +index ca71b75c..656bd225 100644 +--- a/tests/api/test_dnf_base.py ++++ b/tests/api/test_dnf_base.py +@@ -34,9 +34,7 @@ class DnfBaseApiTest(TestCase): + def test_comps(self): + # Base.comps + self.assertHasAttr(self.base, "comps") +- +- # blank initially +- self.assertEqual(self.base.comps, None) ++ self.assertHasType(self.base.comps, dnf.comps.Comps) + + self.base.read_comps() + self.assertHasType(self.base.comps, dnf.comps.Comps) +-- +2.26.2 + diff --git a/SOURCES/0006-Remove-sourcepackages-from-install-upgrade-set.patch b/SOURCES/0006-Remove-sourcepackages-from-install-upgrade-set.patch new file mode 100644 index 0000000..1ef5a4e --- /dev/null +++ b/SOURCES/0006-Remove-sourcepackages-from-install-upgrade-set.patch @@ -0,0 +1,150 @@ +From 8f3ce4868ac009976da7323ea39ebcd9a062e32d Mon Sep 17 00:00:00 2001 +From: Jaroslav Mracek +Date: Mon, 23 Nov 2020 17:00:01 +0100 +Subject: [PATCH 1/3] Remove source packages from install/upgrade set + (RhBug:1898548) + +It prevents Error: Will not install a source rpm package () + +https://bugzilla.redhat.com/show_bug.cgi?id=1898548 +--- + dnf/module/module_base.py | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +diff --git a/dnf/module/module_base.py b/dnf/module/module_base.py +index 04701b9d..49c871c4 100644 +--- a/dnf/module/module_base.py ++++ b/dnf/module/module_base.py +@@ -140,20 +140,21 @@ class ModuleBase(object): + if fail_safe_repo_used: + raise dnf.exceptions.Error(_( + "Installing module from Fail-Safe repository is not allowed")) +- install_base_query = self.base.sack.query().filterm( +- nevra_strict=install_set_artefacts).apply() ++ # Remove source packages they cannot be installed or upgraded ++ base_no_source_query = self.base.sack.query().filterm(arch__neq=['src', 'nosrc']).apply() ++ install_base_query = base_no_source_query.filter(nevra_strict=install_set_artefacts) + + # add hot-fix packages + hot_fix_repos = [i.id for i in self.base.repos.iter_enabled() if i.module_hotfixes] +- hotfix_packages = self.base.sack.query().filterm(reponame=hot_fix_repos).filterm( +- name=install_dict.keys()) ++ hotfix_packages = base_no_source_query.filter( ++ reponame=hot_fix_repos, name=install_dict.keys()) + install_base_query = install_base_query.union(hotfix_packages) + + for pkg_name, set_specs in install_dict.items(): + query = install_base_query.filter(name=pkg_name) + if not query: + # package can also be non-modular or part of another stream +- query = self.base.sack.query().filterm(name=pkg_name) ++ query = base_no_source_query.filter(name=pkg_name) + if not query: + for spec in set_specs: + logger.error(_("Unable to resolve argument {}").format(spec)) +@@ -182,6 +183,9 @@ class ModuleBase(object): + fail_safe_repo = hawkey.MODULE_FAIL_SAFE_REPO_NAME + fail_safe_repo_used = False + ++ # Remove source packages they cannot be installed or upgraded ++ base_no_source_query = self.base.sack.query().filterm(arch__neq=['src', 'nosrc']).apply() ++ + for spec in module_specs: + module_list, nsvcap = self._get_modules(spec) + if not module_list: +@@ -221,7 +225,7 @@ class ModuleBase(object): + + if not upgrade_package_set: + logger.error(_("Unable to match profile in argument {}").format(spec)) +- query = self.base.sack.query().filterm(name=upgrade_package_set) ++ query = base_no_source_query.filter(name=upgrade_package_set) + if query: + sltr = dnf.selector.Selector(self.base.sack) + sltr.set(pkg=query) +-- +2.26.2 + + +From c42680b292b2cca38b24fb18f46f06f800c1934f Mon Sep 17 00:00:00 2001 +From: Jaroslav Mracek +Date: Mon, 23 Nov 2020 17:04:05 +0100 +Subject: [PATCH 2/3] Remove all source packages from query + +--- + dnf/base.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dnf/base.py b/dnf/base.py +index a2955051..a3d9b63f 100644 +--- a/dnf/base.py ++++ b/dnf/base.py +@@ -1550,7 +1550,7 @@ class Base(object): + if (comps_pkg.basearchonly): + query_args.update({'arch': basearch}) + q = self.sack.query().filterm(**query_args).apply() +- q.filterm(arch__neq="src") ++ q.filterm(arch__neq=["src", "nosrc"]) + if not q: + package_string = comps_pkg.name + if comps_pkg.basearchonly: +-- +2.26.2 + + +From 1f68fa6dc59fb350e71a24e787135475f3fb5b4c Mon Sep 17 00:00:00 2001 +From: Jaroslav Mracek +Date: Mon, 23 Nov 2020 17:29:45 +0100 +Subject: [PATCH 3/3] Run tests with sack in tmp directory + +--- + tests/api/test_dnf_module_base.py | 17 +++++++++++------ + 1 file changed, 11 insertions(+), 6 deletions(-) + +diff --git a/tests/api/test_dnf_module_base.py b/tests/api/test_dnf_module_base.py +index aa47555b..18dd080d 100644 +--- a/tests/api/test_dnf_module_base.py ++++ b/tests/api/test_dnf_module_base.py +@@ -7,16 +7,26 @@ from __future__ import unicode_literals + import dnf + import dnf.module.module_base + ++import os ++import shutil ++import tempfile ++ + from .common import TestCase + + + class DnfModuleBaseApiTest(TestCase): + def setUp(self): + self.base = dnf.Base(dnf.conf.Conf()) ++ self._installroot = tempfile.mkdtemp(prefix="dnf_test_installroot_") ++ self.base.conf.installroot = self._installroot ++ self.base.conf.cachedir = os.path.join(self._installroot, "var/cache/dnf") ++ self.base._sack = dnf.sack._build_sack(self.base) + self.moduleBase = dnf.module.module_base.ModuleBase(self.base) + + def tearDown(self): + self.base.close() ++ if self._installroot.startswith("/tmp/"): ++ shutil.rmtree(self._installroot) + + def test_init(self): + moduleBase = dnf.module.module_base.ModuleBase(self.base) +@@ -51,12 +61,7 @@ class DnfModuleBaseApiTest(TestCase): + def test_install(self): + # ModuleBase.install() + self.assertHasAttr(self.moduleBase, "install") +- self.assertRaises( +- AttributeError, +- self.moduleBase.install, +- module_specs=[], +- strict=False, +- ) ++ self.moduleBase.install(module_specs=[], strict=False) + + def test_remove(self): + # ModuleBase.remove() +-- +2.26.2 + diff --git a/SOURCES/0007-Fix-documentation-of-globs-not-supporting-curly-brackets.patch b/SOURCES/0007-Fix-documentation-of-globs-not-supporting-curly-brackets.patch new file mode 100644 index 0000000..2daa3b2 --- /dev/null +++ b/SOURCES/0007-Fix-documentation-of-globs-not-supporting-curly-brackets.patch @@ -0,0 +1,30 @@ +From f3c254581bcb0591a543aee0c7e031c3c9d0a9a1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hr=C3=A1zk=C3=BD?= +Date: Mon, 11 Jan 2021 16:43:25 +0100 +Subject: [PATCH] Fix documentation of globs not supporting curly brackets + += changelog = +msg: Fix documentation of globs not supporting curly brackets +type: bugfix +resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1913418 +--- + doc/command_ref.rst | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/doc/command_ref.rst b/doc/command_ref.rst +index bbce3ddcf4..d11e8dd502 100644 +--- a/doc/command_ref.rst ++++ b/doc/command_ref.rst +@@ -1763,8 +1763,10 @@ The following patterns are supported: + those two characters, inclusive, is matched. If the first character + following the ``[`` is a ``!`` or a ``^`` then any character not enclosed + is matched. +-``{}`` +- Matches any of the comma separated list of enclosed strings. ++ ++Note: Curly brackets (``{}``) are not supported. You can still use them in ++shells that support them and let the shell do the expansion, but if quoted or ++escaped, dnf will not expand them. + + -------------- + NEVRA Matching diff --git a/SOURCES/0008-Module-switch-command.patch b/SOURCES/0008-Module-switch-command.patch new file mode 100644 index 0000000..f464e15 --- /dev/null +++ b/SOURCES/0008-Module-switch-command.patch @@ -0,0 +1,507 @@ +From 6ed0458744090ab307da9d9118690372b2e66ca8 Mon Sep 17 00:00:00 2001 +From: Jaroslav Mracek +Date: Wed, 11 Nov 2020 12:47:21 +0100 +Subject: [PATCH 1/5] Make module_base better industrialized for method reuse + +It will allow to use internal for module switch command. +--- + dnf/module/module_base.py | 29 ++++++++++++++++++++--------- + 1 file changed, 20 insertions(+), 9 deletions(-) + +diff --git a/dnf/module/module_base.py b/dnf/module/module_base.py +index 49c871c4..0da4fab1 100644 +--- a/dnf/module/module_base.py ++++ b/dnf/module/module_base.py +@@ -323,7 +323,7 @@ class ModuleBase(object): + assert len(streamDict) == 1 + return moduleDict + +- def _resolve_specs_enable_update_sack(self, module_specs): ++ def _resolve_specs_enable(self, module_specs): + no_match_specs = [] + error_spec = [] + module_dicts = {} +@@ -339,6 +339,9 @@ class ModuleBase(object): + error_spec.append(spec) + logger.error(ucd(e)) + logger.error(_("Unable to resolve argument {}").format(spec)) ++ return no_match_specs, error_spec, module_dicts ++ ++ def _update_sack(self): + hot_fix_repos = [i.id for i in self.base.repos.iter_enabled() if i.module_hotfixes] + try: + solver_errors = self.base.sack.filter_modules( +@@ -347,6 +350,10 @@ class ModuleBase(object): + debugsolver=self.base.conf.debug_solver) + except hawkey.Exception as e: + raise dnf.exceptions.Error(ucd(e)) ++ return solver_errors ++ ++ def _enable_dependencies(self, module_dicts): ++ error_spec = [] + for spec, (nsvcap, moduleDict) in module_dicts.items(): + for streamDict in moduleDict.values(): + for modules in streamDict.values(): +@@ -357,6 +364,17 @@ class ModuleBase(object): + error_spec.append(spec) + logger.error(ucd(e)) + logger.error(_("Unable to resolve argument {}").format(spec)) ++ return error_spec ++ ++ def _resolve_specs_enable_update_sack(self, module_specs): ++ no_match_specs, error_spec, module_dicts = self._resolve_specs_enable(module_specs) ++ ++ solver_errors = self._update_sack() ++ ++ dependency_error_spec = self._enable_dependencies(module_dicts) ++ if dependency_error_spec: ++ error_spec.extend(dependency_error_spec) ++ + return no_match_specs, error_spec, solver_errors, module_dicts + + def _modules_reset_or_disable(self, module_specs, to_state): +@@ -379,14 +397,7 @@ class ModuleBase(object): + if to_state == STATE_DISABLED: + self.base._moduleContainer.disable(name) + +- hot_fix_repos = [i.id for i in self.base.repos.iter_enabled() if i.module_hotfixes] +- try: +- solver_errors = self.base.sack.filter_modules( +- self.base._moduleContainer, hot_fix_repos, self.base.conf.installroot, +- self.base.conf.module_platform_id, update_only=True, +- debugsolver=self.base.conf.debug_solver) +- except hawkey.Exception as e: +- raise dnf.exceptions.Error(ucd(e)) ++ solver_errors = self._update_sack() + return no_match_specs, solver_errors + + def _get_package_name_set_and_remove_profiles(self, module_list, nsvcap, remove=False): +-- +2.26.2 + + +From e6473f4e6f17bb635e023b8905f29b318b8795bf Mon Sep 17 00:00:00 2001 +From: Jaroslav Mracek +Date: Wed, 11 Nov 2020 17:09:16 +0100 +Subject: [PATCH 2/5] Add module switch-to support (RhBug:1792020) + +It is a combination of module rpm distrosync, module profile switch and +module stream switch. + += changelog = +msg: Add new `module switch-to` command for switching content +of module streams +type: enhancement +resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1792020 +--- + VERSION.cmake | 2 +- + dnf.spec | 2 +- + dnf/cli/commands/module.py | 24 ++++- + dnf/module/module_base.py | 182 ++++++++++++++++++++++++++++++++----- + 4 files changed, 185 insertions(+), 25 deletions(-) + +diff --git a/dnf/cli/commands/module.py b/dnf/cli/commands/module.py +index 5a6c0069..4cdc915e 100644 +--- a/dnf/cli/commands/module.py ++++ b/dnf/cli/commands/module.py +@@ -271,6 +271,28 @@ class ModuleCommand(commands.Command): + + logger.error(dnf.exceptions.MarkingErrors(no_match_group_specs=skipped_groups)) + ++ class SwitchToSubCommand(SubCommand): ++ ++ aliases = ('switch-to',) ++ summary = _('switch a module to a stream and distrosync rpm packages') ++ ++ def configure(self): ++ demands = self.cli.demands ++ demands.available_repos = True ++ demands.sack_activation = True ++ demands.resolving = True ++ demands.root_user = True ++ self.base.conf.module_stream_switch = True ++ ++ def run_on_module(self): ++ try: ++ self.module_base.switch_to(self.opts.module_spec, strict=self.base.conf.strict) ++ except dnf.exceptions.MarkingErrors as e: ++ if self.base.conf.strict: ++ if e.no_match_group_specs or e.error_group_specs: ++ raise e ++ logger.error(str(e)) ++ + class ProvidesSubCommand(SubCommand): + + aliases = ("provides", ) +@@ -319,7 +341,7 @@ class ModuleCommand(commands.Command): + + SUBCMDS = {ListSubCommand, InfoSubCommand, EnableSubCommand, + DisableSubCommand, ResetSubCommand, InstallSubCommand, UpdateSubCommand, +- RemoveSubCommand, ProvidesSubCommand, RepoquerySubCommand} ++ RemoveSubCommand, SwitchToSubCommand, ProvidesSubCommand, RepoquerySubCommand} + + SUBCMDS_NOT_REQUIRED_ARG = {ListSubCommand} + +diff --git a/dnf/module/module_base.py b/dnf/module/module_base.py +index 0da4fab1..03d54f72 100644 +--- a/dnf/module/module_base.py ++++ b/dnf/module/module_base.py +@@ -140,31 +140,140 @@ class ModuleBase(object): + if fail_safe_repo_used: + raise dnf.exceptions.Error(_( + "Installing module from Fail-Safe repository is not allowed")) +- # Remove source packages they cannot be installed or upgraded +- base_no_source_query = self.base.sack.query().filterm(arch__neq=['src', 'nosrc']).apply() +- install_base_query = base_no_source_query.filter(nevra_strict=install_set_artefacts) ++ __, profiles_errors = self._install_profiles_internal( ++ install_set_artefacts, install_dict, strict) ++ if profiles_errors: ++ error_specs.extend(profiles_errors) + +- # add hot-fix packages +- hot_fix_repos = [i.id for i in self.base.repos.iter_enabled() if i.module_hotfixes] +- hotfix_packages = base_no_source_query.filter( +- reponame=hot_fix_repos, name=install_dict.keys()) +- install_base_query = install_base_query.union(hotfix_packages) ++ if no_match_specs or error_specs or solver_errors: ++ raise dnf.exceptions.MarkingErrors(no_match_group_specs=no_match_specs, ++ error_group_specs=error_specs, ++ module_depsolv_errors=solver_errors) + +- for pkg_name, set_specs in install_dict.items(): +- query = install_base_query.filter(name=pkg_name) +- if not query: +- # package can also be non-modular or part of another stream +- query = base_no_source_query.filter(name=pkg_name) +- if not query: +- for spec in set_specs: +- logger.error(_("Unable to resolve argument {}").format(spec)) +- logger.error(_("No match for package {}").format(pkg_name)) +- error_specs.extend(set_specs) +- continue +- self.base._goal.group_members.add(pkg_name) ++ def switch_to(self, module_specs, strict=True): ++ # :api ++ no_match_specs, error_specs, module_dicts = self._resolve_specs_enable(module_specs) ++ # collect name of artifacts from new modules for distrosync ++ new_artifacts_names = set() ++ # collect name of artifacts from active modules for distrosync before sack update ++ active_artifacts_names = set() ++ src_arches = {"nosrc", "src"} ++ for spec, (nsvcap, moduledict) in module_dicts.items(): ++ for name in moduledict.keys(): ++ for module in self.base._moduleContainer.query(name, "", "", "", ""): ++ if self.base._moduleContainer.isModuleActive(module): ++ for artifact in module.getArtifacts(): ++ arch = artifact.rsplit(".", 1)[1] ++ if arch in src_arches: ++ continue ++ pkg_name = artifact.rsplit("-", 2)[0] ++ active_artifacts_names.add(pkg_name) ++ ++ solver_errors = self._update_sack() ++ ++ dependency_error_spec = self._enable_dependencies(module_dicts) ++ if dependency_error_spec: ++ error_specs.extend(dependency_error_spec) ++ ++ # ++ fail_safe_repo = hawkey.MODULE_FAIL_SAFE_REPO_NAME ++ install_dict = {} ++ install_set_artifacts = set() ++ fail_safe_repo_used = False ++ ++ # list of name: [profiles] for module profiles being removed ++ removed_profiles = self.base._moduleContainer.getRemovedProfiles() ++ ++ for spec, (nsvcap, moduledict) in module_dicts.items(): ++ for name, streamdict in moduledict.items(): ++ for stream, module_list in streamdict.items(): ++ install_module_list = [x for x in module_list ++ if self.base._moduleContainer.isModuleActive(x.getId())] ++ if not install_module_list: ++ "No active matches for argument '{0}' in module '{1}:{2}'" ++ logger.error(_("No active matches for argument '{0}' in module " ++ "'{1}:{2}'").format(spec, name, stream)) ++ error_specs.append(spec) ++ continue ++ profiles = [] ++ latest_module = self._get_latest(install_module_list) ++ if latest_module.getRepoID() == fail_safe_repo: ++ msg = _( ++ "Installing module '{0}' from Fail-Safe repository {1} is not allowed") ++ logger.critical(msg.format(latest_module.getNameStream(), fail_safe_repo)) ++ fail_safe_repo_used = True ++ if nsvcap.profile: ++ profiles.extend(latest_module.getProfiles(nsvcap.profile)) ++ if not profiles: ++ available_profiles = latest_module.getProfiles() ++ if available_profiles: ++ profile_names = ", ".join(sorted( ++ [profile.getName() for profile in available_profiles])) ++ msg = _("Unable to match profile for argument {}. Available " ++ "profiles for '{}:{}': {}").format( ++ spec, name, stream, profile_names) ++ else: ++ msg = _("Unable to match profile for argument {}").format(spec) ++ logger.error(msg) ++ no_match_specs.append(spec) ++ continue ++ elif name in removed_profiles: ++ ++ for profile in removed_profiles[name]: ++ module_profiles = latest_module.getProfiles(profile) ++ if not module_profiles: ++ logger.warning( ++ _("Installed profile '{0}' is not available in module " ++ "'{1}' stream '{2}'").format(profile, name, stream)) ++ continue ++ profiles.extend(module_profiles) ++ for profile in profiles: ++ self.base._moduleContainer.install(latest_module, profile.getName()) ++ for pkg_name in profile.getContent(): ++ install_dict.setdefault(pkg_name, set()).add(spec) ++ for module in install_module_list: ++ artifacts = module.getArtifacts() ++ install_set_artifacts.update(artifacts) ++ for artifact in artifacts: ++ arch = artifact.rsplit(".", 1)[1] ++ if arch in src_arches: ++ continue ++ pkg_name = artifact.rsplit("-", 2)[0] ++ new_artifacts_names.add(pkg_name) ++ if fail_safe_repo_used: ++ raise dnf.exceptions.Error(_( ++ "Installing module from Fail-Safe repository is not allowed")) ++ install_base_query, profiles_errors = self._install_profiles_internal( ++ install_set_artifacts, install_dict, strict) ++ if profiles_errors: ++ error_specs.extend(profiles_errors) ++ ++ # distrosync module name ++ all_names = set() ++ all_names.update(new_artifacts_names) ++ all_names.update(active_artifacts_names) ++ remove_query = self.base.sack.query().filterm(empty=True) ++ for pkg_name in all_names: ++ query = self.base.sack.query().filterm(name=pkg_name) ++ installed = query.installed() ++ if not installed: ++ continue ++ available = query.available() ++ if not available: ++ logger.warning(_("No packages available to distrosync for package name " ++ "'{}'").format(pkg_name)) ++ if pkg_name not in new_artifacts_names: ++ remove_query = remove_query.union(query) ++ continue ++ ++ only_new_module = query.intersection(install_base_query) ++ if only_new_module: ++ query = only_new_module + sltr = dnf.selector.Selector(self.base.sack) + sltr.set(pkg=query) +- self.base._goal.install(select=sltr, optional=(not strict)) ++ self.base._goal.distupgrade(select=sltr) ++ self.base._remove_if_unneeded(remove_query) ++ + if no_match_specs or error_specs or solver_errors: + raise dnf.exceptions.MarkingErrors(no_match_group_specs=no_match_specs, + error_group_specs=error_specs, +@@ -183,7 +292,7 @@ class ModuleBase(object): + fail_safe_repo = hawkey.MODULE_FAIL_SAFE_REPO_NAME + fail_safe_repo_used = False + +- # Remove source packages they cannot be installed or upgraded ++ # Remove source packages because they cannot be installed or upgraded + base_no_source_query = self.base.sack.query().filterm(arch__neq=['src', 'nosrc']).apply() + + for spec in module_specs: +@@ -694,6 +803,35 @@ class ModuleBase(object): + def _format_repoid(self, repo_name): + return "{}\n".format(self.base.output.term.bold(repo_name)) + ++ def _install_profiles_internal(self, install_set_artifacts, install_dict, strict): ++ # Remove source packages because they cannot be installed or upgraded ++ base_no_source_query = self.base.sack.query().filterm(arch__neq=['src', 'nosrc']).apply() ++ install_base_query = base_no_source_query.filter(nevra_strict=install_set_artifacts) ++ error_specs = [] ++ ++ # add hot-fix packages ++ hot_fix_repos = [i.id for i in self.base.repos.iter_enabled() if i.module_hotfixes] ++ hotfix_packages = base_no_source_query.filter( ++ reponame=hot_fix_repos, name=install_dict.keys()) ++ install_base_query = install_base_query.union(hotfix_packages) ++ ++ for pkg_name, set_specs in install_dict.items(): ++ query = install_base_query.filter(name=pkg_name) ++ if not query: ++ # package can also be non-modular or part of another stream ++ query = base_no_source_query.filter(name=pkg_name) ++ if not query: ++ for spec in set_specs: ++ logger.error(_("Unable to resolve argument {}").format(spec)) ++ logger.error(_("No match for package {}").format(pkg_name)) ++ error_specs.extend(set_specs) ++ continue ++ self.base._goal.group_members.add(pkg_name) ++ sltr = dnf.selector.Selector(self.base.sack) ++ sltr.set(pkg=query) ++ self.base._goal.install(select=sltr, optional=(not strict)) ++ return install_base_query, error_specs ++ + + def format_modular_solver_errors(errors): + msg = dnf.util._format_resolve_problems(errors) +-- +2.26.2 + + +From df8c74679193bf27db584b3ad225997b2f5f4b87 Mon Sep 17 00:00:00 2001 +From: Jaroslav Mracek +Date: Thu, 12 Nov 2020 13:51:02 +0100 +Subject: [PATCH 3/5] [minor] Rename all variables with artefact to artifact + +--- + dnf/module/module_base.py | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/dnf/module/module_base.py b/dnf/module/module_base.py +index 03d54f72..7298c9a3 100644 +--- a/dnf/module/module_base.py ++++ b/dnf/module/module_base.py +@@ -73,7 +73,7 @@ class ModuleBase(object): + # + fail_safe_repo = hawkey.MODULE_FAIL_SAFE_REPO_NAME + install_dict = {} +- install_set_artefacts = set() ++ install_set_artifacts = set() + fail_safe_repo_used = False + for spec, (nsvcap, moduledict) in module_dicts.items(): + for name, streamdict in moduledict.items(): +@@ -136,12 +136,12 @@ class ModuleBase(object): + for pkg_name in profile.getContent(): + install_dict.setdefault(pkg_name, set()).add(spec) + for module in install_module_list: +- install_set_artefacts.update(module.getArtifacts()) ++ install_set_artifacts.update(module.getArtifacts()) + if fail_safe_repo_used: + raise dnf.exceptions.Error(_( + "Installing module from Fail-Safe repository is not allowed")) + __, profiles_errors = self._install_profiles_internal( +- install_set_artefacts, install_dict, strict) ++ install_set_artifacts, install_dict, strict) + if profiles_errors: + error_specs.extend(profiles_errors) + +@@ -326,8 +326,8 @@ class ModuleBase(object): + else: + for profile in latest_module.getProfiles(): + upgrade_package_set.update(profile.getContent()) +- for artefact in latest_module.getArtifacts(): +- subj = hawkey.Subject(artefact) ++ for artifact in latest_module.getArtifacts(): ++ subj = hawkey.Subject(artifact) + for nevra_obj in subj.get_nevra_possibilities( + forms=[hawkey.FORM_NEVRA]): + upgrade_package_set.add(nevra_obj.name) +-- +2.26.2 + + +From 0818bb80fc0846f602f338a2119671be97c47217 Mon Sep 17 00:00:00 2001 +From: Jaroslav Mracek +Date: Thu, 12 Nov 2020 15:11:29 +0100 +Subject: [PATCH 4/5] [doc] Add description of dnf module switch-to + +--- + doc/command_ref.rst | 30 ++++++++++++++++++++++-------- + 1 file changed, 22 insertions(+), 8 deletions(-) + +diff --git a/doc/command_ref.rst b/doc/command_ref.rst +index 83879013..c12837ea 100644 +--- a/doc/command_ref.rst ++++ b/doc/command_ref.rst +@@ -979,15 +979,31 @@ Module subcommands take :ref:`\\ `... arg + In case no profile was provided, all default profiles get installed. + Module streams get enabled accordingly. + +- This command cannot be used for switching module streams. It is recommended to remove all +- installed content from the module and reset the module using the +- :ref:`reset ` command. After you reset the module, you can install +- the other stream. ++ This command cannot be used for switching module streams. Use the ++ :ref:`dnf module switch-to ` command for that. + + ``dnf [options] module update ...`` + Update packages associated with an active module stream, optionally restricted to a profile. + If the `profile_name` is provided, only the packages referenced by that profile will be updated. + ++.. _module_switch_to_command-label: ++ ++``dnf [options] module switch-to ...`` ++ Switch to or enable a module stream, change versions of installed packages to versions provided ++ by the new stream, and remove packages from the old stream that are no longer available. It also ++ updates installed profiles if they are available for the new stream. When a profile was ++ provided, it installs that profile and does not update any already installed profiles. ++ ++ This command can be used as a stronger version of the ++ :ref:`dnf module enable ` command, which not only enables modules, ++ but also does a `distrosync` to all modular packages in the enabled modules. ++ ++ It can also be used as a stronger version of the ++ :ref:`dnf module install ` command, but it requires to specify ++ profiles that are supposed to be installed, because `switch-to` command does not use `default ++ profiles`. The `switch-to` command doesn't only install profiles, it also makes a `distrosync` ++ to all modular packages in the installed module. ++ + ``dnf [options] module remove ...`` + Remove installed module profiles, including packages that were installed with the + :ref:`dnf module install ` command. Will not remove packages +@@ -1010,10 +1026,8 @@ Module subcommands take :ref:`\\ `... arg + of modular dependency issue the operation will be rejected. To perform the action anyway please use + \-\ :ref:`-skip-broken ` option. + +- This command cannot be used for switching module streams. It is recommended to remove all +- installed content from the module, and reset the module using the +- :ref:`reset ` command. After you reset the module, you can enable +- the other stream. ++ This command cannot be used for switching module streams. Use the ++ :ref:`dnf module switch-to ` command for that. + + .. _module_disable_command-label: + +-- +2.26.2 + + +From 6b0b2b99e40c20706145e774626658825f5bc55d Mon Sep 17 00:00:00 2001 +From: Jaroslav Mracek +Date: Wed, 25 Nov 2020 12:34:30 +0100 +Subject: [PATCH 5/5] Do not use source rpms for module switch + +It prevents misleading message from libsolv that it tries to install +source rpm. +--- + dnf/module/module_base.py | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dnf/module/module_base.py b/dnf/module/module_base.py +index 7298c9a3..02d5d5a3 100644 +--- a/dnf/module/module_base.py ++++ b/dnf/module/module_base.py +@@ -253,8 +253,10 @@ class ModuleBase(object): + all_names.update(new_artifacts_names) + all_names.update(active_artifacts_names) + remove_query = self.base.sack.query().filterm(empty=True) ++ base_no_source_query = self.base.sack.query().filterm(arch__neq=['src', 'nosrc']).apply() ++ + for pkg_name in all_names: +- query = self.base.sack.query().filterm(name=pkg_name) ++ query = base_no_source_query.filter(name=pkg_name) + installed = query.installed() + if not installed: + continue +-- +2.26.2 + diff --git a/SOURCES/0009-yum.misc.decompress-to-handle-uncompressed-files-RhBug-1895059.patch b/SOURCES/0009-yum.misc.decompress-to-handle-uncompressed-files-RhBug-1895059.patch new file mode 100644 index 0000000..240495d --- /dev/null +++ b/SOURCES/0009-yum.misc.decompress-to-handle-uncompressed-files-RhBug-1895059.patch @@ -0,0 +1,107 @@ +From de8bbccc4e035a9a9b5baa3aeb0dbf0cb12f1fe2 Mon Sep 17 00:00:00 2001 +From: Marek Blaha +Date: Wed, 9 Dec 2020 13:45:46 +0100 +Subject: [PATCH 1/1] yum.misc.decompress() to handle uncompressed files + (RhBug:1895059) + +The underlying libdnf function is capable to handle even uncompressed +files - so now uncompressed files are just copied to the destination. +Also unused fn_only parameter of the function was removed. + +This fixes issue with "reposync -m" command when the group metadata file +in the repository is a plain xml file (not compressed). + +https://bugzilla.redhat.com/show_bug.cgi?id=1895059 +--- + dnf/yum/misc.py | 60 +++++++++++++++++++++++++++---------------------- + 1 file changed, 33 insertions(+), 27 deletions(-) + +diff --git a/dnf/yum/misc.py b/dnf/yum/misc.py +index 0f922350..3e3905fe 100644 +--- a/dnf/yum/misc.py ++++ b/dnf/yum/misc.py +@@ -386,34 +386,39 @@ def getloginuid(): + _cached_getloginuid = _getloginuid() + return _cached_getloginuid + +-def decompress(filename, dest=None, fn_only=False, check_timestamps=False): +- """take a filename and decompress it into the same relative location. +- if the file is not compressed just return the file""" +- +- ztype = None +- out = filename # If the file is not compressed, it returns the same file + +- dot_pos = filename.rfind('.') +- if dot_pos > 0: +- ext = filename[dot_pos:] +- if ext in ('.zck', '.xz', '.bz2', '.gz'): +- ztype = ext +- out = dest if dest else filename[:dot_pos] +- +- if ztype and not fn_only: +- if check_timestamps: +- fi = stat_f(filename) +- fo = stat_f(out) +- if fi and fo and fo.st_mtime == fi.st_mtime: +- return out ++def decompress(filename, dest=None, check_timestamps=False): ++ """take a filename and decompress it into the same relative location. ++ When the compression type is not recognized (or file is not compressed), ++ the content of the file is copied to the destination""" ++ ++ if dest: ++ out = dest ++ else: ++ out = None ++ dot_pos = filename.rfind('.') ++ if dot_pos > 0: ++ ext = filename[dot_pos:] ++ if ext in ('.zck', '.xz', '.bz2', '.gz', '.lzma', '.zst'): ++ out = filename[:dot_pos] ++ if out is None: ++ raise dnf.exceptions.MiscError("Could not determine destination filename") ++ ++ if check_timestamps: ++ fi = stat_f(filename) ++ fo = stat_f(out) ++ if fi and fo and fo.st_mtime == fi.st_mtime: ++ return out + +- try: +- libdnf.utils.decompress(filename, out, 0o644, ztype) +- except RuntimeError as e: +- raise dnf.exceptions.MiscError(str(e)) ++ try: ++ # libdnf.utils.decompress either decompress file to the destination or ++ # copy the content if the compression type is not recognized ++ libdnf.utils.decompress(filename, out, 0o644) ++ except RuntimeError as e: ++ raise dnf.exceptions.MiscError(str(e)) + +- if check_timestamps and fi: +- os.utime(out, (fi.st_mtime, fi.st_mtime)) ++ if check_timestamps and fi: ++ os.utime(out, (fi.st_mtime, fi.st_mtime)) + + return out + +@@ -424,13 +429,14 @@ def calculate_repo_gen_dest(filename, generated_name): + os.makedirs(dest, mode=0o755) + return dest + '/' + generated_name + +-def repo_gen_decompress(filename, generated_name, cached=False): ++ ++def repo_gen_decompress(filename, generated_name): + """ This is a wrapper around decompress, where we work out a cached + generated name, and use check_timestamps. filename _must_ be from + a repo. and generated_name is the type of the file. """ + + dest = calculate_repo_gen_dest(filename, generated_name) +- return decompress(filename, dest=dest, check_timestamps=True, fn_only=cached) ++ return decompress(filename, dest=dest, check_timestamps=True) + + def read_in_items_from_dot_dir(thisglob, line_as_list=True): + """ Takes a glob of a dir (like /etc/foo.d/\\*.foo) returns a list of all +-- +2.26.2 + diff --git a/SOURCES/0010-Make-log-rotated-permissions-match-initial-log-permissions-RhBug-1894344.patch b/SOURCES/0010-Make-log-rotated-permissions-match-initial-log-permissions-RhBug-1894344.patch new file mode 100644 index 0000000..3bfdafb --- /dev/null +++ b/SOURCES/0010-Make-log-rotated-permissions-match-initial-log-permissions-RhBug-1894344.patch @@ -0,0 +1,22 @@ +From 04b1a90bb24b7e98d4e001c44f8b3f563ad5f0f6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= +Date: Tue, 24 Nov 2020 14:31:21 +0100 +Subject: [PATCH] Make rotated log file (mode, owner, group) match previous log + settings (RhBug:1894344) + +https://bugzilla.redhat.com/show_bug.cgi?id=1894344 +--- + etc/logrotate.d/dnf | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/etc/logrotate.d/dnf b/etc/logrotate.d/dnf +index b96c6ff9b4..0ce2629f1b 100644 +--- a/etc/logrotate.d/dnf ++++ b/etc/logrotate.d/dnf +@@ -3,5 +3,5 @@ + notifempty + rotate 4 + weekly +- create 0600 root root ++ create + } diff --git a/SOURCES/0011-Add-new-attribute-for-Package--from-repo.patch b/SOURCES/0011-Add-new-attribute-for-Package--from-repo.patch new file mode 100644 index 0000000..49c2a1a --- /dev/null +++ b/SOURCES/0011-Add-new-attribute-for-Package--from-repo.patch @@ -0,0 +1,117 @@ +From eb2aa8c14208da7a567a0d79a8baa9f5201640cd Mon Sep 17 00:00:00 2001 +From: Jaroslav Mracek +Date: Tue, 24 Nov 2020 09:17:41 +0100 +Subject: [PATCH 1/3] Add `from_repo` attribute for Package class + (RhBug:1898968,1879168) + +It as an alias for private attribute _from_repo. + +https://bugzilla.redhat.com/show_bug.cgi?id=1898968 +https://bugzilla.redhat.com/show_bug.cgi?id=1879168 +--- + dnf/cli/commands/repoquery.py | 2 +- + dnf/package.py | 7 +++++-- + doc/api_package.rst | 6 ++++++ + 3 files changed, 12 insertions(+), 3 deletions(-) + +diff --git a/dnf/cli/commands/repoquery.py b/dnf/cli/commands/repoquery.py +index 099a9312d9..a11b440525 100644 +--- a/dnf/cli/commands/repoquery.py ++++ b/dnf/cli/commands/repoquery.py +@@ -44,7 +44,7 @@ + QFORMAT_MATCH = re.compile(r'%(-?\d*?){([:.\w]+?)}') + + QUERY_TAGS = """\ +-name, arch, epoch, version, release, reponame (repoid), evr, ++name, arch, epoch, version, release, reponame (repoid), from_repo, evr, + debug_name, source_name, source_debug_name, + installtime, buildtime, size, downloadsize, installsize, + provides, requires, obsoletes, conflicts, sourcerpm, +diff --git a/dnf/package.py b/dnf/package.py +index d44ce6706c..f647df6bff 100644 +--- a/dnf/package.py ++++ b/dnf/package.py +@@ -76,12 +76,15 @@ def _from_repo(self): + pkgrepo = None + if self._from_system: + pkgrepo = self.base.history.repo(self) +- else: +- pkgrepo = {} + if pkgrepo: + return '@' + pkgrepo + return self.reponame + ++ @property ++ def from_repo(self): ++ # :api ++ return self._from_repo ++ + @property + def _header(self): + return dnf.rpm._header(self.localPkg()) +diff --git a/doc/api_package.rst b/doc/api_package.rst +index 95df5d4b23..48ef8f1d22 100644 +--- a/doc/api_package.rst ++++ b/doc/api_package.rst +@@ -74,6 +74,12 @@ + + Files the package provides (list of strings). + ++ .. attribute:: from_repo ++ ++ For installed packages returns id of repository from which the package was installed prefixed ++ with '@' (if such information is available in the history database). Otherwise returns id of ++ repository the package belongs to (@System for installed packages of unknown origin) (string). ++ + .. attribute:: group + + Group of the package (string). + +From 1a933f8e036cd704fa6e7f77a8448263e93e540f Mon Sep 17 00:00:00 2001 +From: Jaroslav Mracek +Date: Tue, 24 Nov 2020 09:19:42 +0100 +Subject: [PATCH 2/3] Correct description of Package().reponane attribute + +--- + doc/api_package.rst | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/doc/api_package.rst b/doc/api_package.rst +index 48ef8f1d22..a78897babe 100644 +--- a/doc/api_package.rst ++++ b/doc/api_package.rst +@@ -138,7 +138,7 @@ + + .. attribute:: reponame + +- Id of repository the package was installed from (string). ++ Id of repository the package belongs to (@System for installed packages) (string). + + .. attribute:: requires + + +From 24cdb68776507fdae25bed0e82d80df3018aecfc Mon Sep 17 00:00:00 2001 +From: Jaroslav Mracek +Date: Tue, 24 Nov 2020 09:22:07 +0100 +Subject: [PATCH 3/3] Add unittest for new API + +--- + tests/api/test_dnf_package.py | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/tests/api/test_dnf_package.py b/tests/api/test_dnf_package.py +index 04cddc7ecc..5952352bb5 100644 +--- a/tests/api/test_dnf_package.py ++++ b/tests/api/test_dnf_package.py +@@ -163,6 +163,11 @@ def test_reponame(self): + self.assertHasAttr(self.package, "reponame") + self.assertHasType(self.package.reponame, str) + ++ def test_from_repo(self): ++ # Package.reponame ++ self.assertHasAttr(self.package, "from_repo") ++ self.assertHasType(self.package.from_repo, str) ++ + def test_requires(self): + # Package.requires + self.assertHasAttr(self.package, "requires") diff --git a/SOURCES/0012-Change-behaviour-of-Package-.from-repo.patch b/SOURCES/0012-Change-behaviour-of-Package-.from-repo.patch new file mode 100644 index 0000000..b81646b --- /dev/null +++ b/SOURCES/0012-Change-behaviour-of-Package-.from-repo.patch @@ -0,0 +1,80 @@ +From ca06d200d738fd6b23cb05b9776c9fd29288665f Mon Sep 17 00:00:00 2001 +From: Jaroslav Mracek +Date: Wed, 25 Nov 2020 13:00:22 +0100 +Subject: [PATCH 1/2] Change behaviour of Package().from_repo + +The change makes a difference between private attribute _from_repo and +API attribute. _from_repo is required for `dnf info` and we have to keep +it, but for API the magic handling behind could be confusing. +--- + dnf/package.py | 8 +++++++- + doc/api_package.rst | 5 ++--- + 2 files changed, 9 insertions(+), 4 deletions(-) + +diff --git a/dnf/package.py b/dnf/package.py +index f647df6bff..28ca5ef760 100644 +--- a/dnf/package.py ++++ b/dnf/package.py +@@ -73,6 +73,12 @@ def _from_system(self): + + @property + def _from_repo(self): ++ """ ++ For installed packages returns id of repository from which the package was installed ++ prefixed with '@' (if such information is available in the history database). Otherwise ++ returns id of repository the package belongs to (@System for installed packages of unknown ++ origin) ++ """ + pkgrepo = None + if self._from_system: + pkgrepo = self.base.history.repo(self) +@@ -83,7 +89,7 @@ def _from_repo(self): + @property + def from_repo(self): + # :api +- return self._from_repo ++ return self.base.history.repo(self) + + @property + def _header(self): +diff --git a/doc/api_package.rst b/doc/api_package.rst +index a78897babe..634f504ca6 100644 +--- a/doc/api_package.rst ++++ b/doc/api_package.rst +@@ -76,9 +76,8 @@ + + .. attribute:: from_repo + +- For installed packages returns id of repository from which the package was installed prefixed +- with '@' (if such information is available in the history database). Otherwise returns id of +- repository the package belongs to (@System for installed packages of unknown origin) (string). ++ For installed packages returns id of repository from which the package was installed if such ++ information is available in the history database. Otherwise returns an empty string (string). + + .. attribute:: group + + +From 895e61a1281db753dd28f01c20816e83c5316cdd Mon Sep 17 00:00:00 2001 +From: Jaroslav Mracek +Date: Thu, 26 Nov 2020 10:02:08 +0100 +Subject: [PATCH 2/2] fixup! Change behaviour of Package().from_repo + +--- + dnf/package.py | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dnf/package.py b/dnf/package.py +index 28ca5ef760..baef04fa5b 100644 +--- a/dnf/package.py ++++ b/dnf/package.py +@@ -89,7 +89,9 @@ def _from_repo(self): + @property + def from_repo(self): + # :api +- return self.base.history.repo(self) ++ if self._from_system: ++ return self.base.history.repo(self) ++ return "" + + @property + def _header(self): diff --git a/SOURCES/0013-Package-add-a-get-header--method.patch b/SOURCES/0013-Package-add-a-get-header--method.patch new file mode 100644 index 0000000..a5f8478 --- /dev/null +++ b/SOURCES/0013-Package-add-a-get-header--method.patch @@ -0,0 +1,94 @@ +From 38cc67385fb1b36aa0881bc5982bc58d75dac464 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hr=C3=A1zk=C3=BD?= +Date: Wed, 11 Nov 2020 18:45:11 +0100 +Subject: [PATCH] Package: add a get_header() method (RhBug:1876606) + +Adds get_header() method to the Package class, which returns the rpm +header of an installed package. + += changelog = +msg: Add get_header() method to the Package class +type: enhancement +resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1876606 +--- + dnf/package.py | 24 ++++++++++++++++++++++++ + tests/test_package.py | 12 ++++++++++++ + 2 files changed, 36 insertions(+) + +diff --git a/dnf/package.py b/dnf/package.py +index baef04fa5b..836e0e4989 100644 +--- a/dnf/package.py ++++ b/dnf/package.py +@@ -26,11 +26,13 @@ + from dnf.i18n import _ + + import binascii ++import dnf.exceptions + import dnf.rpm + import dnf.yum.misc + import hawkey + import logging + import os ++import rpm + + logger = logging.getLogger("dnf") + +@@ -95,6 +97,11 @@ def from_repo(self): + + @property + def _header(self): ++ """ ++ Returns the header of a locally present rpm package file. As opposed to ++ self.get_header(), which retrieves the header of an installed package ++ from rpmdb. ++ """ + return dnf.rpm._header(self.localPkg()) + + @property +@@ -164,6 +171,23 @@ def debugsource_name(self): + src_name = self.source_name if self.source_name is not None else self.name + return src_name + self.DEBUGSOURCE_SUFFIX + ++ def get_header(self): ++ """ ++ Returns the rpm header of the package if it is installed. If not ++ installed, returns None. The header is not cached, it is retrieved from ++ rpmdb on every call. In case of a failure (e.g. when the rpmdb changes ++ between loading the data and calling this method), raises an instance ++ of PackageNotFoundError. ++ """ ++ if not self._from_system: ++ return None ++ ++ try: ++ # RPMDBI_PACKAGES stands for the header of the package ++ return next(self.base._ts.dbMatch(rpm.RPMDBI_PACKAGES, self.rpmdbid)) ++ except StopIteration: ++ raise dnf.exceptions.PackageNotFoundError("Package not found when attempting to retrieve header", str(self)) ++ + @property + def source_debug_name(self): + # :api +diff --git a/tests/test_package.py b/tests/test_package.py +index cd4872e631..514e5bf099 100644 +--- a/tests/test_package.py ++++ b/tests/test_package.py +@@ -68,6 +68,18 @@ def fn_getter(): + with self.assertRaises(IOError): + pkg._header + ++ # rpm.hdr() is not easy to construct with custom data, we just return a string ++ # instead, as we don't actually need an instance of rpm.hdr for the test ++ @mock.patch("rpm.TransactionSet.dbMatch", lambda self, a, b: iter(["package_header_test_data"])) ++ def test_get_header(self): ++ pkg = self.sack.query().installed().filter(name="pepper")[0] ++ header = pkg.get_header() ++ self.assertEqual(header, "package_header_test_data") ++ ++ pkg = self.sack.query().available().filter(name="pepper")[0] ++ header = pkg.get_header() ++ self.assertEqual(header, None) ++ + @mock.patch("dnf.package.Package.rpmdbid", long(3)) + def test_idx(self): + """ pkg.idx is an int. """ diff --git a/SOURCES/0014-Add-api-function-fill-sack-from-repos-in-cache-RhBug-1865803.patch b/SOURCES/0014-Add-api-function-fill-sack-from-repos-in-cache-RhBug-1865803.patch new file mode 100644 index 0000000..d912d69 --- /dev/null +++ b/SOURCES/0014-Add-api-function-fill-sack-from-repos-in-cache-RhBug-1865803.patch @@ -0,0 +1,115 @@ +From b3542a96c6f77e5cc0b5217e586fcc56fde074d8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= +Date: Wed, 2 Dec 2020 15:27:13 +0100 +Subject: [PATCH 1/2] Add api function: fill_sack_from_repos_in_cache + (RhBug:1865803) + += changelog = +msg: Add api function fill_sack_from_repos_in_cache to allow loading a repo cache with repomd and (solv file or primary xml) only +type: enhancement +resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1865803 +--- + dnf.spec | 2 +- + dnf/base.py | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 63 insertions(+), 1 deletion(-) + +diff --git a/dnf/base.py b/dnf/base.py +index 075e74265a..a10b837340 100644 +--- a/dnf/base.py ++++ b/dnf/base.py +@@ -425,6 +425,68 @@ def fill_sack(self, load_system_repo=True, load_available_repos=True): + self._plugins.run_sack() + return self._sack + ++ def fill_sack_from_repos_in_cache(self, load_system_repo=True): ++ # :api ++ """ ++ Prepare Sack and Goal objects and also load all enabled repositories from cache only, ++ it doesn't download anything and it doesn't check if metadata are expired. ++ If there is not enough metadata present (repond.xml or both primary.xml and solv file ++ are missing) given repo is either skipped or it throws a RepoError exception depending ++ on skip_if_unavailable configuration. ++ """ ++ timer = dnf.logging.Timer('sack setup') ++ self.reset(sack=True, goal=True) ++ self._sack = dnf.sack._build_sack(self) ++ lock = dnf.lock.build_metadata_lock(self.conf.cachedir, self.conf.exit_on_lock) ++ with lock: ++ if load_system_repo is not False: ++ try: ++ # FIXME: If build_cache=True, @System.solv is incorrectly updated in install- ++ # remove loops ++ self._sack.load_system_repo(build_cache=False) ++ except IOError: ++ if load_system_repo != 'auto': ++ raise ++ ++ error_repos = [] ++ # Iterate over installed GPG keys and check their validity using DNSSEC ++ if self.conf.gpgkey_dns_verification: ++ dnf.dnssec.RpmImportedKeys.check_imported_keys_validity() ++ for repo in self.repos.iter_enabled(): ++ try: ++ repo._repo.loadCache(throwExcept=True, ignoreMissing=True) ++ mdload_flags = dict(load_filelists=True, ++ load_presto=repo.deltarpm, ++ load_updateinfo=True) ++ if repo.load_metadata_other: ++ mdload_flags["load_other"] = True ++ ++ self._sack.load_repo(repo._repo, **mdload_flags) ++ ++ logger.debug(_("%s: using metadata from %s."), repo.id, ++ dnf.util.normalize_time( ++ repo._repo.getMaxTimestamp())) ++ except (RuntimeError, hawkey.Exception) as e: ++ if repo.skip_if_unavailable is False: ++ raise dnf.exceptions.RepoError( ++ _("loading repo '{}' failure: {}").format(repo.id, e)) ++ else: ++ logger.debug(_("loading repo '{}' failure: {}").format(repo.id, e)) ++ error_repos.append(repo.id) ++ repo.disable() ++ if error_repos: ++ logger.warning( ++ _("Ignoring repositories: %s"), ', '.join(error_repos)) ++ ++ conf = self.conf ++ self._sack._configure(conf.installonlypkgs, conf.installonly_limit, conf.allow_vendor_change) ++ self._setup_excludes_includes() ++ timer() ++ self._goal = dnf.goal.Goal(self._sack) ++ self._goal.protect_running_kernel = conf.protect_running_kernel ++ self._plugins.run_sack() ++ return self._sack ++ + def _finalize_base(self): + self._tempfile_persistor = dnf.persistor.TempfilePersistor( + self.conf.cachedir) + +From 29ae53918d4a0b65a917aca2f8f43416fee15dfd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= +Date: Thu, 10 Dec 2020 14:54:16 +0100 +Subject: [PATCH 2/2] Add api test for new fill_sack_from_repos_in_cache + +--- + tests/api/test_dnf_base.py | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/tests/api/test_dnf_base.py b/tests/api/test_dnf_base.py +index 656bd22584..335981897e 100644 +--- a/tests/api/test_dnf_base.py ++++ b/tests/api/test_dnf_base.py +@@ -107,6 +107,12 @@ def test_fill_sack(self): + + self.base.fill_sack(load_system_repo=False, load_available_repos=False) + ++ def test_fill_sack_from_repos_in_cache(self): ++ # Base.fill_sack_from_repos_in_cache(self, load_system_repo=True): ++ self.assertHasAttr(self.base, "fill_sack_from_repos_in_cache") ++ ++ self.base.fill_sack_from_repos_in_cache(load_system_repo=False) ++ + def test_close(self): + # Base.close() + self.assertHasAttr(self.base, "close") diff --git a/SOURCES/0015-Add-tests-and-docs-for-fill-sack-from-repos-in-cache-RhBug-1865803.patch b/SOURCES/0015-Add-tests-and-docs-for-fill-sack-from-repos-in-cache-RhBug-1865803.patch new file mode 100644 index 0000000..30dbab6 --- /dev/null +++ b/SOURCES/0015-Add-tests-and-docs-for-fill-sack-from-repos-in-cache-RhBug-1865803.patch @@ -0,0 +1,366 @@ +From a777ff01c79d5e0e2cf3ae7b0652795577253bc3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= +Date: Thu, 14 Jan 2021 09:58:30 +0100 +Subject: [PATCH 1/3] Fix recreate script + +--- + tests/repos/rpm/recreate | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/repos/rpm/recreate b/tests/repos/rpm/recreate +index da348d9799..0fbb9396bd 100755 +--- a/tests/repos/rpm/recreate ++++ b/tests/repos/rpm/recreate +@@ -1,6 +1,6 @@ + #!/bin/bash + +-THISDIR="$( readlink -f "$( dirname "$0 )" )" ++THISDIR="$( readlink -f "$( dirname "$0" )" )" + cd "$THISDIR" + git rm -rf repodata/ + createrepo --no-database -o . .. + +From 5d4c0266f6967c7cd5f0e675b13fa3e9b395e4dd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= +Date: Thu, 14 Jan 2021 10:28:53 +0100 +Subject: [PATCH 2/3] Add unit test for fill_sack_from_repos_in_cache + (RhBug:1865803) + +https://bugzilla.redhat.com/show_bug.cgi?id=1865803 +--- + tests/test_fill_sack_from_repos_in_cache.py | 262 ++++++++++++++++++++ + 1 file changed, 262 insertions(+) + create mode 100644 tests/test_fill_sack_from_repos_in_cache.py + +diff --git a/tests/test_fill_sack_from_repos_in_cache.py b/tests/test_fill_sack_from_repos_in_cache.py +new file mode 100644 +index 0000000000..24b0d4598d +--- /dev/null ++++ b/tests/test_fill_sack_from_repos_in_cache.py +@@ -0,0 +1,262 @@ ++# -*- coding: utf-8 -*- ++ ++# Copyright (C) 2012-2021 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 os ++import tempfile ++import glob ++import shutil ++import unittest ++ ++import dnf.exceptions ++import dnf.repo ++import dnf.sack ++ ++import hawkey ++ ++import tests.support ++from tests.support import mock ++ ++TEST_REPO_NAME = "test-repo" ++ ++ ++class FillSackFromReposInCacheTest(unittest.TestCase): ++ def _create_cache_for_repo(self, repopath, tmpdir): ++ conf = dnf.conf.MainConf() ++ conf.cachedir = os.path.join(tmpdir, "cache") ++ ++ base = dnf.Base(conf=conf) ++ ++ repoconf = dnf.repo.Repo(TEST_REPO_NAME, base.conf) ++ repoconf.baseurl = repopath ++ repoconf.enable() ++ ++ base.repos.add(repoconf) ++ ++ base.fill_sack(load_system_repo=False) ++ base.close() ++ ++ def _setUp_from_repo_path(self, original_repo_path): ++ self.tmpdir = tempfile.mkdtemp(prefix="dnf_test_") ++ ++ self.repo_copy_path = os.path.join(self.tmpdir, "repo") ++ shutil.copytree(original_repo_path, self.repo_copy_path) ++ ++ self._create_cache_for_repo(self.repo_copy_path, self.tmpdir) ++ ++ # Just to be sure remove repo (it shouldn't be used) ++ shutil.rmtree(self.repo_copy_path) ++ ++ # Prepare base for the actual test ++ conf = dnf.conf.MainConf() ++ conf.cachedir = os.path.join(self.tmpdir, "cache") ++ self.test_base = dnf.Base(conf=conf) ++ repoconf = dnf.repo.Repo(TEST_REPO_NAME, conf) ++ repoconf.baseurl = self.repo_copy_path ++ repoconf.enable() ++ self.test_base.repos.add(repoconf) ++ ++ def tearDown(self): ++ self.test_base.close() ++ shutil.rmtree(self.tmpdir) ++ ++ def test_with_solv_solvx_repomd(self): ++ self._setUp_from_repo_path(os.path.join(os.path.abspath(os.path.dirname(__file__)), "repos/rpm")) ++ ++ # Remove xml metadata except repomd ++ # repomd.xml is not compressed and doesn't end with .gz ++ repodata_without_repomd = glob.glob(os.path.join(self.tmpdir, "cache/test-repo-*/repodata/*.gz")) ++ for f in repodata_without_repomd: ++ os.remove(f) ++ ++ # Now we only have cache with just solv, solvx files and repomd.xml ++ ++ self.test_base.fill_sack_from_repos_in_cache(load_system_repo=False) ++ ++ q = self.test_base.sack.query() ++ packages = q.run() ++ self.assertEqual(len(packages), 9) ++ self.assertEqual(packages[0].evr, "4-4") ++ ++ # Use *-updateinfo.solvx ++ adv_pkgs = q.get_advisory_pkgs(hawkey.LT | hawkey.EQ | hawkey.GT) ++ adv_titles = set() ++ for pkg in adv_pkgs: ++ adv_titles.add(pkg.get_advisory(self.test_base.sack).title) ++ self.assertEqual(len(adv_titles), 3) ++ ++ def test_with_just_solv_repomd(self): ++ self._setUp_from_repo_path(os.path.join(os.path.abspath(os.path.dirname(__file__)), "repos/rpm")) ++ ++ # Remove xml metadata except repomd ++ # repomd.xml is not compressed and doesn't end with .gz ++ repodata_without_repomd = glob.glob(os.path.join(self.tmpdir, "cache/test-repo-*/repodata/*.gz")) ++ for f in repodata_without_repomd: ++ os.remove(f) ++ ++ # Remove solvx files ++ solvx = glob.glob(os.path.join(self.tmpdir, "cache/*.solvx")) ++ for f in solvx: ++ os.remove(f) ++ ++ # Now we only have cache with just solv files and repomd.xml ++ ++ self.test_base.fill_sack_from_repos_in_cache(load_system_repo=False) ++ ++ q = self.test_base.sack.query() ++ packages = q.run() ++ self.assertEqual(len(packages), 9) ++ self.assertEqual(packages[0].evr, "4-4") ++ ++ # No *-updateinfo.solvx -> we get no advisory packages ++ adv_pkgs = q.get_advisory_pkgs(hawkey.LT | hawkey.EQ | hawkey.GT) ++ self.assertEqual(len(adv_pkgs), 0) ++ ++ def test_with_xml_metadata(self): ++ self._setUp_from_repo_path(os.path.join(os.path.abspath(os.path.dirname(__file__)), "repos/rpm")) ++ ++ # Remove all solv and solvx files ++ solvx = glob.glob(os.path.join(self.tmpdir, "cache/*.solv*")) ++ for f in solvx: ++ os.remove(f) ++ ++ # Now we only have cache with just xml metadata ++ ++ self.test_base.fill_sack_from_repos_in_cache(load_system_repo=False) ++ ++ q = self.test_base.sack.query() ++ packages = q.run() ++ self.assertEqual(len(packages), 9) ++ self.assertEqual(packages[0].evr, "4-4") ++ ++ def test_exception_without_repomd(self): ++ self._setUp_from_repo_path(os.path.join(os.path.abspath(os.path.dirname(__file__)), "repos/rpm")) ++ ++ # Remove xml metadata ++ repodata_without_repomd = glob.glob(os.path.join(self.tmpdir, "cache/test-repo-*/repodata/*")) ++ for f in repodata_without_repomd: ++ os.remove(f) ++ ++ # Now we only have cache with just solv and solvx files ++ # Since we don't have repomd we cannot verify checksums -> fail (exception) ++ ++ self.assertRaises(dnf.exceptions.RepoError, ++ self.test_base.fill_sack_from_repos_in_cache, load_system_repo=False) ++ ++ def test_exception_with_just_repomd(self): ++ self._setUp_from_repo_path(os.path.join(os.path.abspath(os.path.dirname(__file__)), "repos/rpm")) ++ ++ # Remove xml metadata except repomd ++ # repomd.xml is not compressed and doesn't end with .gz ++ repodata_without_repomd = glob.glob(os.path.join(self.tmpdir, "cache/test-repo-*/repodata/*.gz")) ++ for f in repodata_without_repomd: ++ os.remove(f) ++ ++ # Remove all solv and solvx files ++ solvx = glob.glob(os.path.join(self.tmpdir, "cache/*.solv*")) ++ for f in solvx: ++ os.remove(f) ++ ++ # Now we only have cache with just repomd ++ # repomd is not enough, it doesn't contain the metadata it self -> fail (exception) ++ ++ self.assertRaises(dnf.exceptions.RepoError, ++ self.test_base.fill_sack_from_repos_in_cache, load_system_repo=False) ++ ++ def test_exception_with_checksum_mismatch_and_only_repomd(self): ++ self._setUp_from_repo_path(os.path.join(os.path.abspath(os.path.dirname(__file__)), "repos/rpm")) ++ ++ # Remove xml metadata except repomd ++ # repomd.xml is not compressed and doesn't end with .gz ++ repodata_without_repomd = glob.glob(os.path.join(self.tmpdir, "cache/test-repo-*/repodata/*.gz")) ++ for f in repodata_without_repomd: ++ os.remove(f) ++ ++ # Modify checksum of solv file so it doesn't match with repomd ++ solv = glob.glob(os.path.join(self.tmpdir, "cache/*.solv"))[0] ++ with open(solv, "a") as opensolv: ++ opensolv.write("appended text to change checksum") ++ ++ # Now we only have cache with solvx, modified solv file and just repomd ++ # Since we don't have original xml metadata we cannot regenerate solv -> fail (exception) ++ ++ self.assertRaises(dnf.exceptions.RepoError, ++ self.test_base.fill_sack_from_repos_in_cache, load_system_repo=False) ++ ++ def test_checksum_mistmatch_regenerates_solv(self): ++ self._setUp_from_repo_path(os.path.join(os.path.abspath(os.path.dirname(__file__)), "repos/rpm")) ++ ++ # Modify checksum of solv file so it doesn't match with repomd ++ solv = glob.glob(os.path.join(self.tmpdir, "cache/*.solv"))[0] ++ with open(solv, "a") as opensolv: ++ opensolv.write("appended text to change checksum") ++ ++ # Now we only have cache with solvx, modified solv file and xml metadata. ++ # Checksum mistmatch causes regeneration of solv file and repo works. ++ ++ self.test_base.fill_sack_from_repos_in_cache(load_system_repo=False) ++ ++ q = self.test_base.sack.query() ++ packages = q.run() ++ self.assertEqual(len(packages), 9) ++ self.assertEqual(packages[0].evr, "4-4") ++ ++ def test_with_modules_yaml(self): ++ self._setUp_from_repo_path(os.path.join(os.path.abspath(os.path.dirname(__file__)), ++ "modules/modules/_all/x86_64")) ++ ++ # Now we have full cache (also with modules.yaml) ++ ++ self.test_base.fill_sack_from_repos_in_cache(load_system_repo=False) ++ ++ q = self.test_base.sack.query() ++ packages = q.run() ++ self.assertEqual(len(packages), 8) ++ self.assertEqual(packages[0].evr, "2.02-0.40") ++ ++ self.module_base = dnf.module.module_base.ModuleBase(self.test_base) ++ modules, _ = self.module_base._get_modules("base-runtime*") ++ self.assertEqual(len(modules), 3) ++ self.assertEqual(modules[0].getFullIdentifier(), "base-runtime:f26:1::") ++ ++ def test_with_modular_repo_without_modules_yaml(self): ++ self._setUp_from_repo_path(os.path.join(os.path.abspath(os.path.dirname(__file__)), ++ "modules/modules/_all/x86_64")) ++ ++ # Remove xml and yaml metadata except repomd ++ # repomd.xml is not compressed and doesn't end with .gz ++ repodata_without_repomd = glob.glob(os.path.join(self.tmpdir, "cache/test-repo-*/repodata/*.gz")) ++ for f in repodata_without_repomd: ++ os.remove(f) ++ ++ # Now we have just solv, *-filenames.solvx and repomd.xml (modules.yaml are not processed into *-modules.solvx) ++ ++ self.test_base.fill_sack_from_repos_in_cache(load_system_repo=False) ++ ++ q = self.test_base.sack.query() ++ packages = q.run() ++ # We have many more packages because they are not hidden by modules ++ self.assertEqual(len(packages), 44) ++ self.assertEqual(packages[0].evr, "10.0-7") ++ ++ self.module_base = dnf.module.module_base.ModuleBase(self.test_base) ++ modules, _ = self.module_base._get_modules("base-runtime*") ++ self.assertEqual(len(modules), 0) + +From de6177dba3dc20191e275eec14672570a0c4f4a8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= +Date: Thu, 14 Jan 2021 12:29:06 +0100 +Subject: [PATCH 3/3] Add docs and examples for fill_sack_from_repos_in_cache + (RhBug:1865803) + +https://bugzilla.redhat.com/show_bug.cgi?id=1865803 +--- + doc/api_base.rst | 41 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 41 insertions(+) + +diff --git a/doc/api_base.rst b/doc/api_base.rst +index 24ecb50e43..f0b1992e88 100644 +--- a/doc/api_base.rst ++++ b/doc/api_base.rst +@@ -111,6 +111,47 @@ + print("id: {}".format(repo.id)) + print("baseurl: {}".format(repo.baseurl)) + ++ .. method:: fill_sack_from_repos_in_cache(load_system_repo=True) ++ ++ Prepare Sack and Goal objects and load all enabled repositories from cache only, it doesn't download anything and it doesn't check if metadata are expired. ++ To successfully load a repository cache it requires repond.xml plus metadata (xml, yaml) or repond.xml plus generated cache files (solv, solvx). ++ If there is not enough metadata given repo is either skipped or it throws a :exc:`dnf.exceptions.RepoError` exception depending on :attr:`dnf.conf.Conf.skip_if_unavailable` configuration. ++ ++ All additional metadata are loaded if present but are not generally required. Note that some metadata like updateinfo.xml get processed into a solvx cache file and its sufficient to have either xml or solvx. Module metadata represented by modules.yaml are not processed therefore they are needed when they are defined in repomd.xml. ++ ++ Example of loading all configured repositories from cache and printing available packages' names:: ++ ++ #!/usr/bin/python3 ++ import dnf ++ ++ with dnf.Base() as base: ++ base.read_all_repos() ++ ++ base.fill_sack_from_repos_in_cache(load_system_repo=False) ++ ++ query = base.sack.query().available() ++ for pkg in query.run(): ++ print(pkg.name) ++ ++ Example of loading a single repository and printing available packages' names without reading repository configuration:: ++ ++ #!/usr/bin/python3 ++ import dnf ++ ++ with dnf.Base() as base: ++ repo = dnf.repo.Repo("rawhide", base.conf) ++ ++ # Repository cache is also identified by its source therefore to find it you need to ++ # set metalink, mirrorlist or baseurl to the same value from which it was created. ++ repo.metalink = "https://mirrors.fedoraproject.org/metalink?repo=rawhide&arch=x86_64" ++ ++ base.repos.add(repo) ++ ++ base.fill_sack_from_repos_in_cache(load_system_repo=False) ++ ++ query = base.sack.query().available() ++ for pkg in query.run(): ++ print(pkg.name) + + .. method:: do_transaction([display]) + diff --git a/SOURCES/0016-Run-tests-for-fill-sack-from-repos-in-cache-in-installroot..patch b/SOURCES/0016-Run-tests-for-fill-sack-from-repos-in-cache-in-installroot..patch new file mode 100644 index 0000000..2c8f13b --- /dev/null +++ b/SOURCES/0016-Run-tests-for-fill-sack-from-repos-in-cache-in-installroot..patch @@ -0,0 +1,43 @@ +From 291071a937a1de398641f02002413678398e473c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= +Date: Mon, 8 Feb 2021 08:25:46 +0100 +Subject: [PATCH] Run tests for fill_sack_from_repos_in_cache in installroot + (RhBug:1865803) + +This prevents loading data (like failsafe) from host. + +It also allows testing that there are no modules in the installroot not just +no base-runtime* in test_with_modular_repo_without_modules_yaml. + +https://bugzilla.redhat.com/show_bug.cgi?id=1865803 +--- + tests/test_fill_sack_from_repos_in_cache.py | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/tests/test_fill_sack_from_repos_in_cache.py b/tests/test_fill_sack_from_repos_in_cache.py +index 24b0d4598d..f27235bf84 100644 +--- a/tests/test_fill_sack_from_repos_in_cache.py ++++ b/tests/test_fill_sack_from_repos_in_cache.py +@@ -42,6 +42,7 @@ class FillSackFromReposInCacheTest(unittest.TestCase): + def _create_cache_for_repo(self, repopath, tmpdir): + conf = dnf.conf.MainConf() + conf.cachedir = os.path.join(tmpdir, "cache") ++ conf.installroot = os.path.join(tmpdir) + + base = dnf.Base(conf=conf) + +@@ -68,6 +69,7 @@ def _setUp_from_repo_path(self, original_repo_path): + # Prepare base for the actual test + conf = dnf.conf.MainConf() + conf.cachedir = os.path.join(self.tmpdir, "cache") ++ conf.installroot = os.path.join(self.tmpdir) + self.test_base = dnf.Base(conf=conf) + repoconf = dnf.repo.Repo(TEST_REPO_NAME, conf) + repoconf.baseurl = self.repo_copy_path +@@ -258,5 +260,5 @@ def test_with_modular_repo_without_modules_yaml(self): + self.assertEqual(packages[0].evr, "10.0-7") + + self.module_base = dnf.module.module_base.ModuleBase(self.test_base) +- modules, _ = self.module_base._get_modules("base-runtime*") ++ modules, _ = self.module_base._get_modules("*") + self.assertEqual(len(modules), 0) diff --git a/SOURCES/0017-Set-persistdir-for-fill-sack-from-repos-in-cache-tests-RhBug-1865803.patch b/SOURCES/0017-Set-persistdir-for-fill-sack-from-repos-in-cache-tests-RhBug-1865803.patch new file mode 100644 index 0000000..b6dee78 --- /dev/null +++ b/SOURCES/0017-Set-persistdir-for-fill-sack-from-repos-in-cache-tests-RhBug-1865803.patch @@ -0,0 +1,61 @@ +From 40e762da5cd2d876b6424f4c25b77e8dc2422a0f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= +Date: Mon, 8 Feb 2021 08:25:46 +0100 +Subject: [PATCH] Set persistdir and substitutions for + fill_sack_from_repos_in_cache tests (RhBug:1865803) + +Setting just installroot is not enough because persistdir is not +automatically prepended with installroot if set via API. + +Also assert exact package names which is more useful output in case the +test fails. + +https://bugzilla.redhat.com/show_bug.cgi?id=1865803 +--- + tests/test_fill_sack_from_repos_in_cache.py | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +diff --git a/tests/test_fill_sack_from_repos_in_cache.py b/tests/test_fill_sack_from_repos_in_cache.py +index f27235bf84..23fd2a4337 100644 +--- a/tests/test_fill_sack_from_repos_in_cache.py ++++ b/tests/test_fill_sack_from_repos_in_cache.py +@@ -42,7 +42,10 @@ class FillSackFromReposInCacheTest(unittest.TestCase): + def _create_cache_for_repo(self, repopath, tmpdir): + conf = dnf.conf.MainConf() + conf.cachedir = os.path.join(tmpdir, "cache") +- conf.installroot = os.path.join(tmpdir) ++ conf.installroot = tmpdir ++ conf.persistdir = os.path.join(conf.installroot, conf.persistdir.lstrip("/")) ++ conf.substitutions["arch"] = "x86_64" ++ conf.substitutions["basearch"] = dnf.rpm.basearch(conf.substitutions["arch"]) + + base = dnf.Base(conf=conf) + +@@ -69,7 +72,10 @@ def _setUp_from_repo_path(self, original_repo_path): + # Prepare base for the actual test + conf = dnf.conf.MainConf() + conf.cachedir = os.path.join(self.tmpdir, "cache") +- conf.installroot = os.path.join(self.tmpdir) ++ conf.installroot = self.tmpdir ++ conf.persistdir = os.path.join(conf.installroot, conf.persistdir.lstrip("/")) ++ conf.substitutions["arch"] = "x86_64" ++ conf.substitutions["basearch"] = dnf.rpm.basearch(conf.substitutions["arch"]) + self.test_base = dnf.Base(conf=conf) + repoconf = dnf.repo.Repo(TEST_REPO_NAME, conf) + repoconf.baseurl = self.repo_copy_path +@@ -231,8 +237,13 @@ def test_with_modules_yaml(self): + + q = self.test_base.sack.query() + packages = q.run() +- self.assertEqual(len(packages), 8) +- self.assertEqual(packages[0].evr, "2.02-0.40") ++ ++ pkg_names = [] ++ for pkg in packages: ++ pkg_names.append(pkg.name) ++ ++ self.assertEqual(pkg_names, ['grub2', 'httpd', 'httpd', 'httpd-doc', 'httpd-doc', 'httpd-provides-name-doc', ++ 'httpd-provides-name-version-release-doc', 'libnghttp2']) + + self.module_base = dnf.module.module_base.ModuleBase(self.test_base) + modules, _ = self.module_base._get_modules("base-runtime*") diff --git a/SOURCES/0018-Allow-stream-switching-if-option-enabled.patch b/SOURCES/0018-Allow-stream-switching-if-option-enabled.patch new file mode 100644 index 0000000..0fd4828 --- /dev/null +++ b/SOURCES/0018-Allow-stream-switching-if-option-enabled.patch @@ -0,0 +1,56 @@ +From 9ceb74f77479910f7844a9a87d4b7623687076be Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= +Date: Fri, 24 Jul 2020 07:59:38 +0200 +Subject: [PATCH] Allow stream switching if option enabled + += changelog = +msg: New config option module_allow_stream_switch allows switching enabled streams +type: enhancement +--- + dnf.spec | 2 +- + dnf/cli/cli.py | 19 ++++++++++--------- + 2 files changed, 11 insertions(+), 10 deletions(-) + +diff --git a/dnf.spec b/dnf.spec +index 0e63b2b422..04f6f104c7 100644 +--- a/dnf.spec ++++ b/dnf.spec +@@ -2,7 +2,7 @@ + %undefine __cmake_in_source_build + + # default dependencies +-%global hawkey_version 0.54.4 ++%global hawkey_version 0.55.0 + %global libcomps_version 0.1.8 + %global libmodulemd_version 1.4.0 + %global rpm_version 4.14.0 +diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py +index be737ed3b7..29d7373fa3 100644 +--- a/dnf/cli/cli.py ++++ b/dnf/cli/cli.py +@@ -166,15 +166,16 @@ def do_transaction(self, display=()): + :return: history database transaction ID or None + """ + if dnf.base.WITH_MODULES: +- switchedModules = dict(self._moduleContainer.getSwitchedStreams()) +- if switchedModules: +- report_module_switch(switchedModules) +- msg = _("It is not possible to switch enabled streams of a module.\n" +- "It is recommended to remove all installed content from the module, and " +- "reset the module using '{prog} module reset ' command. After " +- "you reset the module, you can install the other stream.").format( +- prog=dnf.util.MAIN_PROG) +- raise dnf.exceptions.Error(msg) ++ if not self.conf.module_stream_switch: ++ switchedModules = dict(self._moduleContainer.getSwitchedStreams()) ++ if switchedModules: ++ report_module_switch(switchedModules) ++ msg = _("It is not possible to switch enabled streams of a module.\n" ++ "It is recommended to remove all installed content from the module, and " ++ "reset the module using '{prog} module reset ' command. After " ++ "you reset the module, you can install the other stream.").format( ++ prog=dnf.util.MAIN_PROG) ++ raise dnf.exceptions.Error(msg) + + trans = self.transaction + pkg_str = self.output.list_transaction(trans) diff --git a/SPECS/dnf.spec b/SPECS/dnf.spec new file mode 100644 index 0000000..137e6ff --- /dev/null +++ b/SPECS/dnf.spec @@ -0,0 +1,2709 @@ +# default dependencies +%global hawkey_version 0.55.0-5 +%global libcomps_version 0.1.8 +%global libmodulemd_version 1.4.0 +%global rpm_version 4.14.2-35 + +# conflicts +%global conflicts_dnf_plugins_core_version 4.0.16 +%global conflicts_dnf_plugins_extras_version 4.0.4 +%global conflicts_dnfdaemon_version 0.3.19 + +# override dependencies for rhel 7 +%if 0%{?rhel} == 7 + %global rpm_version 4.11.3-32 +%endif + +%if 0%{?rhel} == 7 && 0%{?centos} + %global rpm_version 4.11.3-25.el7.centos.1 +%endif + +# override dependencies for fedora 26 +%if 0%{?fedora} == 26 + %global rpm_version 4.13.0.1-7 +%endif + + +%if 0%{?rhel} && 0%{?rhel} <= 7 +%bcond_with python3 +%else +%bcond_without python3 +%endif + +%if 0%{?rhel} >= 8 || 0%{?fedora} > 29 +# Disable python2 build +%bcond_with python2 +%else +%bcond_without python2 +%endif + +# YUM compat subpackage configuration +# +# level=full -> deploy all compat symlinks (conflicts with yum < 4) +# level=minimal -> deploy a subset of compat symlinks only +# (no conflict with yum >= 3.4.3-505)* +# level=preview -> minimal level with altered paths (no conflict with yum < 4) +# *release 505 renamed /usr/bin/yum to /usr/bin/yum-deprecated +%global yum_compat_level full +%global yum_subpackage_name yum +%if 0%{?fedora} + # Avoid file conflict with yum < 4 in all Fedoras + # It can be resolved by pretrans scriptlet but they are not recommended in Fedora + %global yum_compat_level minimal + %if 0%{?fedora} < 31 + # Avoid name conflict with yum < 4 + %global yum_subpackage_name %{name}-yum + %endif +%endif +%if 0%{?rhel} && 0%{?rhel} <= 7 + %global yum_compat_level preview + %global yum_subpackage_name nextgen-yum4 +%endif + +# paths +%global confdir %{_sysconfdir}/%{name} +%global pluginconfpath %{confdir}/plugins + +%if %{with python2} + %global py2pluginpath %{python2_sitelib}/%{name}-plugins +%endif + +%if %{with python3} + %global py3pluginpath %{python3_sitelib}/%{name}-plugins +%endif + +# Use the same directory of the main package for subpackage licence and docs +%global _docdir_fmt %{name} + + +%global pkg_summary Package manager +%global pkg_description Utility that allows users to manage packages on their systems. \ +It supports RPMs, modules and comps groups & environments. + +Name: dnf +Version: 4.4.2 +Release: 10%{?dist} +Summary: %{pkg_summary} +# For a breakdown of the licensing, see PACKAGE-LICENSING +License: GPLv2+ and GPLv2 and GPL +URL: https://github.com/rpm-software-management/dnf +Source0: %{url}/archive/%{version}/%{name}-%{version}.tar.gz +# https://github.com/rpm-software-management/dnf/commit/f6d1e4308769efaa6175f70d52bfd784c62fbf98 +Patch1: 0001-tests-SQL-write-a-readonly-folder.patch +# https://github.com/rpm-software-management/dnf/commit/c2e4901cec947e5be2e5ff5afa22691841d00bdc +Patch2: 0002-Revert-Fix-setopt-cachedir-writing-outside-of-installroot.patch +# https://github.com/rpm-software-management/dnf/pull/1675 +Patch3: 0003-Post-transaction-summary-is-logged-for-API-users-RhBug-1855158.patch +# https://github.com/rpm-software-management/dnf/pull/1698 +Patch4: 0004-Log-scriptlets-output-also-for-API-users-RhBug-1847340.patch +# https://github.com/rpm-software-management/dnf/pull/1659 +# https://github.com/rpm-software-management/dnf/pull/1689 +# https://github.com/rpm-software-management/dnf/pull/1709 +# https://github.com/rpm-software-management/dnf/pull/1690 +Patch5: 0005-dnf-history-operations-that-work-with-comps-correctly.patch +# https://github.com/rpm-software-management/dnf/pull/1691 +Patch6: 0006-Remove-sourcepackages-from-install-upgrade-set.patch +# https://github.com/rpm-software-management/dnf/pull/1710 +Patch7: 0007-Fix-documentation-of-globs-not-supporting-curly-brackets.patch +# https://github.com/rpm-software-management/dnf/pull/1685 +Patch8: 0008-Module-switch-command.patch +# https://github.com/rpm-software-management/dnf/pull/1702 +Patch9: 0009-yum.misc.decompress-to-handle-uncompressed-files-RhBug-1895059.patch +# https://github.com/rpm-software-management/dnf/pull/1693 +Patch10: 0010-Make-log-rotated-permissions-match-initial-log-permissions-RhBug-1894344.patch +# https://github.com/rpm-software-management/dnf/pull/1692 +Patch11: 0011-Add-new-attribute-for-Package--from-repo.patch +# https://github.com/rpm-software-management/dnf/pull/1695 +Patch12: 0012-Change-behaviour-of-Package-.from-repo.patch +# https://github.com/rpm-software-management/dnf/pull/1686 +Patch13: 0013-Package-add-a-get-header--method.patch +# https://github.com/rpm-software-management/dnf/pull/1703 +Patch14: 0014-Add-api-function-fill-sack-from-repos-in-cache-RhBug-1865803.patch +# https://github.com/rpm-software-management/dnf/pull/1711 +Patch15: 0015-Add-tests-and-docs-for-fill-sack-from-repos-in-cache-RhBug-1865803.patch +# https://github.com/rpm-software-management/dnf/pull/1721 +Patch16: 0016-Run-tests-for-fill-sack-from-repos-in-cache-in-installroot..patch +#https://github.com/rpm-software-management/dnf/pull/1723 +Patch17: 0017-Set-persistdir-for-fill-sack-from-repos-in-cache-tests-RhBug-1865803.patch +# https://github.com/rpm-software-management/dnf/pull/1725 +Patch18: 0018-Allow-stream-switching-if-option-enabled.patch + +BuildArch: noarch +BuildRequires: cmake +BuildRequires: gettext +# Documentation +BuildRequires: systemd +BuildRequires: bash-completion +%if %{with python3} +BuildRequires: %{_bindir}/sphinx-build-3 +Requires: python3-%{name} = %{version}-%{release} +%else +BuildRequires: %{_bindir}/sphinx-build +Requires: python2-%{name} = %{version}-%{release} +%endif +%if 0%{?rhel} && 0%{?rhel} <= 7 +Requires: python-dbus +Requires: %{_bindir}/sqlite3 +%else +%if %{with python3} +Recommends: (python3-dbus if NetworkManager) +%else +Recommends: (python2-dbus if NetworkManager) +%endif +Recommends: (%{_bindir}/sqlite3 if bash-completion) +%endif +Provides: dnf-command(alias) +Provides: dnf-command(autoremove) +Provides: dnf-command(check-update) +Provides: dnf-command(clean) +Provides: dnf-command(distro-sync) +Provides: dnf-command(downgrade) +Provides: dnf-command(group) +Provides: dnf-command(history) +Provides: dnf-command(info) +Provides: dnf-command(install) +Provides: dnf-command(list) +Provides: dnf-command(makecache) +Provides: dnf-command(mark) +Provides: dnf-command(provides) +Provides: dnf-command(reinstall) +Provides: dnf-command(remove) +Provides: dnf-command(repolist) +Provides: dnf-command(repoquery) +Provides: dnf-command(repository-packages) +Provides: dnf-command(search) +Provides: dnf-command(updateinfo) +Provides: dnf-command(upgrade) +Provides: dnf-command(upgrade-to) +Conflicts: python2-dnf-plugins-core < %{conflicts_dnf_plugins_core_version} +Conflicts: python3-dnf-plugins-core < %{conflicts_dnf_plugins_core_version} +Conflicts: python2-dnf-plugins-extras-common < %{conflicts_dnf_plugins_extras_version} +Conflicts: python3-dnf-plugins-extras-common < %{conflicts_dnf_plugins_extras_version} + +%description +%{pkg_description} + +%package data +Summary: Common data and configuration files for DNF +Requires: libreport-filesystem +Obsoletes: %{name}-conf <= %{version}-%{release} +Provides: %{name}-conf = %{version}-%{release} + +%description data +Common data and configuration files for DNF + +%package -n %{yum_subpackage_name} +Requires: %{name} = %{version}-%{release} +Summary: %{pkg_summary} +%if 0%{?fedora} +%if 0%{?fedora} >= 31 +Provides: %{name}-yum = %{version}-%{release} +Obsoletes: %{name}-yum < 5 +%else +Conflicts: yum < 3.4.3-505 +%endif +%endif + +%description -n %{yum_subpackage_name} +%{pkg_description} + +%if %{with python2} +%package -n python2-%{name} +Summary: Python 2 interface to DNF +%{?python_provide:%python_provide python2-%{name}} +BuildRequires: python2-devel +BuildRequires: python2-hawkey >= %{hawkey_version} +BuildRequires: python2-libdnf >= %{hawkey_version} +BuildRequires: python2-libcomps >= %{libcomps_version} +BuildRequires: python2-libdnf +BuildRequires: python2-nose +BuildRequires: libmodulemd >= %{libmodulemd_version} +Requires: libmodulemd >= %{libmodulemd_version} +%if (0%{?rhel} && 0%{?rhel} <= 7) +BuildRequires: pygpgme +Requires: pygpgme +BuildRequires: python-enum34 +Requires: python-enum34 +%else +BuildRequires: python2-gpg +Requires: python2-gpg +BuildRequires: python2-enum34 +Requires: python2-enum34 +%endif +Requires: %{name}-data = %{version}-%{release} +%if 0%{?fedora} +Recommends: deltarpm +# required for DNSSEC main.gpgkey_dns_verification https://dnf.readthedocs.io/en/latest/conf_ref.html +Recommends: python2-unbound +%endif +Requires: python2-hawkey >= %{hawkey_version} +Requires: python2-libdnf >= %{hawkey_version} +Requires: python2-libcomps >= %{libcomps_version} +Requires: python2-libdnf +%if 0%{?rhel} && 0%{?rhel} <= 7 +BuildRequires: rpm-python >= %{rpm_version} +Requires: rpm-python >= %{rpm_version} +%else +BuildRequires: python2-rpm >= %{rpm_version} +Requires: python2-rpm >= %{rpm_version} +Recommends: rpm-plugin-systemd-inhibit +%endif +Conflicts: dnfdaemon < %{conflicts_dnfdaemon_version} + +%description -n python2-%{name} +Python 2 interface to DNF. +%endif +# ^ %%{with python2} + +%if %{with python3} +%package -n python3-%{name} +Summary: Python 3 interface to DNF +%{?python_provide:%python_provide python3-%{name}} +BuildRequires: python3-devel +BuildRequires: python3-hawkey >= %{hawkey_version} +BuildRequires: python3-libdnf >= %{hawkey_version} +BuildRequires: python3-libcomps >= %{libcomps_version} +BuildRequires: python3-libdnf +BuildRequires: libmodulemd >= %{libmodulemd_version} +Requires: libmodulemd >= %{libmodulemd_version} +BuildRequires: python3-nose +BuildRequires: python3-gpg +Requires: python3-gpg +Requires: %{name}-data = %{version}-%{release} +%if 0%{?fedora} +Recommends: deltarpm +%endif +Requires: python3-hawkey >= %{hawkey_version} +Requires: python3-libdnf >= %{hawkey_version} +Requires: python3-libcomps >= %{libcomps_version} +Requires: python3-libdnf +BuildRequires: python3-rpm >= %{rpm_version} +Requires: python3-rpm >= %{rpm_version} +# required for DNSSEC main.gpgkey_dns_verification https://dnf.readthedocs.io/en/latest/conf_ref.html +Recommends: python3-unbound +%if 0%{?rhel} && 0%{?rhel} <= 7 +Requires: rpm-plugin-systemd-inhibit +%else +Recommends: rpm-plugin-systemd-inhibit +%endif + +%description -n python3-%{name} +Python 3 interface to DNF. +%endif + +%package automatic +Summary: %{pkg_summary} - automated upgrades +BuildRequires: systemd +Requires: %{name} = %{version}-%{release} +%{?systemd_requires} + +%description automatic +Systemd units that can periodically download package upgrades and apply them. + + +%prep +%autosetup -p1 +mkdir build-py2 +mkdir build-py3 + + +%build +%if %{with python2} + pushd build-py2 + %cmake .. -DPYTHON_DESIRED:FILEPATH=%{__python2} -DDNF_VERSION=%{version} + %make_build + make doc-man + popd +%endif + +%if %{with python3} + pushd build-py3 + %cmake .. -DPYTHON_DESIRED:FILEPATH=%{__python3} -DDNF_VERSION=%{version} + %make_build + make doc-man + popd +%endif + + +%install +%if %{with python2} + pushd build-py2 + %make_install + popd +%endif + +%if %{with python3} + pushd build-py3 + %make_install + popd +%endif + +%find_lang %{name} +mkdir -p %{buildroot}%{confdir}/vars +mkdir -p %{buildroot}%{confdir}/aliases.d +mkdir -p %{buildroot}%{pluginconfpath}/ +mkdir -p %{buildroot}%{_sysconfdir}/%{name}/modules.d +mkdir -p %{buildroot}%{_sysconfdir}/%{name}/modules.defaults.d +%if %{with python2} +mkdir -p %{buildroot}%{py2pluginpath}/ +%endif +%if %{with python3} +mkdir -p %{buildroot}%{py3pluginpath}/__pycache__/ +%endif +mkdir -p %{buildroot}%{_localstatedir}/log/ +mkdir -p %{buildroot}%{_var}/cache/dnf/ +touch %{buildroot}%{_localstatedir}/log/%{name}.log +%if %{with python3} +ln -sr %{buildroot}%{_bindir}/dnf-3 %{buildroot}%{_bindir}/dnf +mv %{buildroot}%{_bindir}/dnf-automatic-3 %{buildroot}%{_bindir}/dnf-automatic +%else +ln -sr %{buildroot}%{_bindir}/dnf-2 %{buildroot}%{_bindir}/dnf +mv %{buildroot}%{_bindir}/dnf-automatic-2 %{buildroot}%{_bindir}/dnf-automatic +%endif +rm -vf %{buildroot}%{_bindir}/dnf-automatic-* + +# Strict conf distribution +%if 0%{?rhel} +mv -f %{buildroot}%{confdir}/%{name}-strict.conf %{buildroot}%{confdir}/%{name}.conf +%else +rm -vf %{buildroot}%{confdir}/%{name}-strict.conf +%endif + +# YUM compat layer +ln -sr %{buildroot}%{confdir}/%{name}.conf %{buildroot}%{_sysconfdir}/yum.conf +%if %{with python3} +ln -sr %{buildroot}%{_bindir}/dnf-3 %{buildroot}%{_bindir}/yum +%else +%if "%{yum_compat_level}" == "preview" +ln -sr %{buildroot}%{_bindir}/dnf-2 %{buildroot}%{_bindir}/yum4 +ln -sr %{buildroot}%{_mandir}/man8/dnf.8.gz %{buildroot}%{_mandir}/man8/yum4.8.gz +rm -f %{buildroot}%{_mandir}/man8/yum.8.gz +%else +ln -sr %{buildroot}%{_bindir}/dnf-2 %{buildroot}%{_bindir}/yum +%endif +%endif +%if "%{yum_compat_level}" == "full" +mkdir -p %{buildroot}%{_sysconfdir}/yum +ln -sr %{buildroot}%{pluginconfpath} %{buildroot}%{_sysconfdir}/yum/pluginconf.d +ln -sr %{buildroot}%{confdir}/protected.d %{buildroot}%{_sysconfdir}/yum/protected.d +ln -sr %{buildroot}%{confdir}/vars %{buildroot}%{_sysconfdir}/yum/vars +%endif + + +%check +%if %{with python2} + pushd build-py2 + ctest -VV + popd +%endif + +%if %{with python3} + pushd build-py3 + ctest -VV + popd +%endif + + +%post +%systemd_post dnf-makecache.timer + +%preun +%systemd_preun dnf-makecache.timer + +%postun +%systemd_postun_with_restart dnf-makecache.timer + + +%post automatic +%systemd_post dnf-automatic.timer +%systemd_post dnf-automatic-notifyonly.timer +%systemd_post dnf-automatic-download.timer +%systemd_post dnf-automatic-install.timer + +%preun automatic +%systemd_preun dnf-automatic.timer +%systemd_preun dnf-automatic-notifyonly.timer +%systemd_preun dnf-automatic-download.timer +%systemd_preun dnf-automatic-install.timer + +%postun automatic +%systemd_postun_with_restart dnf-automatic.timer +%systemd_postun_with_restart dnf-automatic-notifyonly.timer +%systemd_postun_with_restart dnf-automatic-download.timer +%systemd_postun_with_restart dnf-automatic-install.timer + + +%files -f %{name}.lang +%{_bindir}/%{name} +%if 0%{?rhel} && 0%{?rhel} <= 7 +%{_sysconfdir}/bash_completion.d/%{name} +%else +%dir %{_datadir}/bash-completion +%dir %{_datadir}/bash-completion/completions +%{_datadir}/bash-completion/completions/%{name} +%endif +%{_mandir}/man8/%{name}.8* +%{_mandir}/man8/yum2dnf.8* +%{_mandir}/man7/dnf.modularity.7* +%{_mandir}/man5/dnf-transaction-json.5* +%{_unitdir}/%{name}-makecache.service +%{_unitdir}/%{name}-makecache.timer +%{_var}/cache/%{name}/ + +%files data +%license COPYING PACKAGE-LICENSING +%doc AUTHORS README.rst +%dir %{confdir} +%dir %{confdir}/modules.d +%dir %{confdir}/modules.defaults.d +%dir %{pluginconfpath} +%dir %{confdir}/protected.d +%dir %{confdir}/vars +%dir %{confdir}/aliases.d +%exclude %{confdir}/aliases.d/zypper.conf +%config(noreplace) %{confdir}/%{name}.conf +%config(noreplace) %{confdir}/protected.d/%{name}.conf +%config(noreplace) %{_sysconfdir}/logrotate.d/%{name} +%ghost %attr(644,-,-) %{_localstatedir}/log/hawkey.log +%ghost %attr(644,-,-) %{_localstatedir}/log/%{name}.log +%ghost %attr(644,-,-) %{_localstatedir}/log/%{name}.librepo.log +%ghost %attr(644,-,-) %{_localstatedir}/log/%{name}.rpm.log +%ghost %attr(644,-,-) %{_localstatedir}/log/%{name}.plugin.log +%ghost %attr(755,-,-) %dir %{_sharedstatedir}/%{name} +%ghost %attr(644,-,-) %{_sharedstatedir}/%{name}/groups.json +%ghost %attr(755,-,-) %dir %{_sharedstatedir}/%{name}/yumdb +%ghost %attr(755,-,-) %dir %{_sharedstatedir}/%{name}/history +%{_mandir}/man5/%{name}.conf.5* +%{_tmpfilesdir}/%{name}.conf +%{_sysconfdir}/libreport/events.d/collect_dnf.conf + +%files -n %{yum_subpackage_name} +%if "%{yum_compat_level}" == "full" +%{_bindir}/yum +%{_sysconfdir}/yum.conf +%{_sysconfdir}/yum/pluginconf.d +%{_sysconfdir}/yum/protected.d +%{_sysconfdir}/yum/vars +%{_mandir}/man8/yum.8* +%{_mandir}/man5/yum.conf.5.* +%{_mandir}/man8/yum-shell.8* +%{_mandir}/man1/yum-aliases.1* +%config(noreplace) %{confdir}/protected.d/yum.conf +%else +%exclude %{_sysconfdir}/yum.conf +%exclude %{_sysconfdir}/yum/pluginconf.d +%exclude %{_sysconfdir}/yum/protected.d +%exclude %{_sysconfdir}/yum/vars +%exclude %{confdir}/protected.d/yum.conf +%exclude %{_mandir}/man5/yum.conf.5.* +%exclude %{_mandir}/man8/yum-shell.8* +%exclude %{_mandir}/man1/yum-aliases.1* +%endif + +%if "%{yum_compat_level}" == "minimal" +%{_bindir}/yum +%{_mandir}/man8/yum.8* +%endif + +%if "%{yum_compat_level}" == "preview" +%{_bindir}/yum4 +%{_mandir}/man8/yum4.8* +%exclude %{_mandir}/man8/yum.8* +%endif + +%if %{with python2} +%files -n python2-%{name} +%{_bindir}/%{name}-2 +%exclude %{python2_sitelib}/%{name}/automatic +%{python2_sitelib}/%{name}/ +%dir %{py2pluginpath} +%endif + +%if %{with python3} +%files -n python3-%{name} +%{_bindir}/%{name}-3 +%exclude %{python3_sitelib}/%{name}/automatic +%{python3_sitelib}/%{name}/ +%dir %{py3pluginpath} +%dir %{py3pluginpath}/__pycache__ +%endif + +%files automatic +%{_bindir}/%{name}-automatic +%config(noreplace) %{confdir}/automatic.conf +%{_mandir}/man8/%{name}-automatic.8* +%{_unitdir}/%{name}-automatic.service +%{_unitdir}/%{name}-automatic.timer +%{_unitdir}/%{name}-automatic-notifyonly.service +%{_unitdir}/%{name}-automatic-notifyonly.timer +%{_unitdir}/%{name}-automatic-download.service +%{_unitdir}/%{name}-automatic-download.timer +%{_unitdir}/%{name}-automatic-install.service +%{_unitdir}/%{name}-automatic-install.timer +%if %{with python3} +%{python3_sitelib}/%{name}/automatic/ +%else +%{python2_sitelib}/%{name}/automatic/ +%endif + +%changelog +* Thu Feb 11 2021 Nicola Sella - 4.4.2-10 +- Allow stream switching if option enabled + +* Tue Feb 09 2021 Nicola Sella - 4.4.2-9 +- Set persistdir for fill_sack_from_repos_in_cache tests (RhBug:1865803) + +* Mon Feb 08 2021 Nicola Sella - 4.4.2-8 +- Add api function: fill_sack_from_repos_in_cache (RhBug:1865803) +- Add tests and docs for fill_sack_from_repos_in_cache (RhBug:1865803) +- Run tests for fill_sack_from_repos_in_cache in installroot + +* Fri Feb 05 2021 Nicola Sella - 4.4.2-7 +- Make log rotated permissions match initial log permissions (RhBug:1894344) +- Add new attribute for Package - from_repo +- Change behaviour of Package().from_repo +- Package: add a get_header() method + +* Fri Jan 29 2021 Nicola Sella - 4.4.2-6 +- yum.misc.decompress() to handle uncompressed files (RhBug:1895059) +- Module switch command + +* Fri Jan 15 2021 Nicola Sella - 4.4.2-5 +- Fix patch for dnf history operations + +* Thu Jan 14 2021 Nicola Sella - 4.4.2-4 +- `dnf history` operations that work with comps correctly +- Remove sourcepackages from install/upgrade set +- Fix documentation of globs not supporting curly brackets + +* Thu Jan 07 2021 Nicola Sella - 4.4.2-3 +- Backport patches +- Log scriptlets output also for API users (RhBug:1847340) +- Post transaction summary is logged for API users (RhBug:1855158) + +* Wed Nov 11 2020 Nicola Sella - 4.4.2-2 +- Backport patch Revert "Fix --setopt=cachedir writing outside of installroot" + +* Tue Nov 10 2020 Nicola Sella - 4.4.2-1 +- Update to 4.4.2 +- spec: Fix building with new cmake macros (backport from downstream) +- Warn about key retrieval over http: +- Fix --setopt=cachedir writing outside of installroot +- Add vendor to dnf API (RhBug:1876561) +- Add allow_vendor_change option (RhBug:1788371) (RhBug:1788371) + +* Tue Jul 28 2020 Marek Blaha - 4.2.23-4 +- Update translations + +* Fri Jul 17 2020 Nicola Sella - 4.2.23-3 +- Add logfilelevel configuration (RhBug:1802074) +- [doc] Enhance repo variables documentation (RhBug:1848161,1848615) + +* Wed Jun 10 2020 Ales Matej - 4.2.23-2 +- Handle empty comps group name (RhBug:1826198) + +* Tue Jun 02 2020 Nicola Sella - 4.2.23-1 +- Update to 4.2.23 +- Fix behavior of `install-n` command +- Fix behavior of `localinstall` command +- Fix behavior of `autoremove-n` command +- Fix behavior of `remove-n` command +- Fix behavior of `repoquery-n` command +- Fix behavior of `list-updateinfo` and related aliases +- Refactor code in `repoinfo` to use opts.command correctly. +- Add myself to list of contributors +- Add updated to verbose output of updateinfo list (RhBug: 1801092) +- Fix a couple of missed grammatical errors in updateinfo docs. +- Add comment option (RhBug:1773679) +- Better wording of dnssec email parsing error. +- Print nicer DnssecErrors (RhBug:1813244) +- Add new API for handling gpg signatures (RhBug:1339617) +- Verify GPG signatures (RhBug:1793298) +- Fix a syntax typo +- Fix up Conflicts: on python-dnf-plugins-extras so it actually works +- [doc] Move yum-plugin-post-transaction-actions to dnf-plugins-core +- Remove args "--set-enabled", "--set-disabled" from DNF (RhBug:1727882) +- Search command is now alphabetical (RhBug:1811802) +- Fix downloading packages with full URL as their location +- repo: catch libdnf.error.Error in addition to RuntimeError in load() (RhBug:1788182) +- History tbl to max size when redirect to file (RhBug:1786335,1786316) + +* Mon Apr 06 2020 Ales Matej - 4.2.21-1 +- Update to 4.2.21 +- Running with tsflags=test doesn't update log files +- Allow disabling individual aliases config files (RhBug:1680566) +- List arguments: only first empty value is used (RhBug:1788154) +- Report missing profiles or default as broken module (RhBug:1790967) +- Format history table to use actual terminal width (RhBug:1786316) +- Handle custom exceptions from libdnf +- Fix _skipped_packages to return only skipped (RhBug:1774617) +- Add setter for tsi.reason +- Add new hook for commands: Run_resolved +- Clean also .yaml repository metadata +- Use WantedBy=timers.target for all dnf timers (RhBug:1798475) +- Fix completion helper if solv files not in roon cache (RhBug:1714376) +- Add bash completion for 'dnf module' (RhBug:1565614) +- Check command no longer reports missing %pre and %post deps (RhBug:1543449) +- Check if arguments can be encoded in 'utf-8' +- Fix crash with "dnf -d 6 repolist" (RhBug:1812682) +- Do not print the first empty line for repoinfo +- Redirect logger and repo download progress when --verbose +- Respect repo priority when listing packages (RhBug:1800342) +- Remove misleading green color from the "broken dependencies" lines (RhBug:1814192) +- [repoquery] Fix rich deps matching by using provide expansion from libdnf (RhBug:1534123) +- [repoquery] Do not protect running kernel for --unsafisfied (RhBug:1750745) +- [doc] Document the retries config option only works for packages (RhBug:1783041) +- [doc] repoquery --what* with multiple arguments (RhBug:1790262) +- [doc] Remove incorrect information about includepkgs (RhBug:1813460) +- [doc] Document that list and info commands respect repo priority +- [doc] Document color options + +* Tue Feb 18 2020 Ales Matej - 4.2.17-6 +- Sort packages in transaction output by nevra (RhBug:1773436) +- Add support of commandline packages by repoquery (RhBug:1784148) +- [doc] Document that the include option was removed (RhBug:1786072) +- New API function for setting loggers (RhBug:1788212) + +* Fri Jan 31 2020 Marek Blaha - 4.2.17-5 +- [translations] Update translations from zanata (RhBug:1754959) + +* Mon Jan 13 2020 Ales Matej - 4.2.17-4 +- Fix alias processing with '\' escaping (RhBug:1680482) +- [doc] Explain the backslash notation also near the example (RhBug:1680482) +- Better descriptions for infinite aliases recursion (RhBug:1680488) +- Improve help for 'dnf module' command (RhBug:1758447) +- Unify downgrade exit codes with upgrade (RhBug:1759847) +- Honor priority with check-update (RhBug:1769466) +- Add shell restriction with local packages (RhBug:1773483) +- Restore functionality of remove --oldinstallonly (RhBug:1774670) + +* Thu Dec 12 2019 Pavla Kratochvilova - 4.2.17-3 +- Do a substitution of variables in repo_id (RhBug:1748841) +- Respect order of config files in aliases.d (RhBug:1680489) +- [doc] Remove note about user-agent whitelist (RhBug:1777255) +- Fix detection of the latest module (RhBug:1781769) + +* Mon Nov 25 2019 Ales Matej - 4.2.17-2 +- Update to 4.2.17 +- Enable versionlock for check-update command (RhBug:1750620) +- Add error message when no active modules matched (RhBug:1696204) +- Log mirror failures as warning when repo load fails (RhBug:1713627) +- dnf-automatic: Change all systemd timers to a fixed time of day (RhBug:1754609) +- DNF can use config from the remote location (RhBug:1721091) +- [doc] update reference to plugin documentation (RhBug:1706386) +- [yum compatibility] Report all packages in repoinfo +- [doc] Add definition of active/inactive module stream +- repoquery: Add a switch to disable modular excludes +- Report more informative messages when no match for argument (RhBug:1709563) +- [doc] Add description of excludes in dnf +- Report more descriptive message when removed package is excluded +- Add module repoquery command +- Fix assumptions about ARMv8 and the way the rpm features work (RhBug:1691430) +- Add Requires information into module info commands +- Enhance inheritance of transaction reasons (RhBug:1672618,1769788) + +* Wed Nov 13 2019 Ales Matej - 4.2.16-1 +- Update to 4.2.16 +- Fix downloading local packages into destdir (RhBug:1727137) +- Report skipped packages with identical nevra only once (RhBug:1643109) +- Restore functionality of dnf remove --duplicates (RhBug:1674296) +- Improve API documentation +- Document NEVRA parsing in the man page +- Do not wrap output when no terminal (RhBug:1577889) +- Allow to ship alternative dnf.conf (RhBug:1752249) +- Don't check if repo is expired if it doesn't have loaded metadata (RhBug:1745170) +- Remove duplicate entries from "dnf search" output (RhBug:1742926) +- Set default value of repo name attribute to repo id (RhBug:1669711) +- Allow searching in disabled modules using "dnf module provides" (RhBug:1629667) +- Group install takes obsoletes into account (RhBug:1761137) +- Improve handling of vars +- Do not load metadata for repolist commands (RhBug:1697472,1713055,1728894) +- Fix messages for starting and failing scriptlets (RhBug:1724779) +- Don't show older install-only pkgs updates in updateinfo (RhBug:1649383,1728004) +- Add --ids option to the group command (RhBug:1706382) +- Add --with_cve and --with_bz options to the updateinfo command (RhBug:1750528) + +* Tue Oct 22 2019 Ales Matej - 4.2.11-1 +- Update to 4.2.11 +- Improve modularity documentation (RhBug:1730162,1730162,1730807,1734081) +- Fix detection whether system is running on battery (used by metadata caching timer) (RhBug:1498680) +- New repoquery queryformat: %{reason} +- Print rpm errors during test transaction (RhBug:1730348) +- Fix incorrectly marked profile and stream after failed rpm transaction check (RhBug:1719679) +- Show transaction errors inside dnf shell (RhBug:1743644) +- dnf-automatic now respects versionlock excludes (RhBug:1746562) +- [doc] Add user_agent and countme options +- [history] Don't store failed transactions as succeeded +- [history] Do not require root for informative commands +- [dnssec] Fix UnicodeWarning when using new rpm (RhBug:1699650) +- Apply excludes before modular excludes (RhBug:1709453) +- Improve help for command line arguments (RhBug:1659328) +- Add new modular API method ModuleBase.get_modules +- Mark features used by ansible, anaconda and subscription-manager as an API + +* Mon Oct 21 2019 Pavla Kratochvilova - 4.2.7-7 +- Prevent reinstalling modified packages with same NEVRA (RhBug:1728252,1644241,1760825) + +* Tue Sep 03 2019 Jaroslav Mracek - 4.2.7-6 +- Remove patch to not fail when installing modular RPMs without modular metadata + +* Fri Aug 30 2019 Pavla Kratochvilova - 4.2.7-5 +- Fix: --setopt and repo with dots (RhBug:1746349) + +* Wed Aug 14 2019 Pavla Kratochvilova - 4.2.7-4 +- Prevent printing empty Error Summary (RhBug:1690414) + +* Tue Aug 06 2019 Pavla Kratochvilova - 4.2.7-3 +- Update localizations from zanata (RhBug:1689982) +- Accept multiple specs in repoquery options (RhBug:1667898,1656801) +- Prevent switching modules in all cases (RhBug:1706215) +- Change synchronization of rpm transaction to swdb (RhBug:1737328) +- Print rpm error messages during transaction (RhBug:1677199) +- Report missing default profile as an error (RhBug:1669527,1724564) +- Describe a behavior when plugin is removed (RhBug:1700741) + +* Thu Jul 04 2019 Pavla Kratochvilova - 4.2.7-2 +- Add patch to not fail when installing modular RPMs without modular metadata + +* Tue Jun 11 2019 Pavla Kratochvilova - 4.2.7-1 +- Update to 4.2.7 +- Fix package reinstalls during yum module remove (RhBug:1700529) +- Fail when "-c" option is given nonexistent file (RhBug:1512457) +- Reuse empty lock file instead of stopping dnf (RhBug:1581824) +- Propagate comps 'default' value correctly (RhBug:1674562) +- Better search of provides in /(s)bin/ (RhBug:1657993) +- Add detection for armv7hcnl (RhBug:1691430) +- Fix group install/upgrade when group is not available (RhBug:1707624) +- Report not matching plugins when using --enableplugin/--disableplugin + (RhBug:1673289) (RhBug:1467304) +- Add support of modular FailSafe (RhBug:1623128) +- Replace logrotate with build-in log rotation for dnf.log and dnf.rpm.log + (RhBug:1702690) + +* Mon May 13 2019 Pavla Kratochvilova - 4.2.6-1 +- Update to 4.2.6 +- Use improved config parser that preserves order of data +- Follow RPM security policy for package verification +- Update modules regardless of installed profiles +- [conf] Use environment variables prefixed with DNF_VAR_ +- Allow adjustment of repo from --repofrompath (RhBug:1689591) +- Allow globs in setopt in repoid part +- Add command abbreviations (RhBug:1634232) +- Installroot now requires absolute path +- librepo: Turn on debug logging only if debuglevel is greater than 2 (RhBug:1355764,1580022) +- Document cachedir option (RhBug:1691365) +- Enhance documentation - API examples +- Enhance documentation of --whatdepends option (RhBug:1687070) +- Update documentation: implemented plugins; options; deprecated commands (RhBug:1670835,1673278) +- [doc] Add info of relation update_cache with fill_sack (RhBug:1658694) +- Rename man page from dnf.automatic to dnf-automatic to match command name +- Fix alias list command (RhBug:1666325) +- Fix behavior of ``--bz`` option when specifying more values +- Add protection of yum package (RhBug:1639363) +- Fix ``list --showduplicates`` (RhBug:1655605) +- Retain order of headers in search results (RhBug:1613860) +- Solve traceback with the "dnf install @module" (RhBug:1688823) +- Fix multilib obsoletes (RhBug:1672947) +- Do not remove group package if other packages depend on it +- Remove duplicates from "dnf list" and "dnf info" outputs +- Fix the installation of completion_helper.py +- Fix formatting of message about free space required +- Fix installation failiure when duplicit RPMs are specified (RhBug:1687286) +- Fix issues with terminal hangs when attempting bash completion (RhBug:1702854) +- Allow plugins to terminate dnf (RhBug:1701807) +- [provides] Enhanced detecting of file provides (RhBug:1702621) +- [provides] Sort the output packages alphabetically + +* Mon Apr 08 2019 Pavla Kratochvilova - 4.0.9.2-6 +- Backport patch to unify --help with man for module-spec (RhBug:1678689) + +* Thu Feb 14 2019 Jaroslav Mracek - 4.0.9.2-5 +- Backport patch to not allow direct module switch + +* Fri Feb 08 2019 Jaroslav Mracek - 4.0.9.2-4 +- Backport patch to add support for modular updateinfoxml data + +* Thu Feb 07 2019 Pavla Kratochvilova - 4.0.9.2-3 +- Backport patch: Fix minor problem with suggestion printed to terminal + +* Wed Feb 06 2019 Jaroslav Mracek - 4.0.9.2-2 +- Backport patches for: RHEL should use --best option by default for dnf / libdnf (RhBug:1670776) +- Add --nobest dnf option to revert the new default behavior from commandline + +* Fri Jan 04 2019 Jaroslav Mracek - 4.0.9.2-1 +- Print information about skipped packages after the transaction +- Sort reported skipped packages, force ignore_weak +- Allow to enable modules that break default modules (RhBug:1648839) + +* Mon Dec 17 2018 Daniel Mach - 4.0.9.1-1 +- Updated difference YUM vs. DNF for yum-updateonboot +- Added new command ``dnf alias [options] [list|add|delete] [...]`` to allow the user to + define and manage a list of aliases +- Enhanced documentation +- Unifying return codes for remove operations +- [transaction] Make transaction content available for commands +- Add hotfix packages to install pool (RhBug:1654738) +- Report group operation in transaction table +- [sack] Change algorithm to calculate rpmdb_version +- Add basic integration with %_pkgverify_level (RhBug:1614351) + +* Fri Nov 23 2018 Jaroslav Mracek - 4.0.9-1 +- Added dnf.repo.Repo.get_http_headers +- Added dnf.repo.Repo.set_http_headers +- Added dnf.repo.Repo.add_metadata_type_to_download +- Added dnf.repo.Repo.get_metadata_path +- Added dnf.repo.Repo.get_metadata_content +- Added --changelogs option for check-update command +- [module] Add information about active modules +- Hide messages created only for logging +- Enhanced --setopt option +- [module] Fix dnf remove @ +- [transaction] Make transaction content available for plugins + +* Mon Nov 19 2018 Jaroslav Mracek - 4.0.4-2 +- Backport patches for setting cachedir with --setopt + +* Mon Oct 15 2018 Jaroslav Mracek - 4.0.4-1 +- Update to 4.0.4 +- Add dnssec extension +- Set termforce to AUTO to automatically detect if stdout is terminal +- Repoquery command accepts --changelogs option (RhBug:1483458) +- Calculate sack version from all installed packages (RhBug:1624291) +- [module] Allow to enable module dependencies (RhBug:1622566) + +* Tue Sep 25 2018 Jaroslav Mracek - 3.6.1-1 +- [module] Improved module commands list, info +- [module] Reports error from module solver +- Fix: Error detected when calling 'RepoCB.fastestMirror' (RhBug:1628056) +- Preserve packages from other installed mod profiles (RhBug:1629841) +- [spec] Postpone conflict with yum to Fedora 30+ (RhBug:1600444) +- [cli] Install command recommends alternative packages (RhBug:1625586) +- [cli] Fix case insensitive hint (1628514) +- Fix installed profiles for module info (RhBug:1629689) +- Fix module provides not having consistent output (RhBug:1623866) +- Enhance label for transaction table (RhBug:1609919) +- Implement C_, the gettext function with a context (RhBug:1305340) +- Actually disambiguate some messages using C_ (RhBug:1305340) +- Restore 'strict' choice for group installs (#1461539) +- [repoquery] More strict queryformat parsing (RhBug:1631458) +- Redirect repo progress to std error (RhBug:1626011) +- Unify behavior of remove and module remove (RhBug:1629848) +- Change behavior of disabled module for module install (RhBug:1629711) +- Allow enablement on disabled plugin (RhBug:1614539) +- Resolves: rhbz#1622585 - [modularity] dnf should not be proposing distro-sync +- Resolves: rhbz#1614531 - dnf 3.2 does not depsolve correctly +- Bug 1564369 - don't show duplicate errors in dnf output +- Resolves: rhbz#1597257 - dnf should accept localinstall command, at least as an alias +- Resolves: rhbz#1613860 - dnf search behaviour is slightly confusing +- Resolves: rhbz#1625586 - Advise user about alternatives to the non-existing "python" package +- Resolves: rhbz#1614346 - dnf rollback doesn't work after install/downgrade/upgrade +- Resolves: rhbz#1612752 - platform-python should be used in completion_helper +- Resolves: rhbz#1618421 - dnf module install fails to find non-modular dependencies +- Bug 1629655 - not helpful/complete error message when specifying wrong stream or profile +- Bug 1629709 - disabled modules should be identified in the module listing +- Bug 1630761 - [usability] unable to determine if a stream with [d] is enabled or not +- Bug 1625270 - there is ???% [=== when baseurl is wrong +- Resolves: rhbz#1624056 - quoted baseurl is error out + +* Mon Sep 10 2018 Jaroslav Mracek - 3.5.1-1 +- [module] Fixed list and info subcommands (RhBug:1623388) (RhBug:1623535) + +* Fri Sep 07 2018 Jaroslav Mracek - 3.5.0-1 +- New implementation of modularity +- dnf makecache should not fail in red color if no enabled repos (RhBug:1622090) +- [modularity] dnf module profile command doesn't work (RhBug:1622580) +- [modularity] incorrect output in dnf module list (RhBug:1623398) +- [modularity] dnf could be smarter when installing what's already installed (RhBug:1622599) +- [modularity] dnf module install circular error on missing dependency (RhBug:1620233) +- not descriptive output in dnf verbose (RhBug:1612718) +- RFE: provide way to query all packages from module (RhBug:1569068) + +* Fri Aug 31 2018 Daniel Mach - 3.4.0-1 +- [history] Fix 'attempt to write a readonly database' error in addConsoleOutputLine(). +- [spec] Improve YUM v3 compat layer. +- [doc] document missing link from yum-rhn-plugin to dnf-plugin-spacewalk (RhBug:1580356) +- [doc] document difference between yum and dnf when listing packages (RhBug:1615834) +- [doc] document missing download functionality after transaction table is displayed (RhBug:1585140) +- [systemd] dnf-makecache.timer: move the ordering after network to .service +- [translations] Update translations from zanata. +- [cli] Fix 'already installed' message output. +- [module] change 'module_nsvp' to 'module_spec' +- [module] show module profiles without ', ...' +- [module] unify usability of RepoModuleDict.get_info*(); fix traceback +- [security] fix update count (RhBug:1585138) +- [cli] enable reposync to use --destdir (RhBug:1582152) +- [repo] Replace dnf.repo.Repo with libdnf implementation. +- [dnf] Limit DeprecationWarning to dnf.* modules only. + +* Mon Aug 13 2018 Daniel Mach - 3.3.0-1 +- [misc] Fallback to os.getuid() if /proc/self/loginuid can't be read (RhBug:1597005) +- [translations] Update translations from zanata. +- [doc] Update module documentation. +- [module] Fix `module provides` output. +- [module] Add `module reset` command. +- [module] Fix module disable command +- [repo] Improve error message on broken repo (RhBug:1595796) +- [doc] Enhance a command documentation (RhBug:1361617) +- [module] Automatically save module persistor in do_transaction(). +- [drpm] Fixed setting deltarpm_percentage=0 to switch drpm off +- [repo] Split base.download_packages into two functions +- [output] Use libdnf wrapper for smartcols +- [conf] Do not traceback on empty option (RhBug:1613577) + +* Tue Aug 07 2018 Daniel Mach - 3.2.0-1 +- [sack] Use module_platform_id option. +- [module] Switch module persistor to libdnf implementation. +- [module] Auto-enable module streams based on installed RPMs. +- [transaction] Fix: show packages from the current transaction. +- [conf] Convert any VectorString type to list. +- [module] Replace 'enabled' config option with 'state'. +- [install_specs] Do not exclude groups' packages +- [module] Use module sack filtering from libdnf +- [module] Many UX fixes. + +* Fri Jul 27 2018 Daniel Mach - 3.1.0-1 +- [module] Move 'hotfixes' conf option to libdnf and rename it to 'module_hotfixes'. +- [goal] Exclude @System repo packages from distro_sync. +- [conf] Setup configuration values using C++ bindings. +- [module] Drop module lock command. +- [crypto] Use handle from repo in dnf.crypto.retrieve(). +- [module] Assume a 'default' profile exists for all modules (RhBug:1568165) +- [base] Introduce easy installation of package, group and module specs. + +* Thu Jul 19 2018 Daniel Mach - 3.0.4-1 +- [transaction] Fix 'TransactionItem not found for key' error. +- [module] Allow removing module profile without specifying a stream. +- [module] Fix 'BaseCli' object has no attribute '_yumdb' error. +- [callback] Fix TransactionDisplay.PKG_ERASE redirect to a non-existing constant. +- [spec] Change yum compat package version to 4.0.version. +- [cache] Clean transaction temp files after successfull transaction +- [log] Log messages from libdnf logger +- [transaction] Add states to report rpm transaction progress +- [transaction] Cache TransactionItem during handling of RPM callback (RhBug:1599597) +- [systemd] dnf-makecache.timer: move to multi-user to fix loop + +* Thu Jul 12 2018 Martin Hatina - 3.0.3-1 +- Bug fix release + +* Fri Jun 29 2018 Jaroslav Mracek - 3.0.2-1 +- Update to 3.0.2-1 + +* Tue Jun 26 2018 Jaroslav Mracek - 3.0.1-1 +- Update to 3.0.1-1 +- Support of MODULES - new DNF command `module` +- Add attribute dnf.conf.Conf.proxy_auth_method +- New repoquery option `--depends` and `--whatdepends` +- Enhanced support of variables +- Enhanced documentation +- Resolves: rhbz#1565599 +- Resolves: rhbz#1508839 +- Resolves: rhbz#1506486 +- Resolves: rhbz#1506475 +- Resolves: rhbz#1505577 +- Resolves: rhbz#1505574 +- Resolves: rhbz#1505573 +- Resolves: rhbz#1480481 +- Resolves: rhbz#1496732 +- Resolves: rhbz#1497272 +- Resolves: rhbz#1488100 +- Resolves: rhbz#1488086 +- Resolves: rhbz#1488112 +- Resolves: rhbz#1488105 +- Resolves: rhbz#1488089 +- Resolves: rhbz#1488092 +- Resolves: rhbz#1486839 +- Resolves: rhbz#1486839 +- Resolves: rhbz#1486827 +- Resolves: rhbz#1486816 +- Resolves: rhbz#1565647 +- Resolves: rhbz#1583834 +- Resolves: rhbz#1576921 +- Resolves: rhbz#1270295 +- Resolves: rhbz#1361698 +- Resolves: rhbz#1369847 +- Resolves: rhbz#1368651 +- Resolves: rhbz#1563841 +- Resolves: rhbz#1387622 +- Resolves: rhbz#1575998 +- Resolves: rhbz#1577854 +- Resolves: rhbz#1387622 +- Resolves: rhbz#1542416 +- Resolves: rhbz#1542416 +- Resolves: rhbz#1496153 +- Resolves: rhbz#1568366 +- Resolves: rhbz#1539803 +- Resolves: rhbz#1552576 +- Resolves: rhbz#1545075 +- Resolves: rhbz#1544359 +- Resolves: rhbz#1547672 +- Resolves: rhbz#1537957 +- Resolves: rhbz#1542920 +- Resolves: rhbz#1507129 +- Resolves: rhbz#1512956 +- Resolves: rhbz#1512663 +- Resolves: rhbz#1247083 +- Resolves: rhbz#1247083 +- Resolves: rhbz#1247083 +- Resolves: rhbz#1519325 +- Resolves: rhbz#1492036 +- Resolves: rhbz#1391911 +- Resolves: rhbz#1391911 +- Resolves: rhbz#1479330 +- Resolves: rhbz#1505185 +- Resolves: rhbz#1305232 + +* Wed Oct 18 2017 Igor Gnatenko - 2.7.5-1 +- Improve performance for excludes and includes handling (RHBZ #1500361) +- Fixed problem of handling checksums for local repositories (RHBZ #1502106) +- Fix traceback when using dnf.Base.close() (RHBZ #1503575) + +* Mon Oct 16 2017 Jaroslav Mracek - 2.7.4-1 +- Update to 2.7.4-1 +- Enhanced performance for excludes and includes handling +- Solved memory leaks at time of closing of dnf.Base() +- Resolves: rhbz#1480979 - I thought it abnormal that dnf crashed. +- Resolves: rhbz#1461423 - Memory leak in python-dnf +- Resolves: rhbz#1499564 - dnf list installed crashes +- Resolves: rhbz#1499534 - dnf-2 is much slower than dnf-1 when handling groups +- Resolves: rhbz#1499623 - Mishandling stderr vs stdout (dnf search, dnf repoquery) + +* Fri Oct 06 2017 Igor Gnatenko - 2.7.3-1 +- Fix URL detection (RHBZ #1472847) +- Do not remove downloaded files with --destdir option (RHBZ #1498426) +- Fix handling of conditional packages in comps (RHBZ #1427144) + +* Mon Oct 02 2017 Jaroslav Mracek - 2.7.2-1 +- Update to 2.7.2-1 +- Added new option ``--comment=`` that adds a comment to transaction in history +- :meth:`dnf.Base.pre_configure_plugin` configure plugins by running their pre_configure() method +- Added pre_configure() methotd for plugins and commands to configure dnf before repos are loaded +- Resolves: rhbz#1421478 - dnf repository-packages: error: unrecognized arguments: -x rust-rpm-macros +- Resolves: rhbz#1491560 - 'dnf check' reports spurious "has missing requires of" errors +- Resolves: rhbz#1465292 - DNF remove protected duplicate package +- Resolves: rhbz#1279001 - [RFE] Missing dnf --downloaddir option +- Resolves: rhbz#1212341 - [RFE] Allow plugins to override the core configuration +- Resolves: rhbz#1299482 - mock --init fails with message "Failed calculating RPMDB checksum" +- Resolves: rhbz#1488398 - dnf upstream tests failures on f26 +- Resolves: rhbz#1192811 - dnf whatprovides should show which provides matched a pattern +- Resolves: rhbz#1288845 - "dnf provides" wildcard matching is unreliable (not all packages with matches listed) +- Resolves: rhbz#1473933 - [abrt] dnf-automatic: resolved(): rpm_conf.py:58:resolved:AttributeError: 'Rpmconf' object has no attribute '_interactive' +- Resolves: rhbz#1237349 - dnf autoremove not removing what dnf list extras shows +- Resolves: rhbz#1470050 - the 'priority=' option in /etc/yum.repos.d/*.repo is not respected +- Resolves: rhbz#1347927 - dnf --cacheonly downloads packages +- Resolves: rhbz#1478115 - [abrt] dnf: _hcmd_undo(): __init__.py:888:_hcmd_undo:IndexError: list index out of range +- Resolves: rhbz#1461171 - RFE: support --advisory= with install +- Resolves: rhbz#1448874 - "dnf needs-restarting" vanished from bash completion +- Resolves: rhbz#1495116 - Dnf version fails with traceback in container + +* Mon Aug 07 2017 Jaroslav Mracek 2.6.3-1 +- Fix problem with dnf.Package().remote_location() (RhBug:1476215) (Jaroslav + Mracek) +- Change behavior of -C according to documentation (RhBug:1473964) (Jaroslav + Mracek) +- It should prevent to ask attribute of None (RhBug:1359482) (Jaroslav Mracek) +- Solve a problems with --arch options (RhBug:1476834) (Jaroslav Mracek) +- Use security plugin code for dnf-automatic (Jaroslav Mracek) +- Fix unicode error for python2 (Jaroslav Mracek) +- Inform about packages installed for group (Jaroslav Mracek) +- Provide info if pkg is removed due to dependency (RhBug:1244755) (Jaroslav + Mracek) +- Unify format of %%{_mandir} paths in dnf.spec (Jaroslav Mracek) +- Remove test_yumlayer.py as unneeded test (Jaroslav Mracek) +- Provide yum4 package for rhel7 build (Jaroslav Mracek) +- Make yum compatible layer very minimal (RhBug:1476748) (Jaroslav Mracek) +- Remove metadata_expire from yum compatible layer (Jaroslav Mracek) +- Remove keepcache from yum compatibility layer (Jaroslav Mracek) +- Remove options from yum conf (Jaroslav Mracek) +- Remove unused functionality from yum compatible layer (Jaroslav Mracek) +- Add deplist command for dnf (Jaroslav Mracek) +- Fix problems with --downloaddir options (RhBug:1476464) (Jaroslav Mracek) +- Move description of --forcearch into proper place (Jaroslav Mracek) +- Provide description of --downloaddir option (Jaroslav Mracek) +- Fix if in spec file (Jaroslav Mracek) +- Add description of "test" tsflags (Jaroslav Mracek) +- Enable import gpg_keys with tsflag test (RhBug:1464192) (Jaroslav Mracek) +- Keep old reason when undoing erase (RhBug:1463107) (Eduard Čuba) +- spec: eliminate other weak dependencies for el<=7 (Igor Gnatenko) +- spec: do not strongly require inhibit plugin (Igor Gnatenko) +- Inform that packages are only downloaded (RhBug:1426196) (Jaroslav Mracek) +- Move releasever check after the etc/dnf/vars substitutions. (Alexander + Kanavin) +- Provide substitution for Repodict.add_new_repo() (RhBug:1457507) (Jaroslav + Mracek) + +* Mon Jul 24 2017 Jaroslav Mracek 2.6.2-1 +- Remove autodeglob optimization (Jaroslav Rohel) +- Integrate --destdir with --destdir from download plugin (Ondřej Sojka) +- Add CLI option --destdir (RhBug:1279001) (Ondřej Sojka) +- Add myself to the AUTHORS file (Nathaniel McCallum) +- Add the --forcearch CLI flag (Nathaniel McCallum) +- Add 'ignorearch' option (Nathaniel McCallum) +- Provide an API for setting 'arch' and 'basearch' (Nathaniel McCallum) +- Add nevra forms for repoquery command (Jaroslav Rohel) +- Fix UnicodeDecodeError during checkSig() on non UTF-8 locale (RhBug:1397848) + (Jaroslav Rohel) +- Add dnf option --noautoremove (RhBug:1361424) (Jaroslav Mracek) +- Add group argument for mark command (Jaroslav Mracek) +- Report problems for each pkg during gpgcheck (RhBug:1387925) (Jaroslav + Mracek) +- fix minor spelling mistakes (René Genz) +- Print warning when wrong delimiter in cache (RhBug:1332099) (Vítek Hoch) +- Fix the loading of config for dnf-automatic command_email (RhBug:1470116) + (Jaroslav Rohel) +- Enable download progress bar if redirected output (RhBug:1161950) (Jaroslav + Mracek) +- Support short abbrevations of commands (RhBug:1320254) (Vítek Hoch) +- Remove unused variables kwargs (Jaroslav Mracek) +- Not reinstall packages if install from repository-pkgs used (Jaroslav Mracek) +- bump dnf version to 2.6.0 (Igor Gnatenko) +- spec: use python2- prefix for hawkey (Igor Gnatenko) +- spec: use sphinx-build binary rather than package name (Igor Gnatenko) +- spec: python-bugzilla is not needed for building (Igor Gnatenko) +- spec: fix instructions about generating tarball (Igor Gnatenko) +- po: Update translations (Igor Gnatenko) +- Add an example of installation without weak-deps (RhBug:1424723) (Jaroslav + Mracek) +- Add detection if mirrorlist is used for metalink (Jaroslav Mracek) +- Rename variable (Jaroslav Mracek) +- Add --groupmember option to repoquery (RhBug:1462486) (Jaroslav Mracek) +- Check checksum for local repositories (RhBug:1314405) (Jaroslav Mracek) +- Spelling fixes (Ville Skyttä) +- repoquery --obsoletes prints obsoletes (RhBug:1457368) (Matěj Cepl) +- Provide pkg name hint for icase (RhBug:1339280) (RhBug:1138978) (Jaroslav + Mracek) +- Return only latest pkgs for "dnf list upgrades" (RhBug:1423472) (Jaroslav + Mracek) +- cleanup code not executed in case of exception (Marek Blaha) +- Allow to modify message for user confirmation (Jaroslav Mracek) +- Add autocheck_running_kernel config option (Štěpán Smetana) +- Inform about skipped packages for group install (RhBug:1427365) (Jaroslav + Mracek) +- Remove group remove unneeded pkgs (RhBug:1398871) (RhBug:1432312) (Jaroslav + Mracek) +- po: update translations (Igor Gnatenko) + +* Mon Jun 12 2017 Jaroslav Mracek 2.5.1-1 +- bump version to 2.5.1 + update release notes (Jaroslav Mracek) +- Fix: dnf update --refresh fails for repo_gpgcheck=1 (RhBug:1456419) (Daniel + Mach) +- Don't try to cut datetime message (Jaroslav Rohel) +- Use localized datetime format (RhBug:1445021) (Jaroslav Rohel) +- Work with locale date (Jaroslav Rohel) +- Use ISO 8601 time format in logfile (Jaroslav Rohel) +- Add unitest to prevent callbacks breakage (Jaroslav Mracek) +- Provide compatibility for tools that do not use total_drpms (Jaroslav Mracek) +- Requires strict usage of repoquery --recursive (Jaroslav Mracek) +- Fix output for --resolve with --installed for repoquery (Jaroslav Mracek) +- Remove unnecessary inheritance of yum conf options (Martin Hatina) +- Remove alwaysprompt option support (RhBug:1400714) (Jaroslav Rohel) +- Allow to install groups with multilib_policy=all (RhBug:1250702) (Jaroslav + Mracek) +- Redesign Base.install() to provide alternatives (Jaroslav Mracek) +- Report excludes includes into logger.debug (RhBug:1381988) (Jaroslav Mracek) +- Provide new API to parse string to NEVRA () (Jaroslav Mracek) +- Add more repoquery querytags (Jaroslav Rohel) +- Not hide tracebacks (Jaroslav Mracek) +- Solve error handling for get attr in yumdb (RhBug:1397848) (Jaroslav Mracek) +- Provide a better error if throttle to low (RhBug:1321407) (Jaroslav Mracek) +- Change timeout to 30s (RhBug:1291867) (Jaroslav Mracek) +- Add pre_transaction hook for plugins (Jaroslav Rohel) +- Not download metadata if "dnf history [info|list|userinstalled]" (Jaroslav + Mracek) +- Not download metadata if "dnf repo-pkgs list --installed" (Jaroslav + Mracek) +- Not download metadata if "dnf list --installed" (RhBug:1372895) (Jaroslav + Mracek) +- Format pkg str for repoquery --tree due to -qf (RhBug:1444751) (Jaroslav + Mracek) + +* Mon May 22 2017 Jaroslav Mracek 2.5.0-1 +- Update release notes (Jaroslav Mracek) +- Change documentation for history --userinstalled (RhBug:1370062) (Jaroslav + Mracek) +- Change example to install plugin using versionlock (Jaroslav Mracek) +- Remove unused method Goal.best_run_diff() (Jaroslav Mracek) +- Change recommendations if some problems appear (RhBug:1293067) (Jaroslav + Mracek) +- Report problems for goals with optional=True (Jaroslav Mracek) +- Format resolve problem messages in method in dnf.util (Jaroslav Mracek) +- Enhance reports about broken dep (RhBug:1398040)(RhBug:1393814) (Jaroslav + Mracek) +- search: do not generate error if not match anything (RhBug:1342157) (Jaroslav + Rohel) +- Check if any plugin is removed in transaction (RhBug:1379906) (Jaroslav + Mracek) +- Show progress for DRPM (RhBug:1198975) (Jaroslav Mracek) +- Fix disabledplugin option (Iavael) +- [history]: fixed info command merged output (Eduard Čuba) + +* Thu May 11 2017 Jaroslav Mracek 2.4.1-1 +- bump version to 2.4.1 + update release notes (Jaroslav Mracek) +- goal: do not mark weak dependencies as userinstalled (Igor Gnatenko) +- fix typo in supplements (RhBug:1446756) (Igor Gnatenko) +- Describe present behavior of installonly_limit conf option (Jaroslav Mracek) +- Reset all transaction for groups if Base.reset() (RhBug:1446432) (Jaroslav + Mracek) +- Explain how add negative num for --latest-limit (RhBug:1446641) (Jaroslav + Mracek) +- trivial: don't duplicate option names (Igor Gnatenko) +- Add support for --userinstalled for repoquery command (RhBug:1278124) + (Jaroslav Rohel) +- Fix header of search result sections (RhBug:1301868) (Jaroslav Rohel) +- Filter out src for get_best_selector (Jaroslav Mracek) +- Add minor changes in formating of documentation (Jaroslav Mracek) + +* Tue May 02 2017 Jaroslav Mracek 2.4.0-1 +- po: Update translations (Igor Gnatenko) +- po: Update translations (Igor Gnatenko) +- introduce '--enableplugin' option (Martin Hatina) +- Improve detection of file patterns (Jaroslav Mracek) +- Add method _get_nevra_solution() for subject (Jaroslav Mracek) +- Do not add "*" into query filter in _nevra_to_filters() (Jaroslav Mracek) +- Remove usage of nevra_possibilities_real() (Jaroslav Mracek) +- Increase performance for downgrade_to() (Jaroslav Mracek) +- Add additional keys for get_best_query() (Jaroslav Mracek) +- Increase performance for get_best_selector() (Jaroslav Mracek) +- Increase performance for get_best_query() (Jaroslav Mracek) +- Fix "Package" text translation (RhBug:1302935) (Jaroslav Rohel) +- Create a warning if releasever is None (Jaroslav Mracek) +- Adds cost, excludepkgs, and includepkgs to Doc (RhBug:1248684) (Jaroslav + Mracek) +- Change auto-detection of releasever in empty installroot (Jaroslav Mracek) +- Do not load system repo for makecache command (RhBug:1441636) (Jaroslav + Mracek) +- Do not raise assertion if group inst and rmv pkgs (RhBug:1438438) (Jaroslav + Mracek) +- yum layer using python3 (Martin Hatina) +- Filter url protocols for baseurl in Package.remote_location (Jaroslav Mracek) +- Add armv5tl to arm basearch (Neal Gompa) +- Setup additional parameters for handler for remote packages (Jaroslav Mracek) +- Use same method for user/password setting of every librepo.handle (Jaroslav + Mracek) +- Fix PEP8 violations and remove unused import (Jaroslav Mracek) +- Handle unknown file size in download progress (Jaroslav Mracek) +- Allow to delete cashed files from command line by clean command (Jaroslav + Mracek) +- Save command line packages into chachedir (RhBug:1256313) (Jaroslav Mracek) +- Add progress bar for download of commandline pkgs (RhBug:1161950) (Jaroslav + Mracek) +- Fix minor typo Closes: #781 Approved by: ignatenkobrain (Yuri Chornoivan) +- Mark unremoved packages as failed (RhBug:1421244) (Jaroslav Mracek) + +* Mon Apr 10 2017 Jaroslav Mracek 2.3.0-1 +- update release notes (Jaroslav Mracek) +- po: Update translations (Igor Gnatenko) +- Add require of subcommand for repo-pkgs command (Jaroslav Rohel) +- shell: Fix commands initialization (Jaroslav Rohel) +- po: Update translations (Igor Gnatenko) +- Add support for --location for repoquery command (RhBug:1290137) (Jaroslav + Mracek) +- Add support of --recursive with --resolve in repoquery (Jaroslav Mracek) +- Add --recursive option for repoquery (Jaroslav Mracek) +- Add --whatconflicts for repoquery (Jaroslav Mracek) +- Add support for multiple options for repoquery (Jaroslav Mracek) +- Add multiple format option for repoquery (Jaroslav Mracek) +- Fix problem with "dnf repoquery --querytags" (Jaroslav Mracek) +- Add support of 3 options into updateinfo command (Jaroslav Mracek) +- Add inheritance of reason for obsoleting packages (Jaroslav Mracek) +- Mark installonlypkgs correctly as user installed (RhBug:1349314) (Jaroslav + Mracek) +- Solve a problem with None names in callbacks (Jaroslav Mracek) +- Solve a problem for callbacks (Jaroslav Mracek) +- Revert "remove: CLI: --randomwait" (RhBug:1247122) (Ondřej Sojka) +- po: update translations (Igor Gnatenko) +- po: update translations (Igor Gnatenko) +- Set strings for translations (RhBug:1298717) (Jaroslav Mracek) + +* Mon Mar 27 2017 Jaroslav Mracek 2.2.0-1 +- bump version to 2.2.0 + update release notes (Jaroslav Mracek) +- Add documentation of new API callback actions (RhBug:1411432) (Jaroslav + Mracek) +- Fix python2 doesn't have e.__traceback__ attribute (Jaroslav Mracek) +- Do not report erasing package as None. (Jaroslav Mracek) +- Display scriplet for transaction (RhBug:1411423) (RhBug:1406130) (Jaroslav + Mracek) +- Add support for rpmcallbacks (Jaroslav Mracek) +- AUTHORS: updated (Jaroslav Rohel) +- Not show expiration check if no repo enabled (RhBug:1369212) (Jaroslav + Mracek) +- Fix changelog in dnf spec file (Jaroslav Mracek) +- po: update translations (Igor Gnatenko) +- Add myself (mhatina) to AUTHORS (Martin Hatina) +- po: Update translations (Igor Gnatenko) + +* Tue Mar 21 2017 Jaroslav Mracek 2.1.1-1 +- bump version to 2.1.1 + update release notes (Jaroslav Mracek) +- Sync the translation with locale (Jaroslav Rohel) +- Disable exceptions in logging (Jaroslav Rohel) +- Fix severity info in "updateinfo info" (Jaroslav Mracek) +- Add help for shell commands (Jaroslav Rohel) +- shell: no crash if missing args (Jaroslav Rohel) +- proper check of releasever, when using installroot (RhBug:1417542) (Martin + Hatina) +- Inform about "Cache was expired" with "dnf clean" (RhBug:1401446) (Jaroslav + Mracek) +- crypto: port to the official gpgme bindings (Igor Gnatenko) +- Fix doc example for `fill_sack` method (Lubomír Sedlář) +- po: update translations (Igor Gnatenko) +- Not try to install src package (RhBug:1416699) (Jaroslav Mracek) +- Add usage for add_new_repo() with repofrompath option (Jaroslav Mracek) +- Add new API add_new_repo() in RepoDict() (RhBug:1427132) (Jaroslav Mracek) +- docs: adds documentation for dnf-automatic's Command and CommandEmail + emitters. (rhn) +- docs: fixes typo in section description in automatic (rhn) +- Adds new emitters for dnf-automatic. (rhn) +- po: update translations (Igor Gnatenko) +- Ensure that callback will not kill dnf transaction (Jaroslav Mracek) +- Ensure that name will be not requested on None (RhBug:1397047) (Jaroslav + Mracek) +- Python 3.6 invalid escape sequence deprecation fix (Ville Skyttä) +- display severity information in updateinfo (#741) (Michael Mraka) +- po: update translations (Igor Gnatenko) +- Add --nodocs option for dnf (RhBug:1379628) (Jaroslav Mracek) +- Replace passive plugin noroot (Jaroslav Mracek) +- Fix incorrect formating of string for logger.info (Jaroslav Mracek) +- Not print help if empty line in script for shell command (Jaroslav Mracek) +- Run fill_sack after all repos have changed status (Jaroslav Mracek) +- Remove Hawkey object from repo if rerun of dnf.fill_sack (Jaroslav Mracek) +- util/on_metered_connection: be more polite to failures (Igor Gnatenko) +- cosmetic: i18n: rewording of 'Login user' (RhBug:1424939) (Jan Silhan) +- Fix problem with --whatprovides in repoquery (RhBug:1396992) (Jaroslav + Mracek) +- Add -a and --all option for repoquery (RhBug:1412970) (Jaroslav Mracek) +- Change camel-case of output of grouplist (Jaroslav Mracek) +- Minor correction in release notes (Jaroslav Mracek) +- Minor correction in release notes (Jaroslav Mracek) + +* Thu Feb 16 2017 Jaroslav Mracek 2.1.0-1 +- bump version to 2.1.0 + update release notes (Jaroslav Mracek) +- Fix problem with --recent option in repoquery (Jaroslav Mracek) +- Fix problem with duplicated --obsoletes (RhBug:1421835) (Jaroslav Mracek) +- Python 3.6 invalid escape sequence deprecation fixes (Ville Skyttä) +- Add --repoid as alias for --repo (Jaroslav Mracek) +- introduce dnf.base.Base.update_cache() (Martin Hatina) +- Try to install uninstalled packages if group installed (Jaroslav Mracek) +- Enable search of provides in /usr/(s)bin (RgBug:1421618) (Jaroslav Mracek) +- style: ignore E261 (Igor Gnatenko) +- makecache: do not run on metered connections (RhBug:1415711) (Igor Gnatenko) +- change '--disableplugins' to '--disableplugin' (Martin Hatina) +- cosmetic: removed unused import (Jan Silhan) +- show hint how to display why package was skipped (RhBug:1417627) (Jan Silhan) +- spec: add information how to obtain archive (Igor Gnatenko) +- fix messages (UX) (Jaroslav Rohel) +- zanata update (Jan Silhan) + +* Thu Feb 09 2017 Jaroslav Mracek 2.0.1-1 +- bump version to 2.0.1 + update release notes (Jaroslav Mracek) +- introduce cli 'obsoletes' option (Martin Hatina) +- swap tids if they are in wrong order (RhBug:1409361) (Michael Mraka) +- Disable shell command recursion (Jaroslav Rohel) +- Honor additional arguments for DNF shell repo list command (Jaroslav Rohel) +- don't traceback when bug title is not set (Michael Mraka) +- introducing list-security, info-security etc. commands (Michael Mraka) +- Add lsedlar to contributors list (Lubomír Sedlář) +- Return just name from Package.source_name (Lubomír Sedlář) +- introduce dnf.conf.config.MainConf.exclude() (Martin Hatina) +- systemd: Disable daemons on ostree-managed systems (Colin Walters) +- introduced dnf.base.Base.autoremove() (RhBug:1414512) (Martin Hatina) +- po: update translations (Igor Gnatenko) +- build: use relative directory for translations (Igor Gnatenko) +- Temporary eliminate a problem with install remove loop (Jaroslav Mracek) +- Handle info message when DRPM wastes data (RhBug:1238808) (Daniel + Aleksandersen) +- Fix output for better translation (RhBug:1386085) (Abhijeet Kasurde) +- yum layer refactored (Martin Hatina) +- return values changed to match yum's (Martin Hatina) +- Reword sentence after removing package (RhBug:1286553) (Abhijeet Kasurde) +- Minor documentation revisions (Mark Szymanski) +- Minor code fix (Abhijeet Kasurde) +- automatic: email emitter date header (David Greenhouse) +- Solve problem when no repo and only rpms with upgrade command (Jaroslav + Mracek) +- bash_completion: use system-python if it's available (Igor Gnatenko) +- spec: use system-python for dnf-yum as well (Igor Gnatenko) +- comps/groups: fix tests (Michal Luscon) +- comps: adjust group_upgrade to new CompsTransPkg style (Michal Luscon) +- groups: refactored installation (RhBug:1337731, RhBug:1336879) (Michal + Luscon) +- Increase requirement for hawkey (Jaroslav Mracek) +- Change reporting problems for downgradePkgs() (Jaroslav Mracek) +- Use selector for base.package_upgrade() (Jaroslav Mracek) +- Add usage of selectors for base.package_install() (Jaroslav Mracek) +- Use selector for base.package_downgrade() (Jaroslav Mracek) +- Redirect base.downgrade() to base.downgrade_to() (Jaroslav Mracek) +- Enable wildcard for downgrade command (RhBug:1173349) (Jaroslav Mracek) +- Refactor downgrade cmd behavior (RhBug:1329617)(RhBug:1283255) (Jaroslav + Mracek) +- Redirect logger.info into stderr for repolist (RhBug:1369411) (Jaroslav + Mracek) +- Redirect logger.info into stderr for repoquery (RhBug:1243393) (Jaroslav + Mracek) +- Add possibility for commands to redirect logger (Jaroslav Mracek) +- Put information about metadata expiration into stdout (Jaroslav Mracek) +- Change warning about added repo into info (RhBug:1243393) (Jaroslav Mracek) +- Move grouplist output from logger into stdout (Jaroslav Mracek) +- let repo exclude work the same way as global exclude (Michael Mraka) +- Fix wrong assumptions about metalinks (RhBug:1411349) (Jaroslav Mracek) +- handle --disablerepo/--enablerepo properly with strict (RhBug:1345976) + (Štěpán Smetana) +- Add fix to notify user about no repos (RhBug:1369212) (Abhijeet Kasurde) +- Add information about "hidden" option in dnf doc (RhBug:1349247) (Abhijeet + Kasurde) +- Fix for 'history package-list' (Amit Upadhye) +- Enable multiple args for repoquery -f (RhBug:1403930) (Jaroslav Mracek) +- Set default repo.name as repo._section (Jaroslav Mracek) +- Create from self.forms value forms in cmd.run() (Jaroslav Mracek) +- Add description of swap command into documentation (Jaroslav Mracek) +- Add swap command (RhBug:1403465) (RhBug:1110780) (Jaroslav Mracek) +- Solve a problem with shell when empty line or EOF (Jaroslav Mracek) +- shell: add history of commands (RhBug:1405333) (Michal Luscon) +- Add info if no files with repoquery -l (RhBug:1254879) (Jaroslav Mracek) +- po: update translations (Igor Gnatenko) +- po: migrate to zanata python client and trivial fixes in build (Igor + Gnatenko) +- po: include all possible languages from zanata (Igor Gnatenko) +- po: include comments for translations (Igor Gnatenko) +- shell: catch exceptions from depsolving (Michal Luscon) +- shell: update documentation (Michal Luscon) +- shell: add transaction reset cmd (Michal Luscon) +- shell: add transaction resolve cmd (Michal Luscon) +- shell: provide rewritable demands for cmds (Michal Luscon) +- shell: catch tracebacks from shlex (Michal Luscon) +- shell: handle ctrl+D more gracefully (Michal Luscon) +- groups: set demands in configure instead of run (Michal Luscon) +- shell: implement config cmd (Michal Luscon) +- shell: add help (Michal Luscon) +- shell: make alias repo list -> repolist (Michal Luscon) +- shell: catch exceptions from do_transaction (Michal Luscon) +- shell: resolve transaction in ts run (Michal Luscon) +- shell: add default value for internal methods argument (Michal Luscon) +- shell: create run alias for ts run (Michal Luscon) +- shell: add ts list cmd (Michal Luscon) +- shell: refill sack after every successful repo cmd (Michal Luscon) +- shell: allow running multiple transaction in one session (Michal Luscon) +- shell: add ts command (Michal Luscon) +- shell: catch cmd parsing and run exceptions (Michal Luscon) +- shell: allow to run scripts (Michal Luscon) +- shell: add repo cmd (Michal Luscon) +- shell: add resolving + transaction run support (Michal Luscon) +- shell: implement quit method (Michal Luscon) +- shell: add custom cmds stubs (Michal Luscon) +- shell: implement basic logic (Michal Luscon) +- shell: register new cmd (Michal Luscon) + +* Wed Dec 14 2016 Michal Luscon 2.0.0-1 +- tests: catch ModuleNotFoundError as well (Igor Gnatenko) +- Switch out automatic service for automatic-download and automatic-install + (Pat Riehecky) +- Make upgrade-to alias for upgrade (RhBug:1327999) (Jaroslav Mracek) +- skip appending an empty option (RhBug: 1400081) (Michael Mraka) +- Add description of nevra foems for commands and autoremove args (Jaroslav + Mracek) +- Add support of arguments nevra forms for autoremove command (Jaroslav Mracek) +- Add nevra forms for remove command (Jaroslav Mracek) +- Add nevra forms for install command (Jaroslav Mracek) +- add bin/yum into .gitignore (Michal Luscon) +- clean: acquire all locks before cleaning (RhBug:1293782) (Michal Luscon) +- Change hawkey version requirement (Jaroslav Mracek) +- Add information for translators (RhBug:1386078) (Jaroslav Mracek) +- Change info to warning for clean repoquery output (RhBug:1358245) (Jaroslav + Mracek) +- Add description of pkg flag for Query (RhBug:1243393) (Jaroslav Mracek) +- Add minor changes in documentation (Jaroslav Mracek) +- Do not always overwrite the name with the repo ID (Neal Gompa) + +* Fri Dec 02 2016 Martin Hatina 2.0.0-0.rc2.1 +- See http://dnf.readthedocs.io/en/latest/release_notes.html + +* Thu Sep 29 2016 Michal Luscon 2.0.0-0.rc1.1 +- See http://dnf.readthedocs.io/en/latest/release_notes.html + +* Thu Sep 08 2016 Igor Gnatenko - 1.1.10-2 +- Obsolete dnf-langpacks +- Backport patch for dnf repolist disabled + +* Thu Aug 18 2016 Igor Gnatenko - 1.1.10-1 +- Update to 1.1.10 + +* Tue Aug 09 2016 Igor Gnatenko - 1.1.9-6 +- Fix typo + +* Tue Aug 09 2016 Igor Gnatenko - 1.1.9-5 +- Also change shebang for %%{?system_python_abi} in %%{_bindir}/dnf + +* Tue Aug 09 2016 Igor Gnatenko - 1.1.9-4 +- Add %%{?system_python_abi} + +* Tue Jul 19 2016 Fedora Release Engineering - 1.1.9-3 +- https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages + +* Tue May 24 2016 Michal Luscon 1.1.9-2 +- Revert "group: treat mandatory pkgs as mandatory if strict=true" (RhBug:1337731) +- enforce-api: reflect changes from #992475 in completion_helper (RhBug:1338504) +- enforce-api: add compatibility methods for renamed counterparts (RhBug:1338564) + +* Thu May 19 2016 Igor Gnatenko 1.1.9-1 +- doc: release notes 1.1.9 (Igor Gnatenko) +- spec: correctly set up requirements for python subpkg (Igor Gnatenko) +- spec: follow new packaging guidelines & make compatible with el7 (Igor + Gnatenko) +- zanata update (Jan Silhan) +- enforce-api: add missing bits of Base class (Michal Luscon) +- help: unify help msg strings (Michal Luscon) +- enforce-api: decorate Base class (Michal Luscon) +- util: add decorator informing users of nonapi functions (Michal Luscon) +- Added description for 'autoremove' in dnf help (RhBug:1324086) (Abhijeet + Kasurde) +- i18n: fixup for 0db13feed (Michal Luscon) +- i18n: use fallback mode if terminal does not support UTF-8 (RhBug:1332012) + (Michal Luscon) +- Revert "spec: follow new packaging guidelines & make compatible with el7" + (Michal Luscon) +- move autoglob feature directly to filterm() and filter() (Michael Mraka) +- group: treat mandatory pkgs as mandatory if strict=true (RhBug:1292892) + (Michal Luscon) +- locks: fix lock paths in tmpfsd config since cachedir has been changed + (Michal Luscon) +- remove formating from translation strings (Michal Luscon) +- base: set diskspace check filter before applying the filters (RhBug:1328674) + (Michal Luscon) +- order repos by priority and cost (Michael Mraka) +- spec: follow new packaging guidelines & make compatible with el7 (Igor + Gnatenko) +- bash-completion: first try to set fallback to BASH_COMPLETION_COMPATDIR (Igor + Gnatenko) +- updated copyrights for files changed this year (Michael Mraka) +- cli: fix warning from re.split() about non-empty pattern (RhBug:1286556) + (Igor Gnatenko) +- update authors file (Michal Luscon) +- Define __hash__ method for YumHistoryPackage (RhBug:1245121) (Max Prokhorov) + +* Tue Apr 05 2016 Michal Luscon 1.1.8-1 +- refactor: repo: add md_expired property (Michal Domonkos) +- test: fix cachedir usage in LocalRepoTest (Michal Domonkos) +- clean: operate on all cached repos (RhBug:1278225) (Michal Domonkos) +- refactor: repo: globally define valid repoid chars (Michal Domonkos) +- RepoPersistor: only write to disk when requested (Michal Domonkos) +- clean: remove dead subcommands (Michal Domonkos) +- doc: --best in case of problem (RhBug:1309408) (Jan Silhan) +- Added fix for correct error message for group info (RhBug:1209649) (Abhijeet + Kasurde) +- repo: don't get current timeout for librepo (RhBug:1272977) (Igor Gnatenko) +- doc: fix default timeout value (Michal Luscon) +- cli: inform only about nonzero md cache check interval (Michal Luscon) +- base: report errors in batch at the end of md downloading (Michal Luscon) +- repo: produce more sane error if md download fails (Michal Luscon) +- zanata update (RhBug:1322226) (Jan Silhan) +- doc: Fixed syntax of `assumeyes` and `defaultyes` ref lables in + `conf_ref.rst` (Matt Sturgeon) +- Fix output headers for dnf history command (Michael Dunphy) +- doc: change example of 'dnf-command(repoquery)' (Jaroslav Mracek) +- makacache.service: shorten journal logs (RhBug:1315349) (Michal Luscon) +- config: improve UX of error msg (Michal Luscon) +- Added user friendly message for out of range value (RhBug:1214562) (Abhijeet + Kasurde) +- doc: prefer repoquery to list (Jan Silhan) +- history: fix empty history cmd (RhBug:1313215) (Michal Luscon) +- Very minor tweak to the docs for `--assumeyes` and `--assumeno` (Matt + Sturgeon) + +* Thu Feb 25 2016 Michal Luscon 1.1.7-1 +- Add `/etc/distro.repos.d` as a path owned by the dnf package (Neal Gompa + (ニール・ゴンパ)) +- Change order of search and add new default repodirs (RhBug:1286477) (Neal + Gompa (ニール・ゴンパ)) +- group: don't mark available packages as installed (RhBug:1305356) (Jan + Silhan) +- history: adjust demands for particular subcommands (RhBug:1258503) (Michal + Luscon) +- Added extension command for group list (RhBug:1283432) (Abhijeet Kasurde) +- perf: dnf repository-packages upgrade (RhBug:1306304) (Jan Silhan) +- sack: Pass base.conf.substitutions["arch"] to sack in build_sack() function. + (Daniel Mach) +- build: make python2/3 binaries at build time (Michal Domonkos) +- fix dnf history traceback (RhBug:1303149) (Jan Silhan) +- cli: truncate expiration msg (RhBug:1302217) (Michal Luscon) + +* Mon Jan 25 2016 Michal Luscon 1.1.6-1 +- history: don't fail if there is no history (RhBug:1291895) (Michal Luscon) +- Allow dnf to use a socks5 proxy, since curl support it (RhBug:1256587) + (Michael Scherer) +- output: do not log rpm info twice (RhBug:1287221) (Michal Luscon) +- dnf owns /var/lib/dnf dir (RhBug:1294241) (Jan Silhan) +- Fix handling of repo that never expire (RhBug:1289166) (Jaroslav Mracek) +- Filter out .src packages when multilib_proto=all (Jeff Smith) +- Enable string for translation (RhBug:1294355) (Parag Nemade) +- Let logging format messages on demand (Ville Skyttä) +- clean: include metadata of local repos (RhBug:1226322) (Michal Domonkos) +- completion: Install to where bash-completion.pc says (Ville Skyttä) +- spec: bash completion is not a %%config file (Ville Skyttä) +- Change assertion handling for rpmsack.py (RhBug:1275878) (Jaroslav Mracek) +- cli: fix storing arguments in history (RhBug:1239274) (Ting-Wei Lan) + +* Thu Dec 17 2015 Michal Luscon 1.1.5-1 +- base: save group persistor only after successful transaction (RhBug:1229046) + (Michal Luscon) +- base: do not clean tempfiles after remove transaction (RhBug:1282250) (Michal + Luscon) +- base: clean packages that do not belong to any trans (Michal Luscon) +- upgrade: allow group upgrade via @ syntax (RhBug:1265391) (Michal Luscon) +- spec: Mark license files as %%license where available (Ville Skyttä) +- Remove unused imports (Ville Skyttä) +- Spelling fixes (Ville Skyttä) +- Fix typos in documentation (Rob Cutmore) +- parser: add support for braces in substitution (RhBug:1283017) (Dave + Johansen) +- completion_helper: Don't omit "packages" from clean completions (Ville + Skyttä) +- bash-completion: Avoid unnecessary python invocation per _dnf_helper (Ville + Skyttä) +- repo: Download drpms early (RhBug:1260421) (Ville Skyttä) +- clean: Don't hardcode list of args in two places (Ville Skyttä) +- cli: don't crash if y/n and sys.stdin is None (RhBug:1278382) (Adam + Williamson) +- sp err "environement" -> "environment" (Michael Goodwin) +- Remove -OO from #!/usr/bin/python (RhBug:1230820) (Jaroslav Mracek) +- cli: warn if plugins are disabled (RhBug:1280240) (Michal Luscon) + +* Mon Nov 16 2015 Michal Luscon 1.1.4-1 +- AUTHORS: updated (Jan Silhan) +- query: add compatibility methods (Michal Luscon) +- query: add recent, extras and autoremove methods to Query (Michal Luscon) +- query: add duplicated and latest-limit queries into api (Michal Luscon) +- format the email message with its as_string method (Olivier Andrieu) +- added dnf.i18n.ucd* functions as deprecated API (Jan Silhan) +- i18n: unicode resulting translations (RhBug:1278031) (Jan Silhan) +- po: get rid of new lines in translation (Jan Silhan) +- output: add skip count to summary (RhBug:1264032) (Michal Domonkos) +- groups: fix environment upgrade (Michal Luscon) +- Fix plural strings extraction (RhBug:1209056) (Baurzhan Muftakhidinov) +- po: fixed malformed beginning / ending (Jan Silhan) +- zanata update (Jan Silhan) +- cli: prevent tracebacks after C^ (RhBug:1274946) (Michal Luscon) + +* Wed Oct 14 2015 Michal Luscon 1.1.3-1 +- Update command_ref.rst (Jaroslav Mracek) +- Change in automatic.conf email settings to prevent email error with default + sender name (Jaroslav Mracek) +- Replace assert_called() with assert_called_with() for Py35 support (Neal + Gompa (ニール・ゴンパ)) +- doc: improve documentation (Jaroslav Mracek) +- doc: update the instructions related to nightly builds (Radek Holy) +- Revert "Add the continuous integration script" (Radek Holy) +- Revert "cosmetic: ci: fix the Copr name in the README" (Radek Holy) +- Fix typo in Command.canonical's doctring (Timo Wilken) +- base: group_install is able to exclude mandatory packages + (Related:RhBug:1199868) (Jan Silhan) + +* Wed Sep 30 2015 Michal Luscon 1.1.2-4 +- don't import readline as it causes crashes in Anaconda + (related:RhBug:1258364) + +* Tue Sep 22 2015 Michal Luscon 1.1.2-3 +- Revert "completion_helper: don't get IndexError (RhBug:1250038)" + +* Tue Sep 22 2015 Michal Luscon 1.1.2-2 +- add hawkey version requirement +- revert commit #70956 + +* Tue Sep 22 2015 Michal Luscon 1.1.2-1 +- doc: release notes 1.1.2 (Michal Luscon) +- sanitize non Unicode command attributes (RhBug:1262082) (Jan Silhan) +- don't redirect confirmation to stderr RhBug(1258364) (Vladan Kudlac) +- clean: add rpmdb to usage (Vladan Kudlac) +- completion_helper: don't get IndexError (RhBug:1250038) (Vladan Kudlac) +- add --downloadonly switch (RhBug:1048433) (Adam Salih) +- Add globbing support to base.by_provides() (RhBug:11259650) (Valentina + Mukhamedzhanova) +- spec: packaging python(3)-dnf according to new Fedora guidelines + (RhBug:1260198) (Jaroslav Mracek) +- Bug in Source0: URL in dnf.spec fixed (RhBug:126255) (Jaroslav Mracek) +- To dnf.spec added provides dnf-command(command name) for 21 dnf commands + (RhBug:1259657) (jmracek) +- Expire repo cache on failed package download (Valentina Mukhamedzhanova) +- cosmetic: ci: fix the Copr name in the README (Radek Holy) +- Add the continuous integration script (Radek Holy) +- Set proper charset on email in dnf-automatic (RhBug:1254982) (Valentina + Mukhamedzhanova) +- doc: improve configuration description (RhBug:1261766) (Michal Luscon) +- remove: show from which repo a package is (Vladan Kudlac) +- list: show from which repo a package is (RhBug:1234491) (Vladan Kudlac) +- Spelling/grammar fixes (Ville Skyttä) +- install: fix crash when terminal window is small (RhBug:1256531) (Vladan + Kudlac) +- install: mark unification of the progress bar (Vladan Kudlac) +- fix translations in python3 (RhBug:1254687) (Michal Luscon) +- group: CompsQuery now returns group ids (RhBug:1261656) (Michal Luscon) + +* Tue Sep 08 2015 Michal Luscon 1.1.1-2 +- fix access to demands (RhBug:1259194) (Jan Silhan) +- make clean_requiremets_on_remove=True (RhBug:1260280) (Jan Silhan) + +* Mon Aug 31 2015 Michal Luscon 1.1.1-1 +- Fixed typo (RhBug:1249319) (Adam Salih) +- fixed downgrade with wildcard (RhBug:1234763) (Adam Salih) +- reorganize logic of get_best_selector(s) and query (RhBug:1242946) (Adam + Salih) +- completion_helper: don't crash if exception occurred (RhBug:1225225) (Igor + Gnatenko) +- base: expire cache if repo is not available (Michal Luscon) +- Don't suggest --allowerasing if it is enabled (Christian Stadelmann) +- translation works in python3 (RhBug:1254687) (Jan Silhan) +- logrotate less often (RhBug:1247766) (Jan Silhan) +- implement dnf mark command (RhBug:1125925) (Michal Luscon) +- groups: use comps data to migrate persistor (Michal Luscon) +- groups: preserve api compatibility (Michal Luscon) +- groups: use persistor data for removing env/group (Michal Luscon) +- persistor: add migration and bump version (Michal Luscon) +- persistor: store name and ui_name of group (Michal Luscon) +- show real metadata timestamp on the server in verbose mode (Jan Silhan) +- lock: make rpmdb lock blocking (RhBug:1210289) (Michal Luscon) + +* Wed Aug 12 2015 Michal Luscon 1.1.0-2 +- update: installonly pkgs are not shown in both install and skipped section + (RhBug:1252415) (Jan Silhan) +- output: sort skipped packages (Jan Silhan) +- output: skipped conflicts are set (RhBug:1252032) (Jan Silhan) +- keep the dwongrading package installed if transaction fails (RhBug:1249379) + (Jan Silhan) +- don't store empty attributes (RhBug:1246928) (Michael Mraka) +- doc: correct dnf.conf man section (RhBug:1245349) (Michal Luscon) + +* Mon Aug 10 2015 Michal Luscon 1.1.0-1 +- print skipped pkg with broken deps too (Related:RhBug:1210445) (Jan Silhan) +- history: set commands output as default (RhBug:1218401) (Michal Luscon) +- Update es.po. save:guardar -> save:ahorrar (Máximo Castañeda) +- cosmetic: option arg in Base.*install is replaced with strict (Jan Silhan) +- group: don't fail on first non-existing group (Jan Silhan) +- install: skips local pkgs of lower version when strict=0 + (Related:RhBug:1227952) (Jan Silhan) +- install: skip broken/conflicting packages in groups when strict=0 (Jan + Silhan) +- install: skip broken/conflicting packages when strict=0 (Jan Silhan) +- implemented `strict` config option working in install cmd (RhBug:1197456) + (Jan Silhan) +- fixed 'dnf --quiet repolist' lack of output (RhBug:1236310) (Nick Coghlan) +- Add support for MIPS architecture (Michal Toman) +- package: respect baseurl attribute in localPkg() (RhBug:1219638) (Michal + Luscon) +- Download error message is not written on the same line as progress bar + anymore (RhBug: 1224248) (Adam Salih) +- dnf downgrade does not try to downgrade not installed packages (RhBug: + 1243501) (max9631) +- pkgs not installed due to rpm error are reported (RhBug:1207981) (Adam Salih) +- dnf install checks availability of all given packages (RhBug:1208918) (Adam + Salih) +- implemented install_weak_deps config option (RhBug:1221635) (Jan Silhan) +- ignore SIGPIPE (RhBug:1236306) (Michael Mraka) +- always add LoggingTransactionDisplay to the list of transaction displays + (RhBug:1234639) (Radek Holy) +- Add missing FILES section (RhBug: 1225237) (Adam Salih) +- doc: Add yum vs dnf hook information (RhBug:1244486) (Parag Nemade) +- doc: clarify the expected type of the do_transactions's display parameter + (Radek Holy) +- apichange: add dnf.cli.demand.DemandSheet.transaction_display (Radek Holy) +- apichange: add dnf.callback.TransactionProgress (Radek Holy) +- move the error output from TransactionDisplay into a separate class (Radek + Holy) +- rename TransactionDisplay.errorlog to TransactionDisplay.error (Radek Holy) +- report package verification as a regular RPM transaction event (Radek Holy) +- rename TransactionDisplay.event to TransactionDisplay.progress (Radek Holy) +- apichange: deprecate dnf.callback.LoggingTransactionDisplay (Radek Holy) +- use both CliTransactionDisplay and demands.transaction_display (Radek Holy) +- apichange: accept multiple displays in do_transaction (Radek Holy) +- support multiple displays in RPMTransaction (Radek Holy) + +* Fri Jul 31 2015 Michal Luscon 1.0.2-3 +- Fix regression in group list command introduced by 02c3cc3 (Adam Salih) +- AUTHORS: updated (Jan Silhan) +- stop saying "experimental" (Matthew Miller) + +* Tue Jul 21 2015 Jan Silhan 1.0.2-2 +- fixed python3 syntax error from f427aa2 (Jan Silhan) + +* Fri Jul 17 2015 Michal Luscon 1.0.2-1 +- give --allowerasing hint when error occurs during resolution (RhBug:1148630) + (Jan Silhan) +- show --best hint with skipped packages every time (RhBug:1176351) (Jan Silhan) +- notify about skipped packages when upgrade (RhBug:1210445) (Jan Silhan) +- dnf-automatic: Document apply_updates=no behavior wrt keepcache (Ville + Skyttä) +- persistor: share functionality of JSONDB (Jan Silhan) +- keepcache=0 persists packages till next successful transaction + (RhBug:1220074) (Jan Silhan) +- do not use releasever in cache path (related to RhBug:1173107) (Michael + Mraka) +- doc: add dnf list use case (Michal Luscon) +- repo: allow ntlm proxy auth (RhBug:1219199) (Michal Luscon) +- add a script which updates release notes (Radek Holy) +- doc: reverse the order of release notes (Radek Holy) +- completion_helper: fix tb if list XXX is not known arg (RhBug:1220040) (Igor + Gnatenko) +- configurable maximum number of parallel downloads (RhBug:1230975) (Igor + Gnatenko) +- add info to bash_completion (1nsan3) +- dnf upgrade does not try to upgrade uninstalled packages (RhBug: 1234763) + (Adam Salih) +- dnf group list now checks every package and prints out only invalid ones + (Adam Salih) +- install: return zero exit code if group is already installed (RhBug:1232815) + (Michal Luscon) +- doc: add -b which does the same as --best (Igor Gnatenko) +- support category groups (Michael Mraka) +- cli test update for repofrompath (Michael Mraka) +- documentation for --repofrompath (Michael Mraka) +- implemented --repofrompath option (RhBug:1113384) (Michael Mraka) +- doc: document filter provides and obsoletes (Michal Luscon) +- doc: extend --quiet explanation (RhBug:1133979) (Jan Silhan) +- fixed dnf-automatic email emitter unicode error (RhBug:1238958) (Jan Silhan) +- doc: be specific what 'available' means in list/info (Jan Silhan) +- cosmetic: fixed typo (RhBug:1238252) (Jan Silhan) +- groups: clean dependencies (Michal Luscon) +- groups: fix removing of env that contains previously removed group (Michal + Luscon) +- groups: fix removing of empty group (Michal Luscon) +- AUTHORS: updated (Jan Silhan) +- bash-completion: ignore sqlite3 user configuration (Peter Simonyi) +- Fix package name for rawhide .repo files (Frank Dana) +- Add 'transaction_display' to DemandSheet (Will Woods) +- translation: update (Jan Silhan) +- translation: use zanata instead of transifex (Jan Silhan) +- Updated Polish translation (Piotr Drąg) +- updated georgian translation (George Machitidze) +- group: fixed installing of already installed environment (Jan Silhan) +- conf: change minrate threshold to librepo default (RhBug:1212320) (Michal + Luscon) + +* Tue Jun 09 2015 Michal Luscon 1.0.1-2 +- conf: change minrate threshold to librepo default (RhBug:1212320) +- group: fixed installation of already installed environments + +* Tue Jun 09 2015 Michal Luscon 1.0.1-1 +- doc: document variables in repo conf (Michal Luscon) +- groups: temporary fix for group remove (RhBug:1214968) (Michal Luscon) +- group: print summary of marked groups / environments together at the end (Jan + Silhan) +- group: fixed marking as installed (RhBug:1222694) (Jan Silhan) +- doc: Spelling fixes (Ville Skyttä) +- dnf-automatic: Fix systemd service description (thanks Ville Skyttä) (Jan + Silhan) +- doc: assumeyes added to Base.conf and config option (Jan Silhan) +- optionparser: deleted --obsoletes option that conflicted with repoquery + plugin (Jan Silhan) +- dnf-automatic: Document emit_via default (Ville Skyttä) +- man: yum2dnf don;t show content (RhBug:1225246) (Thanks Adam Salih) (Jan + Silhan) +- doc: allowed chars of repo ID (Jan Silhan) +- doc: minimal repo config file (Jan Silhan) +- doc: configuration files replacement policy (Jan Silhan) +- fixed typo in man page (RhBug:1225168) (Michael Mraka) +- Update authors (Michal Luscon) +- dnf-automatic: add random_sleep option (RhBug:1213985) (Vladan Kudlac) +- don't print bug report statement when rpmdb is corrupted + (Related:RhBug:1225277) (Jan Silhan) +- comps: fix unicode issue (RhBug:1223932) (Thanks Parag) (Parag Nemade) +- logging: setup librepo log in verbose mode (Michal Luscon) +- doc: document the versioning scheme (Radek Holy) +- groups: end up empty group removal before solving (Michal Luscon) +- groups: end up empty installation before solving (RhBug:1223614) (Michal + Luscon) +- doc: add support for transactions/packages/ranges in "dnf history list" + (Radek Holy) +- doc: add support for transaction ranges in "dnf history info" (Radek Holy) +- support ssl client certificates (RhBug:1203661) (Michael Mraka) +- doc: document the "mirrorlist" configuration option (Radek Holy) +- doc: document the "metalink" configuration option (Radek Holy) +- doc: document the "baseurl" configuration option (Radek Holy) +- doc: document the "enabled" configuration option (Radek Holy) +- doc: document the "name" configuration option (Radek Holy) +- Revert "spec: added sqlite requirement" (Jan Silhan) +- spec: added sqlite requirement (Jan Silhan) +- cosmetic: fixed typo in comment (Jan Silhan) +- man: added reference to bug reporting guide (Jan Silhan) +- test: ignore user terminal width (Jan Silhan) +- cosmetic: base: import dnf.util.first (Jan Silhan) +- base.upgrade: inform user when pkg not installed and skipped (RhBug:1187741) + (Jan Silhan) +- disable buildtime c/c++ dependency (Michael Mraka) +- doc: document the new virtual provides (Radek Holy) +- AUTHORS: updated (Jan Silhan) +- AUTHORS: distuinguish authors and contributors (Jan Silhan) +- Create ka.po (George Machitidze) +- Parser: fix path handling (Haikel Guemar) +- doc: metadata_timer_sync checked every hour (Jan Silhan) + +* Wed Apr 29 2015 Michal Luscon 1.0.0-1 +- doc: release notes dnf-1.0.0 (Michal Luscon) +- completion: don't do aliases (RhBug:1215289) (Jan Silhan) +- use Sack.load_repo() instead of Sack.load_yum_repo() (Jan Silhan) +- Repo.name has default value of repo ID (RhBug:1215560) (Jan Silhan) +- cosmetic: get rid of user visible yum references (Jan Silhan) +- moved install_or_skip to dnf.comps (Jan Silhan) +- group: see already installed group during installation (RhBug:1199648) (Jan + Silhan) +- group: install_or_skip returns num of packages to install (Jan Silhan) +- group: made global function install_or_skip (Jan Silhan) +- AUTHORS: updated (Radek Holy) +- describe --refresh option in --help output (Pádraig Brady) +- better no such command message (RhBug:1208773) (Jan Silhan) +- doc: package-cleanup example doesn't print 'No match for argument:...' + garbage (Jan Silhan) +- mention yum check replacement (Michael Mraka) +- added ref to dnf list (Michael Mraka) +- added package-cleanup to dnf translation table (Michael Mraka) +- python3: Repo comparison (RhBug:1208018) (Jan Silhan) +- python3: YumHistoryRpmdbProblem comparison (RhBug:1207861) (Jan Silhan) +- python3: YumHistoryTransaction comparison (Jan Silhan) +- tests: use packages in test_transaction (Radek Holy) +- cosmetic: fix some Pylint errors (Radek Holy) +- updated documentation wrt installonlypkgs and auto removal (Michael Mraka) +- mark installonly packages always as userinstalled (RhBug:1201445) (Michael + Mraka) +- mark username/password as api (Michael Mraka) +- document username/password repo attributes (Michael Mraka) +- support HTTP basic auth (RhBug:1210275) (Michael Mraka) +- cli: better metadata timestamp info (Michal Luscon) +- repo: add metadata mirror failure callback (Michal Luscon) +- dnf-yum: cosmetic: lower case after comma (Jan Silhan) +- dnf-yum: print how to install migrate plugin (Jan Silhan) +- doc: show the real package for each tool in dnf-plugins-extras (Tim + Lauridsen) +- doc: improve the documentation of repo costs (Radek Holy) +- doc: fix debuginfo-install package name (Michal Luscon) +- doc: release notes 0.6.5 (Michal Luscon) +- bash-completion: allow only one subcmd for help (Igor Gnatenko) +- bash-completion: add history completion (Igor Gnatenko) +- bash-completion: add completion for help (Igor Gnatenko) +- bash-completion: check where pointing bin/dnf (Igor Gnatenko) +- bash-completion: implement completion for clean cmd (Igor Gnatenko) +- bash_completion: implement downgrade command (Igor Gnatenko) +- bash-completion: refactor to python helper (Igor Gnatenko) +- command downgrade does downgrade_to (RhBug:1191275) (Jan Silhan) +- AUTHORS: updated (Jan Silhan) +- clean: 'dnf clean all' should also clean presto and updateinfo solvx files + (Parag Nemade) +- dnf-yum: modified warning message (RhBug:1207965) (Jan Silhan) + +* Tue Mar 31 2015 Michal Luscon 0.6.5-1 +- subject: expand every glob name only once (RhBug:1203151) (Michal Luscon) +- group mark: skips already installed groups (Jan Silhan) +- Merge pull request #246 from mluscon/yum2dnf (mluscon) +- Add yum2dnf man page (Michal Luscon) +- doc: extend cli_vs_yum (Michal Luscon) +- dnf-yum package does not conflict with yum 3.4.3-505+ (Jan Silhan) +- fixed double set of demand from 0e4276f (Jan Silhan) +- group: remove cmd don't load available_repos, see 04da412 (Jan Silhan) +- spec: /var/lib/dnf owned by dnf-conf (Jan Silhan) +- spec: apply the weak dependencies only on F21+ (Radek Holy) +- dnf-automatic: fixed python_sitelib (RhBug:1199450) (Jan Silhan) +- Add release instructions (Michal Luscon) +- setup tito to bump version in VERSION.cmake (Michal Luscon) +- initialize to use tito (Michal Luscon) +- prepare repo for tito build system (Michal Luscon) +- spec: recommends bash-completion (RhBug:1190671) (Jan Silhan) +- completion: work with just python(3)-dnf (Jan Silhan) +- spec: move necessary files inside python(3) subpackages (RhBug:1191579) (Jan Silhan) +- bash-completion: use python method to get commands (RhBug:1187579) (Igor Gnatenko) +- api: exposed pluginconfpath main config (RhBug:1195325) (Jan Silhan) +- updated AUTHORS (Jan Silhan) +- add reinstall to bash_completion (Alberto Ruiz) +- added new packages to @System for duplicated query test (Michael Mraka) +- test for duplicated, installonly and latest_limit pkgs (Michael Mraka) +- tests for autoremove, extras and recent pkgs (Michael Mraka) +- moved push_userinstalled from base to goal (Michael Mraka) +- filter or skip 'n' latest packages (Michael Mraka) +- moved recent to query (Michael Mraka) +- moved autoremove to query (Michael Mraka) +- moved extras list to query (Michael Mraka) +- create query for installonly packages (Michael Mraka) +- create query for duplicated packages (Michael Mraka) +- cosmetic: base: fixed pylint warnings (Jan Silhan) +- do transaction cleanup after plugin hook (RhBug:1185977) (Michal Luscon) +- base: extend download lock (RhBug:1157233) (Michal Luscon) +- lock: output meaningful error for malformed lock file (Michal Luscon) +- util: fix race condition in ensure_dir() (Michal Luscon) +- lock: switch metadata lock to blocking mode (Michal Luscon) +- install nonmandatory group packages as optional (Related:RhBug:1167881) (Michal Luscon) +- remove command deletes whole dependency tree (RhBug:1154202) (Jan Silhan) +- cmd list takes as parameter, revert of 526e674 (Jan Silhan) +- spec: own /var/lib/dnf directory (RhBug:1198999) (Jan Silhan) +- transifex update (Jan Silhan) +- doc: fixed systemd execution of dnf-automatic (Jan Silhan) +- doc: how to run dnf-automatic (RhBug:1195240) (Jan Silhan) +- cosmetic: added forgotten :api mark from 05b03fc (Jan Silhan) +- api: exposed Repo.skip_if_unavailable config (RhBug:1189083) (Jan Silhan) +- updated documentation for 'dnf list autoremove' (Michael Mraka) +- reuse list_autoremove() in autoremove command (Michael Mraka) +- function for autoremove package list (Michael Mraka) +- implemented dnf list autoremove (Michael Mraka) +- exclude not documented history subcommands (RhBug:1193914,1193915) (Jan Silhan) +- better file pattern recognition (RhBug:1195385) (Jan Silhan) +- spec: fix Obsoletes of the new DNF (Radek Holy) +- remove boot only constraint and add missing download lock (Michal Luscon) +- util: remove unused user_run_dir() function (Michal Luscon) +- lock: change the destination folder of locks to allow suided programs work properly (RhBug:1195661) (Michal Luscon) +- install dnf-3 only when python3 is enabled (thanks glensc) (Jan Silhan) +- fixed unicode Download error (RhBug:1190458) (Jan Silhan) +- log: print metadata age along with timestamp (Petr Spacek) +- cli: fix double expansion of cachedir (RhBug:1194685) (Michal Luscon) +- removed unused dnf-makecache.cron (Jan Silhan) +- renamed erase command to remove (RhBug:1160806) (Jan Silhan) +- spec: made python3-dnf package installed by default in f23 (Jan Silhan) +- AUTHORS: changed email address (Jan Silhan) +- doc: improve the documentation of the "install" command (Radek Holy) +- "dnf install non-existent" should fail (Radek Holy) +- tests: add some tests of Base.install (Radek Holy) +- tests: add some tests of Base.package_install (Radek Holy) +- Revert "doesn't upgrade packages by installing local packages" (RhBug:1160950) (Radek Holy) +- lint: fix all Pylint errors in test_install (Radek Holy) +- tests: add some tests to test_install (Radek Holy) +- tests: improve some tests in test_install (Radek Holy) +- cosmetic: reorder tests in test_install (Radek Holy) +- cosmetic: rename some tests in test_install and add some docstrings (Radek Holy) +- AUTHORS: updated (Jan Silhan) +- Add support for armv6hl (Peter Hjalmarsson) +- doc: subject.__init__(): what is pkg_spec (Jan Silhan) +- doc: mentioning raising IOError from Base.fill_sack() (Jan Silhan) +- option_parser: fixed splitting multiple values (RhBug:1186710) (Jan Silhan) +- AUTHORS: updated (Jan Silhan) +- Standardize words describing boolean data type (Christopher Meng) + +* Wed Feb 4 2015 Jan Silhan - 0.6.4-1 +- Adapt to librepo-1.7.13, metalink and mirrorlist are not loaded anymore when the repo is local. (Radek Holy) +- not raises value error when no metadata exist (Jan Silhan) +- Remove lock files during boot (RhBug:1154476) (Michal Luscon) +- doc: groups are ordered not categories (Jan Silhan) +- doc: added Package attributes to API (Jan Silhan) +- README: link to bug reporting guide (Jan Silhan) +- README: the official documentation is on readthedoc (Jan Silhan) +- i18n: unicode encoding does not throw error (RhBug:1155877) (Jan Silhan) +- conf: added minrate repo option (Related:RhBug:1175466) (Jan Silhan) +- conf: added timeout repo option (RhBug:1175466) (Jan Silhan) +- doc: api_queries: add 'file' filter description (RhBug:1186461) (Igor Gnatenko) +- doc: documenting enablegroups (Jan Silhan) +- log: printing metadata timestamp (RhBug:1170156) (Jan Silhan) +- base: setup default cachedir value (RhBug:1184943) (Michal Luscon) +- orders groups/environments by display_order tag (RhBug:1177002) (Jan Silhan) +- no need to call create_cmdline_repo (Jan Silhan) +- base: package-spec matches all packages which the name glob pattern fits (RhBug:1169165) (Michal Luscon) +- doc: move dnf.conf to appropriate man page section (RhBug:1167982) (Michal Luscon) +- tests: add test for blocking process lock (Michal Luscon) +- lock: fix several race conditions in process lock mechanism (Michal Luscon) +- base: use blocking process lock during download phase (RhBug:1157233) (Michal Luscon) +- Update the Source0 generation commands in dnf.spec.in file (Parag Nemade) +- Enhancement to dnf.spec.in file which follows current fedora packaging guidelines (Parag Nemade) +- doc: add some examples and documentation of the core use case (RhBug:1138096) (Radek Holy) +- bash-completion: enable downgrading packages for local files (RhBug:1181189) (Igor Gnatenko) +- group: prints plain package name when package not in any repo (RhBug:1181397) (Jan Silhan) +- spec: own __pycache__ for python 3 (Igor Gnatenko) +- changed hawkey.log dir to /var/log (RhBug:1175434) (Jan Silhan) +- bash-completion: handle sqlite errors (Igor Gnatenko) +- use LANG=C when invoking 'dnf help' and 'sed' with regular expressions (Jakub Dorňák) +- spec: own __pycache__ directory for py3 (Igor Gnatenko) +- doc: mentioning Install command accepts path to local rpm package (Jan Silhan) +- groups: in erase and install cmd non-existent group does not abort transaction (Jan Silhan) +- doc: running tests in README (Jan Silhan) +- api: transaction: added install_set and remove_set (RhBug:1162887) (Jan Silhan) +- cosmetic: fixed some typos in documentation (Jan Silhan) +- groups: environments described after @ sign works (RhBug:1156084) (Jan Silhan) +- own /etc/dnf/protected.d (RhBug:1175098) (Jan Silhan) +- i18n: computing width of char right (RhBug:1174136) (Jan Silhan) +- cosmetic: renamed _splitArg -> _split_arg (Jan Silhan) +- conf: removed include name conflict (RhBug:1055910) (Jan Silhan) +- output: removed unpredictable decision based on probability introduced in ab4d2c5 (Jan Silhan) +- output: history list is not limited to 20 records (RhBug:1155918) (Jan Silhan) +- doc: referenced forgotten bug fix to release notes (Jan Silhan) +- cosmetic: doc: removed duplicated word (Jan Silhan) +- doc: described unavailable package corner case with skip_if_unavailable option (RhBug:1119030) (Jan Silhan) +- log: replaced size with maxsize directive (RhBug:1177394) (Jan Silhan) +- spec: fixed %ghost log file names (Jan Silhan) + +* Mon Dec 8 2014 Jan Silhan - 0.6.3-2 +- logging: reverted naming from a6dde81 + +* Mon Dec 8 2014 Jan Silhan - 0.6.3-1 +- transifex update (Jan Silhan) +- bash-completion: don't query if we trying to use local file (RhBug:1153543) (Igor Gnatenko) +- bash-completion: fix local completion (RhBug:1151231) (Igor Gnatenko) +- bash-completion: use sqlite cache from dnf-plugins-core (Igor Gnatenko) +- base: output a whole list of installed packages with glob pattern (RhBug:1163063) (Michal Luscon) +- cli: _process_demands() does not respect --caheonly (RhBug:1151854) (Michal Luscon) +- new authors added (Jan Silhan) +- install: allow installation of provides with glob (Related:RhBug:1148353) (Michal Luscon) +- tests: removed mock patch for _, P_ (Jan Silhan) +- fixed error summary traceback (RhBug:1151740) (Jan Silhan) +- doc: swap command alternative mentioned (RhBug:1110780) (Jan Silhan) +- base: package_reinstall works only with the same package versions (Jan Silhan) +- base: package_install allows install different arch of installed package (Jan Silhan) +- base: package_downgrade prints message on failure (Jan Silhan) +- base: package_upgrade does not reinstall or downgrade (RhBug:1149972) (Jan Silhan) +- groups: searches also within localized names (RhBug:1150474) (Jan Silhan) +- Run tests with C locales. (Daniel Mach) +- Adds new motd emitter for dnf-automatic (RhBug:995537) (Kushal Das) +- Fix wrong cache directory path used to clean up binary cache (Satoshi Matsumoto) +- fix: traceback in history info (RhBug: 1149952) (Tim Lauridsen) +- logging: added logrotate script for hawkey.log (RhBug:1149350) (Jan Silhan) +- output: renamed displayPkgsInGroups (Jan Silhan) +- logging: renamed log files (RhBug:1074715)" (Jan Silhan) +- comps: Environment differentiates optional and mandatory groups (Jan Silhan) +- group info handles environments (RhBug:1147523) (Jan Silhan) +- deltarpm enabled by default (RhBug:1148208) (Jan Silhan) +- doc: deplist command (Jan Silhan) +- doc: minor fixes + repo references changed (Jan Silhan) +- spec: requires rpm-plugin-systemd-inhibit (RhBug:1109927) (Jan Silhan) + +* Fri Oct 3 2014 Jan Silhan - 0.6.2-1 +- transifex update (Jan Silhan) +- refactor: move MakeCacheCommand out into its own file. (Ales Kozumplik) +- api: add dnf.cli.CliError. (Ales Kozumplik) +- Update user_faq.rst (Stef Krie) +- Make --refresh play nice with lazy commands. (Ales Kozumplik) +- bash-completion: more faster completing install/remove (Igor Gnatenko) +- bash-completion: complete 'clean|groups|repolist' using help (Igor Gnatenko) +- Allow some commands to use stale metadata. (RhBug:909856) (Ales Kozumplik) +- does not install new pkgs when updating from local pkgs (RhBug:1134893) (Jan Silhan) +- doesn't upgrade packages by installing local packages (Related:RhBug:1138700) (Jan Silhan) +- refactor: repo: separate concepts of 'expiry' and 'sync strategy'. (Ales Kozumplik) +- fix: dnf.cli.util.* leaks file handles. (Ales Kozumplik) +- remove: YumRPMTransError. (Ales Kozumplik) +- rename: Base's runTransaction -> _run_transaction(). (Ales Kozumplik) +- drop unused parameter of Base.verify_transaction(). (Ales Kozumplik) +- bash-completion: new completion from scratch (RhBug:1070902) (Igor Gnatenko) +- py3: add queue.Queue to pycomp. (Ales Kozumplik) +- locking: store lockfiles with the resource they are locking. (RhBug:1124316) (Ales Kozumplik) +- groups: marks reason 'group' for packages that have no record yet (RhBug:1136584) (Jan Silhan) +- goal: renamed undefined name variable (Jan Silhan) +- refactor: split out and clean up the erase command. (Ales Kozumplik) +- py3: fix traceback in fmtColumns() on a non-subscriptable 'columns'. (Ales Kozumplik) +- groups: allow erasing depending packages on remove (RhBug:1135861) (Ales Kozumplik) +- history: fixed wrong set operation (RhBug:1136223) (Jan Silhan) +- base: does not reinstall pkgs from local rpms with install command (RhBug:1122617) (Jan Silhan) +- refactor: crypto: drop the integer keyid representation altogether. (Ales Kozumplik) +- crypto: fix importing rpmfusion keys. (RhBug:1133830) (Ales Kozumplik) +- refactor: crypto: Key is a class, not an "info" dict. (Ales Kozumplik) +- repos: fix total downloaded size reporting for cached packages. (RhBug:1121184) (Ales Kozumplik) + +* Thu Aug 28 2014 Jan Silhan - 0.6.1-1 +- packaging: add dnf-yum. (Ales Kozumplik) +- cli: added plugins missing hint (RhBug:1132335) (Jan Silhan) +- using ts.addReinstall for package reinstallation (RhBug:1071854) (Jan Silhan) +- Add history redo command. (Radek Holy) +- Add a TransactionConverter class. (Radek Holy) +- bash-completion: complete `help` with commands (Igor Gnatenko) +- bash-completion: generate commands dynamically (Igor Gnatenko) +- base: group_install accepts glob exclude names (RhBug:1131969) (Jan Silhan) +- README: changed references to new repo location (Jan Silhan) +- transifex update (Jan Silhan) +- syntax: fixed indentation (Jan Silhan) +- removed lt.po which was accidentally added in c2e9b39 (Jan Silhan) +- lint: fix convention violations in the new source files (Radek Holy) +- Fix setting of the resolving demand for repo-pkgs command. (Radek Holy) +- Add repository-packages remove-or-distro-sync command. (RhBug:908764) (Radek Holy) +- fix: traceback that GroupPersistor._original might not exist. (RhBug:1130878) (Ales Kozumplik) +- pycomp: drop to_ord(). (Ales Kozumplik) +- refactor: crypto.keyids_from_pubring() using _extract_signing_subkey(). (Ales Kozumplik) +- fix: another 32-bit hex() problem in crypto. (Ales Kozumplik) +- remove: pgpmsg.py. (Ales Kozumplik) +- replace the whole of pgpmsg.py with gpgme and a dummy context. (Ales Kozumplik) +- cosmetic: sort methods of Repo according to the coding standard. (Ales Kozumplik) +- Fix dnf.crypto.keyinfo2keyid(). (Ales Kozumplik) +- util: get rid of an inconvenient 'default_handle' constant. (Ales Kozumplik) +- simplify misc.import_key_to_pubring()'s signature. (Ales Kozumplik) +- cleanup: header of dnf.yum.pgpmsg. (Ales Kozumplik) +- crypto: add crypto.retrieve() and drop Base._retrievePublicKey() (Ales Kozumplik) +- cosmetic: order of functions in dnf.crypto. (Ales Kozumplik) +- unicode: fixed locale.format error (RhBug:1130432) (Jan Silhan) +- remove: misc.valid_detached_sig(). (Ales Kozumplik) +- tests: some tests for dnf.crypto. (Ales Kozumplik) +- crypto: use pubring_dir() context manager systematically. (Ales Kozumplik) +- Drop unused argument from getgpgkeyinfo(). (Ales Kozumplik) +- remove: Base._log_key_import(). (Ales Kozumplik) +- doc: cosmetic: conf_ref: maintain alphabetical order of the options. (Ales Kozumplik) +- crypto: document crypto options for repo. (Ales Kozumplik) +- crypto: fixup procgpgkey() to work with Py3 bytes. (Ales Kozumplik) +- dnf.util.urlopen(): do not create unicode streams for Py3 and bytes for Py2 by default. (Ales Kozumplik) +- lint: delinting of the repo_gpgcheck patchset. (Ales Kozumplik) +- Add CLI parts to let the user confirm key imports. (RhBug:1118236) (Ales Kozumplik) +- gpg: make key decoding work under Py3. (Ales Kozumplik) +- crypto: add dnf.crypto and fix things up so untrusted repo keys can be imported. (Ales Kozumplik) +- transifex update (Jan Silhan) +- syntax: fixed indentation (Jan Silhan) +- packaging: pygpgme is a requirement. (Ales Kozumplik) +- remove: support for gpgcakey gets dropped for now. (Ales Kozumplik) +- repo: smarter _DetailedLibrepoError construction. (Ales Kozumplik) +- repo: nicer error message on librepo's perform() failure. (Ales Kozumplik) +- get_best_selector returns empty selector instead of None (Jan Silhan) +- packaging: add automatic's systemd unit files. (RhBug:1109915) (Ales Kozumplik) +- automatic: handle 'security' update_cmd. (Ales Kozumplik) + +* Tue Aug 12 2014 Aleš Kozumplík - 0.6.0-1 +- lint: fix convention violations in the new source files (Radek Holy) +- Add "updateinfo [] [] security" command. (RhBug:850912) (Radek Holy) +- Add "updateinfo [] [] bugfix" command. (Radek Holy) +- Add "updateinfo [] [] enhancement" command. (Radek Holy) +- Add "updateinfo [] [] [...]" command. (Radek Holy) +- Add "updateinfo [] [] [...]" command. (Radek Holy) +- Add "updateinfo [] all" command. (Radek Holy) +- Add "updateinfo [] updates" command. (Radek Holy) +- Add "updateinfo [] installed" command. (Radek Holy) +- Add "-v updateinfo info" command. (Radek Holy) +- Add "updateinfo info" command. (Radek Holy) +- Add "updateinfo list" command. (Radek Holy) +- Add "updateinfo available" command. (Radek Holy) +- Add "updateinfo summary" command. (Radek Holy) +- Add basic updateinfo command. (Radek Holy) +- test: add updateinfo to the testing repository (Radek Holy) +- test: support adding directory repos to Base stubs (Radek Holy) +- test: really don't break other tests with the DRPM fixture (Radek Holy) +- Load UpdateInfo.xml during the sack preparation. (Radek Holy) +- Add Repo.updateinfo_fn. (Radek Holy) +- lint: add Selector calls to false positives, it's a hawkey type. (Ales Kozumplik) +- removed recursive calling of ucd in DownloadError (Jan Silhan) +- does not throw error when selector is empty (RhBug:1127206) (Jan Silhan) +- remove etc/version-groups.conf, not used. (Ales Kozumplik) +- lint: dnf.conf.parser (Ales Kozumplik) +- rename: dnf.conf.parser.varReplace()->substitute() (Ales Kozumplik) +- pycomp: add urlparse/urllib.parser. (Ales Kozumplik) +- move: dnf.yum.parser -> dnf.conf.parser. (Ales Kozumplik) +- packaging: add dnf-automatic subpackage. (Ales Kozumplik) +- doc: properly list the authors. (Ales Kozumplik) +- automatic: add documentation, including dnf.automatic(8) man page. (Ales Kozumplik) +- dnf-automatic: tool supplying the yum-cron functionality. (Ales Kozumplik) +- doc: cosmetic: fixed indent in proxy directive (Jan Silhan) +- include directive support added (RhBug:1055910) (Jan Silhan) +- refactor: move MultiCallList to util. (Ales Kozumplik) +- cli: do not output that extra starting newline in list_transaction(). (Ales Kozumplik) +- refactor: extract CLI cachedir magic to cli.cachedir_fit. (Ales Kozumplik) +- transifex update (Jan Silhan) +- move: test_output to tests/cli. (Ales Kozumplik) +- refactor: move Term into its own module. (Ales Kozumplik) +- refactoring: cleanup and linting in dnf.exceptions. (Ales Kozumplik) +- lint: test_cli.py (Ales Kozumplik) +- lint: rudimentary cleanups in tests.support. (Ales Kozumplik) +- refactor: loggers are module-level variables. (Ales Kozumplik) +- groups: promote unknown-reason installed packages to 'group' on group install. (RhBug:1116666) (Ales Kozumplik) +- c82267f refactoring droppped plugins.run_transaction(). (Ales Kozumplik) +- cli: sort packages in the transaction summary. (Ales Kozumplik) +- refactor: cli: massively simplify how errors are propagated from do_transaction(). (Ales Kozumplik) +- groups: rearrange things in CLI so user has to confirm the group changes. (Ales Kozumplik) +- groups: committing the persistor data should only happen at one place. (Ales Kozumplik) +- groups: visualizing the groups transactions. (Ales Kozumplik) +- Add dnf.util.get_in() to navigate nested dicts with sequences of keys. (Ales Kozumplik) +- group persistor: generate diffs between old and new DBs. (Ales Kozumplik) +- Better quoting in dnf_pylint. (Ales Kozumplik) +- lint: logging.py. (Ales Kozumplik) +- Do not print tracebacks to the tty on '-d 10' (RhBug:1118272) (Ales Kozumplik) +- search: do not double-report no matches. (Ales Kozumplik) +- refactor: move UpgradeToCommand to its own module. (Ales Kozumplik) + +* Mon Jul 28 2014 Aleš Kozumplík - 0.5.5-1 +- packaging: also add pyliblzma to BuildRequires. (Ales Kozumplik) +- essential cleanup in dnf.yum.misc, removing a couple of functions too. (Ales Kozumplik) +- remove: Base.findDeps and friends. (Ales Kozumplik) +- Make pyliblzma a requriement. (RhBug:1123688) (Ales Kozumplik) +- whole user name can contain non-ascii chars (RhBug:1121280) (Jan Silhan) +- Straighten up the exceptions when getting a packages header. (RhBug:1122900) (Ales Kozumplik) +- tests: refactor: rename test_resource_path() -> resource_path() and use it more. (Ales Kozumplik) +- transifex update (Jan Silhan) +- remove: conf.commands. (Ales Kozumplik) +- proxy username and password, for both CLI and API. (RhBug:1120583) (Ales Kozumplik) +- conf: only 'main' is a reserved section name. (Ales Kozumplik) +- refactoring: cleanup a couple of lint warnings in base.py. (Ales Kozumplik) +- refactoring: move repo reading implementation out of dnf.Base. (Ales Kozumplik) +- refactor: repo_setopts is a CLI thing and doesn't belong to Base. (Ales Kozumplik) +- refactor: move cleanup methods to dnf.cli.commands.clean. (Ales Kozumplik) +- depsolving: doesn't install both architectures of pkg by filename (RhBug:1100946) (Jan Silhan) +- refactor: put CleanCommand in its own module. (Ales Kozumplik) +- cli: avoid 'Error: None' output on malformed CLI commands. (Ales Kozumplik) +- remove the special SIGQUIT handler. (Ales Kozumplik) +- api: In Repo(), cachedir is a required argument. (Ales Kozumplik) +- api: better describe how Repos should be created, example. (RhBug:1117789) (Ales Kozumplik) +- Base._conf lasts the lifetime of Base and can be passed via constructor. (Ales Kozumplik) +- doc: faq: having Yum and DNF installed at the same time. (Ales Kozumplik) +- remove: protected_packages config option, it has been ignored. (Ales Kozumplik) +- fix: misleading error message when no repo is enabled. (Ales Kozumplik) + +* Wed Jul 16 2014 Aleš Kozumplík - 0.5.4-1 +- pkg name from rpm transaction callback is in Unicode (RhBug:1118796) (Jan Silhan) +- packaging: python3-dnf depends on dnf. (RhBug:1119032) (Ales Kozumplik) +- Ship /usr/bin/dnf-3 to run DNF under Py3. (RhBug:1117678) (Ales Kozumplik) +- packaging: own /etc/dnf/plugins. (RhBug:1118178) (Ales Kozumplik) +- fix: pluginconfpath is a list. (Ales Kozumplik) +- cosmetic: use classmethod as a decorator in config.py. (Ales Kozumplik) +- cleanup: imports in dnf.cli.output (Ales Kozumplik) +- lint: straightforward lint fixes in dnf.cli.output. (Ales Kozumplik) +- Repo.__setattr__ has to use the parsed value. (Ales Kozumplik) +- Repo priorities. (RhBug:1048973) (Ales Kozumplik) +- repo: simplify how things are propagated to repo.hawkey_repo. (Ales Kozumplik) +- refactor: concentrate Repo.hawkey_repo construction in Repo.__init__(). (Ales Kozumplik) +- bash-completion: Update command and option lists, sort in same order as --help (Ville Skyttä) +- bash-completion: Use grep -E instead of deprecated egrep (Ville Skyttä) +- output: fixed identation of info command output (Jan Silhan) +- i18n: calculates right width of asian utf-8 strings (RhBug:1116544) (Jan Silhan) +- transifex update + renamed po files to Fedora conventions (Jan Silhan) +- remove: CLI: --randomwait (Ales Kozumplik) +- cli: fix: --installroot has to be used with --releasever (RhBug:1117293) (Ales Kozumplik) +- Base.reset(goal=True) also resets the group persistor (RhBug:1116839) (Ales Kozumplik) +- tests: fix failing DistroSync.test_distro_sync(). (Ales Kozumplik) +- logging: RPM transaction markers are too loud. (Ales Kozumplik) +- logging: silence drpm a bit. (Ales Kozumplik) +- logging: put timing functionality into one place. (Ales Kozumplik) +- repolist: fix traceback with disabled repos. (RhBug:1116845) (Ales Kozumplik) +- refactor: cleanups in repolist. (Ales Kozumplik) +- lint: remove some unused imports. (Ales Kozumplik) +- cli: break out the repolsit command into a separate module. (Ales Kozumplik) +- does not crash with non-ascii user name (RhBug:1108908) (Jan Silhan) +- doc: document 'pluginpath' configuration option. (RhBug:1117102) (Ales Kozumplik) +- Spelling fixes (Ville Skyttä) +- cli: Fix software name in --version help (Ville Skyttä) +- doc: ip_resolve documented at two places. remove one. (Ales Kozumplik) + +* Thu Jul 3 2014 Aleš Kozumplík - 0.5.3-1 +- packaging: bump hawkey dep to 0.4.17. (Ales Kozumplik) +- api: remove Base.select_group(). (Ales Kozumplik) +- tests: cleanup our base test case classes a bit. (Ales Kozumplik) +- Add DNF itself among the protected packages. (Ales Kozumplik) +- api: plugins: add the resolved() hook. (Ales Kozumplik) +- api: expose Transaction introspecting in the API. (RhBug:1067156) (Ales Kozumplik) +- api: add basic documentation for dnf.package.Package. (Ales Kozumplik) +- tests: cosmetic: conf.protected_packages is ignored, drop it in FakeConf. (Ales Kozumplik) +- cli: simplify exception handling more. (Ales Kozumplik) +- Fixed a minor typo in user_faq - 'intall' should be 'install' (Martin Preisler) +- fixed encoding of parsed config line (RhBug:1110800) (Jan Silhan) +- syntax: replaced tab with spaces (Jan Silhan) +- doc: acknowledge the existence of plugins on the man page (RhBug:1112669) (Ales Kozumplik) +- improve the 'got root?' message of why a transaction couldn't start. (RhBug:1111569) (Ales Kozumplik) +- traceback in Base.do_transaction. to_utf8() is gone since 06fb280. (Ales Kozumplik) +- fix traceback from broken string formatting in _retrievePublicKey(). (RhBug:1111997) (Ales Kozumplik) +- doc: replace Yum with DNF in command_ref.rst (Viktor Ashirov) +- Fix a missing s in the title (mscherer) +- api: add dnf.rpm.detect_releasever() (Ales Kozumplik) +- Detect distroverpkg from 'system-release(release)' (RhBug:1047049) (Ales Kozumplik) +- bulid: add dnf/conf to cmake. (Ales Kozumplik) +- lint: clean up most lint messages in dnf.yum.config (Ales Kozumplik) +- remove: couple of dead-code methods in dnf.yum.config. (Ales Kozumplik) +- api: document client's responsibility to preset the substitutions. (RhBug:1104757) (Ales Kozumplik) +- move: rpmUtils -> rpm. (Ales Kozumplik) +- refactor: move yumvar out into its proper module dnf.conf.substitutions. (Ales Kozumplik) +- refactor: turn dnf.conf into a package. (Ales Kozumplik) +- doc: api_base.rst pointing to nonexistent method. (Ales Kozumplik) +- remove: some logging from Transaction.populate_rpm_ts(). (Ales Kozumplik) +- Update cli_vs_yum.rst (James Pearson) +- api: doc: queries relation specifiers, with an example. (RhBug:1105009) (Ales Kozumplik) +- doc: phrasing in ip_resolve documentation. (Ales Kozumplik) +- cli: refactored transferring cmdline options to conf (Jan Silhan) +- cli: added -4/-6 option for using ipv4/ipv6 connection (RhBug:1093420) (Jan Silhan) +- cosmetic: empty set inicialization (Jan Silhan) +- repo: improve the RepoError message to include URL. (Ales Kozumplik) +- remove: dnf.yum.config.writeRawRepoFile(). (Ales Kozumplik) +- remove: bunch of (now) blank config options. (Ales Kozumplik) +- removed unique function (Jan Silhan) +- tests: mock.assert_has_calls() enforces its iterable arguments in py3.4. (Ales Kozumplik) +- logging: improve how repolist logs the total number of packages. (Ales Kozumplik) +- logging: Base.close() should not log to the terminal. (Ales Kozumplik) + +* Wed May 28 2014 Aleš Kozumplík - 0.5.2-1 +- doc: packaging: add license block to each .rst. (Ales Kozumplik) +- cosmetic: replaced yum with dnf in comment (Jan Silhan) +- takes non-ascii cmd line input (RhBug:1092777) (Jan Silhan) +- replaced 'unicode' conversion functions with 'ucd' (RhBug:1095861) (Jan Silhan) +- using write_to_file py2/py3 compatibility write function (Jan Silhan) +- encoding: all encode methods are using utf-8 coding instead of default ascii (Jan Silhan) +- fixed rpmbuild warning of missing file (Jan Silhan) +- transifex update (Jan Silhan) +- fixed typos in comments (Jan Silhan) +- Drop --debugrepodata and susetags generation with it. (Ales Kozumplik) +- doc: document --debugsolver. (Ales Kozumplik) +- fix: 'dnf repo-pkgs' failures (RhBug:1092006) (Radek Holy) +- lint: make dnf_pylint take '-s' that suppresses line/column numbers. (Ales Kozumplik) +- doc: cli_vs_yum: we do not promote installs to the obsoleting package. (RhBug:1096506) (Ales Kozumplik) +- dealing with installonlies, we always need RPMPROB_FILTER_OLDPACKAGE (RhBug:1095580) (Ales Kozumplik) +- transifex update (Jan Silhan) +- arch: recognize noarch as noarch's basearch. (RhBug:1094594) (Ales Kozumplik) +- pylint: clean up dnf.repo. (Ales Kozumplik) +- sslverify: documentation and bumped librepo require. (Ales Kozumplik) +- repos: support sslverify setting. (RhBug:1076045) (Ales Kozumplik) +- search: exact matches should propagate higher. (RhBug:1093888) (Ales Kozumplik) +- refactor: concentrate specific search functionality in commands.search. (Ales Kozumplik) +- refactor: SearchCommand in its own file. (Ales Kozumplik) +- pylint: fix around one hundred pylint issues in dnf.base. (Ales Kozumplik) +- pylint: add simple pylint script (Ales Kozumplik) +- autoerase: write out the debugdata used to calculate redundant packages. (Ales Kozumplik) +- cosmetic: fix pylint comment in test_group.py. (Ales Kozumplik) +- refactor: err_mini_usage() is public. (Ales Kozumplik) +- refactor: fix several pylint errors in dnf.cli.commands.group. (Ales Kozumplik) +- fix: 'dnf remove' is deprecated so autoremove should be autoerase. (Ales Kozumplik) +- doc: command_ref: remove the deprecated aliases from the initial list. (Ales Kozumplik) +- Add autoremove command. (RhBug:963345) (Ales Kozumplik) +- refactor: Base.push_userinstalled() is public. (Ales Kozumplik) +- Remove sudo from dnf-completion.bash RhBug:1073457 (Elad Alfassa) +- exclude switch takes as a parameter (Jan Silhan) +- using nevra glob query during list command (RhBug:1083679) (Jan Silhan) +- removed rpm.RPMPROB_FILTER_REPLACEOLDFILES filter flag (Jan Silhan) +- test: changed tests according to new distro-sync behavior (Jan Silhan) +- packaging: cosmetic: copyright years in bin/dnf. (Ales Kozumplik) +- bin/dnf: run the python interpreter with -OO. (Ales Kozumplik) + +* Fri May 2 2014 Aleš Kozumplík - 0.5.1-1 +- drpm: output stats (RhBug:1065882) (Ales Kozumplik) +- refactor: architectures. (Ales Kozumplik) +- cli: be lot less verbose about dep processing. (Ales Kozumplik) +- groups: do not error out if group install/remove produces no RPM transaction. (Ales Kozumplik) +- fix: do not traceback on comps remove operations if proper pkg reasons can not be found. (Ales Kozumplik) +- fix: tracebacks in 'group remove ...' (Ales Kozumplik) +- groups: move all the logic of persistor saving from main.py to Base. (Ales Kozumplik) +- groups: auto-saving the groups persistor. (RhBug:1089864) (Ales Kozumplik) +- transifex update (Jan Silhan) +- remove: profiling code from cli.main. (Ales Kozumplik) +- remove: removal of dead code (Miroslav Suchý) +- doc: changes to rhbug.py to work on readthedocs.org. (Ales Kozumplik) +- doc: build the documentation without any dependencies (on DNF or anything else). (Ales Kozumplik) +- doc: make clear where one should expect bin/dnf (Miroslav Suchý) +- abrt: disable abrt for 'dnf makecache timer' run from systemd.service. (RhBug:1081753) (Ales Kozumplik) +- remove: stray itertools import from group.py. (Ales Kozumplik) + +* Wed Apr 23 2014 Aleš Kozumplík - 0.5.0-1 +- doc: fix formatting in api_cli.rst. (Ales Kozumplik) +- doc: document operation of 'group upgrade'. (Ales Kozumplik) +- comps: ensure only packages of 'group' reason get deleted on 'group erase'. (Ales Kozumplik) +- comps: store 'group' reason when installing a group-membering package. (Ales Kozumplik) +- Override Goal.get_reason(). (Ales Kozumplik) +- Add dnf.goal.Goal deriving from hawkey.Goal. (Ales Kozumplik) +- fix: encoding of yumdb directory names in py3. (Ales Kozumplik) +- tests: clean up the functions that load seeded comps a bit. (Ales Kozumplik) +- remove: cli._*aybeYouMeant(). (Ales Kozumplik) +- simplify groups/envs API methods in Base a lot. (Ales Kozumplik) +- tests: add test for Base._translate_comps_pkg_types() (Ales Kozumplik) +- refactor: move the group listing etc. methods() away from Base into GroupCommand. (Ales Kozumplik) +- api: add group.upgrade opration to Base and CLI (RhBug:1029022) (Ales Kozumplik) +- remove: OriginalGroupPersistor. (Ales Kozumplik) +- groups: store format version of the groups db. (Ales Kozumplik) +- groups: saving the persistent data. (Ales Kozumplik) +- refactor: extract out the transactioning part of _main(). (Ales Kozumplik) +- groups: Integrate the redone components with Base. (Ales Kozumplik) +- Add comps Solver. (Ales Kozumplik) +- groups: redo the GroupPersistor class. (Ales Kozumplik) +- doc: faq: why we don't check for root. (RhBug:1088166) (Ales Kozumplik) +- cosmetic: reordered import statements (Jan Silhan) +- added --refresh option (RhBug:1064226) (Jan Silhan) +- added forgotten import (Jan Silhan) +- fixed import errors after yum/i18n.py removal (Jan Silhan) +- removed to_utf8 from yum/i18n.py (Jan Silhan) +- removed to_str from yum/i18n.py (Jan Silhan) +- removed utf8_text_fill from yum/i18n.py (Jan Silhan) +- removed utf8_width from yum/i18n.py (Jan Silhan) +- removed utf8_width_fill from yum/i18n.py (Jan Silhan) +- removed to_unicode from yum/i18n.py (Jan Silhan) +- make all strings unicode_literals implicitly (Jan Silhan) +- moved _, P_ to dnf/i18n.py (Jan Silhan) +- removed utf8_valid from yum/i18n.py (Jan Silhan) +- removed str_eq from yum/i18n.py (Jan Silhan) +- removed exception2msg from yum/i18n.py (Jan Silhan) +- removed dummy_wrapper from yum/i18n.py (Jan Silhan) +- cosmetics: leave around the good things from 660c3e5 (documentation, UT). (Ales Kozumplik) +- Revert "fix: provides are not recognized for erase command. (RhBug:1087063)" (Ales Kozumplik) +- fix: provides are not recognized for erase command. (RhBug:1087063) (Ales Kozumplik) +- test: fix UsageTest test, so it work without dnf is installed on the system PEP8 cleanup (Tim Lauridsen) +- cleanup: getSummary() and getUsage() can be dropped entirely now. (Ales Kozumplik) +- test: use Command.usage & Command.summary API in unittest (Tim Lauridsen) +- show plugin commands in separate block api: add new public Command.usage & Command.summary API cleanup: make Commands (Tim Lauridsen) +- tests: move libcomps test to a separate test file. (Ales Kozumplik) +- refactor: put DistoSyncCommand into its own file (Tim Lauridsen) +- refactor: commands.group: _split_extcmd is a static method. (Ales Kozumplik) +- GroupsCommand: make the way comps are searched more robust. (RhBug:1051869) (Ales Kozumplik) +- tests: move GroupCommand tests to a more proper place. (Ales Kozumplik) +- fix leak: Base.__del__ causes GC-uncollectable circles. (Ales Kozumplik) +- gruops: 'list' and similar commands should run without root. (RhBug:1080331) (Ales Kozumplik) +- refactor: conf is given to Output on instantiation. (Ales Kozumplik) +- remove: Command.done_command_once and Command.hidden. (Ales Kozumplik) +- [doc] improve documentation of '--best' (RhBug:1084553) (Ales Kozumplik) +- api: Command.base and Command.cli are API attributes. (Ales Kozumplik) +- demands: similarly to 78661a4, commands should set the exit success_exit_status directly. (Ales Kozumplik) +- demands: commands requiring resolving dymamically need to set the demand now. (Ales Kozumplik) +- doc: typo in group doc. (RhBug:1084139) (Ales Kozumplik) +- api: Base.resolve() takes allow_erasing. (RhBug:1073859) (Ales Kozumplik) +- refactor: OptionParser._checkAbsInstallRoot is static. (Ales Kozumplik) +- option_parser: remove base dependency. (Ales Kozumplik) +- move: dnf.cli.cli.OptionParser -> dnf.cli.option_parser.OptionParser. (Ales Kozumplik) +- doc: 'clean packages' incorrectly mentions we do not delete cached packages. (RhBug:1083767) (Ales Kozumplik) +- fix: TypeError in dnf history info (RHBug: #1082230) (Tim Lauridsen) +- Start new version: 0.5.0. (Ales Kozumplik) +- remove: instance attrs of Base, namely cacheonly. (Ales Kozumplik) +- tests: remove: support.MockCli. (Ales Kozumplik) +- tests: fix locale independence. (Radek Holy) +- cleanups in cli.OptionParser. (Ales Kozumplik) +- fix: PendingDeprecationWarning from RPM in gpgKeyCheck(). (Ales Kozumplik) +- api: add Cli.demands.root_user (RhBug:1062889) (Ales Kozumplik) +- api: add Cli.demands and Command.config() to the API (RhBug:1062884) (Ales Kozumplik) +- Integrate DemandSheet into CLI. (Ales Kozumplik) +- Command.configure() takes the command arguments like run(). (Ales Kozumplik) +- Add dnf.cli.demand.DemandSheet. (Ales Kozumplik) +- remove: dead code for deplist, version and check-rpmdb commands. (Ales Kozumplik) +- sync with transifex (Jan Silhan) +- removed _enc method that did nothing without specspo (Jan Silhan) +- fixed local reinstall error (Jan Silhan) +- Fix Term.MODE setting under Python 3 in case of incapable tty stdout. (Radek Holy) +- tests: move Term tests to better file. (Radek Holy) +- refactor: move ReinstallCommand in its own module. (Ales Kozumplik) +- rename: yumbase (case insensitive) -> base. (Ales Kozumplik) +- fixed py3 error thrown by search command (Jan Silhan) +- fixed wrong named variable (Jan Silhan) +- fixed local downgrade error (Jan Silhan) +- doc: fix Package references that are ambiguous now. (Ales Kozumplik) +- fix: resource leak in yum.misc.checksum() under py3. (Ales Kozumplik) +- fix: leak: couple of files objects left open. (Ales Kozumplik) +- fix PendingDepreaction warning from rpm in _getsysver(). (Ales Kozumplik) +- repo: Repo.cachedir is not a list. (Ales Kozumplik) +- api: add Base.package_install et al. and Base.add_remote_rpm(). (RhBug:1079519) (Ales Kozumplik) +- tests: fix tests broken under foreign locale after 32818b2. (Ales Kozumplik) +- refactor: move install, downgrade and upgrade commands into separate modules. (Ales Kozumplik) +- tests: refactor: make Term tests more isolated. (Radek Holy) +- tests: fix terminfo capability independence. (Radek Holy) +- api: explain that Base is a context manager with a close(). (Ales Kozumplik) +- cosmetic: move stuff around in comps. (Ales Kozumplik) +- api: groups: add comps.Package, add group.package_iter(). (RhBug:1079932) (Ales Kozumplik) +- fixed installation of conflicted packages (RhBug:1061780) (Jan Silhan) +- removed never executed code based on _ts_saved_file variable (Jan Silhan) +- added logrotate script and ownership of log files to dnf (RhBug:1064211) (Jan Silhan) +- fixed: highlight characters broken under py3 (RhBug:1076884) (Jan Silhan) +- remove: base.deselectGroup(). it is not used. (Ales Kozumplik) +- tests: fix broken InstallMultilib.test_install_src_fails(). (Ales Kozumplik) +- groups: support manipulation with environments (RhBug:1063666) (Ales Kozumplik) +- add dnf.util.partition(). (Ales Kozumplik) +- refactor: RepoPersistor: use the global logger instead of an instance variable. (Ales Kozumplik) +- groups: besides installed groups also store persistently the environments. (Ales Kozumplik) +- rename: persistor.Groups -> ClonableDict. (Ales Kozumplik) +- doc: cli_vs_yum: typography in bandwidth limiting section. (Ales Kozumplik) +- doc: cli_vs_yum: we do not partially allow operations that install .srpm. (RhBug:1080489) (Ales Kozumplik) +- refactor: imports order in cli/commands/__init__.py. (Ales Kozumplik) +- refactor: groups: make all commands use _patterns2groups(). (Ales Kozumplik) +- kernel: remove kernel-source from const.INSTALLONLYPKGS. (Ales Kozumplik) +- build: 0.4.19-1 (Ales Kozumplik) +- New version: 0.4.19 (Ales Kozumplik) +- downloads: bump number of downloaded files on a skip. (RhBug:1079621) (Ales Kozumplik) +- packaging: add dnf.cli.commands to the installation. (Ales Kozumplik) +- refactor: put GroupCommand into its separate module. (Ales Kozumplik) +- rename: make cli.commands a subpackage. (Ales Kozumplik) +- AUTHORS: added Albert. (Ales Kozumplik) +- test: fixed CacheTest.test_noroot() when running as root (Albert Uchytil) +- AUTHORS: added Tim. (Ales Kozumplik) +- fixes TypeError: '_DownloadErrors' object is not iterable (RhBug:1078832) (Tim Lauridsen) +- fixed not including .mo files (Jan Silhan) +- comps: _by_pattern() no longer does the comma splitting. (Ales Kozumplik) + +* Mon Mar 24 2014 Aleš Kozumplík - 0.4.19-1 +- downloads: bump number of downloaded files on a skip. (RhBug:1079621) (Ales Kozumplik) +- packaging: add dnf.cli.commands to the installation. (Ales Kozumplik) +- refactor: put GroupCommand into its separate module. (Ales Kozumplik) +- rename: make cli.commands a subpackage. (Ales Kozumplik) +- AUTHORS: added Albert. (Ales Kozumplik) +- test: fixed CacheTest.test_noroot() when running as root (Albert Uchytil) +- AUTHORS: added Tim. (Ales Kozumplik) +- fixes TypeError: '_DownloadErrors' object is not iterable (RhBug:1078832) (Tim Lauridsen) +- fixed not including .mo files (Jan Silhan) +- comps: _by_pattern() no longer does the comma splitting. (Ales Kozumplik) +- including .mo files correctly (Jan Silhan) +- tests: fix locale independence. (Radek Holy) +- remove: unused trashy methods in dnf.yum.misc. (Ales Kozumplik) +- persistor: do not save Groups if it didn't change (RhBug:1077173) (Ales Kozumplik) +- tests: simplify the traceback logging. (Ales Kozumplik) +- main: log IO errors etc. thrown even during Base.__exit__. (Ales Kozumplik) +- logging: do not log IOError tracebacks in verbose mode. (Ales Kozumplik) +- refactor: move out main._main()'s inner error handlers. (Ales Kozumplik) +- added gettext as a build dependency for translation files (Jan Silhan) +- translation: updated .pot file and fetched fresh .po files from transifex (Jan Silhan) +- removed redundant word from persistor translation (Jan Silhan) +- translation: show relative path in generated pot file (Jan Silhan) +- refactor: replaced type comparisons with isinstance (Jan Silhan) +- translation: added mo files generation and including them in rpm package (Jan Silhan) +- removed unused imports in base.py (Jan Silhan) +- doc: typo in Base.group_install(). (Ales Kozumplik) + +* Mon Mar 17 2014 Aleš Kozumplík - 0.4.18-1 +- api: drop items deprecated since 0.4.9 or earlier. (Ales Kozumplik) +- api: deprecate Base.select_group() (Ales Kozumplik) +- doc: document the group marking operations. (Ales Kozumplik) +- api: add Base.group_install() with exclude capability. (Ales Kozumplik) +- groups: recognize 'mark install' instead of 'mark-install'. (Ales Kozumplik) +- Allow installing optional packages from a group. (RhBug:1067136) (Ales Kozumplik) +- groups: add installing groups the object marking style. (Ales Kozumplik) +- groups: add Base.group_remove(). (Ales Kozumplik) +- groups: add support for marking/unmarking groups. (Ales Kozumplik) +- groups: add dnf.persistor.GroupPersistor(), to store the installed groups. (Ales Kozumplik) +- logging: log plugin import tracebacks on the subdebug level. (Ales Kozumplik) +- rename: dnf.persistor.Persistor -> RepoPersistor. (Ales Kozumplik) +- doc: update README and FAQ with the unabbreviated name. (Ales Kozumplik) +- groups: fix grouplist crashes with new libcomps. (Ales Kozumplik) +- Do not terminate for unreadable repository config. (RhBug:1071212) (Ales Kozumplik) +- cli: get rid of ridiculous slashes and the file:// scheme on config read fails. (Ales Kozumplik) +- repo: log more than nothing about a remote repo MD download. (Ales Kozumplik) +- drpm: fallback to .rpm download on drpm rebuild error. (RhBug:1071501) (Ales Kozumplik) +- remove: Base.download_packages()' inner function mediasort(). (Ales Kozumplik) +- tests: tidy up the imports, in particular import mock from support. (Ales Kozumplik) +- changed documentation of distro-sync command (Jan Silhan) +- added distro-sync explicit packages support (RhBug:963710) (Jan Silhan) +- renamed testcase to distro_sync_all (Jan Silhan) +- Minor spelling (Arjun Temurnikar) +- i18n: translate repo sync error message. (Ales Kozumplik) +- add support for ppc64le (Dennis Gilmore) +- there is no arch called arm64 it is aarch64 (Dennis Gilmore) + +* Wed Mar 5 2014 Aleš Kozumplík - 0.4.17-1 +- doc: in the faq, warn users who might install rawhide packages on stable. (RhBug:1071677) (Ales Kozumplik) +- cli: better format the download errors report. (Ales Kozumplik) +- drpm: properly report applydeltarpm errors. (RhBug:1071501) (Ales Kozumplik) +- fixed Japanese translatated message (RhBug:1071455) (Jan Silhan) +- generated and synchronized translations with transifex (Jan Silhan) +- added transifex support to cmake (gettext-export, gettext-update) (Jan Silhan) +- api: expose RepoDict.get_matching() and RepoDict.all() (RhBug:1071323) (Ales Kozumplik) +- api: add Repo.set_progress_bar() to the API. (Ales Kozumplik) +- tests: test_cli_progress uses StringIO to check the output. (Ales Kozumplik) +- downloads: fix counting past 100% on mirror failures (RhBug:1070598) (Ales Kozumplik) +- repo: log callback calls to librepo. (Ales Kozumplik) +- Add repository-packages remove-or-reinstall command. (Radek Holy) +- Support negative filtering by new repository name in Base.reinstall. (Radek Holy) +- Support removal N/A packages in Base.reinstall. (Radek Holy) +- Add repository-packages remove command. (Radek Holy) +- refactor: Reduce amount of code in repository-packages subcommands. (Radek Holy) +- Support filtering by repository name in Base.remove. (Radek Holy) +- remove: BaseCli.erasePkgs (Radek Holy) +- Add repository-packages reinstall command. (Radek Holy) +- exceptions: improve empty key handling in DownloadError.__str__(). (Ales Kozumplik) +- downloads: fix fatal error message return value from download_payloads() (RhBug:1071518) (Ales Kozumplik) +- fixes problem with TypeError in Base.read_comps() in python3 (RhBug:1070710) (Tim Lauridsen) +- fix read_comps: not throwing exceptions when repo has no repodata (RhBug:1059704) (Jan Silhan) +- not decompressing groups when --cacheonly option is set (RhBug:1058224) (Jan Silhan) +- added forgotten import (Jan Silhan) +- Add repository-packages move-to command. (Radek Holy) +- Add repository-packages reinstall-old command. (Radek Holy) +- Support filtering by repository name in Base.reinstall. (Radek Holy) +- tests: test effects instead of mock calls. (Radek Holy) +- Wrap some recently added long lines. (Radek Holy) +- remove: BaseCli.reinstallPkgs (Radek Holy) +- repos: repos can never expire. (RhBug:1069538) (Ales Kozumplik) +- build: rebuild with 9d95442 (updated summaries_cache). (Ales Kozumplik) +- doc: update summaries_cache. (Ales Kozumplik) + +* Wed Feb 26 2014 Aleš Kozumplík - 0.4.16-1 +- fix: ensure MDPayload always has a valid progress attribute. (RhBug:1069996) (Ales Kozumplik) +- refactor: Move repo-pkgs upgrade-to to a standalone class instead of reusing the UpgradeToCommand. (Radek Holy) +- remove: BaseCli.updatePkgs (Radek Holy) +- refactor: Remove the reference to updatePkgs from UpgradeSubCommand. (Radek Holy) +- refactor: Remove the reference to updatePkgs from UpgradeCommand. (Radek Holy) +- refactor: Move repo-pkgs upgrade to a standalone class instead of reusing the UpgradeCommand. (Radek Holy) +- remove: BaseCli.installPkgs (Radek Holy) +- refactor: Remove the reference to installPkgs from InstallSubCommand. (Radek Holy) +- refactor: Remove the reference to installPkgs from InstallCommand. (Radek Holy) +- refactor: Move repo-pkgs install to a standalone class instead of reusing the InstallCommand. (Radek Holy) +- Revert "Support filtering by repository name in install_groupie." (Radek Holy) +- Revert "Support filtering by repository name in Base.select_group." (Radek Holy) +- Drop group filtering by repository name from installPkgs. (Radek Holy) +- Drop "repo-pkgs install @Group" support. (Radek Holy) +- refactor: Move CheckUpdateCommand.check_updates to BaseCli. (Radek Holy) +- refactor: Move repo-pkgs check-update to a standalone class instead of reusing the CheckUpdateCommand. (Radek Holy) +- refactor: Move repo-pkgs list to a standalone class instead of reusing the ListCommand. (Radek Holy) +- tests: Add tests of repo-pkgs info against the documentation. (Radek Holy) +- Fix "repo-pkgs info installed" behavior with respect to the documentation. (Radek Holy) +- refactor: Move MockBase methods to BaseStubMixin. (Radek Holy) +- refactor: Move repo-pkgs info to a standalone class instead of reusing the InfoCommand. (Radek Holy) +- refactor: Move InfoCommand._print_packages to BaseCli.output_packages. (Radek Holy)