##// END OF EJS Templates
fixed issue with passing IP address to log action logger for git
marcink -
r3097:db1b7125 beta
parent child Browse files
Show More
@@ -1,428 +1,430 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 from inspect import isfunction
30 30
31 31 from mercurial.scmutil import revrange
32 32 from mercurial.node import nullrev
33 33
34 34 from rhodecode.lib import helpers as h
35 35 from rhodecode.lib.utils import action_logger
36 36 from rhodecode.lib.vcs.backends.base import EmptyChangeset
37 37 from rhodecode.lib.compat import json
38 38 from rhodecode.lib.exceptions import HTTPLockedRC
39 39 from rhodecode.lib.utils2 import safe_str, datetime_to_time
40 40 from rhodecode.model.db import Repository, User
41 41
42 42
43 43 def _get_scm_size(alias, root_path):
44 44
45 45 if not alias.startswith('.'):
46 46 alias += '.'
47 47
48 48 size_scm, size_root = 0, 0
49 49 for path, dirs, files in os.walk(safe_str(root_path)):
50 50 if path.find(alias) != -1:
51 51 for f in files:
52 52 try:
53 53 size_scm += os.path.getsize(os.path.join(path, f))
54 54 except OSError:
55 55 pass
56 56 else:
57 57 for f in files:
58 58 try:
59 59 size_root += os.path.getsize(os.path.join(path, f))
60 60 except OSError:
61 61 pass
62 62
63 63 size_scm_f = h.format_byte_size(size_scm)
64 64 size_root_f = h.format_byte_size(size_root)
65 65 size_total_f = h.format_byte_size(size_root + size_scm)
66 66
67 67 return size_scm_f, size_root_f, size_total_f
68 68
69 69
70 70 def repo_size(ui, repo, hooktype=None, **kwargs):
71 71 """
72 72 Presents size of repository after push
73 73
74 74 :param ui:
75 75 :param repo:
76 76 :param hooktype:
77 77 """
78 78
79 79 size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', repo.root)
80 80
81 81 last_cs = repo[len(repo) - 1]
82 82
83 83 msg = ('Repository size .hg:%s repo:%s total:%s\n'
84 84 'Last revision is now r%s:%s\n') % (
85 85 size_hg_f, size_root_f, size_total_f, last_cs.rev(), last_cs.hex()[:12]
86 86 )
87 87
88 88 sys.stdout.write(msg)
89 89
90 90
91 91 def pre_push(ui, repo, **kwargs):
92 92 # pre push function, currently used to ban pushing when
93 93 # repository is locked
94 94 try:
95 95 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
96 96 except:
97 97 rc_extras = {}
98 98 extras = dict(repo.ui.configitems('rhodecode_extras'))
99 99
100 100 if 'username' in extras:
101 101 username = extras['username']
102 102 repository = extras['repository']
103 103 scm = extras['scm']
104 104 locked_by = extras['locked_by']
105 105 elif 'username' in rc_extras:
106 106 username = rc_extras['username']
107 107 repository = rc_extras['repository']
108 108 scm = rc_extras['scm']
109 109 locked_by = rc_extras['locked_by']
110 110 else:
111 111 raise Exception('Missing data in repo.ui and os.environ')
112 112
113 113 usr = User.get_by_username(username)
114 114 if locked_by[0] and usr.user_id != int(locked_by[0]):
115 115 locked_by = User.get(locked_by[0]).username
116 116 raise HTTPLockedRC(repository, locked_by)
117 117
118 118
119 119 def pre_pull(ui, repo, **kwargs):
120 120 # pre push function, currently used to ban pushing when
121 121 # repository is locked
122 122 try:
123 123 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
124 124 except:
125 125 rc_extras = {}
126 126 extras = dict(repo.ui.configitems('rhodecode_extras'))
127 127 if 'username' in extras:
128 128 username = extras['username']
129 129 repository = extras['repository']
130 130 scm = extras['scm']
131 131 locked_by = extras['locked_by']
132 132 elif 'username' in rc_extras:
133 133 username = rc_extras['username']
134 134 repository = rc_extras['repository']
135 135 scm = rc_extras['scm']
136 136 locked_by = rc_extras['locked_by']
137 137 else:
138 138 raise Exception('Missing data in repo.ui and os.environ')
139 139
140 140 if locked_by[0]:
141 141 locked_by = User.get(locked_by[0]).username
142 142 raise HTTPLockedRC(repository, locked_by)
143 143
144 144
145 145 def log_pull_action(ui, repo, **kwargs):
146 146 """
147 147 Logs user last pull action
148 148
149 149 :param ui:
150 150 :param repo:
151 151 """
152 152 try:
153 153 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
154 154 except:
155 155 rc_extras = {}
156 156 extras = dict(repo.ui.configitems('rhodecode_extras'))
157 157 if 'username' in extras:
158 158 username = extras['username']
159 159 repository = extras['repository']
160 160 scm = extras['scm']
161 161 make_lock = extras['make_lock']
162 ip = extras['ip']
162 163 elif 'username' in rc_extras:
163 164 username = rc_extras['username']
164 165 repository = rc_extras['repository']
165 166 scm = rc_extras['scm']
166 167 make_lock = rc_extras['make_lock']
168 ip = rc_extras['ip']
167 169 else:
168 170 raise Exception('Missing data in repo.ui and os.environ')
169 171 user = User.get_by_username(username)
170 172 action = 'pull'
171 action_logger(user, action, repository, extras['ip'], commit=True)
173 action_logger(user, action, repository, ip, commit=True)
172 174 # extension hook call
173 175 from rhodecode import EXTENSIONS
174 176 callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
175 177
176 178 if isfunction(callback):
177 179 kw = {}
178 180 kw.update(extras)
179 181 callback(**kw)
180 182
181 183 if make_lock is True:
182 184 Repository.lock(Repository.get_by_repo_name(repository), user.user_id)
183 185 #msg = 'Made lock on repo `%s`' % repository
184 186 #sys.stdout.write(msg)
185 187
186 188 return 0
187 189
188 190
189 191 def log_push_action(ui, repo, **kwargs):
190 192 """
191 193 Maps user last push action to new changeset id, from mercurial
192 194
193 195 :param ui:
194 196 :param repo: repo object containing the `ui` object
195 197 """
196 198
197 199 try:
198 200 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
199 201 except:
200 202 rc_extras = {}
201 203
202 204 extras = dict(repo.ui.configitems('rhodecode_extras'))
203 205 if 'username' in extras:
204 206 username = extras['username']
205 207 repository = extras['repository']
206 208 scm = extras['scm']
207 209 make_lock = extras['make_lock']
208 210 elif 'username' in rc_extras:
209 211 username = rc_extras['username']
210 212 repository = rc_extras['repository']
211 213 scm = rc_extras['scm']
212 214 make_lock = rc_extras['make_lock']
213 215 else:
214 216 raise Exception('Missing data in repo.ui and os.environ')
215 217
216 218 action = 'push' + ':%s'
217 219
218 220 if scm == 'hg':
219 221 node = kwargs['node']
220 222
221 223 def get_revs(repo, rev_opt):
222 224 if rev_opt:
223 225 revs = revrange(repo, rev_opt)
224 226
225 227 if len(revs) == 0:
226 228 return (nullrev, nullrev)
227 229 return (max(revs), min(revs))
228 230 else:
229 231 return (len(repo) - 1, 0)
230 232
231 233 stop, start = get_revs(repo, [node + ':'])
232 234 h = binascii.hexlify
233 235 revs = [h(repo[r].node()) for r in xrange(start, stop + 1)]
234 236 elif scm == 'git':
235 237 revs = kwargs.get('_git_revs', [])
236 238 if '_git_revs' in kwargs:
237 239 kwargs.pop('_git_revs')
238 240
239 241 action = action % ','.join(revs)
240 242
241 243 action_logger(username, action, repository, extras['ip'], commit=True)
242 244
243 245 # extension hook call
244 246 from rhodecode import EXTENSIONS
245 247 callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
246 248 if isfunction(callback):
247 249 kw = {'pushed_revs': revs}
248 250 kw.update(extras)
249 251 callback(**kw)
250 252
251 253 if make_lock is False:
252 254 Repository.unlock(Repository.get_by_repo_name(repository))
253 255 msg = 'Released lock on repo `%s`\n' % repository
254 256 sys.stdout.write(msg)
255 257
256 258 return 0
257 259
258 260
259 261 def log_create_repository(repository_dict, created_by, **kwargs):
260 262 """
261 263 Post create repository Hook. This is a dummy function for admins to re-use
262 264 if needed. It's taken from rhodecode-extensions module and executed
263 265 if present
264 266
265 267 :param repository: dict dump of repository object
266 268 :param created_by: username who created repository
267 269
268 270 available keys of repository_dict:
269 271
270 272 'repo_type',
271 273 'description',
272 274 'private',
273 275 'created_on',
274 276 'enable_downloads',
275 277 'repo_id',
276 278 'user_id',
277 279 'enable_statistics',
278 280 'clone_uri',
279 281 'fork_id',
280 282 'group_id',
281 283 'repo_name'
282 284
283 285 """
284 286 from rhodecode import EXTENSIONS
285 287 callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
286 288 if isfunction(callback):
287 289 kw = {}
288 290 kw.update(repository_dict)
289 291 kw.update({'created_by': created_by})
290 292 kw.update(kwargs)
291 293 return callback(**kw)
292 294
293 295 return 0
294 296
295 297
296 298 def log_delete_repository(repository_dict, deleted_by, **kwargs):
297 299 """
298 300 Post delete repository Hook. This is a dummy function for admins to re-use
299 301 if needed. It's taken from rhodecode-extensions module and executed
300 302 if present
301 303
302 304 :param repository: dict dump of repository object
303 305 :param deleted_by: username who deleted the repository
304 306
305 307 available keys of repository_dict:
306 308
307 309 'repo_type',
308 310 'description',
309 311 'private',
310 312 'created_on',
311 313 'enable_downloads',
312 314 'repo_id',
313 315 'user_id',
314 316 'enable_statistics',
315 317 'clone_uri',
316 318 'fork_id',
317 319 'group_id',
318 320 'repo_name'
319 321
320 322 """
321 323 from rhodecode import EXTENSIONS
322 324 callback = getattr(EXTENSIONS, 'DELETE_REPO_HOOK', None)
323 325 if isfunction(callback):
324 326 kw = {}
325 327 kw.update(repository_dict)
326 328 kw.update({'deleted_by': deleted_by,
327 329 'deleted_on': time.time()})
328 330 kw.update(kwargs)
329 331 return callback(**kw)
330 332
331 333 return 0
332 334
333 335
334 336 handle_git_pre_receive = (lambda repo_path, revs, env:
335 337 handle_git_receive(repo_path, revs, env, hook_type='pre'))
336 338 handle_git_post_receive = (lambda repo_path, revs, env:
337 339 handle_git_receive(repo_path, revs, env, hook_type='post'))
338 340
339 341
340 342 def handle_git_receive(repo_path, revs, env, hook_type='post'):
341 343 """
342 344 A really hacky method that is runned by git post-receive hook and logs
343 345 an push action together with pushed revisions. It's executed by subprocess
344 346 thus needs all info to be able to create a on the fly pylons enviroment,
345 347 connect to database and run the logging code. Hacky as sh*t but works.
346 348
347 349 :param repo_path:
348 350 :type repo_path:
349 351 :param revs:
350 352 :type revs:
351 353 :param env:
352 354 :type env:
353 355 """
354 356 from paste.deploy import appconfig
355 357 from sqlalchemy import engine_from_config
356 358 from rhodecode.config.environment import load_environment
357 359 from rhodecode.model import init_model
358 360 from rhodecode.model.db import RhodeCodeUi
359 361 from rhodecode.lib.utils import make_ui
360 362 extras = json.loads(env['RHODECODE_EXTRAS'])
361 363
362 364 path, ini_name = os.path.split(extras['config'])
363 365 conf = appconfig('config:%s' % ini_name, relative_to=path)
364 366 load_environment(conf.global_conf, conf.local_conf)
365 367
366 368 engine = engine_from_config(conf, 'sqlalchemy.db1.')
367 369 init_model(engine)
368 370
369 371 baseui = make_ui('db')
370 372 # fix if it's not a bare repo
371 373 if repo_path.endswith(os.sep + '.git'):
372 374 repo_path = repo_path[:-5]
373 375
374 376 repo = Repository.get_by_full_path(repo_path)
375 377 if not repo:
376 378 raise OSError('Repository %s not found in database'
377 379 % (safe_str(repo_path)))
378 380
379 381 _hooks = dict(baseui.configitems('hooks')) or {}
380 382
381 383 for k, v in extras.items():
382 384 baseui.setconfig('rhodecode_extras', k, v)
383 385 repo = repo.scm_instance
384 386 repo.ui = baseui
385 387
386 388 if hook_type == 'pre':
387 389 pre_push(baseui, repo)
388 390
389 391 # if push hook is enabled via web interface
390 392 elif hook_type == 'post' and _hooks.get(RhodeCodeUi.HOOK_PUSH):
391 393
392 394 rev_data = []
393 395 for l in revs:
394 396 old_rev, new_rev, ref = l.split(' ')
395 397 _ref_data = ref.split('/')
396 398 if _ref_data[1] in ['tags', 'heads']:
397 399 rev_data.append({'old_rev': old_rev,
398 400 'new_rev': new_rev,
399 401 'ref': ref,
400 402 'type': _ref_data[1],
401 403 'name': _ref_data[2].strip()})
402 404
403 405 git_revs = []
404 406 for push_ref in rev_data:
405 407 _type = push_ref['type']
406 408 if _type == 'heads':
407 409 if push_ref['old_rev'] == EmptyChangeset().raw_id:
408 410 cmd = "for-each-ref --format='%(refname)' 'refs/heads/*'"
409 411 heads = repo.run_git_command(cmd)[0]
410 412 heads = heads.replace(push_ref['ref'], '')
411 413 heads = ' '.join(map(lambda c: c.strip('\n').strip(),
412 414 heads.splitlines()))
413 415 cmd = (('log %(new_rev)s' % push_ref) +
414 416 ' --reverse --pretty=format:"%H" --not ' + heads)
415 417 git_revs += repo.run_git_command(cmd)[0].splitlines()
416 418
417 419 elif push_ref['new_rev'] == EmptyChangeset().raw_id:
418 420 #delete branch case
419 421 git_revs += ['delete_branch=>%s' % push_ref['name']]
420 422 else:
421 423 cmd = (('log %(old_rev)s..%(new_rev)s' % push_ref) +
422 424 ' --reverse --pretty=format:"%H"')
423 425 git_revs += repo.run_git_command(cmd)[0].splitlines()
424 426
425 427 elif _type == 'tags':
426 428 git_revs += ['tag=>%s' % push_ref['name']]
427 429
428 430 log_push_action(baseui, repo, _git_revs=git_revs)
General Comments 0
You need to be logged in to leave comments. Login now