##// END OF EJS Templates
Backported fixe for #834 hooks error on remote pulling
marcink -
r3879:51596d9e default
parent child Browse files
Show More
@@ -1,674 +1,680
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 Exception:
324 except Exception:
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 Exception:
337 except Exception:
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 Exception:
350 except Exception:
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 Exception:
359 except Exception:
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_rc_scm_extras(self, username, repo_name, repo_alias,
409 action=None):
410 from rhodecode import CONFIG
411 from rhodecode.lib.base import _get_ip_addr
412 try:
413 from pylons import request
414 environ = request.environ
415 except TypeError:
416 # we might use this outside of request context, let's fake the
417 # environ data
418 from webob import Request
419 environ = Request.blank('').environ
420 extras = {
421 'ip': _get_ip_addr(environ),
422 'username': username,
423 'action': action or 'push_local',
424 'repository': repo_name,
425 'scm': repo_alias,
426 'config': CONFIG['__file__'],
427 'server_url': get_server_url(environ),
428 'make_lock': None,
429 'locked_by': [None, None]
430 }
431 _set_extras(extras)
432
408 def _handle_push(self, repo, username, action, repo_name, revisions):
433 def _handle_push(self, repo, username, action, repo_name, revisions):
409 """
434 """
410 Triggers push action hooks
435 Triggers push action hooks
411
436
412 :param repo: SCM repo
437 :param repo: SCM repo
413 :param username: username who pushes
438 :param username: username who pushes
414 :param action: push/push_loca/push_remote
439 :param action: push/push_loca/push_remote
415 :param repo_name: name of repo
440 :param repo_name: name of repo
416 :param revisions: list of revisions that we pushed
441 :param revisions: list of revisions that we pushed
417 """
442 """
418 from rhodecode import CONFIG
443 self._handle_rc_scm_extras(username, repo_name, repo_alias=repo.alias)
419 from rhodecode.lib.base import _get_ip_addr
444 _scm_repo = repo._repo
420 try:
421 from pylons import request
422 environ = request.environ
423 except TypeError:
424 # we might use this outside of request context, let's fake the
425 # environ data
426 from webob import Request
427 environ = Request.blank('').environ
428
429 #trigger push hook
445 # trigger push hook
430 extras = {
431 'ip': _get_ip_addr(environ),
432 'username': username,
433 'action': 'push_local',
434 'repository': repo_name,
435 'scm': repo.alias,
436 'config': CONFIG['__file__'],
437 'server_url': get_server_url(environ),
438 'make_lock': None,
439 'locked_by': [None, None]
440 }
441 _scm_repo = repo._repo
442 _set_extras(extras)
443 if repo.alias == 'hg':
446 if repo.alias == 'hg':
444 log_push_action(_scm_repo.ui, _scm_repo, node=revisions[0])
447 log_push_action(_scm_repo.ui, _scm_repo, node=revisions[0])
445 elif repo.alias == 'git':
448 elif repo.alias == 'git':
446 log_push_action(None, _scm_repo, _git_revs=revisions)
449 log_push_action(None, _scm_repo, _git_revs=revisions)
447
450
448 def _get_IMC_module(self, scm_type):
451 def _get_IMC_module(self, scm_type):
449 """
452 """
450 Returns InMemoryCommit class based on scm_type
453 Returns InMemoryCommit class based on scm_type
451
454
452 :param scm_type:
455 :param scm_type:
453 """
456 """
454 if scm_type == 'hg':
457 if scm_type == 'hg':
455 from rhodecode.lib.vcs.backends.hg import \
458 from rhodecode.lib.vcs.backends.hg import \
456 MercurialInMemoryChangeset as IMC
459 MercurialInMemoryChangeset as IMC
457 elif scm_type == 'git':
460 elif scm_type == 'git':
458 from rhodecode.lib.vcs.backends.git import \
461 from rhodecode.lib.vcs.backends.git import \
459 GitInMemoryChangeset as IMC
462 GitInMemoryChangeset as IMC
460 return IMC
463 return IMC
461
464
462 def pull_changes(self, repo, username):
465 def pull_changes(self, repo, username):
463 dbrepo = self.__get_repo(repo)
466 dbrepo = self.__get_repo(repo)
464 clone_uri = dbrepo.clone_uri
467 clone_uri = dbrepo.clone_uri
465 if not clone_uri:
468 if not clone_uri:
466 raise Exception("This repository doesn't have a clone uri")
469 raise Exception("This repository doesn't have a clone uri")
467
470
468 repo = dbrepo.scm_instance
471 repo = dbrepo.scm_instance
469 repo_name = dbrepo.repo_name
472 repo_name = dbrepo.repo_name
470 try:
473 try:
471 if repo.alias == 'git':
474 if repo.alias == 'git':
472 repo.fetch(clone_uri)
475 repo.fetch(clone_uri)
473 else:
476 else:
477 self._handle_rc_scm_extras(username, dbrepo.repo_name,
478 repo.alias, action='push_remote')
474 repo.pull(clone_uri)
479 repo.pull(clone_uri)
480
475 self.mark_for_invalidation(repo_name)
481 self.mark_for_invalidation(repo_name)
476 except Exception:
482 except Exception:
477 log.error(traceback.format_exc())
483 log.error(traceback.format_exc())
478 raise
484 raise
479
485
480 def commit_change(self, repo, repo_name, cs, user, author, message,
486 def commit_change(self, repo, repo_name, cs, user, author, message,
481 content, f_path):
487 content, f_path):
482 """
488 """
483 Commits changes
489 Commits changes
484
490
485 :param repo: SCM instance
491 :param repo: SCM instance
486
492
487 """
493 """
488 user = self._get_user(user)
494 user = self._get_user(user)
489 IMC = self._get_IMC_module(repo.alias)
495 IMC = self._get_IMC_module(repo.alias)
490
496
491 # decoding here will force that we have proper encoded values
497 # decoding here will force that we have proper encoded values
492 # in any other case this will throw exceptions and deny commit
498 # in any other case this will throw exceptions and deny commit
493 content = safe_str(content)
499 content = safe_str(content)
494 path = safe_str(f_path)
500 path = safe_str(f_path)
495 # message and author needs to be unicode
501 # message and author needs to be unicode
496 # proper backend should then translate that into required type
502 # proper backend should then translate that into required type
497 message = safe_unicode(message)
503 message = safe_unicode(message)
498 author = safe_unicode(author)
504 author = safe_unicode(author)
499 imc = IMC(repo)
505 imc = IMC(repo)
500 imc.change(FileNode(path, content, mode=cs.get_file_mode(f_path)))
506 imc.change(FileNode(path, content, mode=cs.get_file_mode(f_path)))
501 tip = imc.commit(message=message,
507 tip = imc.commit(message=message,
502 author=author,
508 author=author,
503 parents=[cs], branch=cs.branch)
509 parents=[cs], branch=cs.branch)
504
510
505 self.mark_for_invalidation(repo_name)
511 self.mark_for_invalidation(repo_name)
506 self._handle_push(repo,
512 self._handle_push(repo,
507 username=user.username,
513 username=user.username,
508 action='push_local',
514 action='push_local',
509 repo_name=repo_name,
515 repo_name=repo_name,
510 revisions=[tip.raw_id])
516 revisions=[tip.raw_id])
511 return tip
517 return tip
512
518
513 def create_node(self, repo, repo_name, cs, user, author, message, content,
519 def create_node(self, repo, repo_name, cs, user, author, message, content,
514 f_path):
520 f_path):
515 user = self._get_user(user)
521 user = self._get_user(user)
516 IMC = self._get_IMC_module(repo.alias)
522 IMC = self._get_IMC_module(repo.alias)
517
523
518 # decoding here will force that we have proper encoded values
524 # decoding here will force that we have proper encoded values
519 # in any other case this will throw exceptions and deny commit
525 # in any other case this will throw exceptions and deny commit
520 if isinstance(content, (basestring,)):
526 if isinstance(content, (basestring,)):
521 content = safe_str(content)
527 content = safe_str(content)
522 elif isinstance(content, (file, cStringIO.OutputType,)):
528 elif isinstance(content, (file, cStringIO.OutputType,)):
523 content = content.read()
529 content = content.read()
524 else:
530 else:
525 raise Exception('Content is of unrecognized type %s' % (
531 raise Exception('Content is of unrecognized type %s' % (
526 type(content)
532 type(content)
527 ))
533 ))
528
534
529 message = safe_unicode(message)
535 message = safe_unicode(message)
530 author = safe_unicode(author)
536 author = safe_unicode(author)
531 path = safe_str(f_path)
537 path = safe_str(f_path)
532 m = IMC(repo)
538 m = IMC(repo)
533
539
534 if isinstance(cs, EmptyChangeset):
540 if isinstance(cs, EmptyChangeset):
535 # EmptyChangeset means we we're editing empty repository
541 # EmptyChangeset means we we're editing empty repository
536 parents = None
542 parents = None
537 else:
543 else:
538 parents = [cs]
544 parents = [cs]
539
545
540 m.add(FileNode(path, content=content))
546 m.add(FileNode(path, content=content))
541 tip = m.commit(message=message,
547 tip = m.commit(message=message,
542 author=author,
548 author=author,
543 parents=parents, branch=cs.branch)
549 parents=parents, branch=cs.branch)
544
550
545 self.mark_for_invalidation(repo_name)
551 self.mark_for_invalidation(repo_name)
546 self._handle_push(repo,
552 self._handle_push(repo,
547 username=user.username,
553 username=user.username,
548 action='push_local',
554 action='push_local',
549 repo_name=repo_name,
555 repo_name=repo_name,
550 revisions=[tip.raw_id])
556 revisions=[tip.raw_id])
551 return tip
557 return tip
552
558
553 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
559 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
554 """
560 """
555 recursive walk in root dir and return a set of all path in that dir
561 recursive walk in root dir and return a set of all path in that dir
556 based on repository walk function
562 based on repository walk function
557
563
558 :param repo_name: name of repository
564 :param repo_name: name of repository
559 :param revision: revision for which to list nodes
565 :param revision: revision for which to list nodes
560 :param root_path: root path to list
566 :param root_path: root path to list
561 :param flat: return as a list, if False returns a dict with decription
567 :param flat: return as a list, if False returns a dict with decription
562
568
563 """
569 """
564 _files = list()
570 _files = list()
565 _dirs = list()
571 _dirs = list()
566 try:
572 try:
567 _repo = self.__get_repo(repo_name)
573 _repo = self.__get_repo(repo_name)
568 changeset = _repo.scm_instance.get_changeset(revision)
574 changeset = _repo.scm_instance.get_changeset(revision)
569 root_path = root_path.lstrip('/')
575 root_path = root_path.lstrip('/')
570 for topnode, dirs, files in changeset.walk(root_path):
576 for topnode, dirs, files in changeset.walk(root_path):
571 for f in files:
577 for f in files:
572 _files.append(f.path if flat else {"name": f.path,
578 _files.append(f.path if flat else {"name": f.path,
573 "type": "file"})
579 "type": "file"})
574 for d in dirs:
580 for d in dirs:
575 _dirs.append(d.path if flat else {"name": d.path,
581 _dirs.append(d.path if flat else {"name": d.path,
576 "type": "dir"})
582 "type": "dir"})
577 except RepositoryError:
583 except RepositoryError:
578 log.debug(traceback.format_exc())
584 log.debug(traceback.format_exc())
579 raise
585 raise
580
586
581 return _dirs, _files
587 return _dirs, _files
582
588
583 def get_unread_journal(self):
589 def get_unread_journal(self):
584 return self.sa.query(UserLog).count()
590 return self.sa.query(UserLog).count()
585
591
586 def get_repo_landing_revs(self, repo=None):
592 def get_repo_landing_revs(self, repo=None):
587 """
593 """
588 Generates select option with tags branches and bookmarks (for hg only)
594 Generates select option with tags branches and bookmarks (for hg only)
589 grouped by type
595 grouped by type
590
596
591 :param repo:
597 :param repo:
592 :type repo:
598 :type repo:
593 """
599 """
594
600
595 hist_l = []
601 hist_l = []
596 choices = []
602 choices = []
597 repo = self.__get_repo(repo)
603 repo = self.__get_repo(repo)
598 hist_l.append(['tip', _('latest tip')])
604 hist_l.append(['tip', _('latest tip')])
599 choices.append('tip')
605 choices.append('tip')
600 if not repo:
606 if not repo:
601 return choices, hist_l
607 return choices, hist_l
602
608
603 repo = repo.scm_instance
609 repo = repo.scm_instance
604
610
605 branches_group = ([(k, k) for k, v in
611 branches_group = ([(k, k) for k, v in
606 repo.branches.iteritems()], _("Branches"))
612 repo.branches.iteritems()], _("Branches"))
607 hist_l.append(branches_group)
613 hist_l.append(branches_group)
608 choices.extend([x[0] for x in branches_group[0]])
614 choices.extend([x[0] for x in branches_group[0]])
609
615
610 if repo.alias == 'hg':
616 if repo.alias == 'hg':
611 bookmarks_group = ([(k, k) for k, v in
617 bookmarks_group = ([(k, k) for k, v in
612 repo.bookmarks.iteritems()], _("Bookmarks"))
618 repo.bookmarks.iteritems()], _("Bookmarks"))
613 hist_l.append(bookmarks_group)
619 hist_l.append(bookmarks_group)
614 choices.extend([x[0] for x in bookmarks_group[0]])
620 choices.extend([x[0] for x in bookmarks_group[0]])
615
621
616 tags_group = ([(k, k) for k, v in
622 tags_group = ([(k, k) for k, v in
617 repo.tags.iteritems()], _("Tags"))
623 repo.tags.iteritems()], _("Tags"))
618 hist_l.append(tags_group)
624 hist_l.append(tags_group)
619 choices.extend([x[0] for x in tags_group[0]])
625 choices.extend([x[0] for x in tags_group[0]])
620
626
621 return choices, hist_l
627 return choices, hist_l
622
628
623 def install_git_hook(self, repo, force_create=False):
629 def install_git_hook(self, repo, force_create=False):
624 """
630 """
625 Creates a rhodecode hook inside a git repository
631 Creates a rhodecode hook inside a git repository
626
632
627 :param repo: Instance of VCS repo
633 :param repo: Instance of VCS repo
628 :param force_create: Create even if same name hook exists
634 :param force_create: Create even if same name hook exists
629 """
635 """
630
636
631 loc = jn(repo.path, 'hooks')
637 loc = jn(repo.path, 'hooks')
632 if not repo.bare:
638 if not repo.bare:
633 loc = jn(repo.path, '.git', 'hooks')
639 loc = jn(repo.path, '.git', 'hooks')
634 if not os.path.isdir(loc):
640 if not os.path.isdir(loc):
635 os.makedirs(loc)
641 os.makedirs(loc)
636
642
637 tmpl_post = pkg_resources.resource_string(
643 tmpl_post = pkg_resources.resource_string(
638 'rhodecode', jn('config', 'post_receive_tmpl.py')
644 'rhodecode', jn('config', 'post_receive_tmpl.py')
639 )
645 )
640 tmpl_pre = pkg_resources.resource_string(
646 tmpl_pre = pkg_resources.resource_string(
641 'rhodecode', jn('config', 'pre_receive_tmpl.py')
647 'rhodecode', jn('config', 'pre_receive_tmpl.py')
642 )
648 )
643
649
644 for h_type, tmpl in [('pre', tmpl_pre), ('post', tmpl_post)]:
650 for h_type, tmpl in [('pre', tmpl_pre), ('post', tmpl_post)]:
645 _hook_file = jn(loc, '%s-receive' % h_type)
651 _hook_file = jn(loc, '%s-receive' % h_type)
646 _rhodecode_hook = False
652 _rhodecode_hook = False
647 log.debug('Installing git hook in repo %s' % repo)
653 log.debug('Installing git hook in repo %s' % repo)
648 if os.path.exists(_hook_file):
654 if os.path.exists(_hook_file):
649 # let's take a look at this hook, maybe it's rhodecode ?
655 # let's take a look at this hook, maybe it's rhodecode ?
650 log.debug('hook exists, checking if it is from rhodecode')
656 log.debug('hook exists, checking if it is from rhodecode')
651 _HOOK_VER_PAT = re.compile(r'^RC_HOOK_VER')
657 _HOOK_VER_PAT = re.compile(r'^RC_HOOK_VER')
652 with open(_hook_file, 'rb') as f:
658 with open(_hook_file, 'rb') as f:
653 data = f.read()
659 data = f.read()
654 matches = re.compile(r'(?:%s)\s*=\s*(.*)'
660 matches = re.compile(r'(?:%s)\s*=\s*(.*)'
655 % 'RC_HOOK_VER').search(data)
661 % 'RC_HOOK_VER').search(data)
656 if matches:
662 if matches:
657 try:
663 try:
658 ver = matches.groups()[0]
664 ver = matches.groups()[0]
659 log.debug('got %s it is rhodecode' % (ver))
665 log.debug('got %s it is rhodecode' % (ver))
660 _rhodecode_hook = True
666 _rhodecode_hook = True
661 except Exception:
667 except Exception:
662 log.error(traceback.format_exc())
668 log.error(traceback.format_exc())
663 else:
669 else:
664 # there is no hook in this dir, so we want to create one
670 # there is no hook in this dir, so we want to create one
665 _rhodecode_hook = True
671 _rhodecode_hook = True
666
672
667 if _rhodecode_hook or force_create:
673 if _rhodecode_hook or force_create:
668 log.debug('writing %s hook file !' % h_type)
674 log.debug('writing %s hook file !' % h_type)
669 with open(_hook_file, 'wb') as f:
675 with open(_hook_file, 'wb') as f:
670 tmpl = tmpl.replace('_TMPL_', rhodecode.__version__)
676 tmpl = tmpl.replace('_TMPL_', rhodecode.__version__)
671 f.write(tmpl)
677 f.write(tmpl)
672 os.chmod(_hook_file, 0755)
678 os.chmod(_hook_file, 0755)
673 else:
679 else:
674 log.debug('skipping writing hook file')
680 log.debug('skipping writing hook file')
General Comments 0
You need to be logged in to leave comments. Login now