Skip to content

Commit e58373e

Browse files
committed
🚧 tests for gitlab merge requests and snippets ; 📼 recorded new betamax
fixed and generalised some requests/gist tests for github as well Signed-off-by: Guyzmo <[email protected]>
1 parent aea6c12 commit e58373e

File tree

43 files changed

+2787
-135
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2787
-135
lines changed

buildout.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ develop-eggs-directory = ${buildout:directory}/var/develop-eggs
99
parts-directory = ${buildout:directory}/var/parts
1010
# develop-dir = ${buildout:directory}/var/clone/
1111
# extensions=gp.vcsdevelop
12-
# vcs-extend-develop=git+https://github.com/gitpython-developers/GitPython#egg=GitPython
12+
# vcs-extend-develop=git+https://github.com/…@…#egg=
1313

1414
[git_repo]
1515
recipe = zc.recipe.egg

git_repo/repo.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -438,12 +438,14 @@ def do_request_fetch(self):
438438
@register_action('snippet', 'list')
439439
def do_gist_list(self):
440440
service = self.get_service(lookup_repository=False)
441-
if self.gist_ref:
441+
if 'github' == service.name and self.gist_ref:
442442
log.info("{:15}\t{:>7}\t{}".format('language', 'size', 'name'))
443+
else:
444+
log.info("{:56}\t{}".format('id', 'title'.ljust(60)))
445+
if self.gist_ref:
443446
for gist_file in service.gist_list(self.gist_ref):
444447
print("{:15}\t{:7}\t{}".format(*gist_file))
445448
else:
446-
log.info("{:56}\t{}".format('id', 'title'.ljust(60)))
447449
for gist in service.gist_list():
448450
print( "{:56}\t{}".format(gist[0], gist[1]))
449451
return 0

git_repo/services/ext/github.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ def request_fetch(self, user, repo, request, pull=False):
266266
try:
267267
for remote in self.repository.remotes:
268268
if remote.name == self.name:
269-
local_branch_name = 'request/{}'.format(request)
269+
local_branch_name = 'requests/github/{}'.format(request)
270270
self.fetch(
271271
remote,
272272
'pull/{}/head'.format(request),

git_repo/services/ext/gitlab.py

Lines changed: 97 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77
from ...exceptions import ArgumentError, ResourceError, ResourceExistsError, ResourceNotFoundError
88

99
import gitlab
10-
from gitlab.exceptions import GitlabCreateError, GitlabGetError
10+
from gitlab.exceptions import GitlabListError, GitlabCreateError, GitlabGetError
1111

12+
from git.exc import GitCommandError
13+
14+
import os
1215
import json, time
1316

1417
@register_target('lab', 'gitlab')
@@ -140,53 +143,56 @@ def get_auth_token(cls, login, password, prompt=None):
140143
return gl.user.private_token
141144

142145
def _deconstruct_snippet_uri(self, uri):
143-
path = uri.split('https://{}/'.format(self.fqdn))[-1]
144-
path = path.split('/')
145-
if 3 == len(path):
146+
path = uri.split('https://{}/'.format(self.fqdn))[-1].split('/')
147+
if 4 == len(path):
146148
user, project_name, _, snippet_id = path
147-
elif 4 == len(path):
148-
user, _, snippet_id = path
149+
elif 2 == len(path):
150+
_, snippet_id = path
151+
project_name = None
152+
user = None
153+
elif 1 == len(path):
154+
snippet_id = path[0]
149155
project_name = None
156+
user = None
150157
else:
151158
raise ResourceNotFoundError('URL is not of a snippet')
152159
return (user, project_name, snippet_id)
153160

154161
def gist_list(self, project=None):
155162
if not project:
156-
raise NotImplementedError('Feature API implementation scheduled for gitlab 8.15')
157-
for project in self.gl.snippets.list():
158-
yield ('https://{}/{}/snippets/{}'.format(
159-
self.fqdn,
160-
snippet.author.username,
161-
snippet.id
162-
), snippet.title)
163+
try:
164+
for snippet in self.gl.snippets.list():
165+
yield (snippet.web_url, snippet.title)
166+
except GitlabListError as err:
167+
if err.response_code == 404:
168+
raise ResourceNotFoundError('Feature not available, please upgrade your gitlab instance.') from err
169+
raise ResourceError('Cannot list snippet') from err
163170
else:
164-
project = self.gl.projects.list(search=project)
165-
if len(project):
166-
project = project[0]
167-
for snippet in project.snippets.list(per_page=100):
168-
yield ('https://{}/{}/{}/snippets/{}'.format(
169-
self.fqdn,
170-
snippet.author.username,
171-
project.name,
172-
snippet.id
173-
), 0, snippet.title)
171+
if '/' not in project:
172+
project = '/'.join([self.username, project])
173+
try:
174+
project = self.gl.projects.get(project)
175+
for snippet in project.snippets.list():
176+
yield (snippet.web_url, 0, snippet.title)
177+
except GitlabGetError as err:
178+
raise ResourceNotFoundError('Could not retrieve project "{}".'.format(project)) from err
174179

175180
def gist_fetch(self, snippet, fname=None):
176181
if fname:
177182
raise ArgumentError('Snippets contain only single file in gitlab.')
178183
try:
179-
_, project_name, snippet_id = self._deconstruct_snippet_uri(snippet)
180-
if project_name:
181-
project = self.gl.projects.list(search=project_name)[0]
182-
snippet = self.gl.project_snippets.get(project_id=project.id, id=snippet_id)
183-
else:
184-
raise NotImplementedError('Feature API implementation scheduled for gitlab 8.15')
185-
snippet = self.gl.snippets.get(id=snippet_id)
184+
*_, snippet_id = self._deconstruct_snippet_uri(snippet)
185+
snippet = self.gl.snippets.get(id=snippet_id)
186+
except GitlabGetError as err:
187+
if err.response_code == 404:
188+
if "The page you're looking for could not be found." in err.response_body.decode('utf-8'):
189+
raise ResourceNotFoundError('Feature not available, please upgrade your gitlab instance.') from err
190+
raise ResourceNotFoundError('Cannot fetch snippet') from err
191+
raise ResourceError('Cannot fetch snippet') from err
186192
except Exception as err:
187193
raise ResourceNotFoundError('Could not find snippet') from err
188194

189-
return snippet.Content().decode('utf-8')
195+
return snippet.raw().decode('utf-8')
190196

191197
def gist_clone(self, gist):
192198
raise ArgumentError('Snippets cannot be cloned in gitlab.')
@@ -204,52 +210,71 @@ def load_file(fname, path='.'):
204210
'visibility_level': 0 if secret else 20
205211
}
206212

207-
if len(gist_pathes) == 2:
208-
gist_proj = gist_pathes[0]
209-
gist_path = gist_pathes[1]
210-
data.update({
211-
'id': gist_proj,
212-
'code': load_file(gist_path),
213-
'file_name': os.path.basename(gist_path),
214-
}
215-
)
216-
gist = self.gl.project_snippets.create(data)
217-
218-
elif len(gist_pathes) == 1:
219-
raise NotImplementedError('Feature API implementation scheduled for gitlab 8.15')
220-
gist_path = gist_pathes[0]
221-
data.update({
222-
'content': load_file(gist_path),
223-
'file_name': os.path.basename(gist_path),
224-
}
225-
)
226-
gist = self.gl.snippets.create(data)
227-
228-
return gist.html_url
229-
230-
def gist_delete(self, gist_id):
231213
try:
232-
_, project_name, snippet_id = self._deconstruct_snippet_uri(snippet)
233-
if project_name:
234-
project = self.gl.projects.list(search=project_name)[0]
235-
snippet = self.gl.project_snippets.get(project_id=project.id, id=snippet_id)
214+
215+
if len(gist_pathes) == 2:
216+
project = gist_pathes[0]
217+
if '/' in project:
218+
*namespace, project = project.split('/')
219+
namespace = '/'.join(namespace)
220+
else:
221+
namespace = self.username
222+
gist_path = gist_pathes[1]
223+
data.update({
224+
'project_id': '/'.join([namespace, project]),
225+
'code': load_file(gist_path),
226+
'file_name': os.path.basename(gist_path),
227+
}
228+
)
229+
gist = self.gl.project_snippets.create(data)
230+
231+
elif len(gist_pathes) == 1:
232+
gist_path = gist_pathes[0]
233+
data.update({
234+
'content': load_file(gist_path),
235+
'file_name': os.path.basename(gist_path),
236+
}
237+
)
238+
gist = self.gl.snippets.create(data)
239+
240+
return gist.web_url
241+
except GitlabCreateError as err:
242+
if err.response_code == 422:
243+
raise ResourceNotFoundError('Feature not available, please upgrade your gitlab instance.') from err
244+
raise ResourceError('Cannot create snippet') from err
245+
246+
def gist_delete(self, snippet):
247+
try:
248+
_, project, snippet_id = self._deconstruct_snippet_uri(snippet)
249+
if project:
250+
if '/' in project:
251+
*namespace, project = project.split('/')
252+
namespace = '/'.join(namespace)
253+
else:
254+
namespace = self.username
255+
snippet = self.gl.projects.get(
256+
'/'.join([namespace, project])
257+
).snippets.get(id=snippet_id)
236258
else:
237-
raise NotImplementedError('Pending feature')
238259
snippet = self.gl.snippets.get(id=snippet_id)
260+
except GitlabCreateError as err:
261+
if err.response_code == 422:
262+
raise ResourceNotFoundError('Cannot delete snippet, please upgrade your gitlab instance.') from err
263+
raise ResourceError('Cannot delete snippet') from err
239264
except Exception as err:
240265
raise ResourceNotFoundError('Could not find snippet') from err
241266

242267
return snippet.delete()
243268

244269
def request_create(self, user, repo, local_branch, remote_branch, title, description=None):
245-
repository = self.gl.projects.list(search=repo)[0]
246-
if not repository:
247-
raise ResourceNotFoundError('Could not find repository `{}/{}`!'.format(user, repo))
248-
if not local_branch:
249-
remote_branch = self.repository.active_branch.name or self.repository.active_branch.name
250-
if not remote_branch:
251-
local_branch = repository.master_branch or 'master'
252270
try:
271+
repository = self.gl.projects.get('/'.join([user, repo]))
272+
if not repository:
273+
raise ResourceNotFoundError('Could not find repository `{}/{}`!'.format(user, repo))
274+
if not local_branch:
275+
remote_branch = self.repository.active_branch.name or self.repository.active_branch.name
276+
if not remote_branch:
277+
local_branch = repository.master_branch or 'master'
253278
request = self.gl.project_mergerequests.create(
254279
project_id=repository.id,
255280
data= {
@@ -259,6 +284,8 @@ def request_create(self, user, repo, local_branch, remote_branch, title, descrip
259284
'description':description
260285
}
261286
)
287+
except GitlabGetError as err:
288+
raise ResourceNotFoundError(err) from err
262289
except Exception as err:
263290
raise ResourceError("Unhandled error: {}".format(err)) from err
264291

@@ -267,7 +294,7 @@ def request_create(self, user, repo, local_branch, remote_branch, title, descrip
267294
'ref': request.iid}
268295

269296
def request_list(self, user, repo):
270-
project = self.gl.projects.list(search=repo)[0]
297+
project = self.gl.projects.get('/'.join([user, repo]))
271298
for mr in self.gl.project_mergerequests.list(project_id=project.id):
272299
yield ( str(mr.iid),
273300
mr.title,
@@ -285,10 +312,10 @@ def request_fetch(self, user, repo, request, pull=False):
285312
try:
286313
for remote in self.repository.remotes:
287314
if remote.name == self.name:
288-
local_branch_name = 'request/{}'.format(request)
315+
local_branch_name = 'requests/gitlab/{}'.format(request)
289316
self.fetch(
290317
remote,
291-
'merge-requests/{}/head'.format(request),
318+
'merge_requests/{}/head'.format(request),
292319
local_branch_name
293320
)
294321
return local_branch_name

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ progress
33
GitPython>=2.1.0
44
uritemplate.py==2.0.0
55
github3.py==0.9.5
6-
python-gitlab>=0.13
6+
python-gitlab>=0.18
77
bitbucket-api

tests/helpers.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ def action_request_list(self, namespace, repository, rq_list_data=[]):
618618
for i, rq in enumerate(rq_list_data):
619619
assert requests[i] == rq
620620

621-
def action_request_fetch(self, namespace, repository, request, pull=False, fail=False):
621+
def action_request_fetch(self, namespace, repository, request, pull=False, fail=False, remote_branch='pull', local_branch='requests'):
622622
local_slug = self.service.format_path(namespace=namespace, repository=repository, rw=False)
623623
with self.recorder.use_cassette(self._make_cassette_name()):
624624
with self.mockup_git(namespace, repository):
@@ -635,10 +635,14 @@ def action_request_fetch(self, namespace, repository, request, pull=False, fail=
635635
'Resolving deltas: 100% (5126/5126), done.',
636636
'From {}:{}/{}'.format(self.service.fqdn, namespace, repository),
637637
' * branch master -> FETCH_HEAD',
638-
' * [new branch] master -> {}/master'.format(self.service.name)]).encode('utf-8'),
638+
' * [new branch] master -> {1}/{0}'.format(request, local_branch)]).encode('utf-8'),
639639
0),
640640
('git version', b'git version 2.8.0', b'', 0),
641-
('git fetch --progress -v {0} pull/{1}/head:request/{1}'.format(self.service.name, request), b'', '\n'.join([
641+
('git fetch --progress -v {0} {2}/{1}/head:{3}/{1}'.format(
642+
self.service.name,
643+
request,
644+
remote_branch,
645+
local_branch), b'', '\n'.join([
642646
'POST git-upload-pack (140 bytes)',
643647
'remote: Counting objects: 8318, done.',
644648
'remote: Compressing objects: 100% (3/3), done.',
@@ -657,22 +661,26 @@ def action_request_fetch(self, namespace, repository, request, pull=False, fail=
657661
with self.mockup_git(namespace, repository):
658662
self.set_mock_popen_commands([
659663
('git version', b'git version 2.8.0', b'', 0),
660-
('git fetch --progress -v {0} pull/{1}/head:request/{1}'.format(self.service.name, request), b'', '\n'.join([
664+
('git fetch --progress -v {0} {2}/{1}/head:{3}/{1}'.format(
665+
self.service.name,
666+
request,
667+
remote_branch,
668+
local_branch), b'', '\n'.join([
661669
'POST git-upload-pack (140 bytes)',
662670
'remote: Counting objects: 8318, done.',
663671
'remote: Compressing objects: 100% (3/3), done.',
664672
'remote: Total 8318 (delta 0), reused 0 (delta 0), pack-reused 8315',
665673
'Receiving objects: 100% (8318/8318), 3.59 MiB | 974.00 KiB/s, done.',
666674
'Resolving deltas: 100% (5126/5126), done.',
667675
'From {}:{}/{}'.format(self.service.fqdn, namespace, repository),
668-
' * [new branch] master -> request/{}'.format(request)]).encode('utf-8'),
676+
' * [new branch] master -> {1}/{0}'.format(request, local_branch)]).encode('utf-8'),
669677
0)
670678
])
671679
self.service.request_fetch(repository, namespace, request)
672680

673681
def action_request_create(self,
674682
namespace, repository, branch,
675-
title, description,
683+
title, description, service,
676684
create_repository='test_create_requests',
677685
create_branch='pr-test'):
678686
'''
@@ -723,7 +731,7 @@ def prepare_project_for_test():
723731
test.write('La meilleure façon de ne pas avancer est de suivre une idée fixe. J.Prévert')
724732
self.repository.git.add('second_file')
725733
self.repository.git.commit(message='Second commit')
726-
self.repository.git.push('github', create_branch)
734+
self.repository.git.push(service, create_branch)
727735
yield
728736
if will_record:
729737
self.service.delete(create_repository)
@@ -749,7 +757,7 @@ def action_gist_list(self, gist=None, gist_list_data=[]):
749757
for i, g in enumerate(gist_list_data):
750758
assert gists[i] == g
751759
else:
752-
gist_files = list(self.service.gist_list())
760+
gist_files = list(self.service.gist_list(gist))
753761
for i, gf in enumerate(gist_list_data):
754762
assert gist_files[i] == gf
755763

0 commit comments

Comments
 (0)