##// END OF EJS Templates
git: use subprocessio for hooks execution to use non-blocking subprocess.
marcink -
r369:defc08d9 default
parent child Browse files
Show More
@@ -1,482 +1,484 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # RhodeCode VCSServer provides access to different vcs backends via network.
3 # RhodeCode VCSServer provides access to different vcs backends via network.
4 # Copyright (C) 2014-2018 RhodeCode GmbH
4 # Copyright (C) 2014-2018 RhodeCode GmbH
5 #
5 #
6 # This program is free software; you can redistribute it and/or modify
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
9 # (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software Foundation,
17 # along with this program; if not, write to the Free Software Foundation,
18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
19
20 import io
20 import io
21 import os
21 import os
22 import sys
22 import sys
23 import json
23 import json
24 import logging
24 import logging
25 import collections
25 import collections
26 import importlib
26 import importlib
27 import subprocess
28
27
29 from httplib import HTTPConnection
28 from httplib import HTTPConnection
30
29
31
30
32 import mercurial.scmutil
31 import mercurial.scmutil
33 import mercurial.node
32 import mercurial.node
34 import simplejson as json
33 import simplejson as json
35
34
36 from vcsserver import exceptions
35 from vcsserver import exceptions, subprocessio, settings
37
36
38 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
39
38
40
39
41 class HooksHttpClient(object):
40 class HooksHttpClient(object):
42 connection = None
41 connection = None
43
42
44 def __init__(self, hooks_uri):
43 def __init__(self, hooks_uri):
45 self.hooks_uri = hooks_uri
44 self.hooks_uri = hooks_uri
46
45
47 def __call__(self, method, extras):
46 def __call__(self, method, extras):
48 connection = HTTPConnection(self.hooks_uri)
47 connection = HTTPConnection(self.hooks_uri)
49 body = self._serialize(method, extras)
48 body = self._serialize(method, extras)
50 connection.request('POST', '/', body)
49 connection.request('POST', '/', body)
51 response = connection.getresponse()
50 response = connection.getresponse()
52 return json.loads(response.read())
51 return json.loads(response.read())
53
52
54 def _serialize(self, hook_name, extras):
53 def _serialize(self, hook_name, extras):
55 data = {
54 data = {
56 'method': hook_name,
55 'method': hook_name,
57 'extras': extras
56 'extras': extras
58 }
57 }
59 return json.dumps(data)
58 return json.dumps(data)
60
59
61
60
62 class HooksDummyClient(object):
61 class HooksDummyClient(object):
63 def __init__(self, hooks_module):
62 def __init__(self, hooks_module):
64 self._hooks_module = importlib.import_module(hooks_module)
63 self._hooks_module = importlib.import_module(hooks_module)
65
64
66 def __call__(self, hook_name, extras):
65 def __call__(self, hook_name, extras):
67 with self._hooks_module.Hooks() as hooks:
66 with self._hooks_module.Hooks() as hooks:
68 return getattr(hooks, hook_name)(extras)
67 return getattr(hooks, hook_name)(extras)
69
68
70
69
71 class RemoteMessageWriter(object):
70 class RemoteMessageWriter(object):
72 """Writer base class."""
71 """Writer base class."""
73 def write(self, message):
72 def write(self, message):
74 raise NotImplementedError()
73 raise NotImplementedError()
75
74
76
75
77 class HgMessageWriter(RemoteMessageWriter):
76 class HgMessageWriter(RemoteMessageWriter):
78 """Writer that knows how to send messages to mercurial clients."""
77 """Writer that knows how to send messages to mercurial clients."""
79
78
80 def __init__(self, ui):
79 def __init__(self, ui):
81 self.ui = ui
80 self.ui = ui
82
81
83 def write(self, message):
82 def write(self, message):
84 # TODO: Check why the quiet flag is set by default.
83 # TODO: Check why the quiet flag is set by default.
85 old = self.ui.quiet
84 old = self.ui.quiet
86 self.ui.quiet = False
85 self.ui.quiet = False
87 self.ui.status(message.encode('utf-8'))
86 self.ui.status(message.encode('utf-8'))
88 self.ui.quiet = old
87 self.ui.quiet = old
89
88
90
89
91 class GitMessageWriter(RemoteMessageWriter):
90 class GitMessageWriter(RemoteMessageWriter):
92 """Writer that knows how to send messages to git clients."""
91 """Writer that knows how to send messages to git clients."""
93
92
94 def __init__(self, stdout=None):
93 def __init__(self, stdout=None):
95 self.stdout = stdout or sys.stdout
94 self.stdout = stdout or sys.stdout
96
95
97 def write(self, message):
96 def write(self, message):
98 self.stdout.write(message.encode('utf-8'))
97 self.stdout.write(message.encode('utf-8'))
99
98
100
99
101 def _handle_exception(result):
100 def _handle_exception(result):
102 exception_class = result.get('exception')
101 exception_class = result.get('exception')
103 exception_traceback = result.get('exception_traceback')
102 exception_traceback = result.get('exception_traceback')
104
103
105 if exception_traceback:
104 if exception_traceback:
106 log.error('Got traceback from remote call:%s', exception_traceback)
105 log.error('Got traceback from remote call:%s', exception_traceback)
107
106
108 if exception_class == 'HTTPLockedRC':
107 if exception_class == 'HTTPLockedRC':
109 raise exceptions.RepositoryLockedException(*result['exception_args'])
108 raise exceptions.RepositoryLockedException(*result['exception_args'])
110 elif exception_class == 'RepositoryError':
109 elif exception_class == 'RepositoryError':
111 raise exceptions.VcsException(*result['exception_args'])
110 raise exceptions.VcsException(*result['exception_args'])
112 elif exception_class:
111 elif exception_class:
113 raise Exception('Got remote exception "%s" with args "%s"' %
112 raise Exception('Got remote exception "%s" with args "%s"' %
114 (exception_class, result['exception_args']))
113 (exception_class, result['exception_args']))
115
114
116
115
117 def _get_hooks_client(extras):
116 def _get_hooks_client(extras):
118 if 'hooks_uri' in extras:
117 if 'hooks_uri' in extras:
119 protocol = extras.get('hooks_protocol')
118 protocol = extras.get('hooks_protocol')
120 return HooksHttpClient(extras['hooks_uri'])
119 return HooksHttpClient(extras['hooks_uri'])
121 else:
120 else:
122 return HooksDummyClient(extras['hooks_module'])
121 return HooksDummyClient(extras['hooks_module'])
123
122
124
123
125 def _call_hook(hook_name, extras, writer):
124 def _call_hook(hook_name, extras, writer):
126 hooks = _get_hooks_client(extras)
125 hooks = _get_hooks_client(extras)
127 result = hooks(hook_name, extras)
126 result = hooks(hook_name, extras)
128 log.debug('Hooks got result: %s', result)
127 log.debug('Hooks got result: %s', result)
129 writer.write(result['output'])
128 writer.write(result['output'])
130 _handle_exception(result)
129 _handle_exception(result)
131
130
132 return result['status']
131 return result['status']
133
132
134
133
135 def _extras_from_ui(ui):
134 def _extras_from_ui(ui):
136 hook_data = ui.config('rhodecode', 'RC_SCM_DATA')
135 hook_data = ui.config('rhodecode', 'RC_SCM_DATA')
137 if not hook_data:
136 if not hook_data:
138 # maybe it's inside environ ?
137 # maybe it's inside environ ?
139 env_hook_data = os.environ.get('RC_SCM_DATA')
138 env_hook_data = os.environ.get('RC_SCM_DATA')
140 if env_hook_data:
139 if env_hook_data:
141 hook_data = env_hook_data
140 hook_data = env_hook_data
142
141
143 extras = {}
142 extras = {}
144 if hook_data:
143 if hook_data:
145 extras = json.loads(hook_data)
144 extras = json.loads(hook_data)
146 return extras
145 return extras
147
146
148
147
149 def _rev_range_hash(repo, node):
148 def _rev_range_hash(repo, node):
150
149
151 commits = []
150 commits = []
152 for rev in xrange(repo[node], len(repo)):
151 for rev in xrange(repo[node], len(repo)):
153 ctx = repo[rev]
152 ctx = repo[rev]
154 commit_id = mercurial.node.hex(ctx.node())
153 commit_id = mercurial.node.hex(ctx.node())
155 branch = ctx.branch()
154 branch = ctx.branch()
156 commits.append((commit_id, branch))
155 commits.append((commit_id, branch))
157
156
158 return commits
157 return commits
159
158
160
159
161 def repo_size(ui, repo, **kwargs):
160 def repo_size(ui, repo, **kwargs):
162 extras = _extras_from_ui(ui)
161 extras = _extras_from_ui(ui)
163 return _call_hook('repo_size', extras, HgMessageWriter(ui))
162 return _call_hook('repo_size', extras, HgMessageWriter(ui))
164
163
165
164
166 def pre_pull(ui, repo, **kwargs):
165 def pre_pull(ui, repo, **kwargs):
167 extras = _extras_from_ui(ui)
166 extras = _extras_from_ui(ui)
168 return _call_hook('pre_pull', extras, HgMessageWriter(ui))
167 return _call_hook('pre_pull', extras, HgMessageWriter(ui))
169
168
170
169
171 def pre_pull_ssh(ui, repo, **kwargs):
170 def pre_pull_ssh(ui, repo, **kwargs):
172 extras = _extras_from_ui(ui)
171 extras = _extras_from_ui(ui)
173 if extras and extras.get('SSH'):
172 if extras and extras.get('SSH'):
174 return pre_pull(ui, repo, **kwargs)
173 return pre_pull(ui, repo, **kwargs)
175 return 0
174 return 0
176
175
177
176
178 def post_pull(ui, repo, **kwargs):
177 def post_pull(ui, repo, **kwargs):
179 extras = _extras_from_ui(ui)
178 extras = _extras_from_ui(ui)
180 return _call_hook('post_pull', extras, HgMessageWriter(ui))
179 return _call_hook('post_pull', extras, HgMessageWriter(ui))
181
180
182
181
183 def post_pull_ssh(ui, repo, **kwargs):
182 def post_pull_ssh(ui, repo, **kwargs):
184 extras = _extras_from_ui(ui)
183 extras = _extras_from_ui(ui)
185 if extras and extras.get('SSH'):
184 if extras and extras.get('SSH'):
186 return post_pull(ui, repo, **kwargs)
185 return post_pull(ui, repo, **kwargs)
187 return 0
186 return 0
188
187
189
188
190 def pre_push(ui, repo, node=None, **kwargs):
189 def pre_push(ui, repo, node=None, **kwargs):
191 extras = _extras_from_ui(ui)
190 extras = _extras_from_ui(ui)
192
191
193 rev_data = []
192 rev_data = []
194 if node and kwargs.get('hooktype') == 'pretxnchangegroup':
193 if node and kwargs.get('hooktype') == 'pretxnchangegroup':
195 branches = collections.defaultdict(list)
194 branches = collections.defaultdict(list)
196 for commit_id, branch in _rev_range_hash(repo, node):
195 for commit_id, branch in _rev_range_hash(repo, node):
197 branches[branch].append(commit_id)
196 branches[branch].append(commit_id)
198
197
199 for branch, commits in branches.iteritems():
198 for branch, commits in branches.iteritems():
200 old_rev = kwargs.get('node_last') or commits[0]
199 old_rev = kwargs.get('node_last') or commits[0]
201 rev_data.append({
200 rev_data.append({
202 'old_rev': old_rev,
201 'old_rev': old_rev,
203 'new_rev': commits[-1],
202 'new_rev': commits[-1],
204 'ref': '',
203 'ref': '',
205 'type': 'branch',
204 'type': 'branch',
206 'name': branch,
205 'name': branch,
207 })
206 })
208
207
209 extras['commit_ids'] = rev_data
208 extras['commit_ids'] = rev_data
210 return _call_hook('pre_push', extras, HgMessageWriter(ui))
209 return _call_hook('pre_push', extras, HgMessageWriter(ui))
211
210
212
211
213 def pre_push_ssh(ui, repo, node=None, **kwargs):
212 def pre_push_ssh(ui, repo, node=None, **kwargs):
214 if _extras_from_ui(ui).get('SSH'):
213 if _extras_from_ui(ui).get('SSH'):
215 return pre_push(ui, repo, node, **kwargs)
214 return pre_push(ui, repo, node, **kwargs)
216
215
217 return 0
216 return 0
218
217
219
218
220 def pre_push_ssh_auth(ui, repo, node=None, **kwargs):
219 def pre_push_ssh_auth(ui, repo, node=None, **kwargs):
221 extras = _extras_from_ui(ui)
220 extras = _extras_from_ui(ui)
222 if extras.get('SSH'):
221 if extras.get('SSH'):
223 permission = extras['SSH_PERMISSIONS']
222 permission = extras['SSH_PERMISSIONS']
224
223
225 if 'repository.write' == permission or 'repository.admin' == permission:
224 if 'repository.write' == permission or 'repository.admin' == permission:
226 return 0
225 return 0
227
226
228 # non-zero ret code
227 # non-zero ret code
229 return 1
228 return 1
230
229
231 return 0
230 return 0
232
231
233
232
234 def post_push(ui, repo, node, **kwargs):
233 def post_push(ui, repo, node, **kwargs):
235 extras = _extras_from_ui(ui)
234 extras = _extras_from_ui(ui)
236
235
237 commit_ids = []
236 commit_ids = []
238 branches = []
237 branches = []
239 bookmarks = []
238 bookmarks = []
240 tags = []
239 tags = []
241
240
242 for commit_id, branch in _rev_range_hash(repo, node):
241 for commit_id, branch in _rev_range_hash(repo, node):
243 commit_ids.append(commit_id)
242 commit_ids.append(commit_id)
244 if branch not in branches:
243 if branch not in branches:
245 branches.append(branch)
244 branches.append(branch)
246
245
247 if hasattr(ui, '_rc_pushkey_branches'):
246 if hasattr(ui, '_rc_pushkey_branches'):
248 bookmarks = ui._rc_pushkey_branches
247 bookmarks = ui._rc_pushkey_branches
249
248
250 extras['commit_ids'] = commit_ids
249 extras['commit_ids'] = commit_ids
251 extras['new_refs'] = {
250 extras['new_refs'] = {
252 'branches': branches,
251 'branches': branches,
253 'bookmarks': bookmarks,
252 'bookmarks': bookmarks,
254 'tags': tags
253 'tags': tags
255 }
254 }
256
255
257 return _call_hook('post_push', extras, HgMessageWriter(ui))
256 return _call_hook('post_push', extras, HgMessageWriter(ui))
258
257
259
258
260 def post_push_ssh(ui, repo, node, **kwargs):
259 def post_push_ssh(ui, repo, node, **kwargs):
261 if _extras_from_ui(ui).get('SSH'):
260 if _extras_from_ui(ui).get('SSH'):
262 return post_push(ui, repo, node, **kwargs)
261 return post_push(ui, repo, node, **kwargs)
263 return 0
262 return 0
264
263
265
264
266 def key_push(ui, repo, **kwargs):
265 def key_push(ui, repo, **kwargs):
267 if kwargs['new'] != '0' and kwargs['namespace'] == 'bookmarks':
266 if kwargs['new'] != '0' and kwargs['namespace'] == 'bookmarks':
268 # store new bookmarks in our UI object propagated later to post_push
267 # store new bookmarks in our UI object propagated later to post_push
269 ui._rc_pushkey_branches = repo[kwargs['key']].bookmarks()
268 ui._rc_pushkey_branches = repo[kwargs['key']].bookmarks()
270 return
269 return
271
270
272
271
273 # backward compat
272 # backward compat
274 log_pull_action = post_pull
273 log_pull_action = post_pull
275
274
276 # backward compat
275 # backward compat
277 log_push_action = post_push
276 log_push_action = post_push
278
277
279
278
280 def handle_git_pre_receive(unused_repo_path, unused_revs, unused_env):
279 def handle_git_pre_receive(unused_repo_path, unused_revs, unused_env):
281 """
280 """
282 Old hook name: keep here for backward compatibility.
281 Old hook name: keep here for backward compatibility.
283
282
284 This is only required when the installed git hooks are not upgraded.
283 This is only required when the installed git hooks are not upgraded.
285 """
284 """
286 pass
285 pass
287
286
288
287
289 def handle_git_post_receive(unused_repo_path, unused_revs, unused_env):
288 def handle_git_post_receive(unused_repo_path, unused_revs, unused_env):
290 """
289 """
291 Old hook name: keep here for backward compatibility.
290 Old hook name: keep here for backward compatibility.
292
291
293 This is only required when the installed git hooks are not upgraded.
292 This is only required when the installed git hooks are not upgraded.
294 """
293 """
295 pass
294 pass
296
295
297
296
298 HookResponse = collections.namedtuple('HookResponse', ('status', 'output'))
297 HookResponse = collections.namedtuple('HookResponse', ('status', 'output'))
299
298
300
299
301 def git_pre_pull(extras):
300 def git_pre_pull(extras):
302 """
301 """
303 Pre pull hook.
302 Pre pull hook.
304
303
305 :param extras: dictionary containing the keys defined in simplevcs
304 :param extras: dictionary containing the keys defined in simplevcs
306 :type extras: dict
305 :type extras: dict
307
306
308 :return: status code of the hook. 0 for success.
307 :return: status code of the hook. 0 for success.
309 :rtype: int
308 :rtype: int
310 """
309 """
311 if 'pull' not in extras['hooks']:
310 if 'pull' not in extras['hooks']:
312 return HookResponse(0, '')
311 return HookResponse(0, '')
313
312
314 stdout = io.BytesIO()
313 stdout = io.BytesIO()
315 try:
314 try:
316 status = _call_hook('pre_pull', extras, GitMessageWriter(stdout))
315 status = _call_hook('pre_pull', extras, GitMessageWriter(stdout))
317 except Exception as error:
316 except Exception as error:
318 status = 128
317 status = 128
319 stdout.write('ERROR: %s\n' % str(error))
318 stdout.write('ERROR: %s\n' % str(error))
320
319
321 return HookResponse(status, stdout.getvalue())
320 return HookResponse(status, stdout.getvalue())
322
321
323
322
324 def git_post_pull(extras):
323 def git_post_pull(extras):
325 """
324 """
326 Post pull hook.
325 Post pull hook.
327
326
328 :param extras: dictionary containing the keys defined in simplevcs
327 :param extras: dictionary containing the keys defined in simplevcs
329 :type extras: dict
328 :type extras: dict
330
329
331 :return: status code of the hook. 0 for success.
330 :return: status code of the hook. 0 for success.
332 :rtype: int
331 :rtype: int
333 """
332 """
334 if 'pull' not in extras['hooks']:
333 if 'pull' not in extras['hooks']:
335 return HookResponse(0, '')
334 return HookResponse(0, '')
336
335
337 stdout = io.BytesIO()
336 stdout = io.BytesIO()
338 try:
337 try:
339 status = _call_hook('post_pull', extras, GitMessageWriter(stdout))
338 status = _call_hook('post_pull', extras, GitMessageWriter(stdout))
340 except Exception as error:
339 except Exception as error:
341 status = 128
340 status = 128
342 stdout.write('ERROR: %s\n' % error)
341 stdout.write('ERROR: %s\n' % error)
343
342
344 return HookResponse(status, stdout.getvalue())
343 return HookResponse(status, stdout.getvalue())
345
344
346
345
347 def _parse_git_ref_lines(revision_lines):
346 def _parse_git_ref_lines(revision_lines):
348 rev_data = []
347 rev_data = []
349 for revision_line in revision_lines or []:
348 for revision_line in revision_lines or []:
350 old_rev, new_rev, ref = revision_line.strip().split(' ')
349 old_rev, new_rev, ref = revision_line.strip().split(' ')
351 ref_data = ref.split('/', 2)
350 ref_data = ref.split('/', 2)
352 if ref_data[1] in ('tags', 'heads'):
351 if ref_data[1] in ('tags', 'heads'):
353 rev_data.append({
352 rev_data.append({
354 'old_rev': old_rev,
353 'old_rev': old_rev,
355 'new_rev': new_rev,
354 'new_rev': new_rev,
356 'ref': ref,
355 'ref': ref,
357 'type': ref_data[1],
356 'type': ref_data[1],
358 'name': ref_data[2],
357 'name': ref_data[2],
359 })
358 })
360 return rev_data
359 return rev_data
361
360
362
361
363 def git_pre_receive(unused_repo_path, revision_lines, env):
362 def git_pre_receive(unused_repo_path, revision_lines, env):
364 """
363 """
365 Pre push hook.
364 Pre push hook.
366
365
367 :param extras: dictionary containing the keys defined in simplevcs
366 :param extras: dictionary containing the keys defined in simplevcs
368 :type extras: dict
367 :type extras: dict
369
368
370 :return: status code of the hook. 0 for success.
369 :return: status code of the hook. 0 for success.
371 :rtype: int
370 :rtype: int
372 """
371 """
373 extras = json.loads(env['RC_SCM_DATA'])
372 extras = json.loads(env['RC_SCM_DATA'])
374 rev_data = _parse_git_ref_lines(revision_lines)
373 rev_data = _parse_git_ref_lines(revision_lines)
375 if 'push' not in extras['hooks']:
374 if 'push' not in extras['hooks']:
376 return 0
375 return 0
377 extras['commit_ids'] = rev_data
376 extras['commit_ids'] = rev_data
378 return _call_hook('pre_push', extras, GitMessageWriter())
377 return _call_hook('pre_push', extras, GitMessageWriter())
379
378
380
379
381 def _run_command(arguments):
380 def _run_command(arguments):
382 """
381 """
383 Run the specified command and return the stdout.
382 Run the specified command and return the stdout.
384
383
385 :param arguments: sequence of program arguments (including the program name)
384 :param arguments: sequence of program arguments (including the program name)
386 :type arguments: list[str]
385 :type arguments: list[str]
387 """
386 """
388 # TODO(skreft): refactor this method and all the other similar ones.
389 # Probably this should be using subprocessio.
390 process = subprocess.Popen(
391 arguments, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
392 stdout, stderr = process.communicate()
393
387
394 if process.returncode != 0:
388 cmd = arguments
395 raise Exception(
389 try:
396 'Command %s exited with exit code %s: stderr:%s' % (
390 gitenv = os.environ.copy()
397 arguments, process.returncode, stderr))
391 _opts = {'env': gitenv, 'shell': False, 'fail_on_stderr': False}
392 p = subprocessio.SubprocessIOChunker(cmd, **_opts)
393 stdout = ''.join(p)
394 except (EnvironmentError, OSError) as err:
395 cmd = ' '.join(cmd) # human friendly CMD
396 tb_err = ("Couldn't run git command (%s).\n"
397 "Original error was:%s\n" % (cmd, err))
398 log.exception(tb_err)
399 raise Exception(tb_err)
398
400
399 return stdout
401 return stdout
400
402
401
403
402 def git_post_receive(unused_repo_path, revision_lines, env):
404 def git_post_receive(unused_repo_path, revision_lines, env):
403 """
405 """
404 Post push hook.
406 Post push hook.
405
407
406 :param extras: dictionary containing the keys defined in simplevcs
408 :param extras: dictionary containing the keys defined in simplevcs
407 :type extras: dict
409 :type extras: dict
408
410
409 :return: status code of the hook. 0 for success.
411 :return: status code of the hook. 0 for success.
410 :rtype: int
412 :rtype: int
411 """
413 """
412 extras = json.loads(env['RC_SCM_DATA'])
414 extras = json.loads(env['RC_SCM_DATA'])
413 if 'push' not in extras['hooks']:
415 if 'push' not in extras['hooks']:
414 return 0
416 return 0
415
417
416 rev_data = _parse_git_ref_lines(revision_lines)
418 rev_data = _parse_git_ref_lines(revision_lines)
417
419
418 git_revs = []
420 git_revs = []
419
421
420 # N.B.(skreft): it is ok to just call git, as git before calling a
422 # N.B.(skreft): it is ok to just call git, as git before calling a
421 # subcommand sets the PATH environment variable so that it point to the
423 # subcommand sets the PATH environment variable so that it point to the
422 # correct version of the git executable.
424 # correct version of the git executable.
423 empty_commit_id = '0' * 40
425 empty_commit_id = '0' * 40
424 branches = []
426 branches = []
425 tags = []
427 tags = []
426 for push_ref in rev_data:
428 for push_ref in rev_data:
427 type_ = push_ref['type']
429 type_ = push_ref['type']
428
430
429 if type_ == 'heads':
431 if type_ == 'heads':
430 if push_ref['old_rev'] == empty_commit_id:
432 if push_ref['old_rev'] == empty_commit_id:
431 # starting new branch case
433 # starting new branch case
432 if push_ref['name'] not in branches:
434 if push_ref['name'] not in branches:
433 branches.append(push_ref['name'])
435 branches.append(push_ref['name'])
434
436
435 # Fix up head revision if needed
437 # Fix up head revision if needed
436 cmd = ['git', 'show', 'HEAD']
438 cmd = [settings.GIT_EXECUTABLE, 'show', 'HEAD']
437 try:
439 try:
438 _run_command(cmd)
440 _run_command(cmd)
439 except Exception:
441 except Exception:
440 cmd = ['git', 'symbolic-ref', 'HEAD',
442 cmd = [settings.GIT_EXECUTABLE, 'symbolic-ref', 'HEAD',
441 'refs/heads/%s' % push_ref['name']]
443 'refs/heads/%s' % push_ref['name']]
442 print("Setting default branch to %s" % push_ref['name'])
444 print("Setting default branch to %s" % push_ref['name'])
443 _run_command(cmd)
445 _run_command(cmd)
444
446
445 cmd = ['git', 'for-each-ref', '--format=%(refname)',
447 cmd = [settings.GIT_EXECUTABLE, 'for-each-ref', '--format=%(refname)',
446 'refs/heads/*']
448 'refs/heads/*']
447 heads = _run_command(cmd)
449 heads = _run_command(cmd)
448 heads = heads.replace(push_ref['ref'], '')
450 heads = heads.replace(push_ref['ref'], '')
449 heads = ' '.join(head for head in heads.splitlines() if head)
451 heads = ' '.join(head for head in heads.splitlines() if head)
450 cmd = ['git', 'log', '--reverse', '--pretty=format:%H',
452 cmd = [settings.GIT_EXECUTABLE, 'log', '--reverse', '--pretty=format:%H',
451 '--', push_ref['new_rev'], '--not', heads]
453 '--', push_ref['new_rev'], '--not', heads]
452 git_revs.extend(_run_command(cmd).splitlines())
454 git_revs.extend(_run_command(cmd).splitlines())
453 elif push_ref['new_rev'] == empty_commit_id:
455 elif push_ref['new_rev'] == empty_commit_id:
454 # delete branch case
456 # delete branch case
455 git_revs.append('delete_branch=>%s' % push_ref['name'])
457 git_revs.append('delete_branch=>%s' % push_ref['name'])
456 else:
458 else:
457 if push_ref['name'] not in branches:
459 if push_ref['name'] not in branches:
458 branches.append(push_ref['name'])
460 branches.append(push_ref['name'])
459
461
460 cmd = ['git', 'log',
462 cmd = [settings.GIT_EXECUTABLE, 'log',
461 '{old_rev}..{new_rev}'.format(**push_ref),
463 '{old_rev}..{new_rev}'.format(**push_ref),
462 '--reverse', '--pretty=format:%H']
464 '--reverse', '--pretty=format:%H']
463 git_revs.extend(_run_command(cmd).splitlines())
465 git_revs.extend(_run_command(cmd).splitlines())
464 elif type_ == 'tags':
466 elif type_ == 'tags':
465 if push_ref['name'] not in tags:
467 if push_ref['name'] not in tags:
466 tags.append(push_ref['name'])
468 tags.append(push_ref['name'])
467 git_revs.append('tag=>%s' % push_ref['name'])
469 git_revs.append('tag=>%s' % push_ref['name'])
468
470
469 extras['commit_ids'] = git_revs
471 extras['commit_ids'] = git_revs
470 extras['new_refs'] = {
472 extras['new_refs'] = {
471 'branches': branches,
473 'branches': branches,
472 'bookmarks': [],
474 'bookmarks': [],
473 'tags': tags,
475 'tags': tags,
474 }
476 }
475
477
476 if 'repo_size' in extras['hooks']:
478 if 'repo_size' in extras['hooks']:
477 try:
479 try:
478 _call_hook('repo_size', extras, GitMessageWriter())
480 _call_hook('repo_size', extras, GitMessageWriter())
479 except:
481 except:
480 pass
482 pass
481
483
482 return _call_hook('post_push', extras, GitMessageWriter())
484 return _call_hook('post_push', extras, GitMessageWriter())
General Comments 0
You need to be logged in to leave comments. Login now