##// END OF EJS Templates
webinterface file commiting executes push hooks ref #594
marcink -
r3478:796738bb beta
parent child Browse files
Show More
@@ -1,435 +1,437 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.hooks
3 rhodecode.lib.hooks
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Hooks runned by rhodecode
6 Hooks runned by rhodecode
7
7
8 :created_on: Aug 6, 2010
8 :created_on: Aug 6, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import os
25 import os
26 import sys
26 import sys
27 import time
27 import time
28 import binascii
28 import binascii
29 from inspect import isfunction
29 from inspect import isfunction
30
30
31 from mercurial.scmutil import revrange
31 from mercurial.scmutil import revrange
32 from mercurial.node import nullrev
32 from mercurial.node import nullrev
33
33
34 from rhodecode.lib import helpers as h
34 from rhodecode.lib import helpers as h
35 from rhodecode.lib.utils import action_logger
35 from rhodecode.lib.utils import action_logger
36 from rhodecode.lib.vcs.backends.base import EmptyChangeset
36 from rhodecode.lib.vcs.backends.base import EmptyChangeset
37 from rhodecode.lib.compat import json
37 from rhodecode.lib.compat import json
38 from rhodecode.lib.exceptions import HTTPLockedRC
38 from rhodecode.lib.exceptions import HTTPLockedRC
39 from rhodecode.lib.utils2 import safe_str, datetime_to_time
39 from rhodecode.lib.utils2 import safe_str, datetime_to_time
40 from rhodecode.model.db import Repository, User
40 from rhodecode.model.db import Repository, User
41
41
42
42
43 def _get_scm_size(alias, root_path):
43 def _get_scm_size(alias, root_path):
44
44
45 if not alias.startswith('.'):
45 if not alias.startswith('.'):
46 alias += '.'
46 alias += '.'
47
47
48 size_scm, size_root = 0, 0
48 size_scm, size_root = 0, 0
49 for path, dirs, files in os.walk(safe_str(root_path)):
49 for path, dirs, files in os.walk(safe_str(root_path)):
50 if path.find(alias) != -1:
50 if path.find(alias) != -1:
51 for f in files:
51 for f in files:
52 try:
52 try:
53 size_scm += os.path.getsize(os.path.join(path, f))
53 size_scm += os.path.getsize(os.path.join(path, f))
54 except OSError:
54 except OSError:
55 pass
55 pass
56 else:
56 else:
57 for f in files:
57 for f in files:
58 try:
58 try:
59 size_root += os.path.getsize(os.path.join(path, f))
59 size_root += os.path.getsize(os.path.join(path, f))
60 except OSError:
60 except OSError:
61 pass
61 pass
62
62
63 size_scm_f = h.format_byte_size(size_scm)
63 size_scm_f = h.format_byte_size(size_scm)
64 size_root_f = h.format_byte_size(size_root)
64 size_root_f = h.format_byte_size(size_root)
65 size_total_f = h.format_byte_size(size_root + size_scm)
65 size_total_f = h.format_byte_size(size_root + size_scm)
66
66
67 return size_scm_f, size_root_f, size_total_f
67 return size_scm_f, size_root_f, size_total_f
68
68
69
69
70 def repo_size(ui, repo, hooktype=None, **kwargs):
70 def repo_size(ui, repo, hooktype=None, **kwargs):
71 """
71 """
72 Presents size of repository after push
72 Presents size of repository after push
73
73
74 :param ui:
74 :param ui:
75 :param repo:
75 :param repo:
76 :param hooktype:
76 :param hooktype:
77 """
77 """
78
78
79 size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', repo.root)
79 size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', repo.root)
80
80
81 last_cs = repo[len(repo) - 1]
81 last_cs = repo[len(repo) - 1]
82
82
83 msg = ('Repository size .hg:%s repo:%s total:%s\n'
83 msg = ('Repository size .hg:%s repo:%s total:%s\n'
84 'Last revision is now r%s:%s\n') % (
84 'Last revision is now r%s:%s\n') % (
85 size_hg_f, size_root_f, size_total_f, last_cs.rev(), last_cs.hex()[:12]
85 size_hg_f, size_root_f, size_total_f, last_cs.rev(), last_cs.hex()[:12]
86 )
86 )
87
87
88 sys.stdout.write(msg)
88 sys.stdout.write(msg)
89
89
90
90
91 def pre_push(ui, repo, **kwargs):
91 def pre_push(ui, repo, **kwargs):
92 # pre push function, currently used to ban pushing when
92 # pre push function, currently used to ban pushing when
93 # repository is locked
93 # repository is locked
94 try:
94 try:
95 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
95 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
96 except:
96 except:
97 rc_extras = {}
97 rc_extras = {}
98 extras = dict(repo.ui.configitems('rhodecode_extras'))
98 extras = dict(repo.ui.configitems('rhodecode_extras'))
99
99
100 if 'username' in extras:
100 if 'username' in extras:
101 username = extras['username']
101 username = extras['username']
102 repository = extras['repository']
102 repository = extras['repository']
103 scm = extras['scm']
103 scm = extras['scm']
104 locked_by = extras['locked_by']
104 locked_by = extras['locked_by']
105 elif 'username' in rc_extras:
105 elif 'username' in rc_extras:
106 username = rc_extras['username']
106 username = rc_extras['username']
107 repository = rc_extras['repository']
107 repository = rc_extras['repository']
108 scm = rc_extras['scm']
108 scm = rc_extras['scm']
109 locked_by = rc_extras['locked_by']
109 locked_by = rc_extras['locked_by']
110 else:
110 else:
111 raise Exception('Missing data in repo.ui and os.environ')
111 raise Exception('Missing data in repo.ui and os.environ')
112
112
113 usr = User.get_by_username(username)
113 usr = User.get_by_username(username)
114 if locked_by[0] and usr.user_id != int(locked_by[0]):
114 if locked_by[0] and usr.user_id != int(locked_by[0]):
115 locked_by = User.get(locked_by[0]).username
115 locked_by = User.get(locked_by[0]).username
116 raise HTTPLockedRC(repository, locked_by)
116 raise HTTPLockedRC(repository, locked_by)
117
117
118
118
119 def pre_pull(ui, repo, **kwargs):
119 def pre_pull(ui, repo, **kwargs):
120 # pre push function, currently used to ban pushing when
120 # pre push function, currently used to ban pushing when
121 # repository is locked
121 # repository is locked
122 try:
122 try:
123 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
123 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
124 except:
124 except:
125 rc_extras = {}
125 rc_extras = {}
126 extras = dict(repo.ui.configitems('rhodecode_extras'))
126 extras = dict(repo.ui.configitems('rhodecode_extras'))
127 if 'username' in extras:
127 if 'username' in extras:
128 username = extras['username']
128 username = extras['username']
129 repository = extras['repository']
129 repository = extras['repository']
130 scm = extras['scm']
130 scm = extras['scm']
131 locked_by = extras['locked_by']
131 locked_by = extras['locked_by']
132 elif 'username' in rc_extras:
132 elif 'username' in rc_extras:
133 username = rc_extras['username']
133 username = rc_extras['username']
134 repository = rc_extras['repository']
134 repository = rc_extras['repository']
135 scm = rc_extras['scm']
135 scm = rc_extras['scm']
136 locked_by = rc_extras['locked_by']
136 locked_by = rc_extras['locked_by']
137 else:
137 else:
138 raise Exception('Missing data in repo.ui and os.environ')
138 raise Exception('Missing data in repo.ui and os.environ')
139
139
140 if locked_by[0]:
140 if locked_by[0]:
141 locked_by = User.get(locked_by[0]).username
141 locked_by = User.get(locked_by[0]).username
142 raise HTTPLockedRC(repository, locked_by)
142 raise HTTPLockedRC(repository, locked_by)
143
143
144
144
145 def log_pull_action(ui, repo, **kwargs):
145 def log_pull_action(ui, repo, **kwargs):
146 """
146 """
147 Logs user last pull action
147 Logs user last pull action
148
148
149 :param ui:
149 :param ui:
150 :param repo:
150 :param repo:
151 """
151 """
152 try:
152 try:
153 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
153 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
154 except:
154 except:
155 rc_extras = {}
155 rc_extras = {}
156 extras = dict(repo.ui.configitems('rhodecode_extras'))
156 extras = dict(repo.ui.configitems('rhodecode_extras'))
157 if 'username' in extras:
157 if 'username' in extras:
158 username = extras['username']
158 username = extras['username']
159 repository = extras['repository']
159 repository = extras['repository']
160 scm = extras['scm']
160 scm = extras['scm']
161 make_lock = extras['make_lock']
161 make_lock = extras['make_lock']
162 ip = extras['ip']
162 ip = extras['ip']
163 elif 'username' in rc_extras:
163 elif 'username' in rc_extras:
164 username = rc_extras['username']
164 username = rc_extras['username']
165 repository = rc_extras['repository']
165 repository = rc_extras['repository']
166 scm = rc_extras['scm']
166 scm = rc_extras['scm']
167 make_lock = rc_extras['make_lock']
167 make_lock = rc_extras['make_lock']
168 ip = rc_extras['ip']
168 ip = rc_extras['ip']
169 else:
169 else:
170 raise Exception('Missing data in repo.ui and os.environ')
170 raise Exception('Missing data in repo.ui and os.environ')
171 user = User.get_by_username(username)
171 user = User.get_by_username(username)
172 action = 'pull'
172 action = 'pull'
173 action_logger(user, action, repository, ip, commit=True)
173 action_logger(user, action, repository, ip, commit=True)
174 # extension hook call
174 # extension hook call
175 from rhodecode import EXTENSIONS
175 from rhodecode import EXTENSIONS
176 callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
176 callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
177
177
178 if isfunction(callback):
178 if isfunction(callback):
179 kw = {}
179 kw = {}
180 kw.update(extras)
180 kw.update(extras)
181 callback(**kw)
181 callback(**kw)
182
182
183 if make_lock is True:
183 if make_lock is True:
184 Repository.lock(Repository.get_by_repo_name(repository), user.user_id)
184 Repository.lock(Repository.get_by_repo_name(repository), user.user_id)
185 #msg = 'Made lock on repo `%s`' % repository
185 #msg = 'Made lock on repo `%s`' % repository
186 #sys.stdout.write(msg)
186 #sys.stdout.write(msg)
187
187
188 return 0
188 return 0
189
189
190
190
191 def log_push_action(ui, repo, **kwargs):
191 def log_push_action(ui, repo, **kwargs):
192 """
192 """
193 Maps user last push action to new changeset id, from mercurial
193 Maps user last push action to new changeset id, from mercurial
194
194
195 :param ui:
195 :param ui:
196 :param repo: repo object containing the `ui` object
196 :param repo: repo object containing the `ui` object
197 """
197 """
198
198
199 try:
199 try:
200 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
200 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
201 except:
201 except:
202 rc_extras = {}
202 rc_extras = {}
203
203
204 extras = dict(repo.ui.configitems('rhodecode_extras'))
204 extras = dict(repo.ui.configitems('rhodecode_extras'))
205 if 'username' in extras:
205 if 'username' in extras:
206 username = extras['username']
206 username = extras['username']
207 repository = extras['repository']
207 repository = extras['repository']
208 scm = extras['scm']
208 scm = extras['scm']
209 make_lock = extras['make_lock']
209 make_lock = extras['make_lock']
210 action = extras['action']
210 elif 'username' in rc_extras:
211 elif 'username' in rc_extras:
211 username = rc_extras['username']
212 username = rc_extras['username']
212 repository = rc_extras['repository']
213 repository = rc_extras['repository']
213 scm = rc_extras['scm']
214 scm = rc_extras['scm']
214 make_lock = rc_extras['make_lock']
215 make_lock = rc_extras['make_lock']
216 action = extras['action']
215 else:
217 else:
216 raise Exception('Missing data in repo.ui and os.environ')
218 raise Exception('Missing data in repo.ui and os.environ')
217
219
218 action = 'push' + ':%s'
220 action = action + ':%s'
219
221
220 if scm == 'hg':
222 if scm == 'hg':
221 node = kwargs['node']
223 node = kwargs['node']
222
224
223 def get_revs(repo, rev_opt):
225 def get_revs(repo, rev_opt):
224 if rev_opt:
226 if rev_opt:
225 revs = revrange(repo, rev_opt)
227 revs = revrange(repo, rev_opt)
226
228
227 if len(revs) == 0:
229 if len(revs) == 0:
228 return (nullrev, nullrev)
230 return (nullrev, nullrev)
229 return (max(revs), min(revs))
231 return (max(revs), min(revs))
230 else:
232 else:
231 return (len(repo) - 1, 0)
233 return (len(repo) - 1, 0)
232
234
233 stop, start = get_revs(repo, [node + ':'])
235 stop, start = get_revs(repo, [node + ':'])
234 h = binascii.hexlify
236 h = binascii.hexlify
235 revs = [h(repo[r].node()) for r in xrange(start, stop + 1)]
237 revs = [h(repo[r].node()) for r in xrange(start, stop + 1)]
236 elif scm == 'git':
238 elif scm == 'git':
237 revs = kwargs.get('_git_revs', [])
239 revs = kwargs.get('_git_revs', [])
238 if '_git_revs' in kwargs:
240 if '_git_revs' in kwargs:
239 kwargs.pop('_git_revs')
241 kwargs.pop('_git_revs')
240
242
241 action = action % ','.join(revs)
243 action = action % ','.join(revs)
242
244
243 action_logger(username, action, repository, extras['ip'], commit=True)
245 action_logger(username, action, repository, extras['ip'], commit=True)
244
246
245 # extension hook call
247 # extension hook call
246 from rhodecode import EXTENSIONS
248 from rhodecode import EXTENSIONS
247 callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
249 callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
248 if isfunction(callback):
250 if isfunction(callback):
249 kw = {'pushed_revs': revs}
251 kw = {'pushed_revs': revs}
250 kw.update(extras)
252 kw.update(extras)
251 callback(**kw)
253 callback(**kw)
252
254
253 if make_lock is False:
255 if make_lock is False:
254 Repository.unlock(Repository.get_by_repo_name(repository))
256 Repository.unlock(Repository.get_by_repo_name(repository))
255 msg = 'Released lock on repo `%s`\n' % repository
257 msg = 'Released lock on repo `%s`\n' % repository
256 sys.stdout.write(msg)
258 sys.stdout.write(msg)
257
259
258 return 0
260 return 0
259
261
260
262
261 def log_create_repository(repository_dict, created_by, **kwargs):
263 def log_create_repository(repository_dict, created_by, **kwargs):
262 """
264 """
263 Post create repository Hook. This is a dummy function for admins to re-use
265 Post create repository Hook. This is a dummy function for admins to re-use
264 if needed. It's taken from rhodecode-extensions module and executed
266 if needed. It's taken from rhodecode-extensions module and executed
265 if present
267 if present
266
268
267 :param repository: dict dump of repository object
269 :param repository: dict dump of repository object
268 :param created_by: username who created repository
270 :param created_by: username who created repository
269
271
270 available keys of repository_dict:
272 available keys of repository_dict:
271
273
272 'repo_type',
274 'repo_type',
273 'description',
275 'description',
274 'private',
276 'private',
275 'created_on',
277 'created_on',
276 'enable_downloads',
278 'enable_downloads',
277 'repo_id',
279 'repo_id',
278 'user_id',
280 'user_id',
279 'enable_statistics',
281 'enable_statistics',
280 'clone_uri',
282 'clone_uri',
281 'fork_id',
283 'fork_id',
282 'group_id',
284 'group_id',
283 'repo_name'
285 'repo_name'
284
286
285 """
287 """
286 from rhodecode import EXTENSIONS
288 from rhodecode import EXTENSIONS
287 callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
289 callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
288 if isfunction(callback):
290 if isfunction(callback):
289 kw = {}
291 kw = {}
290 kw.update(repository_dict)
292 kw.update(repository_dict)
291 kw.update({'created_by': created_by})
293 kw.update({'created_by': created_by})
292 kw.update(kwargs)
294 kw.update(kwargs)
293 return callback(**kw)
295 return callback(**kw)
294
296
295 return 0
297 return 0
296
298
297
299
298 def log_delete_repository(repository_dict, deleted_by, **kwargs):
300 def log_delete_repository(repository_dict, deleted_by, **kwargs):
299 """
301 """
300 Post delete repository Hook. This is a dummy function for admins to re-use
302 Post delete repository Hook. This is a dummy function for admins to re-use
301 if needed. It's taken from rhodecode-extensions module and executed
303 if needed. It's taken from rhodecode-extensions module and executed
302 if present
304 if present
303
305
304 :param repository: dict dump of repository object
306 :param repository: dict dump of repository object
305 :param deleted_by: username who deleted the repository
307 :param deleted_by: username who deleted the repository
306
308
307 available keys of repository_dict:
309 available keys of repository_dict:
308
310
309 'repo_type',
311 'repo_type',
310 'description',
312 'description',
311 'private',
313 'private',
312 'created_on',
314 'created_on',
313 'enable_downloads',
315 'enable_downloads',
314 'repo_id',
316 'repo_id',
315 'user_id',
317 'user_id',
316 'enable_statistics',
318 'enable_statistics',
317 'clone_uri',
319 'clone_uri',
318 'fork_id',
320 'fork_id',
319 'group_id',
321 'group_id',
320 'repo_name'
322 'repo_name'
321
323
322 """
324 """
323 from rhodecode import EXTENSIONS
325 from rhodecode import EXTENSIONS
324 callback = getattr(EXTENSIONS, 'DELETE_REPO_HOOK', None)
326 callback = getattr(EXTENSIONS, 'DELETE_REPO_HOOK', None)
325 if isfunction(callback):
327 if isfunction(callback):
326 kw = {}
328 kw = {}
327 kw.update(repository_dict)
329 kw.update(repository_dict)
328 kw.update({'deleted_by': deleted_by,
330 kw.update({'deleted_by': deleted_by,
329 'deleted_on': time.time()})
331 'deleted_on': time.time()})
330 kw.update(kwargs)
332 kw.update(kwargs)
331 return callback(**kw)
333 return callback(**kw)
332
334
333 return 0
335 return 0
334
336
335
337
336 handle_git_pre_receive = (lambda repo_path, revs, env:
338 handle_git_pre_receive = (lambda repo_path, revs, env:
337 handle_git_receive(repo_path, revs, env, hook_type='pre'))
339 handle_git_receive(repo_path, revs, env, hook_type='pre'))
338 handle_git_post_receive = (lambda repo_path, revs, env:
340 handle_git_post_receive = (lambda repo_path, revs, env:
339 handle_git_receive(repo_path, revs, env, hook_type='post'))
341 handle_git_receive(repo_path, revs, env, hook_type='post'))
340
342
341
343
342 def handle_git_receive(repo_path, revs, env, hook_type='post'):
344 def handle_git_receive(repo_path, revs, env, hook_type='post'):
343 """
345 """
344 A really hacky method that is runned by git post-receive hook and logs
346 A really hacky method that is runned by git post-receive hook and logs
345 an push action together with pushed revisions. It's executed by subprocess
347 an push action together with pushed revisions. It's executed by subprocess
346 thus needs all info to be able to create a on the fly pylons enviroment,
348 thus needs all info to be able to create a on the fly pylons enviroment,
347 connect to database and run the logging code. Hacky as sh*t but works.
349 connect to database and run the logging code. Hacky as sh*t but works.
348
350
349 :param repo_path:
351 :param repo_path:
350 :type repo_path:
352 :type repo_path:
351 :param revs:
353 :param revs:
352 :type revs:
354 :type revs:
353 :param env:
355 :param env:
354 :type env:
356 :type env:
355 """
357 """
356 from paste.deploy import appconfig
358 from paste.deploy import appconfig
357 from sqlalchemy import engine_from_config
359 from sqlalchemy import engine_from_config
358 from rhodecode.config.environment import load_environment
360 from rhodecode.config.environment import load_environment
359 from rhodecode.model import init_model
361 from rhodecode.model import init_model
360 from rhodecode.model.db import RhodeCodeUi
362 from rhodecode.model.db import RhodeCodeUi
361 from rhodecode.lib.utils import make_ui
363 from rhodecode.lib.utils import make_ui
362 extras = json.loads(env['RHODECODE_EXTRAS'])
364 extras = json.loads(env['RHODECODE_EXTRAS'])
363
365
364 path, ini_name = os.path.split(extras['config'])
366 path, ini_name = os.path.split(extras['config'])
365 conf = appconfig('config:%s' % ini_name, relative_to=path)
367 conf = appconfig('config:%s' % ini_name, relative_to=path)
366 load_environment(conf.global_conf, conf.local_conf)
368 load_environment(conf.global_conf, conf.local_conf)
367
369
368 engine = engine_from_config(conf, 'sqlalchemy.db1.')
370 engine = engine_from_config(conf, 'sqlalchemy.db1.')
369 init_model(engine)
371 init_model(engine)
370
372
371 baseui = make_ui('db')
373 baseui = make_ui('db')
372 # fix if it's not a bare repo
374 # fix if it's not a bare repo
373 if repo_path.endswith(os.sep + '.git'):
375 if repo_path.endswith(os.sep + '.git'):
374 repo_path = repo_path[:-5]
376 repo_path = repo_path[:-5]
375
377
376 repo = Repository.get_by_full_path(repo_path)
378 repo = Repository.get_by_full_path(repo_path)
377 if not repo:
379 if not repo:
378 raise OSError('Repository %s not found in database'
380 raise OSError('Repository %s not found in database'
379 % (safe_str(repo_path)))
381 % (safe_str(repo_path)))
380
382
381 _hooks = dict(baseui.configitems('hooks')) or {}
383 _hooks = dict(baseui.configitems('hooks')) or {}
382
384
383 for k, v in extras.items():
385 for k, v in extras.items():
384 baseui.setconfig('rhodecode_extras', k, v)
386 baseui.setconfig('rhodecode_extras', k, v)
385 if hook_type == 'pre':
387 if hook_type == 'pre':
386 repo = repo.scm_instance
388 repo = repo.scm_instance
387 else:
389 else:
388 #post push shouldn't use the cached instance never
390 #post push shouldn't use the cached instance never
389 repo = repo.scm_instance_no_cache
391 repo = repo.scm_instance_no_cache
390
392
391 repo.ui = baseui
393 repo.ui = baseui
392
394
393 if hook_type == 'pre':
395 if hook_type == 'pre':
394 pre_push(baseui, repo)
396 pre_push(baseui, repo)
395
397
396 # if push hook is enabled via web interface
398 # if push hook is enabled via web interface
397 elif hook_type == 'post' and _hooks.get(RhodeCodeUi.HOOK_PUSH):
399 elif hook_type == 'post' and _hooks.get(RhodeCodeUi.HOOK_PUSH):
398
400
399 rev_data = []
401 rev_data = []
400 for l in revs:
402 for l in revs:
401 old_rev, new_rev, ref = l.split(' ')
403 old_rev, new_rev, ref = l.split(' ')
402 _ref_data = ref.split('/')
404 _ref_data = ref.split('/')
403 if _ref_data[1] in ['tags', 'heads']:
405 if _ref_data[1] in ['tags', 'heads']:
404 rev_data.append({'old_rev': old_rev,
406 rev_data.append({'old_rev': old_rev,
405 'new_rev': new_rev,
407 'new_rev': new_rev,
406 'ref': ref,
408 'ref': ref,
407 'type': _ref_data[1],
409 'type': _ref_data[1],
408 'name': _ref_data[2].strip()})
410 'name': _ref_data[2].strip()})
409
411
410 git_revs = []
412 git_revs = []
411 for push_ref in rev_data:
413 for push_ref in rev_data:
412 _type = push_ref['type']
414 _type = push_ref['type']
413 if _type == 'heads':
415 if _type == 'heads':
414 if push_ref['old_rev'] == EmptyChangeset().raw_id:
416 if push_ref['old_rev'] == EmptyChangeset().raw_id:
415 cmd = "for-each-ref --format='%(refname)' 'refs/heads/*'"
417 cmd = "for-each-ref --format='%(refname)' 'refs/heads/*'"
416 heads = repo.run_git_command(cmd)[0]
418 heads = repo.run_git_command(cmd)[0]
417 heads = heads.replace(push_ref['ref'], '')
419 heads = heads.replace(push_ref['ref'], '')
418 heads = ' '.join(map(lambda c: c.strip('\n').strip(),
420 heads = ' '.join(map(lambda c: c.strip('\n').strip(),
419 heads.splitlines()))
421 heads.splitlines()))
420 cmd = (('log %(new_rev)s' % push_ref) +
422 cmd = (('log %(new_rev)s' % push_ref) +
421 ' --reverse --pretty=format:"%H" --not ' + heads)
423 ' --reverse --pretty=format:"%H" --not ' + heads)
422 git_revs += repo.run_git_command(cmd)[0].splitlines()
424 git_revs += repo.run_git_command(cmd)[0].splitlines()
423
425
424 elif push_ref['new_rev'] == EmptyChangeset().raw_id:
426 elif push_ref['new_rev'] == EmptyChangeset().raw_id:
425 #delete branch case
427 #delete branch case
426 git_revs += ['delete_branch=>%s' % push_ref['name']]
428 git_revs += ['delete_branch=>%s' % push_ref['name']]
427 else:
429 else:
428 cmd = (('log %(old_rev)s..%(new_rev)s' % push_ref) +
430 cmd = (('log %(old_rev)s..%(new_rev)s' % push_ref) +
429 ' --reverse --pretty=format:"%H"')
431 ' --reverse --pretty=format:"%H"')
430 git_revs += repo.run_git_command(cmd)[0].splitlines()
432 git_revs += repo.run_git_command(cmd)[0].splitlines()
431
433
432 elif _type == 'tags':
434 elif _type == 'tags':
433 git_revs += ['tag=>%s' % push_ref['name']]
435 git_revs += ['tag=>%s' % push_ref['name']]
434
436
435 log_push_action(baseui, repo, _git_revs=git_revs)
437 log_push_action(baseui, repo, _git_revs=git_revs)
@@ -1,631 +1,651 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.scm
3 rhodecode.model.scm
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Scm model for RhodeCode
6 Scm model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import with_statement
25 from __future__ import with_statement
26 import os
26 import os
27 import re
27 import re
28 import time
28 import time
29 import traceback
29 import traceback
30 import logging
30 import logging
31 import cStringIO
31 import cStringIO
32 import pkg_resources
32 import pkg_resources
33 from os.path import dirname as dn, join as jn
33 from os.path import dirname as dn, join as jn
34
34
35 from sqlalchemy import func
35 from sqlalchemy import func
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 import rhodecode
38 import rhodecode
39 from rhodecode.lib.vcs import get_backend
39 from rhodecode.lib.vcs import get_backend
40 from rhodecode.lib.vcs.exceptions import RepositoryError
40 from rhodecode.lib.vcs.exceptions import RepositoryError
41 from rhodecode.lib.vcs.utils.lazy import LazyProperty
41 from rhodecode.lib.vcs.utils.lazy import LazyProperty
42 from rhodecode.lib.vcs.nodes import FileNode
42 from rhodecode.lib.vcs.nodes import FileNode
43 from rhodecode.lib.vcs.backends.base import EmptyChangeset
43 from rhodecode.lib.vcs.backends.base import EmptyChangeset
44
44
45 from rhodecode import BACKENDS
45 from rhodecode import BACKENDS
46 from rhodecode.lib import helpers as h
46 from rhodecode.lib import helpers as h
47 from rhodecode.lib.utils2 import safe_str, safe_unicode
47 from rhodecode.lib.utils2 import safe_str, safe_unicode, get_server_url
48 from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
48 from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
49 from rhodecode.lib.utils import get_filesystem_repos, make_ui, \
49 from rhodecode.lib.utils import get_filesystem_repos, make_ui, \
50 action_logger, REMOVED_REPO_PAT
50 action_logger, REMOVED_REPO_PAT
51 from rhodecode.model import BaseModel
51 from rhodecode.model import BaseModel
52 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
52 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
53 UserFollowing, UserLog, User, RepoGroup, PullRequest
53 UserFollowing, UserLog, User, RepoGroup, PullRequest
54 from rhodecode.lib.hooks import log_push_action
54
55
55 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
56
57
57
58
58 class UserTemp(object):
59 class UserTemp(object):
59 def __init__(self, user_id):
60 def __init__(self, user_id):
60 self.user_id = user_id
61 self.user_id = user_id
61
62
62 def __repr__(self):
63 def __repr__(self):
63 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
64 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
64
65
65
66
66 class RepoTemp(object):
67 class RepoTemp(object):
67 def __init__(self, repo_id):
68 def __init__(self, repo_id):
68 self.repo_id = repo_id
69 self.repo_id = repo_id
69
70
70 def __repr__(self):
71 def __repr__(self):
71 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
72 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
72
73
73
74
74 class CachedRepoList(object):
75 class CachedRepoList(object):
75 """
76 """
76 Cached repo list, uses in-memory cache after initialization, that is
77 Cached repo list, uses in-memory cache after initialization, that is
77 super fast
78 super fast
78 """
79 """
79
80
80 def __init__(self, db_repo_list, repos_path, order_by=None, perm_set=None):
81 def __init__(self, db_repo_list, repos_path, order_by=None, perm_set=None):
81 self.db_repo_list = db_repo_list
82 self.db_repo_list = db_repo_list
82 self.repos_path = repos_path
83 self.repos_path = repos_path
83 self.order_by = order_by
84 self.order_by = order_by
84 self.reversed = (order_by or '').startswith('-')
85 self.reversed = (order_by or '').startswith('-')
85 if not perm_set:
86 if not perm_set:
86 perm_set = ['repository.read', 'repository.write',
87 perm_set = ['repository.read', 'repository.write',
87 'repository.admin']
88 'repository.admin']
88 self.perm_set = perm_set
89 self.perm_set = perm_set
89
90
90 def __len__(self):
91 def __len__(self):
91 return len(self.db_repo_list)
92 return len(self.db_repo_list)
92
93
93 def __repr__(self):
94 def __repr__(self):
94 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
95 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
95
96
96 def __iter__(self):
97 def __iter__(self):
97 # pre-propagated cache_map to save executing select statements
98 # pre-propagated cache_map to save executing select statements
98 # for each repo
99 # for each repo
99 cache_map = CacheInvalidation.get_cache_map()
100 cache_map = CacheInvalidation.get_cache_map()
100
101
101 for dbr in self.db_repo_list:
102 for dbr in self.db_repo_list:
102 scmr = dbr.scm_instance_cached(cache_map)
103 scmr = dbr.scm_instance_cached(cache_map)
103 # check permission at this level
104 # check permission at this level
104 if not HasRepoPermissionAny(
105 if not HasRepoPermissionAny(
105 *self.perm_set
106 *self.perm_set
106 )(dbr.repo_name, 'get repo check'):
107 )(dbr.repo_name, 'get repo check'):
107 continue
108 continue
108
109
109 try:
110 try:
110 last_change = scmr.last_change
111 last_change = scmr.last_change
111 tip = h.get_changeset_safe(scmr, 'tip')
112 tip = h.get_changeset_safe(scmr, 'tip')
112 except Exception:
113 except Exception:
113 log.error(
114 log.error(
114 '%s this repository is present in database but it '
115 '%s this repository is present in database but it '
115 'cannot be created as an scm instance, org_exc:%s'
116 'cannot be created as an scm instance, org_exc:%s'
116 % (dbr.repo_name, traceback.format_exc())
117 % (dbr.repo_name, traceback.format_exc())
117 )
118 )
118 continue
119 continue
119
120
120 tmp_d = {}
121 tmp_d = {}
121 tmp_d['name'] = dbr.repo_name
122 tmp_d['name'] = dbr.repo_name
122 tmp_d['name_sort'] = tmp_d['name'].lower()
123 tmp_d['name_sort'] = tmp_d['name'].lower()
123 tmp_d['raw_name'] = tmp_d['name'].lower()
124 tmp_d['raw_name'] = tmp_d['name'].lower()
124 tmp_d['description'] = dbr.description
125 tmp_d['description'] = dbr.description
125 tmp_d['description_sort'] = tmp_d['description'].lower()
126 tmp_d['description_sort'] = tmp_d['description'].lower()
126 tmp_d['last_change'] = last_change
127 tmp_d['last_change'] = last_change
127 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
128 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
128 tmp_d['tip'] = tip.raw_id
129 tmp_d['tip'] = tip.raw_id
129 tmp_d['tip_sort'] = tip.revision
130 tmp_d['tip_sort'] = tip.revision
130 tmp_d['rev'] = tip.revision
131 tmp_d['rev'] = tip.revision
131 tmp_d['contact'] = dbr.user.full_contact
132 tmp_d['contact'] = dbr.user.full_contact
132 tmp_d['contact_sort'] = tmp_d['contact']
133 tmp_d['contact_sort'] = tmp_d['contact']
133 tmp_d['owner_sort'] = tmp_d['contact']
134 tmp_d['owner_sort'] = tmp_d['contact']
134 tmp_d['repo_archives'] = list(scmr._get_archives())
135 tmp_d['repo_archives'] = list(scmr._get_archives())
135 tmp_d['last_msg'] = tip.message
136 tmp_d['last_msg'] = tip.message
136 tmp_d['author'] = tip.author
137 tmp_d['author'] = tip.author
137 tmp_d['dbrepo'] = dbr.get_dict()
138 tmp_d['dbrepo'] = dbr.get_dict()
138 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
139 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
139 yield tmp_d
140 yield tmp_d
140
141
141
142
142 class SimpleCachedRepoList(CachedRepoList):
143 class SimpleCachedRepoList(CachedRepoList):
143 """
144 """
144 Lighter version of CachedRepoList without the scm initialisation
145 Lighter version of CachedRepoList without the scm initialisation
145 """
146 """
146
147
147 def __iter__(self):
148 def __iter__(self):
148 for dbr in self.db_repo_list:
149 for dbr in self.db_repo_list:
149 # check permission at this level
150 # check permission at this level
150 if not HasRepoPermissionAny(
151 if not HasRepoPermissionAny(
151 *self.perm_set
152 *self.perm_set
152 )(dbr.repo_name, 'get repo check'):
153 )(dbr.repo_name, 'get repo check'):
153 continue
154 continue
154
155
155 tmp_d = {}
156 tmp_d = {}
156 tmp_d['name'] = dbr.repo_name
157 tmp_d['name'] = dbr.repo_name
157 tmp_d['name_sort'] = tmp_d['name'].lower()
158 tmp_d['name_sort'] = tmp_d['name'].lower()
158 tmp_d['raw_name'] = tmp_d['name'].lower()
159 tmp_d['raw_name'] = tmp_d['name'].lower()
159 tmp_d['description'] = dbr.description
160 tmp_d['description'] = dbr.description
160 tmp_d['description_sort'] = tmp_d['description'].lower()
161 tmp_d['description_sort'] = tmp_d['description'].lower()
161 tmp_d['dbrepo'] = dbr.get_dict()
162 tmp_d['dbrepo'] = dbr.get_dict()
162 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
163 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
163 yield tmp_d
164 yield tmp_d
164
165
165
166
166 class GroupList(object):
167 class GroupList(object):
167
168
168 def __init__(self, db_repo_group_list, perm_set=None):
169 def __init__(self, db_repo_group_list, perm_set=None):
169 """
170 """
170 Creates iterator from given list of group objects, additionally
171 Creates iterator from given list of group objects, additionally
171 checking permission for them from perm_set var
172 checking permission for them from perm_set var
172
173
173 :param db_repo_group_list:
174 :param db_repo_group_list:
174 :param perm_set: list of permissons to check
175 :param perm_set: list of permissons to check
175 """
176 """
176 self.db_repo_group_list = db_repo_group_list
177 self.db_repo_group_list = db_repo_group_list
177 if not perm_set:
178 if not perm_set:
178 perm_set = ['group.read', 'group.write', 'group.admin']
179 perm_set = ['group.read', 'group.write', 'group.admin']
179 self.perm_set = perm_set
180 self.perm_set = perm_set
180
181
181 def __len__(self):
182 def __len__(self):
182 return len(self.db_repo_group_list)
183 return len(self.db_repo_group_list)
183
184
184 def __repr__(self):
185 def __repr__(self):
185 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
186 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
186
187
187 def __iter__(self):
188 def __iter__(self):
188 for dbgr in self.db_repo_group_list:
189 for dbgr in self.db_repo_group_list:
189 # check permission at this level
190 # check permission at this level
190 if not HasReposGroupPermissionAny(
191 if not HasReposGroupPermissionAny(
191 *self.perm_set
192 *self.perm_set
192 )(dbgr.group_name, 'get group repo check'):
193 )(dbgr.group_name, 'get group repo check'):
193 continue
194 continue
194
195
195 yield dbgr
196 yield dbgr
196
197
197
198
198 class ScmModel(BaseModel):
199 class ScmModel(BaseModel):
199 """
200 """
200 Generic Scm Model
201 Generic Scm Model
201 """
202 """
202
203
203 def __get_repo(self, instance):
204 def __get_repo(self, instance):
204 cls = Repository
205 cls = Repository
205 if isinstance(instance, cls):
206 if isinstance(instance, cls):
206 return instance
207 return instance
207 elif isinstance(instance, int) or safe_str(instance).isdigit():
208 elif isinstance(instance, int) or safe_str(instance).isdigit():
208 return cls.get(instance)
209 return cls.get(instance)
209 elif isinstance(instance, basestring):
210 elif isinstance(instance, basestring):
210 return cls.get_by_repo_name(instance)
211 return cls.get_by_repo_name(instance)
211 elif instance:
212 elif instance:
212 raise Exception('given object must be int, basestr or Instance'
213 raise Exception('given object must be int, basestr or Instance'
213 ' of %s got %s' % (type(cls), type(instance)))
214 ' of %s got %s' % (type(cls), type(instance)))
214
215
215 @LazyProperty
216 @LazyProperty
216 def repos_path(self):
217 def repos_path(self):
217 """
218 """
218 Get's the repositories root path from database
219 Get's the repositories root path from database
219 """
220 """
220
221
221 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
222 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
222
223
223 return q.ui_value
224 return q.ui_value
224
225
225 def repo_scan(self, repos_path=None):
226 def repo_scan(self, repos_path=None):
226 """
227 """
227 Listing of repositories in given path. This path should not be a
228 Listing of repositories in given path. This path should not be a
228 repository itself. Return a dictionary of repository objects
229 repository itself. Return a dictionary of repository objects
229
230
230 :param repos_path: path to directory containing repositories
231 :param repos_path: path to directory containing repositories
231 """
232 """
232
233
233 if repos_path is None:
234 if repos_path is None:
234 repos_path = self.repos_path
235 repos_path = self.repos_path
235
236
236 log.info('scanning for repositories in %s' % repos_path)
237 log.info('scanning for repositories in %s' % repos_path)
237
238
238 baseui = make_ui('db')
239 baseui = make_ui('db')
239 repos = {}
240 repos = {}
240
241
241 for name, path in get_filesystem_repos(repos_path, recursive=True):
242 for name, path in get_filesystem_repos(repos_path, recursive=True):
242 # name need to be decomposed and put back together using the /
243 # name need to be decomposed and put back together using the /
243 # since this is internal storage separator for rhodecode
244 # since this is internal storage separator for rhodecode
244 name = Repository.normalize_repo_name(name)
245 name = Repository.normalize_repo_name(name)
245
246
246 try:
247 try:
247 if name in repos:
248 if name in repos:
248 raise RepositoryError('Duplicate repository name %s '
249 raise RepositoryError('Duplicate repository name %s '
249 'found in %s' % (name, path))
250 'found in %s' % (name, path))
250 else:
251 else:
251
252
252 klass = get_backend(path[0])
253 klass = get_backend(path[0])
253
254
254 if path[0] == 'hg' and path[0] in BACKENDS.keys():
255 if path[0] == 'hg' and path[0] in BACKENDS.keys():
255 repos[name] = klass(safe_str(path[1]), baseui=baseui)
256 repos[name] = klass(safe_str(path[1]), baseui=baseui)
256
257
257 if path[0] == 'git' and path[0] in BACKENDS.keys():
258 if path[0] == 'git' and path[0] in BACKENDS.keys():
258 repos[name] = klass(path[1])
259 repos[name] = klass(path[1])
259 except OSError:
260 except OSError:
260 continue
261 continue
261 log.debug('found %s paths with repositories' % (len(repos)))
262 log.debug('found %s paths with repositories' % (len(repos)))
262 return repos
263 return repos
263
264
264 def get_repos(self, all_repos=None, sort_key=None, simple=False):
265 def get_repos(self, all_repos=None, sort_key=None, simple=False):
265 """
266 """
266 Get all repos from db and for each repo create it's
267 Get all repos from db and for each repo create it's
267 backend instance and fill that backed with information from database
268 backend instance and fill that backed with information from database
268
269
269 :param all_repos: list of repository names as strings
270 :param all_repos: list of repository names as strings
270 give specific repositories list, good for filtering
271 give specific repositories list, good for filtering
271
272
272 :param sort_key: initial sorting of repos
273 :param sort_key: initial sorting of repos
273 :param simple: use SimpleCachedList - one without the SCM info
274 :param simple: use SimpleCachedList - one without the SCM info
274 """
275 """
275 if all_repos is None:
276 if all_repos is None:
276 all_repos = self.sa.query(Repository)\
277 all_repos = self.sa.query(Repository)\
277 .filter(Repository.group_id == None)\
278 .filter(Repository.group_id == None)\
278 .order_by(func.lower(Repository.repo_name)).all()
279 .order_by(func.lower(Repository.repo_name)).all()
279 if simple:
280 if simple:
280 repo_iter = SimpleCachedRepoList(all_repos,
281 repo_iter = SimpleCachedRepoList(all_repos,
281 repos_path=self.repos_path,
282 repos_path=self.repos_path,
282 order_by=sort_key)
283 order_by=sort_key)
283 else:
284 else:
284 repo_iter = CachedRepoList(all_repos,
285 repo_iter = CachedRepoList(all_repos,
285 repos_path=self.repos_path,
286 repos_path=self.repos_path,
286 order_by=sort_key)
287 order_by=sort_key)
287
288
288 return repo_iter
289 return repo_iter
289
290
290 def get_repos_groups(self, all_groups=None):
291 def get_repos_groups(self, all_groups=None):
291 if all_groups is None:
292 if all_groups is None:
292 all_groups = RepoGroup.query()\
293 all_groups = RepoGroup.query()\
293 .filter(RepoGroup.group_parent_id == None).all()
294 .filter(RepoGroup.group_parent_id == None).all()
294 return [x for x in GroupList(all_groups)]
295 return [x for x in GroupList(all_groups)]
295
296
296 def mark_for_invalidation(self, repo_name):
297 def mark_for_invalidation(self, repo_name):
297 """
298 """
298 Puts cache invalidation task into db for
299 Puts cache invalidation task into db for
299 further global cache invalidation
300 further global cache invalidation
300
301
301 :param repo_name: this repo that should invalidation take place
302 :param repo_name: this repo that should invalidation take place
302 """
303 """
303 invalidated_keys = CacheInvalidation.set_invalidate(repo_name=repo_name)
304 invalidated_keys = CacheInvalidation.set_invalidate(repo_name=repo_name)
304 repo = Repository.get_by_repo_name(repo_name)
305 repo = Repository.get_by_repo_name(repo_name)
305 if repo:
306 if repo:
306 repo.update_changeset_cache()
307 repo.update_changeset_cache()
307 return invalidated_keys
308 return invalidated_keys
308
309
309 def toggle_following_repo(self, follow_repo_id, user_id):
310 def toggle_following_repo(self, follow_repo_id, user_id):
310
311
311 f = self.sa.query(UserFollowing)\
312 f = self.sa.query(UserFollowing)\
312 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
313 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
313 .filter(UserFollowing.user_id == user_id).scalar()
314 .filter(UserFollowing.user_id == user_id).scalar()
314
315
315 if f is not None:
316 if f is not None:
316 try:
317 try:
317 self.sa.delete(f)
318 self.sa.delete(f)
318 action_logger(UserTemp(user_id),
319 action_logger(UserTemp(user_id),
319 'stopped_following_repo',
320 'stopped_following_repo',
320 RepoTemp(follow_repo_id))
321 RepoTemp(follow_repo_id))
321 return
322 return
322 except:
323 except:
323 log.error(traceback.format_exc())
324 log.error(traceback.format_exc())
324 raise
325 raise
325
326
326 try:
327 try:
327 f = UserFollowing()
328 f = UserFollowing()
328 f.user_id = user_id
329 f.user_id = user_id
329 f.follows_repo_id = follow_repo_id
330 f.follows_repo_id = follow_repo_id
330 self.sa.add(f)
331 self.sa.add(f)
331
332
332 action_logger(UserTemp(user_id),
333 action_logger(UserTemp(user_id),
333 'started_following_repo',
334 'started_following_repo',
334 RepoTemp(follow_repo_id))
335 RepoTemp(follow_repo_id))
335 except:
336 except:
336 log.error(traceback.format_exc())
337 log.error(traceback.format_exc())
337 raise
338 raise
338
339
339 def toggle_following_user(self, follow_user_id, user_id):
340 def toggle_following_user(self, follow_user_id, user_id):
340 f = self.sa.query(UserFollowing)\
341 f = self.sa.query(UserFollowing)\
341 .filter(UserFollowing.follows_user_id == follow_user_id)\
342 .filter(UserFollowing.follows_user_id == follow_user_id)\
342 .filter(UserFollowing.user_id == user_id).scalar()
343 .filter(UserFollowing.user_id == user_id).scalar()
343
344
344 if f is not None:
345 if f is not None:
345 try:
346 try:
346 self.sa.delete(f)
347 self.sa.delete(f)
347 return
348 return
348 except:
349 except:
349 log.error(traceback.format_exc())
350 log.error(traceback.format_exc())
350 raise
351 raise
351
352
352 try:
353 try:
353 f = UserFollowing()
354 f = UserFollowing()
354 f.user_id = user_id
355 f.user_id = user_id
355 f.follows_user_id = follow_user_id
356 f.follows_user_id = follow_user_id
356 self.sa.add(f)
357 self.sa.add(f)
357 except:
358 except:
358 log.error(traceback.format_exc())
359 log.error(traceback.format_exc())
359 raise
360 raise
360
361
361 def is_following_repo(self, repo_name, user_id, cache=False):
362 def is_following_repo(self, repo_name, user_id, cache=False):
362 r = self.sa.query(Repository)\
363 r = self.sa.query(Repository)\
363 .filter(Repository.repo_name == repo_name).scalar()
364 .filter(Repository.repo_name == repo_name).scalar()
364
365
365 f = self.sa.query(UserFollowing)\
366 f = self.sa.query(UserFollowing)\
366 .filter(UserFollowing.follows_repository == r)\
367 .filter(UserFollowing.follows_repository == r)\
367 .filter(UserFollowing.user_id == user_id).scalar()
368 .filter(UserFollowing.user_id == user_id).scalar()
368
369
369 return f is not None
370 return f is not None
370
371
371 def is_following_user(self, username, user_id, cache=False):
372 def is_following_user(self, username, user_id, cache=False):
372 u = User.get_by_username(username)
373 u = User.get_by_username(username)
373
374
374 f = self.sa.query(UserFollowing)\
375 f = self.sa.query(UserFollowing)\
375 .filter(UserFollowing.follows_user == u)\
376 .filter(UserFollowing.follows_user == u)\
376 .filter(UserFollowing.user_id == user_id).scalar()
377 .filter(UserFollowing.user_id == user_id).scalar()
377
378
378 return f is not None
379 return f is not None
379
380
380 def get_followers(self, repo):
381 def get_followers(self, repo):
381 repo = self._get_repo(repo)
382 repo = self._get_repo(repo)
382
383
383 return self.sa.query(UserFollowing)\
384 return self.sa.query(UserFollowing)\
384 .filter(UserFollowing.follows_repository == repo).count()
385 .filter(UserFollowing.follows_repository == repo).count()
385
386
386 def get_forks(self, repo):
387 def get_forks(self, repo):
387 repo = self._get_repo(repo)
388 repo = self._get_repo(repo)
388 return self.sa.query(Repository)\
389 return self.sa.query(Repository)\
389 .filter(Repository.fork == repo).count()
390 .filter(Repository.fork == repo).count()
390
391
391 def get_pull_requests(self, repo):
392 def get_pull_requests(self, repo):
392 repo = self._get_repo(repo)
393 repo = self._get_repo(repo)
393 return self.sa.query(PullRequest)\
394 return self.sa.query(PullRequest)\
394 .filter(PullRequest.other_repo == repo).count()
395 .filter(PullRequest.other_repo == repo).count()
395
396
396 def mark_as_fork(self, repo, fork, user):
397 def mark_as_fork(self, repo, fork, user):
397 repo = self.__get_repo(repo)
398 repo = self.__get_repo(repo)
398 fork = self.__get_repo(fork)
399 fork = self.__get_repo(fork)
399 if fork and repo.repo_id == fork.repo_id:
400 if fork and repo.repo_id == fork.repo_id:
400 raise Exception("Cannot set repository as fork of itself")
401 raise Exception("Cannot set repository as fork of itself")
401 repo.fork = fork
402 repo.fork = fork
402 self.sa.add(repo)
403 self.sa.add(repo)
403 return repo
404 return repo
404
405
406 def _handle_push(self, repo, username, action, repo_name, repo_scm, revisions):
407 from rhodecode import CONFIG
408 from rhodecode.lib.base import _get_ip_addr
409 from pylons import request
410 environ = request.environ
411
412 #trigger push hook
413 extras = {
414 'ip': _get_ip_addr(environ),
415 'username': username,
416 'action': 'push_local',
417 'repository': repo_name,
418 'scm': repo_scm,
419 'config': CONFIG['__file__'],
420 'server_url': get_server_url(environ),
421 'make_lock': None,
422 'locked_by': [None, None]
423 }
424 _scm_repo = repo._repo
425 repo.inject_ui(**extras)
426 if repo_scm == 'hg':
427 log_push_action(_scm_repo.ui, _scm_repo, node=revisions[0])
428 elif repo_scm == 'git':
429 log_push_action(_scm_repo.ui, _scm_repo, _git_revs=revisions)
430
405 def pull_changes(self, repo, username):
431 def pull_changes(self, repo, username):
406 dbrepo = self.__get_repo(repo)
432 dbrepo = self.__get_repo(repo)
407 clone_uri = dbrepo.clone_uri
433 clone_uri = dbrepo.clone_uri
408 if not clone_uri:
434 if not clone_uri:
409 raise Exception("This repository doesn't have a clone uri")
435 raise Exception("This repository doesn't have a clone uri")
410
436
411 repo = dbrepo.scm_instance
437 repo = dbrepo.scm_instance
412 from rhodecode import CONFIG
438 repo_name = dbrepo.repo_name
413 try:
439 try:
414 extras = {
415 'ip': '',
416 'username': username,
417 'action': 'push_remote',
418 'repository': dbrepo.repo_name,
419 'scm': repo.alias,
420 'config': CONFIG['__file__'],
421 'make_lock': None,
422 'locked_by': [None, None]
423 }
424
425 Repository.inject_ui(repo, extras=extras)
426
427 if repo.alias == 'git':
440 if repo.alias == 'git':
428 repo.fetch(clone_uri)
441 repo.fetch(clone_uri)
429 else:
442 else:
430 repo.pull(clone_uri)
443 repo.pull(clone_uri)
431 self.mark_for_invalidation(dbrepo.repo_name)
444 self.mark_for_invalidation(repo_name)
432 except:
445 except:
433 log.error(traceback.format_exc())
446 log.error(traceback.format_exc())
434 raise
447 raise
435
448
436 def commit_change(self, repo, repo_name, cs, user, author, message,
449 def commit_change(self, repo, repo_name, cs, user, author, message,
437 content, f_path):
450 content, f_path):
438 """
451 """
439 Commits changes
452 Commits changes
440
453
441 :param repo: SCM instance
454 :param repo: SCM instance
442
455
443 """
456 """
444
445 if repo.alias == 'hg':
457 if repo.alias == 'hg':
446 from rhodecode.lib.vcs.backends.hg import \
458 from rhodecode.lib.vcs.backends.hg import \
447 MercurialInMemoryChangeset as IMC
459 MercurialInMemoryChangeset as IMC
448 elif repo.alias == 'git':
460 elif repo.alias == 'git':
449 from rhodecode.lib.vcs.backends.git import \
461 from rhodecode.lib.vcs.backends.git import \
450 GitInMemoryChangeset as IMC
462 GitInMemoryChangeset as IMC
451
463
452 # decoding here will force that we have proper encoded values
464 # decoding here will force that we have proper encoded values
453 # in any other case this will throw exceptions and deny commit
465 # in any other case this will throw exceptions and deny commit
454 content = safe_str(content)
466 content = safe_str(content)
455 path = safe_str(f_path)
467 path = safe_str(f_path)
456 # message and author needs to be unicode
468 # message and author needs to be unicode
457 # proper backend should then translate that into required type
469 # proper backend should then translate that into required type
458 message = safe_unicode(message)
470 message = safe_unicode(message)
459 author = safe_unicode(author)
471 author = safe_unicode(author)
460 m = IMC(repo)
472 m = IMC(repo)
461 m.change(FileNode(path, content))
473 m.change(FileNode(path, content))
462 tip = m.commit(message=message,
474 tip = m.commit(message=message,
463 author=author,
475 author=author,
464 parents=[cs], branch=cs.branch)
476 parents=[cs], branch=cs.branch)
465
477
466 action = 'push_local:%s' % tip.raw_id
467 action_logger(user, action, repo_name)
468 self.mark_for_invalidation(repo_name)
478 self.mark_for_invalidation(repo_name)
479 self._handle_push(repo,
480 username=user.username,
481 action='push_local',
482 repo_name=repo_name,
483 repo_scm=repo.alias,
484 revisions=[tip.raw_id])
469 return tip
485 return tip
470
486
471 def create_node(self, repo, repo_name, cs, user, author, message, content,
487 def create_node(self, repo, repo_name, cs, user, author, message, content,
472 f_path):
488 f_path):
473 if repo.alias == 'hg':
489 if repo.alias == 'hg':
474 from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC
490 from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC
475 elif repo.alias == 'git':
491 elif repo.alias == 'git':
476 from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC
492 from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC
477 # decoding here will force that we have proper encoded values
493 # decoding here will force that we have proper encoded values
478 # in any other case this will throw exceptions and deny commit
494 # in any other case this will throw exceptions and deny commit
479
495
480 if isinstance(content, (basestring,)):
496 if isinstance(content, (basestring,)):
481 content = safe_str(content)
497 content = safe_str(content)
482 elif isinstance(content, (file, cStringIO.OutputType,)):
498 elif isinstance(content, (file, cStringIO.OutputType,)):
483 content = content.read()
499 content = content.read()
484 else:
500 else:
485 raise Exception('Content is of unrecognized type %s' % (
501 raise Exception('Content is of unrecognized type %s' % (
486 type(content)
502 type(content)
487 ))
503 ))
488
504
489 message = safe_unicode(message)
505 message = safe_unicode(message)
490 author = safe_unicode(author)
506 author = safe_unicode(author)
491 path = safe_str(f_path)
507 path = safe_str(f_path)
492 m = IMC(repo)
508 m = IMC(repo)
493
509
494 if isinstance(cs, EmptyChangeset):
510 if isinstance(cs, EmptyChangeset):
495 # EmptyChangeset means we we're editing empty repository
511 # EmptyChangeset means we we're editing empty repository
496 parents = None
512 parents = None
497 else:
513 else:
498 parents = [cs]
514 parents = [cs]
499
515
500 m.add(FileNode(path, content=content))
516 m.add(FileNode(path, content=content))
501 tip = m.commit(message=message,
517 tip = m.commit(message=message,
502 author=author,
518 author=author,
503 parents=parents, branch=cs.branch)
519 parents=parents, branch=cs.branch)
504
520
505 action = 'push_local:%s' % tip.raw_id
506 action_logger(user, action, repo_name)
507 self.mark_for_invalidation(repo_name)
521 self.mark_for_invalidation(repo_name)
522 self._handle_push(repo,
523 username=user.username,
524 action='push_local',
525 repo_name=repo_name,
526 repo_scm=repo.alias,
527 revisions=[tip.raw_id])
508 return tip
528 return tip
509
529
510 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
530 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
511 """
531 """
512 recursive walk in root dir and return a set of all path in that dir
532 recursive walk in root dir and return a set of all path in that dir
513 based on repository walk function
533 based on repository walk function
514
534
515 :param repo_name: name of repository
535 :param repo_name: name of repository
516 :param revision: revision for which to list nodes
536 :param revision: revision for which to list nodes
517 :param root_path: root path to list
537 :param root_path: root path to list
518 :param flat: return as a list, if False returns a dict with decription
538 :param flat: return as a list, if False returns a dict with decription
519
539
520 """
540 """
521 _files = list()
541 _files = list()
522 _dirs = list()
542 _dirs = list()
523 try:
543 try:
524 _repo = self.__get_repo(repo_name)
544 _repo = self.__get_repo(repo_name)
525 changeset = _repo.scm_instance.get_changeset(revision)
545 changeset = _repo.scm_instance.get_changeset(revision)
526 root_path = root_path.lstrip('/')
546 root_path = root_path.lstrip('/')
527 for topnode, dirs, files in changeset.walk(root_path):
547 for topnode, dirs, files in changeset.walk(root_path):
528 for f in files:
548 for f in files:
529 _files.append(f.path if flat else {"name": f.path,
549 _files.append(f.path if flat else {"name": f.path,
530 "type": "file"})
550 "type": "file"})
531 for d in dirs:
551 for d in dirs:
532 _dirs.append(d.path if flat else {"name": d.path,
552 _dirs.append(d.path if flat else {"name": d.path,
533 "type": "dir"})
553 "type": "dir"})
534 except RepositoryError:
554 except RepositoryError:
535 log.debug(traceback.format_exc())
555 log.debug(traceback.format_exc())
536 raise
556 raise
537
557
538 return _dirs, _files
558 return _dirs, _files
539
559
540 def get_unread_journal(self):
560 def get_unread_journal(self):
541 return self.sa.query(UserLog).count()
561 return self.sa.query(UserLog).count()
542
562
543 def get_repo_landing_revs(self, repo=None):
563 def get_repo_landing_revs(self, repo=None):
544 """
564 """
545 Generates select option with tags branches and bookmarks (for hg only)
565 Generates select option with tags branches and bookmarks (for hg only)
546 grouped by type
566 grouped by type
547
567
548 :param repo:
568 :param repo:
549 :type repo:
569 :type repo:
550 """
570 """
551
571
552 hist_l = []
572 hist_l = []
553 choices = []
573 choices = []
554 repo = self.__get_repo(repo)
574 repo = self.__get_repo(repo)
555 hist_l.append(['tip', _('latest tip')])
575 hist_l.append(['tip', _('latest tip')])
556 choices.append('tip')
576 choices.append('tip')
557 if not repo:
577 if not repo:
558 return choices, hist_l
578 return choices, hist_l
559
579
560 repo = repo.scm_instance
580 repo = repo.scm_instance
561
581
562 branches_group = ([(k, k) for k, v in
582 branches_group = ([(k, k) for k, v in
563 repo.branches.iteritems()], _("Branches"))
583 repo.branches.iteritems()], _("Branches"))
564 hist_l.append(branches_group)
584 hist_l.append(branches_group)
565 choices.extend([x[0] for x in branches_group[0]])
585 choices.extend([x[0] for x in branches_group[0]])
566
586
567 if repo.alias == 'hg':
587 if repo.alias == 'hg':
568 bookmarks_group = ([(k, k) for k, v in
588 bookmarks_group = ([(k, k) for k, v in
569 repo.bookmarks.iteritems()], _("Bookmarks"))
589 repo.bookmarks.iteritems()], _("Bookmarks"))
570 hist_l.append(bookmarks_group)
590 hist_l.append(bookmarks_group)
571 choices.extend([x[0] for x in bookmarks_group[0]])
591 choices.extend([x[0] for x in bookmarks_group[0]])
572
592
573 tags_group = ([(k, k) for k, v in
593 tags_group = ([(k, k) for k, v in
574 repo.tags.iteritems()], _("Tags"))
594 repo.tags.iteritems()], _("Tags"))
575 hist_l.append(tags_group)
595 hist_l.append(tags_group)
576 choices.extend([x[0] for x in tags_group[0]])
596 choices.extend([x[0] for x in tags_group[0]])
577
597
578 return choices, hist_l
598 return choices, hist_l
579
599
580 def install_git_hook(self, repo, force_create=False):
600 def install_git_hook(self, repo, force_create=False):
581 """
601 """
582 Creates a rhodecode hook inside a git repository
602 Creates a rhodecode hook inside a git repository
583
603
584 :param repo: Instance of VCS repo
604 :param repo: Instance of VCS repo
585 :param force_create: Create even if same name hook exists
605 :param force_create: Create even if same name hook exists
586 """
606 """
587
607
588 loc = jn(repo.path, 'hooks')
608 loc = jn(repo.path, 'hooks')
589 if not repo.bare:
609 if not repo.bare:
590 loc = jn(repo.path, '.git', 'hooks')
610 loc = jn(repo.path, '.git', 'hooks')
591 if not os.path.isdir(loc):
611 if not os.path.isdir(loc):
592 os.makedirs(loc)
612 os.makedirs(loc)
593
613
594 tmpl_post = pkg_resources.resource_string(
614 tmpl_post = pkg_resources.resource_string(
595 'rhodecode', jn('config', 'post_receive_tmpl.py')
615 'rhodecode', jn('config', 'post_receive_tmpl.py')
596 )
616 )
597 tmpl_pre = pkg_resources.resource_string(
617 tmpl_pre = pkg_resources.resource_string(
598 'rhodecode', jn('config', 'pre_receive_tmpl.py')
618 'rhodecode', jn('config', 'pre_receive_tmpl.py')
599 )
619 )
600
620
601 for h_type, tmpl in [('pre', tmpl_pre), ('post', tmpl_post)]:
621 for h_type, tmpl in [('pre', tmpl_pre), ('post', tmpl_post)]:
602 _hook_file = jn(loc, '%s-receive' % h_type)
622 _hook_file = jn(loc, '%s-receive' % h_type)
603 _rhodecode_hook = False
623 _rhodecode_hook = False
604 log.debug('Installing git hook in repo %s' % repo)
624 log.debug('Installing git hook in repo %s' % repo)
605 if os.path.exists(_hook_file):
625 if os.path.exists(_hook_file):
606 # let's take a look at this hook, maybe it's rhodecode ?
626 # let's take a look at this hook, maybe it's rhodecode ?
607 log.debug('hook exists, checking if it is from rhodecode')
627 log.debug('hook exists, checking if it is from rhodecode')
608 _HOOK_VER_PAT = re.compile(r'^RC_HOOK_VER')
628 _HOOK_VER_PAT = re.compile(r'^RC_HOOK_VER')
609 with open(_hook_file, 'rb') as f:
629 with open(_hook_file, 'rb') as f:
610 data = f.read()
630 data = f.read()
611 matches = re.compile(r'(?:%s)\s*=\s*(.*)'
631 matches = re.compile(r'(?:%s)\s*=\s*(.*)'
612 % 'RC_HOOK_VER').search(data)
632 % 'RC_HOOK_VER').search(data)
613 if matches:
633 if matches:
614 try:
634 try:
615 ver = matches.groups()[0]
635 ver = matches.groups()[0]
616 log.debug('got %s it is rhodecode' % (ver))
636 log.debug('got %s it is rhodecode' % (ver))
617 _rhodecode_hook = True
637 _rhodecode_hook = True
618 except:
638 except:
619 log.error(traceback.format_exc())
639 log.error(traceback.format_exc())
620 else:
640 else:
621 # there is no hook in this dir, so we want to create one
641 # there is no hook in this dir, so we want to create one
622 _rhodecode_hook = True
642 _rhodecode_hook = True
623
643
624 if _rhodecode_hook or force_create:
644 if _rhodecode_hook or force_create:
625 log.debug('writing %s hook file !' % h_type)
645 log.debug('writing %s hook file !' % h_type)
626 with open(_hook_file, 'wb') as f:
646 with open(_hook_file, 'wb') as f:
627 tmpl = tmpl.replace('_TMPL_', rhodecode.__version__)
647 tmpl = tmpl.replace('_TMPL_', rhodecode.__version__)
628 f.write(tmpl)
648 f.write(tmpl)
629 os.chmod(_hook_file, 0755)
649 os.chmod(_hook_file, 0755)
630 else:
650 else:
631 log.debug('skipping writing hook file')
651 log.debug('skipping writing hook file')
General Comments 0
You need to be logged in to leave comments. Login now