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