Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package libdnf for openSUSE:Factory checked 
in at 2026-06-03 20:21:37
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libdnf (Old)
 and      /work/SRC/openSUSE:Factory/.libdnf.new.1937 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libdnf"

Wed Jun  3 20:21:37 2026 rev:43 rq:1356713 version:0.75.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/libdnf/libdnf.changes    2025-07-27 
16:26:17.913825448 +0200
+++ /work/SRC/openSUSE:Factory/.libdnf.new.1937/libdnf.changes  2026-06-03 
20:22:39.417772083 +0200
@@ -1,0 +2,47 @@
+Tue Jun  2 11:22:13 UTC 2026 - Petr Gajdos <[email protected]>
+
+- version update to 0.75.0
+  * context: Support libdnf5 drop-in directories and repository overrides. This
+  * allows applications using the context part of libdnf (e.g. microdnf,
+    PackageKit) to take into account the main configuration from drop-in
+  * directories and repository overrides, similar to how libdnf5 does.
+    These directories are also monitored for changes (except when using 
non-root
+    installroot path.)
+    This feature can be disabled at build time (ENABLE_DNF5_CONF_DROP_IN,
+    ENABLE_DNF5_CONF_REPOS_OVERRIDE CMake options).
+  * context: dnf_context_set_install_root() now sets installroot also to global
+    mainConf configuration.
+  * IniParser: Support glob range definition in section names
+  * history database: Add "persistence" column (possible values are UNKNOWN,
+    PERSIST, or TRANSIENT).
+  * conf: Add usr_drift_protected_paths configuration option which can be
+    configured by adding .conf files to the drop-in directory
+    /etc/dnf/usr-drift-protected-paths.d, similar to /etc/dnf/protected.d.
+  * Distributions will be able to add paths that are known to cause problems
+    when their contents drift with respect to /usr, e.g. /etc/pam.d.
+  * context: Save repository configuration with dnf_repo_commit() to override 
file.
+    Previously, repository configuration changes were written directly to the
+    original configuration file. Now they are written to the overwrite file
+    "99-config_manager.repo" for compatibility with the dnf5 config-manager.
+  * config: Convert "protected_packages" to an append option
+  * Don't prepend installroot to varsdir in libdnf::dnf_context_load_vars()
+  * Fix file name comparison in filesystem::createSortedFileList()
+  * Stop importing subkeys to RPM >= 5.99.90 because RPM 6 handles subkeys
+    automatically.
+  * Fix typos in messages in package problems dictionary
+  * build: Fix searching libdnf header files when generating bindings with Swig
+  * build: Don't probe for libcheck dependency if no tests are going to be 
built
+  * spec: Consistently use CMake RPM macros
+  * tests: Replace deprecated "check" macros
+  * tests: Verify "fopen" return value otherwise we could crash
+  * New functions filesystem::pathJoin(), filesystem::createSortedFileList(),
+    filesystem::getRealpath(), filesystem::isSubdirectory().
+  * Add libdnf::MergedTransaction::listPersistences() method.
+  * Always use result config.optBinds() by reference, not copy
+  * Remove unused functions with a bug
+  * config: Support optionTListAppend for options lacking fromString
+- modified patches
+  * libdnf-0.55.0-Switch-allow_vendor_change-off.patch (refreshed)
+  * libdnf-0.72.0-with-static-libsolvext.patch (refreshed)
+
+-------------------------------------------------------------------

Old:
----
  libdnf-0.74.0.tar.gz

New:
----
  libdnf-0.75.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ libdnf.spec ++++++
--- /var/tmp/diff_new_pack.5EtBxK/_old  2026-06-03 20:22:41.205846260 +0200
+++ /var/tmp/diff_new_pack.5EtBxK/_new  2026-06-03 20:22:41.213846592 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package libdnf
 #
-# Copyright (c) 2024 SUSE LLC
+# Copyright (c) 2026 SUSE LLC and contributors
 # Copyright (c) 2023 Neal Gompa <[email protected]>.
 # Copyright (c) 2025 Andreas Stieger <[email protected]>
 #
@@ -35,7 +35,7 @@
 %define devname %{name}-devel
 
 Name:           libdnf
-Version:        0.74.0
+Version:        0.75.0
 Release:        0
 Summary:        Library providing C and Python APIs atop libsolv
 License:        LGPL-2.1-or-later

++++++ libdnf-0.55.0-Switch-allow_vendor_change-off.patch ++++++
--- /var/tmp/diff_new_pack.5EtBxK/_old  2026-06-03 20:22:41.341851902 +0200
+++ /var/tmp/diff_new_pack.5EtBxK/_new  2026-06-03 20:22:41.345852068 +0200
@@ -10,11 +10,11 @@
  libdnf/dnf-sack.cpp        | 2 +-
  2 files changed, 2 insertions(+), 2 deletions(-)
 
-diff --git a/libdnf/conf/ConfigMain.cpp b/libdnf/conf/ConfigMain.cpp
-index 1ffd3b33..c451015d 100644
---- a/libdnf/conf/ConfigMain.cpp
-+++ b/libdnf/conf/ConfigMain.cpp
-@@ -216,7 +216,7 @@ class ConfigMain::Impl {
+Index: libdnf-0.75.0/libdnf/conf/ConfigMain.cpp
+===================================================================
+--- libdnf-0.75.0.orig/libdnf/conf/ConfigMain.cpp
++++ libdnf-0.75.0/libdnf/conf/ConfigMain.cpp
+@@ -223,7 +223,7 @@ class ConfigMain::Impl {
      OptionBool obsoletes{true};
      OptionBool showdupesfromrepos{false};
      OptionBool exit_on_lock{false};
@@ -23,10 +23,10 @@
      OptionSeconds metadata_timer_sync{60 * 60 * 3}; // 3 hours
      OptionStringList disable_excludes{std::vector<std::string>{}};
      OptionEnum<std::string> multilib_policy{"best", {"best", "all"}}; // :api
-diff --git a/libdnf/dnf-sack.cpp b/libdnf/dnf-sack.cpp
-index 9fd2c72d..e0b53b60 100644
---- a/libdnf/dnf-sack.cpp
-+++ b/libdnf/dnf-sack.cpp
+Index: libdnf-0.75.0/libdnf/dnf-sack.cpp
+===================================================================
+--- libdnf-0.75.0.orig/libdnf/dnf-sack.cpp
++++ libdnf-0.75.0/libdnf/dnf-sack.cpp
 @@ -190,7 +190,7 @@ dnf_sack_init(DnfSack *sack)
      priv->running_kernel_fn = running_kernel;
      priv->considered_uptodate = TRUE;
@@ -36,7 +36,4 @@
      queue_init(&priv->installonly);
  
      /* logging up after this*/
--- 
-2.28.0
-
 

++++++ libdnf-0.72.0-with-static-libsolvext.patch ++++++
--- /var/tmp/diff_new_pack.5EtBxK/_old  2026-06-03 20:22:41.481857710 +0200
+++ /var/tmp/diff_new_pack.5EtBxK/_new  2026-06-03 20:22:41.513859038 +0200
@@ -18,11 +18,11 @@
  3 files changed, 43 insertions(+)
  create mode 100644 cmake/modules/FindLZMA.cmake
 
-diff --git a/CMakeLists.txt b/CMakeLists.txt
-index 6444c374..15c86b40 100644
---- a/CMakeLists.txt
-+++ b/CMakeLists.txt
-@@ -68,6 +68,19 @@ pkg_check_modules(RPM REQUIRED rpm>=4.15.0)
+Index: libdnf-0.75.0/CMakeLists.txt
+===================================================================
+--- libdnf-0.75.0.orig/CMakeLists.txt
++++ libdnf-0.75.0/CMakeLists.txt
+@@ -76,6 +76,19 @@ endif()
  pkg_check_modules(SMARTCOLS REQUIRED smartcols)
  pkg_check_modules(SQLite3 REQUIRED sqlite3)
  
@@ -39,14 +39,13 @@
 +find_library (ZSTD_LIBRARY NAMES zstd)
 +# End static libsolvext dynamic library dependencies
 +
- # always enable linking with libdnf utils
- include_directories(${CMAKE_SOURCE_DIR} libdnf/utils/)
- 
-diff --git a/cmake/modules/FindLZMA.cmake b/cmake/modules/FindLZMA.cmake
-new file mode 100644
-index 00000000..eb112dff
+ if (WITH_ZCHUNK)
+     pkg_check_modules(ZCHUNKLIB zck>=0.9.11 REQUIRED)
+     set (CMAKE_CXX_FLAGS          "${CMAKE_CXX_FLAGS} -DWITH_ZCHUNK")
+Index: libdnf-0.75.0/cmake/modules/FindLZMA.cmake
+===================================================================
 --- /dev/null
-+++ b/cmake/modules/FindLZMA.cmake
++++ libdnf-0.75.0/cmake/modules/FindLZMA.cmake
 @@ -0,0 +1,25 @@
 +# - Find lzma
 +# Find the native LZMA headers and library
@@ -73,10 +72,10 @@
 +ELSE(LZMA_FOUND)
 +  SET( LZMA_LIBRARIES )
 +ENDIF(LZMA_FOUND)
-diff --git a/libdnf/CMakeLists.txt b/libdnf/CMakeLists.txt
-index f37d236a..854a50e9 100644
---- a/libdnf/CMakeLists.txt
-+++ b/libdnf/CMakeLists.txt
+Index: libdnf-0.75.0/libdnf/CMakeLists.txt
+===================================================================
+--- libdnf-0.75.0.orig/libdnf/CMakeLists.txt
++++ libdnf-0.75.0/libdnf/CMakeLists.txt
 @@ -81,6 +81,11 @@ target_link_libraries(libdnf
      ${JSONC_LIBRARIES}
      ${LIBMODULEMD_LIBRARIES}
@@ -89,7 +88,4 @@
  )
  
  if(ENABLE_RHSM_SUPPORT)
--- 
-2.41.0
-
 

++++++ libdnf-0.74.0.tar.gz -> libdnf-0.75.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/.github/workflows/ci.yml 
new/libdnf-0.75.0/.github/workflows/ci.yml
--- old/libdnf-0.74.0/.github/workflows/ci.yml  2025-03-06 22:45:00.000000000 
+0100
+++ new/libdnf-0.75.0/.github/workflows/ci.yml  1970-01-01 01:00:00.000000000 
+0100
@@ -1,81 +0,0 @@
----
-name: DNF CI
-on: pull_request_target
-
-jobs:
-  copr-build:
-    name: Copr Build
-    runs-on: ubuntu-latest
-    container:
-      image: ghcr.io/rpm-software-management/dnf-ci-host
-    outputs:
-      package-urls: ${{steps.copr-build.outputs.package-urls}}
-    steps:
-      - name: Check out ci-dnf-stack
-        uses: actions/checkout@v2
-        with:
-          repository: rpm-software-management/ci-dnf-stack
-          ref: dnf-4-stack
-
-
-      - name: Setup CI
-        id: setup-ci
-        uses: ./.github/actions/setup-ci
-        with:
-          copr-user: ${{secrets.COPR_USER}}
-          copr-api-token: ${{secrets.COPR_API_TOKEN}}
-
-      - name: Check out sources
-        uses: actions/checkout@v2
-        with:
-          path: gits/${{github.event.repository.name}}
-          ref: ${{github.event.pull_request.head.sha}}  # check out the PR HEAD
-          fetch-depth: 0
-
-      - name: Run Copr Build
-        id: copr-build
-        uses: ./.github/actions/copr-build
-        with:
-          copr-user: ${{steps.setup-ci.outputs.copr-user}}
-
-  integration-tests:
-    name: DNF Integration Tests
-    needs: copr-build
-    runs-on: ubuntu-latest
-    container:
-      image: ghcr.io/rpm-software-management/dnf-ci-host
-      options: --privileged
-      volumes:
-        # A workaround for an undeterministic "OCI not found" error, see
-        # https://github.com/containers/podman/issues/10321
-        - /var/lib/mycontainer:/var/lib/containers
-    steps:
-      - name: Check out ci-dnf-stack
-        uses: actions/checkout@v2
-        with:
-          repository: rpm-software-management/ci-dnf-stack
-          ref: dnf-4-stack
-
-      - name: Run Integration Tests
-        uses: ./.github/actions/integration-tests
-        with:
-          package-urls: ${{needs.copr-build.outputs.package-urls}}
-
-  ansible-tests:
-    name: Ansible Tests
-    needs: copr-build
-    runs-on: ubuntu-latest
-    container:
-      image: ghcr.io/rpm-software-management/dnf-ci-host
-      options: --privileged
-    steps:
-      - name: Check out ci-dnf-stack
-        uses: actions/checkout@v2
-        with:
-          repository: rpm-software-management/ci-dnf-stack
-          ref: dnf-4-stack
-
-      - name: Run Ansible Tests
-        uses: ./.github/actions/ansible-tests
-        with:
-          package-urls: ${{needs.copr-build.outputs.package-urls}}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/.packit.yaml 
new/libdnf-0.75.0/.packit.yaml
--- old/libdnf-0.74.0/.packit.yaml      2025-03-06 22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/.packit.yaml      2025-10-20 15:41:49.000000000 +0200
@@ -3,17 +3,33 @@
 
 specfile_path: libdnf.spec
 
+# Build the package with the current specfile version (otherwise it uses last 
git tag).
+# This is needed because other packages can depend on the new version.
+actions:
+  get-current-version:
+    - rpmspec --srpm --query --queryformat "%{version}" libdnf.spec
+
 jobs:
   - job: copr_build
     trigger: pull_request
     targets:
+      - epel-9
+      - epel-10
       - fedora-all
+    additional_repos:
+      - "copr://rpmsoftwaremanagement/dnf-nightly"
   - job: tests
     trigger: pull_request
     identifier: "dnf-tests"
     targets:
+      # EPELs fail now for unrelated reasons
       - fedora-all
     fmf_url: https://github.com/rpm-software-management/ci-dnf-stack.git
     fmf_ref: enable-tmt-dnf-4-stack
     tmt_plan: "^/plans/integration/behave-dnf$"
-
+    tf_extra_params:
+      environments:
+        - artifacts:
+            - type: repository-file
+              # We use the rawhide repo file for all fedora releases, the url 
doesn't change and it is not rawhide specific (it contains 
"fedora-$releasever-$basearch")
+              id: 
https://copr.fedorainfracloud.org/coprs/rpmsoftwaremanagement/dnf-nightly/repo/fedora-rawhide/rpmsoftwaremanagement-dnf-nightly-fedora-rawhide.repo
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/CMakeLists.txt 
new/libdnf-0.75.0/CMakeLists.txt
--- old/libdnf-0.74.0/CMakeLists.txt    2025-03-06 22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/CMakeLists.txt    2025-10-20 15:41:49.000000000 +0200
@@ -31,6 +31,8 @@
 option(WITH_HTML "Enables hawkey HTML generation" ON)
 option(WITH_MAN "Enables hawkey man page generation" ON)
 option(WITH_ZCHUNK "Build with zchunk support" ON)
+option(ENABLE_DNF5_CONF_DROP_IN "Build with support for libdnf5 drop-in 
configuration directories?" ON)
+option(ENABLE_DNF5_CONF_REPOS_OVERRIDE "Build with support for libdnf5 
repository configuration overrides?" ON)
 option(ENABLE_RHSM_SUPPORT "Build with Red Hat Subscription Manager support?" 
OFF)
 option(ENABLE_SOLV_URPMREORDER "Build with support for URPM-like solution 
reordering?" OFF)
 option(WITH_TESTS "Enables unit tests" ON)
@@ -40,6 +42,9 @@
 option(WITH_SANITIZERS "Build with address, leak and undefined sanitizers 
(DEBUG ONLY)" OFF)
 
 
+# always place our header files before system ones
+include_directories(${CMAKE_SOURCE_DIR} libdnf/utils/)
+
 # load pkg-config first; it's required by other modules
 find_package(PkgConfig REQUIRED)
 if(APPLE)
@@ -54,7 +59,6 @@
 
 
 # build dependencies via pkg-config
-pkg_check_modules(CHECK REQUIRED check)
 pkg_check_modules(GLIB REQUIRED gio-unix-2.0>=2.46.0)
 include_directories(${GLIB_INCLUDE_DIRS})
 pkg_check_modules(JSONC REQUIRED json-c)
@@ -63,13 +67,15 @@
 pkg_check_modules(REPO REQUIRED librepo>=1.18.0)
 include_directories(${REPO_INCLUDE_DIRS})
 link_directories(${REPO_LIBRARY_DIRS})
+
 pkg_check_modules(RPM REQUIRED rpm>=4.15.0)
+if (RPM_VERSION VERSION_GREATER_EQUAL "5.99.90")
+    add_definitions(-DRPM_AUTOADDS_SUBKEYS)
+endif()
+
 pkg_check_modules(SMARTCOLS REQUIRED smartcols)
 pkg_check_modules(SQLite3 REQUIRED sqlite3)
 
-# always enable linking with libdnf utils
-include_directories(${CMAKE_SOURCE_DIR} libdnf/utils/)
-
 if (WITH_ZCHUNK)
     pkg_check_modules(ZCHUNKLIB zck>=0.9.11 REQUIRED)
     set (CMAKE_CXX_FLAGS          "${CMAKE_CXX_FLAGS} -DWITH_ZCHUNK")
@@ -132,6 +138,16 @@
 # tests
 add_definitions(-DTESTDATADIR="${CMAKE_SOURCE_DIR}/data/tests")
 
+# Use libdnf5 drop-in configuration directories including distribution 
configuration.
+if(ENABLE_DNF5_CONF_DROP_IN)
+    add_definitions(-DDNF5_CONF_DROP_IN)
+endif()
+
+# Use libdnf5 repository overrides. Save repo conf chnges to 
"99-config_manager.repo" override.
+if(ENABLE_DNF5_CONF_REPOS_OVERRIDE)
+    add_definitions(-DDNF5_CONF_REPOS_OVERRIDE)
+endif()
+
 # librhsm
 if(ENABLE_RHSM_SUPPORT)
     add_definitions(-DRHSM_SUPPORT)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/README.md new/libdnf-0.75.0/README.md
--- old/libdnf-0.74.0/README.md 2025-03-06 22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/README.md 2025-10-20 15:41:49.000000000 +0200
@@ -4,10 +4,7 @@
 This library provides a high level package-manager. It's core library of 
[dnf](https://github.com/rpm-software-management/dnf), 
[PackageKit](https://github.com/hughsie/PackageKit) and 
[rpm-ostree](https://github.com/projectatomic/rpm-ostree). It's replacement for 
deprecated [hawkey library](https://github.com/rpm-software-management/hawkey) 
which it contains inside and uses 
[librepo](https://github.com/rpm-software-management/librepo) under the hood.
 
 :warning: :warning: :warning:
-**Note that libdnf is currently being reworked and is
-considered unstable. Once major users like PackageKit and
-DNF are fully ported, a new stable release will be
-considered.**
+**Note that DNF4 and libdnf are superseded by [DNF5 and 
libdnf5](https://github.com/rpm-software-management/dnf5). DNF5 is the default 
package manager in Fedora as of Fedora 41. New projects should integrate with 
libdnf5 instead.**
 :warning: :warning: :warning:
 
 License
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/VERSION.cmake 
new/libdnf-0.75.0/VERSION.cmake
--- old/libdnf-0.74.0/VERSION.cmake     2025-03-06 22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/VERSION.cmake     2025-10-20 15:41:49.000000000 +0200
@@ -1,5 +1,5 @@
 set (DEFAULT_LIBDNF_MAJOR_VERSION 0)
-set (DEFAULT_LIBDNF_MINOR_VERSION 74)
+set (DEFAULT_LIBDNF_MINOR_VERSION 75)
 set (DEFAULT_LIBDNF_MICRO_VERSION 0)
 
 if(DEFINED LIBDNF_MAJOR_VERSION)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/docs/release_notes.rst 
new/libdnf-0.75.0/docs/release_notes.rst
--- old/libdnf-0.74.0/docs/release_notes.rst    2025-03-06 22:45:00.000000000 
+0100
+++ new/libdnf-0.75.0/docs/release_notes.rst    2025-10-20 15:41:49.000000000 
+0200
@@ -20,6 +20,61 @@
 ######################
 
 ====================
+0.75.0 Release Notes
+====================
+
+Enhancements:
+
+- context: Support libdnf5 drop-in directories and repository overrides. This
+  allows applications using the context part of libdnf (e.g.  microdnf,
+  PackageKit) to take into account the main configuration from drop-in
+  directories and repository overrides, similar to how libdnf5 does.
+  These directories are also monitored for changes (except when using non-root
+  installroot path.)
+  This feature can be disabled at build time (ENABLE_DNF5_CONF_DROP_IN,
+  ENABLE_DNF5_CONF_REPOS_OVERRIDE CMake options).
+- context: dnf_context_set_install_root() now sets installroot also to global
+  mainConf configuration.
+- IniParser: Support glob range definition in section names
+- history database: Add "persistence" column (possible values are UNKNOWN,
+  PERSIST, or TRANSIENT).
+- conf: Add usr_drift_protected_paths configuration option which can be
+  configured by adding .conf files to the drop-in directory
+  /etc/dnf/usr-drift-protected-paths.d, similar to /etc/dnf/protected.d.
+  Distributions will be able to add paths that are known to cause problems
+  when their contents drift with respect to /usr, e.g. /etc/pam.d.
+
+Changes:
+
+- context: Save repository configuration with dnf_repo_commit() to override 
file.
+  Previously, repository configuration changes were written directly to the
+  original configuration file. Now they are written to the overwrite file
+  "99-config_manager.repo" for compatibility with the dnf5 config-manager.
+- config: Convert "protected_packages" to an append option
+
+Bug fixes:
+
+- Don't prepend installroot to varsdir in libdnf::dnf_context_load_vars()
+- Fix file name comparison in filesystem::createSortedFileList()
+- Stop importing subkeys to RPM >= 5.99.90 because RPM 6 handles subkeys
+  automatically.
+- Fix typos in messages in package problems dictionary
+- build: Fix searching libdnf header files when generating bindings with Swig
+- build: Don't probe for libcheck dependency if no tests are going to be built
+- spec: Consistently use CMake RPM macros
+- tests: Replace deprecated "check" macros
+- tests: Verify "fopen" return value otherwise we could crash
+
+Internal changes:
+
+- New functions filesystem::pathJoin(), filesystem::createSortedFileList(),
+  filesystem::getRealpath(), filesystem::isSubdirectory().
+- Add libdnf::MergedTransaction::listPersistences() method.
+- Always use result config.optBinds() by reference, not copy
+- Remove unused functions with a bug
+- config: Support optionTListAppend for options lacking fromString
+
+====================
 0.74.0 Release Notes
 ====================
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/conf/Config-private.hpp 
new/libdnf-0.75.0/libdnf/conf/Config-private.hpp
--- old/libdnf-0.74.0/libdnf/conf/Config-private.hpp    2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/conf/Config-private.hpp    2025-10-20 
15:41:49.000000000 +0200
@@ -22,6 +22,7 @@
 #define _LIBDNF_CONFIG_PRIVATE_HPP
 
 #include "Option.hpp"
+#include "OptionStringList.hpp"
 
 namespace libdnf {
 
@@ -33,7 +34,7 @@
         return;
     }
     auto addPriority = priority < option.getPriority() ? option.getPriority() 
: priority;
-    auto val = option.fromString(value);
+    auto val = OptionStringList(std::vector<std::string>{}).fromString(value);
     bool first = true;
     for (auto & item : val) {
         if (item.empty()) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/conf/ConfigMain.cpp 
new/libdnf-0.75.0/libdnf/conf/ConfigMain.cpp
--- old/libdnf-0.74.0/libdnf/conf/ConfigMain.cpp        2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/conf/ConfigMain.cpp        2025-10-20 
15:41:49.000000000 +0200
@@ -293,6 +293,8 @@
     OptionBool countme{false};
     OptionBool protect_running_kernel{true};
 
+    OptionStringList 
usr_drift_protected_paths{resolveGlobs("glob:/etc/dnf/usr-drift-protected-paths.d/*.conf")};
+
     // Repo main config
 
     OptionNumber<std::uint32_t> retries{10};
@@ -460,6 +462,12 @@
     owner.optBinds().add("countme", countme);
     owner.optBinds().add("protect_running_kernel", protect_running_kernel);
     owner.optBinds().add("persistence", persistence);
+    owner.optBinds().add("usr_drift_protected_paths", 
usr_drift_protected_paths,
+        [&](Option::Priority priority, const std::string & value){
+            if (priority >= usr_drift_protected_paths.getPriority())
+                usr_drift_protected_paths.set(priority, resolveGlobs(value));
+        }, nullptr, false
+    );
 
     // Repo main config
 
@@ -506,11 +514,11 @@
     owner.optBinds().add("proxy_username", proxy_username);
     owner.optBinds().add("proxy_password", proxy_password);
     owner.optBinds().add("proxy_auth_method", proxy_auth_method);
+
     owner.optBinds().add("protected_packages", protected_packages,
         [&](Option::Priority priority, const std::string & value){
-            if (priority >= protected_packages.getPriority())
-                protected_packages.set(priority, resolveGlobs(value));
-        }, nullptr, false
+            optionTListAppend(protected_packages, priority, 
resolveGlobs(value));
+        }, nullptr, true
     );
 
     owner.optBinds().add("username", username);
@@ -616,6 +624,7 @@
 OptionBool & ConfigMain::downloadonly() { return pImpl->downloadonly; }
 OptionBool & ConfigMain::ignorearch() { return pImpl->ignorearch; }
 OptionEnum<std::string> & ConfigMain::persistence() { return 
pImpl->persistence; }
+OptionStringList & ConfigMain::usr_drift_protected_paths() { return 
pImpl->usr_drift_protected_paths; }
 
 OptionString & ConfigMain::module_platform_id() { return 
pImpl->module_platform_id; }
 OptionBool & ConfigMain::module_stream_switch() { return 
pImpl->module_stream_switch; }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/conf/ConfigMain.hpp 
new/libdnf-0.75.0/libdnf/conf/ConfigMain.hpp
--- old/libdnf-0.74.0/libdnf/conf/ConfigMain.hpp        2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/conf/ConfigMain.hpp        2025-10-20 
15:41:49.000000000 +0200
@@ -126,6 +126,7 @@
     OptionBool & downloadonly();
     OptionBool & ignorearch();
     OptionEnum<std::string> & persistence();
+    OptionStringList & usr_drift_protected_paths();
 
     OptionString & module_platform_id();
     OptionBool & module_stream_switch();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/conf/ConfigRepo.cpp 
new/libdnf-0.75.0/libdnf/conf/ConfigRepo.cpp
--- old/libdnf-0.74.0/libdnf/conf/ConfigRepo.cpp        2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/conf/ConfigRepo.cpp        2025-10-20 
15:41:49.000000000 +0200
@@ -145,7 +145,13 @@
     owner.optBinds().add("proxy_auth_method", proxy_auth_method);
     owner.optBinds().add("username", username);
     owner.optBinds().add("password", password);
-    owner.optBinds().add("protected_packages", protected_packages);
+
+    owner.optBinds().add("protected_packages", protected_packages,
+        [&](Option::Priority priority, const std::string & value){
+            optionTListAppend(protected_packages, priority, value);
+        }, nullptr, true
+    );
+
     owner.optBinds().add("gpgcheck", gpgcheck);
     owner.optBinds().add("repo_gpgcheck", repo_gpgcheck);
     owner.optBinds().add("enablegroups", enablegroups);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/conf/Const.hpp 
new/libdnf-0.75.0/libdnf/conf/Const.hpp
--- old/libdnf-0.74.0/libdnf/conf/Const.hpp     2025-03-06 22:45:00.000000000 
+0100
+++ new/libdnf-0.75.0/libdnf/conf/Const.hpp     2025-10-20 15:41:49.000000000 
+0200
@@ -30,6 +30,13 @@
 constexpr const char * SYSTEM_CACHEDIR = "/var/cache/dnf";
 
 constexpr const char * CONF_FILENAME = "/etc/dnf/dnf.conf";
+// drop-in configuration directories
+constexpr const char * CONF_DIR = "/etc/dnf/libdnf5.conf.d";
+constexpr const char * DISTRIBUTION_CONF_DIR = 
"/usr/share/dnf5/libdnf.conf.d/";
+
+// directories with repository configuration overides
+constexpr const char * REPOS_OVERRIDE_DIR = "/etc/dnf/repos.override.d";
+constexpr const char * DISTRIBUTION_REPOS_OVERRIDE_DIR = 
"/usr/share/dnf5/repos.override.d";
 
 // More important varsdirs must be on the end of vector
 const std::vector<std::string> VARS_DIRS{"/etc/yum/vars", "/etc/dnf/vars"};
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/dnf-context.cpp 
new/libdnf-0.75.0/libdnf/dnf-context.cpp
--- old/libdnf-0.74.0/libdnf/dnf-context.cpp    2025-03-06 22:45:00.000000000 
+0100
+++ new/libdnf-0.75.0/libdnf/dnf-context.cpp    2025-10-20 15:41:49.000000000 
+0200
@@ -80,6 +80,7 @@
 #include "plugin/plugin-private.hpp"
 #include "utils/GLibLogger.hpp"
 #include "utils/os-release.hpp"
+#include "utils/utils.hpp"
 
 
 #define MAX_NATIVE_ARCHES    12
@@ -1516,6 +1517,9 @@
     DnfContextPrivate *priv = GET_PRIVATE(context);
     g_free(priv->install_root);
     priv->install_root = g_strdup(install_root);
+
+    auto & mainConf = libdnf::getGlobalMainConfig(false);
+    mainConf.installroot().set(libdnf::Option::Priority::RUNTIME, 
install_root);
 }
 
 /**
@@ -3927,7 +3931,7 @@
     auto priv = GET_PRIVATE(context);
     priv->vars->clear();
     for (auto dir = dnf_context_get_vars_dir(context); *dir; ++dir)
-        ConfigMain::addVarsFromDir(*priv->vars, 
std::string(priv->install_root) + *dir);
+        ConfigMain::addVarsFromDir(*priv->vars, *dir);
     ConfigMain::addVarsFromEnv(*priv->vars);
     priv->varsCached = true;
 }
@@ -3992,6 +3996,27 @@
     globalSetoptsInSync = true;
 }
 
+static void
+load_from_parser(const libdnf::ConfigParser & parser, const std::string & 
cfgPath) {
+    const auto & cfgParserData = parser.getData();
+    auto cfgParserDataIter = cfgParserData.find("main");
+    if (cfgParserDataIter != cfgParserData.end()) {
+        auto & optBinds = globalMainConfig->optBinds();
+        const auto & cfgParserMainSect = cfgParserDataIter->second;
+        for (const auto & opt : cfgParserMainSect) {
+            auto optBindsIter = optBinds.find(opt.first);
+            if (optBindsIter != optBinds.end()) {
+                try {
+                    
optBindsIter->second.newString(libdnf::Option::Priority::MAINCONFIG, 
opt.second);
+                } catch (const std::exception & ex) {
+                    g_warning("Config error in file \"%s\" section \"main\" 
key \"%s\": %s",
+                                cfgPath.c_str(), opt.first.c_str(), ex.what());
+                }
+            }
+        }
+    }
+}
+
 libdnf::ConfigMain & getGlobalMainConfig(bool canReadConfigFile)
 {
     std::lock_guard<std::mutex> guard(getGlobalMainConfigMutex);
@@ -4010,27 +4035,37 @@
             }
         }
 
-        libdnf::ConfigParser parser;
         const std::string 
cfgPath{globalMainConfig->config_file_path().getValue()};
         try {
-            parser.read(cfgPath);
-            const auto & cfgParserData = parser.getData();
-            auto cfgParserDataIter = cfgParserData.find("main");
-            if (cfgParserDataIter != cfgParserData.end()) {
-                auto optBinds = globalMainConfig->optBinds();
-                const auto & cfgParserMainSect = cfgParserDataIter->second;
-                for (const auto & opt : cfgParserMainSect) {
-                    auto optBindsIter = optBinds.find(opt.first);
-                    if (optBindsIter != optBinds.end()) {
-                        try {
-                            
optBindsIter->second.newString(libdnf::Option::Priority::MAINCONFIG, 
opt.second);
-                        } catch (const std::exception & ex) {
-                            g_warning("Config error in file \"%s\" section 
\"main\" key \"%s\": %s",
-                                      cfgPath.c_str(), opt.first.c_str(), 
ex.what());
-                        }
-                    }
+#ifdef DNF5_CONF_DROP_IN
+            std::string conf_dir_path{CONF_DIR};
+            std::string dist_conf_dir_path{DISTRIBUTION_CONF_DIR};
+
+            // If the main configuration file is from install_root, read 
drop-in directories from install_root
+            const std::string installroot_path = 
globalMainConfig->installroot().getValue();
+            if (installroot_path != "/") {
+                const bool from_installroot = 
libdnf::filesystem::isSubdirectory(installroot_path, cfgPath);
+                if (from_installroot) {
+                    conf_dir_path = 
libdnf::filesystem::pathJoin(installroot_path, conf_dir_path);
+                    dist_conf_dir_path = 
libdnf::filesystem::pathJoin(installroot_path, dist_conf_dir_path);
                 }
             }
+
+            // Loads configuration from drop-in directories
+            const auto paths = 
libdnf::filesystem::createSortedFileList({conf_dir_path, dist_conf_dir_path}, 
"*.conf");
+            for (const auto & path : paths) {
+                libdnf::ConfigParser parser;
+                parser.read(path);
+                load_from_parser(parser, path);
+            }
+#endif
+
+            // Finally, if a user configuration filename is defined or the 
file exists in the default location,
+            // it will be loaded.
+            libdnf::ConfigParser parser;
+            parser.read(cfgPath);
+            load_from_parser(parser, cfgPath);
+
         } catch (const libdnf::ConfigParser::CantOpenFile & ex) {
             if (configFilePath) {
                 // Only warning is logged. But error is reported to the caller 
during loading
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/dnf-keyring.cpp 
new/libdnf-0.75.0/libdnf/dnf-keyring.cpp
--- old/libdnf-0.74.0/libdnf/dnf-keyring.cpp    2025-03-06 22:45:00.000000000 
+0100
+++ new/libdnf-0.75.0/libdnf/dnf-keyring.cpp    2025-10-20 15:41:49.000000000 
+0200
@@ -131,6 +131,10 @@
         goto out;
     }
 
+#ifndef RPM_AUTOADDS_SUBKEYS
+    /* RPM before 5.99.90 required adding subkeys explicitly.
+     * RPM >= 5.99.90 processes subkeys automatically with a primary key and
+     * fails on processing standalone subkeys in rpmKeyringAddKey(). */
     subkeys = rpmGetSubkeys(pubkey, &nsubkeys);
     for (int i = 0; i < nsubkeys; i++) {
         rpmPubkey subkey = subkeys[i];
@@ -144,6 +148,7 @@
             goto out;
         }
     }
+#endif
 
     /* success */
     g_debug("added missing public key %s to rpmdb", filename);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/dnf-repo-loader.cpp 
new/libdnf-0.75.0/libdnf/dnf-repo-loader.cpp
--- old/libdnf-0.74.0/libdnf/dnf-repo-loader.cpp        2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/dnf-repo-loader.cpp        2025-10-20 
15:41:49.000000000 +0200
@@ -31,8 +31,9 @@
  * See also: #DnfRepo
  */
 
-#include <strings.h>
+#include "conf/Const.hpp"
 
+#include <strings.h>
 #include <gio/gunixmounts.h>
 #include <librepo/util.h>
 #include <string.h>
@@ -595,6 +596,14 @@
         auto repo_dir = *iter;
         dnf_repo_loader_setup_monitor(self, repo_dir, true);
     }
+
+    /* setup a file monitor on the drop-in configuration directories */
+    dnf_repo_loader_setup_monitor(self, libdnf::CONF_DIR, true);
+    dnf_repo_loader_setup_monitor(self, libdnf::DISTRIBUTION_CONF_DIR, true);
+
+    /* setup a file monitor on the repositories overrides directories */
+    dnf_repo_loader_setup_monitor(self, libdnf::REPOS_OVERRIDE_DIR, true);
+    dnf_repo_loader_setup_monitor(self, 
libdnf::DISTRIBUTION_REPOS_OVERRIDE_DIR, true);
 }
 
 /**
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/dnf-repo.cpp 
new/libdnf-0.75.0/libdnf/dnf-repo.cpp
--- old/libdnf-0.74.0/libdnf/dnf-repo.cpp       2025-03-06 22:45:00.000000000 
+0100
+++ new/libdnf-0.75.0/libdnf/dnf-repo.cpp       2025-10-20 15:41:49.000000000 
+0200
@@ -33,6 +33,7 @@
  * See also: #DnfRepo
  */
 
+#include "conf/Const.hpp"
 #include "conf/OptionBool.hpp"
 #include "conf/ConfigParser.hpp"
 
@@ -57,9 +58,13 @@
 #include "dnf-types.h"
 #include "dnf-utils.h"
 #include "utils/File.hpp"
+#include "utils/filesystem.hpp"
 #include "utils/url-encode.hpp"
 #include "utils/utils.hpp"
 
+#ifdef DNF5_CONF_REPOS_OVERRIDE
+#include <map>
+#endif
 #include <set>
 #include <string>
 #include <vector>
@@ -85,6 +90,9 @@
     LrHandle        *repo_handle;
     LrResult        *repo_result;
     LrUrlVars       *urlvars;
+#ifdef DNF5_CONF_REPOS_OVERRIDE
+    std::map<std::string, std::string> *config_changes; // dnf_repo_set_data, 
dnf_repo_commit
+#endif
     bool            unit_test_mode;  /* ugly hack for unit tests */
 } DnfRepoPrivate;
 
@@ -120,6 +128,10 @@
     if (priv->context != NULL)
         g_object_remove_weak_pointer(G_OBJECT(priv->context),
                                      (void **) &priv->context);
+#ifdef DNF5_CONF_REPOS_OVERRIDE
+    if (priv->config_changes)
+        delete priv->config_changes;
+#endif
 
     G_OBJECT_CLASS(dnf_repo_parent_class)->finalize(object);
 }
@@ -924,6 +936,66 @@
     }
 }
 
+#ifdef DNF5_CONF_REPOS_OVERRIDE
+/* Loads repository configuration overrides */
+static void
+dnf_repo_conf_load_overrides(DnfRepo *repo, const char *repoId)
+{
+    DnfRepoPrivate *priv = GET_PRIVATE(repo);
+    auto & config = *priv->repo->getConfig();
+
+    std::string repos_override_dir_path{libdnf::REPOS_OVERRIDE_DIR};
+    std::string 
dist_repos_override_dir_path{libdnf::DISTRIBUTION_REPOS_OVERRIDE_DIR};
+
+    // If the repoconfig file is from install_root, read overrides from 
install_root
+    const std::string installroot_path = 
dnf_context_get_install_root(priv->context);
+    if (priv->filename && installroot_path != "/") {
+        const bool from_installroot = 
libdnf::filesystem::isSubdirectory(installroot_path, priv->filename);
+        if (from_installroot) {
+            repos_override_dir_path = 
libdnf::filesystem::pathJoin(installroot_path, repos_override_dir_path);
+            dist_repos_override_dir_path = 
libdnf::filesystem::pathJoin(installroot_path, dist_repos_override_dir_path);
+        }
+    }
+
+    const auto paths = libdnf::filesystem::createSortedFileList(
+        {repos_override_dir_path, dist_repos_override_dir_path}, "*.repo");
+
+    for (const auto & path : paths) {
+        libdnf::ConfigParser parser;
+        parser.read(path);
+        const auto & cfg_parser_data = parser.getData();
+        for (const auto & cfg_parser_data_iter : cfg_parser_data) {
+            const auto & section = cfg_parser_data_iter.first;
+            g_autofree gchar * repo_id_pattern = dnf_repo_substitute(repo, 
section.c_str());
+
+            if (fnmatch(repo_id_pattern, repoId, FNM_EXTMATCH) != 0) {
+                continue;
+            }
+
+            auto & optBinds = config.optBinds();
+            for (const auto & opt : cfg_parser_data_iter.second) {
+                auto optBindsIter = optBinds.find(opt.first);
+                if (optBindsIter != optBinds.end()) {
+
+                    // Substitute vars.
+                    g_autofree gchar * subst_value = dnf_repo_substitute(repo, 
opt.second.c_str());
+
+                    try {
+                        
optBindsIter->second.newString(libdnf::Option::Priority::REPOCONFIG, 
subst_value);
+                    } catch (const std::exception & ex) {
+                        g_warning("Config error in file \"%s\" section \"%s\" 
key \"%s\": %s",
+                                    path.c_str(), repo_id_pattern, 
opt.first.c_str(), ex.what());
+                    }
+                } else {
+                    g_debug("Unknown configuration option in file \"%s\": %s = 
%s", path.c_str(), opt.first.c_str(),
+                            opt.second.c_str());
+                }
+            }
+        }
+    }
+}
+#endif
+
 static void
 dnf_repo_apply_setopts(libdnf::ConfigRepo &config, const char *repoId)
 {
@@ -967,6 +1039,9 @@
     // Reload repository configuration from keyfile.
     if (reloadFromGKeyFile) {
         dnf_repo_conf_from_gkeyfile(repo, repoId, priv->keyfile);
+#ifdef DNF5_CONF_REPOS_OVERRIDE
+        dnf_repo_conf_load_overrides(repo, repoId);
+#endif
         dnf_repo_apply_setopts(*conf, repoId);
     }
 
@@ -1248,6 +1323,9 @@
 
     auto conf = priv->repo->getConfig();
     dnf_repo_conf_from_gkeyfile(repo, repoId, priv->keyfile);
+#ifdef DNF5_CONF_REPOS_OVERRIDE
+    dnf_repo_conf_load_overrides(repo, repoId);
+#endif
     dnf_repo_apply_setopts(*conf, repoId);
 
     auto sslverify = conf->sslverify().getValue();
@@ -2041,10 +2119,50 @@
                   GError **error) try
 {
     DnfRepoPrivate *priv = GET_PRIVATE(repo);
+
+#ifdef DNF5_CONF_REPOS_OVERRIDE
+    // we note the changes
+    // dnf_repo_commit writes them to the override configuration file
+    if (!priv->config_changes) {
+        priv->config_changes = new std::map<std::string, std::string>;
+    }
+    (*priv->config_changes)[parameter] = value;
+#endif
+
     g_key_file_set_string(priv->keyfile, priv->repo->getId().c_str(), 
parameter, value);
     return TRUE;
 } CATCH_TO_GERROR(FALSE)
 
+#ifdef DNF5_CONF_REPOS_OVERRIDE
+static std::string
+get_repos_config_override_dir_path(DnfRepo *repo)
+{
+    DnfRepoPrivate *priv = GET_PRIVATE(repo);
+
+    if (priv->filename) {
+        auto install_root = dnf_context_get_install_root(priv->context);
+        if (strcmp(install_root, "/") != 0 && 
libdnf::filesystem::isSubdirectory(install_root, priv->filename)) {
+            // we are working with install_root and the repository 
configuration file is located in installroot
+            // -> we will use overrides from install_root
+            return libdnf::filesystem::pathJoin(install_root, 
libdnf::REPOS_OVERRIDE_DIR);
+        }
+    }
+
+    return libdnf::REPOS_OVERRIDE_DIR;
+}
+
+static void
+modify_config(libdnf::ConfigParser & parser, const std::string & section_id, 
const std::map<std::string, std::string> & opts)
+{
+    if (!parser.hasSection(section_id)) {
+        parser.addSection(section_id);
+    }
+    for (const auto & key_val : opts) {
+        parser.setValue(section_id, key_val.first, key_val.second, "");
+    }
+}
+#endif
+
 /**
  * dnf_repo_commit:
  * @repo: a #DnfRepo instance.
@@ -2071,11 +2189,51 @@
         return FALSE;
     }
 
+#ifdef DNF5_CONF_REPOS_OVERRIDE
+    constexpr const char * REPOS_OVERRIDE_CFG_HEADER =
+    "# Generated by libdnf.\n# Do not modify this file manually.\n";
+    const std::string CFG_MANAGER_REPOS_OVERRIDE_FILENAME = 
"99-config_manager.repo";
+
+    // Write new and modify existing options in the repositories overrides 
configuration file.
+    if (priv->config_changes && !priv->config_changes->empty()) {
+        const auto repos_config_override_dir_path = 
get_repos_config_override_dir_path(repo);
+        // Create directory tree if not exist
+        libdnf::makeDirPath(repos_config_override_dir_path);
+
+        auto repos_override_file_path =
+            libdnf::filesystem::pathJoin(repos_config_override_dir_path, 
CFG_MANAGER_REPOS_OVERRIDE_FILENAME);
+
+        libdnf::ConfigParser parser;
+
+        const bool exists = 
libdnf::filesystem::exists(repos_override_file_path);
+        if (exists) {
+            parser.read(repos_override_file_path);
+        }
+
+        parser.getHeader() = REPOS_OVERRIDE_CFG_HEADER;
+
+        modify_config(parser, priv->repo->getId(), *priv->config_changes);
+
+        parser.write(repos_override_file_path, false);
+        if (!exists) {
+            // Sets permissions to "rw-r--r--"
+            chmod(repos_override_file_path.c_str(), S_IRUSR | S_IWUSR | 
S_IRGRP | S_IROTH);
+        }
+
+        // Config changes were written to file, we delete the list of changes 
for writing
+        delete priv->config_changes;
+        priv->config_changes = nullptr;
+    }
+    return TRUE;
+
+#else
+
     /* dump updated file to disk */
     data = g_key_file_to_data(priv->keyfile, NULL, error);
     if (data == NULL)
         return FALSE;
     return g_file_set_contents(priv->filename, data, -1, error);
+#endif
 } CATCH_TO_GERROR(FALSE)
 
 /**
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/goal/Goal.cpp 
new/libdnf-0.75.0/libdnf/goal/Goal.cpp
--- old/libdnf-0.74.0/libdnf/goal/Goal.cpp      2025-03-06 22:45:00.000000000 
+0100
+++ new/libdnf-0.75.0/libdnf/goal/Goal.cpp      2025-10-20 15:41:49.000000000 
+0200
@@ -87,8 +87,8 @@
 };
 
 static const std::map<int, const char *> PKG_PROBLEMS_DICT = {
-    {RULE_DISTUPGRADE, M_("%s from %s  does not belong to a distupgrade 
repository")},
-    {RULE_INFARCH, M_("%s from %s  has inferior architecture")},
+    {RULE_DISTUPGRADE, M_("%s from %s does not belong to a distupgrade 
repository")},
+    {RULE_INFARCH, M_("%s from %s has inferior architecture")},
     {RULE_UPDATE, M_("problem with installed package ")},
     {RULE_JOB, M_("conflicting requests")},
     {RULE_JOB_UNSUPPORTED, M_("unsupported request")},
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libdnf-0.74.0/libdnf/transaction/MergedTransaction.cpp 
new/libdnf-0.75.0/libdnf/transaction/MergedTransaction.cpp
--- old/libdnf-0.74.0/libdnf/transaction/MergedTransaction.cpp  2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/transaction/MergedTransaction.cpp  2025-10-20 
15:41:49.000000000 +0200
@@ -97,6 +97,16 @@
     return cmdLines;
 }
 
+std::vector< TransactionPersistence >
+MergedTransaction::listPersistences() const
+{
+    std::vector< TransactionPersistence > persistences;
+    for (auto t : transactions) {
+        persistences.push_back(t->getPersistence());
+    }
+    return persistences;
+}
+
 std::vector< TransactionState >
 MergedTransaction::listStates() const
 {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libdnf-0.74.0/libdnf/transaction/MergedTransaction.hpp 
new/libdnf-0.75.0/libdnf/transaction/MergedTransaction.hpp
--- old/libdnf-0.74.0/libdnf/transaction/MergedTransaction.hpp  2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/transaction/MergedTransaction.hpp  2025-10-20 
15:41:49.000000000 +0200
@@ -47,6 +47,7 @@
     std::vector< int64_t > listIds() const;
     std::vector< uint32_t > listUserIds() const;
     std::vector< std::string > listCmdlines() const;
+    std::vector< TransactionPersistence > listPersistences() const;
     std::vector< TransactionState > listStates() const;
     std::vector< std::string > listReleasevers() const;
     std::vector< std::string > listComments() const;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/transaction/Swdb.cpp 
new/libdnf-0.75.0/libdnf/transaction/Swdb.cpp
--- old/libdnf-0.74.0/libdnf/transaction/Swdb.cpp       2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/transaction/Swdb.cpp       2025-10-20 
15:41:49.000000000 +0200
@@ -385,6 +385,14 @@
     transactionInProgress->setReleasever(value);
 }
 
+void
+Swdb::setPersistence(TransactionPersistence persistence)
+{
+    if (!transactionInProgress) {
+        throw std::logic_error(_("Not in progress"));
+    }
+    transactionInProgress->setPersistence(persistence);
+}
 
 void
 Swdb::addConsoleOutputLine(int fileDescriptor, std::string line)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/transaction/Swdb.hpp 
new/libdnf-0.75.0/libdnf/transaction/Swdb.hpp
--- old/libdnf-0.74.0/libdnf/transaction/Swdb.hpp       2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/transaction/Swdb.hpp       2025-10-20 
15:41:49.000000000 +0200
@@ -114,6 +114,7 @@
 
     // misc
     void setReleasever(std::string value);
+    void setPersistence(TransactionPersistence value);
     void addConsoleOutputLine(int fileDescriptor, std::string line);
 
     /**
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/transaction/Transaction.cpp 
new/libdnf-0.75.0/libdnf/transaction/Transaction.cpp
--- old/libdnf-0.74.0/libdnf/transaction/Transaction.cpp        2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/transaction/Transaction.cpp        2025-10-20 
15:41:49.000000000 +0200
@@ -82,6 +82,7 @@
         "  releasever, "
         "  user_id, "
         "  cmdline, "
+        "  persistence, "
         "  state, "
         "  comment "
         "FROM "
@@ -100,6 +101,7 @@
     releasever = query.get< std::string >("releasever");
     userId = query.get< uint32_t >("user_id");
     cmdline = query.get< std::string >("cmdline");
+    persistence = 
static_cast<TransactionPersistence>(query.get<int>("persistence"));
     state = static_cast< TransactionState >(query.get< int >("state"));
     comment = query.get< std::string >("comment");
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/transaction/Transaction.hpp 
new/libdnf-0.75.0/libdnf/transaction/Transaction.hpp
--- old/libdnf-0.74.0/libdnf/transaction/Transaction.hpp        2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/transaction/Transaction.hpp        2025-10-20 
15:41:49.000000000 +0200
@@ -55,6 +55,8 @@
     const std::string &getReleasever() const noexcept { return releasever; }
     uint32_t getUserId() const noexcept { return userId; }
     const std::string &getCmdline() const noexcept { return cmdline; }
+    TransactionPersistence getPersistence() const noexcept { return 
persistence; }
+
     TransactionState getState() const noexcept { return state; }
     const std::string &getComment() const noexcept { return comment; }
 
@@ -79,6 +81,7 @@
     std::string releasever;
     uint32_t userId = 0;
     std::string cmdline;
+    TransactionPersistence persistence = TransactionPersistence::UNKNOWN;
     TransactionState state = TransactionState::UNKNOWN;
     std::string comment;
 };
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/transaction/Transformer.cpp 
new/libdnf-0.75.0/libdnf/transaction/Transformer.cpp
--- old/libdnf-0.74.0/libdnf/transaction/Transformer.cpp        2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/transaction/Transformer.cpp        2025-10-20 
15:41:49.000000000 +0200
@@ -53,6 +53,10 @@
 #include "sql/migrate_tables_1_2.sql"
     ;
 
+static const char * const sql_migrate_tables_1_3 =
+#include "sql/migrate_tables_1_3.sql"
+    ;
+
 void
 Transformer::createDatabase(SQLite3Ptr conn)
 {
@@ -70,6 +74,9 @@
 
         if (schemaVersion == "1.1") {
             conn->exec(sql_migrate_tables_1_2);
+            conn->exec(sql_migrate_tables_1_3);
+        } else if (schemaVersion == "1.2") {
+            conn->exec(sql_migrate_tables_1_3);
         }
     }
     else {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/transaction/Transformer.hpp 
new/libdnf-0.75.0/libdnf/transaction/Transformer.hpp
--- old/libdnf-0.74.0/libdnf/transaction/Transformer.hpp        2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/transaction/Transformer.hpp        2025-10-20 
15:41:49.000000000 +0200
@@ -60,7 +60,7 @@
     static void migrateSchema(SQLite3Ptr conn);
 
     static TransactionItemReason getReason(const std::string &reason);
-    static const char *getVersion() noexcept { return "1.2"; }
+    static const char *getVersion() noexcept { return "1.3"; }
 
 protected:
     void transformTrans(SQLite3Ptr swdb, SQLite3Ptr history);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/transaction/Types.hpp 
new/libdnf-0.75.0/libdnf/transaction/Types.hpp
--- old/libdnf-0.74.0/libdnf/transaction/Types.hpp      2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/transaction/Types.hpp      2025-10-20 
15:41:49.000000000 +0200
@@ -56,6 +56,12 @@
     REASON_CHANGE = 11 // a package was kept on the system but it's reason has 
changed
 };
 
+enum class TransactionPersistence : int {
+    UNKNOWN = 0,
+    PERSIST = 1,
+    TRANSIENT = 2,
+};
+
 } // namespace libdnf
 /*
 Install
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libdnf-0.74.0/libdnf/transaction/private/Transaction.cpp 
new/libdnf-0.75.0/libdnf/transaction/private/Transaction.cpp
--- old/libdnf-0.74.0/libdnf/transaction/private/Transaction.cpp        
2025-03-06 22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/transaction/private/Transaction.cpp        
2025-10-20 15:41:49.000000000 +0200
@@ -76,12 +76,13 @@
         "    releasever, "
         "    user_id, "
         "    cmdline, "
+        "    persistence, "
         "    state, "
         "    comment, "
         "    id "
         "  ) "
         "VALUES "
-        "  (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+        "  (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
     SQLite3::Statement query(*conn.get(), sql);
     query.bindv(getDtBegin(),
                 getDtEnd(),
@@ -90,10 +91,11 @@
                 getReleasever(),
                 getUserId(),
                 getCmdline(),
+                static_cast<int>(getPersistence()),
                 static_cast< int >(getState()),
                 getComment());
     if (getId() > 0) {
-        query.bind(9, getId());
+        query.bind(10, getId());
     }
     query.step();
     setId(conn->lastInsertRowID());
@@ -138,6 +140,7 @@
         "  releasever=?, "
         "  user_id=?, "
         "  cmdline=?, "
+        "  persistence=?, "
         "  state=?, "
         "  comment=? "
         "WHERE "
@@ -150,6 +153,7 @@
                 getReleasever(),
                 getUserId(),
                 getCmdline(),
+                static_cast<int>(getPersistence()),
                 static_cast< int >(getState()),
                 getComment(),
                 getId());
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libdnf-0.74.0/libdnf/transaction/private/Transaction.hpp 
new/libdnf-0.75.0/libdnf/transaction/private/Transaction.hpp
--- old/libdnf-0.74.0/libdnf/transaction/private/Transaction.hpp        
2025-03-06 22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/transaction/private/Transaction.hpp        
2025-10-20 15:41:49.000000000 +0200
@@ -42,6 +42,7 @@
     void setReleasever(const std::string &value) { releasever = value; }
     void setUserId(uint32_t value) { userId = value; }
     void setCmdline(const std::string &value) { cmdline = value; }
+    void setPersistence(TransactionPersistence value) { persistence = value; }
     void setState(TransactionState value) { state = value; }
     void setComment(const std::string &value) { comment = value; }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libdnf-0.74.0/libdnf/transaction/sql/migrate_tables_1_3.sql 
new/libdnf-0.75.0/libdnf/transaction/sql/migrate_tables_1_3.sql
--- old/libdnf-0.74.0/libdnf/transaction/sql/migrate_tables_1_3.sql     
1970-01-01 01:00:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/transaction/sql/migrate_tables_1_3.sql     
2025-10-20 15:41:49.000000000 +0200
@@ -0,0 +1,9 @@
+R"**(
+BEGIN TRANSACTION;
+    ALTER TABLE trans
+        ADD persistence INTEGER DEFAULT 0;
+    UPDATE config
+        SET value = '1.3'
+        WHERE key = 'version';
+COMMIT;
+)**"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/utils/iniparser/iniparser.cpp 
new/libdnf-0.75.0/libdnf/utils/iniparser/iniparser.cpp
--- old/libdnf-0.74.0/libdnf/utils/iniparser/iniparser.cpp      2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/utils/iniparser/iniparser.cpp      2025-10-20 
15:41:49.000000000 +0200
@@ -70,6 +70,35 @@
     return "IniParser: Missing '='";
 }
 
+
+namespace {
+
+// Returns the position of the first ']' character that does not define a 
list/range.
+std::size_t findEndOfSectionName(const std::string & str, std::size_t pos) {
+    if (pos >= str.size()) {
+        return std::string::npos;
+    }
+
+    bool range = false;
+    for (std::size_t idx = pos;; ++idx) {
+        const auto ch = str[idx];
+        if (ch == ']') {
+            if (range) {
+                range = false;
+            } else {
+                return idx;
+            }
+        } else if (ch == '[') {
+            range = true;
+        } else if (ch == '\0' || ch == '\n' || ch == '\r') {
+            return std::string::npos;
+        }
+    }
+}
+
+}  // namespace
+
+
 IniParser::IniParser(const std::string & filePath)
 : is(new std::ifstream(filePath))
 {
@@ -161,7 +190,7 @@
         }
 
         if (line[start] == '[') {
-            auto endSectPos = line.find("]", ++start);
+            auto endSectPos = findEndOfSectionName(line, ++start);
             if (endSectPos == line.npos)
                 throw MissingBracket(lineNumber);
             else if (endSectPos == start)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/utils/smartcols/Table.cpp 
new/libdnf-0.75.0/libdnf/utils/smartcols/Table.cpp
--- old/libdnf-0.74.0/libdnf/utils/smartcols/Table.cpp  2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/utils/smartcols/Table.cpp  2025-10-20 
15:41:49.000000000 +0200
@@ -99,24 +99,12 @@
     lines.push_back(line);
 }
 
-void Table::removeColumn(const std::shared_ptr<Column> &column)
-{
-    std::remove(std::begin(columns), std::end(columns), column);
-    scols_table_remove_column(table, column->column);
-}
-
 void Table::removeColumns()
 {
     columns.clear();
     scols_table_remove_columns(table);
 }
 
-void Table::removeLine(const std::shared_ptr<Line> &line)
-{
-    std::remove(std::begin(lines), std::end(lines), line);
-    scols_table_remove_line(table, line->line);
-}
-
 void Table::removeLines()
 {
     lines.clear();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/utils/smartcols/Table.hpp 
new/libdnf-0.75.0/libdnf/utils/smartcols/Table.hpp
--- old/libdnf-0.74.0/libdnf/utils/smartcols/Table.hpp  2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf/utils/smartcols/Table.hpp  2025-10-20 
15:41:49.000000000 +0200
@@ -92,14 +92,12 @@
     void enableNolinesep(bool enable) { scols_table_enable_nolinesep(table, 
enable); }
 
     void addColumn(const std::shared_ptr<Column> &column);
-    void removeColumn(const std::shared_ptr<Column> &column);
     void removeColumns();
     void moveColumn(const std::shared_ptr<Column> &before, const 
std::shared_ptr<Column> &toMove);
     std::shared_ptr<Column> newColumn(const std::string &name, double 
widthHint = 0, int flags = 0);
     std::shared_ptr<Column> 
nextColumn(std::vector<std::shared_ptr<Column>>::iterator &iterator) { return 
*(iterator++); }
 
     void addLine(const std::shared_ptr<Line> &line);
-    void removeLine(const std::shared_ptr<Line> &line);
     void removeLines();
     std::shared_ptr<Line> newLine();
     std::shared_ptr<Line> newLine(const std::shared_ptr<Line> &parent);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/utils/utils.cpp 
new/libdnf-0.75.0/libdnf/utils/utils.cpp
--- old/libdnf-0.74.0/libdnf/utils/utils.cpp    2025-03-06 22:45:00.000000000 
+0100
+++ new/libdnf-0.75.0/libdnf/utils/utils.cpp    2025-10-20 15:41:49.000000000 
+0200
@@ -9,6 +9,7 @@
 #include <sys/stat.h>
 #include <dirent.h>
 #include <cstring>
+#include <glob.h>
 #include <stdexcept>
 
 extern "C" {
@@ -18,7 +19,9 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <stdlib.h>
 #include <string.h>
+#include <memory>
 #include <random>
 
 namespace libdnf {
@@ -233,8 +236,41 @@
     return S_ISDIR(buf.st_mode);
 }
 
+std::string getRealpath(const std::string & path)
+{
+    char * resolved = realpath(path.c_str(), nullptr);
+    if (!resolved) {
+        //throw std::runtime_error("realpath error for \"" + path + "\": " + 
strerror(errno));
+        return {};
+    }
+    std::unique_ptr<char, decltype(&::free)> resolved_owner(resolved, &::free);
+    return resolved;
+}
+
+bool isSubdirectory(const std::string & parent, const std::string & child)
+{
+    std::string parent_path = getRealpath(parent);
+    std::string child_path = getRealpath(child);
+
+    if (parent_path.empty() || child_path.empty()) {
+        return false;
+    }
+
+    if (parent_path.back() != '/')
+        parent_path += '/';
+
+    return child_path.compare(0, parent_path.size(), parent_path) == 0;
+}
+
+std::string pathJoin(const std::string & p1, const std::string & p2)
+{
+    auto ret = p1;
+    if (ret.back() != '/')
+        ret.push_back('/');
+    return ret + p2;
+}
 
-std::vector<std::string> getDirContent(const std::string &dirPath)
+std::vector<std::string> getDirContent(const std::string & dirPath)
 {
     std::vector<std::string> content{};
     struct dirent *ent;
@@ -242,16 +278,12 @@
 
     if (dir != nullptr) {
         while ((ent = readdir(dir)) != nullptr) {
-            if (strcmp(ent->d_name, "..") == 0 ||
-                    strcmp(ent->d_name, ".") == 0 )
+            const auto * file_name = ent->d_name;
+            if (strcmp(file_name, "..") == 0 ||
+                    strcmp(file_name, ".") == 0 )
                 continue;
 
-            auto fullPath = dirPath;
-            if (!string::endsWith(fullPath, "/"))
-                fullPath += "/";
-            fullPath += ent->d_name;
-
-            content.emplace_back(fullPath);
+            content.emplace_back(pathJoin(dirPath, file_name));
         }
         closedir (dir);
     }
@@ -259,6 +291,46 @@
     return content;
 }
 
+std::vector<std::string> createSortedFileList(
+    const std::vector<std::string> & directories, const std::string & 
file_name_pattern) {
+    std::vector<std::string> paths;
+
+    for (const auto & dir : directories) {
+        const auto pattern = pathJoin(dir, file_name_pattern);
+        glob_t globResult;
+        auto ret = glob(pattern.c_str(), GLOB_MARK | GLOB_NOSORT, NULL, 
&globResult );
+        if (ret != 0 && ret != GLOB_NOMATCH) {
+            globfree(&globResult);
+            continue;
+        }
+        for (size_t i = 0; i < globResult.gl_pathc; ++i) {
+            auto path = globResult.gl_pathv[i];
+            if (path[strlen(path)-1] == '/') {
+                continue;
+            }
+            auto * path_fname = basename(path);
+            bool found{false};
+            for (const auto & path_in_list : paths) {
+                if (strcmp(path_fname, basename(path_in_list.c_str())) == 0) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                paths.push_back(path);
+            }
+        }
+        globfree(&globResult);
+    }
+
+    // sort all drop-in configuration files alphabetically by their names
+    std::sort(paths.begin(), paths.end(), [](const std::string & p1, const 
std::string & p2) {
+        return strcmp(basename(p1.c_str()), basename(p2.c_str())) < 0;
+    });
+
+    return paths;
+}
+
 void decompress(const char * inPath, const char * outPath, mode_t outMode, 
const char * compressType)
 {
     auto inFd = open(inPath, O_RDONLY);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf/utils/utils.hpp 
new/libdnf-0.75.0/libdnf/utils/utils.hpp
--- old/libdnf-0.74.0/libdnf/utils/utils.hpp    2025-03-06 22:45:00.000000000 
+0100
+++ new/libdnf-0.75.0/libdnf/utils/utils.hpp    2025-10-20 15:41:49.000000000 
+0200
@@ -57,9 +57,20 @@
 namespace filesystem {
 bool exists (const std::string &name);
 bool isDIR(const std::string& dirPath);
+std::string getRealpath(const std::string & path);
+bool isSubdirectory(const std::string & parent, const std::string & child);
+std::string pathJoin(const std::string & p1, const std::string & p2);
 std::vector<std::string> getDirContent(const std::string &dirPath);
 
 /**
+*  Creates an alphabetically sorted list of all files in `directories` which 
names match the `pattern_file_name`.
+*  If a file with the same name is in multiple directories, only the first 
file found is added to the list.
+*  Directories are traversed in the same order as they are in the input vector.
+*/
+std::vector<std::string> createSortedFileList(
+    const std::vector<std::string> & directories, const std::string & 
file_name_pattern);
+
+/**
 * @brief Decompress file.
 *
 * @param inPath Path to input (compressed) file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/libdnf.spec 
new/libdnf-0.75.0/libdnf.spec
--- old/libdnf-0.74.0/libdnf.spec       2025-03-06 22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/libdnf.spec       2025-10-20 15:41:49.000000000 +0200
@@ -4,7 +4,7 @@
 %global dnf_conflict 4.11.0
 %global swig_version 3.0.12
 %global libdnf_major_version 0
-%global libdnf_minor_version 74
+%global libdnf_minor_version 75
 %global libdnf_micro_version 0
 
 %define __cmake_in_source_build 1
@@ -96,6 +96,9 @@
 Requires:       libmodulemd%{?_isa} >= %{libmodulemd_version}
 Requires:       libsolv%{?_isa} >= %{libsolv_version}
 Requires:       librepo%{?_isa} >= %{librepo_version}
+%if 0%{?fedora} >= 43 || 0%{?rhel} >= 11
+Requires:       rpm-libs%{?_isa} >= 5.99.90
+%endif
 
 %if %{without python2}
 # Obsoleted from here so we can track the fast growing version easily.
@@ -204,7 +207,7 @@
   %endif
   %cmake -DPYTHON_DESIRED:FILEPATH=%{__python2} -DWITH_MAN=OFF ../ 
%{!?with_zchunk:-DWITH_ZCHUNK=OFF} %{!?with_valgrind:-DDISABLE_VALGRIND=1} 
%{_cmake_opts} -DLIBDNF_MAJOR_VERSION=%{libdnf_major_version} 
-DLIBDNF_MINOR_VERSION=%{libdnf_minor_version} 
-DLIBDNF_MICRO_VERSION=%{libdnf_micro_version} \
     -DWITH_SANITIZERS=%{?with_sanitizers:ON}%{!?with_sanitizers:OFF}
-  %make_build
+  %cmake_build
 popd
 %endif
 # endif with python2
@@ -218,28 +221,32 @@
   %endif
   %cmake -DPYTHON_DESIRED:FILEPATH=%{__python3} -DWITH_GIR=0 -DWITH_MAN=0 
-Dgtkdoc=0 ../ %{!?with_zchunk:-DWITH_ZCHUNK=OFF} 
%{!?with_valgrind:-DDISABLE_VALGRIND=1} %{_cmake_opts} 
-DLIBDNF_MAJOR_VERSION=%{libdnf_major_version} 
-DLIBDNF_MINOR_VERSION=%{libdnf_minor_version} 
-DLIBDNF_MICRO_VERSION=%{libdnf_micro_version} \
     -DWITH_SANITIZERS=%{?with_sanitizers:ON}%{!?with_sanitizers:OFF}
-  %make_build
+  %cmake_build
 popd
 %endif
 
 %check
+%if 0%{?rhel} == 9 && %{defined ctest}
+# Work around broken passing options to ctest macro, RHEL-120543
+%global ctest(-) %{expand:%{macrobody:ctest}}
+%endif
 %if %{with python2}
 pushd build-py2
-  make ARGS="-V" test
+  %ctest -V
 popd
 %endif
 %if %{with python3}
 # If we didn't run the general tests yet, do it now.
 %if %{without python2}
 pushd build-py3
-  make ARGS="-V" test
+  %ctest -V
 popd
 %else
 # Otherwise, run just the Python tests, not all of
 # them, since we have coverage of the core from the
 # first build
 pushd build-py3/python/hawkey/tests
-  make ARGS="-V" test
+  %ctest -V
 popd
 %endif
 %endif
@@ -247,12 +254,12 @@
 %install
 %if %{with python2}
 pushd build-py2
-  %make_install
+  %cmake_install
 popd
 %endif
 %if %{with python3}
 pushd build-py3
-  %make_install
+  %cmake_install
 popd
 %endif
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/tests/CMakeLists.txt 
new/libdnf-0.75.0/tests/CMakeLists.txt
--- old/libdnf-0.74.0/tests/CMakeLists.txt      2025-03-06 22:45:00.000000000 
+0100
+++ new/libdnf-0.75.0/tests/CMakeLists.txt      2025-10-20 15:41:49.000000000 
+0200
@@ -1,3 +1,6 @@
+pkg_check_modules(CHECK REQUIRED check)
+pkg_check_modules(CPPUNIT REQUIRED cppunit)
+
 add_subdirectory(libdnf/conf)
 add_subdirectory(libdnf/module/modulemd)
 add_subdirectory(libdnf/module)
@@ -7,10 +10,6 @@
 add_subdirectory(hawkey)
 add_subdirectory(libdnf)
 
-
-
-pkg_check_modules(CPPUNIT REQUIRED cppunit)
-
 set(LIBDNF_TEST_SOURCES
     ${LIBDNF_TEST_SOURCES}
     ${CMAKE_CURRENT_SOURCE_DIR}/run_tests.cpp
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/tests/hawkey/test_goal.cpp 
new/libdnf-0.75.0/tests/hawkey/test_goal.cpp
--- old/libdnf-0.74.0/tests/hawkey/test_goal.cpp        2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/tests/hawkey/test_goal.cpp        2025-10-20 
15:41:49.000000000 +0200
@@ -50,8 +50,8 @@
     hy_query_filter(q, HY_PKG_REPONAME, HY_NEQ, HY_SYSTEM_REPO_NAME);
     hy_query_filter_latest_per_arch(q, 1);
     GPtrArray *plist = hy_query_run(q);
-    fail_unless(plist->len == 1,
-                "get_latest_pkg() failed finding '%s'.", name);
+    ck_assert_msg(plist->len == 1,
+                  "get_latest_pkg() failed finding '%s'.", name);
     auto pkg = static_cast<DnfPackage *>(g_object_ref(g_ptr_array_index(plist, 
0)));
     hy_query_free(q);
     g_ptr_array_unref(plist);
@@ -436,7 +436,7 @@
     va_start(names, plist);
     while ((name = va_arg(names, char *)) != NULL) {
         if (i++ >= count) {
-            fail("assert_list_names(): list too short");
+            ck_abort_msg("assert_list_names(): list too short");
         }
         bool found = false;
         for (auto string: stringVector) {
@@ -446,8 +446,8 @@
             }
         }
         if ((wanted && !found) || (!wanted && found)) {
-            fail_unless(false, "assert_list_names(): element '%s' %sfound 
'%zu'",
-                        name, wanted ? "not ": "", stringVector.size());
+            ck_abort_msg("assert_list_names(): element '%s' %sfound '%zu'",
+                         name, wanted ? "not ": "", stringVector.size());
         }
     }
     // In the wanted case; we expect all the pkgs in the lists to fully
@@ -455,7 +455,7 @@
     // all the passed pkg arguments are *not* found in the list, which is
     // already checked above.
     if (wanted) {
-        fail_unless(i == count, "assert_list_names(): too many items in the 
list (%d vs %d)", i, count);
+        ck_assert_msg(i == count, "assert_list_names(): too many items in the 
list (%d vs %d)", i, count);
     }
     va_end(names);
 }
@@ -493,7 +493,7 @@
     g_ptr_array_unref(plist_obs);
     g_ptr_array_unref(plist);
 
-    fail_unless(size_and_free(hy_goal_list_installs(goal, NULL)) == 0);
+    ck_assert(size_and_free(hy_goal_list_installs(goal, NULL)) == 0);
     hy_goal_free(goal);
 }
 END_TEST
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libdnf-0.74.0/tests/hawkey/test_iutil.cpp 
new/libdnf-0.75.0/tests/hawkey/test_iutil.cpp
--- old/libdnf-0.74.0/tests/hawkey/test_iutil.cpp       2025-03-06 
22:45:00.000000000 +0100
+++ new/libdnf-0.75.0/tests/hawkey/test_iutil.cpp       2025-10-20 
15:41:49.000000000 +0200
@@ -122,7 +122,7 @@
     repowriter_free(writer);
     fclose(fp);
 
-    fp = fopen(new_file, "r");
+    fail_if((fp = fopen(new_file, "r")) == NULL);
     std::unique_ptr<SolvUserdata, decltype(solv_free)*> dnf_solvfile = 
solv_userdata_read(fp);
     fail_unless(dnf_solvfile);
     fail_unless(solv_userdata_verify(dnf_solvfile.get(), cs_computed));

Reply via email to