Compare commits

...

7 Commits

Author SHA1 Message Date
eabdullin bd2abd9099 Import from AlmaLinux stable repository 2024-05-31 17:33:35 +00:00
eabdullin 79a532a9b3 import UBI dnf-4.7.0-20.el8 2024-05-22 14:29:50 +00:00
eabdullin 15037b66f4 import UBI dnf-4.7.0-19.el8 2023-11-14 20:02:36 +00:00
CentOS Sources 6fcd77d89f import dnf-4.7.0-16.el8_8 2023-05-16 07:34:42 +00:00
CentOS Sources 32f838c3cf import dnf-4.7.0-11.el8 2022-11-08 06:58:44 +00:00
CentOS Sources ac0151f877 import dnf-4.7.0-8.el8 2022-05-10 10:05:09 +00:00
CentOS Sources 6779ba8005 import dnf-4.7.0-4.el8 2021-12-02 16:37:23 +00:00
70 changed files with 30669 additions and 16869 deletions

View File

@ -1 +0,0 @@
5941a49cfd466aeed4ec882a33647912c2a89245 SOURCES/dnf-4.4.2.tar.gz

2
.gitignore vendored
View File

@ -1 +1 @@
SOURCES/dnf-4.4.2.tar.gz
SOURCES/dnf-4.7.0.tar.gz

View File

@ -0,0 +1,26 @@
From 423c987b6b14ec0a6277181ac7c038b50033296d Mon Sep 17 00:00:00 2001
From: Pavla Kratochvilova <pkratoch@redhat.com>
Date: Wed, 19 May 2021 12:58:30 +0200
Subject: [PATCH] Set top-level directory for unittest
In some build environments, the top-level directory is not added to
the sys.path and the tests fail. This fixes the issue.
---
tests/CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index b15cc62b..dedc46fd 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,6 +1,6 @@
ADD_TEST(
NAME test
- COMMAND ${PYTHON_EXECUTABLE} -m unittest discover -s tests
+ COMMAND ${PYTHON_EXECUTABLE} -m unittest discover -s tests -t ${PROJECT_SOURCE_DIR}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
# For libdnf built with sanitizers, has no effect otherwise.
--
2.35.1

View File

@ -1,45 +0,0 @@
From 66e08009b8254462cb2c454ff2320355633c20d6 Mon Sep 17 00:00:00 2001
From: Nicola Sella <nsella@redhat.com>
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

View File

@ -1,26 +0,0 @@
From c2e4901cec947e5be2e5ff5afa22691841d00bdc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hr=C3=A1zk=C3=BD?= <lhrazky@redhat.com>
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:

View File

@ -0,0 +1,36 @@
From 8522c4651678097157fd9f133a451c892021d30b Mon Sep 17 00:00:00 2001
From: Alexander Kanavin <alex.kanavin@gmail.com>
Date: Tue, 4 May 2021 22:03:30 +0200
Subject: [PATCH] dnf/rpm/miscutils.py: fix usage of _()
Specifically:
- an import of _ was missing
- _ was reused for a different purpose
---
dnf/rpm/miscutils.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dnf/rpm/miscutils.py b/dnf/rpm/miscutils.py
index 235aaf28..7e33d4c4 100644
--- a/dnf/rpm/miscutils.py
+++ b/dnf/rpm/miscutils.py
@@ -22,6 +22,7 @@ import subprocess
import logging
from dnf.i18n import ucd
+from dnf.i18n import _
from shutil import which
@@ -46,7 +47,7 @@ def _verifyPkgUsingRpmkeys(package, installroot):
env={'LC_ALL': 'C'},
stdout=subprocess.PIPE,
cwd='/') as p:
- data, _ = p.communicate()
+ data, err = p.communicate()
if p.returncode != 0 or data != (package.encode('ascii', 'strict') + b': digests signatures OK\n'):
return 0
else:
--
2.35.1

View File

@ -0,0 +1,56 @@
From f109c57ab18d8b1a80e707df3c3f7ad8930bdd42 Mon Sep 17 00:00:00 2001
From: Demi Marie Obenour <demi@invisiblethingslab.com>
Date: Tue, 27 Apr 2021 21:07:19 -0400
Subject: [PATCH] Pass the package to rpmkeys stdin
This avoids having to compute the expected stdout value, which will
always be the constant "-: digests signatures OK\n".
---
dnf/rpm/miscutils.py | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/dnf/rpm/miscutils.py b/dnf/rpm/miscutils.py
index 7e33d4c4..5f2621c2 100644
--- a/dnf/rpm/miscutils.py
+++ b/dnf/rpm/miscutils.py
@@ -29,7 +29,8 @@ from shutil import which
logger = logging.getLogger('dnf')
-def _verifyPkgUsingRpmkeys(package, installroot):
+def _verifyPkgUsingRpmkeys(package, installroot, fdno):
+ os.lseek(fdno, 0, os.SEEK_SET)
rpmkeys_binary = '/usr/bin/rpmkeys'
if not os.path.isfile(rpmkeys_binary):
rpmkeys_binary = which("rpmkeys")
@@ -40,15 +41,16 @@ def _verifyPkgUsingRpmkeys(package, installroot):
logger.critical(_('Cannot find rpmkeys executable to verify signatures.'))
return 0
- args = ('rpmkeys', '--checksig', '--root', installroot, '--define', '_pkgverify_level all', '--', package)
+ args = ('rpmkeys', '--checksig', '--root', installroot, '--define', '_pkgverify_level all', '-')
with subprocess.Popen(
args=args,
executable=rpmkeys_binary,
env={'LC_ALL': 'C'},
+ stdin=fdno,
stdout=subprocess.PIPE,
cwd='/') as p:
data, err = p.communicate()
- if p.returncode != 0 or data != (package.encode('ascii', 'strict') + b': digests signatures OK\n'):
+ if p.returncode != 0 or data != b'-: digests signatures OK\n':
return 0
else:
return 1
@@ -85,7 +87,7 @@ def checkSig(ts, package):
if siginfo == '(none)':
value = 4
- elif "Key ID" in siginfo and _verifyPkgUsingRpmkeys(package, ts.ts.rootDir):
+ elif "Key ID" in siginfo and _verifyPkgUsingRpmkeys(package, ts.ts.rootDir, fdno):
value = 0
else:
raise ValueError('Unexpected return value %r from hdr.sprintf when checking signature.' % siginfo)
--
2.35.1

View File

@ -1,567 +0,0 @@
From 9ed390d08a9f2b66f4e352532fa526fc64e329d4 Mon Sep 17 00:00:00 2001
From: Marek Blaha <mblaha@redhat.com>
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 <mblaha@redhat.com>
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 <mblaha@redhat.com>
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

View File

@ -1,130 +0,0 @@
From df64fd36d7fefe39a96fea3f41e35785bebd37ec Mon Sep 17 00:00:00 2001
From: Marek Blaha <mblaha@redhat.com>
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 <mblaha@redhat.com>
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
'''

View File

@ -0,0 +1,174 @@
From 9798e9ee85f1ba39c816fa08fd3d6168cc8b29e8 Mon Sep 17 00:00:00 2001
From: Demi Marie Obenour <demi@invisiblethingslab.com>
Date: Fri, 9 Apr 2021 13:03:03 -0400
Subject: [PATCH] Use rpmkeys alone to verify signature
This avoids having to actually parse the package to check its signature,
which reduces attack surface. If the output of rpmkeys cannot be
parsed, we assume the package is corrupt (the most likely cause).
---
dnf/rpm/miscutils.py | 126 ++++++++++++++++++++++---------------------
1 file changed, 66 insertions(+), 60 deletions(-)
diff --git a/dnf/rpm/miscutils.py b/dnf/rpm/miscutils.py
index 5f2621c2..9d5b2860 100644
--- a/dnf/rpm/miscutils.py
+++ b/dnf/rpm/miscutils.py
@@ -13,47 +13,84 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
# Copyright 2003 Duke University
-from __future__ import print_function, absolute_import
-from __future__ import unicode_literals
+from __future__ import print_function, absolute_import, unicode_literals
-import rpm
import os
import subprocess
import logging
-
-from dnf.i18n import ucd
-from dnf.i18n import _
from shutil import which
+from dnf.i18n import _
-logger = logging.getLogger('dnf')
+_logger = logging.getLogger('dnf')
+_rpmkeys_binary = None
+def _find_rpmkeys_binary():
+ global _rpmkeys_binary
+ if _rpmkeys_binary is None:
+ _rpmkeys_binary = which("rpmkeys")
+ _logger.debug(_('Using rpmkeys executable at %s to verify signatures'),
+ _rpmkeys_binary)
+ return _rpmkeys_binary
-def _verifyPkgUsingRpmkeys(package, installroot, fdno):
- os.lseek(fdno, 0, os.SEEK_SET)
- rpmkeys_binary = '/usr/bin/rpmkeys'
- if not os.path.isfile(rpmkeys_binary):
- rpmkeys_binary = which("rpmkeys")
- logger.info(_('Using rpmkeys executable from {path} to verify signature for package: {package}.').format(
- path=rpmkeys_binary, package=package))
+def _process_rpm_output(data):
+ # No signatures or digests = corrupt package.
+ # There is at least one line for -: and another (empty) entry after the
+ # last newline.
+ if len(data) < 3 or data[0] != b'-:' or data[-1]:
+ return 2
+ seen_sig, missing_key, not_trusted, not_signed = False, False, False, False
+ for i in data[1:-1]:
+ if b': BAD' in i:
+ return 2
+ elif i.endswith(b': NOKEY'):
+ missing_key = True
+ elif i.endswith(b': NOTTRUSTED'):
+ not_trusted = True
+ elif i.endswith(b': NOTFOUND'):
+ not_signed = True
+ elif not i.endswith(b': OK'):
+ return 2
+ if not_trusted:
+ return 3
+ elif missing_key:
+ return 1
+ elif not_signed:
+ return 4
+ # we still check return code, so this is safe
+ return 0
- if not os.path.isfile(rpmkeys_binary):
- logger.critical(_('Cannot find rpmkeys executable to verify signatures.'))
- return 0
+def _verifyPackageUsingRpmkeys(package, installroot):
+ rpmkeys_binary = _find_rpmkeys_binary()
+ if rpmkeys_binary is None or not os.path.isfile(rpmkeys_binary):
+ _logger.critical(_('Cannot find rpmkeys executable to verify signatures.'))
+ return 2
- args = ('rpmkeys', '--checksig', '--root', installroot, '--define', '_pkgverify_level all', '-')
+ # "--define=_pkgverify_level all" enforces signature checking;
+ # "--define=_pkgverify_flags 0x0" ensures that all signatures and digests
+ # are checked.
+ args = ('rpmkeys', '--checksig', '--root', installroot, '--verbose',
+ '--define=_pkgverify_level all', '--define=_pkgverify_flags 0x0',
+ '-')
with subprocess.Popen(
args=args,
executable=rpmkeys_binary,
env={'LC_ALL': 'C'},
- stdin=fdno,
stdout=subprocess.PIPE,
- cwd='/') as p:
- data, err = p.communicate()
- if p.returncode != 0 or data != b'-: digests signatures OK\n':
- return 0
- else:
- return 1
+ cwd='/',
+ stdin=package) as p:
+ data = p.communicate()[0]
+ returncode = p.returncode
+ if type(returncode) is not int:
+ raise AssertionError('Popen set return code to non-int')
+ # rpmkeys can return something other than 0 or 1 in the case of a
+ # fatal error (OOM, abort() called, SIGSEGV, etc)
+ if returncode >= 2 or returncode < 0:
+ return 2
+ ret = _process_rpm_output(data.split(b'\n'))
+ if ret:
+ return ret
+ return 2 if returncode else 0
def checkSig(ts, package):
"""Takes a transaction set and a package, check it's sigs,
@@ -63,40 +100,9 @@ def checkSig(ts, package):
return 3 if the key is not trusted
return 4 if the pkg is not gpg or pgp signed"""
- value = 4
- currentflags = ts.setVSFlags(0)
- fdno = os.open(package, os.O_RDONLY)
+ fdno = os.open(package, os.O_RDONLY|os.O_NOCTTY|os.O_CLOEXEC)
try:
- hdr = ts.hdrFromFdno(fdno)
- except rpm.error as e:
- if str(e) == "public key not available":
- value = 1
- elif str(e) == "public key not trusted":
- value = 3
- elif str(e) == "error reading package header":
- value = 2
- else:
- raise ValueError('Unexpected error value %r from ts.hdrFromFdno when checking signature.' % str(e))
- else:
- # checks signature from an hdr
- string = '%|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:' \
- '{%|SIGGPG?{%{SIGGPG:pgpsig}}:{%|SIGPGP?{%{SIGPGP:pgpsig}}:{(none)}|}|}|}|'
- try:
- siginfo = hdr.sprintf(string)
- siginfo = ucd(siginfo)
-
- if siginfo == '(none)':
- value = 4
- elif "Key ID" in siginfo and _verifyPkgUsingRpmkeys(package, ts.ts.rootDir, fdno):
- value = 0
- else:
- raise ValueError('Unexpected return value %r from hdr.sprintf when checking signature.' % siginfo)
- except UnicodeDecodeError:
- pass
-
- del hdr
-
- os.close(fdno)
-
- ts.setVSFlags(currentflags) # put things back like they were before
+ value = _verifyPackageUsingRpmkeys(fdno, ts.ts.rootDir)
+ finally:
+ os.close(fdno)
return value
--
2.35.1

View File

@ -0,0 +1,37 @@
From 185330e5d5f5e07f40ed08c706fd997abffd5e78 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
Date: Thu, 3 Jun 2021 11:23:31 +0200
Subject: [PATCH] Lower _pkgverify_level to signature for signature checking
with rpmkeys
We don't want to be veryfing digests as well when checking signatures.
It would break legacy package installation in FIPS mode due to MD5
digest being unverifiable (see https://access.redhat.com/solutions/5221661)
Follow up for https://github.com/rpm-software-management/dnf/pull/1753
---
dnf/rpm/miscutils.py | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/dnf/rpm/miscutils.py b/dnf/rpm/miscutils.py
index 9d5b2860..46ef4754 100644
--- a/dnf/rpm/miscutils.py
+++ b/dnf/rpm/miscutils.py
@@ -66,11 +66,10 @@ def _verifyPackageUsingRpmkeys(package, installroot):
_logger.critical(_('Cannot find rpmkeys executable to verify signatures.'))
return 2
- # "--define=_pkgverify_level all" enforces signature checking;
- # "--define=_pkgverify_flags 0x0" ensures that all signatures and digests
- # are checked.
+ # "--define=_pkgverify_level signature" enforces signature checking;
+ # "--define=_pkgverify_flags 0x0" ensures that all signatures are checked.
args = ('rpmkeys', '--checksig', '--root', installroot, '--verbose',
- '--define=_pkgverify_level all', '--define=_pkgverify_flags 0x0',
+ '--define=_pkgverify_level signature', '--define=_pkgverify_flags 0x0',
'-')
with subprocess.Popen(
args=args,
--
2.35.1

View File

@ -1,150 +0,0 @@
From 8f3ce4868ac009976da7323ea39ebcd9a062e32d Mon Sep 17 00:00:00 2001
From: Jaroslav Mracek <jmracek@redhat.com>
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 <jmracek@redhat.com>
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 <jmracek@redhat.com>
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

View File

@ -0,0 +1,101 @@
From 6766d3af1993d48f5548746e68268e674e52bd1d Mon Sep 17 00:00:00 2001
From: Gary Leydon <gary.leydon@yale.edu>
Date: Fri, 21 May 2021 14:13:59 -0400
Subject: [PATCH] add default colors to documentation
---
doc/conf_ref.rst | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/doc/conf_ref.rst b/doc/conf_ref.rst
index ec5bac2a..fcaa0319 100644
--- a/doc/conf_ref.rst
+++ b/doc/conf_ref.rst
@@ -498,72 +498,72 @@ configuration file by your distribution to override the DNF defaults.
:ref:`color <color-label>`
Color of available packages that are older than installed packages.
- The option is used during list operations.
+ The option is used during list operations. Default is dim,cyan.
``color_list_available_install``
:ref:`color <color-label>`
Color of packages that are available for installation and none of their versions in installed.
- The option is used during list operations.
+ The option is used during list operations. Default is normal.
``color_list_available_reinstall``
:ref:`color <color-label>`
- Color of available packages that are identical to installed versions and are available for reinstalls.
+ Color of available packages that are identical to installed versions and are available for reinstalls. Default is bold,underline,green.
The option is used during list operations.
``color_list_available_upgrade``
:ref:`color <color-label>`
- Color of available packages that are newer than installed packages.
+ Color of available packages that are newer than installed packages. Default is bold,blue.
The option is used during list operations.
``color_list_installed_extra``
:ref:`color <color-label>`
Color of installed packages that do not have any version among available packages.
- The option is used during list operations.
+ The option is used during list operations. Default is bold,red.
``color_list_installed_newer``
:ref:`color <color-label>`
Color of installed packages that are newer than any version among available packages.
- The option is used during list operations.
+ The option is used during list operations. Default is bold,yellow.
``color_list_installed_older``
:ref:`color <color-label>`
Color of installed packages that are older than any version among available packages.
- The option is used during list operations.
+ The option is used during list operations. Default is bold.
``color_list_installed_reinstall``
:ref:`color <color-label>`
Color of installed packages that are among available packages and can be reinstalled.
- The option is used during list operations.
+ The option is used during list operations. Default is normal.
``color_search_match``
:ref:`color <color-label>`
- Color of patterns matched in search output.
+ Color of patterns matched in search output. Default is bold.
``color_update_installed``
:ref:`color <color-label>`
- Color of removed packages.
+ Color of removed packages. Default is normal.
This option is used during displaying transactions.
``color_update_local``
:ref:`color <color-label>`
Color of local packages that are installed from the @commandline repository.
- This option is used during displaying transactions.
+ This option is used during displaying transactions. Default is bold.
``color_update_remote``
:ref:`color <color-label>`
Color of packages that are installed/upgraded/downgraded from remote repositories.
- This option is used during displaying transactions.
+ This option is used during displaying transactions. Default is normal.
==============
--
2.35.1

View File

@ -1,30 +0,0 @@
From f3c254581bcb0591a543aee0c7e031c3c9d0a9a1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hr=C3=A1zk=C3=BD?= <lhrazky@redhat.com>
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

View File

@ -0,0 +1,21 @@
From 276e3b1d19bfad2a72f75ecbcce478e4f1e575db Mon Sep 17 00:00:00 2001
From: Gary Leydon <gary.leydon@yale.edu>
Date: Fri, 21 May 2021 14:16:21 -0400
Subject: [PATCH] add author
---
AUTHORS | 1 +
1 file changed, 1 insertion(+)
diff --git a/AUTHORS b/AUTHORS
index 1981dc4e..f8c9eb83 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -95,3 +95,4 @@ DNF CONTRIBUTORS
Vladan Kudlac <vladankudlac@gmail.com>
Will Woods <wwoods@redhat.com>
Furkan Karcıoğlu <krc440002@gmail.com>
+ Gary Leydon <gary.leydon@yale.edu>
--
2.35.1

View File

@ -1,507 +0,0 @@
From 6ed0458744090ab307da9d9118690372b2e66ca8 Mon Sep 17 00:00:00 2001
From: Jaroslav Mracek <jmracek@redhat.com>
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 <jmracek@redhat.com>
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)
+
+ # <package_name, set_of_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 <jmracek@redhat.com>
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):
# <package_name, set_of_spec>
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 <jmracek@redhat.com>
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:`\<module-spec>\ <specifying_modules-label>`... 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 <module_reset_command-label>` 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 <module_switch_to_command-label>` command for that.
``dnf [options] module update <module-spec>...``
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 <module-spec>...``
+ 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 <module_enable_command-label>` 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 <module_install_command-label>` 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 <module-spec>...``
Remove installed module profiles, including packages that were installed with the
:ref:`dnf module install <module_install_command-label>` command. Will not remove packages
@@ -1010,10 +1026,8 @@ Module subcommands take :ref:`\<module-spec>\ <specifying_modules-label>`... arg
of modular dependency issue the operation will be rejected. To perform the action anyway please use
\-\ :ref:`-skip-broken <skip-broken_option-label>` 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 <module_reset_command-label>` 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 <module_switch_to_command-label>` command for that.
.. _module_disable_command-label:
--
2.26.2
From 6b0b2b99e40c20706145e774626658825f5bc55d Mon Sep 17 00:00:00 2001
From: Jaroslav Mracek <jmracek@redhat.com>
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

View File

@ -0,0 +1,75 @@
From 5cfe87de2ecd645c2aa8b210bd98171e8dd72fe5 Mon Sep 17 00:00:00 2001
From: Gary Leydon <gary.leydon@yale.edu>
Date: Thu, 27 May 2021 11:52:42 -0400
Subject: [PATCH] update colors according to libdnf/libdnf/conf/ConfigMain.cpp
---
doc/conf_ref.rst | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/doc/conf_ref.rst b/doc/conf_ref.rst
index fcaa0319..016bd00c 100644
--- a/doc/conf_ref.rst
+++ b/doc/conf_ref.rst
@@ -498,13 +498,13 @@ configuration file by your distribution to override the DNF defaults.
:ref:`color <color-label>`
Color of available packages that are older than installed packages.
- The option is used during list operations. Default is dim,cyan.
+ The option is used during list operations. Default is magenta.
``color_list_available_install``
:ref:`color <color-label>`
Color of packages that are available for installation and none of their versions in installed.
- The option is used during list operations. Default is normal.
+ The option is used during list operations. Default is bold,cyan.
``color_list_available_reinstall``
:ref:`color <color-label>`
@@ -534,36 +534,36 @@ configuration file by your distribution to override the DNF defaults.
:ref:`color <color-label>`
Color of installed packages that are older than any version among available packages.
- The option is used during list operations. Default is bold.
+ The option is used during list operations. Default is yellow.
``color_list_installed_reinstall``
:ref:`color <color-label>`
Color of installed packages that are among available packages and can be reinstalled.
- The option is used during list operations. Default is normal.
+ The option is used during list operations. Default is cyan.
``color_search_match``
:ref:`color <color-label>`
- Color of patterns matched in search output. Default is bold.
+ Color of patterns matched in search output. Default is bold,magenta.
``color_update_installed``
:ref:`color <color-label>`
- Color of removed packages. Default is normal.
+ Color of removed packages. Default is red.
This option is used during displaying transactions.
``color_update_local``
:ref:`color <color-label>`
Color of local packages that are installed from the @commandline repository.
- This option is used during displaying transactions. Default is bold.
+ This option is used during displaying transactions. Default is green.
``color_update_remote``
:ref:`color <color-label>`
Color of packages that are installed/upgraded/downgraded from remote repositories.
- This option is used during displaying transactions. Default is normal.
+ This option is used during displaying transactions. Default is bold,green.
==============
--
2.35.1

View File

@ -0,0 +1,81 @@
From f5cb86b83aedaa18fd784d06d8f1479b9127c6f5 Mon Sep 17 00:00:00 2001
From: Marek Blaha <mblaha@redhat.com>
Date: Wed, 6 Oct 2021 09:43:37 +0200
Subject: [PATCH] Fix reporting irrecoverable errors on packages download
The original _irrecoverable property returns random dictionary - either
packages irrecoverable errors, or global fatal error or even new empty
dictionary. This makes it prone to programmer errors like:
errs._irrecoverable[pkg] = [err]
which may lead to setting the error into the newly created empty
dictionary instead of packages errors dictionary as intended.
I turned the property to method which I consider more clear.
---
dnf/base.py | 8 ++++----
dnf/repo.py | 9 ++++-----
2 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/dnf/base.py b/dnf/base.py
index 0949ddf8..b0a378c2 100644
--- a/dnf/base.py
+++ b/dnf/base.py
@@ -1165,8 +1165,8 @@ class Base(object):
progress.start(len(payloads), est_remote_size)
errors = dnf.repo._download_payloads(payloads, drpm)
- if errors._irrecoverable:
- raise dnf.exceptions.DownloadError(errors._irrecoverable)
+ if errors._irrecoverable():
+ raise dnf.exceptions.DownloadError(errors._irrecoverable())
remote_size = sum(errors._bandwidth_used(pload)
for pload in payloads)
@@ -1191,8 +1191,8 @@ class Base(object):
progress.start(len(payloads), est_remote_size)
errors = dnf.repo._download_payloads(payloads, drpm)
- if errors._irrecoverable:
- raise dnf.exceptions.DownloadError(errors._irrecoverable)
+ if errors._irrecoverable():
+ raise dnf.exceptions.DownloadError(errors._irrecoverable())
remote_size += \
sum(errors._bandwidth_used(pload) for pload in payloads)
diff --git a/dnf/repo.py b/dnf/repo.py
index b5c9849e..b454e981 100644
--- a/dnf/repo.py
+++ b/dnf/repo.py
@@ -112,7 +112,7 @@ def _download_payloads(payloads, drpm):
errs._skipped.add(pkg)
continue
pkg.repo._repo.expire()
- errs._irrecoverable[pkg] = [err]
+ errs._pkg_irrecoverable[pkg] = [err]
return errs
@@ -131,15 +131,14 @@ def _update_saving(saving, payloads, errs):
class _DownloadErrors(object):
def __init__(self):
- self._val_irrecoverable = {}
+ self._pkg_irrecoverable = {}
self._val_recoverable = {}
self._fatal = None
self._skipped = set()
- @property
def _irrecoverable(self):
- if self._val_irrecoverable:
- return self._val_irrecoverable
+ if self._pkg_irrecoverable:
+ return self._pkg_irrecoverable
if self._fatal:
return {'': [self._fatal]}
return {}
--
2.35.1

View File

@ -1,107 +0,0 @@
From de8bbccc4e035a9a9b5baa3aeb0dbf0cb12f1fe2 Mon Sep 17 00:00:00 2001
From: Marek Blaha <mblaha@redhat.com>
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

View File

@ -0,0 +1,68 @@
From ca3d7f06c8f4c1c901dc853ac33c06976b46c61e Mon Sep 17 00:00:00 2001
From: Marek Blaha <mblaha@redhat.com>
Date: Wed, 6 Oct 2021 09:56:05 +0200
Subject: [PATCH] Add fail_fast parameter to download_payloads methods
Unlike in the rpm transaction, reposync needs to switch the fail_fast
off to download as much packages from repository as possible.
---
dnf/base.py | 6 +++---
dnf/repo.py | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/dnf/base.py b/dnf/base.py
index b0a378c2..c258a5a7 100644
--- a/dnf/base.py
+++ b/dnf/base.py
@@ -1151,7 +1151,7 @@ class Base(object):
timer()
self._trans_success = True
- def _download_remote_payloads(self, payloads, drpm, progress, callback_total):
+ def _download_remote_payloads(self, payloads, drpm, progress, callback_total, fail_fast=True):
lock = dnf.lock.build_download_lock(self.conf.cachedir, self.conf.exit_on_lock)
with lock:
beg_download = time.time()
@@ -1163,7 +1163,7 @@ class Base(object):
progress.start(len(payloads), est_remote_size, total_drpms=total_drpm)
else:
progress.start(len(payloads), est_remote_size)
- errors = dnf.repo._download_payloads(payloads, drpm)
+ errors = dnf.repo._download_payloads(payloads, drpm, fail_fast)
if errors._irrecoverable():
raise dnf.exceptions.DownloadError(errors._irrecoverable())
@@ -1189,7 +1189,7 @@ class Base(object):
est_remote_size = sum(pload.download_size
for pload in payloads)
progress.start(len(payloads), est_remote_size)
- errors = dnf.repo._download_payloads(payloads, drpm)
+ errors = dnf.repo._download_payloads(payloads, drpm, fail_fast)
if errors._irrecoverable():
raise dnf.exceptions.DownloadError(errors._irrecoverable())
diff --git a/dnf/repo.py b/dnf/repo.py
index b454e981..bb422309 100644
--- a/dnf/repo.py
+++ b/dnf/repo.py
@@ -84,7 +84,7 @@ def _pkg2payload(pkg, progress, *factories):
raise ValueError(_('no matching payload factory for %s') % pkg)
-def _download_payloads(payloads, drpm):
+def _download_payloads(payloads, drpm, fail_fast=True):
# download packages
def _download_sort_key(payload):
return not hasattr(payload, 'delta')
@@ -94,7 +94,7 @@ def _download_payloads(payloads, drpm):
for pload in sorted(payloads, key=_download_sort_key)]
errs = _DownloadErrors()
try:
- libdnf.repo.PackageTarget.downloadPackages(libdnf.repo.VectorPPackageTarget(targets), True)
+ libdnf.repo.PackageTarget.downloadPackages(libdnf.repo.VectorPPackageTarget(targets), fail_fast)
except RuntimeError as e:
errs._fatal = str(e)
drpm.wait()
--
2.35.1

View File

@ -1,22 +0,0 @@
From 04b1a90bb24b7e98d4e001c44f8b3f563ad5f0f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
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
}

View File

@ -1,117 +0,0 @@
From eb2aa8c14208da7a567a0d79a8baa9f5201640cd Mon Sep 17 00:00:00 2001
From: Jaroslav Mracek <jmracek@redhat.com>
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 <jmracek@redhat.com>
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 <jmracek@redhat.com>
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")

View File

@ -0,0 +1,139 @@
From f0f037db8219b1e74be4ed86f5eea53b63ca1d88 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hr=C3=A1zk=C3=BD?= <lhrazky@redhat.com>
Date: Tue, 20 Jul 2021 15:29:59 +0200
Subject: [PATCH] comps: Make the install_or_skip() method not catch CompsError
anymore
According to its docstring, the original intention of the method was to
not fail on installing an already installed group/environment.
However, the CompsError is no longer thrown when attempting to install
an already installed group or environment. It was changed to logging a
warning directly in 5210b9dc and then the check was removed completely
in 217ca0fa.
For the other case for which an instance of CompsError can be thrown
from the install_group() and install_environment() methods, which is
when a group or environment is not found, we certainly want to throw an
error (see the linked bugs), therefore there's no reason to catch the
exception anymore.
The install_or_skip() method is preserved as part of the API so as not
to break compatibility any more than necessary.
msg: API: Raise CompsError when group/env not found in install_group and install_environment
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1947958
related: https://bugzilla.redhat.com/show_bug.cgi?id=1943206
---
dnf/base.py | 8 ++------
dnf/cli/commands/group.py | 4 ++--
dnf/comps.py | 20 ++++++++++----------
doc/api_base.rst | 4 ++--
4 files changed, 16 insertions(+), 20 deletions(-)
diff --git a/dnf/base.py b/dnf/base.py
index c258a5a7..babca31d 100644
--- a/dnf/base.py
+++ b/dnf/base.py
@@ -1668,9 +1668,7 @@ class Base(object):
if not isinstance(types, int):
types = libdnf.transaction.listToCompsPackageType(types)
- trans = dnf.comps.install_or_skip(solver._environment_install,
- env_id, types, exclude or set(),
- strict, exclude_groups)
+ trans = solver._environment_install(env_id, types, exclude or set(), strict, exclude_groups)
if not trans:
return 0
return self._add_comps_trans(trans)
@@ -1713,9 +1711,7 @@ class Base(object):
if not isinstance(pkg_types, int):
pkg_types = libdnf.transaction.listToCompsPackageType(pkg_types)
- trans = dnf.comps.install_or_skip(solver._group_install,
- grp_id, pkg_types, exclude_pkgnames,
- strict)
+ trans = solver._group_install(grp_id, pkg_types, exclude_pkgnames, strict)
if not trans:
return 0
if strict:
diff --git a/dnf/cli/commands/group.py b/dnf/cli/commands/group.py
index cf542799..fd723c48 100644
--- a/dnf/cli/commands/group.py
+++ b/dnf/cli/commands/group.py
@@ -244,9 +244,9 @@ class GroupCommand(commands.Command):
types = tuple(self.base.conf.group_package_types)
pkg_types = libdnf.transaction.listToCompsPackageType(types)
for env_id in res.environments:
- dnf.comps.install_or_skip(solver._environment_install, env_id, pkg_types)
+ solver._environment_install(env_id, pkg_types)
for group_id in res.groups:
- dnf.comps.install_or_skip(solver._group_install, group_id, pkg_types)
+ solver._group_install(group_id, pkg_types)
def _mark_remove(self, patterns):
q = CompsQuery(self.base.comps, self.base.history,
diff --git a/dnf/comps.py b/dnf/comps.py
index 89765337..461eb274 100644
--- a/dnf/comps.py
+++ b/dnf/comps.py
@@ -93,15 +93,15 @@ def _fn_display_order(group):
def install_or_skip(install_fnc, grp_or_env_id, types, exclude=None,
strict=True, exclude_groups=None):
- """Either mark in persistor as installed given `grp_or_env` (group
- or environment) or skip it (if it's already installed).
- `install_fnc` has to be Solver._group_install
- or Solver._environment_install.
- """
- try:
- return install_fnc(grp_or_env_id, types, exclude, strict, exclude_groups)
- except dnf.comps.CompsError as e:
- logger.warning("%s, %s", ucd(e)[:-1], _("skipping."))
+ """
+ Installs a group or an environment identified by grp_or_env_id.
+ This method is preserved for API compatibility. It used to catch an
+ exception thrown when a gorup or env was already installed, which is no
+ longer thrown.
+ `install_fnc` has to be Solver._group_install or
+ Solver._environment_install.
+ """
+ return install_fnc(grp_or_env_id, types, exclude, strict, exclude_groups)
class _Langs(object):
@@ -592,7 +592,7 @@ class Solver(object):
assert dnf.util.is_string_type(group_id)
return self.history.env.is_removable_group(group_id)
- def _environment_install(self, env_id, pkg_types, exclude, strict=True, exclude_groups=None):
+ def _environment_install(self, env_id, pkg_types, exclude=None, strict=True, exclude_groups=None):
assert dnf.util.is_string_type(env_id)
comps_env = self.comps._environment_by_id(env_id)
if not comps_env:
diff --git a/doc/api_base.rst b/doc/api_base.rst
index 20d7945e..03396b69 100644
--- a/doc/api_base.rst
+++ b/doc/api_base.rst
@@ -179,7 +179,7 @@
.. method:: group_install(group_id, pkg_types, exclude=None, strict=True)
- Mark group with corresponding `group_id` installed and mark the packages in the group for installation. Return the number of packages that the operation has marked for installation. `pkg_types` is a sequence of strings determining the kinds of packages to be installed, where the respective groups can be selected by including ``"mandatory"``, ``"default"`` or ``"optional"`` in it. If `exclude` is given, it has to be an iterable of package name glob patterns: :meth:`.group_install` will then not mark the respective packages for installation whenever possible. Parameter `strict` is a boolean indicating whether group packages that exist but are non-installable due to e.g. dependency issues should be skipped (False) or cause transaction to fail to resolve (True).
+ Mark group with corresponding `group_id` installed and mark the packages in the group for installation. Return the number of packages that the operation has marked for installation. `pkg_types` is a sequence of strings determining the kinds of packages to be installed, where the respective groups can be selected by including ``"mandatory"``, ``"default"`` or ``"optional"`` in it. If `exclude` is given, it has to be an iterable of package name glob patterns: :meth:`.group_install` will then not mark the respective packages for installation whenever possible. Parameter `strict` is a boolean indicating whether group packages that exist but are non-installable due to e.g. dependency issues should be skipped (False) or cause transaction to fail to resolve (True). Raises :exc:`dnf.exceptions.CompsError` in case the group doesn't exist.
.. method:: group_remove(group_id)
@@ -191,7 +191,7 @@
.. method:: environment_install(env_id, types, exclude=None, strict=True, exclude_groups=None)
- Similar to :meth:`.group_install` but operates on environmental groups. `exclude_groups` is an iterable of group IDs that will not be marked as installed.
+ Similar to :meth:`.group_install` but operates on environmental groups. `exclude_groups` is an iterable of group IDs that will not be marked as installed. Raises :exc:`dnf.exceptions.CompsError` in case the group doesn't exist.
.. method:: environment_remove(env_id)
--
2.35.1

View File

@ -1,80 +0,0 @@
From ca06d200d738fd6b23cb05b9776c9fd29288665f Mon Sep 17 00:00:00 2001
From: Jaroslav Mracek <jmracek@redhat.com>
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 <jmracek@redhat.com>
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):

View File

@ -0,0 +1,30 @@
From 683b92811abcb6cbbc00353010ec18e2cf655912 Mon Sep 17 00:00:00 2001
From: Jaroslav Mracek <jmracek@redhat.com>
Date: Mon, 6 Sep 2021 12:40:59 +0200
Subject: [PATCH] [doc] Improve description of multilib_policy=all
(RhBug:1996681,1995630)
https://bugzilla.redhat.com/show_bug.cgi?id=1996681
https://bugzilla.redhat.com/show_bug.cgi?id=1995630
---
doc/conf_ref.rst | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/doc/conf_ref.rst b/doc/conf_ref.rst
index 016bd00c..83b14ecd 100644
--- a/doc/conf_ref.rst
+++ b/doc/conf_ref.rst
@@ -351,7 +351,9 @@ configuration file by your distribution to override the DNF defaults.
``multilib_policy``
:ref:`string <string-label>`
- Controls how multilib packages are treated during install operations. Can either be ``"best"`` (the default) for the depsolver to prefer packages which best match the system's architecture, or ``"all"`` to install all available packages with compatible architectures.
+ Controls how multilib packages are treated during install operations. Can either be ``"best"`` (the default) for
+ the depsolver to prefer packages which best match the system's architecture, or ``"all"`` to install packages for
+ all available architectures.
.. _obsoletes_conf_option-label:
--
2.35.1

View File

@ -0,0 +1,34 @@
From db52d259645daf8ca0ae06e829787d36171f2d5b Mon Sep 17 00:00:00 2001
From: Jaroslav Rohel <jrohel@redhat.com>
Date: Wed, 20 Oct 2021 09:20:03 +0200
Subject: [PATCH] Fix: Python dnf API does not respect cacheonly
(RhBug:1862970)
`Repo` object has always been constructed with default synchronization
strategy. The configuration option `cacheonly` was ignored. DNF
application set synchronization strategy later in the `Cli` object
during processing demands.
The fix takes into account the `cacheonly` option during the construction
of the `Repo` object. Synchronization strategy may still be overriden
during demand processing.
---
dnf/repo.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dnf/repo.py b/dnf/repo.py
index bb422309..1822cf01 100644
--- a/dnf/repo.py
+++ b/dnf/repo.py
@@ -434,7 +434,7 @@ class Repo(dnf.conf.RepoConf):
self._pkgdir = None
self._key_import = _NullKeyImport()
self.metadata = None # :api
- self._repo.setSyncStrategy(self.DEFAULT_SYNC)
+ self._repo.setSyncStrategy(SYNC_ONLY_CACHE if parent_conf and parent_conf.cacheonly else self.DEFAULT_SYNC)
if parent_conf:
self._repo.setSubstitutions(parent_conf.substitutions)
self._substitutions = dnf.conf.substitutions.Substitutions()
--
2.35.1

View File

@ -1,94 +0,0 @@
From 38cc67385fb1b36aa0881bc5982bc58d75dac464 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hr=C3=A1zk=C3=BD?= <lhrazky@redhat.com>
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. """

View File

@ -1,115 +0,0 @@
From b3542a96c6f77e5cc0b5217e586fcc56fde074d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
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?= <amatej@redhat.com>
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")

View File

@ -0,0 +1,26 @@
From f8025df597685a0bd0c347b1a60c280f03bdca6f Mon Sep 17 00:00:00 2001
From: Jaroslav Rohel <jrohel@redhat.com>
Date: Fri, 5 Nov 2021 08:52:56 +0100
Subject: [PATCH] Documentation: API notes for cacheonly
---
doc/conf_ref.rst | 3 +++
1 file changed, 3 insertions(+)
diff --git a/doc/conf_ref.rst b/doc/conf_ref.rst
index 83b14ecd..75bcdf75 100644
--- a/doc/conf_ref.rst
+++ b/doc/conf_ref.rst
@@ -129,6 +129,9 @@ configuration file by your distribution to override the DNF defaults.
If set to ``True`` DNF will run entirely from system cache, will not update
the cache and will use it even in case it is expired. Default is ``False``.
+ API Notes: Must be set before repository objects are created. Plugins must set
+ this in the pre_config hook. Later changes are ignored.
+
.. _check_config_file_age-label:
``check_config_file_age``
--
2.35.1

View File

@ -1,366 +0,0 @@
From a777ff01c79d5e0e2cf3ae7b0652795577253bc3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
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?= <amatej@redhat.com>
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?= <amatej@redhat.com>
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])

View File

@ -0,0 +1,39 @@
From 6af9938c87cf409f886f21b59ec45c54eda6c8b2 Mon Sep 17 00:00:00 2001
From: Jaroslav Mracek <jmracek@redhat.com>
Date: Tue, 2 Nov 2021 14:23:22 +0100
Subject: [PATCH] Allow destdir option with modulesync command
---
dnf/cli/cli.py | 2 +-
doc/command_ref.rst | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
index 65769978..a315201c 100644
--- a/dnf/cli/cli.py
+++ b/dnf/cli/cli.py
@@ -810,7 +810,7 @@ class Cli(object):
if opts.destdir is not None:
self.base.conf.destdir = opts.destdir
if not self.base.conf.downloadonly and opts.command not in (
- 'download', 'system-upgrade', 'reposync'):
+ 'download', 'system-upgrade', 'reposync', 'modulesync'):
logger.critical(_('--destdir or --downloaddir must be used with --downloadonly '
'or download or system-upgrade command.')
)
diff --git a/doc/command_ref.rst b/doc/command_ref.rst
index f96c0eac..42aec72c 100644
--- a/doc/command_ref.rst
+++ b/doc/command_ref.rst
@@ -182,7 +182,7 @@ Options
``--downloaddir=<path>, --destdir=<path>``
Redirect downloaded packages to provided directory. The option has to be used together with the \-\
:ref:`-downloadonly <downloadonly-label>` command line option, with the
- ``download`` command (dnf-plugins-core) or with the ``system-upgrade`` command
+ ``download``, ``modulesync`` or ``reposync`` commands (dnf-plugins-core) or with the ``system-upgrade`` command
(dnf-plugins-extras).
.. _downloadonly-label:
--
2.35.1

View File

@ -1,43 +0,0 @@
From 291071a937a1de398641f02002413678398e473c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
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)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,57 @@
From 27f26c607c44b8331b23c861241a8509c2452531 Mon Sep 17 00:00:00 2001
From: sbluhm <stefan.bluhm@clacee.eu>
Date: Sun, 9 Jan 2022 14:30:19 +0100
Subject: [PATCH] Add documentation for query api flags
= changelog =
msg: Add documentation for query api flags
type: enhancement
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2035577
---
AUTHORS | 1 +
doc/api_sack.rst | 14 +++++++++++++-
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/AUTHORS b/AUTHORS
index f8c9eb83..0077c7ea 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -91,6 +91,7 @@ DNF CONTRIBUTORS
Petr Spacek <pspacek@redhat.com>
Rob Cutmore <robcutmore@gmail.com>
Satoshi Matsumoto <kaorimatz@gmail.com>
+ Stefan Bluhm <stefan.bluhm@clacee.eu>
Tomas Kasparek <tkasparek@redhat.com>
Vladan Kudlac <vladankudlac@gmail.com>
Will Woods <wwoods@redhat.com>
diff --git a/doc/api_sack.rst b/doc/api_sack.rst
index 79719878..ac843fc9 100644
--- a/doc/api_sack.rst
+++ b/doc/api_sack.rst
@@ -25,10 +25,22 @@
The package sack. Contains metadata information about all known packages, installed and available.
- .. method:: query()
+ .. method:: query(flags=hawkey.APPLY_EXCLUDES)
Return a :class:`Query<dnf.query.Query>` for querying packages contained in this sack.
+ :ref:`Package filtering <excluded_packages-label>` is applied when creating the query object. The behavior can be adapted using flags. Possible flags:
+
+
+ ============================== ===========================================================================
+ Flag Value meaning
+ ============================== ===========================================================================
+ hawkey.APPLY_EXCLUDES Apply all package filtering.
+ hawkey.IGNORE_EXCLUDES Ignore all package filtering.
+ hawkey.IGNORE_REGULAR_EXCLUDES Ignore regular excludes defined by configuration files or the command line.
+ hawkey.IGNORE_MODULAR_EXCLUDES Ignore modular filtering.
+ ============================== ===========================================================================
+
.. function:: rpmdb_sack(base)
Returns a new instance of sack containing only installed packages (@System repo). Useful to get list of the installed RPMs after transaction.
--
2.35.1

View File

@ -1,61 +0,0 @@
From 40e762da5cd2d876b6424f4c25b77e8dc2422a0f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
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*")

View File

@ -1,56 +0,0 @@
From 9ceb74f77479910f7844a9a87d4b7623687076be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
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 <module_name>' 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 <module_name>' 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)

View File

@ -0,0 +1,37 @@
From 9bd0423e1e543ed5f83924ec61aa253eced24cf8 Mon Sep 17 00:00:00 2001
From: Marek Blaha <mblaha@redhat.com>
Date: Mon, 14 Mar 2022 09:49:52 +0100
Subject: [PATCH] Fix processing of download errors (RhBug: 2024527)
Users with different than english locale are not able to update their
systems in case that some of updates are already downloaded in the dnf
cache (e.g. using dnf-automatic).
The error string is taken from librepo target where it is stored
untranslated. Therefore we need to compare untranslated versions of the
string.
= changelog =
msg: Fix download errors handling in non-english locales
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2024527
---
dnf/repo.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dnf/repo.py b/dnf/repo.py
index 1822cf01..ec1a2537 100644
--- a/dnf/repo.py
+++ b/dnf/repo.py
@@ -108,7 +108,7 @@ def _download_payloads(payloads, drpm, fail_fast=True):
callbacks = tgt.getCallbacks()
payload = callbacks.package_pload
pkg = payload.pkg
- if err == _('Already downloaded'):
+ if err == 'Already downloaded':
errs._skipped.add(pkg)
continue
pkg.repo._repo.expire()
--
2.35.1

View File

@ -0,0 +1,81 @@
From 0da73ea1304005b796842d96679d6ea31cdeea3c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
Date: Tue, 1 Mar 2022 12:42:23 +0100
Subject: [PATCH] Fix unittests that relied on checksum being at the end of
solvfiles
---
tests/test_fill_sack_from_repos_in_cache.py | 26 +++++++++++----------
1 file changed, 14 insertions(+), 12 deletions(-)
diff --git a/tests/test_fill_sack_from_repos_in_cache.py b/tests/test_fill_sack_from_repos_in_cache.py
index a8de287c..30d02cfe 100644
--- a/tests/test_fill_sack_from_repos_in_cache.py
+++ b/tests/test_fill_sack_from_repos_in_cache.py
@@ -39,7 +39,7 @@ TEST_REPO_NAME = "test-repo"
class FillSackFromReposInCacheTest(unittest.TestCase):
- def _create_cache_for_repo(self, repopath, tmpdir):
+ def _create_cache_for_repo(self, repopath, tmpdir, repo_name=TEST_REPO_NAME):
conf = dnf.conf.MainConf()
conf.cachedir = os.path.join(tmpdir, "cache")
conf.installroot = tmpdir
@@ -49,7 +49,7 @@ class FillSackFromReposInCacheTest(unittest.TestCase):
base = dnf.Base(conf=conf)
- repoconf = dnf.repo.Repo(TEST_REPO_NAME, base.conf)
+ repoconf = dnf.repo.Repo(repo_name, base.conf)
repoconf.baseurl = repopath
repoconf.enable()
@@ -194,6 +194,8 @@ class FillSackFromReposInCacheTest(unittest.TestCase):
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"))
+ self._create_cache_for_repo(os.path.join(os.path.abspath(os.path.dirname(__file__)), "repos/drpm"),
+ self.tmpdir, "drpm-repo")
# Remove xml metadata except repomd
# repomd.xml is not compressed and doesn't end with .gz
@@ -201,12 +203,11 @@ class FillSackFromReposInCacheTest(unittest.TestCase):
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")
+ # Replace solvfile of test-repo with solvfile from drpm-repo which has different data (different checksum)
+ shutil.move(os.path.join(self.tmpdir, "cache/drpm-repo.solv"),
+ os.path.join(self.tmpdir, "cache/test-repo.solv"))
- # Now we only have cache with solvx, modified solv file and just repomd
+ # Now we only have cache with solvx, mismatching solv file and just repomd
# Since we don't have original xml metadata we cannot regenerate solv -> fail (exception)
self.assertRaises(dnf.exceptions.RepoError,
@@ -214,13 +215,14 @@ class FillSackFromReposInCacheTest(unittest.TestCase):
def test_checksum_mistmatch_regenerates_solv(self):
self._setUp_from_repo_path(os.path.join(os.path.abspath(os.path.dirname(__file__)), "repos/rpm"))
+ self._create_cache_for_repo(os.path.join(os.path.abspath(os.path.dirname(__file__)), "repos/drpm"),
+ self.tmpdir, "drpm-repo")
- # 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")
+ # Replace solvfile of test-repo with solvfile from drpm-repo which has different data (different checksum)
+ shutil.move(os.path.join(self.tmpdir, "cache/drpm-repo.solv"),
+ os.path.join(self.tmpdir, "cache/test-repo.solv"))
- # Now we only have cache with solvx, modified solv file and xml metadata.
+ # Now we only have cache with solvx, mismatching 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)
--
2.35.1

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,64 @@
From fe87499e6745795b1dc6225fa102a1242eb9ffc8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hr=C3=A1zk=C3=BD?= <lhrazky@redhat.com>
Date: Thu, 17 Feb 2022 18:46:22 +0100
Subject: [PATCH] cli/commands/history: Fix history undo on a Reason Change
The previous reason needs to be fetched from the history db. It's
inefficient to parse the nevra after it was serialized in a previous
step, but that would need bigger code restructuring.
= changelog =
msg: Fix history undo on a Reason Change
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2053014
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2010259
---
dnf.spec | 2 +-
dnf/cli/commands/history.py | 11 +++++++++++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/dnf.spec b/dnf.spec
index 02c8b577..36c3932e 100644
--- a/dnf.spec
+++ b/dnf.spec
@@ -2,7 +2,7 @@
%define __cmake_in_source_build 1
# default dependencies
-%global hawkey_version 0.61.1
+%global hawkey_version 0.66.0
%global libcomps_version 0.1.8
%global libmodulemd_version 2.9.3
%global rpm_version 4.14.0
diff --git a/dnf/cli/commands/history.py b/dnf/cli/commands/history.py
index 293d93fc..21d04a1a 100644
--- a/dnf/cli/commands/history.py
+++ b/dnf/cli/commands/history.py
@@ -223,6 +223,7 @@ class HistoryCommand(commands.Command):
"Reinstall": "Reinstalled",
"Obsoleted": "Install",
"Obsolete": "Obsoleted",
+ "Reason Change": "Reason Change",
}
data = serialize_transaction(trans)
@@ -235,6 +236,16 @@ class HistoryCommand(commands.Command):
if ti["action"] == "Install" and ti.get("reason", None) == "clean":
ti["reason"] = "dependency"
+ if ti["action"] == "Reason Change" and "nevra" in ti:
+ subj = hawkey.Subject(ti["nevra"])
+ nevra = subj.get_nevra_possibilities(forms=[hawkey.FORM_NEVRA])[0]
+ reason = self.output.history.swdb.resolveRPMTransactionItemReason(
+ nevra.name,
+ nevra.arch,
+ trans.tids()[0] - 1
+ )
+ ti["reason"] = libdnf.transaction.TransactionItemReasonToString(reason)
+
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
--
2.35.1

View File

@ -0,0 +1,30 @@
From 55f6691d5663c59b675064b04e19288365e92d24 Mon Sep 17 00:00:00 2001
From: Nicola Sella <nsella@redhat.com>
Date: Tue, 15 Mar 2022 16:26:10 +0100
Subject: [PATCH] Fix remove when no repos are enabled (RhBz:2064341)
msg: When no repositories are enabled, dnf group exits and does not
remove an installed group.
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2064341
type: bugfix
---
dnf/cli/commands/group.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dnf/cli/commands/group.py b/dnf/cli/commands/group.py
index fd723c48..c5c23230 100644
--- a/dnf/cli/commands/group.py
+++ b/dnf/cli/commands/group.py
@@ -358,7 +358,8 @@ class GroupCommand(commands.Command):
else:
demands.available_repos = True
- commands._checkEnabledRepo(self.base)
+ if cmd not in ('remove'):
+ commands._checkEnabledRepo(self.base)
if cmd in ('install', 'upgrade'):
commands._checkGPGKey(self.base, self.cli)
--
2.35.1

View File

@ -0,0 +1,30 @@
From 8f05ee29b7398fa6d18c7113a533f1d8726239df Mon Sep 17 00:00:00 2001
From: Jaroslav Rohel <jrohel@redhat.com>
Date: Tue, 12 Apr 2022 12:25:05 +0200
Subject: [PATCH] [doc] Improve "proxy" configuration option documentation
(RhBug:2072332)
---
doc/conf_ref.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/conf_ref.rst b/doc/conf_ref.rst
index 75bcdf75..885a4560 100644
--- a/doc/conf_ref.rst
+++ b/doc/conf_ref.rst
@@ -833,10 +833,10 @@ configuration.
``proxy``
:ref:`string <string-label>`
- URL of a proxy server to connect through. Set to an empty string to disable the proxy setting inherited from the main section and use direct connection instead. The expected format of this option is ``<scheme>://<ip-or-hostname>[:port]``.
+ URL of a proxy server to connect through. Set to an empty string in the repository configuration to disable proxy setting inherited from the main section. The expected format of this option is ``<scheme>://<ip-or-hostname>[:port]``.
(For backward compatibility, '_none_' can be used instead of the empty string.)
- Note: The curl environment variables (such as ``http_proxy``) are effective if this option is unset. See the ``curl`` man page for details.
+ Note: The curl environment variables (such as ``http_proxy``) are effective if this option is unset (or '_none_' is set in the repository configuration). See the ``curl`` man page for details.
``proxy_username``
:ref:`string <string-label>`
--
2.35.1

View File

@ -0,0 +1,318 @@
From 88a6289a4f72b11253c01a5a5d834b74d5abb6c3 Mon Sep 17 00:00:00 2001
From: Laszlo Ersek <lersek@redhat.com>
Date: Sun, 24 Apr 2022 09:08:28 +0200
Subject: [PATCH] Base.reset: plug (temporary) leak of libsolv's page file
descriptors
Consider the following call paths (mixed Python and C), extending from
livecd-creator down to libsolv:
main [livecd-tools/tools/livecd-creator]
install() [livecd-tools/imgcreate/creator.py]
fill_sack() [dnf/dnf/base.py]
_add_repo_to_sack() [dnf/dnf/base.py]
load_repo() [libdnf/python/hawkey/sack-py.cpp]
dnf_sack_load_repo() [libdnf/libdnf/dnf-sack.cpp]
write_main() [libdnf/libdnf/dnf-sack.cpp]
repo_add_solv() [libsolv/src/repo_solv.c]
repopagestore_read_or_setup_pages() [libsolv/src/repopage.c]
dup()
write_ext() [libdnf/libdnf/dnf-sack.cpp]
repo_add_solv() [libsolv/src/repo_solv.c]
repopagestore_read_or_setup_pages() [libsolv/src/repopage.c]
dup()
The dup() calls create the following file descriptors (output from
"lsof"):
> COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
> python3 6500 root 7r REG 8,1 25320727 395438 /var/tmp/imgcreate-mytcghah/install_root/var/cache/dnf/fedora.solv (deleted)
> python3 6500 root 8r REG 8,1 52531426 395450 /var/tmp/imgcreate-mytcghah/install_root/var/cache/dnf/fedora-filenames.solvx
These file descriptors are *owned* by the DnfSack object (which is derived
from GObject), as follows:
sack->priv->pool->repos[1]->repodata[1]->store.pagefd = 7
sack->priv->pool->repos[1]->repodata[2]->store.pagefd = 8
^ ^ ^ ^ ^ ^ ^
| | | | | | |
| | | | | | int
| | | | | Repopagestore [libsolv/src/repopage.h]
| | | | Repodata [libsolv/src/repodata.h]
| | | struct s_Repo [libsolv/src/repo.h]
| | struct s_Pool (aka Pool) [libsolv/src/pool.h]
| DnfSackPrivate [libdnf/libdnf/dnf-sack.cpp]
DnfSack [libdnf/libdnf/dnf-sack.h]
The file descriptors are *supposed* to be closed on the following call
path:
main [livecd-tools/tools/livecd-creator]
install() [livecd-tools/imgcreate/creator.py]
close() [livecd-tools/imgcreate/dnfinst.py]
close() [dnf/dnf/base.py]
reset() [dnf/dnf/base.py]
_sack = None
_goal = None
_transaction = None
...
dnf_sack_finalize() [libdnf/libdnf/dnf-sack.cpp]
pool_free() [libsolv/src/pool.c]
pool_freeallrepos() [libsolv/src/pool.c]
repo_freedata() [libsolv/src/repo.c]
repodata_freedata() [libsolv/src/repodata.c]
repopagestore_free() [libsolv/src/repopage.c]
close()
Namely, when dnf.Base.reset() [dnf/dnf/base.py] is called with (sack=True,
goal=True), the reference counts of the objects pointed to by the "_sack",
"_goal" and "_transaction" fields are supposed to reach zero, and then, as
part of the DnfSack object's finalization, the libsolv file descriptors
are supposed to be closed.
Now, while this *may* happen immediately in dnf.Base.reset(), it may as
well not. The reason is that there is a multitude of *circular references*
between DnfSack and the packages that it contains. When dnf.Base.reset()
is entered, we have the following picture:
_sack _goal
| |
v v
+----------------+ +-------------+
| DnfSack object | <--- | Goal object |
+----------------+ +-------------+
|^ |^ |^
|| || ||
|| || ||
+--||----||----||---+
| v| v| v| | <-- _transaction
| Pkg1 Pkg2 PkgN |
| |
| Transaction oject |
+-------------------+
That is, the reference count of the DnfSack object is (1 + 1 + N), where N
is the number of packages in the transaction. Details:
(a) The first reference comes from the "_sack" field, established like
this:
main [livecd-tools/tools/livecd-creator]
install() [livecd-tools/imgcreate/creator.py]
fill_sack() [dnf/dnf/base.py]
_build_sack() [dnf/dnf/sack.py]
Sack()
sack_init() [libdnf/python/hawkey/sack-py.cpp]
dnf_sack_new() [libdnf/libdnf/dnf-sack.cpp]
(b) The second reference on the DnfSack object comes from "_goal":
main [livecd-tools/tools/livecd-creator]
install() [livecd-tools/imgcreate/creator.py]
fill_sack() [dnf/dnf/base.py]
_goal = Goal(_sack)
goal_init() [libdnf/python/hawkey/goal-py.cpp]
Py_INCREF(_sack)
(c) Then there is one reference to "_sack" *per package* in the
transaction:
main [livecd-tools/tools/livecd-creator]
install() [livecd-tools/imgcreate/creator.py]
runInstall() [livecd-tools/imgcreate/dnfinst.py]
resolve() [dnf/dnf/base.py]
_goal2transaction() [dnf/dnf/base.py]
list_installs() [libdnf/python/hawkey/goal-py.cpp]
list_generic() [libdnf/python/hawkey/goal-py.cpp]
packagelist_to_pylist() [libdnf/python/hawkey/iutil-py.cpp]
new_package() [libdnf/python/hawkey/sack-py.cpp]
Py_BuildValue()
ts.add_install()
list_installs() creates a list of packages that need to be installed
by DNF. Inside the loop in packagelist_to_pylist(), which constructs
the elements of that list, Py_BuildValue() is called with the "O"
format specifier, and that increases the reference count on "_sack".
Subsequently, in the _goal2transaction() method, we iterate over the
package list created by list_installs(), and add each package to the
transaction (ts.add_install()). After _goal2transaction() returns,
this transaction is assigned to "self._transaction" in resolve(). This
is where the last N (back-)references on the DnfSack object come from.
(d) Now, to quote the defintion of the DnfSack object
("libdnf/docs/hawkey/tutorial-py.rst"):
> *Sack* is an abstraction for a collection of packages.
That's why the DnfSack object references all the Pkg1 through PkgN
packages.
So, when the dnf.Base.reset() method completes, the picture changes like
this:
_sack _goal
| |
-- [CUT] -- -- [CUT] --
| |
v | v
+----------------+ [C] +-------------+
| DnfSack object | <-[U]- | Goal object |
+----------------+ [T] +-------------+
|^ |^ |^ |
|| || ||
|| || || |
+--||----||----||---+ [C]
| v| v| v| | <--[U]-- _transaction
| Pkg1 Pkg2 PkgN | [T]
| | |
| Transaction oject |
+-------------------+
and we are left with N reference cycles (one between each package and the
same DnfSack object).
This set of cycles can only be cleaned up by Python's generational garbage
collector <https://stackify.com/python-garbage-collection/>. The GC will
collect the DnfSack object, and consequently close the libsolv page file
descriptors via dnf_sack_finalize() -- but garbage collection will happen
*only eventually*, unpredictably.
This means that the dnf.Base.reset() method breaks its interface contract:
> Make the Base object forget about various things.
because the libsolv file descriptors can (and frequently do, in practice)
survive dnf.Base.reset().
In general, as long as the garbage collector only tracks process-private
memory blocks, there's nothing wrong; however, file descriptors are
visible to the kernel. When dnf.Base.reset() *temporarily* leaks file
descriptors as explained above, then immediately subsequent operations
that depend on those file descriptors having been closed, can fail.
An example is livecd-creator's unmounting of:
/var/tmp/imgcreate-mytcghah/install_root/var/cache/dnf
which the kernel refuses, due to libsolv's still open file descriptors
pointing into that filesystem:
> umount: /var/tmp/imgcreate-mytcghah/install_root/var/cache/dnf: target
> is busy.
> Unable to unmount /var/tmp/imgcreate-mytcghah/install_root/var/cache/dnf
> normally, using lazy unmount
(Unfortunately, the whole lazy umount idea is misguided in livecd-tools;
it's a misfeature that should be removed, as it permits the corruption of
the loop-backed filesystem. Now that the real bug is being fixed in DNF,
lazy umount is not needed as a (broken) workaround in livecd-tools. But
that's a separate patch for livecd-tools:
<https://github.com/livecd-tools/livecd-tools/pull/227>.)
Plug the fd leak by forcing a garbage collection in dnf.Base.reset()
whenever we cut the "_sack", "_goal" and "_transaction" links -- that is,
when the "sack" and "goal" parameters are True.
Note that precisely due to the unpredictable behavior of the garbage
collector, reproducing the bug may prove elusive. In order to reproduce it
deterministically, through usage with livecd-creator, disabling automatic
garbage collection with the following patch (for livecd-tools) is
sufficient:
> diff --git a/tools/livecd-creator b/tools/livecd-creator
> index 291de10cbbf9..8d2c740c238b 100755
> --- a/tools/livecd-creator
> +++ b/tools/livecd-creator
> @@ -31,6 +31,8 @@ from dnf.exceptions import Error as DnfBaseError
> import imgcreate
> from imgcreate.errors import KickstartError
>
> +import gc
> +
> class Usage(Exception):
> def __init__(self, msg = None, no_error = False):
> Exception.__init__(self, msg, no_error)
> @@ -261,5 +263,6 @@ def do_nss_libs_hack():
> return hack
>
> if __name__ == "__main__":
> + gc.disable()
> hack = do_nss_libs_hack()
> sys.exit(main())
Also note that you need to use livecd-tools at git commit 4afde9352e82 or
later, for this fix to make any difference: said commit fixes a different
(independent) bug in livecd-tools that produces identical symptoms, but
from a different origin. In other words, if you don't have commit
4afde9352e82 in your livecd-tools install, then said bug in livecd-tools
will mask this DNF fix.
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
(cherry picked from commit 5ce5ed1ea08ad6e198c1c1642c4d9ea2db6eab86)
---
dnf/base.py | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/dnf/base.py b/dnf/base.py
index babca31d..852fcdd8 100644
--- a/dnf/base.py
+++ b/dnf/base.py
@@ -72,6 +72,7 @@ import dnf.transaction
import dnf.util
import dnf.yum.rpmtrans
import functools
+import gc
import hawkey
import itertools
import logging
@@ -568,6 +569,46 @@ class Base(object):
self._comps_trans = dnf.comps.TransactionBunch()
self._transaction = None
self._update_security_filters = []
+ if sack and goal:
+ # We've just done this, above:
+ #
+ # _sack _goal
+ # | |
+ # -- [CUT] -- -- [CUT] --
+ # | |
+ # v | v
+ # +----------------+ [C] +-------------+
+ # | DnfSack object | <-[U]- | Goal object |
+ # +----------------+ [T] +-------------+
+ # |^ |^ |^ |
+ # || || ||
+ # || || || |
+ # +--||----||----||---+ [C]
+ # | v| v| v| | <--[U]-- _transaction
+ # | Pkg1 Pkg2 PkgN | [T]
+ # | | |
+ # | Transaction oject |
+ # +-------------------+
+ #
+ # At this point, the DnfSack object would be released only
+ # eventually, by Python's generational garbage collector, due to the
+ # cyclic references DnfSack<->Pkg1 ... DnfSack<->PkgN.
+ #
+ # The delayed release is a problem: the DnfSack object may
+ # (indirectly) own "page file" file descriptors in libsolv, via
+ # libdnf. For example,
+ #
+ # sack->priv->pool->repos[1]->repodata[1]->store.pagefd = 7
+ # sack->priv->pool->repos[1]->repodata[2]->store.pagefd = 8
+ #
+ # These file descriptors are closed when the DnfSack object is
+ # eventually released, that is, when dnf_sack_finalize() (in libdnf)
+ # calls pool_free() (in libsolv).
+ #
+ # We need that to happen right now, as callers may want to unmount
+ # the filesystems which those file descriptors refer to immediately
+ # after reset() returns. Therefore, force a garbage collection here.
+ gc.collect()
def _closeRpmDB(self):
"""Closes down the instances of rpmdb that could be open."""
--
2.35.1

View File

@ -0,0 +1,31 @@
From 00f3016ec0d79186f08c2f0ebf450bdc3dab1311 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
Date: Thu, 23 Jun 2022 09:33:45 +0200
Subject: [PATCH] doc: Describe how gpg keys are stored for `repo_ggpcheck`
(RhBug:2020678)
https://bugzilla.redhat.com/show_bug.cgi?id=2020678
---
doc/conf_ref.rst | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/doc/conf_ref.rst b/doc/conf_ref.rst
index 885a4560..decb49ff 100644
--- a/doc/conf_ref.rst
+++ b/doc/conf_ref.rst
@@ -906,6 +906,12 @@ configuration.
:ref:`boolean <boolean-label>`
Whether to perform GPG signature check on this repository's metadata. The default is False.
+ Note that GPG keys for this check are stored separately from GPG keys used in package signature
+ verification. Furthermore, they are also stored separately for each repository.
+
+ This means that dnf may ask to import the same key multiple times. For example, when a key was
+ already imported for package signature verification and this option is turned on, it may be needed
+ to import it again for the repository.
``retries``
:ref:`integer <integer-label>`
--
2.36.1

View File

@ -0,0 +1,64 @@
From 25bc75cbe63289864c09ab25144ee4af232bd8f4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
Date: Mon, 4 Jul 2022 09:43:25 +0200
Subject: [PATCH] Add only relevant pkgs to upgrade transaction (RhBug:2097757)
https://bugzilla.redhat.com/show_bug.cgi?id=2097757
Without this patch dnf can create the following transaction during dnf upgrade --security when there is an advisory for B-2-2:
```
repo @System 0 testtags <inline>
#>=Pkg: A 1 1 x86_64
#>=Pkg: B 1 1 x86_64
#>=Req: A = 1-1
repo available 0 testtags <inline>
#>=Pkg: A 2 2 x86_64
#>=Pkg: B 2 2 x86_64
#>=Req: A = 2-2
system x86_64 rpm @System
job update oneof A-1-1.x86_64@@System B-2-2.x86_64@available [targeted,setevr,setarch]
result transaction,problems
```
Problem is that without forcebest nothing gets upgraded despite the available advisory and --security switch.
This can also be seen in CI test case: rpm-software-management/ci-dnf-stack#1130
---
dnf/base.py | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/dnf/base.py b/dnf/base.py
index 852fcdd8..82466831 100644
--- a/dnf/base.py
+++ b/dnf/base.py
@@ -2135,7 +2135,24 @@ class Base(object):
query.filterm(reponame=reponame)
query = self._merge_update_filters(query, pkg_spec=pkg_spec, upgrade=True)
if query:
- query = query.union(installed_query.latest())
+ # Given that we use libsolv's targeted transactions, we need to ensure that the transaction contains both
+ # the new targeted version and also the current installed version (for the upgraded package). This is
+ # because if it only contained the new version, libsolv would decide to reinstall the package even if it
+ # had just a different buildtime or vendor but the same version
+ # (https://github.com/openSUSE/libsolv/issues/287)
+ # - In general, the query already contains both the new and installed versions but not always.
+ # If repository-packages command is used, the installed packages are filtered out because they are from
+ # the @system repo. We need to add them back in.
+ # - However we need to add installed versions of just the packages that are being upgraded. We don't want
+ # to add all installed packages because it could increase the number of solutions for the transaction
+ # (especially without --best) and since libsolv prefers the smallest possible upgrade it could result
+ # in no upgrade even if there is one available. This is a problem in general but its critical with
+ # --security transactions (https://bugzilla.redhat.com/show_bug.cgi?id=2097757)
+ # - We want to add only the latest versions of installed packages, this is specifically for installonly
+ # packages. Otherwise if for example kernel-1 and kernel-3 were installed and present in the
+ # transaction libsolv could decide to install kernel-2 because it is an upgrade for kernel-1 even
+ # though we don't want it because there already is a newer version present.
+ query = query.union(installed_query.latest().filter(name=[pkg.name for pkg in query]))
sltr = dnf.selector.Selector(self.sack)
sltr.set(pkg=query)
self._goal.upgrade(select=sltr)
--
2.36.1

View File

@ -0,0 +1,37 @@
From fea1f456d3d5f3015ebcff4008959916bdaaf6d6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
Date: Mon, 4 Jul 2022 09:46:29 +0200
Subject: [PATCH] Use `installed_all` because `installed_query` is filtered
user input
`installed_query` could be missing packages. If we specify we want to
upgrade a specific nevra that is not yet installed, then `installed_query`
is empty because it is based on user input, but there could be other
versions of the pkg installed.
Eg: if kernel-1 and kernel-3 are installed and we specify we want to
upgrade kernel-2, nothing should be done because we already have higher
version, but now `installed_query` would be empty and kernel-2 would be
installed.
Therefore, we need to use `installed_all`.
---
dnf/base.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dnf/base.py b/dnf/base.py
index 82466831..e606d9fa 100644
--- a/dnf/base.py
+++ b/dnf/base.py
@@ -2152,7 +2152,7 @@ class Base(object):
# packages. Otherwise if for example kernel-1 and kernel-3 were installed and present in the
# transaction libsolv could decide to install kernel-2 because it is an upgrade for kernel-1 even
# though we don't want it because there already is a newer version present.
- query = query.union(installed_query.latest().filter(name=[pkg.name for pkg in query]))
+ query = query.union(installed_all.latest().filter(name=[pkg.name for pkg in query]))
sltr = dnf.selector.Selector(self.sack)
sltr.set(pkg=query)
self._goal.upgrade(select=sltr)
--
2.36.1

View File

@ -0,0 +1,47 @@
From 66a37245e82c60b972ee35879f9c29c27466a27b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
Date: Mon, 25 Jul 2022 12:44:17 +0200
Subject: [PATCH] Don't include resolved advisories for obsoletes with sec.
filters (RhBug:2101421)
This makes the obsoletes security filters consistent with upgrade
security filters.
This API is used from check-update and from Info and List commands.
- For check-update we don't want to include resolved advisories to have
identical result to the actual update. That is bz2101421 use case.
- For Info and List commands the --obsoletes switch: "List packages
installed on the system that are obsoleted by packages in any known
repository." Given this specification in makes sense not to
consider resolved advisories when we also use security filters.
There is still a general case when someone uses the API or any potential
future use and I think it is best to have the behavior unified for
"upgrades" and "obsoletes".
= changelog =
msg: Don't include resolved advisories for obsoletes filtering with security filters
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2101421
Tests: https://github.com/rpm-software-management/ci-dnf-stack/pull/1134
---
dnf/base.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dnf/base.py b/dnf/base.py
index e606d9fa..e623d98e 100644
--- a/dnf/base.py
+++ b/dnf/base.py
@@ -1565,7 +1565,7 @@ class Base(object):
obsoletes = query_for_repo(
self.sack.query()).filter(obsoletes_by_priority=inst)
# reduce a query to security upgrades if they are specified
- obsoletes = self._merge_update_filters(obsoletes, warning=False)
+ obsoletes = self._merge_update_filters(obsoletes, warning=False, upgrade=True)
obsoletesTuples = []
for new in obsoletes:
obsoleted_reldeps = new.obsoletes
--
2.37.1

View File

@ -0,0 +1,35 @@
From 553a2c585db50599d5028ea6bb6462281bb88d02 Mon Sep 17 00:00:00 2001
From: Jaroslav Mracek <jmracek@redhat.com>
Date: Mon, 11 Jul 2022 12:27:14 +0200
Subject: [PATCH] Set default value for variable to prevent crash
(RhBug:2091636)
It ensure that read of file ended successfully.
https://bugzilla.redhat.com/show_bug.cgi?id=2091636
---
dnf/conf/substitutions.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/dnf/conf/substitutions.py b/dnf/conf/substitutions.py
index 703e4a4f..1281bdf0 100644
--- a/dnf/conf/substitutions.py
+++ b/dnf/conf/substitutions.py
@@ -53,6 +53,7 @@ class Substitutions(dict):
continue
for fsvar in fsvars:
filepath = os.path.join(dir_fsvars, fsvar)
+ val = None
if os.path.isfile(filepath):
try:
with open(filepath) as fp:
@@ -61,4 +62,5 @@ class Substitutions(dict):
val = val[:-1]
except (OSError, IOError):
continue
- self[fsvar] = val
+ if val is not None:
+ self[fsvar] = val
--
2.37.1

View File

@ -0,0 +1,31 @@
From 96a5bd61ab3b35f00f0b52bcd6428c7aea7d1ca5 Mon Sep 17 00:00:00 2001
From: Jan Kolarik <jkolarik@redhat.com>
Date: Wed, 7 Sep 2022 14:27:07 +0200
Subject: [PATCH] Add doc related to --destdir and --downloadonly options
(RhBug:2100811)
= changelog =
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2100811
---
doc/command_ref.rst | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/doc/command_ref.rst b/doc/command_ref.rst
index 42aec72c..7a02448c 100644
--- a/doc/command_ref.rst
+++ b/doc/command_ref.rst
@@ -190,6 +190,10 @@ Options
``--downloadonly``
Download the resolved package set without performing any rpm transaction (install/upgrade/erase).
+ Packages are removed after the next successful transaction. This applies also when used together
+ with ``--destdir`` option as the directory is considered as a part of the DNF cache. To persist
+ the packages, use the ``download`` command instead.
+
``-e <error level>, --errorlevel=<error level>``
Error output level. This is an integer value between 0 (no error output) and
10 (shows all error messages), default is 3. Deprecated, use ``-v`` instead.
--
2.37.1

View File

@ -0,0 +1,79 @@
From aa724a639a641943ecf39038fd694abc2037e66d Mon Sep 17 00:00:00 2001
From: Jan Kolarik <jkolarik@redhat.com>
Date: Mon, 22 Aug 2022 10:38:30 +0200
Subject: [PATCH] Expose plugin unload method to API (RhBug:2047251)
= changelog =
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2047251
---
dnf/base.py | 5 +++++
dnf/plugin.py | 1 +
doc/api_base.rst | 4 ++++
tests/api/test_dnf_base.py | 7 +++++++
4 files changed, 17 insertions(+)
diff --git a/dnf/base.py b/dnf/base.py
index e623d98e..4ddfae15 100644
--- a/dnf/base.py
+++ b/dnf/base.py
@@ -316,6 +316,11 @@ class Base(object):
"""Run plugins configure() method."""
self._plugins._run_config()
+ def unload_plugins(self):
+ # :api
+ """Run plugins unload() method."""
+ self._plugins._unload()
+
def update_cache(self, timer=False):
# :api
diff --git a/dnf/plugin.py b/dnf/plugin.py
index 06066e79..87c1f08f 100644
--- a/dnf/plugin.py
+++ b/dnf/plugin.py
@@ -164,6 +164,7 @@ class Plugins(object):
self._caller('transaction')
def _unload(self):
+ logger.debug(_('Plugins were unloaded'))
del sys.modules[DYNAMIC_PACKAGE]
def unload_removed_plugins(self, transaction):
diff --git a/doc/api_base.rst b/doc/api_base.rst
index 03396b69..35cbeef5 100644
--- a/doc/api_base.rst
+++ b/doc/api_base.rst
@@ -97,6 +97,10 @@
Configure plugins by running their configure() method.
+ .. method:: unload_plugins()
+
+ Unload all plugins.
+
.. method:: fill_sack([load_system_repo=True, load_available_repos=True])
Setup the package sack. If `load_system_repo` is ``True``, load information about packages in the local RPMDB into the sack. Else no package is considered installed during dependency solving. If `load_available_repos` is ``True``, load information about packages from the available repositories into the sack.
diff --git a/tests/api/test_dnf_base.py b/tests/api/test_dnf_base.py
index 33598189..e84e272b 100644
--- a/tests/api/test_dnf_base.py
+++ b/tests/api/test_dnf_base.py
@@ -95,6 +95,13 @@ class DnfBaseApiTest(TestCase):
self.base.configure_plugins()
+ def test_unload_plugins(self):
+ # Base.unload_plugins()
+ self.assertHasAttr(self.base, "unload_plugins")
+
+ self.base.init_plugins()
+ self.base.unload_plugins()
+
def test_update_cache(self):
# Base.update_cache(self, timer=False)
self.assertHasAttr(self.base, "update_cache")
--
2.37.1

View File

@ -0,0 +1,105 @@
From 7ba2cd6a86945e0ec6f9ed866e2ef6b6759ee092 Mon Sep 17 00:00:00 2001
From: Jan Kolarik <jkolarik@redhat.com>
Date: Thu, 25 Aug 2022 08:06:34 +0200
Subject: [PATCH] Add support for group upgrade rollback (RhBug:2016070)
= changelog =
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2016070
---
dnf/db/group.py | 8 +++++++-
dnf/transaction_sr.py | 24 ++++++++++++++++++++++++
2 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/dnf/db/group.py b/dnf/db/group.py
index 4dc8cb06..312e3b98 100644
--- a/dnf/db/group.py
+++ b/dnf/db/group.py
@@ -34,14 +34,16 @@ class PersistorBase(object):
self._installed = {}
self._removed = {}
self._upgraded = {}
+ self._downgraded = {}
def __len__(self):
- return len(self._installed) + len(self._removed) + len(self._upgraded)
+ return len(self._installed) + len(self._removed) + len(self._upgraded) + len(self._downgraded)
def clean(self):
self._installed = {}
self._removed = {}
self._upgraded = {}
+ self._downgraded = {}
def _get_obj_id(self, obj):
raise NotImplementedError
@@ -62,6 +64,10 @@ class PersistorBase(object):
self._upgraded[self._get_obj_id(obj)] = obj
self._add_to_history(obj, libdnf.transaction.TransactionItemAction_UPGRADE)
+ def downgrade(self, obj):
+ self._downgraded[self._get_obj_id(obj)] = obj
+ self._add_to_history(obj, libdnf.transaction.TransactionItemAction_DOWNGRADE)
+
def new(self, obj_id, name, translated_name, pkg_types):
raise NotImplementedError
diff --git a/dnf/transaction_sr.py b/dnf/transaction_sr.py
index dae8d300..5d403a3e 100644
--- a/dnf/transaction_sr.py
+++ b/dnf/transaction_sr.py
@@ -416,6 +416,16 @@ class TransactionReplay(object):
if swdb_group is not None:
self._base.history.group.upgrade(swdb_group)
+ def _swdb_group_downgrade(self, group_id, pkg_types, pkgs):
+ if not self._base.history.group.get(group_id):
+ self._raise_or_warn(self._ignore_installed, _("Group id '%s' is not installed.") % group_id)
+ return
+
+ swdb_group = self._create_swdb_group(group_id, pkg_types, pkgs)
+
+ if swdb_group is not None:
+ self._base.history.group.downgrade(swdb_group)
+
def _swdb_group_remove(self, group_id, pkg_types, pkgs):
if not self._base.history.group.get(group_id):
self._raise_or_warn(self._ignore_installed, _("Group id '%s' is not installed.") % group_id)
@@ -482,6 +492,16 @@ class TransactionReplay(object):
if swdb_env is not None:
self._base.history.env.upgrade(swdb_env)
+ def _swdb_environment_downgrade(self, env_id, pkg_types, groups):
+ if not self._base.history.env.get(env_id):
+ self._raise_or_warn(self._ignore_installed, _("Environment id '%s' is not installed.") % env_id)
+ return
+
+ swdb_env = self._create_swdb_environment(env_id, pkg_types, groups)
+
+ if swdb_env is not None:
+ self._base.history.env.downgrade(swdb_env)
+
def _swdb_environment_remove(self, env_id, pkg_types, groups):
if not self._base.history.env.get(env_id):
self._raise_or_warn(self._ignore_installed, _("Environment id '%s' is not installed.") % env_id)
@@ -535,6 +555,8 @@ class TransactionReplay(object):
self._swdb_group_install(group_id, pkg_types, group_data["packages"])
elif action == "Upgrade":
self._swdb_group_upgrade(group_id, pkg_types, group_data["packages"])
+ elif action == "Downgraded":
+ self._swdb_group_downgrade(group_id, pkg_types, group_data["packages"])
elif action == "Removed":
self._swdb_group_remove(group_id, pkg_types, group_data["packages"])
else:
@@ -564,6 +586,8 @@ class TransactionReplay(object):
self._swdb_environment_install(env_id, pkg_types, env_data["groups"])
elif action == "Upgrade":
self._swdb_environment_upgrade(env_id, pkg_types, env_data["groups"])
+ elif action == "Downgraded":
+ self._swdb_environment_downgrade(env_id, pkg_types, env_data["groups"])
elif action == "Removed":
self._swdb_environment_remove(env_id, pkg_types, env_data["groups"])
else:
--
2.37.1

View File

@ -0,0 +1,34 @@
From 46562dc76e50d86eed99a102af74a1187a4303e4 Mon Sep 17 00:00:00 2001
From: Nicola Sella <nsella@redhat.com>
Date: Thu, 11 Aug 2022 13:56:11 +0200
Subject: [PATCH] Fix upgrade from file to noarch pkg (RhBug:2006018)
= changelog =
msg: Fix upgrade pkg from file when installed pkg is noarch and upgrades
to a different arch
type: bugfix
resolves: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=2006018
---
dnf/base.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/dnf/base.py b/dnf/base.py
index 4ddfae15..aba411ea 100644
--- a/dnf/base.py
+++ b/dnf/base.py
@@ -2109,7 +2109,11 @@ class Base(object):
sltr.set(pkg=[pkg])
self._goal.upgrade(select=sltr)
return 1
- q = installed.filter(name=pkg.name, arch=[pkg.arch, "noarch"])
+ # do not filter by arch if the package is noarch
+ if pkg.arch == "noarch":
+ q = installed.filter(name=pkg.name)
+ else:
+ q = installed.filter(name=pkg.name, arch=[pkg.arch, "noarch"])
if not q:
msg = _("Package %s not installed, cannot update it.")
logger.warning(msg, pkg.name)
--
2.37.1

View File

@ -0,0 +1,62 @@
From 7a265cf17fe3531e45dde8ae622c496bef1e17ae Mon Sep 17 00:00:00 2001
From: Jan Kolarik <jkolarik@redhat.com>
Date: Wed, 10 Aug 2022 16:24:08 +0200
Subject: [PATCH] Allow passing plugin parameters with dashes in names
(RhBug:1980712)
= changelog =
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1980712
---
dnf/plugin.py | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/dnf/plugin.py b/dnf/plugin.py
index 87c1f08f..b083727d 100644
--- a/dnf/plugin.py
+++ b/dnf/plugin.py
@@ -225,17 +225,17 @@ def _get_plugins_files(paths, disable_plugins, enable_plugins):
matched = True
enable_pattern_tested = False
for pattern_skip in disable_plugins:
- if fnmatch.fnmatch(plugin_name, pattern_skip):
+ if _plugin_name_matches_pattern(plugin_name, pattern_skip):
pattern_disable_found.add(pattern_skip)
matched = False
for pattern_enable in enable_plugins:
- if fnmatch.fnmatch(plugin_name, pattern_enable):
+ if _plugin_name_matches_pattern(plugin_name, pattern_enable):
matched = True
pattern_enable_found.add(pattern_enable)
enable_pattern_tested = True
if not enable_pattern_tested:
for pattern_enable in enable_plugins:
- if fnmatch.fnmatch(plugin_name, pattern_enable):
+ if _plugin_name_matches_pattern(plugin_name, pattern_enable):
pattern_enable_found.add(pattern_enable)
if matched:
plugins.append(fn)
@@ -250,6 +250,20 @@ def _get_plugins_files(paths, disable_plugins, enable_plugins):
return plugins
+def _plugin_name_matches_pattern(plugin_name, pattern):
+ """
+ Checks plugin name matches the pattern.
+
+ The alternative plugin name using dashes instead of underscores is tried
+ in case of original name is not matched.
+
+ (see https://bugzilla.redhat.com/show_bug.cgi?id=1980712)
+ """
+
+ try_names = set((plugin_name, plugin_name.replace('_', '-')))
+ return any(fnmatch.fnmatch(name, pattern) for name in try_names)
+
+
def register_command(command_class):
# :api
"""A class decorator for automatic command registration."""
--
2.37.1

View File

@ -0,0 +1,97 @@
From 9700b8fabd102fcf289281c3c04238da90d7b28e Mon Sep 17 00:00:00 2001
From: Jan Kolarik <jkolarik@redhat.com>
Date: Tue, 13 Sep 2022 14:35:10 +0200
Subject: [PATCH] Fix plugins unit tests + unload plugins upon their deletion
=changelog=
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2134309
---
dnf/plugin.py | 8 ++++++--
tests/api/test_dnf_base.py | 24 +++++++++++++++++++-----
2 files changed, 25 insertions(+), 7 deletions(-)
diff --git a/dnf/plugin.py b/dnf/plugin.py
index b083727d..d2f46ce3 100644
--- a/dnf/plugin.py
+++ b/dnf/plugin.py
@@ -98,6 +98,9 @@ class Plugins(object):
self.plugin_cls = []
self.plugins = []
+ def __del__(self):
+ self._unload()
+
def _caller(self, method):
for plugin in self.plugins:
try:
@@ -164,8 +167,9 @@ class Plugins(object):
self._caller('transaction')
def _unload(self):
- logger.debug(_('Plugins were unloaded'))
- del sys.modules[DYNAMIC_PACKAGE]
+ if DYNAMIC_PACKAGE in sys.modules:
+ logger.log(dnf.logging.DDEBUG, 'Plugins were unloaded.')
+ del sys.modules[DYNAMIC_PACKAGE]
def unload_removed_plugins(self, transaction):
"""
diff --git a/tests/api/test_dnf_base.py b/tests/api/test_dnf_base.py
index e84e272b..19754b07 100644
--- a/tests/api/test_dnf_base.py
+++ b/tests/api/test_dnf_base.py
@@ -7,10 +7,23 @@ from __future__ import unicode_literals
import dnf
import dnf.conf
+import tests.support
+
from .common import TestCase
from .common import TOUR_4_4
+def conf_with_empty_plugins():
+ """
+ Use empty configuration to avoid importing plugins from default paths
+ which would lead to crash of other tests.
+ """
+ conf = tests.support.FakeConf()
+ conf.plugins = True
+ conf.pluginpath = []
+ return conf
+
+
class DnfBaseApiTest(TestCase):
def setUp(self):
self.base = dnf.Base(dnf.conf.Conf())
@@ -75,13 +88,12 @@ class DnfBaseApiTest(TestCase):
self.assertHasType(self.base.transaction, dnf.db.group.RPMTransaction)
def test_init_plugins(self):
- # Base.init_plugins(disabled_glob=(), enable_plugins=(), cli=None)
+ # Base.init_plugins()
self.assertHasAttr(self.base, "init_plugins")
- # disable plugins to avoid calling dnf.plugin.Plugins._load() multiple times
- # which causes the tests to crash
- self.base.conf.plugins = False
- self.base.init_plugins(disabled_glob=(), enable_plugins=(), cli=None)
+ self.base._conf = conf_with_empty_plugins()
+
+ self.base.init_plugins()
def test_pre_configure_plugins(self):
# Base.pre_configure_plugins()
@@ -99,6 +111,8 @@ class DnfBaseApiTest(TestCase):
# Base.unload_plugins()
self.assertHasAttr(self.base, "unload_plugins")
+ self.base._conf = conf_with_empty_plugins()
+
self.base.init_plugins()
self.base.unload_plugins()
--
2.37.3

View File

@ -0,0 +1,31 @@
From c9251d182be0bfa66345220cffe0842b44a061a8 Mon Sep 17 00:00:00 2001
From: Jan Kolarik <jkolarik@redhat.com>
Date: Wed, 31 Aug 2022 07:49:39 +0200
Subject: [PATCH] Move system-upgrade plugin to core (RhBug:2054235)
Just doc fix.
= changelog =
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2054235
---
doc/command_ref.rst | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/doc/command_ref.rst b/doc/command_ref.rst
index 7a02448c..bee6a109 100644
--- a/doc/command_ref.rst
+++ b/doc/command_ref.rst
@@ -182,8 +182,7 @@ Options
``--downloaddir=<path>, --destdir=<path>``
Redirect downloaded packages to provided directory. The option has to be used together with the \-\
:ref:`-downloadonly <downloadonly-label>` command line option, with the
- ``download``, ``modulesync`` or ``reposync`` commands (dnf-plugins-core) or with the ``system-upgrade`` command
- (dnf-plugins-extras).
+ ``download``, ``modulesync``, ``reposync`` or ``system-upgrade`` commands (dnf-plugins-core).
.. _downloadonly-label:
--
2.37.3

View File

@ -0,0 +1,58 @@
From a32b2f7d596247124ad6ff5ab71bc83bf78f0518 Mon Sep 17 00:00:00 2001
From: Jan Kolarik <jkolarik@redhat.com>
Date: Tue, 13 Sep 2022 13:55:35 +0200
Subject: [PATCH] Add support for rollback of group upgrade rollback
(RhBug:2016070)
= changelog =
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2016070
---
dnf/transaction_sr.py | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/dnf/transaction_sr.py b/dnf/transaction_sr.py
index 5d403a3e..b389f152 100644
--- a/dnf/transaction_sr.py
+++ b/dnf/transaction_sr.py
@@ -553,12 +553,14 @@ class TransactionReplay(object):
if action == "Install":
self._swdb_group_install(group_id, pkg_types, group_data["packages"])
- elif action == "Upgrade":
- self._swdb_group_upgrade(group_id, pkg_types, group_data["packages"])
- elif action == "Downgraded":
- self._swdb_group_downgrade(group_id, pkg_types, group_data["packages"])
elif action == "Removed":
self._swdb_group_remove(group_id, pkg_types, group_data["packages"])
+ # Groups are not versioned, but a reverse transaction could be applied,
+ # therefore we treat both actions the same way
+ elif action == "Upgrade" or action == "Upgraded":
+ self._swdb_group_upgrade(group_id, pkg_types, group_data["packages"])
+ elif action == "Downgrade" or action == "Downgraded":
+ self._swdb_group_downgrade(group_id, pkg_types, group_data["packages"])
else:
errors.append(TransactionError(
_('Unexpected value of group action "{action}" for group "{group}".')
@@ -584,12 +586,14 @@ class TransactionReplay(object):
if action == "Install":
self._swdb_environment_install(env_id, pkg_types, env_data["groups"])
- elif action == "Upgrade":
- self._swdb_environment_upgrade(env_id, pkg_types, env_data["groups"])
- elif action == "Downgraded":
- self._swdb_environment_downgrade(env_id, pkg_types, env_data["groups"])
elif action == "Removed":
self._swdb_environment_remove(env_id, pkg_types, env_data["groups"])
+ # Environments are not versioned, but a reverse transaction could be applied,
+ # therefore we treat both actions the same way
+ elif action == "Upgrade" or action == "Upgraded":
+ self._swdb_environment_upgrade(env_id, pkg_types, env_data["groups"])
+ elif action == "Downgrade" or action == "Downgraded":
+ self._swdb_environment_downgrade(env_id, pkg_types, env_data["groups"])
else:
errors.append(TransactionError(
_('Unexpected value of environment action "{action}" for environment "{env}".')
--
2.37.3

View File

@ -0,0 +1,95 @@
From 97fe94c94f030f5596a3a3ac52748bdd7544ad52 Mon Sep 17 00:00:00 2001
From: Jan Kolarik <jkolarik@redhat.com>
Date: Tue, 1 Nov 2022 09:15:08 +0000
Subject: [PATCH] Document changes to offline-upgrade command (RhBug:1939975)
A support for security filters was added to the offline-upgrade command. This commit adds the documentation into the man pages.
= changelog =
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1939975
---
doc/command_ref.rst | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/doc/command_ref.rst b/doc/command_ref.rst
index bee6a109..7279b3a4 100644
--- a/doc/command_ref.rst
+++ b/doc/command_ref.rst
@@ -113,7 +113,7 @@ Options
``--advisory=<advisory>, --advisories=<advisory>``
Include packages corresponding to the advisory ID, Eg. FEDORA-2201-123.
- Applicable for the install, repoquery, updateinfo and upgrade commands.
+ Applicable for the ``install``, ``repoquery``, ``updateinfo``, ``upgrade`` and ``offline-upgrade`` (dnf-plugins-core) commands.
``--allowerasing``
Allow erasing of installed packages to resolve dependencies. This option could be used as an alternative to the ``yum swap`` command where packages to remove are not explicitly defined.
@@ -125,12 +125,12 @@ Options
Try the best available package versions in transactions. Specifically during :ref:`dnf upgrade <upgrade_command-label>`, which by default skips over updates that can not be installed for dependency reasons, the switch forces DNF to only consider the latest packages. When running into packages with broken dependencies, DNF will fail giving a reason why the latest version can not be installed.
``--bugfix``
- Include packages that fix a bugfix issue. Applicable for the install, repoquery, updateinfo and
- upgrade commands.
+ Include packages that fix a bugfix issue.
+ Applicable for the ``install``, ``repoquery``, ``updateinfo``, ``upgrade`` and ``offline-upgrade`` (dnf-plugins-core) commands.
``--bz=<bugzilla>, --bzs=<bugzilla>``
- Include packages that fix a Bugzilla ID, Eg. 123123. Applicable for the install, repoquery,
- updateinfo and upgrade commands.
+ Include packages that fix a Bugzilla ID, Eg. 123123.
+ Applicable for the ``install``, ``repoquery``, ``updateinfo``, ``upgrade`` and ``offline-upgrade`` (dnf-plugins-core) commands.
``-C, --cacheonly``
Run entirely from system cache, don't update the cache and use it even in case it is expired.
@@ -148,8 +148,8 @@ Options
``--cve=<cves>, --cves=<cves>``
Include packages that fix a CVE (Common Vulnerabilities and Exposures) ID
- (http://cve.mitre.org/about/), Eg. CVE-2201-0123. Applicable for the install, repoquery, updateinfo,
- and upgrade commands.
+ (http://cve.mitre.org/about/), Eg. CVE-2201-0123.
+ Applicable for the ``install``, ``repoquery``, ``updateinfo``, ``upgrade`` and ``offline-upgrade`` (dnf-plugins-core) commands.
``-d <debug level>, --debuglevel=<debug level>``
Debugging output level. This is an integer value between 0 (no additional information strings) and 10 (shows all debugging information, even that not understandable to the user), default is 2. Deprecated, use ``-v`` instead.
@@ -208,8 +208,8 @@ Options
Enable additional repositories by an id or a glob.
``--enhancement``
- Include enhancement relevant packages. Applicable for the install, repoquery, updateinfo and
- upgrade commands.
+ Include enhancement relevant packages.
+ Applicable for the ``install``, ``repoquery``, ``updateinfo``, ``upgrade`` and ``offline-upgrade`` (dnf-plugins-core) commands.
.. _exclude_option-label:
@@ -280,8 +280,8 @@ Options
``--setopt`` using configuration from ``/path/dnf.conf``.
``--newpackage``
- Include newpackage relevant packages. Applicable for the install, repoquery, updateinfo and
- upgrade commands.
+ Include newpackage relevant packages.
+ Applicable for the ``install``, ``repoquery``, ``updateinfo``, ``upgrade`` and ``offline-upgrade`` (dnf-plugins-core) commands.
``--noautoremove``
Disable removal of dependencies that are no longer used. It sets
@@ -353,11 +353,11 @@ Options
``--sec-severity=<severity>, --secseverity=<severity>``
Includes packages that provide a fix for an issue of the specified severity.
- Applicable for the install, repoquery, updateinfo and upgrade commands.
+ Applicable for the ``install``, ``repoquery``, ``updateinfo``, ``upgrade`` and ``offline-upgrade`` (dnf-plugins-core) commands.
``--security``
- Includes packages that provide a fix for a security issue. Applicable for the
- upgrade command.
+ Includes packages that provide a fix for a security issue.
+ Applicable for the ``install``, ``repoquery``, ``updateinfo``, ``upgrade`` and ``offline-upgrade`` (dnf-plugins-core) commands.
.. _setopt_option-label:
--
2.38.1

View File

@ -0,0 +1,114 @@
From f1fbef17862e033bf9518bd318339b405f2664dd Mon Sep 17 00:00:00 2001
From: Nicola Sella <nsella@redhat.com>
Date: Mon, 22 Mar 2021 17:37:51 +0100
Subject: [PATCH 1/2] Better explain traceback of rpm.error with dnf
=changelog=
msg: Add dnf.error message to explain rpm.error traceback when package not found after resolving a transaction
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1815327
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1887293
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1909845
---
dnf/db/group.py | 78 ++++++++++++++++++++++++++-----------------------
1 file changed, 41 insertions(+), 37 deletions(-)
diff --git a/dnf/db/group.py b/dnf/db/group.py
index 312e3b98..3a17019a 100644
--- a/dnf/db/group.py
+++ b/dnf/db/group.py
@@ -26,6 +26,7 @@ import dnf.exceptions
from dnf.i18n import _
from dnf.util import logger
+import rpm
class PersistorBase(object):
def __init__(self, history):
@@ -316,43 +317,46 @@ class RPMTransaction(object):
modular_problems = 0
for tsi in self:
- if tsi.action == libdnf.transaction.TransactionItemAction_DOWNGRADE:
- hdr = tsi.pkg._header
- modular_problems += self._test_fail_safe(hdr, tsi.pkg)
- ts.addInstall(hdr, tsi, 'u')
- elif tsi.action == libdnf.transaction.TransactionItemAction_DOWNGRADED:
- ts.addErase(tsi.pkg.idx)
- elif tsi.action == libdnf.transaction.TransactionItemAction_INSTALL:
- hdr = tsi.pkg._header
- modular_problems += self._test_fail_safe(hdr, tsi.pkg)
- ts.addInstall(hdr, tsi, 'i')
- elif tsi.action == libdnf.transaction.TransactionItemAction_OBSOLETE:
- hdr = tsi.pkg._header
- modular_problems += self._test_fail_safe(hdr, tsi.pkg)
- ts.addInstall(hdr, tsi, 'u')
- elif tsi.action == libdnf.transaction.TransactionItemAction_OBSOLETED:
- ts.addErase(tsi.pkg.idx)
- elif tsi.action == libdnf.transaction.TransactionItemAction_REINSTALL:
- # note: in rpm 4.12 there should not be set
- # rpm.RPMPROB_FILTER_REPLACEPKG to work
- hdr = tsi.pkg._header
- modular_problems += self._test_fail_safe(hdr, tsi.pkg)
- ts.addReinstall(hdr, tsi)
- elif tsi.action == libdnf.transaction.TransactionItemAction_REINSTALLED:
- # Required when multiple packages with the same NEVRA marked as installed
- ts.addErase(tsi.pkg.idx)
- elif tsi.action == libdnf.transaction.TransactionItemAction_REMOVE:
- ts.addErase(tsi.pkg.idx)
- elif tsi.action == libdnf.transaction.TransactionItemAction_UPGRADE:
- hdr = tsi.pkg._header
- modular_problems += self._test_fail_safe(hdr, tsi.pkg)
- ts.addInstall(hdr, tsi, 'u')
- elif tsi.action == libdnf.transaction.TransactionItemAction_UPGRADED:
- ts.addErase(tsi.pkg.idx)
- elif tsi.action == libdnf.transaction.TransactionItemAction_REASON_CHANGE:
- pass
- else:
- raise RuntimeError("TransactionItemAction not handled: %s" % tsi.action)
+ try:
+ if tsi.action == libdnf.transaction.TransactionItemAction_DOWNGRADE:
+ hdr = tsi.pkg._header
+ modular_problems += self._test_fail_safe(hdr, tsi.pkg)
+ ts.addInstall(hdr, tsi, 'u')
+ elif tsi.action == libdnf.transaction.TransactionItemAction_DOWNGRADED:
+ ts.addErase(tsi.pkg.idx)
+ elif tsi.action == libdnf.transaction.TransactionItemAction_INSTALL:
+ hdr = tsi.pkg._header
+ modular_problems += self._test_fail_safe(hdr, tsi.pkg)
+ ts.addInstall(hdr, tsi, 'i')
+ elif tsi.action == libdnf.transaction.TransactionItemAction_OBSOLETE:
+ hdr = tsi.pkg._header
+ modular_problems += self._test_fail_safe(hdr, tsi.pkg)
+ ts.addInstall(hdr, tsi, 'u')
+ elif tsi.action == libdnf.transaction.TransactionItemAction_OBSOLETED:
+ ts.addErase(tsi.pkg.idx)
+ elif tsi.action == libdnf.transaction.TransactionItemAction_REINSTALL:
+ # note: in rpm 4.12 there should not be set
+ # rpm.RPMPROB_FILTER_REPLACEPKG to work
+ hdr = tsi.pkg._header
+ modular_problems += self._test_fail_safe(hdr, tsi.pkg)
+ ts.addReinstall(hdr, tsi)
+ elif tsi.action == libdnf.transaction.TransactionItemAction_REINSTALLED:
+ # Required when multiple packages with the same NEVRA marked as installed
+ ts.addErase(tsi.pkg.idx)
+ elif tsi.action == libdnf.transaction.TransactionItemAction_REMOVE:
+ ts.addErase(tsi.pkg.idx)
+ elif tsi.action == libdnf.transaction.TransactionItemAction_UPGRADE:
+ hdr = tsi.pkg._header
+ modular_problems += self._test_fail_safe(hdr, tsi.pkg)
+ ts.addInstall(hdr, tsi, 'u')
+ elif tsi.action == libdnf.transaction.TransactionItemAction_UPGRADED:
+ ts.addErase(tsi.pkg.idx)
+ elif tsi.action == libdnf.transaction.TransactionItemAction_REASON_CHANGE:
+ pass
+ else:
+ raise RuntimeError("TransactionItemAction not handled: %s" % tsi.action)
+ except rpm.error as e:
+ raise dnf.exceptions.Error(_("An rpm exception occurred: %s" % e))
if modular_problems:
raise dnf.exceptions.Error(_("No available modular metadata for modular package"))
--
2.39.0

View File

@ -0,0 +1,50 @@
From 23742561dcb168604d9668815a8c1ebbdf516d39 Mon Sep 17 00:00:00 2001
From: Jan Kolarik <jkolarik@redhat.com>
Date: Wed, 23 Nov 2022 08:44:41 +0000
Subject: [PATCH 2/2] Ignore processing variable files with unsupported
encoding (RhBug:2141215)
This issue could be seen for example when there are some temporary files stored by text editors in the `/etc/dnf/vars` folder. These files could be in the binary format and causes `UnicodeDecodeError` exception to be thrown during processing of the var files.
= changelog =
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2141215
---
dnf/conf/substitutions.py | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/dnf/conf/substitutions.py b/dnf/conf/substitutions.py
index 1281bdf0..4d0f0d55 100644
--- a/dnf/conf/substitutions.py
+++ b/dnf/conf/substitutions.py
@@ -18,13 +18,15 @@
# Red Hat, Inc.
#
+import logging
import os
import re
-import dnf
-import dnf.exceptions
+from dnf.i18n import _
ENVIRONMENT_VARS_RE = re.compile(r'^DNF_VAR_[A-Za-z0-9_]+$')
+logger = logging.getLogger('dnf')
+
class Substitutions(dict):
# :api
@@ -60,7 +62,8 @@ class Substitutions(dict):
val = fp.readline()
if val and val[-1] == '\n':
val = val[:-1]
- except (OSError, IOError):
+ except (OSError, IOError, UnicodeDecodeError) as e:
+ logger.warning(_("Error when parsing a variable from file '{0}': {1}").format(filepath, e))
continue
if val is not None:
self[fsvar] = val
--
2.39.0

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
From 33c354ed52be8f8fa2d43aff8ba1fe1540e1744c Mon Sep 17 00:00:00 2001
From: Kyle Walker <kwalker@redhat.com>
Date: Tue, 20 Dec 2022 08:42:03 -0500
Subject: [PATCH] Omit src RPMs from check-update (RhBug: 2151910)
The current check-update operation relies on src RPMs not being included
in the available repos. When those repos are enabled, *.src RPMs can be
emitted as updates that are available. Those RPMs are not updated in the
traditional fashion and can cause confusion to end users.
This change unconditionally filters out src packages in the
_list_patterns() callpath.
= changelog =
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2151910
---
dnf/base.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/dnf/base.py b/dnf/base.py
index aba411e..8c19276 100644
--- a/dnf/base.py
+++ b/dnf/base.py
@@ -1519,6 +1519,8 @@ class Base(object):
updates = query_for_repo(q).filterm(upgrades_by_priority=True)
# reduce a query to security upgrades if they are specified
updates = self._merge_update_filters(updates, upgrade=True)
+ # reduce a query to remove src RPMs
+ updates.filterm(arch__neq=['src', 'nosrc'])
# reduce a query to latest packages
updates = updates.latest().run()
@@ -1571,6 +1573,8 @@ class Base(object):
self.sack.query()).filter(obsoletes_by_priority=inst)
# reduce a query to security upgrades if they are specified
obsoletes = self._merge_update_filters(obsoletes, warning=False, upgrade=True)
+ # reduce a query to remove src RPMs
+ obsoletes.filterm(arch__neq=['src', 'nosrc'])
obsoletesTuples = []
for new in obsoletes:
obsoleted_reldeps = new.obsoletes
--
libgit2 1.3.2

View File

@ -0,0 +1,56 @@
From 2658062d4c176201d0decf03929a89b44761c072 Mon Sep 17 00:00:00 2001
From: Marek Blaha <mblaha@redhat.com>
Date: Mon, 3 Apr 2023 12:19:40 +0200
Subject: [PATCH] Backport: automatic: Fix online detection with proxy (RhBz:2022440)
In case the proxy is configured (either for a repo of globally) it is
used also for detecting whether the system is online.
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2022440
---
dnf/automatic/main.py | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/dnf/automatic/main.py b/dnf/automatic/main.py
index b53d9c0..93ce13c 100644
--- a/dnf/automatic/main.py
+++ b/dnf/automatic/main.py
@@ -251,21 +251,29 @@ def wait_for_network(repos, timeout):
'http': 80,
'https': 443,
'ftp': 21,
+ 'socks': 1080,
+ 'socks5': 1080,
}
def remote_address(url_list):
for url in url_list:
parsed_url = dnf.pycomp.urlparse.urlparse(url)
- if parsed_url.hostname and parsed_url.scheme in remote_schemes:
- yield (parsed_url.hostname,
- parsed_url.port or remote_schemes[parsed_url.scheme])
+ if (not parsed_url.hostname) \
+ or (not parsed_url.port and parsed_url.scheme not in remote_schemes):
+ # skip urls without hostname or without recognized port
+ continue
+ yield (parsed_url.hostname,
+ parsed_url.port or remote_schemes[parsed_url.scheme])
# collect possible remote repositories urls
addresses = set()
for repo in repos.iter_enabled():
- addresses.update(remote_address(repo.baseurl))
- addresses.update(remote_address([repo.mirrorlist]))
- addresses.update(remote_address([repo.metalink]))
+ if repo.proxy:
+ addresses.update(remote_address([repo.proxy]))
+ else:
+ addresses.update(remote_address(repo.baseurl))
+ addresses.update(remote_address([repo.mirrorlist]))
+ addresses.update(remote_address([repo.metalink]))
if not addresses:
# there is no remote repository enabled so network connection should not be needed
--
libgit2 1.3.2

View File

@ -0,0 +1,39 @@
From 46aeabda1980621ca0f87528e0a81b4f59d886f0 Mon Sep 17 00:00:00 2001
From: Jan Kolarik <jkolarik@redhat.com>
Date: Thu, 20 Apr 2023 10:10:14 +0000
Subject: [PATCH] automatic: Return an error when transaction fails
(RhBug:2170093)
In case of no global error occurred within the transaction, we still need to check state of individual transaction items for any failure.
This is matching the logic in `BaseCli.do_transaction` method, where the error is emitted after printing the transaction summary.
= changelog =
msg: automatic: Return an error when transaction fails
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2170093
---
dnf/automatic/main.py | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/dnf/automatic/main.py b/dnf/automatic/main.py
index 93ce13cc..ccd9ab64 100644
--- a/dnf/automatic/main.py
+++ b/dnf/automatic/main.py
@@ -346,6 +346,13 @@ def main(args):
gpgsigcheck(base, trans.install_set)
base.do_transaction()
+
+ # In case of no global error occurred within the transaction,
+ # we need to check state of individual transaction items.
+ for tsi in trans:
+ if tsi.state == libdnf.transaction.TransactionItemState_ERROR:
+ raise dnf.exceptions.Error(_('Transaction failed'))
+
emitters.notify_applied()
emitters.commit()
except dnf.exceptions.Error as exc:
--
2.40.1

View File

@ -0,0 +1,63 @@
From a74209ff53c9a51293b45434196dff49002c5691 Mon Sep 17 00:00:00 2001
From: Evan Goode <mail@evangoo.de>
Date: Tue, 30 May 2023 20:48:54 +0000
Subject: [PATCH] Document symbols in `dnf history list` output
This patch adds documentation for the symbols shown in the "Action(s)"
and "Altered" columns of `dnf history list`
The "Action(s)" column abbreviates the names of transaction actions when
there was more than one action, e.g. a transaction that both installs
and upgrades packages would be displayed as "I, U".
The "Altered" column prints some extra symbols when something unusual
happened with the transaction, like if any warnings were printed or if
it completed with a non-zero status.
Some language was taken from the yum man pages:
https://github.com/rpm-software-management/yum/blob/master/docs/yum.8.
It appears we no longer use the "P" or "s" symbols.
Resolves https://bugzilla.redhat.com/show_bug.cgi?id=2172067
(RhBug:2172067)
= changelog =
msg: Document the symbols in the output of `dnf history list`
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2172067
---
doc/command_ref.rst | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/doc/command_ref.rst b/doc/command_ref.rst
index 7279b3a4..f8149e86 100644
--- a/doc/command_ref.rst
+++ b/doc/command_ref.rst
@@ -701,6 +701,24 @@ transactions and act according to this information (assuming the
which specifies a transaction by a package which it manipulated. When no
transaction is specified, list all known transactions.
+ The "Action(s)" column lists each type of action taken in the transaction. The possible values are:
+
+ * Install (I): a new package was installed on the system
+ * Downgrade (D): an older version of a package replaced the previously-installed version
+ * Obsolete (O): an obsolete package was replaced by a new package
+ * Upgrade (U): a newer version of the package replaced the previously-installed version
+ * Remove (E): a package was removed from the system
+ * Reinstall (R): a package was reinstalled with the same version
+ * Reason change (C): a package was kept in the system but its reason for being installed changed
+
+ The "Altered" column lists the number of actions taken in each transaction, possibly followed by one or two the following symbols:
+
+ * ``>``: The RPM database was changed, outside DNF, after the transaction
+ * ``<``: The RPM database was changed, outside DNF, before the transaction
+ * ``*``: The transaction aborted before completion
+ * ``#``: The transaction completed, but with a non-zero status
+ * ``E``: The transaction completed successfully, but had warning/error output
+
``--reverse``
The order of ``history list`` output is printed in reverse order.
--
2.40.1

View File

@ -0,0 +1,85 @@
From 29f4df4bf7bf7cb9099dbc7c834441ce4e75b623 Mon Sep 17 00:00:00 2001
From: Miro Hrončok <miro@hroncok.cz>
Date: Wed, 23 Feb 2022 13:25:12 +0100
Subject: [PATCH] RHEL-1245: Remove /usr/bin from sys.path to avoid accidentally importing garbage
See https://bugzilla.redhat.com/show_bug.cgi?id=2057340
and https://github.com/benjaminp/six/issues/359
dnf should never import Python modules from /usr/bin but users can
have files in there that look like Python modules and Python will
try to import them and fail.
Consider a tool that is *not* written in Python and is called "copy.pyc".
Naturally, it resides in /usr/bin/copy.pyc and dnf fails:
Traceback (most recent call last):
File "/usr/bin/dnf", line 57, in <module>
from dnf.cli import main
File "/usr/lib/python3.10/site-packages/dnf/__init__.py", line 30, in <module>
import dnf.base
File "/usr/lib/python3.10/site-packages/dnf/base.py", line 31, in <module>
from copy import deepcopy
ImportError: bad magic number in 'copy': b'...'
Similarly, a tool actually written in Python, called "copy.py"
might as well own /usr/bin/copy.py and dnf fails as well:
Traceback (most recent call last):
File "/usr/bin/dnf", line 57, in <module>
from dnf.cli import main
File "/usr/lib/python3.10/site-packages/dnf/__init__.py", line 30, in <module>
import dnf.base
File "/usr/lib/python3.10/site-packages/dnf/base.py", line 31, in <module>
from copy import deepcopy
ImportError: cannot import name 'deepcopy' from 'copy' (/usr/bin/copy.py)
Either problem can happen for a variety of names.
We better not let that happen.
A more general solution that would prevent Python doing this entirely
does not exists yet, see https://discuss.python.org/t/4235
Hence, proposing this to dnf, which is a critical piece of the system.
---
bin/dnf-automatic.in | 6 +++++-
bin/dnf.in | 6 +++++-
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/bin/dnf-automatic.in b/bin/dnf-automatic.in
index 5b06aa2..17e35a0 100755
--- a/bin/dnf-automatic.in
+++ b/bin/dnf-automatic.in
@@ -23,7 +23,11 @@ import os
import sys
here = sys.path[0]
-if here != '/usr/bin':
+if here == '/usr/bin':
+ # we never import Python modules from /usr/bin
+ # removing this lowers the risk of accidental imports of weird files
+ del sys.path[0]
+else:
# git checkout
dnf_toplevel = os.path.dirname(here)
sys.path[0] = dnf_toplevel
diff --git a/bin/dnf.in b/bin/dnf.in
index 645d0f0..55ceb3f 100755
--- a/bin/dnf.in
+++ b/bin/dnf.in
@@ -48,7 +48,11 @@ if __name__ != "__main__":
sys.exit(1)
here = sys.path[0]
-if here != '/usr/bin':
+if here == '/usr/bin':
+ # we never import Python modules from /usr/bin
+ # removing this lowers the risk of accidental imports of weird files
+ del sys.path[0]
+else:
# git checkout
import os
dnf_toplevel = os.path.dirname(here)
--
libgit2 1.6.4

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,53 @@
From 8bc3b7a217de41c0a9bc52cd9cac50cde9e9ee65 Mon Sep 17 00:00:00 2001
From: Anish Bhatt <anish.bhatt@salesforce.com>
Date: Mon, 10 Jul 2023 10:09:17 -0700
Subject: [PATCH] When parsing over a KVP list, do not return till the whole
list is parsed
---
dnf/repodict.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dnf/repodict.py b/dnf/repodict.py
index ffa0f8ed..82c05ac0 100644
--- a/dnf/repodict.py
+++ b/dnf/repodict.py
@@ -79,8 +79,8 @@ class RepoDict(dict):
if isinstance(value, str):
substituted.append(
libdnf.conf.ConfigParser.substitute(value, conf.substitutions))
- if substituted:
- return substituted
+ if substituted:
+ return substituted
return values
repo = dnf.repo.Repo(repoid, conf)
--
2.41.0
From 89c6f3633f55acd31d44a487ce76dd89c12d795c Mon Sep 17 00:00:00 2001
From: Anish Bhatt <anish.bhatt@salesforce.com>
Date: Mon, 10 Jul 2023 10:10:30 -0700
Subject: [PATCH] Add to authors
---
AUTHORS | 1 +
1 file changed, 1 insertion(+)
diff --git a/AUTHORS b/AUTHORS
index 0077c7ea..eb1e0121 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -63,6 +63,7 @@ DNF CONTRIBUTORS
Adam Williamson <awilliam@redhat.com>
Albert Uchytil <auchytil@redhat.com>
Alberto Ruiz <aruiz@redhat.com>
+ Anish Bhatt <anish.bhatt@salesforce.com>
Baurzhan Muftakhidinov <baurthefirst@gmail.com>
Christopher Meng <cickumqt@gmail.com>
Daniel Mach <dmach@redhat.com>
--
2.41.0

View File

@ -0,0 +1,32 @@
diff -aruN dnf-4.7.0/dnf/const.py.in dnf-4.7.0_alma/dnf/const.py.in
--- dnf-4.7.0/dnf/const.py.in 2021-04-12 18:26:33.000000000 +0300
+++ dnf-4.7.0_alma/dnf/const.py.in 2021-12-30 10:30:33.806575400 +0300
@@ -55,4 +55,4 @@
USER_AGENT = "dnf/%s" % VERSION
BUGTRACKER_COMPONENT=NAME.lower()
-BUGTRACKER='https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora&component=%s' % BUGTRACKER_COMPONENT
+BUGTRACKER='https://bugs.almalinux.org/'
diff -aruN dnf-4.7.0/doc/conf.py.in dnf-4.7.0_alma/doc/conf.py.in
--- dnf-4.7.0/doc/conf.py.in 2021-04-12 18:26:33.000000000 +0300
+++ dnf-4.7.0_alma/doc/conf.py.in 2021-12-30 10:34:07.810855800 +0300
@@ -267,5 +267,5 @@
.. _DNF: https://github.com/rpm-software-management/dnf/
.. _hawkey: http://rpm-software-management.github.io/hawkey/
.. _YUM: http://yum.baseurl.org/
-.. _bugzilla: https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora&component=dnf
+.. _bugzilla: https://bugs.almalinux.org/
"""
diff -aruN dnf-4.7.0/tests/test_config.py dnf-4.7.0_alma/tests/test_config.py
--- dnf-4.7.0/tests/test_config.py 2021-04-12 18:26:33.000000000 +0300
+++ dnf-4.7.0_alma/tests/test_config.py 2021-12-30 10:33:24.147815500 +0300
@@ -55,8 +55,7 @@
def test_bugtracker(self):
conf = Conf()
self.assertEqual(conf.bugtracker_url,
- "https://bugzilla.redhat.com/enter_bug.cgi" +
- "?product=Fedora&component=dnf")
+ "https://bugs.almalinux.org/")
def test_conf_from_file(self):
conf = Conf()

View File

@ -1,11 +1,14 @@
# Always build out-of-source
%define __cmake_in_source_build 1
# default dependencies
%global hawkey_version 0.55.0-5
%global hawkey_version 0.63.0-8
%global libcomps_version 0.1.8
%global libmodulemd_version 1.4.0
%global libmodulemd_version 2.9.3
%global rpm_version 4.14.2-35
# conflicts
%global conflicts_dnf_plugins_core_version 4.0.16
%global conflicts_dnf_plugins_core_version 4.0.20
%global conflicts_dnf_plugins_extras_version 4.0.4
%global conflicts_dnfdaemon_version 0.3.19
@ -24,19 +27,6 @@
%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)
@ -64,13 +54,7 @@
%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
%global py3pluginpath %{python3_sitelib}/%{name}-plugins
# Use the same directory of the main package for subpackage licence and docs
%global _docdir_fmt %{name}
@ -81,53 +65,68 @@
It supports RPMs, modules and comps groups & environments.
Name: dnf
Version: 4.4.2
Release: 11%{?dist}
Version: 4.7.0
Release: 20%{?dist}.alma
Summary: %{pkg_summary}
# For a breakdown of the licensing, see PACKAGE-LICENSING
License: GPLv2+ and GPLv2 and GPL
License: GPLv2+
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
Patch19: 0019-Update-translations.patch
# Patches are stored in the upstream repository in this branch:
# https://github.com/rpm-software-management/dnf/tree/rhel-8.7
# Use "git format-patch -N" to regenerate the patch files.
Patch0001: 0001-Set-top-level-directory-for-unittest.patch
Patch0002: 0002-dnf-rpm-miscutils.py-fix-usage-of-_.patch
Patch0003: 0003-Pass-the-package-to-rpmkeys-stdin.patch
Patch0004: 0004-Use-rpmkeys-alone-to-verify-signature.patch
Patch0005: 0005-Lower-_pkgverify_level-to-signature-for-signature-ch.patch
Patch0006: 0006-add-default-colors-to-documentation.patch
Patch0007: 0007-add-author.patch
Patch0008: 0008-update-colors-according-to-libdnf-libdnf-conf-Config.patch
Patch0009: 0009-Fix-reporting-irrecoverable-errors-on-packages-downl.patch
Patch0010: 0010-Add-fail_fast-parameter-to-download_payloads-methods.patch
Patch0011: 0011-comps-Make-the-install_or_skip-method-not-catch-Comp.patch
Patch0012: 0012-doc-Improve-description-of-multilib_policy-all-RhBug.patch
Patch0013: 0013-Fix-Python-dnf-API-does-not-respect-cacheonly-RhBug-.patch
Patch0014: 0014-Documentation-API-notes-for-cacheonly.patch
Patch0015: 0015-Allow-destdir-option-with-modulesync-command.patch
Patch0016: 0016-Update-translations-RhBug-2017270.patch
Patch0017: 0017-Add-documentation-for-query-api-flags.patch
Patch0018: 0018-Fix-processing-of-download-errors-RhBug-2024527.patch
Patch0019: 0019-Fix-unittests-that-relied-on-checksum-being-at-the-e.patch
Patch0020: 0020-cli-commands-history-Fix-history-undo-on-a-Reason-Ch.patch
Patch0021: 0021-Fix-remove-when-no-repos-are-enabled-RhBz-2064341.patch
Patch0022: 0022-doc-Improve-proxy-configuration-option-documentation.patch
Patch0023: 0023-Base.reset-plug-temporary-leak-of-libsolv-s-page-fil.patch
Patch0024: 0024-doc-Describe-how-gpg-keys-are-stored-for-repo_ggpche.patch
Patch0025: 0025-Add-only-relevant-pkgs-to-upgrade-transaction-RhBug-.patch
Patch0026: 0026-Use-installed_all-because-installed_query-is-filtere.patch
Patch0027: 0027-Don-t-include-resolved-advisories-for-obsoletes-with.patch
Patch0028: 0028-Set-default-value-for-variable-to-prevent-crash-RhBu.patch
Patch0029: 0029-Add-doc-related-to-destdir-and-downloadonly-options-.patch
Patch0030: 0030-Expose-plugin-unload-method-to-API-RhBug-2047251.patch
Patch0031: 0031-Add-support-for-group-upgrade-rollback-RhBug-2016070.patch
Patch0032: 0032-Fix-upgrade-from-file-to-noarch-pkg-RhBug-2006018.patch
Patch0033: 0033-Allow-passing-plugin-parameters-with-dashes-in-names.patch
Patch0034: 0034-Fix-plugins-unit-tests-unload-plugins-upon-their-del.patch
Patch0035: 0035-Move-system-upgrade-plugin-to-core-RhBug-2054235.patch
Patch0036: 0036-Add-support-for-rollback-of-group-upgrade-rollback-R.patch
Patch0037: 0037-Document-changes-to-offline-upgrade-command.patch
Patch0038: 0038-Better-explain-traceback-of-rpm.error-with-dnf.patch
Patch0039: 0039-Ignore-processing-variable-files-with-unsupported-en.patch
Patch0040: 0040-Update-translations.patch
Patch0041: 0041-Omit-src-RPMs-from-check-update-RhBug-2151910.patch
Patch0042: 0042-Backport-automatic-Fix-onl-detect-proxy-RhBz2022440.patch
Patch0043: 0043-automatic-Return-an-error-when-transaction-fails-RhB.patch
Patch0044: 0044-Document-symbols-in-dnf-history-list-output.patch
Patch0045: 0045-RHEL-1245-Remove-usrbin-from-syspath-noimpor-garbage.patch
Patch0046: 0046-RHEL-6393-Fix-japanese-translations.patch
Patch0047: 0047-RHEL-11786-Fix-substitution-in-kvp-in-add_new_repo.patch
#Almalinux patches
Patch10000: almalinux_bugtracker.patch
BuildArch: noarch
BuildRequires: cmake
@ -135,22 +134,13 @@ 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)
@ -176,9 +166,7 @@ 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
@ -208,55 +196,6 @@ Conflicts: yum < 3.4.3-505
%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}}
@ -267,7 +206,6 @@ 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}
@ -290,7 +228,6 @@ Recommends: rpm-plugin-systemd-inhibit
%description -n python3-%{name}
Python 3 interface to DNF.
%endif
%package automatic
Summary: %{pkg_summary} - automated upgrades
@ -304,40 +241,22 @@ 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
pushd build-py3
%cmake .. -DPYTHON_DESIRED:FILEPATH=%{__python3} -DDNF_VERSION=%{version}
%make_build
make doc-man
popd
%install
%if %{with python2}
pushd build-py2
%make_install
popd
%endif
%if %{with python3}
pushd build-py3
%make_install
popd
%endif
pushd build-py3
%make_install
popd
%find_lang %{name}
mkdir -p %{buildroot}%{confdir}/vars
@ -345,22 +264,12 @@ 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
@ -372,17 +281,7 @@ rm -vf %{buildroot}%{confdir}/%{name}-strict.conf
# 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
@ -392,17 +291,10 @@ ln -sr %{buildroot}%{confdir}/vars %{buildroot}%{_sysconfdir}/yum/vars
%check
%if %{with python2}
pushd build-py2
ctest -VV
popd
%endif
%if %{with python3}
pushd build-py3
ctest -VV
popd
%endif
pushd build-py3
ctest -VV
popd
%post
@ -512,22 +404,12 @@ ln -sr %{buildroot}%{confdir}/vars %{buildroot}%{_sysconfdir}/yum/vars
%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
@ -541,16 +423,117 @@ ln -sr %{buildroot}%{confdir}/vars %{buildroot}%{_sysconfdir}/yum/vars
%{_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
* Mon Mar 8 2021 Marek Blaha <mblaha@redhat.com> - 4.4.2-11
* Wed Mar 27 2024 Eduard Abdullin <eabdullin@almalinux.org> - 4.7.0-20.alma
- AlmaLinux changes
* Mon Oct 16 2023 Jaroslav Rohel <jrohel@redhat.com> - 4.7.0-20
- Remove /usr/bin from sys.path to avoid accidentally importing garbage (RHEL-1245)
- Fix japanese translations (RHEL-6393)
- Fix substitution in kay-value-pair list in add_new_repo (RHEL-11786)
* Wed Jun 28 2023 Jaroslav Rohel <jrohel@redhat.com> - 4.7.0-19
- Document symbols in `dnf history list` output (RhBug:2172067)
* Wed May 31 2023 Nicola Sella <nsella@redhat.com> - 4.7.0-18
- Return an error when transaction fails (RhBug:2170093)
* Wed May 17 2023 Jaroslav Rohel <jrohel@redhat.com> - 4.7.0-17
- Omit src RPMs from check-update (RhBug:2151910,2203069)
- automatic: Fix online detection with proxy (RhBug:2022440,2189851)
* Wed Mar 08 2023 Marek Blaha <mblaha@redhat.com> - 4.7.0-16
- Update translations
* Thu Jan 05 2023 Nicola Sella <nsella@redhat.com> - 4.7.0-15
- Ignore processing variable files with unsupported encoding (RhBug:2141215)
- Better explain traceback of rpm.error with dnf
* Wed Nov 30 2022 Nicola Sella <nsella@redhat.com> - 4.7.0-14
- Document changes to offline-upgrade command (RhBug:1939975,2139324)
* Wed Oct 26 2022 Nicola Sella <nsella@redhat.com> - 4.7.0-13
- Add support for rollback of group upgrade rollback (RhBug:2016070)
- Move system-upgrade plugin to core (RhBug:2054235)
- Fix plugins unit tests + unload plugins upon their deletion (RhBug:2134309)
* Tue Sep 13 2022 Lukas Hrazky <lhrazky@redhat.com> - 4.7.0-12
- Allow passing plugin parameters with dashes in names (RhBug:1980712)
- Fix upgrade from file to noarch pkg (RhBug:2006018)
- Add support for group upgrade rollback (RhBug:2016070)
- Expose plugin unload method to API (RhBug:2047251)
- Add doc related to --destdir and --downloadonly options (RhBug:2100811)
- Set default value for variable to prevent crash (RhBug:2091636)
- Don't include resolved advisories for obsoletes with sec. filters (RhBug:2101421)
* Tue Jul 19 2022 Lukas Hrazky <lhrazky@redhat.com> - 4.7.0-11
- [doc] Describe how gpg keys are stored for `repo_ggpcheck`
- Add only relevant pkgs to upgrade transaction (RhBug:2097757)
* Tue May 24 2022 Richard W.M. Jones <rjones@redhat.com> - 4.7.0-10
- Backport fix for leaks of libsolv's page file descriptors in Base object
resolves: rhbz#2087734
- Include instructions for regenerating patches.
* Wed May 04 2022 Lukas Hrazky <lhrazky@redhat.com> - 4.7.0-8
- Add documentation for query api flags
- Fix processing of download errors
- Fix history undo on a Reason Change
- Fix remove when no repos are enabled
- Improve "proxy" configuration option documentation
* Fri Jan 14 2022 Pavla Kratochvilova <pkratoch@redhat.com> - 4.7.0-7
- Rebuild with new release number
* Tue Jan 11 2022 Pavla Kratochvilova <pkratoch@redhat.com> - 4.7.0-6
- Allow destdir option with modulesync command
* Tue Nov 09 2021 Pavla Kratochvilova <pkratoch@redhat.com> - 4.7.0-5
- Bump release number because of conflicting version of 8.5 build
* Tue Nov 09 2021 Pavla Kratochvilova <pkratoch@redhat.com> - 4.7.0-4
- Add fail_fast parameter to _download_remote_payloads() method
- Throw CompsError when a group or environment is not found for the install methods
- Respect cacheonly in python dnf API (RhBug:1862970)
- [doc] Improve description of multilib_policy=all (RhBug:1996681,1995630)
- [doc] Document default colors
* Mon Aug 16 2021 Pavla Kratochvilova <pkratoch@redhat.com> - 4.7.0-3
- Improve signature checking using rpmkeys (RhBug:1967454)
* Tue Jul 27 2021 Pavla Kratochvilova <pkratoch@redhat.com> - 4.7.0-2
- Fix covscan issue: dnf/rpm/miscutils.py: fix usage of _()
* Wed May 19 2021 Pavla Kratochvilova <pkratoch@redhat.com> - 4.7.0-1
- Update to 4.7.0
- New optional parameter for filter_modules enables following modular obsoletes based on a config option module_obsoletes
- Fix module remove --all when no match spec (RhBug:1904490)
- Make an error message more informative (RhBug:1814831)
- Expand history to full term size when output is redirected (RhBug:1852577) (RhBug:1852577,1906970)
- Print additional information when verifying GPG key using DNS
- Enhanced detection of plugins removed in transaction (RhBug:1929163)
- Improve repo config path ordering to fix a comps merging issue (RhBug:1928181)
- Keep reason when package is removed (RhBug:1921063)
- Improve mechanism for application of security filters (RhBug:1918475)
- [API] Add new method for reset of security filters
- Remove hardcoded logfile permissions (RhBug:1910084)
- Preserve file mode during log rotation (RhBug:1910084)
- Increase loglevel in case of invalid config options
- Prevent traceback (catch ValueError) if pkg is from cmdline
- Check for specific key string when verifing signatures (RhBug:1915990)
- Use rpmkeys binary to verify package signature (RhBug:1915990)
- [doc] Improve description of modular filtering
- [doc] deprecated alias for dnf repoquery --deplist <deplist_option-label>
- [doc] Describe install with just a name and obsoletes (RhBug:1902279)
- [doc] Fix: "sslcacert" contains path to the file
- [doc] Added proxy ssl configuration options, increase libdnf require
- [doc] Update documentation for module_obsoletes and module_stream_switch
- [doc] Improve documentation for Hotfix repositories
- [doc] fix: "makecache" command downloads only enabled repositories
- [doc] Add info that maximum parallel downloads is 20
- [doc] installonly_limit documentation follows behavior
- [doc] Add documentation for config option sslverifystatus (RhBug:1814383)
- The noroot plugin no longer exists, remove mention
* Thu Feb 11 2021 Nicola Sella <nsella@redhat.com> - 4.4.2-10
- Allow stream switching if option enabled