diff --git a/dnf5.spec b/dnf5.spec
index 560d7c102..2794b4670 100644
--- a/dnf5.spec
+++ b/dnf5.spec
@@ -58,6 +58,7 @@ Provides: dnf5-command(module)
Provides: dnf5-command(offline)
Provides: dnf5-command(provides)
Provides: dnf5-command(reinstall)
+Provides: dnf5-command(replay)
Provides: dnf5-command(remove)
Provides: dnf5-command(repo)
Provides: dnf5-command(repoquery)
@@ -308,6 +309,7 @@ It supports RPM packages, modulemd modules, and comps groups & environments.
%{_mandir}/man8/dnf*-provides.8.*
%{_mandir}/man8/dnf*-reinstall.8.*
%{_mandir}/man8/dnf*-remove.8.*
+%{_mandir}/man8/dnf*-replay.8.*
%{_mandir}/man8/dnf*-repo.8.*
%{_mandir}/man8/dnf*-repoquery.8.*
%{_mandir}/man8/dnf*-search.8.*
diff --git a/dnf5/commands/history/history.cpp b/dnf5/commands/history/history.cpp
index ba4612bf6..693485366 100644
--- a/dnf5/commands/history/history.cpp
+++ b/dnf5/commands/history/history.cpp
@@ -22,7 +22,6 @@ along with libdnf. If not, see .
#include "history_info.hpp"
#include "history_list.hpp"
#include "history_redo.hpp"
-#include "history_replay.hpp"
#include "history_rollback.hpp"
#include "history_store.hpp"
#include "history_undo.hpp"
@@ -61,7 +60,6 @@ void HistoryCommand::register_subcommands() {
// register_subcommand(std::make_unique(get_context()), software_management_commands_group);
// register_subcommand(std::make_unique(get_context()), software_management_commands_group);
register_subcommand(std::make_unique(get_context()), software_management_commands_group);
- // register_subcommand(std::make_unique(get_context()), software_management_commands_group);
}
void HistoryCommand::pre_configure() {
diff --git a/dnf5/commands/history/history_replay.cpp b/dnf5/commands/history/history_replay.cpp
deleted file mode 100644
index 41e0fd2bd..000000000
--- a/dnf5/commands/history/history_replay.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
-Copyright Contributors to the libdnf project.
-
-This file is part of libdnf: https://github.com/rpm-software-management/libdnf/
-
-Libdnf is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-Libdnf is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with libdnf. If not, see .
-*/
-
-#include "history_replay.hpp"
-
-namespace dnf5 {
-
-void HistoryReplayCommand::set_argument_parser() {
- get_argument_parser_command()->set_description("Replay a transaction that was previously stored to a file");
-}
-
-void HistoryReplayCommand::run() {}
-
-} // namespace dnf5
diff --git a/dnf5/commands/history/history_store.cpp b/dnf5/commands/history/history_store.cpp
index 9f3067720..04d0b7b36 100644
--- a/dnf5/commands/history/history_store.cpp
+++ b/dnf5/commands/history/history_store.cpp
@@ -36,11 +36,11 @@ void HistoryStoreCommand::set_argument_parser() {
auto & parser = ctx.get_argument_parser();
output_option = dynamic_cast(
- parser.add_init_value(std::make_unique("./transaction.json")));
+ parser.add_init_value(std::make_unique("./transaction")));
auto query_format = parser.add_new_named_arg("output");
query_format->set_long_name("output");
query_format->set_short_name('o');
- query_format->set_description("File path for storing the transaction, default is \"./transaction.json\"");
+ query_format->set_description("Path to a directory for storing the transaction, default is \"./transaction\"");
query_format->set_has_value(true);
query_format->set_arg_value_help("PATH");
query_format->link_value(output_option);
@@ -55,11 +55,14 @@ void HistoryStoreCommand::run() {
std::vector transactions;
auto logger = get_context().get_base().get_logger();
- std::filesystem::path tmp_path(output_option->get_value());
+ std::filesystem::create_directories(output_option->get_value());
- if (std::filesystem::exists(tmp_path)) {
+ std::filesystem::path trans_file_path(output_option->get_value());
+ trans_file_path /= TRANSACTION_JSON;
+
+ if (std::filesystem::exists(trans_file_path)) {
std::cout << libdnf5::utils::sformat(
- _("File \"{}\" already exists, it will be overwritten.\n"), tmp_path.string());
+ _("File \"{}\" already exists, it will be overwritten.\n"), trans_file_path.string());
// ask user for the file overwrite confirmation
if (!libdnf5::cli::utils::userconfirm::userconfirm(get_context().get_base().get_config())) {
throw libdnf5::cli::AbortedByUserError();
@@ -81,12 +84,12 @@ void HistoryStoreCommand::run() {
const std::string json = transactions[0].serialize();
- auto tmp_file = libdnf5::utils::fs::TempFile(tmp_path.parent_path(), tmp_path.filename());
+ auto tmp_file = libdnf5::utils::fs::TempFile(trans_file_path.parent_path(), trans_file_path.filename());
auto & file = tmp_file.open_as_file("w+");
file.write(json);
file.close();
- std::filesystem::rename(tmp_file.get_path(), output_option->get_value());
+ std::filesystem::rename(tmp_file.get_path(), trans_file_path);
tmp_file.release();
}
diff --git a/dnf5/commands/offline/offline.cpp b/dnf5/commands/offline/offline.cpp
index 31c3bdef3..52f47e8cc 100644
--- a/dnf5/commands/offline/offline.cpp
+++ b/dnf5/commands/offline/offline.cpp
@@ -449,7 +449,7 @@ void OfflineExecuteCommand::run() {
const auto & installroot = ctx.get_base().get_config().get_installroot_option().get_value();
const auto & datadir = installroot / libdnf5::offline::DEFAULT_DATADIR.relative_path();
std::filesystem::create_directories(datadir);
- const auto & transaction_json_path = datadir / "transaction.json";
+ const auto & transaction_json_path = datadir / TRANSACTION_JSON;
const auto & goal = std::make_unique(ctx.get_base());
diff --git a/dnf5/commands/replay/replay.cpp b/dnf5/commands/replay/replay.cpp
new file mode 100644
index 000000000..8b71984c3
--- /dev/null
+++ b/dnf5/commands/replay/replay.cpp
@@ -0,0 +1,77 @@
+/*
+Copyright Contributors to the libdnf project.
+
+This file is part of libdnf: https://github.com/rpm-software-management/libdnf/
+
+Libdnf is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+Libdnf is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with libdnf. If not, see .
+*/
+
+#include "replay.hpp"
+
+#include "commands/history/arguments.hpp"
+
+#include
+#include
+
+namespace dnf5 {
+
+void ReplayCommand::set_parent_command() {
+ auto * arg_parser_parent_cmd = get_session().get_argument_parser().get_root_command();
+ auto * arg_parser_this_cmd = get_argument_parser_command();
+ arg_parser_parent_cmd->register_command(arg_parser_this_cmd);
+ arg_parser_parent_cmd->get_group("software_management_commands").register_argument(arg_parser_this_cmd);
+}
+
+void ReplayCommand::set_argument_parser() {
+ auto & cmd = *get_argument_parser_command();
+ cmd.set_description("Replay a transaction that was previously stored to a directory");
+ auto & ctx = get_context();
+ auto & parser = ctx.get_argument_parser();
+
+ auto * transaction_path_arg = parser.add_new_positional_arg("transaction-path", 1, nullptr, nullptr);
+ transaction_path_arg->set_description(
+ "Path to a directory with stored transaction. Only a single path with one transaction is supported.");
+ transaction_path_arg->set_parse_hook_func([this](
+ [[maybe_unused]] libdnf5::cli::ArgumentParser::PositionalArg * arg,
+ [[maybe_unused]] int argc,
+ const char * const argv[]) {
+ transaction_path = argv[0];
+ return true;
+ });
+ cmd.register_positional_arg(transaction_path_arg);
+
+ auto skip_broken = std::make_unique(*this);
+ std::make_unique(*this);
+ ignore_extras = std::make_unique(*this);
+ ignore_installed = std::make_unique(*this);
+}
+
+void ReplayCommand::configure() {
+ auto & context = get_context();
+ context.set_load_system_repo(true);
+ context.set_load_available_repos(Context::LoadAvailableRepos::ENABLED);
+}
+
+void ReplayCommand::run() {
+ auto & context = get_context();
+ auto settings = libdnf5::GoalJobSettings();
+
+ settings.set_ignore_extras(ignore_extras->get_value());
+ settings.set_ignore_installed(ignore_installed->get_value());
+
+ context.get_goal()->add_serialized_transaction(
+ std::filesystem::path(transaction_path) / TRANSACTION_JSON, settings);
+}
+
+} // namespace dnf5
diff --git a/dnf5/commands/history/history_replay.hpp b/dnf5/commands/replay/replay.hpp
similarity index 61%
rename from dnf5/commands/history/history_replay.hpp
rename to dnf5/commands/replay/replay.hpp
index 9f379d930..3e5bbf2bf 100644
--- a/dnf5/commands/history/history_replay.hpp
+++ b/dnf5/commands/replay/replay.hpp
@@ -18,24 +18,32 @@ along with libdnf. If not, see .
*/
-#ifndef DNF5_COMMANDS_HISTORY_HISTORY_REPLAY_HPP
-#define DNF5_COMMANDS_HISTORY_HISTORY_REPLAY_HPP
+#ifndef DNF5_COMMANDS_REPLAY__REPLAY_HPP
+#define DNF5_COMMANDS_REPLAY__REPLAY_HPP
#include
-
+#include
namespace dnf5 {
-class HistoryReplayCommand : public Command {
+class ReplayCommand : public Command {
public:
- explicit HistoryReplayCommand(Context & context) : Command(context, "replay") {}
+ explicit ReplayCommand(Context & context) : Command(context, "replay") {}
+ void set_parent_command() override;
+ void configure() override;
void set_argument_parser() override;
void run() override;
+
+private:
+ std::string transaction_path;
+
+ std::unique_ptr ignore_extras{nullptr};
+ std::unique_ptr ignore_installed{nullptr};
};
} // namespace dnf5
-#endif // DNF5_COMMANDS_HISTORY_HISTORY_REPLAY_HPP
+#endif // DNF5_COMMANDS_REPLAY__REPLAY_HPP
diff --git a/dnf5/context.cpp b/dnf5/context.cpp
index 83e210ce8..94a71dca7 100644
--- a/dnf5/context.cpp
+++ b/dnf5/context.cpp
@@ -47,7 +47,6 @@ along with libdnf. If not, see .
#include
#include
#include
-#include
#include
#include
#include
@@ -344,7 +343,7 @@ void Context::Impl::store_offline(libdnf5::base::Transaction & transaction) {
// First, serialize the transaction
transaction.store_comps(comps_location);
- const auto transaction_json_path = offline_datadir / "transaction.json";
+ const auto transaction_json_path = offline_datadir / TRANSACTION_JSON;
libdnf5::utils::fs::File transaction_json_file{transaction_json_path, "w"};
transaction_json_file.write(transaction.serialize(packages_in_trans_dir, comps_in_trans_dir));
transaction_json_file.close();
@@ -405,7 +404,7 @@ void Context::Impl::store_offline(libdnf5::base::Transaction & transaction) {
void Context::Impl::download_and_run(libdnf5::base::Transaction & transaction) {
if (!transaction_store_path.empty()) {
- auto transaction_location = transaction_store_path / "transaction.json";
+ auto transaction_location = transaction_store_path / TRANSACTION_JSON;
constexpr const char * packages_in_trans_dir{"./packages"};
auto packages_location = transaction_store_path / packages_in_trans_dir;
constexpr const char * comps_in_trans_dir{"./comps"};
diff --git a/dnf5/include/dnf5/context.hpp b/dnf5/include/dnf5/context.hpp
index ab9148d97..505fa8401 100644
--- a/dnf5/include/dnf5/context.hpp
+++ b/dnf5/include/dnf5/context.hpp
@@ -32,10 +32,8 @@ along with libdnf. If not, see .
#include
#include
-#include
#include
#include
-#include
#include
#include
#include
@@ -43,6 +41,8 @@ along with libdnf. If not, see .
namespace dnf5 {
+constexpr const char * TRANSACTION_JSON = "transaction.json";
+
class Plugins;
class DNF_API Context : public libdnf5::cli::session::Session {
diff --git a/dnf5/main.cpp b/dnf5/main.cpp
index cc1df1cff..8c26b13a9 100644
--- a/dnf5/main.cpp
+++ b/dnf5/main.cpp
@@ -40,6 +40,7 @@ along with libdnf. If not, see .
#include "commands/provides/provides.hpp"
#include "commands/reinstall/reinstall.hpp"
#include "commands/remove/remove.hpp"
+#include "commands/replay/replay.hpp"
#include "commands/repo/repo.hpp"
#include "commands/repoquery/repoquery.hpp"
#include "commands/search/search.hpp"
@@ -674,6 +675,7 @@ static void add_commands(Context & context) {
context.add_and_initialize_command(std::make_unique(context));
context.add_and_initialize_command(std::make_unique(context));
context.add_and_initialize_command(std::make_unique(context));
+ context.add_and_initialize_command(std::make_unique(context));
context.add_and_initialize_command(std::make_unique(context));
context.add_and_initialize_command(std::make_unique(context));
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
index ebb15e4f7..27052df06 100644
--- a/doc/CMakeLists.txt
+++ b/doc/CMakeLists.txt
@@ -78,6 +78,7 @@ if(WITH_MAN)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/man/dnf5-provides.8 DESTINATION share/man/man8)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/man/dnf5-reinstall.8 DESTINATION share/man/man8)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/man/dnf5-remove.8 DESTINATION share/man/man8)
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/man/dnf5-replay.8 DESTINATION share/man/man8)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/man/dnf5-repo.8 DESTINATION share/man/man8)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/man/dnf5-repoquery.8 DESTINATION share/man/man8)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/man/dnf5-search.8 DESTINATION share/man/man8)
diff --git a/doc/changes.rst b/doc/changes.rst
index ce26ea2e5..0de9dcd62 100644
--- a/doc/changes.rst
+++ b/doc/changes.rst
@@ -203,6 +203,10 @@ Changes to individual commands
``history``
* ``undo`` subcommand now accepts ``--ignore-extras`` and ``--ignore-installed`` like original ``history replay`` command.
+ * ``store`` subcommand now creates a directory with transaction JSON file instead of a single transaction JSON file directly.
+ * ``store`` subcommand's ``--output`` option now accepts a directory path instead of a file. The default is ``./transaction``.
+ * ``replay`` subcommand was moved to a standalone ``replay`` command, that now accepts a path to a directory instead of a file path.
+ The directory can be created with ``--store`` option and in addition to the JSON transaction, it can contain packages, group and environments used in the transaction.
``info``
* Dropped ``--all`` option since this behavior is the default one.
diff --git a/doc/commands/history.8.rst b/doc/commands/history.8.rst
index c7a0bb9ba..9120b0fb9 100644
--- a/doc/commands/history.8.rst
+++ b/doc/commands/history.8.rst
@@ -59,10 +59,7 @@ Subcommands
| Undo all transactions performed after the specified transaction.
``store``
- | Store the transaction into the file.
-
-``replay``
- | Replay the transaction that was previously stored into the file.
+ | Store the transaction into a directory.
Options for ``list`` and ``info``
diff --git a/doc/commands/index.rst b/doc/commands/index.rst
index a08dd737b..18154c957 100644
--- a/doc/commands/index.rst
+++ b/doc/commands/index.rst
@@ -27,6 +27,7 @@ DNF5 Commands
provides.8
reinstall.8
remove.8
+ replay.8
repo.8
repoquery.8
search.8
diff --git a/doc/commands/replay.8.rst b/doc/commands/replay.8.rst
new file mode 100644
index 000000000..d2d7719c2
--- /dev/null
+++ b/doc/commands/replay.8.rst
@@ -0,0 +1,72 @@
+..
+ Copyright Contributors to the libdnf project.
+
+ This file is part of libdnf: https://github.com/rpm-software-management/libdnf/
+
+ Libdnf is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ Libdnf is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with libdnf. If not, see .
+
+.. _replay_command_ref-label:
+
+###############
+ Replay Command
+###############
+
+Synopsis
+========
+
+``dnf5 replay [options] ``
+
+
+Description
+===========
+
+Replay a transaction stored in a directory at ````. The transaction directory can be created either by
+the ``--store`` option, available for all transaction commands, or by `History Store Command`. The replay will perform
+the exact same operations on the packages as in the original transaction and will return with an error in case of any
+differences in installed packages or their versions.
+
+To run the replay the transaction directory has to contain a file with the transaction in JSON format named ``transaction.json``.
+The directory can also contain packages, comps groups or comps environments that will be used used in the replayed transaction.
+
+
+Options
+=======
+
+``--ignore-extras``
+ | Don't consider extra packages pulled into the transaction as errors.
+ | They will still be reported as warnings.
+
+``--ignore-installed``
+ | Don't consider mismatches between installed and stored transaction packages as errors.
+ | They will still be reported as warnings.
+ | For install actions skip already installed packages.
+ | For upgrade actions skip groups or environments that are not installed.
+ | For remove actions skip not installed packages/groups/environments.
+ | Using this option can result in an empty transaction.
+
+``--skip-broken``
+ | Resolve any dependency problems by removing packages that are causing problems from the transaction.
+
+``--skip-unavailable``
+ | In case some packages stored in the transaction are not available on the target system,
+ | skip them instead of erroring out.
+
+Examples
+========
+
+``dnf5 replay ./transaction``
+ | Replay a transaction stored at ./transaction.
+
+``dnf5 replay ./transaction --skip-unavailable``
+ | Replay a transaction stored at ./transaction skipping unavailable packages.
diff --git a/doc/conf.py.in b/doc/conf.py.in
index 8893b0838..f2d86d80b 100644
--- a/doc/conf.py.in
+++ b/doc/conf.py.in
@@ -134,6 +134,7 @@ man_pages = [
('commands/provides.8', 'dnf5-provides', 'Provides Command', AUTHORS, 8),
('commands/reinstall.8', 'dnf5-reinstall', 'Reinstall Command', AUTHORS, 8),
('commands/remove.8', 'dnf5-remove', 'Remove Command', AUTHORS, 8),
+ ('commands/replay.8', 'dnf5-replay', 'Replay Command', AUTHORS, 8),
('commands/repo.8', 'dnf5-repo', 'Repo Command', AUTHORS, 8),
('commands/repoquery.8', 'dnf5-repoquery', 'Repoquery Command', AUTHORS, 8),
('commands/search.8', 'dnf5-search', 'Search Command', AUTHORS, 8),
diff --git a/doc/dnf5.8.rst b/doc/dnf5.8.rst
index 1c3f2a002..d49f38ecf 100644
--- a/doc/dnf5.8.rst
+++ b/doc/dnf5.8.rst
@@ -112,6 +112,9 @@ For more details see the separate man page for the specific command, f.e. ``man
:ref:`remove `
| Remove packages.
+:ref:`replay `
+ | Replay stored transactions.
+
:ref:`repo `
| Manage repositories.
diff --git a/include/libdnf5/base/goal_elements.hpp b/include/libdnf5/base/goal_elements.hpp
index 54d17cf8f..a38a5802c 100644
--- a/include/libdnf5/base/goal_elements.hpp
+++ b/include/libdnf5/base/goal_elements.hpp
@@ -121,7 +121,8 @@ enum class GoalProblem : uint32_t {
MODULE_CANNOT_SWITH_STREAMS = (1 << 21),
/// Error when transaction contains additional unexpected elements.
/// Used when replaying transactions.
- EXTRA = (1 << 22)
+ EXTRA = (1 << 22),
+ MALFORMED = (1 << 23)
};
/// Types of Goal actions
@@ -144,6 +145,7 @@ enum class GoalAction {
ENABLE,
DISABLE,
RESET,
+ REPLAY_PARSE,
REPLAY_INSTALL,
REPLAY_REMOVE,
REPLAY_UPGRADE,
diff --git a/libdnf5/base/goal.cpp b/libdnf5/base/goal.cpp
index 342207fc6..6415d0bdd 100644
--- a/libdnf5/base/goal.cpp
+++ b/libdnf5/base/goal.cpp
@@ -573,6 +573,9 @@ GoalProblem Goal::Impl::add_specs_to_goal(base::Transaction & transaction) {
case GoalAction::RESET: {
libdnf_throw_assertion("Unsupported action \"RESET\"");
}
+ case GoalAction::REPLAY_PARSE: {
+ libdnf_throw_assertion("Unsupported action \"REPLAY PARSE\"");
+ }
case GoalAction::REPLAY_INSTALL: {
libdnf_throw_assertion("Unsupported action \"REPLAY INSTALL\"");
}
@@ -661,10 +664,22 @@ GoalProblem Goal::Impl::add_serialized_transaction_to_goal(base::Transaction & t
auto & [replay_path, settings] = *serialized_transaction;
utils::fs::File replay_file(replay_path, "r");
- auto replay_location = replay_path.remove_filename();
- auto replay = transaction::parse_transaction_replay(replay_file.read());
-
- return add_replay_to_goal(transaction, replay, settings, replay_location);
+ auto replay_location = replay_path;
+ replay_location.remove_filename();
+ try {
+ auto replay = transaction::parse_transaction_replay(replay_file.read());
+ return add_replay_to_goal(transaction, replay, settings, replay_location);
+ } catch (const libdnf5::transaction::TransactionReplayError & ex) {
+ transaction.p_impl->add_resolve_log(
+ GoalAction::REPLAY_PARSE,
+ libdnf5::GoalProblem::MALFORMED,
+ settings,
+ libdnf5::transaction::TransactionItemType::PACKAGE,
+ replay_path,
+ {ex.what()},
+ libdnf5::Logger::Level::ERROR);
+ return libdnf5::GoalProblem::MALFORMED;
+ }
}
static std::set query_to_vec_of_nevra_str(const libdnf5::rpm::PackageQuery & query) {
diff --git a/libdnf5/base/goal_elements.cpp b/libdnf5/base/goal_elements.cpp
index dcc00b99d..46455801a 100644
--- a/libdnf5/base/goal_elements.cpp
+++ b/libdnf5/base/goal_elements.cpp
@@ -380,6 +380,8 @@ std::string goal_action_to_string(GoalAction action) {
return _("Disable");
case GoalAction::RESET:
return _("Reset");
+ case GoalAction::REPLAY_PARSE:
+ return _("Parse serialized transaction");
case GoalAction::REPLAY_INSTALL:
return _("Install action");
case GoalAction::REPLAY_REMOVE:
diff --git a/libdnf5/base/log_event.cpp b/libdnf5/base/log_event.cpp
index b3693263a..15a29cecd 100644
--- a/libdnf5/base/log_event.cpp
+++ b/libdnf5/base/log_event.cpp
@@ -337,6 +337,9 @@ std::string LogEvent::to_string(
*spec,
*additional_data.begin()));
}
+ case GoalProblem::MALFORMED: {
+ return ret.append(utils::sformat(_("Cannot parse file: '{0}': {1}.\n"), *spec, *additional_data.begin()));
+ }
}
return ret;
}
diff --git a/libdnf5/transaction/transaction_sr.cpp b/libdnf5/transaction/transaction_sr.cpp
index 88a0645cd..48562213f 100644
--- a/libdnf5/transaction/transaction_sr.cpp
+++ b/libdnf5/transaction/transaction_sr.cpp
@@ -44,13 +44,6 @@ namespace libdnf5::transaction {
constexpr const char * VERSION_MAJOR = "1";
constexpr const char * VERSION_MINOR = "0";
-class TransactionReplayError : public Error {
-public:
- using Error::Error;
- const char * get_domain_name() const noexcept override { return "libdnf5::transaction"; }
- const char * get_name() const noexcept override { return "TransactionReplayError"; }
-};
-
TransactionReplay parse_transaction_replay(const std::string & json_serialized_transaction) {
if (json_serialized_transaction.empty()) {
throw TransactionReplayError(M_("Transaction replay JSON serialized transaction input is empty"));
diff --git a/libdnf5/transaction/transaction_sr.hpp b/libdnf5/transaction/transaction_sr.hpp
index 830cbb72b..e72ca076d 100644
--- a/libdnf5/transaction/transaction_sr.hpp
+++ b/libdnf5/transaction/transaction_sr.hpp
@@ -29,6 +29,13 @@ along with libdnf. If not, see .
namespace libdnf5::transaction {
+class TransactionReplayError : public Error {
+public:
+ using Error::Error;
+ const char * get_domain_name() const noexcept override { return "libdnf5::transaction"; }
+ const char * get_name() const noexcept override { return "TransactionReplayError"; }
+};
+
struct GroupReplay {
TransactionItemAction action;