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