Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add replay command to replay stored transactions #1536

Merged
merged 5 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions dnf5.spec
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.*
Expand Down
2 changes: 0 additions & 2 deletions dnf5/commands/history/history.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ along with libdnf. If not, see <https://www.gnu.org/licenses/>.
#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"
Expand Down Expand Up @@ -61,7 +60,6 @@ void HistoryCommand::register_subcommands() {
// register_subcommand(std::make_unique<HistoryRedoCommand>(get_context()), software_management_commands_group);
// register_subcommand(std::make_unique<HistoryRollbackCommand>(get_context()), software_management_commands_group);
register_subcommand(std::make_unique<HistoryStoreCommand>(get_context()), software_management_commands_group);
// register_subcommand(std::make_unique<HistoryReplayCommand>(get_context()), software_management_commands_group);
}

void HistoryCommand::pre_configure() {
Expand Down
30 changes: 0 additions & 30 deletions dnf5/commands/history/history_replay.cpp

This file was deleted.

17 changes: 10 additions & 7 deletions dnf5/commands/history/history_store.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ void HistoryStoreCommand::set_argument_parser() {
auto & parser = ctx.get_argument_parser();

output_option = dynamic_cast<libdnf5::OptionString *>(
parser.add_init_value(std::make_unique<libdnf5::OptionString>("./transaction.json")));
parser.add_init_value(std::make_unique<libdnf5::OptionString>("./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\"");
j-mracek marked this conversation as resolved.
Show resolved Hide resolved
query_format->set_has_value(true);
query_format->set_arg_value_help("PATH");
query_format->link_value(output_option);
Expand All @@ -55,11 +55,14 @@ void HistoryStoreCommand::run() {
std::vector<libdnf5::transaction::Transaction> 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();
Expand All @@ -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();
}

Expand Down
2 changes: 1 addition & 1 deletion dnf5/commands/offline/offline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<libdnf5::Goal>(ctx.get_base());

Expand Down
77 changes: 77 additions & 0 deletions dnf5/commands/replay/replay.cpp
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
*/

#include "replay.hpp"

#include "commands/history/arguments.hpp"

#include <dnf5/shared_options.hpp>
#include <libdnf5/utils/bgettext/bgettext-mark-domain.h>

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");
j-mracek marked this conversation as resolved.
Show resolved Hide resolved
j-mracek marked this conversation as resolved.
Show resolved Hide resolved
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<SkipBrokenOption>(*this);
std::make_unique<SkipUnavailableOption>(*this);
ignore_extras = std::make_unique<IgnoreExtrasOption>(*this);
ignore_installed = std::make_unique<IgnoreInstalledOption>(*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
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,32 @@ along with libdnf. If not, see <https://www.gnu.org/licenses/>.
*/


#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 <dnf5/context.hpp>

#include <libdnf5/conf/option_bool.hpp>

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<libdnf5::cli::session::BoolOption> ignore_extras{nullptr};
std::unique_ptr<libdnf5::cli::session::BoolOption> ignore_installed{nullptr};
};


} // namespace dnf5


#endif // DNF5_COMMANDS_HISTORY_HISTORY_REPLAY_HPP
#endif // DNF5_COMMANDS_REPLAY__REPLAY_HPP
5 changes: 2 additions & 3 deletions dnf5/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ along with libdnf. If not, see <https://www.gnu.org/licenses/>.
#include <iostream>
#include <regex>
#include <set>
#include <stdexcept>
#include <string>
#include <tuple>
#include <utility>
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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"};
Expand Down
4 changes: 2 additions & 2 deletions dnf5/include/dnf5/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@ along with libdnf. If not, see <https://www.gnu.org/licenses/>.
#include <libdnf5/rpm/package.hpp>

#include <filesystem>
#include <functional>
#include <iostream>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

namespace dnf5 {

constexpr const char * TRANSACTION_JSON = "transaction.json";

class Plugins;

class DNF_API Context : public libdnf5::cli::session::Session {
Expand Down
2 changes: 2 additions & 0 deletions dnf5/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ along with libdnf. If not, see <https://www.gnu.org/licenses/>.
#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"
Expand Down Expand Up @@ -674,6 +675,7 @@ static void add_commands(Context & context) {
context.add_and_initialize_command(std::make_unique<MarkCommand>(context));
context.add_and_initialize_command(std::make_unique<AutoremoveCommand>(context));
context.add_and_initialize_command(std::make_unique<ProvidesCommand>(context));
context.add_and_initialize_command(std::make_unique<ReplayCommand>(context));

context.add_and_initialize_command(std::make_unique<LeavesCommand>(context));
context.add_and_initialize_command(std::make_unique<RepoqueryCommand>(context));
Expand Down
1 change: 1 addition & 0 deletions doc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions doc/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 1 addition & 4 deletions doc/commands/history.8.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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``
Expand Down
1 change: 1 addition & 0 deletions doc/commands/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ DNF5 Commands
provides.8
reinstall.8
remove.8
replay.8
repo.8
repoquery.8
search.8
Expand Down
Loading
Loading