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 }