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));
