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