From fe6aa3aa87bdff77ca667e012a9d1cc34f1fb8ea Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 24 Aug 2018 13:33:01 +0200 Subject: Implement bdep-ci command --- bdep/git.cxx | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) (limited to 'bdep/git.cxx') diff --git a/bdep/git.cxx b/bdep/git.cxx index a5e2be4..e9b1eba 100644 --- a/bdep/git.cxx +++ b/bdep/git.cxx @@ -85,4 +85,167 @@ namespace bdep return r; } + + url + git_remote_url (const dir_path& repo, + const char* opt, + const char* what, + const char* cfg) + { + auto git_config = [&repo] (const char* name) -> optional + { + return git_line (semantic_version {2, 1, 0}, + repo, + true /* ignore_error */, + "config", + "--get", + name); + }; + + auto parse_url = [] (const string& s, const char* w) + { + try + { + return url (s); + } + catch (const invalid_argument& e) + { + fail << "invalid " << w << " value '" << s << "': " << e << endf; + } + }; + + // First try the custom config value if specified. + // + if (cfg != nullptr) + { + if (optional l = git_config (cfg)) + { + return parse_url (*l, cfg); + } + } + + // Next is remote.origin.build2Url. + // + if (optional l = git_config ("remote.origin.build2Url")) + { + return parse_url (*l, "remote.origin.build2Url"); + } + + // Finally, get remote.origin.url and try to derive an HTTPS URL from it. + // + if (optional l = git_config ("remote.origin.url")) + { + string& s (*l); + + // This one will be fuzzy and hairy. Here are some representative + // examples of what we can encounter: + // + // example.org:/path/to/repo.git + // user@example.org:/path/to/repo.git + // user@example.org:~user/path/to/repo.git + // ssh://user@example.org/path/to/repo.git + // + // git://example.org/path/to/repo.git + // + // http://example.org/path/to/repo.git + // https://example.org/path/to/repo.git + // + // /path/to/repo.git + // file:///path/to/repo.git + // + // Note that git seem to always make remote.origin.url absolute in + // case of a local filesystem path. + // + // So the algorithm will be as follows: + // + // 1. If there is scheme, then parse as URL. + // + // 2. Otherwise, check if this is an absolute path. + // + // 3. Otherwise, assume SSH : thing. + // + url u; + + // Find the scheme. + // + // Note that in example.org:/path/... example.org is a valid scheme. To + // distinguish this, we check if the scheme contains any dots (none of + // the schemes recognized by git currently do and probably never will). + // + size_t p (s.find (':')); + if (p != string::npos && // Has ':'. + url::traits::find (s, p) == 0 && // Scheme starts at 0. + s.rfind ('.', p - 1) == string::npos) // No dots in scheme. + { + u = parse_url (s, "remote.origin.url"); + } + else + { + // Absolute path or the SSH thing. + // + if (path::traits::absolute (s)) + { + // This is what we want to end up with: + // + // file:///tmp + // file:///c:/tmp + // + const char* h (s[0] == '/' ? "file://" : "file:///"); + u = parse_url (h + s, "remote.origin.url"); + } + else if (p != string::npos) + { + // This can still include user (user@host) so let's add the scheme, + // replace/erase ':', and parse it as a string representation of a + // URL. + // + if (s[p + 1] == '/') // POSIX notation. + s.erase (p, 1); + else + s[p] = '/'; + + u = parse_url ("ssh://" + s, "remote.origin.url"); + } + else + fail << "invalid remote.origin.url value '" << s << "': not a URL"; + } + + // A remote URL gotta have authority. + // + if (u.authority) + { + if (u.scheme == "http" || u.scheme == "https") + { + // This can still include the user which we most likely don't want. + // + u.authority->user.clear (); + return u; + } + + // Derive an HTTPS URL from a remote URL (and hope for the best). + // + if (u.scheme != "file" && u.path) + return url ("https", u.authority->host, *u.path); + } + + diag_record dr (fail); + + dr << "unable to derive " << what << " from " << u; + + dr << info << "consider setting git "; + if (cfg != nullptr) + dr << cfg << " or "; + dr << "remote.origin.build2Url value"; + + if (opt != nullptr) + dr << info << "or use " << opt << " to specify explicitly"; + } + + // We don't necessarily want to suggest remote.origin.build2* or the + // option since preferably this should be derived automatically from + // remote.origin.url. + // + fail << "unable to discover " << what << ": no git remote.origin.url " + << "value" << endf; + } } -- cgit v1.1