From 514d52cbdf40d0aaa1c5e18f2bb05ba91ab622d5 Mon Sep 17 00:00:00 2001 From: Marek Blaha Date: Tue, 3 Sep 2024 13:08:34 +0200 Subject: [PATCH 1/2] dnf5: Run transaction test for offline transactions A serialized offline transaction consists only of local packages downloaded to the `/offline` location. Unfortunately, PGP checks for these packages are disabled by default due to the `localpkg_gpgcheck` option default value. Instead of testing the serialized transaction, this patch performs an RPM transaction test on the originally resolved transaction, which still retains information about the repositories from which the packages were downloaded. This approach also saves time required for deserialization and resolving the testing transaction. --- dnf5/context.cpp | 49 +++++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/dnf5/context.cpp b/dnf5/context.cpp index fe9b73deb..1d628bf55 100644 --- a/dnf5/context.cpp +++ b/dnf5/context.cpp @@ -325,6 +325,27 @@ void Context::Impl::load_repos(bool load_system) { } void Context::Impl::store_offline(libdnf5::base::Transaction & transaction) { + // Test the transaction + base.get_config().get_tsflags_option().set(libdnf5::Option::Priority::RUNTIME, "test"); + print_info(_("Testing offline transaction")); + auto result = transaction.run(); + if (result != libdnf5::base::Transaction::TransactionRunResult::SUCCESS) { + print_error(libdnf5::utils::sformat( + _("Transaction failed: {}"), libdnf5::base::Transaction::transaction_result_to_string(result))); + for (auto const & entry : transaction.get_gpg_signature_problems()) { + print_error(entry); + } + for (auto & problem : transaction.get_transaction_problems()) { + print_error(libdnf5::utils::sformat(_(" - {}"), problem)); + } + throw libdnf5::cli::SilentCommandExitError(1); + } + + for (auto const & entry : transaction.get_gpg_signature_problems()) { + print_error(entry); + } + + // Serialize the transaction const auto & installroot = base.get_config().get_installroot_option().get_value(); const auto & offline_datadir = installroot / libdnf5::offline::DEFAULT_DATADIR.relative_path(); std::filesystem::create_directories(offline_datadir); @@ -349,7 +370,6 @@ void Context::Impl::store_offline(libdnf5::base::Transaction & transaction) { offline_data.set_status(libdnf5::offline::STATUS_DOWNLOAD_INCOMPLETE); state.write(); - // First, serialize the transaction transaction.store_comps(comps_location); const auto transaction_json_path = offline_datadir / TRANSACTION_JSON; @@ -357,33 +377,6 @@ void Context::Impl::store_offline(libdnf5::base::Transaction & transaction) { transaction_json_file.write(transaction.serialize(packages_in_trans_dir, comps_in_trans_dir)); transaction_json_file.close(); - // Then, test the serialized transaction - const auto & goal = std::make_unique(base); - goal->add_serialized_transaction(transaction_json_path); - auto test_transaction = goal->resolve(); - if (test_transaction.get_problems() != libdnf5::GoalProblem::NO_PROBLEM) { - throw libdnf5::cli::GoalResolveError(transaction); - } - base.get_config().get_tsflags_option().set(libdnf5::Option::Priority::RUNTIME, "test"); - - print_info(_("Testing offline transaction")); - auto result = test_transaction.run(); - if (result != libdnf5::base::Transaction::TransactionRunResult::SUCCESS) { - print_error(libdnf5::utils::sformat( - _("Transaction failed: {}"), libdnf5::base::Transaction::transaction_result_to_string(result))); - for (auto const & entry : transaction.get_gpg_signature_problems()) { - print_error(entry); - } - for (auto & problem : test_transaction.get_transaction_problems()) { - print_error(libdnf5::utils::sformat(_(" - {}"), problem)); - } - throw libdnf5::cli::SilentCommandExitError(1); - } - - for (auto const & entry : test_transaction.get_gpg_signature_problems()) { - print_error(entry); - } - // Download and transaction test complete. Fill out entries in offline // transaction state file. offline_data.set_status(libdnf5::offline::STATUS_DOWNLOAD_COMPLETE); From c171eb6a7e1bc5672d0eb1926a67279884a09a54 Mon Sep 17 00:00:00 2001 From: Marek Blaha Date: Thu, 5 Sep 2024 10:24:41 +0200 Subject: [PATCH 2/2] dnf5: Check offline transaction state before download The transaction package download can be a lengthy and network intensive operation. Begin the download only after the user confirms that the previously scheduled offline transaction can be canceled. --- dnf5/context.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/dnf5/context.cpp b/dnf5/context.cpp index 1d628bf55..0cedb106e 100644 --- a/dnf5/context.cpp +++ b/dnf5/context.cpp @@ -349,7 +349,6 @@ void Context::Impl::store_offline(libdnf5::base::Transaction & transaction) { const auto & installroot = base.get_config().get_installroot_option().get_value(); const auto & offline_datadir = installroot / libdnf5::offline::DEFAULT_DATADIR.relative_path(); std::filesystem::create_directories(offline_datadir); - constexpr const char * packages_in_trans_dir{"./packages"}; constexpr const char * comps_in_trans_dir{"./comps"}; const auto & comps_location = offline_datadir / comps_in_trans_dir; @@ -358,15 +357,6 @@ void Context::Impl::store_offline(libdnf5::base::Transaction & transaction) { libdnf5::offline::OfflineTransactionState state{state_path}; auto & offline_data = state.get_data(); - if (offline_data.get_status() != libdnf5::offline::STATUS_DOWNLOAD_INCOMPLETE) { - print_error(_("There is already an offline transaction queued, initiated by the following command:")); - print_error(fmt::format("\t{}", offline_data.get_cmd_line())); - print_error(_("Continuing will cancel the old offline transaction and replace it with this one.")); - if (!libdnf5::cli::utils::userconfirm::userconfirm(base.get_config())) { - throw libdnf5::cli::AbortedByUserError(); - } - } - offline_data.set_status(libdnf5::offline::STATUS_DOWNLOAD_INCOMPLETE); state.write(); @@ -436,6 +426,19 @@ void Context::Impl::download_and_run(libdnf5::base::Transaction & transaction) { const auto & installroot = base.get_config().get_installroot_option().get_value(); const auto & offline_datadir = installroot / libdnf5::offline::DEFAULT_DATADIR.relative_path(); std::filesystem::create_directories(offline_datadir); + const std::filesystem::path state_path{offline_datadir / libdnf5::offline::TRANSACTION_STATE_FILENAME}; + libdnf5::offline::OfflineTransactionState state{state_path}; + + // Check whether there is another pending offline transaction present + auto & offline_data = state.get_data(); + if (offline_data.get_status() != libdnf5::offline::STATUS_DOWNLOAD_INCOMPLETE) { + print_error(_("There is already an offline transaction queued, initiated by the following command:")); + print_error(fmt::format("\t{}", offline_data.get_cmd_line())); + print_error(_("Continuing will cancel the old offline transaction and replace it with this one.")); + if (!libdnf5::cli::utils::userconfirm::userconfirm(base.get_config())) { + throw libdnf5::cli::AbortedByUserError(); + } + } base.get_config().get_destdir_option().set(offline_datadir / "packages"); transaction.set_download_local_pkgs(true);