LCOV - code coverage report
Current view: top level - src/esys/repo/exe - cmdsync.cpp (source / functions) Hit Total Coverage
Test: esysrepo_coverage.info Lines: 70 242 28.9 %
Date: 2026-08-01 10:43:40 Functions: 10 25 40.0 %

          Line data    Source code
       1             : /*!
       2             :  * \file esys/repo/exe/cmdsync.cpp
       3             :  * \brief
       4             :  *
       5             :  * \cond
       6             :  * __legal_b__
       7             :  *
       8             :  * Copyright (c) 2020-2022 Michel Gillet
       9             :  * Distributed under the MIT License.
      10             :  * (See accompanying file LICENSE.txt or
      11             :  * copy at https://opensource.org/licenses/MIT)
      12             :  *
      13             :  * __legal_e__
      14             :  * \endcond
      15             :  *
      16             :  */
      17             : 
      18             : #include "esys/repo/esysrepo_prec.h"
      19             : #include "esys/repo/exe/cmdsync.h"
      20             : #include "esys/repo/manifest/capture.h"
      21             : #include "esys/repo/manifest/sync.h"
      22             : #include "esys/repo/manifest/syncrepos.h"
      23             : #include "esys/repo/resultcode.h"
      24             : #include "esys/repo/tui/is_tty.h"
      25             : #include "esys/repo/tui/run_compact_sync_tui.h"
      26             : 
      27             : #include <esys/trace/call.h>
      28             : #include <esys/trace/macros.h>
      29             : 
      30             : #include <boost/filesystem.hpp>
      31             : 
      32             : #include <cstdlib>
      33             : #include <cstring>
      34             : #include <iostream>
      35             : #include <sstream>
      36             : 
      37             : namespace esys::repo::exe
      38             : {
      39             : 
      40             : namespace
      41             : {
      42             : 
      43          20 : bool env_flag_enabled(const char *name)
      44             : {
      45          20 :     const char *value = std::getenv(name);
      46          20 :     if (value == nullptr || value[0] == '\0') return false;
      47           0 :     if (std::strcmp(value, "1") == 0) return true;
      48           0 :     if (std::strcmp(value, "true") == 0) return true;
      49           0 :     if (std::strcmp(value, "TRUE") == 0) return true;
      50           0 :     if (std::strcmp(value, "yes") == 0) return true;
      51           0 :     if (std::strcmp(value, "YES") == 0) return true;
      52           0 :     if (std::strcmp(value, "on") == 0) return true;
      53           0 :     if (std::strcmp(value, "ON") == 0) return true;
      54             :     return false;
      55             : }
      56             : 
      57             : } // namespace
      58             : 
      59          30 : CmdSync::CmdSync()
      60          60 :     : Cmd("Sync")
      61             : {
      62          30 : }
      63             : 
      64          30 : CmdSync::~CmdSync() = default;
      65             : 
      66           0 : void CmdSync::set_force(bool force)
      67             : {
      68           0 :     m_force = force;
      69           0 : }
      70             : 
      71          20 : bool CmdSync::get_force() const
      72             : {
      73          20 :     return m_force;
      74             : }
      75             : 
      76           2 : void CmdSync::set_branch(const std::string &branch)
      77             : {
      78           2 :     m_branch = branch;
      79           2 : }
      80             : 
      81          44 : const std::string &CmdSync::get_branch() const
      82             : {
      83          44 :     return m_branch;
      84             : }
      85             : 
      86           0 : void CmdSync::set_alt_address(bool alt_address)
      87             : {
      88           0 :     m_alt_address = alt_address;
      89           0 : }
      90           0 : bool CmdSync::get_alt_address() const
      91             : {
      92           0 :     return m_alt_address;
      93             : }
      94             : 
      95           0 : void CmdSync::set_multi(bool multi)
      96             : {
      97           0 :     m_multi = multi;
      98           0 : }
      99             : 
     100          20 : bool CmdSync::get_multi() const
     101             : {
     102          20 :     return m_multi;
     103             : }
     104             : 
     105           0 : void CmdSync::set_tui(bool tui)
     106             : {
     107           0 :     m_tui = tui;
     108           0 : }
     109             : 
     110          20 : bool CmdSync::get_tui() const
     111             : {
     112          20 :     return m_tui;
     113             : }
     114             : 
     115           0 : void CmdSync::set_tui_demo(bool tui_demo)
     116             : {
     117           0 :     m_tui_demo = tui_demo;
     118           0 :     if (tui_demo) m_tui = true;
     119           0 : }
     120             : 
     121           0 : bool CmdSync::get_tui_demo() const
     122             : {
     123           0 :     return m_tui_demo;
     124             : }
     125             : 
     126          20 : Result CmdSync::sync_manifest()
     127             : {
     128          20 :     Result result;
     129          20 :     ETRC_CALL_RET_NP(result);
     130             : 
     131          20 :     manifest::Sync sync;
     132             : 
     133          40 :     if (get_config_folder() == nullptr) return ETRC_RET(ESYSREPO_RESULT(ResultCode::CMD_CONFIG_FOLDER_NULLPTR));
     134             : 
     135          20 :     sync.set_config_folder(get_config_folder());
     136          20 :     sync.set_git(get_git());
     137          20 :     sync.set_logger_if(get_logger_if());
     138          20 :     if (!get_branch().empty()) sync.set_branch(get_branch());
     139             : 
     140          40 :     return ETRC_RET(ESYSREPO_RESULT(sync.run()));
     141          20 : }
     142             : 
     143           0 : Result CmdSync::handle_capture_item(std::shared_ptr<manifest::Capture::Item> item)
     144             : {
     145           0 :     Result result;
     146           0 :     ETRC_CALL_RET(result, item);
     147             : 
     148           0 :     if (get_workspace_path().empty()) return ETRC_RET(ESYSREPO_RESULT(ResultCode::CMD_WORKSPACE_PATH_IS_EMPTY));
     149             : 
     150           0 :     boost::filesystem::path git_path_folder = get_workspace_path();
     151           0 :     git_path_folder /= item->get_path();
     152           0 :     git_path_folder = git_path_folder.lexically_normal().make_preferred();
     153             : 
     154           0 :     auto git_helper = new_git_helper();
     155           0 :     result = git_helper->open(git_path_folder.string(), log::Level::DEBUG);
     156           0 :     if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
     157             : 
     158           0 :     GitHelper::AutoClose auto_close(git_helper.get());
     159             : 
     160           0 :     bool dirty = false;
     161           0 :     result = git_helper->is_dirty(dirty, log::Level::DEBUG);
     162           0 :     if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
     163             : 
     164             :     // If there is a remote defined, create it and fetch it
     165           0 :     if (!item->get_remote_name().empty())
     166             :     {
     167           0 :         result = git_helper->get_git()->add_remote(item->get_remote_name(), item->get_remote_url());
     168           0 :         if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
     169             : 
     170           0 :         result = git_helper->get_git()->fetch(item->get_remote_name());
     171           0 :         if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
     172             :     }
     173             : 
     174             :     // Checkout the new revision
     175           0 :     result = git_helper->checkout(item->get_revision(), false, log::Level::DEBUG);
     176           0 :     if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
     177             : 
     178           0 :     return ETRC_RET(ESYSREPO_RESULT(ResultCode::OK));
     179           0 : }
     180             : 
     181           0 : Result CmdSync::do_multi()
     182             : {
     183           0 :     Result result;
     184           0 :     ETRC_CALL_RET_NP(result);
     185             : 
     186             :     // First we need to get the working folder
     187           0 :     std::string folder_path;
     188             : 
     189           0 :     if (!get_folder().empty())
     190           0 :         folder_path = get_folder();
     191           0 :     else if (!get_workspace_path().empty())
     192           0 :         folder_path = get_workspace_path();
     193             :     else
     194           0 :         folder_path = boost::filesystem::current_path().string();
     195           0 :     if (folder_path.empty()) return ETRC_RET(ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR));
     196             : 
     197           0 :     auto git_folder_path = find_git_root_path(folder_path);
     198           0 :     auto git_helper = new_git_helper();
     199             : 
     200           0 :     set_git_folder_path(git_folder_path);
     201           0 :     result = git_helper->open(git_folder_path, log::Level::DEBUG);
     202           0 :     if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
     203             : 
     204           0 :     GitHelper::AutoClose auto_close(git_helper.get());
     205             : 
     206           0 :     int result_int = git_helper->get_git()->add_notes_ref("esysrepo");
     207           0 :     if (result_int < 0) return ETRC_RET(ESYSREPO_RESULT(ResultCode::GIT_RAW_INT_ERROR, result_int));
     208             : 
     209           0 :     result = git_helper->get_git()->fetch_all_notes();
     210           0 :     if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
     211             : 
     212           0 :     git::Commit last_commit;
     213             : 
     214           0 :     result = git_helper->get_git()->get_last_commit(last_commit, true);
     215           0 :     if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
     216             : 
     217           0 :     std::vector<std::shared_ptr<git::Note>> notes;
     218           0 :     result_int = last_commit.get_notes("esysrepo", notes);
     219           0 :     if (result_int < 0) return ETRC_RET(ESYSREPO_RESULT(ResultCode::GIT_RAW_INT_ERROR, result_int));
     220             : 
     221           0 :     std::ostringstream oss;
     222             : 
     223           0 :     oss << "Found " << notes.size() << " esysrepo multi note.";
     224             : 
     225           0 :     info(oss.str());
     226             : 
     227           0 :     if (notes.empty()) return ETRC_RET(ESYSREPO_RESULT(ResultCode::OK));
     228           0 :     if (notes.size() != 1) return ETRC_RET(ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR));
     229             : 
     230           0 :     std::string note_text = notes[0]->get_message();
     231           0 :     auto capture = std::make_shared<manifest::Capture>();
     232           0 :     result = load_capture(capture, note_text);
     233           0 :     if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
     234             : 
     235           0 :     oss.str("");
     236           0 :     print_capture(oss, capture);
     237           0 :     info(oss.str());
     238             : 
     239           0 :     Result rresult;
     240           0 :     for (auto repo : capture->get_items())
     241             :     {
     242           0 :         result = handle_capture_item(repo);
     243           0 :         if (result.error())
     244             :         {
     245           0 :             std::ostringstream e_oss;
     246             : 
     247           0 :             e_oss << "repo '" << repo->get_path() << "' could set revivions '" << repo->get_revision() << "'";
     248           0 :             error(e_oss.str());
     249           0 :             rresult.add(result);
     250           0 :         }
     251           0 :     }
     252             : 
     253           0 :     return ETRC_RET(ESYSREPO_RESULT(rresult));
     254           0 : }
     255             : 
     256           0 : void CmdSync::set_git_folder_path(const std::string &git_folder_path)
     257             : {
     258           0 :     m_git_folder_path = git_folder_path;
     259           0 : }
     260             : 
     261           0 : const std::string &CmdSync::get_git_folder_path() const
     262             : {
     263           0 :     return m_git_folder_path;
     264             : }
     265             : 
     266          20 : Result CmdSync::impl_run()
     267             : {
     268          20 :     Result result;
     269          20 :     ETRC_CALL_RET_NP(result);
     270             : 
     271          20 :     if (env_flag_enabled("ESYSREPO_TUI_DEMO")) set_tui_demo(true);
     272             : 
     273          20 :     if (get_tui()) return ETRC_RET(ESYSREPO_RESULT(impl_run_tui()));
     274             : 
     275          20 :     if (get_force()) warn("Option --force-sync is not implemented yet");
     276             : 
     277          20 :     result = default_handling_folder_workspace();
     278          20 :     if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
     279             : 
     280          20 :     result = open_esysrepo_folder();
     281          20 :     if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
     282             : 
     283          20 :     result = sync_manifest();
     284          20 :     if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
     285             : 
     286          20 :     result = load_manifest();
     287          20 :     if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
     288             : 
     289          20 :     manifest::SyncRepos sync_repos;
     290             : 
     291          20 :     if (!get_branch().empty()) sync_repos.set_branch(get_branch());
     292             : 
     293          20 :     if (get_sub_args().size() != 0)
     294           4 :         sync_repos.set_folders_to_sync(get_sub_args());
     295          16 :     else if (get_groups().size() != 0)
     296             :     {
     297           2 :         std::vector<std::string> repos;
     298             : 
     299           4 :         for (auto &group : get_groups())
     300             :         {
     301           2 :             auto group_ptr = get_manifest()->get_groups().find_group_by_name(group);
     302           2 :             if (group_ptr == nullptr)
     303             :             {
     304             :                 //! \TODO
     305           0 :                 continue;
     306             :             }
     307           8 :             for (auto repo : group_ptr->get_repos())
     308             :             {
     309           6 :                 repos.push_back(repo->get_path());
     310           6 :             }
     311           2 :         }
     312             : 
     313           2 :         sync_repos.set_folders_to_sync(repos);
     314           2 :     }
     315             : 
     316          20 :     if (get_debug()) sync_repos.set_log_level(log::Level::DEBUG);
     317             : 
     318          20 :     sync_repos.set_job_count(get_job_count());
     319          20 :     sync_repos.set_logger_if(get_logger_if());
     320          20 :     sync_repos.set_config_folder(get_config_folder());
     321          20 :     sync_repos.set_git(get_git());
     322          20 :     sync_repos.set_manifest(get_manifest());
     323          20 :     sync_repos.set_tui(false);
     324             : 
     325          20 :     get_git()->detect_ssh_agent(true);
     326             : 
     327          20 :     result = sync_repos.run();
     328          20 :     if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
     329             : 
     330          40 :     if (!get_multi()) return ETRC_RET(ESYSREPO_RESULT(ResultCode::OK));
     331           0 :     return ETRC_RET(ESYSREPO_RESULT(do_multi()));
     332          20 : }
     333             : 
     334           0 : Result CmdSync::impl_run_tui()
     335             : {
     336           0 :     Result result;
     337           0 :     ETRC_CALL_RET_NP(result);
     338             : 
     339             : #ifndef ESYSREPO_HAVE_TUI
     340             :     error("--tui requested but esysrepo was built without FTXUI (Conan with_tui / ESYSREPO_WITH_TUI)");
     341             :     return ETRC_RET(ESYSREPO_RESULT(ResultCode::NOT_IMPLEMENTED));
     342             : #else
     343           0 :     if (!tui::is_tty())
     344             :     {
     345           0 :         const char *msg =
     346             :             "--tui requires a real Win32 console (cmd.exe, PowerShell, or Windows "
     347             :             "Terminal). Git Bash/mintty and redirected stdout are not supported.";
     348           0 :         error(msg);
     349           0 :         std::cerr << "ERROR: " << msg << std::endl;
     350           0 :         return ETRC_RET(ESYSREPO_RESULT(ResultCode::GENERIC_ERROR));
     351             :     }
     352             : 
     353           0 :     if (get_force()) warn("Option --force-sync is not implemented yet");
     354             : 
     355           0 :     const std::size_t ui_workers =
     356           0 :         get_job_count() <= 0 ? std::size_t{1} : static_cast<std::size_t>(get_job_count());
     357             : 
     358           0 :     auto work = [this](progress::ProgressSession &session, tui::SyncTuiControl &control) -> Result {
     359           0 :         auto prev_logger = get_logger_if();
     360           0 :         if (control.logger) set_logger_if(control.logger);
     361             : 
     362           0 :         auto restore_logger = [&]() {
     363           0 :             if (prev_logger) set_logger_if(prev_logger);
     364           0 :         };
     365             : 
     366           0 :         if (control.set_phase) control.set_phase("Loading workspace…");
     367             : 
     368           0 :         Result phase = default_handling_folder_workspace();
     369           0 :         if (phase.error())
     370             :         {
     371           0 :             restore_logger();
     372             :             return phase;
     373             :         }
     374             : 
     375           0 :         phase = open_esysrepo_folder();
     376           0 :         if (phase.error())
     377             :         {
     378           0 :             restore_logger();
     379             :             return phase;
     380             :         }
     381             : 
     382           0 :         if (control.set_phase) control.set_phase("Loading manifest…");
     383             : 
     384           0 :         phase = sync_manifest();
     385           0 :         if (phase.error())
     386             :         {
     387           0 :             restore_logger();
     388             :             return phase;
     389             :         }
     390             : 
     391           0 :         phase = load_manifest();
     392           0 :         if (phase.error())
     393             :         {
     394           0 :             restore_logger();
     395             :             return phase;
     396             :         }
     397             : 
     398           0 :         manifest::SyncRepos sync_repos;
     399             : 
     400           0 :         if (!get_branch().empty()) sync_repos.set_branch(get_branch());
     401             : 
     402           0 :         if (get_sub_args().size() != 0)
     403           0 :             sync_repos.set_folders_to_sync(get_sub_args());
     404           0 :         else if (get_groups().size() != 0)
     405             :         {
     406           0 :             std::vector<std::string> repos;
     407           0 :             for (auto &group : get_groups())
     408             :             {
     409           0 :                 auto group_ptr = get_manifest()->get_groups().find_group_by_name(group);
     410           0 :                 if (group_ptr == nullptr) continue;
     411           0 :                 for (auto repo : group_ptr->get_repos()) repos.push_back(repo->get_path());
     412           0 :             }
     413           0 :             sync_repos.set_folders_to_sync(repos);
     414           0 :         }
     415             : 
     416           0 :         if (get_debug()) sync_repos.set_log_level(log::Level::DEBUG);
     417             : 
     418           0 :         sync_repos.set_job_count(get_job_count());
     419           0 :         sync_repos.set_logger_if(get_logger_if());
     420           0 :         sync_repos.set_config_folder(get_config_folder());
     421           0 :         sync_repos.set_git(get_git());
     422           0 :         sync_repos.set_manifest(get_manifest());
     423           0 :         sync_repos.set_tui(false);
     424           0 :         sync_repos.set_alt_address(get_alt_address());
     425             : 
     426           0 :         get_git()->detect_ssh_agent(true);
     427           0 :         if (control.logger) get_git()->set_logger_if(control.logger);
     428             : 
     429           0 :         phase = sync_repos.run_with_session(session, &control);
     430           0 :         restore_logger();
     431           0 :         return phase;
     432           0 :     };
     433             : 
     434           0 :     tui::SyncTuiOptions tui_options;
     435           0 :     tui_options.demo = get_tui_demo();
     436           0 :     result = tui::run_compact_sync_tui(work, {}, ui_workers, get_logger_if(), tui_options);
     437           0 :     if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
     438             : 
     439           0 :     if (!get_multi()) return ETRC_RET(ESYSREPO_RESULT(ResultCode::OK));
     440           0 :     return ETRC_RET(ESYSREPO_RESULT(do_multi()));
     441             : #endif
     442           0 : }
     443             : 
     444             : } // namespace esys::repo::exe

Generated by: LCOV version 1.14