From 65944566d8d3ae81d3dc3acb8c82944f5698ca5d Mon Sep 17 00:00:00 2001 From: Paul Cristian Sarbu Date: Wed, 15 Feb 2023 17:12:15 +0100 Subject: structure cleanup: move remote operations of GitRepo to other_tools... ...in order to not include unwanted dependencies in just proper. The new class extends the GitRepo class used for just's Git tree operations and gets used in all of just-mr's async maps. --- src/buildtool/file_system/git_repo.cpp | 399 ++------------------------------- 1 file changed, 15 insertions(+), 384 deletions(-) (limited to 'src/buildtool/file_system/git_repo.cpp') diff --git a/src/buildtool/file_system/git_repo.cpp b/src/buildtool/file_system/git_repo.cpp index d2cec500..baac7cf5 100644 --- a/src/buildtool/file_system/git_repo.cpp +++ b/src/buildtool/file_system/git_repo.cpp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include +#include "src/buildtool/file_system/git_repo.hpp" #include "src/buildtool/logging/logger.hpp" #include "src/utils/cpp/hex_string.hpp" @@ -26,46 +26,6 @@ extern "C" { #ifndef BOOTSTRAP_BUILD_TOOL namespace { -constexpr std::size_t kWaitTime{2}; // time in ms between tries for git locks - -constexpr std::size_t kOIDRawSize{GIT_OID_RAWSZ}; -constexpr std::size_t kOIDHexSize{GIT_OID_HEXSZ}; - -[[nodiscard]] auto GitLastError() noexcept -> std::string { - git_error const* err{nullptr}; - if ((err = git_error_last()) != nullptr and err->message != nullptr) { - return fmt::format("error code {}: {}", err->klass, err->message); - } - return ""; -} - -[[nodiscard]] auto GitObjectID(std::string const& id, - bool is_hex_id = false) noexcept - -> std::optional { - if (id.size() < (is_hex_id ? kOIDHexSize : kOIDRawSize)) { - Logger::Log(LogLevel::Error, - "invalid git object id {}", - is_hex_id ? id : ToHexString(id)); - return std::nullopt; - } - git_oid oid{}; - if (is_hex_id and git_oid_fromstr(&oid, id.c_str()) == 0) { - return oid; - } - if (not is_hex_id and - git_oid_fromraw( - &oid, - reinterpret_cast(id.data()) // NOLINT - ) == 0) { - return oid; - } - Logger::Log(LogLevel::Error, - "parsing git object id {} failed with:\n{}", - is_hex_id ? id : ToHexString(id), - GitLastError()); - return std::nullopt; -} - [[nodiscard]] auto ToHexString(git_oid const& oid) noexcept -> std::optional { std::string hex_id(GIT_OID_HEXSZ, '\0'); @@ -278,90 +238,6 @@ void backend_free(git_odb_backend* /*_backend*/) {} // A backend that can be used to read and create tree objects in-memory. auto const kInMemoryODBParent = CreateInMemoryODBParent(); -struct FetchIntoODBBackend { - git_odb_backend parent; - git_odb* target_odb; // the odb where the fetch objects will end up into -}; - -[[nodiscard]] auto fetch_backend_writepack(git_odb_writepack** _writepack, - git_odb_backend* _backend, - [[maybe_unused]] git_odb* odb, - git_indexer_progress_cb progress_cb, - void* progress_payload) -> int { - if (_backend != nullptr) { - auto* b = reinterpret_cast(_backend); // NOLINT - return git_odb_write_pack( - _writepack, b->target_odb, progress_cb, progress_payload); - } - return GIT_ERROR; -} - -[[nodiscard]] auto fetch_backend_exists(git_odb_backend* _backend, - const git_oid* oid) -> int { - if (_backend != nullptr) { - auto* b = reinterpret_cast(_backend); // NOLINT - return git_odb_exists(b->target_odb, oid); - } - return GIT_ERROR; -} - -void fetch_backend_free(git_odb_backend* /*_backend*/) {} - -[[nodiscard]] auto CreateFetchIntoODBParent() -> git_odb_backend { - git_odb_backend b{}; - b.version = GIT_ODB_BACKEND_VERSION; - // only populate the functions needed - b.writepack = &fetch_backend_writepack; // needed for fetch - b.exists = &fetch_backend_exists; - b.free = &fetch_backend_free; - return b; -} - -// 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 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 #endif // BOOTSTRAP_BUILD_TOOL @@ -521,6 +397,20 @@ auto GitRepo::GetGitCAS() const noexcept -> GitCASPtr { return git_cas_; } +auto GitRepo::GetRepoRef() const noexcept + -> std::unique_ptr const& { + return repo_; +} + +auto GitRepo::GetGitPath() const noexcept -> std::filesystem::path const& { + return git_cas_->git_path_; +} + +auto GitRepo::GetGitOdb() const noexcept + -> std::unique_ptr const& { + return git_cas_->odb_; +} + auto GitRepo::StageAndCommitAllAnonymous(std::string const& message, anon_logger_ptr const& logger) noexcept -> std::optional { @@ -753,182 +643,6 @@ auto GitRepo::GetHeadCommit(anon_logger_ptr const& logger) noexcept #endif // BOOTSTRAP_BUILD_TOOL } -auto GitRepo::GetCommitFromRemote(std::string const& repo_url, - std::string const& branch, - anon_logger_ptr const& logger) noexcept - -> std::optional { -#ifdef BOOTSTRAP_BUILD_TOOL - return std::nullopt; -#else - try { - // only possible for real repository! - if (IsRepoFake()) { - (*logger)("cannot update commit using a fake repository!", - true /*fatal*/); - return std::nullopt; - } - // create remote - git_remote* remote_ptr{nullptr}; - if (git_remote_create_anonymous( - &remote_ptr, repo_.get(), repo_url.c_str()) != 0) { - (*logger)( - fmt::format("creating anonymous remote for git repository {} " - "failed with:\n{}", - GetGitCAS()->git_path_.string(), - GitLastError()), - true /*fatal*/); - git_remote_free(remote_ptr); - return std::nullopt; - } - auto remote = std::unique_ptr( - remote_ptr, remote_closer); - - // 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(repo_.get()); - - git_proxy_options proxy_opts{}; - git_proxy_options_init(&proxy_opts, GIT_PROXY_OPTIONS_VERSION); - - // set the option to auto-detect proxy settings - proxy_opts.type = GIT_PROXY_AUTO; - - if (git_remote_connect(remote.get(), - GIT_DIRECTION_FETCH, - &callbacks, - &proxy_opts, - nullptr) != 0) { - (*logger)( - fmt::format("connecting to remote {} for git repository {} " - "failed with:\n{}", - repo_url, - GetGitCAS()->git_path_.string(), - GitLastError()), - true /*fatal*/); - return std::nullopt; - } - // get the list of refs from remote - // NOTE: refs will be owned by remote, so we DON'T have to free it! - git_remote_head const** refs = nullptr; - size_t refs_len = 0; - if (git_remote_ls(&refs, &refs_len, remote.get()) != 0) { - (*logger)( - fmt::format("refs retrieval from remote {} failed with:\n{}", - repo_url, - GitLastError()), - true /*fatal*/); - return std::nullopt; - } - // figure out what remote branch the local one is tracking - for (size_t i = 0; i < refs_len; ++i) { - // by treating each read reference string as a path we can easily - // check for the branch name - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - std::filesystem::path ref_name_as_path{refs[i]->name}; - if (ref_name_as_path.filename() == branch) { - // branch found! - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - std::string new_commit_hash{git_oid_tostr_s(&refs[i]->oid)}; - return new_commit_hash; - } - } - (*logger)(fmt::format("could not find branch {} for remote {}", - branch, - repo_url, - GitLastError()), - true /*fatal*/); - return std::nullopt; - } catch (std::exception const& ex) { - Logger::Log(LogLevel::Error, - "get commit from remote failed with:\n{}", - ex.what()); - return std::nullopt; - } -#endif // BOOTSTRAP_BUILD_TOOL -} - -auto GitRepo::FetchFromRemote(std::string const& repo_url, - std::optional const& branch, - anon_logger_ptr const& logger) noexcept -> bool { -#ifdef BOOTSTRAP_BUILD_TOOL - return false; -#else - try { - // only possible for real repository! - if (IsRepoFake()) { - (*logger)("cannot fetch commit using a fake repository!", - true /*fatal*/); - return false; - } - // create remote from repo - git_remote* remote_ptr{nullptr}; - if (git_remote_create_anonymous( - &remote_ptr, repo_.get(), repo_url.c_str()) != 0) { - (*logger)(fmt::format("creating remote {} for git repository {} " - "failed with:\n{}", - repo_url, - GetGitCAS()->git_path_.string(), - GitLastError()), - true /*fatal*/); - // cleanup resources - git_remote_free(remote_ptr); - return false; - } - // wrap remote object - auto remote = std::unique_ptr( - remote_ptr, remote_closer); - - // define default fetch options - git_fetch_options fetch_opts{}; - git_fetch_options_init(&fetch_opts, GIT_FETCH_OPTIONS_VERSION); - - // 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(repo_.get()); - - // disable update of the FETCH_HEAD pointer - fetch_opts.update_fetchhead = 0; - - // setup fetch refspecs array - git_strarray refspecs_array_obj{}; - if (branch) { - // make sure we check for tags as well - std::string tag = fmt::format("+refs/tags/{}", *branch); - std::string head = fmt::format("+refs/heads/{}", *branch); - PopulateStrarray(&refspecs_array_obj, {tag, head}); - } - auto refspecs_array = - std::unique_ptr( - &refspecs_array_obj, strarray_deleter); - - if (git_remote_fetch( - remote.get(), refspecs_array.get(), &fetch_opts, nullptr) != - 0) { - (*logger)( - fmt::format("fetching{} in git repository {} failed " - "with:\n{}", - branch ? fmt::format(" branch {}", *branch) : "", - GetGitCAS()->git_path_.string(), - GitLastError()), - true /*fatal*/); - return false; - } - return true; // success! - } catch (std::exception const& ex) { - Logger::Log( - LogLevel::Error, "fetch from remote failed with:\n{}", ex.what()); - return false; - } -#endif // BOOTSTRAP_BUILD_TOOL -} - auto GitRepo::GetSubtreeFromCommit(std::string const& commit, std::string const& subdir, anon_logger_ptr const& logger) noexcept @@ -1183,89 +897,6 @@ auto GitRepo::CheckCommitExists(std::string const& commit, #endif // BOOTSTRAP_BUILD_TOOL } -auto GitRepo::UpdateCommitViaTmpRepo(std::filesystem::path const& tmp_repo_path, - std::string const& repo_url, - std::string const& branch, - anon_logger_ptr const& logger) - const noexcept -> std::optional { -#ifdef BOOTSTRAP_BUILD_TOOL - return std::nullopt; -#else - try { - // preferably with a "fake" repository! - if (not IsRepoFake()) { - (*logger)("WARNING: commit update called on a real repository!\n", - false /*fatal*/); - } - // create the temporary real repository - auto tmp_repo = GitRepo::InitAndOpen(tmp_repo_path, /*is_bare=*/true); - if (tmp_repo == std::nullopt) { - return std::nullopt; - } - // setup wrapped logger - auto wrapped_logger = std::make_shared( - [logger](auto const& msg, bool fatal) { - (*logger)( - fmt::format("While doing commit update via tmp repo:\n{}", - msg), - fatal); - }); - return tmp_repo->GetCommitFromRemote(repo_url, branch, wrapped_logger); - } catch (std::exception const& ex) { - Logger::Log(LogLevel::Error, - "update commit via tmp repo failed with:\n{}", - ex.what()); - return std::nullopt; - } -#endif // BOOTSTRAP_BUILD_TOOL -} - -auto GitRepo::FetchViaTmpRepo(std::filesystem::path const& tmp_repo_path, - std::string const& repo_url, - std::optional const& branch, - anon_logger_ptr const& logger) noexcept -> bool { -#ifdef BOOTSTRAP_BUILD_TOOL - return false; -#else - try { - // preferably with a "fake" repository! - if (not IsRepoFake()) { - (*logger)("WARNING: branch fetch called on a real repository!\n", - false /*fatal*/); - } - // create the temporary real repository - // it can be bare, as the refspecs for this fetch will be given - // explicitly. - auto tmp_repo = GitRepo::InitAndOpen(tmp_repo_path, /*is_bare=*/true); - if (tmp_repo == std::nullopt) { - return false; - } - // add backend, with max priority - FetchIntoODBBackend b{kFetchIntoODBParent, git_cas_->odb_.get()}; - if (git_odb_add_backend( - tmp_repo->GetGitCAS()->odb_.get(), - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - reinterpret_cast(&b), - std::numeric_limits::max()) == 0) { - // setup wrapped logger - auto wrapped_logger = std::make_shared( - [logger](auto const& msg, bool fatal) { - (*logger)( - fmt::format( - "While doing branch fetch via tmp repo:\n{}", msg), - fatal); - }); - return tmp_repo->FetchFromRemote(repo_url, branch, wrapped_logger); - } - return false; - } catch (std::exception const& ex) { - Logger::Log( - LogLevel::Error, "fetch via tmp repo failed with:\n{}", ex.what()); - return false; - } -#endif // BOOTSTRAP_BUILD_TOOL -} - auto GitRepo::GetRepoRootFromPath(std::filesystem::path const& fpath, anon_logger_ptr const& logger) noexcept -> std::optional { -- cgit v1.2.3