From d08ca5dc8d1f1362a1a63c5b2ff164ad73948932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= Date: Thu, 6 Jun 2024 14:11:14 +0200 Subject: [PATCH] Add `replay` command to replay stored transactions In comparison to dnf4 the command: - is no longer a `history` subcommand, it is a standalone command. - accepts as an argument a path to a directory where the transaction is stored. This is because now any transaction can be stored using the `--store` option which also stores the elements (packages, groups..) of the transaction. --- dnf5.spec | 2 + dnf5/commands/history/history.cpp | 2 - dnf5/commands/history/history_replay.cpp | 30 -------- dnf5/commands/replay/replay.cpp | 74 +++++++++++++++++++ .../history_replay.hpp => replay/replay.hpp} | 17 +++-- dnf5/main.cpp | 2 + doc/CMakeLists.txt | 1 + doc/changes.rst | 5 ++ doc/commands/history.8.rst | 3 - doc/commands/index.rst | 1 + doc/commands/replay.8.rst | 58 +++++++++++++++ doc/conf.py.in | 1 + doc/dnf5.8.rst | 3 + 13 files changed, 158 insertions(+), 41 deletions(-) delete mode 100644 dnf5/commands/history/history_replay.cpp create mode 100644 dnf5/commands/replay/replay.cpp rename dnf5/commands/{history/history_replay.hpp => replay/replay.hpp} (68%) create mode 100644 doc/commands/replay.8.rst diff --git a/dnf5.spec b/dnf5.spec index 803b8aaef..fa1f5248f 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 80a6f49a2..ceae67ae4 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/replay/replay.cpp b/dnf5/commands/replay/replay.cpp new file mode 100644 index 000000000..aa7339743 --- /dev/null +++ b/dnf5/commands/replay/replay.cpp @@ -0,0 +1,74 @@ +/* +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 +#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."); + 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_unavailable = 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(); + + //TODO(amatej): add `--ignore-installed` and `--ignore-extras` options + + try { + context.get_goal()->add_serialized_transaction(transaction_path + TRANSACTION_JSON, settings); + } catch (const libdnf5::Error & ex) { + throw libdnf5::cli::CommandExitError( + 1, M_("Error while handling: {}: {}"), transaction_path, std::string(ex.what())); + } +} + +} // namespace dnf5 diff --git a/dnf5/commands/history/history_replay.hpp b/dnf5/commands/replay/replay.hpp similarity index 68% rename from dnf5/commands/history/history_replay.hpp rename to dnf5/commands/replay/replay.hpp index 9f379d930..7d8d4e15d 100644 --- a/dnf5/commands/history/history_replay.hpp +++ b/dnf5/commands/replay/replay.hpp @@ -18,24 +18,29 @@ 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; }; } // namespace dnf5 -#endif // DNF5_COMMANDS_HISTORY_HISTORY_REPLAY_HPP +#endif // DNF5_COMMANDS_REPLAY__REPLAY_HPP diff --git a/dnf5/main.cpp b/dnf5/main.cpp index c81da185d..7703d08eb 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 864e0582f..1ea6bb6f7 100644 --- a/doc/changes.rst +++ b/doc/changes.rst @@ -201,6 +201,11 @@ Changes to individual commands ``help`` * Dropped. The functionality is replaced by the ``--help`` option. +``history`` + * ``store`` subcommand now outputs a directory with transaction JSON file instead of a single transaction JSON file directly. + * ``replay`` subcommand was moved to a standalone ``replay`` command, that now accepts a path to a directory instead of a single file. + The directory can be created with ``--store`` option and in addition to the JSON transaction can contain packages, group and environments used in the transaction. + ``info`` * Dropped ``--all`` option since this behavior is the default one. * Dropped ``--updates`` option, only ``--upgrades`` is available now. diff --git a/doc/commands/history.8.rst b/doc/commands/history.8.rst index 955f87acb..1bb083566 100644 --- a/doc/commands/history.8.rst +++ b/doc/commands/history.8.rst @@ -61,9 +61,6 @@ Subcommands ``store`` | Store the transaction into a directory. -``replay`` - | Replay the transaction that was previously stored into the file. - Options ======= 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..93370933d --- /dev/null +++ b/doc/commands/replay.8.rst @@ -0,0 +1,58 @@ +.. + 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 +======= + +``--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.