diff --git a/0019-fix-compare-RPMItem-in-transaction-with-rpmvercmp.patch b/0019-fix-compare-RPMItem-in-transaction-with-rpmvercmp.patch new file mode 100644 index 0000000..8f1bce8 --- /dev/null +++ b/0019-fix-compare-RPMItem-in-transaction-with-rpmvercmp.patch @@ -0,0 +1,206 @@ +From 433af8870a741f04d2e55616e83d251a972eac32 Mon Sep 17 00:00:00 2001 +From: Matej Focko +Date: Fri, 30 Jan 2026 12:28:09 +0100 +Subject: [PATCH] fix: compare RPMItem in transaction with rpmvercmp +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Previous implementation resorts to manual comparison of the EVR fields +including the following issues: + +• There appears to be a typo when comparing epochs: + + } else if (epoch < 0) { + + instead of + + } else if (epochDif < 0) { + + N.B. ‹epoch < 0› is unreachable + • RPM packaging guidelines **do not** define any requirements on the + value (and is internally represented as ‹int32_t›) + • On the contrary Fedora packaging guidelines denote: + + If present, it MUST consist of a positive integer. + +• There is a manual parsing of the version fields by “chopping off” the + parts ofversion and parsing them via ‹std::stoi›. + +• More importantly, reported by bugs below, the release fields are + ignored altogether. + +Instead of manually processing the EVR, with a potential room to error, +switch to using the ‹::rpmvercmp› from ‹rpm/rpmver.h› that should be the +“source of truth” in this case. + +Also cover the discovered problems with regression tests in unit tests. + +Fixes RHEL-81778 +Fixes RHEL-81779 +Fixes RHEL-128443 (RHEL10 clone of RHEL-81779) + +Signed-off-by: Matej Focko +(cherry picked from commit 1bcd660168302c120b73b99238babf39f3131faa) +--- + libdnf/transaction/RPMItem.cpp | 39 +++++++-------- + tests/libdnf/transaction/RpmItemTest.cpp | 61 ++++++++++++++++++++++++ + tests/libdnf/transaction/RpmItemTest.hpp | 2 + + 3 files changed, 80 insertions(+), 22 deletions(-) + +diff --git a/libdnf/transaction/RPMItem.cpp b/libdnf/transaction/RPMItem.cpp +index ecce789d..92c26d09 100644 +--- a/libdnf/transaction/RPMItem.cpp ++++ b/libdnf/transaction/RPMItem.cpp +@@ -20,6 +20,7 @@ + + #include + #include ++#include + #include + + #include "../hy-subject.h" +@@ -343,35 +344,29 @@ RPMItem::resolveTransactionItemReason(SQLite3Ptr conn, + * Compare RPM packages + * This method doesn't care about compare package names + * \param other RPMItem to compare with +- * \return true if other package is newer (has higher version and/or epoch) ++ * \return true if other package is newer (has higher epoch, version, or release) + */ + bool + RPMItem::operator<(const RPMItem &other) const + { +- // compare epochs +- int32_t epochDif = other.getEpoch() - getEpoch(); +- if (epochDif > 0) { +- return true; +- } else if (epoch < 0) { +- return false; ++ // Compare epochs ++ if (getEpoch() != other.getEpoch()) { ++ return getEpoch() < other.getEpoch(); + } + +- // compare versions +- std::stringstream versionThis(getVersion()); +- std::stringstream versionOther(other.getVersion()); +- +- std::string bufferThis; +- std::string bufferOther; +- while (std::getline(versionThis, bufferThis, '.') && +- std::getline(versionOther, bufferOther, '.')) { +- int subVersionThis = std::stoi(bufferThis); +- int subVersionOther = std::stoi(bufferOther); +- if (subVersionThis == subVersionOther) { +- continue; +- } +- return subVersionOther > subVersionThis; ++ // Compare versions ++ auto version = getVersion(); ++ auto otherVersion = other.getVersion(); ++ auto cmpResult = ::rpmvercmp(version.c_str(), otherVersion.c_str()); ++ if (cmpResult != 0) { ++ return cmpResult < 0; + } +- return false; ++ ++ // Compare releases ++ auto release = getRelease(); ++ auto otherRelease = other.getRelease(); ++ cmpResult = ::rpmvercmp(release.c_str(), otherRelease.c_str()); ++ return cmpResult < 0; + } + + std::vector< int64_t > +diff --git a/tests/libdnf/transaction/RpmItemTest.cpp b/tests/libdnf/transaction/RpmItemTest.cpp +index 774c716a..503e4ec3 100644 +--- a/tests/libdnf/transaction/RpmItemTest.cpp ++++ b/tests/libdnf/transaction/RpmItemTest.cpp +@@ -119,3 +119,64 @@ RpmItemTest::testGetTransactionItems() + //CPPUNIT_ASSERT(createMs.count() == 0); + //CPPUNIT_ASSERT(readMs.count() == 0); + } ++ ++/** ++ * Regression test for RHEL-81778 ++ * Regression test for RHEL-81779 ++ */ ++void ++RpmItemTest::testComparison() ++{ ++ auto a = std::make_shared< RPMItem >(conn); ++ a->setName("rsyslog"); ++ a->setEpoch(0); ++ a->setVersion("8.2102.0"); ++ a->setRelease("1.el9"); ++ a->setArch("aarch64"); ++ ++ auto b = std::make_shared< RPMItem >(conn); ++ b->setName("rsyslog"); ++ b->setEpoch(0); ++ b->setVersion("8.2102.0"); ++ b->setRelease("5.el9"); ++ b->setArch("aarch64"); ++ ++ // rsyslog-8.2102.0-1.el9.aarch64 < rsyslog-8.2102.0-5.el9.aarch64 ++ CPPUNIT_ASSERT(*a < *b); ++ CPPUNIT_ASSERT(!(*b < *a)); ++ ++ a->setRelease("2.el9"); ++ b->setRelease("1.el9"); ++ ++ // rsyslog-8.2102.0-1.el9.aarch64 < rsyslog-8.2102.0-2.el9.aarch64 ++ CPPUNIT_ASSERT(*b < *a); ++ CPPUNIT_ASSERT(!(*a < *b)); ++ ++ b->setRelease("10.el9"); ++ ++ // rsyslog-8.2102.0-2.el9.aarch64 < rsyslog-8.2102.0-10.el9.aarch64 ++ CPPUNIT_ASSERT(*a < *b); ++ CPPUNIT_ASSERT(!(*b < *a)); ++ ++ // Cover fixed comparison of epochs (previously falling through to version ++ // comparison when left side being newer) ++ { ++ auto a = std::make_shared< RPMItem >(conn); ++ a->setName("rsyslog"); ++ a->setEpoch(0); ++ a->setVersion("8.2102.0"); ++ a->setRelease("1.el9"); ++ a->setArch("aarch64"); ++ ++ auto b = std::make_shared< RPMItem >(conn); ++ b->setName("rsyslog"); ++ b->setEpoch(3); ++ b->setVersion("8.2101.0"); ++ b->setRelease("1.el9"); ++ b->setArch("aarch64"); ++ ++ // rsyslog-0:8.2102.0-1.el9.aarch64 < rsyslog-3:8.2101.0-1.el9.aarch64 ++ CPPUNIT_ASSERT(*a < *b); ++ CPPUNIT_ASSERT(!(*b < *a)); ++ } ++} +diff --git a/tests/libdnf/transaction/RpmItemTest.hpp b/tests/libdnf/transaction/RpmItemTest.hpp +index 59cc6c59..bb4cd930 100644 +--- a/tests/libdnf/transaction/RpmItemTest.hpp ++++ b/tests/libdnf/transaction/RpmItemTest.hpp +@@ -10,6 +10,7 @@ class RpmItemTest : public CppUnit::TestCase { + CPPUNIT_TEST(testCreate); + CPPUNIT_TEST(testCreateDuplicates); + CPPUNIT_TEST(testGetTransactionItems); ++ CPPUNIT_TEST(testComparison); + CPPUNIT_TEST_SUITE_END(); + + public: +@@ -19,6 +20,7 @@ public: + void testCreate(); + void testCreateDuplicates(); + void testGetTransactionItems(); ++ void testComparison(); + + private: + std::shared_ptr< SQLite3 > conn; +-- +2.52.0 + diff --git a/0020-Mark-all-protected-packages-as-user-installed-for-al.patch b/0020-Mark-all-protected-packages-as-user-installed-for-al.patch new file mode 100644 index 0000000..ea7c2e7 --- /dev/null +++ b/0020-Mark-all-protected-packages-as-user-installed-for-al.patch @@ -0,0 +1,34 @@ +From 9d5c8b965294d52a0feda55f274d033a279fe090 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= +Date: Fri, 9 Jan 2026 07:26:34 +0100 +Subject: [PATCH 1/5] Mark all protected packages as user installed for all + transactions + +Closes: https://github.com/rpm-software-management/dnf/issues/2192 +--- + libdnf/goal/Goal.cpp | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/libdnf/goal/Goal.cpp b/libdnf/goal/Goal.cpp +index 9882273c..f2b20ced 100644 +--- a/libdnf/goal/Goal.cpp ++++ b/libdnf/goal/Goal.cpp +@@ -987,6 +987,15 @@ Goal::jobLength() + bool + Goal::run(DnfGoalActions flags) + { ++ // Automatically mark all protected packages as user installed. ++ // When a protected package is installed as a dependency it can block ++ // removal of the last package that depends on it (because the protected ++ // package cannot be removed, not even as an unused dependency). ++ // To prevent this and still correctly resolve dependencies of the protected ++ // packages mark them all as user installed. ++ if (pImpl->protectedPkgs) { ++ userInstalled(*pImpl->protectedPkgs); ++ } + auto job = pImpl->constructJob(flags); + pImpl->actions = static_cast(pImpl->actions | flags); + int ret = pImpl->solve(job->getQueue(), flags); +-- +2.53.0 + diff --git a/0021-Add-filterUnneededExtraUserinstalled-and-Python-vers.patch b/0021-Add-filterUnneededExtraUserinstalled-and-Python-vers.patch new file mode 100644 index 0000000..837b5b1 --- /dev/null +++ b/0021-Add-filterUnneededExtraUserinstalled-and-Python-vers.patch @@ -0,0 +1,149 @@ +From 93d8bfbe36913804d8963f04e4d9491d6bfa25a1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= +Date: Mon, 26 Jan 2026 14:07:37 +0100 +Subject: [PATCH 2/5] Add `filterUnneededExtraUserinstalled` and Python version + to the API + +This can be used to get unneeded packages with for example protected +packages extra marked as userinstalled. +--- + libdnf/sack/query.cpp | 18 +++++++++++---- + libdnf/sack/query.hpp | 1 + + python/hawkey/query-py.cpp | 47 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 62 insertions(+), 4 deletions(-) + +diff --git a/libdnf/sack/query.cpp b/libdnf/sack/query.cpp +index e4682927..c1c50ec1 100644 +--- a/libdnf/sack/query.cpp ++++ b/libdnf/sack/query.cpp +@@ -830,7 +830,7 @@ private: + void filterUpdownByPriority(const Filter & f, Map *m); + void filterUpdownAble(const Filter &f, Map *m); + void filterDataiterator(const Filter & f, Map *m); +- int filterUnneededOrSafeToRemove(const Swdb &swdb, bool debug_solver, bool safeToRemove); ++ int filterUnneededOrSafeToRemove(const Swdb &swdb, bool debug_solver, bool safeToRemove, PackageSet *extra_userinstalled); + void obsoletesByPriority(Pool * pool, Solvable * candidate, Map * m, const Map * target, int obsprovides); + + bool isGlob(const std::vector &matches) const; +@@ -2245,7 +2245,7 @@ Query::Impl::filterDataiterator(const Filter & f, Map *m) + } + + int +-Query::Impl::filterUnneededOrSafeToRemove(const Swdb &swdb, bool debug_solver, bool safeToRemove) ++Query::Impl::filterUnneededOrSafeToRemove(const Swdb &swdb, bool debug_solver, bool safeToRemove, PackageSet *extra_userinstalled) + { + apply(); + Goal goal(sack); +@@ -2260,6 +2260,10 @@ Query::Impl::filterUnneededOrSafeToRemove(const Swdb &swdb, bool debug_solver, b + } + goal.userInstalled(*userInstalled); + ++ if (extra_userinstalled != NULL) { ++ goal.userInstalled(*extra_userinstalled); ++ } ++ + int ret1 = goal.run(DNF_NONE); + if (ret1) + return -1; +@@ -2575,13 +2579,19 @@ Query::filterDuplicated() + int + Query::filterUnneeded(const Swdb &swdb, bool debug_solver) + { +- return pImpl->filterUnneededOrSafeToRemove(swdb, debug_solver, false); ++ return pImpl->filterUnneededOrSafeToRemove(swdb, debug_solver, false, NULL); + } + + int + Query::filterSafeToRemove(const Swdb &swdb, bool debug_solver) + { +- return pImpl->filterUnneededOrSafeToRemove(swdb, debug_solver, true); ++ return pImpl->filterUnneededOrSafeToRemove(swdb, debug_solver, true, NULL); ++} ++ ++int ++Query::filterUnneededExtraUserinstalled(const Swdb &swdb, PackageSet &extra_userinstalled, bool debug_solver) ++{ ++ return pImpl->filterUnneededOrSafeToRemove(swdb, debug_solver, false, &extra_userinstalled); + } + + void +diff --git a/libdnf/sack/query.hpp b/libdnf/sack/query.hpp +index 306b24e3..a5ed7745 100644 +--- a/libdnf/sack/query.hpp ++++ b/libdnf/sack/query.hpp +@@ -177,6 +177,7 @@ public: + void filterRecent(const long unsigned int recent_limit); + void filterDuplicated(); + int filterUnneeded(const Swdb &swdb, bool debug_solver); ++ int filterUnneededExtraUserinstalled(const Swdb &swdb, PackageSet &extra_userinstalled, bool debug_solver); + int filterSafeToRemove(const Swdb &swdb, bool debug_solver); + void getAdvisoryPkgs(int cmpType, std::vector & advisoryPkgs); + void filterUserInstalled(const Swdb &swdb); +diff --git a/python/hawkey/query-py.cpp b/python/hawkey/query-py.cpp +index a25a0d6b..b30cc22f 100644 +--- a/python/hawkey/query-py.cpp ++++ b/python/hawkey/query-py.cpp +@@ -819,6 +819,52 @@ filter_unneeded(PyObject *self, PyObject *args, PyObject *kwds) try + return filter_unneeded_or_safe_to_remove(self, args, kwds, false); + } CATCH_TO_PYTHON + ++static PyObject * ++filter_unneeded_extra_userinstalled(PyObject *self, PyObject *args, PyObject *kwds) try ++{ ++ const char *kwlist[] = {"swdb", "extra_userinstalled", "debug_solver", NULL}; ++ PyObject *pySwdb; ++ PyObject *extra_userinstalled; ++ PyObject *debug_solver = NULL; ++ ++ if (!PyArg_ParseTupleAndKeywords( ++ args, kwds, "OO|O!", (char **)kwlist, &pySwdb, &extra_userinstalled, &PyBool_Type, &debug_solver)) { ++ return NULL; ++ } ++ ++ UniquePtrPyObject thisPySwdb(PyObject_GetAttrString(pySwdb, "this")); ++ auto swigSwdb = reinterpret_cast< SwdbSwigPyObject * >(thisPySwdb.get()); ++ if (swigSwdb == nullptr) { ++ PyErr_SetString(PyExc_SystemError, "Unable to parse SwigPyObject"); ++ return NULL; ++ } ++ libdnf::Swdb *swdb = swigSwdb->ptr; ++ if (swdb == NULL) { ++ PyErr_SetString(PyExc_SystemError, "Unable to parse swig object"); ++ return NULL; ++ } ++ ++ HyQuery query = ((_QueryObject *) self)->query; ++ auto extra_userinstalled_pset = pyseq_to_packageset(extra_userinstalled, query->getSack()); ++ if (!extra_userinstalled_pset) { ++ PyErr_SetString(PyExc_SystemError, "Unable to parse SwigPyObject: extra_userinstalled PackageSet"); ++ return NULL; ++ } ++ ++ std::unique_ptr self_query_copy(new libdnf::Query(*query)); ++ gboolean c_debug_solver = debug_solver != NULL && PyObject_IsTrue(debug_solver); ++ ++ int ret = self_query_copy->filterUnneededExtraUserinstalled(*swdb, *extra_userinstalled_pset, c_debug_solver); ++ if (ret == -1) { ++ PyErr_SetString(PyExc_SystemError, "Unable to provide query with unneded filter"); ++ return NULL; ++ } ++ ++ PyObject *final_query = queryToPyObject(self_query_copy.release(), ((_QueryObject *) self)->sack, ++ Py_TYPE(self)); ++ return final_query; ++} CATCH_TO_PYTHON ++ + static PyObject * + q_add(_QueryObject *self, PyObject *list) try + { +@@ -1081,6 +1127,7 @@ static struct PyMethodDef query_methods[] = { + {"_nevra", (PyCFunction)add_nevra_or_other_filter, METH_VARARGS, NULL}, + {"_recent", (PyCFunction)add_filter_recent, METH_VARARGS, NULL}, + {"_unneeded", (PyCFunction)filter_unneeded, METH_KEYWORDS|METH_VARARGS, NULL}, ++ {"_unneeded_extra_userinstalled", (PyCFunction)filter_unneeded_extra_userinstalled, METH_KEYWORDS|METH_VARARGS, NULL}, + {"_safe_to_remove", (PyCFunction)filter_safe_to_remove, METH_KEYWORDS|METH_VARARGS, NULL}, + {NULL} /* sentinel */ + }; +-- +2.53.0 + diff --git a/0022-Describe-all-problems-even-when-there-are-protected-.patch b/0022-Describe-all-problems-even-when-there-are-protected-.patch new file mode 100644 index 0000000..89f3789 --- /dev/null +++ b/0022-Describe-all-problems-even-when-there-are-protected-.patch @@ -0,0 +1,31 @@ +From 3da41b9d881fc5f0bb8c09b4d1ae4dde9626dd1e Mon Sep 17 00:00:00 2001 +From: Evan Goode +Date: Tue, 27 Jan 2026 13:00:50 -0500 +Subject: [PATCH 4/5] Describe all problems even when there are protected + removals + +Previously, if a transaction involved removal (or dependency break) of a +protected package, only the protected package problem would be described +by `describeProblemRules`. Information about excluded or versionlocked +packages would be missing. + +For: RHEL-146507 +--- + libdnf/goal/Goal.cpp | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/libdnf/goal/Goal.cpp b/libdnf/goal/Goal.cpp +index f2b20ced..47d9888e 100644 +--- a/libdnf/goal/Goal.cpp ++++ b/libdnf/goal/Goal.cpp +@@ -1108,7 +1108,6 @@ Goal::describeProblemRules(unsigned i, bool pkgs) + auto problem = pImpl->describeProtectedRemoval(); + if (!problem.empty()) { + output.push_back(std::move(problem)); +- return output; + } + auto solv = pImpl->solv; + +-- +2.53.0 + diff --git a/0023-Clearer-error-for-protected-package-broken-dependenc.patch b/0023-Clearer-error-for-protected-package-broken-dependenc.patch new file mode 100644 index 0000000..f4ab826 --- /dev/null +++ b/0023-Clearer-error-for-protected-package-broken-dependenc.patch @@ -0,0 +1,60 @@ +From 35f16bc89b567460dd7857f632a38790839baca9 Mon Sep 17 00:00:00 2001 +From: Evan Goode +Date: Tue, 27 Jan 2026 13:19:14 -0500 +Subject: [PATCH 5/5] Clearer error for protected package broken dependencies + +The error message now distinguishes removal of a protected package from +breaking dependencies of protected packages. +--- + libdnf/goal/Goal.cpp | 17 +++++++++++------ + 1 file changed, 11 insertions(+), 6 deletions(-) + +diff --git a/libdnf/goal/Goal.cpp b/libdnf/goal/Goal.cpp +index 47d9888e..4544cd9e 100644 +--- a/libdnf/goal/Goal.cpp ++++ b/libdnf/goal/Goal.cpp +@@ -1752,11 +1752,11 @@ Goal::Impl::protectedInRemovals() + std::string + Goal::Impl::describeProtectedRemoval() + { +- std::string message(_("The operation would result in removing" +- " the following protected packages: ")); + Pool * pool = solv->pool; + + if (removalOfProtected && removalOfProtected->size()) { ++ const std::string removal_message(_("The operation would result in removing " ++ "the following protected packages: ")); + Id id = -1; + std::vector names; + while((id = removalOfProtected->next(id)) != -1) { +@@ -1766,9 +1766,13 @@ Goal::Impl::describeProtectedRemoval() + if (names.empty()) { + return {}; + } +- return message + std::accumulate(std::next(names.begin()), names.end(), +- std::string(names[0]), [](std::string a, std::string b) { return a + ", " + b; }); ++ return removal_message + ++ std::accumulate(std::next(names.begin()), names.end(), std::string(names[0]), ++ [](std::string a, std::string b) { return a + ", " + b; }); + } ++ ++ const std::string broken_dependency_message(_("The operation would result in broken " ++ "dependencies for the following protected packages: ")); + auto pset = brokenDependencyAllPkgs(DNF_PACKAGE_STATE_INSTALLED); + Id id = -1; + Id protected_kernel = protectedRunningKernel(); +@@ -1781,8 +1785,9 @@ Goal::Impl::describeProtectedRemoval() + } + if (names.empty()) + return {}; +- return message + std::accumulate(std::next(names.begin()), names.end(), std::string(names[0]), +- [](std::string a, std::string b) { return a + ", " + b; }); ++ return broken_dependency_message + ++ std::accumulate(std::next(names.begin()), names.end(), std::string(names[0]), ++ [](std::string a, std::string b) { return a + ", " + b; }); + } + + } +-- +2.53.0 + diff --git a/0024-Goal-set-protected-as-userinstalled-only-for-the-tem.patch b/0024-Goal-set-protected-as-userinstalled-only-for-the-tem.patch new file mode 100644 index 0000000..c57b6a0 --- /dev/null +++ b/0024-Goal-set-protected-as-userinstalled-only-for-the-tem.patch @@ -0,0 +1,57 @@ +From 83d3c39df84e96860efd35c2e18903dd16d050c7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= +Date: Tue, 10 Feb 2026 13:04:42 +0100 +Subject: [PATCH] Goal: set protected as userinstalled only for the temporary + job + +This way the goal's `staging` is not affected. +--- + libdnf/goal/Goal.cpp | 25 ++++++++++++++++--------- + 1 file changed, 16 insertions(+), 9 deletions(-) + +diff --git a/libdnf/goal/Goal.cpp b/libdnf/goal/Goal.cpp +index 4544cd9e..c624fb41 100644 +--- a/libdnf/goal/Goal.cpp ++++ b/libdnf/goal/Goal.cpp +@@ -987,15 +987,6 @@ Goal::jobLength() + bool + Goal::run(DnfGoalActions flags) + { +- // Automatically mark all protected packages as user installed. +- // When a protected package is installed as a dependency it can block +- // removal of the last package that depends on it (because the protected +- // package cannot be removed, not even as an unused dependency). +- // To prevent this and still correctly resolve dependencies of the protected +- // packages mark them all as user installed. +- if (pImpl->protectedPkgs) { +- userInstalled(*pImpl->protectedPkgs); +- } + auto job = pImpl->constructJob(flags); + pImpl->actions = static_cast(pImpl->actions | flags); + int ret = pImpl->solve(job->getQueue(), flags); +@@ -1444,6 +1435,22 @@ Goal::Impl::constructJob(DnfGoalActions flags) + + allowUninstallAllButProtected(job->getQueue(), flags); + ++ // Automatically mark all protected packages as user installed. ++ // When a protected package is installed as a dependency it can block ++ // removal of the last package that depends on it (because the protected ++ // package cannot be removed, not even as an unused dependency). ++ // To prevent this and still correctly resolve dependencies of the protected ++ // packages mark them all as user installed. ++ if (protectedPkgs) { ++ Id id = -1; ++ while (true) { ++ id = protectedPkgs->next(id); ++ if (id == -1) ++ break; ++ queue_push2(job->getQueue(), SOLVER_SOLVABLE|SOLVER_USERINSTALLED, id); ++ } ++ } ++ + if (flags & DNF_VERIFY) + job->pushBack(SOLVER_VERIFY|SOLVER_SOLVABLE_ALL, 0); + +-- +2.53.0 + diff --git a/0019-tests-Add-tests-for-dnf_keyring_add_public_key.patch b/0025-tests-Add-tests-for-dnf_keyring_add_public_key.patch similarity index 98% rename from 0019-tests-Add-tests-for-dnf_keyring_add_public_key.patch rename to 0025-tests-Add-tests-for-dnf_keyring_add_public_key.patch index 66e1535..14f5f97 100644 --- a/0019-tests-Add-tests-for-dnf_keyring_add_public_key.patch +++ b/0025-tests-Add-tests-for-dnf_keyring_add_public_key.patch @@ -1,7 +1,7 @@ -From d7ce52b5793e8c0be03bbdfac416e7c76bfbb03a Mon Sep 17 00:00:00 2001 +From 72252383942769f8ad7858e3d5abe2cb64488371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= Date: Fri, 13 Mar 2026 17:40:15 +0100 -Subject: [PATCH 19/24] tests: Add tests for dnf_keyring_add_public_key() +Subject: [PATCH 25/30] tests: Add tests for dnf_keyring_add_public_key() Upstream commit: dc3f0098e30a9154f530ea4fc31c500927a7ad01 diff --git a/0020-Move-importing-a-key-from-a-memory-block-into-a-sepa.patch b/0026-Move-importing-a-key-from-a-memory-block-into-a-sepa.patch similarity index 97% rename from 0020-Move-importing-a-key-from-a-memory-block-into-a-sepa.patch rename to 0026-Move-importing-a-key-from-a-memory-block-into-a-sepa.patch index f3fe358..f894cc2 100644 --- a/0020-Move-importing-a-key-from-a-memory-block-into-a-sepa.patch +++ b/0026-Move-importing-a-key-from-a-memory-block-into-a-sepa.patch @@ -1,7 +1,7 @@ -From 7e50092e3000e428efc410fe19c926a482fa2156 Mon Sep 17 00:00:00 2001 +From 49df864cde21f9d160bfd422058a3e33970222cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= Date: Fri, 13 Mar 2026 17:40:15 +0100 -Subject: [PATCH 20/24] Move importing a key from a memory block into a +Subject: [PATCH 26/30] Move importing a key from a memory block into a separate function Upstream commit: 89b6944551c08c17c5be12db17211201264350a1 diff --git a/0021-Log-identifiers-of-keys-imported-by-dnf_keyring_add_.patch b/0027-Log-identifiers-of-keys-imported-by-dnf_keyring_add_.patch similarity index 98% rename from 0021-Log-identifiers-of-keys-imported-by-dnf_keyring_add_.patch rename to 0027-Log-identifiers-of-keys-imported-by-dnf_keyring_add_.patch index 7f9a124..1817076 100644 --- a/0021-Log-identifiers-of-keys-imported-by-dnf_keyring_add_.patch +++ b/0027-Log-identifiers-of-keys-imported-by-dnf_keyring_add_.patch @@ -1,7 +1,7 @@ -From b078d8bb7d8c76f839a3be1d5fdbcb7ab76de8ea Mon Sep 17 00:00:00 2001 +From 435b2a678af4f428d66044f7c5035c640abbc780 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= Date: Wed, 18 Mar 2026 13:36:18 +0100 -Subject: [PATCH 21/24] Log identifiers of keys imported by +Subject: [PATCH 27/30] Log identifiers of keys imported by dnf_keyring_add_public_key() Upstream commit: e0c56202ebde444bb73065843ac3dc2f7e4a8f3a diff --git a/0022-Fix-dnf_keyring_add_public_key-to-add-all-keys-from-.patch b/0028-Fix-dnf_keyring_add_public_key-to-add-all-keys-from-.patch similarity index 98% rename from 0022-Fix-dnf_keyring_add_public_key-to-add-all-keys-from-.patch rename to 0028-Fix-dnf_keyring_add_public_key-to-add-all-keys-from-.patch index 93072db..bee817d 100644 --- a/0022-Fix-dnf_keyring_add_public_key-to-add-all-keys-from-.patch +++ b/0028-Fix-dnf_keyring_add_public_key-to-add-all-keys-from-.patch @@ -1,7 +1,7 @@ -From 3892eefb77e95a4dd9113a727503bb4ce29eac2c Mon Sep 17 00:00:00 2001 +From 569f317bbf22e4ccc3a473cd3ee895a2891852f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= Date: Wed, 18 Mar 2026 13:36:18 +0100 -Subject: [PATCH 22/24] Fix dnf_keyring_add_public_key() to add all keys from +Subject: [PATCH 28/30] Fix dnf_keyring_add_public_key() to add all keys from an ASCII-armored block Upstream commit: 21e58e48d73a0edba62510bac8d49392c5166ebb diff --git a/0023-Fix-dnf_keyring_add_public_key-to-add-keys-from-all-.patch b/0029-Fix-dnf_keyring_add_public_key-to-add-keys-from-all-.patch similarity index 99% rename from 0023-Fix-dnf_keyring_add_public_key-to-add-keys-from-all-.patch rename to 0029-Fix-dnf_keyring_add_public_key-to-add-keys-from-all-.patch index 00ede31..e5ac98a 100644 --- a/0023-Fix-dnf_keyring_add_public_key-to-add-keys-from-all-.patch +++ b/0029-Fix-dnf_keyring_add_public_key-to-add-keys-from-all-.patch @@ -1,7 +1,7 @@ -From 6e64245b6092d412b59a5571ce6fda588b132ebb Mon Sep 17 00:00:00 2001 +From 3b3c8910837d51431e9cc79e131671f48ea5f04b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= Date: Wed, 18 Mar 2026 15:46:30 +0100 -Subject: [PATCH 23/24] Fix dnf_keyring_add_public_key() to add keys from all +Subject: [PATCH 29/30] Fix dnf_keyring_add_public_key() to add keys from all ASCII-armored blocks Upstream commit: af94b4cc5b7f96829ab8cb9d7dc3fbc92fa56d92 diff --git a/0024-Fix-formatting-error-messages-when-importing-subkeys.patch b/0030-Fix-formatting-error-messages-when-importing-subkeys.patch similarity index 95% rename from 0024-Fix-formatting-error-messages-when-importing-subkeys.patch rename to 0030-Fix-formatting-error-messages-when-importing-subkeys.patch index 6e1b90a..8148ad1 100644 --- a/0024-Fix-formatting-error-messages-when-importing-subkeys.patch +++ b/0030-Fix-formatting-error-messages-when-importing-subkeys.patch @@ -1,7 +1,7 @@ -From aa80ec474cd76f9a895c84d6d2c0429b89b65a8f Mon Sep 17 00:00:00 2001 +From 60ab51a163451fe6da0173ae7a3a05d8689ff7ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= Date: Thu, 9 Apr 2026 16:26:20 +0200 -Subject: [PATCH 24/24] Fix formatting error messages when importing subkeys +Subject: [PATCH 30/30] Fix formatting error messages when importing subkeys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/libdnf.spec b/libdnf.spec index 1e8a16e..1332bf1 100644 --- a/libdnf.spec +++ b/libdnf.spec @@ -56,7 +56,7 @@ Name: libdnf Version: %{libdnf_major_version}.%{libdnf_minor_version}.%{libdnf_micro_version} -Release: 12%{?dist}.1.alma.1 +Release: 15%{?dist}.alma.1 Summary: Library providing simplified C and Python API to libsolv License: LGPL-2.1-or-later URL: https://github.com/rpm-software-management/libdnf @@ -79,12 +79,18 @@ Patch15: 0015-module-Warn-if-module-config-file-is-inaccessible.patch Patch16: 0016-history-DB-Add-persistence-column.patch Patch17: 0017-MergedTransaction-listPersistences.patch Patch18: 0018-conf-Add-usr_drift_protected_paths.patch -Patch19: 0019-tests-Add-tests-for-dnf_keyring_add_public_key.patch -Patch20: 0020-Move-importing-a-key-from-a-memory-block-into-a-sepa.patch -Patch21: 0021-Log-identifiers-of-keys-imported-by-dnf_keyring_add_.patch -Patch22: 0022-Fix-dnf_keyring_add_public_key-to-add-all-keys-from-.patch -Patch23: 0023-Fix-dnf_keyring_add_public_key-to-add-keys-from-all-.patch -Patch24: 0024-Fix-formatting-error-messages-when-importing-subkeys.patch +Patch19: 0019-fix-compare-RPMItem-in-transaction-with-rpmvercmp.patch +Patch20: 0020-Mark-all-protected-packages-as-user-installed-for-al.patch +Patch21: 0021-Add-filterUnneededExtraUserinstalled-and-Python-vers.patch +Patch22: 0022-Describe-all-problems-even-when-there-are-protected-.patch +Patch23: 0023-Clearer-error-for-protected-package-broken-dependenc.patch +Patch24: 0024-Goal-set-protected-as-userinstalled-only-for-the-tem.patch +Patch25: 0025-tests-Add-tests-for-dnf_keyring_add_public_key.patch +Patch26: 0026-Move-importing-a-key-from-a-memory-block-into-a-sepa.patch +Patch27: 0027-Log-identifiers-of-keys-imported-by-dnf_keyring_add_.patch +Patch28: 0028-Fix-dnf_keyring_add_public_key-to-add-all-keys-from-.patch +Patch29: 0029-Fix-dnf_keyring_add_public_key-to-add-keys-from-all-.patch +Patch30: 0030-Fix-formatting-error-messages-when-importing-subkeys.patch # AlmaLinux Patch Patch1001: 0001-Add-link-to-AlmaLinux-bugtracker.patch @@ -333,14 +339,23 @@ popd %endif %changelog -* Tue May 05 2026 Eduard Abdullin - 0.73.1-12.1.alma.1 +* Tue May 19 2026 Eduard Abdullin - 0.73.1-15.alma.1 - Add x86_64_v2 to arch_map - Add link to AlmaLinux bugtracker - Fix tests on x86_64_v2 -* Mon Apr 13 2026 Petr Pisar - 0.73.1-12.1 +* Mon Apr 13 2026 Petr Pisar - 0.73.1-15 - Fix dnf_keyring_add_public_key() to add multiple keys from a single file - (RHEL-156041) + (RHEL-156063) + +* Mon Feb 09 2026 Ales Matej - 0.73.1-14 +- Mark all protected packages as user installed for all transactions (RHEL-128445) +- Add `filterUnneededExtraUserinstalled` and Python version to the API +- Describe all problems even when there are protected removals (RHEL-146507) +- Clearer error for protected package broken dependencies + +* Tue Feb 03 2026 Matej Focko - 0.73.1-13 +- Fix comparison of RPM items in the transaction (RHEL-128443) * Thu Jun 26 2025 Evan Goode - 0.73.1-12 - Bump version due to failed build