Line data Source code
1 : /*! 2 : * \file esys/repo/exe/cmdmanifest.cpp 3 : * \brief 4 : * 5 : * \cond 6 : * __legal_b__ 7 : * 8 : * Copyright (c) 2020-2021 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/cmdmanifest.h" 20 : #include "esys/repo/manifest/file.h" 21 : #include "esys/repo/grepo/manifest.h" 22 : #include "esys/repo/manifest/directorymngr.h" 23 : #include "esys/repo/manifest/repository.h" 24 : #include "esys/repo/manifest/xmlfile.h" 25 : #include "esys/repo/githelper.h" 26 : #include "esys/repo/userfolder.h" 27 : 28 : #include <esys/trace/call.h> 29 : #include <esys/trace/macros.h> 30 : 31 : #include <boost/filesystem.hpp> 32 : 33 : #include <iostream> 34 : #include <fstream> 35 : #include <sstream> 36 : 37 : namespace esys::repo::exe 38 : { 39 : 40 64 : CmdManifest::TaskDelDir::TaskDelDir() = default; 41 : 42 0 : void CmdManifest::TaskDelDir::set_name(const std::string &name) 43 : { 44 0 : m_name = name; 45 0 : } 46 : 47 0 : const std::string &CmdManifest::TaskDelDir::get_name() const 48 : { 49 0 : return m_name; 50 : } 51 : 52 32 : CmdManifest::TaskAddDir::TaskAddDir() = default; 53 : 54 0 : void CmdManifest::TaskAddDir::set_url_ssh(const std::string &url_ssh) 55 : { 56 0 : m_url_ssh = url_ssh; 57 0 : } 58 : 59 0 : const std::string &CmdManifest::TaskAddDir::get_url_ssh() const 60 : { 61 0 : return m_url_ssh; 62 : } 63 : 64 0 : void CmdManifest::TaskAddDir::set_url_https(const std::string &url_https) 65 : { 66 0 : m_url_https = url_https; 67 0 : } 68 : 69 0 : const std::string &CmdManifest::TaskAddDir::get_url_https() const 70 : { 71 0 : return m_url_https; 72 : } 73 : 74 32 : CmdManifest::CmdManifest() 75 64 : : Cmd("Manifest") 76 : { 77 32 : } 78 : 79 32 : CmdManifest::~CmdManifest() = default; 80 : 81 2 : void CmdManifest::set_task(Task task) 82 : { 83 2 : m_task = task; 84 2 : } 85 : 86 4 : CmdManifest::Task CmdManifest::get_task() const 87 : { 88 4 : return m_task; 89 : } 90 : 91 0 : void CmdManifest::set_task_add_dir(const TaskAddDir &task_add_dir) 92 : { 93 0 : m_task_add_dir = task_add_dir; 94 0 : set_task(Task::ADD_DIR); 95 0 : } 96 : 97 0 : const CmdManifest::TaskAddDir &CmdManifest::get_task_add_dir() const 98 : { 99 0 : return m_task_add_dir; 100 : } 101 : 102 0 : void CmdManifest::set_task_del_dir(const TaskDelDir &task_del_dir) 103 : { 104 0 : m_task_del_dir = task_del_dir; 105 0 : set_task(Task::DEL_DIR); 106 0 : } 107 : 108 0 : const CmdManifest::TaskDelDir &CmdManifest::get_task_del_dir() const 109 : { 110 0 : return m_task_del_dir; 111 : } 112 : 113 0 : void CmdManifest::set_dirs(const std::string &dirs) 114 : { 115 0 : m_dirs = dirs; 116 0 : } 117 : 118 0 : const std::string &CmdManifest::get_dirs() const 119 : { 120 0 : return m_dirs; 121 : } 122 : 123 2 : void CmdManifest::set_output_file(const std::string &output_file) 124 : { 125 2 : m_output_file = output_file; 126 2 : } 127 : 128 5 : const std::string &CmdManifest::get_output_file() const 129 : { 130 5 : return m_output_file; 131 : } 132 : 133 1 : void CmdManifest::set_merge_inputs(const std::vector<std::string> &paths) 134 : { 135 1 : m_merge_inputs = paths; 136 1 : } 137 : 138 1 : const std::vector<std::string> &CmdManifest::get_merge_inputs() const 139 : { 140 1 : return m_merge_inputs; 141 : } 142 : 143 1 : void CmdManifest::set_on_conflict(manifest::Merge::OnConflict on_conflict) 144 : { 145 1 : m_on_conflict = on_conflict; 146 1 : } 147 : 148 1 : manifest::Merge::OnConflict CmdManifest::get_on_conflict() const 149 : { 150 1 : return m_on_conflict; 151 : } 152 : 153 1 : void CmdManifest::set_merge_format(manifest::Merge::OutputFormat format) 154 : { 155 1 : m_merge_format = format; 156 1 : } 157 : 158 0 : manifest::Merge::OutputFormat CmdManifest::get_merge_format() const 159 : { 160 0 : return m_merge_format; 161 : } 162 : 163 0 : void CmdManifest::set_merge_parse_mode(manifest::ParseMode mode) 164 : { 165 0 : m_merge_parse_mode = mode; 166 0 : } 167 : 168 0 : manifest::ParseMode CmdManifest::get_merge_parse_mode() const 169 : { 170 0 : return m_merge_parse_mode; 171 : } 172 : 173 3 : Result CmdManifest::update_revision_as_head() 174 : { 175 11 : for (auto location : get_manifest()->get_locations()) 176 : { 177 31 : for (auto repo : location->get_repos()) 178 : { 179 52 : Result result = update_revision_as_head(repo); 180 26 : if (result.error()) return ESYSREPO_RESULT(result); 181 52 : } 182 5 : } 183 3 : return ESYSREPO_RESULT(ResultCode::OK); 184 : } 185 : 186 26 : Result CmdManifest::update_revision_as_head(std::shared_ptr<manifest::Repository> repo) 187 : { 188 26 : auto git_helper = new_git_helper(); 189 : 190 26 : boost::filesystem::path repo_path = get_config_folder()->get_workspace_path(); 191 26 : repo_path /= repo->get_path(); 192 78 : repo_path = boost::filesystem::absolute(repo_path).lexically_normal().make_preferred(); 193 : 194 26 : std::string revision = repo->get_revision(); 195 64 : if (revision.empty()) revision = get_manifest()->get_default_revision(); 196 : 197 26 : Result rresult = git_helper->open(repo_path.string(), log::Level::DEBUG); 198 26 : if (rresult.error()) return ESYSREPO_RESULT(rresult); 199 : 200 26 : rresult = git_helper->fetch(log::Level::DEBUG); 201 26 : if (rresult.error()) return ESYSREPO_RESULT(rresult); 202 : 203 26 : std::string hash; 204 26 : rresult = git_helper->get_hash(revision, hash, log::Level::DEBUG); 205 26 : if (rresult.error()) 206 : { 207 0 : git_helper->close_on_error(log::Level::DEBUG); 208 0 : return ESYSREPO_RESULT(rresult); 209 : } 210 26 : repo->set_revision(hash); 211 : 212 26 : return git_helper->close(log::Level::DEBUG); 213 52 : } 214 : 215 3 : Result CmdManifest::do_revision_as_head() 216 : { 217 3 : Result result = only_one_folder_or_workspace(); 218 3 : if (result.error()) return ESYSREPO_RESULT(result); 219 : 220 3 : result = open_esysrepo_folder(); 221 3 : if (result.error()) return ESYSREPO_RESULT(result); 222 : 223 3 : result = load_manifest(); 224 3 : if (result.error()) return ESYSREPO_RESULT(result); 225 : 226 3 : result = update_revision_as_head(); 227 3 : if (result.error()) 228 : { 229 0 : std::string err_str = "Failed to get the revision as HEAD"; 230 0 : error(err_str); 231 0 : return ESYSREPO_RESULT(result, err_str); 232 0 : } 233 : 234 3 : std::shared_ptr<std::ofstream> fos; 235 3 : std::ostream *os = nullptr; 236 : 237 3 : if (get_output_file().empty()) 238 : os = &std::cout; 239 : else 240 : { 241 1 : fos = std::make_shared<std::ofstream>(get_output_file()); 242 1 : os = fos.get(); 243 : } 244 : 245 3 : std::shared_ptr<manifest::FileBase> file; 246 : 247 6 : switch (get_manifest()->get_type()) 248 : { 249 2 : case manifest::Type::GOOGLE_MANIFEST: file = std::make_shared<grepo::Manifest>(); break; 250 4 : case manifest::Type::ESYSREPO_MANIFEST: file = std::make_shared<manifest::XMLFile>(); break; 251 0 : default: error("Manifest format not supported."); return generic_error(-1); 252 : } 253 : 254 3 : file->set_data(get_manifest()); 255 3 : result = file->write(*os); 256 : 257 3 : if (fos != nullptr) fos->close(); 258 6 : return ESYSREPO_RESULT(result); 259 6 : } 260 : 261 0 : Result CmdManifest::do_list_dirs() 262 : { 263 0 : UserFolder user_folder; 264 : 265 0 : auto result = user_folder.read(); 266 0 : if (result.error()) return ESYSREPO_RESULT(result); 267 : 268 0 : if (user_folder.get_user_config() == nullptr) return ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR); 269 : 270 0 : auto mds = user_folder.get_user_config()->get_manifest_directories(); 271 0 : if (mds == nullptr) return ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR); 272 : 273 0 : std::ostringstream oss; 274 : 275 0 : for (auto idx = 0; idx < mds->get_items().size(); ++idx) 276 : { 277 0 : auto md = mds->get_items()[idx]; 278 0 : oss.str(""); 279 : 280 0 : oss << "[" << idx << "] " << md->get_name() << std::endl; 281 0 : oss << " " << md->get_url_ssh() << std::endl; 282 0 : oss << " " << md->get_url_https(); 283 : 284 0 : info(oss.str()); 285 0 : } 286 0 : return ESYSREPO_RESULT(ResultCode::OK); 287 0 : } 288 : 289 0 : Result CmdManifest::do_list_projects() 290 : { 291 0 : UserFolder user_folder; 292 : 293 0 : auto result = user_folder.read(); 294 0 : if (result.error()) return ESYSREPO_RESULT(result); 295 : 296 0 : if (user_folder.get_user_config() == nullptr) return ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR); 297 : 298 0 : auto mds = user_folder.get_user_config()->get_manifest_directories(); 299 0 : if (mds == nullptr) return ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR); 300 : 301 0 : manifest::DirectoryMngr mngr; 302 : 303 0 : mngr.set_folder_path(user_folder.get_user_config()->get_manifest_directories_path()); 304 0 : mngr.set_directories(mds); 305 : 306 0 : result = mngr.load(); 307 0 : if (result.error()) return ESYSREPO_RESULT(result); 308 : 309 0 : std::ostringstream oss; 310 : 311 0 : for (auto idx = 0; idx < mds->get_items().size(); ++idx) 312 : { 313 0 : auto md = mds->get_items()[idx]; 314 0 : oss.str(""); 315 : 316 0 : oss << "[" << idx << "] " << md->get_name() << std::endl; 317 0 : oss << " urls:" << std::endl; 318 0 : oss << " " << md->get_url_ssh() << std::endl; 319 0 : oss << " " << md->get_url_https(); 320 : 321 0 : if (md->get_directory() == nullptr) 322 : { 323 0 : warn("Manifest directory has no projects."); 324 0 : continue; 325 : } 326 0 : oss << std::endl << " projects:"; 327 0 : for (auto dir : md->get_directory()->get_items()) 328 : { 329 0 : oss << std::endl << " " << dir->get_name(); 330 : // oss << " " << dir 331 0 : } 332 : 333 0 : info(oss.str()); 334 0 : } 335 0 : return ESYSREPO_RESULT(ResultCode::OK); 336 0 : } 337 : 338 0 : Result CmdManifest::do_add_dir() 339 : { 340 0 : UserFolder user_folder; 341 : 342 0 : auto result = user_folder.read(); 343 0 : if (result.error()) return ESYSREPO_RESULT(result); 344 : 345 0 : if (user_folder.get_user_config() == nullptr) return ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR); 346 : 347 0 : auto mds = user_folder.get_user_config()->get_manifest_directories(); 348 0 : if (mds == nullptr) return ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR); 349 : 350 0 : auto item = std::make_shared<manifest::Directories::Item>(); 351 0 : item->set_name(get_task_add_dir().get_name()); 352 0 : item->set_url_ssh(get_task_add_dir().get_url_ssh()); 353 0 : item->set_url_https(get_task_add_dir().get_url_https()); 354 : 355 0 : mds->add_item(item->get_name(), item); 356 : 357 0 : result = user_folder.write(); 358 0 : if (result.error()) return ESYSREPO_RESULT(result); 359 : 360 0 : info("Added manifest directory '" + get_task_del_dir().get_name() + "'."); 361 0 : return ESYSREPO_RESULT(ResultCode::OK); 362 0 : } 363 : 364 0 : Result CmdManifest::do_del_dir() 365 : { 366 0 : UserFolder user_folder; 367 : 368 0 : auto result = user_folder.read(); 369 0 : if (result.error()) return ESYSREPO_RESULT(result); 370 : 371 0 : if (user_folder.get_user_config() == nullptr) return ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR); 372 : 373 0 : auto mds = user_folder.get_user_config()->get_manifest_directories(); 374 0 : if (mds == nullptr) return ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR); 375 : 376 0 : result = mds->del_item(get_task_del_dir().get_name()); 377 0 : if (result.error()) return ESYSREPO_RESULT(result); 378 : 379 0 : result = user_folder.write(); 380 0 : if (result.error()) return ESYSREPO_RESULT(result); 381 : 382 0 : info("Deleted manifest directory '" + get_task_del_dir().get_name() + "'."); 383 0 : return ESYSREPO_RESULT(ResultCode::OK); 384 0 : } 385 : 386 0 : Result CmdManifest::do_merge() 387 : { 388 0 : manifest::Merge merge; 389 0 : merge.set_inputs(get_merge_inputs()); 390 0 : merge.set_output(get_output_file()); 391 0 : merge.set_on_conflict(get_on_conflict()); 392 0 : merge.set_output_format(get_merge_format()); 393 0 : merge.set_parse_mode(get_merge_parse_mode()); 394 : 395 0 : Result result = merge.run(); 396 0 : if (result.error()) 397 : { 398 0 : std::ostringstream oss; 399 0 : oss << result; 400 0 : error(oss.str()); 401 0 : return ESYSREPO_RESULT(result); 402 0 : } 403 : 404 0 : if (!get_output_file().empty()) 405 : { 406 0 : info("Wrote merged manifest to '" + merge.get_resolved_output() + "'."); 407 0 : return ESYSREPO_RESULT(ResultCode::OK); 408 : } 409 : 410 0 : if (merge.get_result() == nullptr) return ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR); 411 : 412 0 : std::shared_ptr<manifest::FileBase> file; 413 0 : if (get_merge_format() == manifest::Merge::OutputFormat::Google) 414 0 : file = std::make_shared<grepo::Manifest>(); 415 : else 416 0 : file = std::make_shared<manifest::XMLFile>(); 417 0 : file->set_data(merge.get_result()); 418 0 : return file->write(std::cout); 419 0 : } 420 : 421 3 : Result CmdManifest::impl_run() 422 : { 423 3 : Result result; 424 3 : ETRC_CALL_RET_NP(result); 425 : 426 3 : switch (get_task()) 427 : { 428 3 : case Task::REVISION_AS_HEAD: 429 6 : case Task::NOT_SET: return ETRC_RET(do_revision_as_head()); 430 0 : case Task::LIST_DIRS: return ETRC_RET(do_list_dirs()); 431 0 : case Task::LIST_PROJECTS: return ETRC_RET(do_list_projects()); 432 0 : case Task::ADD_DIR: return ETRC_RET(do_add_dir()); 433 0 : case Task::DEL_DIR: return ETRC_RET(do_del_dir()); 434 0 : case Task::MERGE: return ETRC_RET(do_merge()); 435 0 : default: return ETRC_RET(ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR)); 436 : } 437 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::OK)); 438 3 : } 439 : 440 : } // namespace esys::repo::exe