diff options
-rw-r--r-- | src/buildtool/file_system/git_repo.cpp | 2 | ||||
-rw-r--r-- | src/other_tools/git_operations/TARGETS | 1 | ||||
-rw-r--r-- | src/other_tools/git_operations/git_repo_remote.cpp | 139 | ||||
-rw-r--r-- | src/other_tools/git_operations/git_repo_remote.hpp | 12 | ||||
-rw-r--r-- | test/other_tools/git_operations/git_repo_remote.test.cpp | 6 |
5 files changed, 101 insertions, 59 deletions
diff --git a/src/buildtool/file_system/git_repo.cpp b/src/buildtool/file_system/git_repo.cpp index baac7cf5..0b56099c 100644 --- a/src/buildtool/file_system/git_repo.cpp +++ b/src/buildtool/file_system/git_repo.cpp @@ -1073,7 +1073,7 @@ auto GitRepo::ReadTreeData(std::string const& data, } auto GitRepo::CreateShallowTree(tree_entries_t const& entries) noexcept - -> std::optional<std::pair<std::string, std::string> > { + -> std::optional<std::pair<std::string, std::string>> { #ifndef BOOTSTRAP_BUILD_TOOL try { InMemoryODBBackend b{kInMemoryODBParent, &entries}; diff --git a/src/other_tools/git_operations/TARGETS b/src/other_tools/git_operations/TARGETS index 27be26ed..19dabe4f 100644 --- a/src/other_tools/git_operations/TARGETS +++ b/src/other_tools/git_operations/TARGETS @@ -32,6 +32,7 @@ , ["src/buildtool/file_system", "git_utils"] , ["@", "fmt", "", "fmt"] , ["", "libgit2"] + , "git_config_settings" ] } , "git_config_settings": diff --git a/src/other_tools/git_operations/git_repo_remote.cpp b/src/other_tools/git_operations/git_repo_remote.cpp index 7d2ee7f4..f599c108 100644 --- a/src/other_tools/git_operations/git_repo_remote.cpp +++ b/src/other_tools/git_operations/git_repo_remote.cpp @@ -17,6 +17,7 @@ #include "fmt/core.h" #include "src/buildtool/file_system/git_utils.hpp" #include "src/buildtool/logging/logger.hpp" +#include "src/other_tools/git_operations/git_config_settings.hpp" extern "C" { #include <git2.h> @@ -67,48 +68,6 @@ void fetch_backend_free(git_odb_backend* /*_backend*/) {} // A backend that can be used to fetch from the remote of another repository. auto const kFetchIntoODBParent = CreateFetchIntoODBParent(); -// callback to enable SSL certificate check for remote fetch -const auto certificate_check_cb = [](git_cert* /*cert*/, - int /*valid*/, - const char* /*host*/, - void* /*payload*/) -> int { return 1; }; - -// callback to remote fetch without an SSL certificate check -const auto certificate_passthrough_cb = [](git_cert* /*cert*/, - int /*valid*/, - const char* /*host*/, - void* /*payload*/) -> int { - return 0; -}; - -/// \brief Set a custom SSL certificate check callback to honor the existing Git -/// configuration of a repository trying to connect to a remote. -[[nodiscard]] auto SetCustomSSLCertificateCheckCallback(git_repository* repo) - -> git_transport_certificate_check_cb { - // check SSL verification settings, from most to least specific - std::optional<int> check_cert{std::nullopt}; - // check gitconfig; ignore errors - git_config* cfg{nullptr}; - int tmp{}; - if (git_repository_config(&cfg, repo) == 0 and - git_config_get_bool(&tmp, cfg, "http.sslVerify") == 0) { - check_cert = tmp; - } - if (not check_cert) { - // check for GIT_SSL_NO_VERIFY environment variable - const char* ssl_no_verify_var{std::getenv("GIT_SSL_NO_VERIFY")}; - if (ssl_no_verify_var != nullptr and - git_config_parse_bool(&tmp, ssl_no_verify_var) == 0) { - check_cert = tmp; - } - } - // cleanup memory - git_config_free(cfg); - // set callback - return (check_cert and check_cert.value() == 0) ? certificate_passthrough_cb - : certificate_check_cb; -} - } // namespace auto GitRepoRemote::Open(GitCASPtr git_cas) noexcept @@ -157,7 +116,8 @@ auto GitRepoRemote::InitAndOpen(std::filesystem::path const& repo_path, return std::nullopt; } -auto GitRepoRemote::GetCommitFromRemote(std::string const& repo_url, +auto GitRepoRemote::GetCommitFromRemote(std::shared_ptr<git_config> cfg, + std::string const& repo_url, std::string const& branch, anon_logger_ptr const& logger) noexcept -> std::optional<std::string> { @@ -183,14 +143,34 @@ auto GitRepoRemote::GetCommitFromRemote(std::string const& repo_url, } auto remote = std::unique_ptr<git_remote, decltype(&remote_closer)>( remote_ptr, remote_closer); + // get the canonical url + auto canonical_url = std::string(git_remote_url(remote.get())); + + // get a well-defined config file + if (not cfg) { + // get config snapshot of current repo (shared with caller) + cfg = GetConfigSnapshot(); + if (cfg == nullptr) { + (*logger)(fmt::format("retrieving config object in get commit " + "from remote failed with:\n{}", + GitLastError()), + true /*fatal*/); + return std::nullopt; + } + } // connect to remote git_remote_callbacks callbacks{}; git_remote_init_callbacks(&callbacks, GIT_REMOTE_CALLBACKS_VERSION); - // set custom SSL verification callback - callbacks.certificate_check = - SetCustomSSLCertificateCheckCallback(GetRepoRef().get()); + // set custom SSL verification callback; use canonicalized url + auto cert_check = + GitConfigSettings::GetSSLCallback(cfg, canonical_url, logger); + if (not cert_check) { + // error occurred while handling the url + return std::nullopt; + } + callbacks.certificate_check = *cert_check; git_proxy_options proxy_opts{}; git_proxy_options_init(&proxy_opts, GIT_PROXY_OPTIONS_VERSION); @@ -251,7 +231,8 @@ auto GitRepoRemote::GetCommitFromRemote(std::string const& repo_url, } } -auto GitRepoRemote::FetchFromRemote(std::string const& repo_url, +auto GitRepoRemote::FetchFromRemote(std::shared_ptr<git_config> cfg, + std::string const& repo_url, std::optional<std::string> const& branch, anon_logger_ptr const& logger) noexcept -> bool { @@ -279,6 +260,24 @@ auto GitRepoRemote::FetchFromRemote(std::string const& repo_url, // wrap remote object auto remote = std::unique_ptr<git_remote, decltype(&remote_closer)>( remote_ptr, remote_closer); + // get the canonical url + auto canonical_url = std::string(git_remote_url(remote.get())); + + // get a well-defined config file + if (not cfg) { + // get config snapshot of current repo + git_config* cfg_ptr{nullptr}; + if (git_repository_config_snapshot(&cfg_ptr, GetRepoRef().get()) != + 0) { + (*logger)(fmt::format("retrieving config object in fetch from " + "remote failed with:\n{}", + GitLastError()), + true /*fatal*/); + return false; + } + // share pointer with caller + cfg.reset(cfg_ptr, config_closer); + } // define default fetch options git_fetch_options fetch_opts{}; @@ -287,9 +286,25 @@ auto GitRepoRemote::FetchFromRemote(std::string const& repo_url, // set the option to auto-detect proxy settings fetch_opts.proxy_opts.type = GIT_PROXY_AUTO; - // set custom SSL verification callback - fetch_opts.callbacks.certificate_check = - SetCustomSSLCertificateCheckCallback(GetRepoRef().get()); + // set custom SSL verification callback; use canonicalized url + if (not cfg) { + // get config snapshot of current repo (shared with caller) + cfg = GetConfigSnapshot(); + if (cfg == nullptr) { + (*logger)(fmt::format("retrieving config object in fetch from " + "remote failed with:\n{}", + GitLastError()), + true /*fatal*/); + return false; + } + } + auto cert_check = + GitConfigSettings::GetSSLCallback(cfg, canonical_url, logger); + if (not cert_check) { + // error occurred while handling the url + return false; + } + fetch_opts.callbacks.certificate_check = *cert_check; // disable update of the FETCH_HEAD pointer fetch_opts.update_fetchhead = 0; @@ -352,7 +367,17 @@ auto GitRepoRemote::UpdateCommitViaTmpRepo( msg), fatal); }); - return tmp_repo->GetCommitFromRemote(repo_url, branch, wrapped_logger); + // get the config of the correct target repo + auto cfg = GetConfigSnapshot(); + if (cfg == nullptr) { + (*logger)(fmt::format("retrieving config object in update commit " + "via tmp repo failed with:\n{}", + GitLastError()), + true /*fatal*/); + return std::nullopt; + } + return tmp_repo->GetCommitFromRemote( + cfg, repo_url, branch, wrapped_logger); } catch (std::exception const& ex) { Logger::Log(LogLevel::Error, "update commit via tmp repo failed with:\n{}", @@ -395,7 +420,17 @@ auto GitRepoRemote::FetchViaTmpRepo(std::filesystem::path const& tmp_repo_path, "While doing branch fetch via tmp repo:\n{}", msg), fatal); }); - return tmp_repo->FetchFromRemote(repo_url, branch, wrapped_logger); + // get the config of the correct target repo + auto cfg = GetConfigSnapshot(); + if (cfg == nullptr) { + (*logger)(fmt::format("retrieving config object in fetch via " + "tmp repo failed with:\n{}", + GitLastError()), + true /*fatal*/); + return false; + } + return tmp_repo->FetchFromRemote( + cfg, repo_url, branch, wrapped_logger); } return false; } catch (std::exception const& ex) { diff --git a/src/other_tools/git_operations/git_repo_remote.hpp b/src/other_tools/git_operations/git_repo_remote.hpp index 334f7fca..8b0021b8 100644 --- a/src/other_tools/git_operations/git_repo_remote.hpp +++ b/src/other_tools/git_operations/git_repo_remote.hpp @@ -50,9 +50,12 @@ class GitRepoRemote : public GitRepo { /// \brief Retrieve commit hash from remote branch given its name. /// Only possible with real repository and thus non-thread-safe. + /// If non-null, use given config snapshot to interact with config entries; + /// otherwise, use a snapshot from the current repo and share pointer to it. /// Returns the retrieved commit hash, or nullopt if failure. /// It guarantees the logger is called exactly once with fatal if failure. [[nodiscard]] auto GetCommitFromRemote( + std::shared_ptr<git_config> cfg, std::string const& repo_url, std::string const& branch, anon_logger_ptr const& logger) noexcept -> std::optional<std::string>; @@ -60,9 +63,12 @@ class GitRepoRemote : public GitRepo { /// \brief Fetch from given remote. It can either fetch a given named /// branch, or it can fetch with base refspecs. /// Only possible with real repository and thus non-thread-safe. - /// Returns a success flag. - /// It guarantees the logger is called exactly once with fatal if failure. - [[nodiscard]] auto FetchFromRemote(std::string const& repo_url, + /// If non-null, use given config snapshot to interact with config entries; + /// otherwise, use a snapshot from the current repo and share pointer to it. + /// Returns a success flag. It guarantees the logger is called + /// exactly once with fatal if failure. + [[nodiscard]] auto FetchFromRemote(std::shared_ptr<git_config> cfg, + std::string const& repo_url, std::optional<std::string> const& branch, anon_logger_ptr const& logger) noexcept -> bool; diff --git a/test/other_tools/git_operations/git_repo_remote.test.cpp b/test/other_tools/git_operations/git_repo_remote.test.cpp index 89f67d41..4b7a2a77 100644 --- a/test/other_tools/git_operations/git_repo_remote.test.cpp +++ b/test/other_tools/git_operations/git_repo_remote.test.cpp @@ -185,7 +185,7 @@ TEST_CASE("Single-threaded real repository remote operations", // remote ls auto remote_commit = repo_remote_ls_bare->GetCommitFromRemote( - *repo_path, "master", logger); + nullptr, *repo_path, "master", logger); REQUIRE(remote_commit); CHECK(*remote_commit == kRootCommit); } @@ -198,7 +198,7 @@ TEST_CASE("Single-threaded real repository remote operations", // fetch CHECK(repo_fetch_all_bare->FetchFromRemote( - *repo_path, std::nullopt, logger)); + nullptr, *repo_path, std::nullopt, logger)); } SECTION("Fetch branch from remote") { @@ -211,7 +211,7 @@ TEST_CASE("Single-threaded real repository remote operations", // fetch CHECK(repo_fetch_branch_bare->FetchFromRemote( - *repo_path, "master", logger)); + nullptr, *repo_path, "master", logger)); } } |