From 26e903ea7a9e96d87b24aebe4e3b3146091b97e4 Mon Sep 17 00:00:00 2001 From: Soo Hwan Na Date: Sun, 7 Jul 2024 21:54:05 +0900 Subject: [PATCH] TgBot++: android_builder: Improve sync error matching --- src/android_builder/tasks/RepoSyncTask.cpp | 69 ++++++++++++++++++---- src/android_builder/tasks/RepoSyncTask.hpp | 17 ++++-- 2 files changed, 69 insertions(+), 17 deletions(-) diff --git a/src/android_builder/tasks/RepoSyncTask.cpp b/src/android_builder/tasks/RepoSyncTask.cpp index ddd047a9..3101a6b9 100644 --- a/src/android_builder/tasks/RepoSyncTask.cpp +++ b/src/android_builder/tasks/RepoSyncTask.cpp @@ -11,6 +11,7 @@ #include #include "CStringLifetime.h" +#include "git2/types.h" #include "tasks/PerBuildData.hpp" namespace { @@ -20,6 +21,25 @@ struct RAIIGit { void addCleanup(std::function cleanupFn) { cleanups.emplace_back(cleanupFn); } + void add_repo_cleanup(git_repository* repo) { + addCleanup([repo] { git_repository_free(repo); }); + } + void add_ref_cleanup(git_reference* ref) { + addCleanup([ref] { git_reference_free(ref); }); + } + void add_commit_cleanup(git_commit* commit) { + addCleanup([commit] { git_commit_free(commit); }); + } + void add_tree_cleanup(git_tree* tree) { + addCleanup([tree] { git_tree_free(tree); }); + } + void add_object_cleanup(git_object* obj) { + addCleanup([obj] { git_object_free(obj); }); + } + void add_remote_cleanup(git_remote* remote) { + addCleanup([remote] { git_remote_free(remote); }); + } + RAIIGit() { git_libgit2_init(); } ~RAIIGit() { for (auto& cleanup : cleanups) { @@ -53,7 +73,7 @@ bool tryToMakeItMine(const PerBuildData& data) { LOG(ERROR) << "Failed to open repository: " << git_error_last_str(); return false; } - raii.addCleanup([repo] { git_repository_free(repo); }); + raii.add_repo_cleanup(repo); ret = git_remote_lookup(&remote, repo, kRemoteRepoName.data()); if (ret != 0) { @@ -61,7 +81,7 @@ bool tryToMakeItMine(const PerBuildData& data) { << git_error_last_str(); return false; } - raii.addCleanup([remote] { git_remote_free(remote); }); + raii.add_remote_cleanup(remote); remote_url = git_remote_url(remote); if (remote_url == nullptr) { @@ -80,7 +100,7 @@ bool tryToMakeItMine(const PerBuildData& data) { LOG(ERROR) << "Failed to get HEAD reference: " << git_error_last_str(); return false; } - raii.addCleanup([head_ref] { git_reference_free(head_ref); }); + raii.add_ref_cleanup(head_ref); current_branch = git_reference_shorthand(head_ref); LOG(INFO) << "Current branch: " << current_branch; @@ -125,8 +145,7 @@ bool tryToMakeItMine(const PerBuildData& data) { << git_error_last_str(); return false; } - raii.addCleanup( - [target_remote_ref] { git_reference_free(target_remote_ref); }); + raii.add_ref_cleanup(target_remote_ref); // Get the commit of the target branch ref ret = git_commit_lookup(&commit, repo, @@ -136,7 +155,7 @@ bool tryToMakeItMine(const PerBuildData& data) { << git_error_last_str(); return false; } - raii.addCleanup([commit] { git_commit_free(commit); }); + raii.add_commit_cleanup(commit); // Create the local branch ref pointing to the commit ret = git_branch_create(&target_ref, repo, branch_name.get(), @@ -146,7 +165,7 @@ bool tryToMakeItMine(const PerBuildData& data) { << git_error_last_str(); return false; } - raii.addCleanup([target_ref] { git_reference_free(target_ref); }); + raii.add_ref_cleanup(target_ref); // Checkout the local branch ret = git_checkout_head(repo, &checkout_opts); @@ -158,7 +177,8 @@ bool tryToMakeItMine(const PerBuildData& data) { LOG(INFO) << "Success on checking out remote branch"; } else { - raii.addCleanup([target_ref] { git_reference_free(target_ref); }); + // Switching to the branch directly + raii.add_ref_cleanup(target_ref); // Get the object of the target branch ref ret = git_reference_peel(&treeish, target_ref, GIT_OBJECT_TREE); @@ -167,7 +187,7 @@ bool tryToMakeItMine(const PerBuildData& data) { << git_error_last_str(); return false; } - raii.addCleanup([treeish] { git_object_free(treeish); }); + raii.add_object_cleanup(treeish); ret = git_checkout_tree(repo, treeish, nullptr); if (ret != 0) { @@ -189,6 +209,11 @@ bool tryToMakeItMine(const PerBuildData& data) { } // namespace bool RepoSyncLocalHook::process(const std::string& line) { + static const std::regex kRepoRemoveFail( + "error: ([a-zA-Z0-9_\\-\\/]+): Cannot remove project: " + "uncommitted changes are present\\."); + std::smatch smatch; + if (line.find(kUpdatingFiles) != std::string::npos) { // Stop logspam return true; @@ -207,6 +232,25 @@ bool RepoSyncLocalHook::process(const std::string& line) { return true; } + if (std::regex_search(line, smatch, kRepoRemoveFail)) { + std::error_code ec; + errorAndLog("Repo sync failed due to local issue: RemoveFail"); + hasRemoveIssues = true; + hadProblems = true; + if (smatch.size() > 1) { + std::string directory = smatch[1].str(); + LOG(WARNING) << "Will remove directory: " << directory; + std::filesystem::remove_all(directory, ec); + if (ec) { + errorAndLog("Failed to remove: " + directory + ": " + + ec.message()); + hadFatalProblems = true; + } else { + LOG(INFO) << "Successfully removed"; + } + } + } + if (hasCheckoutIssues) { std::error_code ec; if (line.find(kRepoCheckoutFailEnd) != std::string::npos) { @@ -229,7 +273,10 @@ bool RepoSyncLocalHook::process(const std::string& line) { bool RepoSyncNetworkHook::process(const std::string& line) { static const std::regex kSyncErrorNetworkRegex( R"(^error:\s+Cannot\s+fetch\s+[^\s]+(?:/[^\s]+)*\s+from\s+https:\/\/.+$)"); - if (std::regex_match(line, kSyncErrorNetworkRegex)) { + static const std::regex kSyncErrorNetworkRegex2( + R"(^Failed to connect to (github\.com|gitlab\.com) port \d+ after \d+ ms: Couldn't connect to server$)"); + if (std::regex_match(line, kSyncErrorNetworkRegex) || + std::regex_match(line, kSyncErrorNetworkRegex2)) { LOG(INFO) << "Detected sync issue, caused by network"; hadProblems = true; return true; @@ -284,7 +331,7 @@ void RepoSyncTask::onNewStderrBuffer(ForkAndRun::BufferType& buffer) { if (networkHook.process(line)) { continue; } - std::cerr << "Repo sync stderr: " << line << std::endl; + // std::cerr << "Repo sync stderr: " << line << std::endl; } } diff --git a/src/android_builder/tasks/RepoSyncTask.hpp b/src/android_builder/tasks/RepoSyncTask.hpp index 35e8949a..8b61f019 100644 --- a/src/android_builder/tasks/RepoSyncTask.hpp +++ b/src/android_builder/tasks/RepoSyncTask.hpp @@ -1,6 +1,7 @@ #include #include #include + #include "PerBuildData.hpp" class NewStdErrBufferHook { @@ -9,7 +10,7 @@ class NewStdErrBufferHook { protected: bool hadProblems = false; bool hadFatalProblems = false; - + void errorAndLog(const std::string& message) { LOG(ERROR) << message; logMessage << message << std::endl; @@ -36,9 +37,7 @@ class NewStdErrBufferHook { [[nodiscard]] std::string getLogMessage() const noexcept { return logMessage.str(); } - [[nodiscard]] bool hasProblems() const noexcept { - return hadProblems; - } + [[nodiscard]] bool hasProblems() const noexcept { return hadProblems; } [[nodiscard]] bool hasFatalProblems() const noexcept { return hadProblems && hadFatalProblems; } @@ -60,6 +59,8 @@ class RepoSyncLocalHook : public NewStdErrBufferHook { "Try re-running with \"-j1 --fail-fast\" to exit at the first error."; bool hasCheckoutIssues = false; + bool hasRemoveIssues = false; + public: bool process(const std::string& line) override; ~RepoSyncLocalHook() override = default; @@ -86,7 +87,7 @@ struct RepoSyncTask : ForkAndRun { * otherwise. */ bool runFunction() override; - + /** * @brief Handles new standard error data. * @@ -98,6 +99,10 @@ struct RepoSyncTask : ForkAndRun { */ void onNewStderrBuffer(ForkAndRun::BufferType& buffer) override; + void onNewStdoutBuffer(ForkAndRun::BufferType& buffer) override { + onNewStderrBuffer(buffer); + } + /** * @brief Handles the process exit event. * @@ -130,7 +135,7 @@ struct RepoSyncTask : ForkAndRun { private: PerBuildData data; - + RepoSyncLocalHook localHook; RepoSyncNetworkHook networkHook; }; \ No newline at end of file