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