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.bitbucket; 7 8 import dubregistry.cache; 9 import dubregistry.repositories.repository; 10 import std.string : format, startsWith; 11 import std.typecons; 12 import vibe.core.log; 13 import vibe.core.stream; 14 import vibe.data.json; 15 import vibe.inet.url; 16 17 18 class BitbucketRepository : Repository { 19 private { 20 string m_owner; 21 string m_project; 22 } 23 24 static void register() 25 { 26 Repository factory(Json info){ 27 return new BitbucketRepository(info.owner.get!string, info.project.get!string); 28 } 29 addRepositoryFactory("bitbucket", &factory); 30 } 31 32 this(string owner, string project) 33 { 34 m_owner = owner; 35 m_project = project; 36 } 37 38 RefInfo[] getTags() 39 { 40 Json tags; 41 try tags = readJson("https://api.bitbucket.org/1.0/repositories/"~m_owner~"/"~m_project~"/tags"); 42 catch( Exception e ) { throw new Exception("Failed to get tags: "~e.msg); } 43 RefInfo[] ret; 44 foreach( string tagname, tag; tags ){ 45 try { 46 auto commit_hash = tag.raw_node.get!string(); 47 auto commit_date = bbToIsoDate(tag.utctimestamp.get!string()); 48 ret ~= RefInfo(tagname, commit_hash, commit_date); 49 logDebug("Found tag for %s/%s: %s", m_owner, m_project, tagname); 50 } catch( Exception e ){ 51 throw new Exception("Failed to process tag "~tag.name.get!string~": "~e.msg); 52 } 53 } 54 return ret; 55 } 56 57 RefInfo[] getBranches() 58 { 59 Json branches = readJson("https://api.bitbucket.org/1.0/repositories/"~m_owner~"/"~m_project~"/branches"); 60 RefInfo[] ret; 61 foreach( string branchname, branch; branches ){ 62 auto commit_hash = branch.raw_node.get!string(); 63 auto commit_date = bbToIsoDate(branch.utctimestamp.get!string()); 64 ret ~= RefInfo(branchname, commit_hash, commit_date); 65 logDebug("Found branch for %s/%s: %s", m_owner, m_project, branchname); 66 } 67 return ret; 68 } 69 70 void readFile(string commit_sha, Path path, scope void delegate(scope InputStream) reader) 71 { 72 assert(path.absolute, "Passed relative path to readFile."); 73 auto url = "https://bitbucket.org/api/1.0/repositories/"~m_owner~"/"~m_project~"/raw/"~commit_sha~path.toString(); 74 downloadCached(url, (scope input) { 75 reader(input); 76 }, true); 77 } 78 79 string getDownloadUrl(string ver) 80 { 81 if( ver.startsWith("~") ) ver = ver[1 .. $]; 82 else ver = ver; 83 return "https://bitbucket.org/"~m_owner~"/"~m_project~"/get/"~ver~".zip"; 84 } 85 86 void download(string ver, scope void delegate(scope InputStream) del) 87 { 88 downloadCached(getDownloadUrl(ver), del); 89 } 90 } 91 92 private auto bbToIsoDate(string bbdate) 93 { 94 import std.array, std.datetime : SysTime; 95 auto ttz = bbdate.split("+"); 96 if( ttz.length < 2 ) ttz ~= "00:00"; 97 auto parts = ttz[0].split("-"); 98 parts = parts[0 .. $-1] ~ parts[$-1].split(" "); 99 parts = parts[0 .. $-1] ~ parts[$-1].split(":"); 100 101 return SysTime.fromISOString(format("%s%s%sT%s%s%s+%s", parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], ttz[1])); 102 }