##// END OF EJS Templates
hooks: make_lock is tristate...
Mads Kiilerich -
r3672:55585c86 beta
parent child Browse files
Show More
@@ -1,390 +1,390 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.hooks
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Hooks runned by rhodecode
7 7
8 8 :created_on: Aug 6, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 import os
26 26 import sys
27 27 import time
28 28 import binascii
29 29 import traceback
30 30 from inspect import isfunction
31 31
32 32 from mercurial.scmutil import revrange
33 33 from mercurial.node import nullrev
34 34
35 35 from rhodecode.lib import helpers as h
36 36 from rhodecode.lib.utils import action_logger
37 37 from rhodecode.lib.vcs.backends.base import EmptyChangeset
38 38 from rhodecode.lib.compat import json
39 39 from rhodecode.lib.exceptions import HTTPLockedRC
40 40 from rhodecode.lib.utils2 import safe_str, _extract_extras
41 41 from rhodecode.model.db import Repository, User
42 42
43 43
44 44 def _get_scm_size(alias, root_path):
45 45
46 46 if not alias.startswith('.'):
47 47 alias += '.'
48 48
49 49 size_scm, size_root = 0, 0
50 50 for path, dirs, files in os.walk(safe_str(root_path)):
51 51 if path.find(alias) != -1:
52 52 for f in files:
53 53 try:
54 54 size_scm += os.path.getsize(os.path.join(path, f))
55 55 except OSError:
56 56 pass
57 57 else:
58 58 for f in files:
59 59 try:
60 60 size_root += os.path.getsize(os.path.join(path, f))
61 61 except OSError:
62 62 pass
63 63
64 64 size_scm_f = h.format_byte_size(size_scm)
65 65 size_root_f = h.format_byte_size(size_root)
66 66 size_total_f = h.format_byte_size(size_root + size_scm)
67 67
68 68 return size_scm_f, size_root_f, size_total_f
69 69
70 70
71 71 def repo_size(ui, repo, hooktype=None, **kwargs):
72 72 """
73 73 Presents size of repository after push
74 74
75 75 :param ui:
76 76 :param repo:
77 77 :param hooktype:
78 78 """
79 79
80 80 size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', repo.root)
81 81
82 82 last_cs = repo[len(repo) - 1]
83 83
84 84 msg = ('Repository size .hg:%s repo:%s total:%s\n'
85 85 'Last revision is now r%s:%s\n') % (
86 86 size_hg_f, size_root_f, size_total_f, last_cs.rev(), last_cs.hex()[:12]
87 87 )
88 88
89 89 sys.stdout.write(msg)
90 90
91 91
92 92 def pre_push(ui, repo, **kwargs):
93 93 # pre push function, currently used to ban pushing when
94 94 # repository is locked
95 95 ex = _extract_extras()
96 96
97 97 usr = User.get_by_username(ex.username)
98 98 if ex.locked_by[0] and usr.user_id != int(ex.locked_by[0]):
99 99 locked_by = User.get(ex.locked_by[0]).username
100 100 # this exception is interpreted in git/hg middlewares and based
101 101 # on that proper return code is server to client
102 102 _http_ret = HTTPLockedRC(ex.repository, locked_by)
103 103 if str(_http_ret.code).startswith('2'):
104 104 #2xx Codes don't raise exceptions
105 105 sys.stdout.write(_http_ret.title)
106 106 else:
107 107 raise _http_ret
108 108
109 109
110 110 def pre_pull(ui, repo, **kwargs):
111 111 # pre push function, currently used to ban pushing when
112 112 # repository is locked
113 113 ex = _extract_extras()
114 114 if ex.locked_by[0]:
115 115 locked_by = User.get(ex.locked_by[0]).username
116 116 # this exception is interpreted in git/hg middlewares and based
117 117 # on that proper return code is server to client
118 118 _http_ret = HTTPLockedRC(ex.repository, locked_by)
119 119 if str(_http_ret.code).startswith('2'):
120 120 #2xx Codes don't raise exceptions
121 121 sys.stdout.write(_http_ret.title)
122 122 else:
123 123 raise _http_ret
124 124
125 125
126 126 def log_pull_action(ui, repo, **kwargs):
127 127 """
128 128 Logs user last pull action
129 129
130 130 :param ui:
131 131 :param repo:
132 132 """
133 133 ex = _extract_extras()
134 134
135 135 user = User.get_by_username(ex.username)
136 136 action = 'pull'
137 137 action_logger(user, action, ex.repository, ex.ip, commit=True)
138 138 # extension hook call
139 139 from rhodecode import EXTENSIONS
140 140 callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
141 141 if isfunction(callback):
142 142 kw = {}
143 143 kw.update(ex)
144 144 callback(**kw)
145 145
146 if ex.make_lock:
146 if ex.make_lock is not None and ex.make_lock:
147 147 Repository.lock(Repository.get_by_repo_name(ex.repository), user.user_id)
148 148 #msg = 'Made lock on repo `%s`' % repository
149 149 #sys.stdout.write(msg)
150 150
151 151 if ex.locked_by[0]:
152 152 locked_by = User.get(ex.locked_by[0]).username
153 153 _http_ret = HTTPLockedRC(ex.repository, locked_by)
154 154 if str(_http_ret.code).startswith('2'):
155 155 #2xx Codes don't raise exceptions
156 156 sys.stdout.write(_http_ret.title)
157 157 return 0
158 158
159 159
160 160 def log_push_action(ui, repo, **kwargs):
161 161 """
162 162 Maps user last push action to new changeset id, from mercurial
163 163
164 164 :param ui:
165 165 :param repo: repo object containing the `ui` object
166 166 """
167 167
168 168 ex = _extract_extras()
169 169
170 170 action = ex.action + ':%s'
171 171
172 172 if ex.scm == 'hg':
173 173 node = kwargs['node']
174 174
175 175 def get_revs(repo, rev_opt):
176 176 if rev_opt:
177 177 revs = revrange(repo, rev_opt)
178 178
179 179 if len(revs) == 0:
180 180 return (nullrev, nullrev)
181 181 return (max(revs), min(revs))
182 182 else:
183 183 return (len(repo) - 1, 0)
184 184
185 185 stop, start = get_revs(repo, [node + ':'])
186 186 h = binascii.hexlify
187 187 revs = [h(repo[r].node()) for r in xrange(start, stop + 1)]
188 188 elif ex.scm == 'git':
189 189 revs = kwargs.get('_git_revs', [])
190 190 if '_git_revs' in kwargs:
191 191 kwargs.pop('_git_revs')
192 192
193 193 action = action % ','.join(revs)
194 194
195 195 action_logger(ex.username, action, ex.repository, ex.ip, commit=True)
196 196
197 197 # extension hook call
198 198 from rhodecode import EXTENSIONS
199 199 callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
200 200 if isfunction(callback):
201 201 kw = {'pushed_revs': revs}
202 202 kw.update(ex)
203 203 callback(**kw)
204 204
205 if not ex.make_lock:
205 if ex.make_lock is not None and not ex.make_lock:
206 206 Repository.unlock(Repository.get_by_repo_name(ex.repository))
207 207 msg = 'Released lock on repo `%s`\n' % ex.repository
208 208 sys.stdout.write(msg)
209 209
210 210 if ex.locked_by[0]:
211 211 locked_by = User.get(ex.locked_by[0]).username
212 212 _http_ret = HTTPLockedRC(ex.repository, locked_by)
213 213 if str(_http_ret.code).startswith('2'):
214 214 #2xx Codes don't raise exceptions
215 215 sys.stdout.write(_http_ret.title)
216 216
217 217 return 0
218 218
219 219
220 220 def log_create_repository(repository_dict, created_by, **kwargs):
221 221 """
222 222 Post create repository Hook. This is a dummy function for admins to re-use
223 223 if needed. It's taken from rhodecode-extensions module and executed
224 224 if present
225 225
226 226 :param repository: dict dump of repository object
227 227 :param created_by: username who created repository
228 228
229 229 available keys of repository_dict:
230 230
231 231 'repo_type',
232 232 'description',
233 233 'private',
234 234 'created_on',
235 235 'enable_downloads',
236 236 'repo_id',
237 237 'user_id',
238 238 'enable_statistics',
239 239 'clone_uri',
240 240 'fork_id',
241 241 'group_id',
242 242 'repo_name'
243 243
244 244 """
245 245 from rhodecode import EXTENSIONS
246 246 callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
247 247 if isfunction(callback):
248 248 kw = {}
249 249 kw.update(repository_dict)
250 250 kw.update({'created_by': created_by})
251 251 kw.update(kwargs)
252 252 return callback(**kw)
253 253
254 254 return 0
255 255
256 256
257 257 def log_delete_repository(repository_dict, deleted_by, **kwargs):
258 258 """
259 259 Post delete repository Hook. This is a dummy function for admins to re-use
260 260 if needed. It's taken from rhodecode-extensions module and executed
261 261 if present
262 262
263 263 :param repository: dict dump of repository object
264 264 :param deleted_by: username who deleted the repository
265 265
266 266 available keys of repository_dict:
267 267
268 268 'repo_type',
269 269 'description',
270 270 'private',
271 271 'created_on',
272 272 'enable_downloads',
273 273 'repo_id',
274 274 'user_id',
275 275 'enable_statistics',
276 276 'clone_uri',
277 277 'fork_id',
278 278 'group_id',
279 279 'repo_name'
280 280
281 281 """
282 282 from rhodecode import EXTENSIONS
283 283 callback = getattr(EXTENSIONS, 'DELETE_REPO_HOOK', None)
284 284 if isfunction(callback):
285 285 kw = {}
286 286 kw.update(repository_dict)
287 287 kw.update({'deleted_by': deleted_by,
288 288 'deleted_on': time.time()})
289 289 kw.update(kwargs)
290 290 return callback(**kw)
291 291
292 292 return 0
293 293
294 294
295 295 handle_git_pre_receive = (lambda repo_path, revs, env:
296 296 handle_git_receive(repo_path, revs, env, hook_type='pre'))
297 297 handle_git_post_receive = (lambda repo_path, revs, env:
298 298 handle_git_receive(repo_path, revs, env, hook_type='post'))
299 299
300 300
301 301 def handle_git_receive(repo_path, revs, env, hook_type='post'):
302 302 """
303 303 A really hacky method that is runned by git post-receive hook and logs
304 304 an push action together with pushed revisions. It's executed by subprocess
305 305 thus needs all info to be able to create a on the fly pylons enviroment,
306 306 connect to database and run the logging code. Hacky as sh*t but works.
307 307
308 308 :param repo_path:
309 309 :type repo_path:
310 310 :param revs:
311 311 :type revs:
312 312 :param env:
313 313 :type env:
314 314 """
315 315 from paste.deploy import appconfig
316 316 from sqlalchemy import engine_from_config
317 317 from rhodecode.config.environment import load_environment
318 318 from rhodecode.model import init_model
319 319 from rhodecode.model.db import RhodeCodeUi
320 320 from rhodecode.lib.utils import make_ui
321 321 extras = _extract_extras(env)
322 322
323 323 path, ini_name = os.path.split(extras['config'])
324 324 conf = appconfig('config:%s' % ini_name, relative_to=path)
325 325 load_environment(conf.global_conf, conf.local_conf)
326 326
327 327 engine = engine_from_config(conf, 'sqlalchemy.db1.')
328 328 init_model(engine)
329 329
330 330 baseui = make_ui('db')
331 331 # fix if it's not a bare repo
332 332 if repo_path.endswith(os.sep + '.git'):
333 333 repo_path = repo_path[:-5]
334 334
335 335 repo = Repository.get_by_full_path(repo_path)
336 336 if not repo:
337 337 raise OSError('Repository %s not found in database'
338 338 % (safe_str(repo_path)))
339 339
340 340 _hooks = dict(baseui.configitems('hooks')) or {}
341 341
342 342 if hook_type == 'pre':
343 343 repo = repo.scm_instance
344 344 else:
345 345 #post push shouldn't use the cached instance never
346 346 repo = repo.scm_instance_no_cache()
347 347
348 348 if hook_type == 'pre':
349 349 pre_push(baseui, repo)
350 350
351 351 # if push hook is enabled via web interface
352 352 elif hook_type == 'post' and _hooks.get(RhodeCodeUi.HOOK_PUSH):
353 353
354 354 rev_data = []
355 355 for l in revs:
356 356 old_rev, new_rev, ref = l.split(' ')
357 357 _ref_data = ref.split('/')
358 358 if _ref_data[1] in ['tags', 'heads']:
359 359 rev_data.append({'old_rev': old_rev,
360 360 'new_rev': new_rev,
361 361 'ref': ref,
362 362 'type': _ref_data[1],
363 363 'name': _ref_data[2].strip()})
364 364
365 365 git_revs = []
366 366 for push_ref in rev_data:
367 367 _type = push_ref['type']
368 368 if _type == 'heads':
369 369 if push_ref['old_rev'] == EmptyChangeset().raw_id:
370 370 cmd = "for-each-ref --format='%(refname)' 'refs/heads/*'"
371 371 heads = repo.run_git_command(cmd)[0]
372 372 heads = heads.replace(push_ref['ref'], '')
373 373 heads = ' '.join(map(lambda c: c.strip('\n').strip(),
374 374 heads.splitlines()))
375 375 cmd = (('log %(new_rev)s' % push_ref) +
376 376 ' --reverse --pretty=format:"%H" --not ' + heads)
377 377 git_revs += repo.run_git_command(cmd)[0].splitlines()
378 378
379 379 elif push_ref['new_rev'] == EmptyChangeset().raw_id:
380 380 #delete branch case
381 381 git_revs += ['delete_branch=>%s' % push_ref['name']]
382 382 else:
383 383 cmd = (('log %(old_rev)s..%(new_rev)s' % push_ref) +
384 384 ' --reverse --pretty=format:"%H"')
385 385 git_revs += repo.run_git_command(cmd)[0].splitlines()
386 386
387 387 elif _type == 'tags':
388 388 git_revs += ['tag=>%s' % push_ref['name']]
389 389
390 390 log_push_action(baseui, repo, _git_revs=git_revs)
General Comments 0
You need to be logged in to leave comments. Login now