Skip to content

Commit 4beb0d9

Browse files
committed
🚧 Added gist and request support
1 parent 5f5ac01 commit 4beb0d9

File tree

1 file changed

+137
-0
lines changed

1 file changed

+137
-0
lines changed

git_repo/services/ext/bitbucket.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from pybitbucket.bitbucket import Client, Bitbucket
1010
from pybitbucket.auth import BasicAuthenticator
1111
from pybitbucket.repository import Repository, RepositoryForkPolicy
12+
from pybitbucket.snippet import Snippet
1213

1314
from requests import Request, Session
1415
from requests.exceptions import HTTPError
@@ -75,6 +76,142 @@ def get_repository(self, user, repo):
7576
except HTTPError as err:
7677
raise ResourceNotFoundError('Cannot retrieve repository: {}/{} does not exists.'.format(user, repo))
7778

79+
def _format_gist(self, gist):
80+
return gist.split('/')[-1] if gist.startswith('http') else gist
81+
82+
def gist_list(self, gist=None):
83+
if not gist:
84+
for snippet in list(self.bb.snippetByOwner(owner=self.user)):
85+
if isinstance(snippet, Snippet):
86+
yield (snippet.links['html']['href'], snippet.title)
87+
else:
88+
try:
89+
snippet = next(self.bb.snippetByOwnerAndSnippetId(owner=self.user, snippet_id=self._format_gist(gist)))
90+
for snippet_file in snippet.filenames:
91+
yield ('N.A.',
92+
0,
93+
snippet_file)
94+
except HTTPError as err:
95+
if '404' in err.args[0].split(' '):
96+
raise ResourceNotFoundError("Could not find snippet {}.".format(gist)) from err
97+
raise ResourceError("Couldn't fetch snippet details: {}".format(err)) from err
98+
99+
def gist_fetch(self, gist, fname=None):
100+
gist = self._format_gist(gist)
101+
try:
102+
user = self.user
103+
snippet = next(self.bb.snippetByOwnerAndSnippetId(owner=user, snippet_id=gist))
104+
except HTTPError as err:
105+
if '404' in err.args[0].split(' '):
106+
raise ResourceNotFoundError("Could not find snippet {}.".format(gist)) from err
107+
raise ResourceError("Couldn't fetch snippet details: {}".format(err)) from err
108+
if len(snippet.filenames) == 1 and not fname:
109+
gist_file = snippet.filenames[0]
110+
else:
111+
if fname in snippet.filenames:
112+
gist_file = fname
113+
else:
114+
raise ResourceNotFoundError('Could not find file within gist.')
115+
116+
return self.bb_client.session.get(
117+
'https://bitbucket.org/!api/2.0/snippets/{}/{}/files/{}'.format(user, gist, gist_file)
118+
).content.decode('utf-8')
119+
120+
def gist_clone(self, gist):
121+
gist = self._format_gist(gist)
122+
try:
123+
snippet = next(self.bb.snippetByOwnerAndSnippetId(owner=self.user, snippet_id=gist))
124+
remotes = {it['name']: it['href'] for it in snippet.links['clone']}
125+
if 'ssh' in remotes:
126+
remote = remotes['ssh']
127+
elif 'ssh' in remotes:
128+
remote = remotes['https']
129+
else:
130+
raise ResourceError("Couldn't find appropriate method for cloning.")
131+
except HTTPError as err:
132+
if '404' in err.args[0].split(' '):
133+
raise ResourceNotFoundError("Could not find snippet {}.".format(gist)) from err
134+
raise ResourceError("Couldn't fetch snippet details: {}".format(err)) from err
135+
remote = self.repository.create_remote('gist', remote)
136+
self.pull(remote, 'master')
137+
138+
def gist_create(self, gist_pathes, description, secret=False):
139+
def load_file(fname, path='.'):
140+
with open(os.path.join(path, fname), 'r') as f:
141+
return {'content': f.read()}
142+
143+
gist_files = dict()
144+
for gist_path in gist_pathes:
145+
if not os.path.isdir(gist_path):
146+
gist_files[os.path.basename(gist_path)] = load_file(gist_path)
147+
else:
148+
for gist_file in os.listdir(gist_path):
149+
if not os.path.isdir(os.path.join(gist_path, gist_file)) and not gist_file.startswith('.'):
150+
gist_files[gist_file] = load_file(gist_file, gist_path)
151+
152+
gist = self.gh.create_gist(
153+
description=description,
154+
files=gist_files,
155+
public=not secret # isn't it obvious? ☺
156+
)
157+
158+
return gist.html_url
159+
160+
def gist_delete(self, gist_id):
161+
gist = self.gh.gist(self._format_gist(gist_id))
162+
if not gist:
163+
raise ResourceNotFoundError('Could not find gist')
164+
gist.delete()
165+
166+
def request_create(self, user, repo, local_branch, remote_branch, title, description=None):
167+
repository = self.gh.repository(user, repo)
168+
if not repository:
169+
raise ResourceNotFoundError('Could not find repository `{}/{}`!'.format(user, repo))
170+
if not remote_branch:
171+
remote_branch = self.repository.active_branch.name
172+
if not local_branch:
173+
local_branch = repository.master_branch or 'master'
174+
try:
175+
request = repository.create_pull(title,
176+
base=local_branch,
177+
head=':'.join([user, remote_branch]),
178+
body=description)
179+
except github3.models.GitHubError as err:
180+
if err.code == 422:
181+
if err.message == 'Validation Failed':
182+
for error in err.errors:
183+
if 'message' in error:
184+
raise ResourceError(error['message'])
185+
raise ResourceError("Unhandled formatting error: {}".format(err.errors))
186+
raise ResourceError(err.message)
187+
188+
return {'local': local_branch, 'remote': remote_branch, 'ref': request.number}
189+
190+
def request_list(self, user, repo):
191+
repository = self.gh.repository(user, repo)
192+
for pull in repository.iter_pulls():
193+
yield ( str(pull.number), pull.title, pull.links['issue'] )
194+
195+
def request_fetch(self, user, repo, request, pull=False):
196+
if pull:
197+
raise NotImplementedError('Pull operation on requests for merge are not yet supported')
198+
try:
199+
for remote in self.repository.remotes:
200+
if remote.name == self.name:
201+
local_branch_name = 'request/{}'.format(request)
202+
self.fetch(
203+
remote,
204+
'pull/{}/head'.format(request),
205+
local_branch_name
206+
)
207+
return local_branch_name
208+
else:
209+
raise ResourceNotFoundError('Could not find remote {}'.format(self.name))
210+
except GitCommandError as err:
211+
if 'Error when fetching: fatal: Couldn\'t find remote ref' in err.command[0]:
212+
raise ResourceNotFoundError('Could not find opened request #{}'.format(request)) from err
213+
raise err
214+
78215
@classmethod
79216
def get_auth_token(cls, login, password, prompt=None):
80217
log.warn("/!\\ Due to API limitations, the bitbucket login/password is stored as plaintext in configuration.")

0 commit comments

Comments
 (0)