Skip to content

Commit 00d42eb

Browse files
committed
Add gerrit review support
1 parent 2bbaeb3 commit 00d42eb

File tree

6 files changed

+153
-7
lines changed

6 files changed

+153
-7
lines changed

git_repo/repo.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -426,17 +426,18 @@ def request_edition(repository, from_branch, onto_target):
426426

427427
service = self.get_service(resolve_targets=('upstream', '{service}', 'origin'))
428428

429-
new_request = service.request_create(self.namespace,
429+
print_iter(service.request_create(
430+
self.namespace,
430431
self.repo_name,
431432
self.local_branch,
432433
self.remote_branch,
433434
self.title,
434435
self.message,
435436
self._auto_slug,
436-
request_edition)
437-
log.info('Successfully created request of `{local}` onto `{project}:{remote}`, with id `{ref}`!'.format(**new_request))
438-
if 'url' in new_request:
439-
log.info('available at: {url}'.format(**new_request))
437+
request_edition
438+
)
439+
)
440+
440441
return 0
441442

442443
@register_action('request', 'fetch')

git_repo/services/ext/gerrit.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env python
22

3+
from ...exceptions import ResourceNotFoundError
34
from ..service import register_target, RepositoryService
45

56
from gerritclient import client
@@ -92,3 +93,38 @@ def get_parent_project_url(self, namespace, repo, rw=True):
9293
# Gerrit parent project concept is quite different from other services,
9394
# so it is better to always return None here
9495
return None
96+
97+
def request_create(self, onto_user, onto_repo, from_branch, onto_branch=None, title=None, description=None, auto_slug=False, edit=None):
98+
from_branch = from_branch or self.repository.active_branch.name
99+
onto_branch = onto_branch or 'HEAD:refs/for/' + from_branch
100+
remote = self.repository.remote(self.name)
101+
info, lines = self.push(remote, onto_branch)
102+
new_changes = []
103+
new_changes_lines = False
104+
for line in lines:
105+
if line.startswith('remote:'):
106+
line = line[len('remote:'):].strip()
107+
108+
if 'New Changes' in line:
109+
new_changes_lines = True
110+
111+
if new_changes_lines and self.fqdn in line:
112+
url = line.split(' ')[0]
113+
new_changes.append(url)
114+
115+
if len(new_changes) > 0:
116+
yield 'Created new review request of `{local}` onto `{project}:{remote}`'.format(
117+
local = from_branch,
118+
project = '/'.join([onto_user, onto_repo]),
119+
remote = onto_branch
120+
)
121+
yield 'with changeset {} available at {}'
122+
for url in new_changes:
123+
yield [url, url.split('/')[-1]]
124+
else:
125+
yield 'Review request of `{local}` was not created'.format(
126+
local = from_branch
127+
)
128+
yield '{} -> {}: {}'
129+
for element in info:
130+
yield [element.local_ref, element.remote_ref_string, element.summary]

git_repo/services/service.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from git import RemoteProgress, config as git_config
1212
from progress.bar import IncrementalBar as Bar
1313

14+
from enum import Enum
1415
from urllib.parse import ParseResult
1516
from subprocess import call
1617

@@ -32,8 +33,18 @@
3233

3334
class ProgressBar(RemoteProgress): # pragma: no cover
3435
'''Nice looking progress bar for long running commands'''
35-
def setup(self, repo_name):
36-
self.bar = Bar(message='Pulling from {}'.format(repo_name), suffix='')
36+
37+
class Action(Enum):
38+
PULL = 1
39+
PUSH = 2
40+
41+
def setup(self, repo_name, action=Action.PULL):
42+
if action == ProgressBar.Action.PULL:
43+
message = 'Pulling from {}'.format(repo_name)
44+
elif action == ProgressBar.Action.PUSH:
45+
message = 'Pushing to {}'.format(repo_name)
46+
47+
self.bar = Bar(message=message, suffix='')
3748

3849
def update(self, op_code, cur_count, max_count=100, message=''):
3950
#log.info("{}, {}, {}, {}".format(op_code, cur_count, max_count, message))
@@ -311,6 +322,21 @@ def pull(self, remote, branch=None):
311322
remote.pull(progress=pb)
312323
print()
313324

325+
def push(self, remote, branch=None):
326+
'''Push a repository
327+
:param remote: git-remote instance
328+
:param branch: name of the branch to push
329+
:return: PushInfo, git push output lines
330+
'''
331+
pb = ProgressBar()
332+
pb.setup(self.name, ProgressBar.Action.PUSH)
333+
if branch:
334+
result = remote.push(branch, progress=pb)
335+
else: #pragma: no cover
336+
result = remote.push(progress=pb)
337+
print()
338+
return result, pb.other_lines
339+
314340
def fetch(self, remote, remote_branch, local_branch, force=False):
315341
'''Pull a repository
316342
:param remote: git-remote instance

tests/helpers.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,35 @@ def test_edit(repository, from_branch):
804804
edit=test_edit)
805805
return request
806806

807+
def action_request_create_by_push(self, namespace, repository, branch, remote_ref):
808+
local_slug = self.service.format_path(namespace=namespace, repository=repository, rw=True)
809+
self.repository.create_remote('all', url=local_slug)
810+
self.repository.create_remote(self.service.name, url=local_slug)
811+
812+
with self.mockup_git(namespace, repository):
813+
self.set_mock_popen_commands([
814+
('git version', b'git version 2.7.4', b'', 0),
815+
('git push --porcelain --progress {} HEAD:{}'.format(self.service.name, remote_ref), b'', '\n'.join([
816+
'Counting objects: 6, done.',
817+
'Delta compression using up to 2 threads.',
818+
'Compressing objects: 100% (6/6), done.',
819+
'Writing objects: 100% (6/6), 515 bytes | 0 bytes/s, done.',
820+
'Total 6 (delta 4), reused 0 (delta 0)',
821+
'remote: Resolving deltas: 100% (4/4)',
822+
'remote: Processing changes: new: 1, refs: 1, done',
823+
'remote: ',
824+
'remote: New Changes:',
825+
'remote: https://{}/391808 One more test'.format(self.service.fqdn),
826+
'remote: ',
827+
'To {}'.format(local_slug),
828+
'* HEAD:refs/for/{} [new branch]'.format(branch),
829+
'Done'
830+
]).encode('utf-8'), 0)
831+
])
832+
with self.recorder.use_cassette(self._make_cassette_name()):
833+
self.service.connect()
834+
self.service.request_create(namespace, repository, branch)
835+
807836
def action_gist_list(self, gist=None, gist_list_data=[]):
808837
with self.recorder.use_cassette(self._make_cassette_name()):
809838
self.service.connect()
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"http_interactions": [
3+
{
4+
"recorded_at": "2017-12-15T14:57:54",
5+
"request": {
6+
"body": {
7+
"encoding": "utf-8",
8+
"string": ""
9+
},
10+
"headers": {
11+
"Accept": "application/json",
12+
"Accept-Encoding": "identity",
13+
"Authorization": "Basic UGhhbnRvbS00MjovRk45NTI2TE84MkxYTXJucWJUNWE3Y25wSkY4b2hVTVFRQ1l6TkgzTVE=",
14+
"Connection": "keep-alive",
15+
"Content-Type": "application/json",
16+
"User-Agent": "python-requests/2.18.2"
17+
},
18+
"method": "GET",
19+
"uri": "https://review.gerrithub.io/a/config/server/version"
20+
},
21+
"response": {
22+
"body": {
23+
"encoding": "utf-8",
24+
"string": ")]}'\n\"2.13.6-3044-g7e9c06d\"\n"
25+
},
26+
"headers": {
27+
"Cache-Control": "no-cache, no-store, max-age=0, must-revalidate",
28+
"Connection": "close",
29+
"Content-Disposition": "attachment",
30+
"Content-Length": "28",
31+
"Content-Type": "application/json;charset=utf-8",
32+
"Date": "Fri, 15 Dec 2017 14:57:54 GMT",
33+
"Expires": "Mon, 01 Jan 1990 00:00:00 GMT",
34+
"Pragma": "no-cache",
35+
"Server": "Apache/2.4.6 (CentOS)",
36+
"Set-Cookie": "JSESSIONID=11nhmra540f822wlpl1srbr9s;Path=/;Secure",
37+
"X-Content-Type-Options": "nosniff"
38+
},
39+
"status": {
40+
"code": 200,
41+
"message": "OK"
42+
},
43+
"url": "https://review.gerrithub.io/a/config/server/version"
44+
}
45+
}
46+
],
47+
"recorded_with": "betamax/0.5.1"
48+
}

tests/integration/test_gerrit.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,9 @@ def get_requests_session(self):
3030
def test_00_clone(self):
3131
self.action_clone(namespace='TestUser',
3232
repository='DemoRepository')
33+
34+
def test_01_review(self):
35+
self.action_request_create_by_push(namespace='TestUser',
36+
repository='DemoRepository',
37+
branch='master',
38+
remote_ref='refs/for/master')

0 commit comments

Comments
 (0)