From da5b114794bd71e6cfccea360b1e1956ed58b8b3 Mon Sep 17 00:00:00 2001 From: Jaroslav Rohel Date: Fri, 3 Nov 2023 00:29:16 +0100 Subject: [PATCH] [dnf5 plugin] config-manager: Support "--add-or-replace" in "addrepo" What happens when the destination repository configuration file already exists? By default throw an error. --overwrite - Allow overwriting of existing repository configuration file --add-or-replace - Allow adding or replacing a repository in the existing configuration file --- .../config-manager_plugin/addrepo.cpp | 41 +++++++++++++++---- .../config-manager_plugin/addrepo.hpp | 17 +++++--- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/dnf5-plugins/config-manager_plugin/addrepo.cpp b/dnf5-plugins/config-manager_plugin/addrepo.cpp index a6d967c75..9ab306a46 100644 --- a/dnf5-plugins/config-manager_plugin/addrepo.cpp +++ b/dnf5-plugins/config-manager_plugin/addrepo.cpp @@ -209,6 +209,16 @@ void ConfigManagerAddRepoCommand::set_argument_parser() { }); cmd.register_named_arg(set_opt); + auto add_or_replace = parser.add_new_named_arg("add-or-replace"); + add_or_replace->set_long_name("add-or-replace"); + add_or_replace->set_description("Allow adding or replacing a repository in the existing configuration file"); + add_or_replace->set_has_value(false); + add_or_replace->set_parse_hook_func([this](cli::ArgumentParser::NamedArg *, const char *, const char *) { + file_policy = FilePolicy::ADD_OR_REPLACE; + return true; + }); + cmd.register_named_arg(add_or_replace); + auto create_missing_dirs_opt = parser.add_new_named_arg("create-missing-dir"); create_missing_dirs_opt->set_long_name("create-missing-dir"); create_missing_dirs_opt->set_description("Allow creation of missing directories"); @@ -224,7 +234,7 @@ void ConfigManagerAddRepoCommand::set_argument_parser() { overwrite_opt->set_description("Allow overwriting of existing repository configuration file"); overwrite_opt->set_has_value(false); overwrite_opt->set_parse_hook_func([this](cli::ArgumentParser::NamedArg *, const char *, const char *) { - overwrite = true; + file_policy = FilePolicy::OVERWRITE; return true; }); cmd.register_named_arg(overwrite_opt); @@ -243,6 +253,7 @@ void ConfigManagerAddRepoCommand::set_argument_parser() { cmd.register_named_arg(save_filename_opt); // Set conflicting arguments + add_or_replace->add_conflict_argument(*from_repofile_opt); repo_id_opt->add_conflict_argument(*from_repofile_opt); set_opt->add_conflict_argument(*from_repofile_opt); } @@ -285,7 +296,7 @@ void ConfigManagerAddRepoCommand::add_repos_from_repofile( } auto dest_path = dest_repo_dir / save_filename; - test_if_filepath_not_exist(dest_path); + test_if_filepath_not_exist(dest_path, false); // Creates an open temporary file. It then closes it but does not remove it. // In the following code, this temporary file is used to store the copied/downloaded configuration. @@ -395,10 +406,19 @@ void ConfigManagerAddRepoCommand::create_repo( } auto dest_path = dest_repo_dir / save_filename; - test_if_filepath_not_exist(dest_path); + test_if_filepath_not_exist(dest_path, true); test_if_ids_not_already_exist({repo_id}, dest_path); ConfigParser parser; + + if (file_policy == FilePolicy::ADD_OR_REPLACE && std::filesystem::exists(dest_path)) { + parser.read(dest_path); + if (parser.has_section(repo_id)) { + // If the repository with the id already exists, it will be removed. + parser.remove_section(repo_id); + } + } + parser.add_section(repo_id); // Sets the default repository name. May be overwritten with "--set=name=". @@ -421,8 +441,9 @@ void ConfigManagerAddRepoCommand::create_repo( } -void ConfigManagerAddRepoCommand::test_if_filepath_not_exist(const std::filesystem::path & path) const { - if (!overwrite && std::filesystem::exists(path)) { +void ConfigManagerAddRepoCommand::test_if_filepath_not_exist( + const std::filesystem::path & path, bool show_hint_add_or_replace) const { + if (file_policy == FilePolicy::ERROR && std::filesystem::exists(path)) { ConfigParser parser; parser.read(path); std::string repo_ids; @@ -435,11 +456,13 @@ void ConfigManagerAddRepoCommand::test_if_filepath_not_exist(const std::filesyst } repo_ids += repo_id; } - throw ConfigManagerError( + constexpr BgettextMessage msg1 = + M_("File \"{}\" already exists and configures repositories with IDs \"{}\"." + " Add \"--add-or-replace\" or \"--overwrite\"."); + constexpr BgettextMessage msg2 = M_("File \"{}\" already exists and configures repositories with IDs \"{}\"." - " Add \"--overwrite\" to overwrite."), - path.string(), - repo_ids); + " Add \"--overwrite\" to overwrite."); + throw ConfigManagerError(show_hint_add_or_replace ? msg1 : msg2, path.string(), repo_ids); } } diff --git a/dnf5-plugins/config-manager_plugin/addrepo.hpp b/dnf5-plugins/config-manager_plugin/addrepo.hpp index 282195b8a..702b62013 100644 --- a/dnf5-plugins/config-manager_plugin/addrepo.hpp +++ b/dnf5-plugins/config-manager_plugin/addrepo.hpp @@ -37,6 +37,13 @@ class ConfigManagerAddRepoCommand : public Command { void configure() override; private: + // Defines what happens when the destination repository configuration file already exists. + enum class FilePolicy { + ERROR, // Throw an error + OVERWRITE, // Allow overwriting of existing repository configuration file + ADD_OR_REPLACE // Allow adding or replacing a repository in the existing configuration file + }; + struct SourceRepofile { std::string location; bool is_local_path; @@ -60,7 +67,7 @@ class ConfigManagerAddRepoCommand : public Command { /// Tests if the file does not exist. /// @param path Path to check. /// @throws ConfigManagerError Trown if `path` already exist and overwriting is not allowed. - void test_if_filepath_not_exist(const std::filesystem::path & path) const; + void test_if_filepath_not_exist(const std::filesystem::path & path, bool show_hint_add_or_replace) const; /// Tests if the repositories IDs in the vector do not already exist in the configuration. /// @param repo_ids List of repositories IDs to check. @@ -72,10 +79,10 @@ class ConfigManagerAddRepoCommand : public Command { libdnf5::ConfigMain tmp_config; libdnf5::repo::ConfigRepo tmp_repo_conf{tmp_config, "temporary_to_check_repository_options"}; - SourceRepofile source_repofile; // Location of source repository configuration file. - std::string repo_id; // The user-defined ID of the newly created repository. - bool create_missing_dirs{false}; // Allows to create missing directories. - bool overwrite{false}; // Allows to overwrite an existing configuration file. + SourceRepofile source_repofile; // Location of source repository configuration file. + std::string repo_id; // The user-defined ID of the newly created repository. + bool create_missing_dirs{false}; // Allows to create missing directories. + FilePolicy file_policy{FilePolicy::ERROR}; std::string save_filename; // User-defined name of newly saved configuration file. std::map repo_opts; // Options for the new repository. };