5 #include <boost/filesystem/fstream.hpp> 6 #include <boost/filesystem/operations.hpp> 8 namespace fs = boost::filesystem;
12 Result
fetchModule(
const fs::path& workspacePath,
const fs::path& name,
bool recursive) {
18 auto modCtx = res.addScopedContext({{
"Module Name", name.string()}});
20 if (name ==
"lang") {
return res; }
24 std::string cloneInto;
29 auto fileName = workspacePath /
"src" / fs::path(name).replace_extension(
".chimod");
30 bool exists = fs::is_regular_file(fileName);
36 auto repoPath = workspacePath /
"src" / cloneInto;
37 if (cloneInto ==
"" || !fs::is_directory(repoPath /
".git")) {
43 auto repoPathCtx = res.addScopedContext({{
"Repo Path", repoPath.string()}});
45 if (type == VCSType::Unknown) {
46 res.addEntry(
"EUKN",
"Could not resolve URL for module", {});
49 assert(type == VCSType::Git &&
"Currently only Git is implemented for fetching modules.");
53 int err = git_repository_open(&repo, repoPath.string().c_str());
55 res.addEntry(
"EUKN",
"Failed to open git repository",
56 {{
"Error Message", giterr_last()->message}});
62 err = git_remote_lookup(&origin, repo,
"origin");
64 res.addEntry(
"EUKN",
"Failed to get remote origin",
65 {{
"Error Message", giterr_last()->message}});
70 git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
71 err = git_remote_fetch(origin,
nullptr, &opts,
nullptr);
73 res.addEntry(
"EUKN",
"Failed to fetch repo",
74 {{
"Error Message", giterr_last()->message}});
79 std::pair<std::string, git_oid> oid_to_merge;
80 git_repository_fetchhead_foreach(
82 [](
const char* name,
const char* ,
const git_oid* oid,
unsigned int is_merge,
83 void* payload) ->
int {
84 auto& oids_to_merge = *
reinterpret_cast<std::pair<std::string, git_oid>*
>(payload);
86 if (is_merge != 0u) { oids_to_merge = {name, *oid}; }
94 git_annotated_commit* originmaster;
95 err = git_annotated_commit_lookup(&originmaster, repo, &oid_to_merge.second);
97 res.addEntry(
"EUKN",
"Failed to get new head from repo",
98 {{
"Error Message", giterr_last()->message}});
102 auto annotatedCommits =
const_cast<const git_annotated_commit**
>(&originmaster);
105 git_merge_analysis_t anaylisis;
106 git_merge_preference_t pref;
107 git_merge_analysis(&anaylisis, &pref, repo, annotatedCommits, 1);
109 if ((anaylisis & GIT_MERGE_ANALYSIS_UP_TO_DATE) != 0 ||
110 (anaylisis & GIT_MERGE_ANALYSIS_NONE) != 0) {
115 if ((anaylisis & GIT_MERGE_ANALYSIS_FASTFORWARD) != 0) {
119 git_reference* master;
120 err = git_repository_head(&master, repo);
123 res.addEntry(
"EUKN",
"Failed to get reference to master",
124 {{
"Error Message", giterr_last()->message}});
129 git_reference* createdRef;
130 err = git_reference_set_target(&createdRef, master, &oid_to_merge.second,
"pull");
132 res.addEntry(
"EUKN",
"Failed to fast forward",
133 {{
"Error Message", giterr_last()->message}});
139 err = git_repository_index(&head, repo);
141 res.addEntry(
"EUKN",
"Failed to get HEAD",
142 {{
"Error Message", giterr_last()->message}});
148 err = git_index_write_tree_to(&oid, head, repo);
150 res.addEntry(
"EUKN",
"Failed to write index to tree",
151 {{
"Error Message", giterr_last()->message}});
155 }
else if ((anaylisis & GIT_MERGE_ANALYSIS_NORMAL) != 0) {
157 git_merge_options mergeOpts = GIT_MERGE_OPTIONS_INIT;
158 git_checkout_options checkoutOpts = GIT_CHECKOUT_OPTIONS_INIT;
159 checkoutOpts.checkout_strategy = GIT_CHECKOUT_SAFE;
161 err = git_merge(repo, annotatedCommits, 1, &mergeOpts, &checkoutOpts);
163 res.addEntry(
"EUKN",
"Failed to merge branch",
164 {{
"Error Message", giterr_last()->message}});
172 err = git_repository_index(&head, repo);
174 res.addEntry(
"EUKN",
"Failed to get HEAD",
175 {{
"Error Message", giterr_last()->message}});
180 if (git_index_has_conflicts(head) != 0) {
182 res.addEntry(
"WUKN",
"Merge conflicts when pulling, manually resolve them.", {});
189 git_signature* committerSignature;
190 err = git_signature_now(&committerSignature,
"Chigraph Fetch",
193 res.addEntry(
"EUKN",
"Failed to create git signature",
194 {{
"Error Message", giterr_last()->message}});
199 git_commit* origin_master_commit;
200 err = git_commit_lookup(&origin_master_commit, repo, &oid_to_merge.second);
202 res.addEntry(
"EUKN",
"Failed to get commit for origin/master",
203 {{
"Error Message", giterr_last()->message}});
208 git_oid parent_headoid{};
209 err = git_reference_name_to_id(&parent_headoid, repo,
"HEAD");
211 res.addEntry(
"EUKN",
"Failed to get reference to HEAD",
212 {{
"Error Message", giterr_last()->message}});
216 git_commit* head_parent;
217 err = git_commit_lookup(&head_parent, repo, &parent_headoid);
219 res.addEntry(
"EUKN",
"Failed to get commit from oid",
220 {{
"Error Message", giterr_last()->message}});
226 err = git_commit_tree(&tree, head_parent);
228 res.addEntry(
"EUKN",
"Failed to git tree from commit",
229 {{
"Error Message", giterr_last()->message}});
232 const git_commit* parents[] = {head_parent, origin_master_commit};
235 std::string commitMsg = std::string(
"Merge ") + git_oid_tostr_s(&oid_to_merge.second);
236 err = git_commit_create(&newCommit, repo,
"HEAD", committerSignature,
237 committerSignature,
"UTF-8", commitMsg.c_str(), tree,
238 sizeof(parents) /
sizeof(git_commit*),
239 static_cast<const git_commit**
>(parents));
241 res.addEntry(
"EUKN",
"Failed to create commit",
242 {{
"Error Message", giterr_last()->message}});
246 git_annotated_commit_free(originmaster);
247 git_repository_state_cleanup(repo);
252 if (type == VCSType::Unknown) {
253 res.addEntry(
"EUKN",
"Could not resolve URL for module", {});
256 assert(type == VCSType::Git);
258 auto absCloneInto = workspacePath /
"src" / cloneInto;
260 fs::create_directories(absCloneInto.parent_path());
263 git_repository* repo;
264 int err = git_clone(&repo, url.c_str(), absCloneInto.string().c_str(),
nullptr);
268 res.addEntry(
"EUKN",
"Failed to clone repository",
269 {{
"Error Code", err}, {
"Error Message", giterr_last()->message}});
274 if (!fs::is_regular_file(fileName)) {
275 res.addEntry(
"EUKN",
"Module doesn't exist", {{
"File Name", fileName.string()}});
284 fs::ifstream file{fileName};
286 }
catch (std::exception& e) {
287 res.addEntry(
"EUKN",
"Failed to parse JSON",
288 {{
"File", fileName.string()}, {
"Error Message", e.what()}});
291 if (j.find(
"dependencies") != j.end() || !j[
"dependencies"].is_array()) {
return res; }
294 for (
const auto& dep : j[
"dependencies"]) {
295 std::string depName = dep;
304 const boost::filesystem::path& path) {
307 auto beginIter = path.begin();
308 if (beginIter != path.end() && *beginIter ==
"github.com") {
309 std::string folderName = beginIter->string();
313 if (beginIter != path.end()) {
315 folderName += beginIter->string();
317 if (beginIter != path.end()) {
319 folderName += beginIter->string();
321 return std::make_tuple(VCSType::Git,
"https://" + folderName, folderName);
325 return std::make_tuple(VCSType::Unknown,
"",
"");
Result fetchModule(const boost::filesystem::path &workspacePath, const boost::filesystem::path &name, bool recursive)
Downloads a module from a remote URL, currently supports.
The namespace where chigraph lives.
std::tuple< VCSType, std::string, std::string > resolveUrlFromModuleName(const boost::filesystem::path &path)
Get the URL for a VCS repository from a module name.