1 /** 2 Copyright: © 2013 rejectedsoftware e.K. 3 License: Subject to the terms of the GNU GPLv3 license, as written in the included LICENSE.txt file. 4 Authors: Sönke Ludwig 5 */ 6 module dubregistry.repositories.github; 7 8 import dubregistry.cache; 9 import dubregistry.dbcontroller : DbRepository; 10 import dubregistry.repositories.repository; 11 import std.string : startsWith; 12 import std.typecons; 13 import vibe.core.log; 14 import vibe.core.stream; 15 import vibe.data.json; 16 import vibe.inet.url; 17 18 19 class GithubRepository : Repository { 20 private { 21 string m_owner; 22 string m_project; 23 string m_authUser; 24 string m_authPassword; 25 } 26 27 static void register(string user, string password) 28 { 29 Repository factory(DbRepository info){ 30 return new GithubRepository(info.owner, info.project, user, password); 31 } 32 addRepositoryFactory("github", &factory); 33 } 34 35 this(string owner, string project, string auth_user, string auth_password) 36 { 37 m_owner = owner; 38 m_project = project; 39 m_authUser = auth_user; 40 m_authPassword = auth_password; 41 } 42 43 RefInfo[] getTags() 44 { 45 import std.datetime : SysTime; 46 47 Json tags; 48 try tags = readJson(getAPIURLPrefix()~"/repos/"~m_owner~"/"~m_project~"/tags?per_page=100"); 49 catch( Exception e ) { throw new Exception("Failed to get tags: "~e.msg); } 50 RefInfo[] ret; 51 foreach_reverse (tag; tags) { 52 try { 53 auto tagname = tag["name"].get!string; 54 Json commit = readJson(getAPIURLPrefix()~"/repos/"~m_owner~"/"~m_project~"/commits/"~tag["commit"]["sha"].get!string, true, true); 55 ret ~= RefInfo(tagname, tag["commit"]["sha"].get!string, SysTime.fromISOExtString(commit["commit"]["committer"]["date"].get!string)); 56 logDebug("Found tag for %s/%s: %s", m_owner, m_project, tagname); 57 } catch( Exception e ){ 58 throw new Exception("Failed to process tag "~tag["name"].get!string~": "~e.msg); 59 } 60 } 61 return ret; 62 } 63 64 RefInfo[] getBranches() 65 { 66 import std.datetime : SysTime; 67 68 Json branches = readJson(getAPIURLPrefix()~"/repos/"~m_owner~"/"~m_project~"/branches"); 69 RefInfo[] ret; 70 foreach_reverse( branch; branches ){ 71 auto branchname = branch["name"].get!string; 72 Json commit = readJson(getAPIURLPrefix()~"/repos/"~m_owner~"/"~m_project~"/commits/"~branch["commit"]["sha"].get!string, true, true); 73 ret ~= RefInfo(branchname, branch["commit"]["sha"].get!string, SysTime.fromISOExtString(commit["commit"]["committer"]["date"].get!string)); 74 logDebug("Found branch for %s/%s: %s", m_owner, m_project, branchname); 75 } 76 return ret; 77 } 78 79 RepositoryInfo getInfo() 80 { 81 auto nfo = readJson(getAPIURLPrefix()~"/repos/"~m_owner~"/"~m_project); 82 RepositoryInfo ret; 83 ret.isFork = nfo["fork"].opt!bool; 84 ret.stats.stars = nfo["stargazers_count"].opt!uint; 85 ret.stats.watchers = nfo["subscribers_count"].opt!uint; 86 ret.stats.forks = nfo["forks_count"].opt!uint; 87 ret.stats.issues = nfo["open_issues_count"].opt!uint; // conflates PRs and Issues 88 return ret; 89 } 90 91 void readFile(string commit_sha, Path path, scope void delegate(scope InputStream) reader) 92 { 93 assert(path.absolute, "Passed relative path to readFile."); 94 auto url = getContentURLPrefix()~"/"~m_owner~"/"~m_project~"/"~commit_sha~path.toString(); 95 downloadCached(url, (scope input) { 96 reader(input); 97 }, true); 98 } 99 100 string getDownloadUrl(string ver) 101 { 102 import std.uri : encodeComponent; 103 if( ver.startsWith("~") ) ver = ver[1 .. $]; 104 else ver = ver; 105 return "https://github.com/"~m_owner~"/"~m_project~"/archive/"~encodeComponent(ver)~".zip"; 106 } 107 108 void download(string ver, scope void delegate(scope InputStream) del) 109 { 110 downloadCached(getDownloadUrl(ver), del); 111 } 112 113 private string getAPIURLPrefix() { 114 if (m_authUser.length) return "https://"~m_authUser~":"~m_authPassword~"@api.github.com"; 115 else return "https://api.github.com"; 116 } 117 118 private string getContentURLPrefix() { 119 return "https://raw.github.com"; 120 } 121 }