diff options
author | Paul Cristian Sarbu <paul.cristian.sarbu@huawei.com> | 2023-03-21 14:58:54 +0100 |
---|---|---|
committer | Paul Cristian Sarbu <paul.cristian.sarbu@huawei.com> | 2023-03-23 09:50:42 +0100 |
commit | 12d0c288ea23e1bd7e0401223ba413bca3d94869 (patch) | |
tree | b6e16385368863b11ba8656b7b651440c9f5783b /src | |
parent | 17143ed02545efab55c175db66a9e31db4cce0f0 (diff) | |
download | justbuild-12d0c288ea23e1bd7e0401223ba413bca3d94869.tar.gz |
just-mr: Shell out to system Git for update commit from SSH remote...
...due to limitations in SSH support in libgit2. In this case, we
simply execute 'git ls-remote <repo> [<branch>]' and then parse
the output. Remote interogation requires no local repository, so
it is an asynchronious operation by default.
Diffstat (limited to 'src')
-rw-r--r-- | src/other_tools/git_operations/git_repo_remote.cpp | 118 | ||||
-rw-r--r-- | src/other_tools/git_operations/git_repo_remote.hpp | 13 | ||||
-rw-r--r-- | src/other_tools/ops_maps/git_update_map.cpp | 22 | ||||
-rw-r--r-- | src/other_tools/ops_maps/git_update_map.hpp | 1 |
4 files changed, 111 insertions, 43 deletions
diff --git a/src/other_tools/git_operations/git_repo_remote.cpp b/src/other_tools/git_operations/git_repo_remote.cpp index 6cff2a82..ab9d7ce2 100644 --- a/src/other_tools/git_operations/git_repo_remote.cpp +++ b/src/other_tools/git_operations/git_repo_remote.cpp @@ -387,46 +387,106 @@ auto GitRepoRemote::FetchFromRemote(std::shared_ptr<git_config> cfg, } auto GitRepoRemote::UpdateCommitViaTmpRepo( - std::filesystem::path const& tmp_repo_path, + std::filesystem::path const& tmp_dir, std::string const& repo_url, std::string const& branch, + std::vector<std::string> const& launcher, anon_logger_ptr const& logger) const noexcept -> std::optional<std::string> { try { - // preferably with a "fake" repository! - if (not IsRepoFake()) { - Logger::Log(LogLevel::Debug, - "Commit update called on a real repository"); + // check for internally supported protocols + if (IsSupported(repo_url)) { + // preferably with a "fake" repository! + if (not IsRepoFake()) { + Logger::Log(LogLevel::Debug, + "Commit update called on a real repository"); + } + // create the temporary real repository + auto tmp_repo = + GitRepoRemote::InitAndOpen(tmp_dir, /*is_bare=*/true); + if (tmp_repo == std::nullopt) { + return std::nullopt; + } + // setup wrapped logger + auto wrapped_logger = std::make_shared<anon_logger_t>( + [logger](auto const& msg, bool fatal) { + (*logger)( + fmt::format( + "While doing commit update via tmp repo:\n{}", msg), + fatal); + }); + // 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); + } + // default to shelling out to git for non-explicitly supported protocols + auto cmdline = launcher; + cmdline.insert(cmdline.end(), {"git", "ls-remote", repo_url, branch}); + // set up the system command + SystemCommand system{repo_url}; + auto const command_output = + system.Execute(cmdline, + {}, // default env + GetGitPath(), // which path is not actually relevant + tmp_dir); + // output file can be read anyway + std::string out_str{}; + auto cmd_out = FileSystemManager::ReadFile(command_output->stdout_file); + if (cmd_out) { + out_str = *cmd_out; } - // create the temporary real repository - auto tmp_repo = - GitRepoRemote::InitAndOpen(tmp_repo_path, /*is_bare=*/true); - if (tmp_repo == std::nullopt) { + // check for command failure + if (not command_output) { + std::string err_str{}; + auto cmd_err = + FileSystemManager::ReadFile(command_output->stderr_file); + + if (cmd_err) { + err_str = *cmd_err; + } + std::string output{}; + if (!out_str.empty() || !err_str.empty()) { + output = fmt::format(" with output:\n{}{}", out_str, err_str); + } + (*logger)(fmt::format("List remote commits command {} failed{}", + nlohmann::json(cmdline).dump(), + output), + /*fatal=*/true); return std::nullopt; } - // setup wrapped logger - auto wrapped_logger = std::make_shared<anon_logger_t>( - [logger](auto const& msg, bool fatal) { - (*logger)( - fmt::format("While doing commit update via tmp repo:\n{}", - msg), - fatal); - }); - // 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*/); + // report failure to read generated output file or the file is empty + if (out_str.empty()) { + (*logger)(fmt::format("List remote commits command {} failed to " + "produce an output", + nlohmann::json(cmdline).dump()), + /*fatal=*/true); + return std::nullopt; } - return tmp_repo->GetCommitFromRemote( - cfg, repo_url, branch, wrapped_logger); + // parse the output: it should contain two tab-separated columns, + // with the commit being the first entry + auto str_len = out_str.find('\t'); + if (str_len == std::string::npos) { + (*logger)(fmt::format("List remote commits command {} produced " + "malformed output:\n{}", + out_str), + /*fatal=*/true); + return std::nullopt; + } + // success! + return out_str.substr(0, str_len); } catch (std::exception const& ex) { - Logger::Log(LogLevel::Error, - "update commit via tmp repo failed with:\n{}", - ex.what()); + Logger::Log( + LogLevel::Error, "Update commit failed with:\n{}", ex.what()); return std::nullopt; } } diff --git a/src/other_tools/git_operations/git_repo_remote.hpp b/src/other_tools/git_operations/git_repo_remote.hpp index 64c827c4..9b68ada9 100644 --- a/src/other_tools/git_operations/git_repo_remote.hpp +++ b/src/other_tools/git_operations/git_repo_remote.hpp @@ -73,18 +73,21 @@ class GitRepoRemote : public GitRepo { anon_logger_ptr const& logger) noexcept -> bool; - /// \brief Get commit from remote via a temporary repository. - /// Calling it from a fake repository allows thread-safe use. - /// Creates a temporary real repository at the given location and uses it to - /// retrieve from the remote the commit of a branch given its name. + /// \brief Get commit from given branch on the remote. If URL is SSH, shells + /// out to system git to perform an ls-remote call, ensuring correct + /// handling of the remote connection settings (in particular proxy and + /// SSH). A temporary directory is needed to pipe the stdout and stderr to. + /// If URL is non-SSH, uses tmp dir to connect to remote and retrieve the + /// commit of a branch asynchronously using libgit2. /// Caller needs to make sure the temporary directory exists and that the /// given path is thread- and process-safe! /// Returns the commit hash, as a string, or nullopt if failure. /// It guarantees the logger is called exactly once with fatal if failure. [[nodiscard]] auto UpdateCommitViaTmpRepo( - std::filesystem::path const& tmp_repo_path, + std::filesystem::path const& tmp_dir, std::string const& repo_url, std::string const& branch, + std::vector<std::string> const& launcher, anon_logger_ptr const& logger) const noexcept -> std::optional<std::string>; diff --git a/src/other_tools/ops_maps/git_update_map.cpp b/src/other_tools/ops_maps/git_update_map.cpp index c4305e69..f096d3a9 100644 --- a/src/other_tools/ops_maps/git_update_map.cpp +++ b/src/other_tools/ops_maps/git_update_map.cpp @@ -21,13 +21,14 @@ #include "src/other_tools/just_mr/utils.hpp" #include "src/utils/cpp/tmp_dir.hpp" -auto CreateGitUpdateMap(GitCASPtr const& git_cas, std::size_t jobs) - -> GitUpdateMap { - auto update_commits = [git_cas](auto /* unused */, - auto setter, - auto logger, - auto /* unused */, - auto const& key) { +auto CreateGitUpdateMap(GitCASPtr const& git_cas, + std::vector<std::string> const& launcher, + std::size_t jobs) -> GitUpdateMap { + auto update_commits = [git_cas, launcher](auto /* unused */, + auto setter, + auto logger, + auto /* unused */, + auto const& key) { // perform git update commit auto git_repo = GitRepoRemote::Open(git_cas); // wrap the tmp odb if (not git_repo) { @@ -55,8 +56,11 @@ auto CreateGitUpdateMap(GitCASPtr const& git_cas, std::size_t jobs) // update commit auto id = fmt::format("{}:{}", key.first, key.second); JustMRProgress::Instance().TaskTracker().Start(id); - auto new_commit = git_repo->UpdateCommitViaTmpRepo( - tmp_dir->GetPath(), key.first, key.second, wrapped_logger); + auto new_commit = git_repo->UpdateCommitViaTmpRepo(tmp_dir->GetPath(), + key.first, + key.second, + launcher, + wrapped_logger); JustMRProgress::Instance().TaskTracker().Stop(id); if (not new_commit) { return; diff --git a/src/other_tools/ops_maps/git_update_map.hpp b/src/other_tools/ops_maps/git_update_map.hpp index d4804545..93a5da30 100644 --- a/src/other_tools/ops_maps/git_update_map.hpp +++ b/src/other_tools/ops_maps/git_update_map.hpp @@ -38,6 +38,7 @@ struct hash<StringPair> { } // namespace std [[nodiscard]] auto CreateGitUpdateMap(GitCASPtr const& git_cas, + std::vector<std::string> const& launcher, std::size_t jobs) -> GitUpdateMap; #endif // INCLUDED_SRC_OTHER_TOOLS_OPS_MAPS_GIT_UPDATE_MAP_HPP
\ No newline at end of file |