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