##// END OF EJS Templates
Created install_git_hook more verbose version of previos code....
marcink -
r2618:e1370dcb beta
parent child Browse files
Show More
@@ -1,539 +1,526 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.repo
3 rhodecode.model.repo
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 Repository model for rhodecode
6 Repository model for rhodecode
7
7
8 :created_on: Jun 5, 2010
8 :created_on: Jun 5, 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 shutil
27 import shutil
28 import logging
28 import logging
29 import traceback
29 import traceback
30 import pkg_resources
31 from os.path import dirname as dn, join as jn
32 from datetime import datetime
30 from datetime import datetime
33
31
34 from rhodecode.lib.vcs.backends import get_backend
32 from rhodecode.lib.vcs.backends import get_backend
35 from rhodecode.lib.compat import json
33 from rhodecode.lib.compat import json
36 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode
34 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode
37 from rhodecode.lib.caching_query import FromCache
35 from rhodecode.lib.caching_query import FromCache
38 from rhodecode.lib.hooks import log_create_repository
36 from rhodecode.lib.hooks import log_create_repository
39
37
40 from rhodecode.model import BaseModel
38 from rhodecode.model import BaseModel
41 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
39 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
42 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup
40 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup
43 from rhodecode.lib import helpers as h
41 from rhodecode.lib import helpers as h
44
42
45
43
46 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
47
45
48
46
49 class RepoModel(BaseModel):
47 class RepoModel(BaseModel):
50
48
51 cls = Repository
49 cls = Repository
52 URL_SEPARATOR = Repository.url_sep()
50 URL_SEPARATOR = Repository.url_sep()
53
51
54 def __get_users_group(self, users_group):
52 def __get_users_group(self, users_group):
55 return self._get_instance(UsersGroup, users_group,
53 return self._get_instance(UsersGroup, users_group,
56 callback=UsersGroup.get_by_group_name)
54 callback=UsersGroup.get_by_group_name)
57
55
58 def __get_repos_group(self, repos_group):
56 def __get_repos_group(self, repos_group):
59 return self._get_instance(RepoGroup, repos_group,
57 return self._get_instance(RepoGroup, repos_group,
60 callback=RepoGroup.get_by_group_name)
58 callback=RepoGroup.get_by_group_name)
61
59
62 @LazyProperty
60 @LazyProperty
63 def repos_path(self):
61 def repos_path(self):
64 """
62 """
65 Get's the repositories root path from database
63 Get's the repositories root path from database
66 """
64 """
67
65
68 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
66 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
69 return q.ui_value
67 return q.ui_value
70
68
71 def get(self, repo_id, cache=False):
69 def get(self, repo_id, cache=False):
72 repo = self.sa.query(Repository)\
70 repo = self.sa.query(Repository)\
73 .filter(Repository.repo_id == repo_id)
71 .filter(Repository.repo_id == repo_id)
74
72
75 if cache:
73 if cache:
76 repo = repo.options(FromCache("sql_cache_short",
74 repo = repo.options(FromCache("sql_cache_short",
77 "get_repo_%s" % repo_id))
75 "get_repo_%s" % repo_id))
78 return repo.scalar()
76 return repo.scalar()
79
77
80 def get_repo(self, repository):
78 def get_repo(self, repository):
81 return self._get_repo(repository)
79 return self._get_repo(repository)
82
80
83 def get_by_repo_name(self, repo_name, cache=False):
81 def get_by_repo_name(self, repo_name, cache=False):
84 repo = self.sa.query(Repository)\
82 repo = self.sa.query(Repository)\
85 .filter(Repository.repo_name == repo_name)
83 .filter(Repository.repo_name == repo_name)
86
84
87 if cache:
85 if cache:
88 repo = repo.options(FromCache("sql_cache_short",
86 repo = repo.options(FromCache("sql_cache_short",
89 "get_repo_%s" % repo_name))
87 "get_repo_%s" % repo_name))
90 return repo.scalar()
88 return repo.scalar()
91
89
92 def get_users_js(self):
90 def get_users_js(self):
93 users = self.sa.query(User).filter(User.active == True).all()
91 users = self.sa.query(User).filter(User.active == True).all()
94 return json.dumps([
92 return json.dumps([
95 {
93 {
96 'id': u.user_id,
94 'id': u.user_id,
97 'fname': u.name,
95 'fname': u.name,
98 'lname': u.lastname,
96 'lname': u.lastname,
99 'nname': u.username,
97 'nname': u.username,
100 'gravatar_lnk': h.gravatar_url(u.email, 14)
98 'gravatar_lnk': h.gravatar_url(u.email, 14)
101 } for u in users]
99 } for u in users]
102 )
100 )
103
101
104 def get_users_groups_js(self):
102 def get_users_groups_js(self):
105 users_groups = self.sa.query(UsersGroup)\
103 users_groups = self.sa.query(UsersGroup)\
106 .filter(UsersGroup.users_group_active == True).all()
104 .filter(UsersGroup.users_group_active == True).all()
107
105
108 return json.dumps([
106 return json.dumps([
109 {
107 {
110 'id': gr.users_group_id,
108 'id': gr.users_group_id,
111 'grname': gr.users_group_name,
109 'grname': gr.users_group_name,
112 'grmembers': len(gr.members),
110 'grmembers': len(gr.members),
113 } for gr in users_groups]
111 } for gr in users_groups]
114 )
112 )
115
113
116 def _get_defaults(self, repo_name):
114 def _get_defaults(self, repo_name):
117 """
115 """
118 Get's information about repository, and returns a dict for
116 Get's information about repository, and returns a dict for
119 usage in forms
117 usage in forms
120
118
121 :param repo_name:
119 :param repo_name:
122 """
120 """
123
121
124 repo_info = Repository.get_by_repo_name(repo_name)
122 repo_info = Repository.get_by_repo_name(repo_name)
125
123
126 if repo_info is None:
124 if repo_info is None:
127 return None
125 return None
128
126
129 defaults = repo_info.get_dict()
127 defaults = repo_info.get_dict()
130 group, repo_name = repo_info.groups_and_repo
128 group, repo_name = repo_info.groups_and_repo
131 defaults['repo_name'] = repo_name
129 defaults['repo_name'] = repo_name
132 defaults['repo_group'] = getattr(group[-1] if group else None,
130 defaults['repo_group'] = getattr(group[-1] if group else None,
133 'group_id', None)
131 'group_id', None)
134
132
135 # fill owner
133 # fill owner
136 if repo_info.user:
134 if repo_info.user:
137 defaults.update({'user': repo_info.user.username})
135 defaults.update({'user': repo_info.user.username})
138 else:
136 else:
139 replacement_user = User.query().filter(User.admin ==
137 replacement_user = User.query().filter(User.admin ==
140 True).first().username
138 True).first().username
141 defaults.update({'user': replacement_user})
139 defaults.update({'user': replacement_user})
142
140
143 # fill repository users
141 # fill repository users
144 for p in repo_info.repo_to_perm:
142 for p in repo_info.repo_to_perm:
145 defaults.update({'u_perm_%s' % p.user.username:
143 defaults.update({'u_perm_%s' % p.user.username:
146 p.permission.permission_name})
144 p.permission.permission_name})
147
145
148 # fill repository groups
146 # fill repository groups
149 for p in repo_info.users_group_to_perm:
147 for p in repo_info.users_group_to_perm:
150 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
148 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
151 p.permission.permission_name})
149 p.permission.permission_name})
152
150
153 return defaults
151 return defaults
154
152
155 def update(self, repo_name, form_data):
153 def update(self, repo_name, form_data):
156 try:
154 try:
157 cur_repo = self.get_by_repo_name(repo_name, cache=False)
155 cur_repo = self.get_by_repo_name(repo_name, cache=False)
158
156
159 # update permissions
157 # update permissions
160 for member, perm, member_type in form_data['perms_updates']:
158 for member, perm, member_type in form_data['perms_updates']:
161 if member_type == 'user':
159 if member_type == 'user':
162 # this updates existing one
160 # this updates existing one
163 RepoModel().grant_user_permission(
161 RepoModel().grant_user_permission(
164 repo=cur_repo, user=member, perm=perm
162 repo=cur_repo, user=member, perm=perm
165 )
163 )
166 else:
164 else:
167 RepoModel().grant_users_group_permission(
165 RepoModel().grant_users_group_permission(
168 repo=cur_repo, group_name=member, perm=perm
166 repo=cur_repo, group_name=member, perm=perm
169 )
167 )
170 # set new permissions
168 # set new permissions
171 for member, perm, member_type in form_data['perms_new']:
169 for member, perm, member_type in form_data['perms_new']:
172 if member_type == 'user':
170 if member_type == 'user':
173 RepoModel().grant_user_permission(
171 RepoModel().grant_user_permission(
174 repo=cur_repo, user=member, perm=perm
172 repo=cur_repo, user=member, perm=perm
175 )
173 )
176 else:
174 else:
177 RepoModel().grant_users_group_permission(
175 RepoModel().grant_users_group_permission(
178 repo=cur_repo, group_name=member, perm=perm
176 repo=cur_repo, group_name=member, perm=perm
179 )
177 )
180
178
181 # update current repo
179 # update current repo
182 for k, v in form_data.items():
180 for k, v in form_data.items():
183 if k == 'user':
181 if k == 'user':
184 cur_repo.user = User.get_by_username(v)
182 cur_repo.user = User.get_by_username(v)
185 elif k == 'repo_name':
183 elif k == 'repo_name':
186 pass
184 pass
187 elif k == 'repo_group':
185 elif k == 'repo_group':
188 cur_repo.group = RepoGroup.get(v)
186 cur_repo.group = RepoGroup.get(v)
189
187
190 else:
188 else:
191 setattr(cur_repo, k, v)
189 setattr(cur_repo, k, v)
192
190
193 new_name = cur_repo.get_new_name(form_data['repo_name'])
191 new_name = cur_repo.get_new_name(form_data['repo_name'])
194 cur_repo.repo_name = new_name
192 cur_repo.repo_name = new_name
195
193
196 self.sa.add(cur_repo)
194 self.sa.add(cur_repo)
197
195
198 if repo_name != new_name:
196 if repo_name != new_name:
199 # rename repository
197 # rename repository
200 self.__rename_repo(old=repo_name, new=new_name)
198 self.__rename_repo(old=repo_name, new=new_name)
201
199
202 return cur_repo
200 return cur_repo
203 except:
201 except:
204 log.error(traceback.format_exc())
202 log.error(traceback.format_exc())
205 raise
203 raise
206
204
207 def create_repo(self, repo_name, repo_type, description, owner,
205 def create_repo(self, repo_name, repo_type, description, owner,
208 private=False, clone_uri=None, repos_group=None,
206 private=False, clone_uri=None, repos_group=None,
209 landing_rev='tip', just_db=False, fork_of=None,
207 landing_rev='tip', just_db=False, fork_of=None,
210 copy_fork_permissions=False):
208 copy_fork_permissions=False):
211 from rhodecode.model.scm import ScmModel
209 from rhodecode.model.scm import ScmModel
212
210
213 owner = self._get_user(owner)
211 owner = self._get_user(owner)
214 fork_of = self._get_repo(fork_of)
212 fork_of = self._get_repo(fork_of)
215 repos_group = self.__get_repos_group(repos_group)
213 repos_group = self.__get_repos_group(repos_group)
216 try:
214 try:
217
215
218 # repo name is just a name of repository
216 # repo name is just a name of repository
219 # while repo_name_full is a full qualified name that is combined
217 # while repo_name_full is a full qualified name that is combined
220 # with name and path of group
218 # with name and path of group
221 repo_name_full = repo_name
219 repo_name_full = repo_name
222 repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
220 repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
223
221
224 new_repo = Repository()
222 new_repo = Repository()
225 new_repo.enable_statistics = False
223 new_repo.enable_statistics = False
226 new_repo.repo_name = repo_name_full
224 new_repo.repo_name = repo_name_full
227 new_repo.repo_type = repo_type
225 new_repo.repo_type = repo_type
228 new_repo.user = owner
226 new_repo.user = owner
229 new_repo.group = repos_group
227 new_repo.group = repos_group
230 new_repo.description = description or repo_name
228 new_repo.description = description or repo_name
231 new_repo.private = private
229 new_repo.private = private
232 new_repo.clone_uri = clone_uri
230 new_repo.clone_uri = clone_uri
233 new_repo.landing_rev = landing_rev
231 new_repo.landing_rev = landing_rev
234
232
235 if fork_of:
233 if fork_of:
236 parent_repo = fork_of
234 parent_repo = fork_of
237 new_repo.fork = parent_repo
235 new_repo.fork = parent_repo
238
236
239 self.sa.add(new_repo)
237 self.sa.add(new_repo)
240
238
241 def _create_default_perms():
239 def _create_default_perms():
242 # create default permission
240 # create default permission
243 repo_to_perm = UserRepoToPerm()
241 repo_to_perm = UserRepoToPerm()
244 default = 'repository.read'
242 default = 'repository.read'
245 for p in User.get_by_username('default').user_perms:
243 for p in User.get_by_username('default').user_perms:
246 if p.permission.permission_name.startswith('repository.'):
244 if p.permission.permission_name.startswith('repository.'):
247 default = p.permission.permission_name
245 default = p.permission.permission_name
248 break
246 break
249
247
250 default_perm = 'repository.none' if private else default
248 default_perm = 'repository.none' if private else default
251
249
252 repo_to_perm.permission_id = self.sa.query(Permission)\
250 repo_to_perm.permission_id = self.sa.query(Permission)\
253 .filter(Permission.permission_name == default_perm)\
251 .filter(Permission.permission_name == default_perm)\
254 .one().permission_id
252 .one().permission_id
255
253
256 repo_to_perm.repository = new_repo
254 repo_to_perm.repository = new_repo
257 repo_to_perm.user_id = User.get_by_username('default').user_id
255 repo_to_perm.user_id = User.get_by_username('default').user_id
258
256
259 self.sa.add(repo_to_perm)
257 self.sa.add(repo_to_perm)
260
258
261 if fork_of:
259 if fork_of:
262 if copy_fork_permissions:
260 if copy_fork_permissions:
263 repo = fork_of
261 repo = fork_of
264 user_perms = UserRepoToPerm.query()\
262 user_perms = UserRepoToPerm.query()\
265 .filter(UserRepoToPerm.repository == repo).all()
263 .filter(UserRepoToPerm.repository == repo).all()
266 group_perms = UsersGroupRepoToPerm.query()\
264 group_perms = UsersGroupRepoToPerm.query()\
267 .filter(UsersGroupRepoToPerm.repository == repo).all()
265 .filter(UsersGroupRepoToPerm.repository == repo).all()
268
266
269 for perm in user_perms:
267 for perm in user_perms:
270 UserRepoToPerm.create(perm.user, new_repo,
268 UserRepoToPerm.create(perm.user, new_repo,
271 perm.permission)
269 perm.permission)
272
270
273 for perm in group_perms:
271 for perm in group_perms:
274 UsersGroupRepoToPerm.create(perm.users_group, new_repo,
272 UsersGroupRepoToPerm.create(perm.users_group, new_repo,
275 perm.permission)
273 perm.permission)
276 else:
274 else:
277 _create_default_perms()
275 _create_default_perms()
278 else:
276 else:
279 _create_default_perms()
277 _create_default_perms()
280
278
281 if not just_db:
279 if not just_db:
282 self.__create_repo(repo_name, repo_type,
280 self.__create_repo(repo_name, repo_type,
283 repos_group,
281 repos_group,
284 clone_uri)
282 clone_uri)
285 log_create_repository(new_repo.get_dict(),
283 log_create_repository(new_repo.get_dict(),
286 created_by=owner.username)
284 created_by=owner.username)
287
285 else:
286 # install the githook if it's a git repo
287 if repo_type == 'git':
288 ScmModel().install_git_hook(repo=new_repo.scm_instance)
288 # now automatically start following this repository as owner
289 # now automatically start following this repository as owner
289 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
290 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
290 owner.user_id)
291 owner.user_id)
291 return new_repo
292 return new_repo
292 except:
293 except:
293 print traceback.format_exc()
294 log.error(traceback.format_exc())
294 log.error(traceback.format_exc())
295 raise
295 raise
296
296
297 def create(self, form_data, cur_user, just_db=False, fork=None):
297 def create(self, form_data, cur_user, just_db=False, fork=None):
298
298
299 repo_name = form_data['repo_name_full']
299 repo_name = form_data['repo_name_full']
300 repo_type = form_data['repo_type']
300 repo_type = form_data['repo_type']
301 description = form_data['description']
301 description = form_data['description']
302 owner = cur_user
302 owner = cur_user
303 private = form_data['private']
303 private = form_data['private']
304 clone_uri = form_data.get('clone_uri')
304 clone_uri = form_data.get('clone_uri')
305 repos_group = form_data['repo_group']
305 repos_group = form_data['repo_group']
306 landing_rev = form_data['landing_rev']
306 landing_rev = form_data['landing_rev']
307 copy_fork_permissions = form_data.get('copy_permissions')
307 copy_fork_permissions = form_data.get('copy_permissions')
308 fork_of = form_data.get('fork_parent_id')
308 fork_of = form_data.get('fork_parent_id')
309 return self.create_repo(
309 return self.create_repo(
310 repo_name, repo_type, description, owner, private, clone_uri,
310 repo_name, repo_type, description, owner, private, clone_uri,
311 repos_group, landing_rev, just_db, fork_of, copy_fork_permissions
311 repos_group, landing_rev, just_db, fork_of, copy_fork_permissions
312 )
312 )
313
313
314 def create_fork(self, form_data, cur_user):
314 def create_fork(self, form_data, cur_user):
315 """
315 """
316 Simple wrapper into executing celery task for fork creation
316 Simple wrapper into executing celery task for fork creation
317
317
318 :param form_data:
318 :param form_data:
319 :param cur_user:
319 :param cur_user:
320 """
320 """
321 from rhodecode.lib.celerylib import tasks, run_task
321 from rhodecode.lib.celerylib import tasks, run_task
322 run_task(tasks.create_repo_fork, form_data, cur_user)
322 run_task(tasks.create_repo_fork, form_data, cur_user)
323
323
324 def delete(self, repo):
324 def delete(self, repo):
325 repo = self._get_repo(repo)
325 repo = self._get_repo(repo)
326 if repo:
326 if repo:
327 try:
327 try:
328 self.sa.delete(repo)
328 self.sa.delete(repo)
329 self.__delete_repo(repo)
329 self.__delete_repo(repo)
330 except:
330 except:
331 log.error(traceback.format_exc())
331 log.error(traceback.format_exc())
332 raise
332 raise
333
333
334 def grant_user_permission(self, repo, user, perm):
334 def grant_user_permission(self, repo, user, perm):
335 """
335 """
336 Grant permission for user on given repository, or update existing one
336 Grant permission for user on given repository, or update existing one
337 if found
337 if found
338
338
339 :param repo: Instance of Repository, repository_id, or repository name
339 :param repo: Instance of Repository, repository_id, or repository name
340 :param user: Instance of User, user_id or username
340 :param user: Instance of User, user_id or username
341 :param perm: Instance of Permission, or permission_name
341 :param perm: Instance of Permission, or permission_name
342 """
342 """
343 user = self._get_user(user)
343 user = self._get_user(user)
344 repo = self._get_repo(repo)
344 repo = self._get_repo(repo)
345 permission = self._get_perm(perm)
345 permission = self._get_perm(perm)
346
346
347 # check if we have that permission already
347 # check if we have that permission already
348 obj = self.sa.query(UserRepoToPerm)\
348 obj = self.sa.query(UserRepoToPerm)\
349 .filter(UserRepoToPerm.user == user)\
349 .filter(UserRepoToPerm.user == user)\
350 .filter(UserRepoToPerm.repository == repo)\
350 .filter(UserRepoToPerm.repository == repo)\
351 .scalar()
351 .scalar()
352 if obj is None:
352 if obj is None:
353 # create new !
353 # create new !
354 obj = UserRepoToPerm()
354 obj = UserRepoToPerm()
355 obj.repository = repo
355 obj.repository = repo
356 obj.user = user
356 obj.user = user
357 obj.permission = permission
357 obj.permission = permission
358 self.sa.add(obj)
358 self.sa.add(obj)
359
359
360 def revoke_user_permission(self, repo, user):
360 def revoke_user_permission(self, repo, user):
361 """
361 """
362 Revoke permission for user on given repository
362 Revoke permission for user on given repository
363
363
364 :param repo: Instance of Repository, repository_id, or repository name
364 :param repo: Instance of Repository, repository_id, or repository name
365 :param user: Instance of User, user_id or username
365 :param user: Instance of User, user_id or username
366 """
366 """
367
367
368 user = self._get_user(user)
368 user = self._get_user(user)
369 repo = self._get_repo(repo)
369 repo = self._get_repo(repo)
370
370
371 obj = self.sa.query(UserRepoToPerm)\
371 obj = self.sa.query(UserRepoToPerm)\
372 .filter(UserRepoToPerm.repository == repo)\
372 .filter(UserRepoToPerm.repository == repo)\
373 .filter(UserRepoToPerm.user == user)\
373 .filter(UserRepoToPerm.user == user)\
374 .one()
374 .one()
375 self.sa.delete(obj)
375 self.sa.delete(obj)
376
376
377 def grant_users_group_permission(self, repo, group_name, perm):
377 def grant_users_group_permission(self, repo, group_name, perm):
378 """
378 """
379 Grant permission for users group on given repository, or update
379 Grant permission for users group on given repository, or update
380 existing one if found
380 existing one if found
381
381
382 :param repo: Instance of Repository, repository_id, or repository name
382 :param repo: Instance of Repository, repository_id, or repository name
383 :param group_name: Instance of UserGroup, users_group_id,
383 :param group_name: Instance of UserGroup, users_group_id,
384 or users group name
384 or users group name
385 :param perm: Instance of Permission, or permission_name
385 :param perm: Instance of Permission, or permission_name
386 """
386 """
387 repo = self._get_repo(repo)
387 repo = self._get_repo(repo)
388 group_name = self.__get_users_group(group_name)
388 group_name = self.__get_users_group(group_name)
389 permission = self._get_perm(perm)
389 permission = self._get_perm(perm)
390
390
391 # check if we have that permission already
391 # check if we have that permission already
392 obj = self.sa.query(UsersGroupRepoToPerm)\
392 obj = self.sa.query(UsersGroupRepoToPerm)\
393 .filter(UsersGroupRepoToPerm.users_group == group_name)\
393 .filter(UsersGroupRepoToPerm.users_group == group_name)\
394 .filter(UsersGroupRepoToPerm.repository == repo)\
394 .filter(UsersGroupRepoToPerm.repository == repo)\
395 .scalar()
395 .scalar()
396
396
397 if obj is None:
397 if obj is None:
398 # create new
398 # create new
399 obj = UsersGroupRepoToPerm()
399 obj = UsersGroupRepoToPerm()
400
400
401 obj.repository = repo
401 obj.repository = repo
402 obj.users_group = group_name
402 obj.users_group = group_name
403 obj.permission = permission
403 obj.permission = permission
404 self.sa.add(obj)
404 self.sa.add(obj)
405
405
406 def revoke_users_group_permission(self, repo, group_name):
406 def revoke_users_group_permission(self, repo, group_name):
407 """
407 """
408 Revoke permission for users group on given repository
408 Revoke permission for users group on given repository
409
409
410 :param repo: Instance of Repository, repository_id, or repository name
410 :param repo: Instance of Repository, repository_id, or repository name
411 :param group_name: Instance of UserGroup, users_group_id,
411 :param group_name: Instance of UserGroup, users_group_id,
412 or users group name
412 or users group name
413 """
413 """
414 repo = self._get_repo(repo)
414 repo = self._get_repo(repo)
415 group_name = self.__get_users_group(group_name)
415 group_name = self.__get_users_group(group_name)
416
416
417 obj = self.sa.query(UsersGroupRepoToPerm)\
417 obj = self.sa.query(UsersGroupRepoToPerm)\
418 .filter(UsersGroupRepoToPerm.repository == repo)\
418 .filter(UsersGroupRepoToPerm.repository == repo)\
419 .filter(UsersGroupRepoToPerm.users_group == group_name)\
419 .filter(UsersGroupRepoToPerm.users_group == group_name)\
420 .one()
420 .one()
421 self.sa.delete(obj)
421 self.sa.delete(obj)
422
422
423 def delete_stats(self, repo_name):
423 def delete_stats(self, repo_name):
424 """
424 """
425 removes stats for given repo
425 removes stats for given repo
426
426
427 :param repo_name:
427 :param repo_name:
428 """
428 """
429 try:
429 try:
430 obj = self.sa.query(Statistics)\
430 obj = self.sa.query(Statistics)\
431 .filter(Statistics.repository ==
431 .filter(Statistics.repository ==
432 self.get_by_repo_name(repo_name))\
432 self.get_by_repo_name(repo_name))\
433 .one()
433 .one()
434 self.sa.delete(obj)
434 self.sa.delete(obj)
435 except:
435 except:
436 log.error(traceback.format_exc())
436 log.error(traceback.format_exc())
437 raise
437 raise
438
438
439 def __create_repo(self, repo_name, alias, parent, clone_uri=False):
439 def __create_repo(self, repo_name, alias, parent, clone_uri=False):
440 """
440 """
441 makes repository on filesystem. It's group aware means it'll create
441 makes repository on filesystem. It's group aware means it'll create
442 a repository within a group, and alter the paths accordingly of
442 a repository within a group, and alter the paths accordingly of
443 group location
443 group location
444
444
445 :param repo_name:
445 :param repo_name:
446 :param alias:
446 :param alias:
447 :param parent_id:
447 :param parent_id:
448 :param clone_uri:
448 :param clone_uri:
449 """
449 """
450 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
450 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
451 from rhodecode.model.scm import ScmModel
451
452
452 if parent:
453 if parent:
453 new_parent_path = os.sep.join(parent.full_path_splitted)
454 new_parent_path = os.sep.join(parent.full_path_splitted)
454 else:
455 else:
455 new_parent_path = ''
456 new_parent_path = ''
456
457
457 # we need to make it str for mercurial
458 # we need to make it str for mercurial
458 repo_path = os.path.join(*map(lambda x: safe_str(x),
459 repo_path = os.path.join(*map(lambda x: safe_str(x),
459 [self.repos_path, new_parent_path, repo_name]))
460 [self.repos_path, new_parent_path, repo_name]))
460
461
461 # check if this path is not a repository
462 # check if this path is not a repository
462 if is_valid_repo(repo_path, self.repos_path):
463 if is_valid_repo(repo_path, self.repos_path):
463 raise Exception('This path %s is a valid repository' % repo_path)
464 raise Exception('This path %s is a valid repository' % repo_path)
464
465
465 # check if this path is a group
466 # check if this path is a group
466 if is_valid_repos_group(repo_path, self.repos_path):
467 if is_valid_repos_group(repo_path, self.repos_path):
467 raise Exception('This path %s is a valid group' % repo_path)
468 raise Exception('This path %s is a valid group' % repo_path)
468
469
469 log.info('creating repo %s in %s @ %s' % (
470 log.info('creating repo %s in %s @ %s' % (
470 repo_name, safe_unicode(repo_path), clone_uri
471 repo_name, safe_unicode(repo_path), clone_uri
471 )
472 )
472 )
473 )
473 backend = get_backend(alias)
474 backend = get_backend(alias)
474 if alias == 'hg':
475 if alias == 'hg':
475 backend(repo_path, create=True, src_url=clone_uri)
476 backend(repo_path, create=True, src_url=clone_uri)
476 elif alias == 'git':
477 elif alias == 'git':
477 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
478 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
478 # add rhodecode hook into this repo
479 # add rhodecode hook into this repo
479
480 ScmModel().install_git_hook(repo=r)
480 loc = jn(r.path, 'hooks')
481 if not r.bare:
482 loc = jn(r.path, '.git', 'hooks')
483 if not os.path.isdir(loc):
484 os.makedirs(loc)
485
486 tmpl = pkg_resources.resource_string(
487 'rhodecode', jn('config', 'post_receive_tmpl.py')
488 )
489 _hook_file = jn(loc, 'post-receive')
490 with open(_hook_file, 'wb') as f:
491 f.write(tmpl)
492 os.chmod(_hook_file, 0755)
493
494 else:
481 else:
495 raise Exception('Undefined alias %s' % alias)
482 raise Exception('Undefined alias %s' % alias)
496
483
497 def __rename_repo(self, old, new):
484 def __rename_repo(self, old, new):
498 """
485 """
499 renames repository on filesystem
486 renames repository on filesystem
500
487
501 :param old: old name
488 :param old: old name
502 :param new: new name
489 :param new: new name
503 """
490 """
504 log.info('renaming repo from %s to %s' % (old, new))
491 log.info('renaming repo from %s to %s' % (old, new))
505
492
506 old_path = os.path.join(self.repos_path, old)
493 old_path = os.path.join(self.repos_path, old)
507 new_path = os.path.join(self.repos_path, new)
494 new_path = os.path.join(self.repos_path, new)
508 if os.path.isdir(new_path):
495 if os.path.isdir(new_path):
509 raise Exception(
496 raise Exception(
510 'Was trying to rename to already existing dir %s' % new_path
497 'Was trying to rename to already existing dir %s' % new_path
511 )
498 )
512 shutil.move(old_path, new_path)
499 shutil.move(old_path, new_path)
513
500
514 def __delete_repo(self, repo):
501 def __delete_repo(self, repo):
515 """
502 """
516 removes repo from filesystem, the removal is acctually made by
503 removes repo from filesystem, the removal is acctually made by
517 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
504 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
518 repository is no longer valid for rhodecode, can be undeleted later on
505 repository is no longer valid for rhodecode, can be undeleted later on
519 by reverting the renames on this repository
506 by reverting the renames on this repository
520
507
521 :param repo: repo object
508 :param repo: repo object
522 """
509 """
523 rm_path = os.path.join(self.repos_path, repo.repo_name)
510 rm_path = os.path.join(self.repos_path, repo.repo_name)
524 log.info("Removing %s" % (rm_path))
511 log.info("Removing %s" % (rm_path))
525 # disable hg/git internal that it doesn't get detected as repo
512 # disable hg/git internal that it doesn't get detected as repo
526 alias = repo.repo_type
513 alias = repo.repo_type
527
514
528 bare = getattr(repo.scm_instance, 'bare', False)
515 bare = getattr(repo.scm_instance, 'bare', False)
529
516
530 if not bare:
517 if not bare:
531 # skip this for bare git repos
518 # skip this for bare git repos
532 shutil.move(os.path.join(rm_path, '.%s' % alias),
519 shutil.move(os.path.join(rm_path, '.%s' % alias),
533 os.path.join(rm_path, 'rm__.%s' % alias))
520 os.path.join(rm_path, 'rm__.%s' % alias))
534 # disable repo
521 # disable repo
535 _now = datetime.now()
522 _now = datetime.now()
536 _ms = str(_now.microsecond).rjust(6, '0')
523 _ms = str(_now.microsecond).rjust(6, '0')
537 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
524 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
538 repo.repo_name)
525 repo.repo_name)
539 shutil.move(rm_path, os.path.join(self.repos_path, _d))
526 shutil.move(rm_path, os.path.join(self.repos_path, _d))
@@ -1,547 +1,597 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 import os
25 import os
26 import re
26 import time
27 import time
27 import traceback
28 import traceback
28 import logging
29 import logging
29 import cStringIO
30 import cStringIO
31 import pkg_resources
32 from os.path import dirname as dn, join as jn
30
33
31 from sqlalchemy import func
34 from sqlalchemy import func
32 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
33
36
37 import rhodecode
34 from rhodecode.lib.vcs import get_backend
38 from rhodecode.lib.vcs import get_backend
35 from rhodecode.lib.vcs.exceptions import RepositoryError
39 from rhodecode.lib.vcs.exceptions import RepositoryError
36 from rhodecode.lib.vcs.utils.lazy import LazyProperty
40 from rhodecode.lib.vcs.utils.lazy import LazyProperty
37 from rhodecode.lib.vcs.nodes import FileNode
41 from rhodecode.lib.vcs.nodes import FileNode
38
42
39 from rhodecode import BACKENDS
43 from rhodecode import BACKENDS
40 from rhodecode.lib import helpers as h
44 from rhodecode.lib import helpers as h
41 from rhodecode.lib.utils2 import safe_str, safe_unicode
45 from rhodecode.lib.utils2 import safe_str, safe_unicode
42 from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
46 from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
43 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
47 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
44 action_logger, EmptyChangeset, REMOVED_REPO_PAT
48 action_logger, EmptyChangeset, REMOVED_REPO_PAT
45 from rhodecode.model import BaseModel
49 from rhodecode.model import BaseModel
46 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
50 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
47 UserFollowing, UserLog, User, RepoGroup, PullRequest
51 UserFollowing, UserLog, User, RepoGroup, PullRequest
48
52
49 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
50
54
51
55
52 class UserTemp(object):
56 class UserTemp(object):
53 def __init__(self, user_id):
57 def __init__(self, user_id):
54 self.user_id = user_id
58 self.user_id = user_id
55
59
56 def __repr__(self):
60 def __repr__(self):
57 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
61 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
58
62
59
63
60 class RepoTemp(object):
64 class RepoTemp(object):
61 def __init__(self, repo_id):
65 def __init__(self, repo_id):
62 self.repo_id = repo_id
66 self.repo_id = repo_id
63
67
64 def __repr__(self):
68 def __repr__(self):
65 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
69 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
66
70
67
71
68 class CachedRepoList(object):
72 class CachedRepoList(object):
69 """
73 """
70 Cached repo list, uses in-memory cache after initialization, that is
74 Cached repo list, uses in-memory cache after initialization, that is
71 super fast
75 super fast
72 """
76 """
73
77
74 def __init__(self, db_repo_list, repos_path, order_by=None):
78 def __init__(self, db_repo_list, repos_path, order_by=None):
75 self.db_repo_list = db_repo_list
79 self.db_repo_list = db_repo_list
76 self.repos_path = repos_path
80 self.repos_path = repos_path
77 self.order_by = order_by
81 self.order_by = order_by
78 self.reversed = (order_by or '').startswith('-')
82 self.reversed = (order_by or '').startswith('-')
79
83
80 def __len__(self):
84 def __len__(self):
81 return len(self.db_repo_list)
85 return len(self.db_repo_list)
82
86
83 def __repr__(self):
87 def __repr__(self):
84 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
88 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
85
89
86 def __iter__(self):
90 def __iter__(self):
87 # pre-propagated cache_map to save executing select statements
91 # pre-propagated cache_map to save executing select statements
88 # for each repo
92 # for each repo
89 cache_map = CacheInvalidation.get_cache_map()
93 cache_map = CacheInvalidation.get_cache_map()
90
94
91 for dbr in self.db_repo_list:
95 for dbr in self.db_repo_list:
92 scmr = dbr.scm_instance_cached(cache_map)
96 scmr = dbr.scm_instance_cached(cache_map)
93 # check permission at this level
97 # check permission at this level
94 if not HasRepoPermissionAny(
98 if not HasRepoPermissionAny(
95 'repository.read', 'repository.write', 'repository.admin'
99 'repository.read', 'repository.write', 'repository.admin'
96 )(dbr.repo_name, 'get repo check'):
100 )(dbr.repo_name, 'get repo check'):
97 continue
101 continue
98
102
99 if scmr is None:
103 if scmr is None:
100 log.error(
104 log.error(
101 '%s this repository is present in database but it '
105 '%s this repository is present in database but it '
102 'cannot be created as an scm instance' % dbr.repo_name
106 'cannot be created as an scm instance' % dbr.repo_name
103 )
107 )
104 continue
108 continue
105
109
106 last_change = scmr.last_change
110 last_change = scmr.last_change
107 tip = h.get_changeset_safe(scmr, 'tip')
111 tip = h.get_changeset_safe(scmr, 'tip')
108
112
109 tmp_d = {}
113 tmp_d = {}
110 tmp_d['name'] = dbr.repo_name
114 tmp_d['name'] = dbr.repo_name
111 tmp_d['name_sort'] = tmp_d['name'].lower()
115 tmp_d['name_sort'] = tmp_d['name'].lower()
112 tmp_d['description'] = dbr.description
116 tmp_d['description'] = dbr.description
113 tmp_d['description_sort'] = tmp_d['description'].lower()
117 tmp_d['description_sort'] = tmp_d['description'].lower()
114 tmp_d['last_change'] = last_change
118 tmp_d['last_change'] = last_change
115 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
119 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
116 tmp_d['tip'] = tip.raw_id
120 tmp_d['tip'] = tip.raw_id
117 tmp_d['tip_sort'] = tip.revision
121 tmp_d['tip_sort'] = tip.revision
118 tmp_d['rev'] = tip.revision
122 tmp_d['rev'] = tip.revision
119 tmp_d['contact'] = dbr.user.full_contact
123 tmp_d['contact'] = dbr.user.full_contact
120 tmp_d['contact_sort'] = tmp_d['contact']
124 tmp_d['contact_sort'] = tmp_d['contact']
121 tmp_d['owner_sort'] = tmp_d['contact']
125 tmp_d['owner_sort'] = tmp_d['contact']
122 tmp_d['repo_archives'] = list(scmr._get_archives())
126 tmp_d['repo_archives'] = list(scmr._get_archives())
123 tmp_d['last_msg'] = tip.message
127 tmp_d['last_msg'] = tip.message
124 tmp_d['author'] = tip.author
128 tmp_d['author'] = tip.author
125 tmp_d['dbrepo'] = dbr.get_dict()
129 tmp_d['dbrepo'] = dbr.get_dict()
126 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
130 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
127 yield tmp_d
131 yield tmp_d
128
132
129
133
130 class SimpleCachedRepoList(CachedRepoList):
134 class SimpleCachedRepoList(CachedRepoList):
131 """
135 """
132 Lighter version of CachedRepoList without the scm initialisation
136 Lighter version of CachedRepoList without the scm initialisation
133 """
137 """
134
138
135 def __iter__(self):
139 def __iter__(self):
136 for dbr in self.db_repo_list:
140 for dbr in self.db_repo_list:
137 # check permission at this level
141 # check permission at this level
138 if not HasRepoPermissionAny(
142 if not HasRepoPermissionAny(
139 'repository.read', 'repository.write', 'repository.admin'
143 'repository.read', 'repository.write', 'repository.admin'
140 )(dbr.repo_name, 'get repo check'):
144 )(dbr.repo_name, 'get repo check'):
141 continue
145 continue
142
146
143 tmp_d = {}
147 tmp_d = {}
144 tmp_d['name'] = dbr.repo_name
148 tmp_d['name'] = dbr.repo_name
145 tmp_d['name_sort'] = tmp_d['name'].lower()
149 tmp_d['name_sort'] = tmp_d['name'].lower()
146 tmp_d['description'] = dbr.description
150 tmp_d['description'] = dbr.description
147 tmp_d['description_sort'] = tmp_d['description'].lower()
151 tmp_d['description_sort'] = tmp_d['description'].lower()
148 tmp_d['dbrepo'] = dbr.get_dict()
152 tmp_d['dbrepo'] = dbr.get_dict()
149 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
153 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
150 yield tmp_d
154 yield tmp_d
151
155
152
156
153 class GroupList(object):
157 class GroupList(object):
154
158
155 def __init__(self, db_repo_group_list):
159 def __init__(self, db_repo_group_list):
156 self.db_repo_group_list = db_repo_group_list
160 self.db_repo_group_list = db_repo_group_list
157
161
158 def __len__(self):
162 def __len__(self):
159 return len(self.db_repo_group_list)
163 return len(self.db_repo_group_list)
160
164
161 def __repr__(self):
165 def __repr__(self):
162 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
166 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
163
167
164 def __iter__(self):
168 def __iter__(self):
165 for dbgr in self.db_repo_group_list:
169 for dbgr in self.db_repo_group_list:
166 # check permission at this level
170 # check permission at this level
167 if not HasReposGroupPermissionAny(
171 if not HasReposGroupPermissionAny(
168 'group.read', 'group.write', 'group.admin'
172 'group.read', 'group.write', 'group.admin'
169 )(dbgr.group_name, 'get group repo check'):
173 )(dbgr.group_name, 'get group repo check'):
170 continue
174 continue
171
175
172 yield dbgr
176 yield dbgr
173
177
174
178
175 class ScmModel(BaseModel):
179 class ScmModel(BaseModel):
176 """
180 """
177 Generic Scm Model
181 Generic Scm Model
178 """
182 """
179
183
180 def __get_repo(self, instance):
184 def __get_repo(self, instance):
181 cls = Repository
185 cls = Repository
182 if isinstance(instance, cls):
186 if isinstance(instance, cls):
183 return instance
187 return instance
184 elif isinstance(instance, int) or str(instance).isdigit():
188 elif isinstance(instance, int) or str(instance).isdigit():
185 return cls.get(instance)
189 return cls.get(instance)
186 elif isinstance(instance, basestring):
190 elif isinstance(instance, basestring):
187 return cls.get_by_repo_name(instance)
191 return cls.get_by_repo_name(instance)
188 elif instance:
192 elif instance:
189 raise Exception('given object must be int, basestr or Instance'
193 raise Exception('given object must be int, basestr or Instance'
190 ' of %s got %s' % (type(cls), type(instance)))
194 ' of %s got %s' % (type(cls), type(instance)))
191
195
192 @LazyProperty
196 @LazyProperty
193 def repos_path(self):
197 def repos_path(self):
194 """
198 """
195 Get's the repositories root path from database
199 Get's the repositories root path from database
196 """
200 """
197
201
198 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
202 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
199
203
200 return q.ui_value
204 return q.ui_value
201
205
202 def repo_scan(self, repos_path=None):
206 def repo_scan(self, repos_path=None):
203 """
207 """
204 Listing of repositories in given path. This path should not be a
208 Listing of repositories in given path. This path should not be a
205 repository itself. Return a dictionary of repository objects
209 repository itself. Return a dictionary of repository objects
206
210
207 :param repos_path: path to directory containing repositories
211 :param repos_path: path to directory containing repositories
208 """
212 """
209
213
210 if repos_path is None:
214 if repos_path is None:
211 repos_path = self.repos_path
215 repos_path = self.repos_path
212
216
213 log.info('scanning for repositories in %s' % repos_path)
217 log.info('scanning for repositories in %s' % repos_path)
214
218
215 baseui = make_ui('db')
219 baseui = make_ui('db')
216 repos = {}
220 repos = {}
217
221
218 for name, path in get_filesystem_repos(repos_path, recursive=True):
222 for name, path in get_filesystem_repos(repos_path, recursive=True):
219 # skip removed repos
223 # skip removed repos
220 if REMOVED_REPO_PAT.match(name):
224 if REMOVED_REPO_PAT.match(name):
221 continue
225 continue
222
226
223 # name need to be decomposed and put back together using the /
227 # name need to be decomposed and put back together using the /
224 # since this is internal storage separator for rhodecode
228 # since this is internal storage separator for rhodecode
225 name = Repository.url_sep().join(name.split(os.sep))
229 name = Repository.url_sep().join(name.split(os.sep))
226
230
227 try:
231 try:
228 if name in repos:
232 if name in repos:
229 raise RepositoryError('Duplicate repository name %s '
233 raise RepositoryError('Duplicate repository name %s '
230 'found in %s' % (name, path))
234 'found in %s' % (name, path))
231 else:
235 else:
232
236
233 klass = get_backend(path[0])
237 klass = get_backend(path[0])
234
238
235 if path[0] == 'hg' and path[0] in BACKENDS.keys():
239 if path[0] == 'hg' and path[0] in BACKENDS.keys():
236 repos[name] = klass(safe_str(path[1]), baseui=baseui)
240 repos[name] = klass(safe_str(path[1]), baseui=baseui)
237
241
238 if path[0] == 'git' and path[0] in BACKENDS.keys():
242 if path[0] == 'git' and path[0] in BACKENDS.keys():
239 repos[name] = klass(path[1])
243 repos[name] = klass(path[1])
240 except OSError:
244 except OSError:
241 continue
245 continue
242
246
243 return repos
247 return repos
244
248
245 def get_repos(self, all_repos=None, sort_key=None, simple=False):
249 def get_repos(self, all_repos=None, sort_key=None, simple=False):
246 """
250 """
247 Get all repos from db and for each repo create it's
251 Get all repos from db and for each repo create it's
248 backend instance and fill that backed with information from database
252 backend instance and fill that backed with information from database
249
253
250 :param all_repos: list of repository names as strings
254 :param all_repos: list of repository names as strings
251 give specific repositories list, good for filtering
255 give specific repositories list, good for filtering
252
256
253 :param sort_key: initial sorting of repos
257 :param sort_key: initial sorting of repos
254 :param simple: use SimpleCachedList - one without the SCM info
258 :param simple: use SimpleCachedList - one without the SCM info
255 """
259 """
256 if all_repos is None:
260 if all_repos is None:
257 all_repos = self.sa.query(Repository)\
261 all_repos = self.sa.query(Repository)\
258 .filter(Repository.group_id == None)\
262 .filter(Repository.group_id == None)\
259 .order_by(func.lower(Repository.repo_name)).all()
263 .order_by(func.lower(Repository.repo_name)).all()
260 if simple:
264 if simple:
261 repo_iter = SimpleCachedRepoList(all_repos,
265 repo_iter = SimpleCachedRepoList(all_repos,
262 repos_path=self.repos_path,
266 repos_path=self.repos_path,
263 order_by=sort_key)
267 order_by=sort_key)
264 else:
268 else:
265 repo_iter = CachedRepoList(all_repos,
269 repo_iter = CachedRepoList(all_repos,
266 repos_path=self.repos_path,
270 repos_path=self.repos_path,
267 order_by=sort_key)
271 order_by=sort_key)
268
272
269 return repo_iter
273 return repo_iter
270
274
271 def get_repos_groups(self, all_groups=None):
275 def get_repos_groups(self, all_groups=None):
272 if all_groups is None:
276 if all_groups is None:
273 all_groups = RepoGroup.query()\
277 all_groups = RepoGroup.query()\
274 .filter(RepoGroup.group_parent_id == None).all()
278 .filter(RepoGroup.group_parent_id == None).all()
275 group_iter = GroupList(all_groups)
279 group_iter = GroupList(all_groups)
276
280
277 return group_iter
281 return group_iter
278
282
279 def mark_for_invalidation(self, repo_name):
283 def mark_for_invalidation(self, repo_name):
280 """
284 """
281 Puts cache invalidation task into db for
285 Puts cache invalidation task into db for
282 further global cache invalidation
286 further global cache invalidation
283
287
284 :param repo_name: this repo that should invalidation take place
288 :param repo_name: this repo that should invalidation take place
285 """
289 """
286 CacheInvalidation.set_invalidate(repo_name)
290 CacheInvalidation.set_invalidate(repo_name)
287
291
288 def toggle_following_repo(self, follow_repo_id, user_id):
292 def toggle_following_repo(self, follow_repo_id, user_id):
289
293
290 f = self.sa.query(UserFollowing)\
294 f = self.sa.query(UserFollowing)\
291 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
295 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
292 .filter(UserFollowing.user_id == user_id).scalar()
296 .filter(UserFollowing.user_id == user_id).scalar()
293
297
294 if f is not None:
298 if f is not None:
295 try:
299 try:
296 self.sa.delete(f)
300 self.sa.delete(f)
297 action_logger(UserTemp(user_id),
301 action_logger(UserTemp(user_id),
298 'stopped_following_repo',
302 'stopped_following_repo',
299 RepoTemp(follow_repo_id))
303 RepoTemp(follow_repo_id))
300 return
304 return
301 except:
305 except:
302 log.error(traceback.format_exc())
306 log.error(traceback.format_exc())
303 raise
307 raise
304
308
305 try:
309 try:
306 f = UserFollowing()
310 f = UserFollowing()
307 f.user_id = user_id
311 f.user_id = user_id
308 f.follows_repo_id = follow_repo_id
312 f.follows_repo_id = follow_repo_id
309 self.sa.add(f)
313 self.sa.add(f)
310
314
311 action_logger(UserTemp(user_id),
315 action_logger(UserTemp(user_id),
312 'started_following_repo',
316 'started_following_repo',
313 RepoTemp(follow_repo_id))
317 RepoTemp(follow_repo_id))
314 except:
318 except:
315 log.error(traceback.format_exc())
319 log.error(traceback.format_exc())
316 raise
320 raise
317
321
318 def toggle_following_user(self, follow_user_id, user_id):
322 def toggle_following_user(self, follow_user_id, user_id):
319 f = self.sa.query(UserFollowing)\
323 f = self.sa.query(UserFollowing)\
320 .filter(UserFollowing.follows_user_id == follow_user_id)\
324 .filter(UserFollowing.follows_user_id == follow_user_id)\
321 .filter(UserFollowing.user_id == user_id).scalar()
325 .filter(UserFollowing.user_id == user_id).scalar()
322
326
323 if f is not None:
327 if f is not None:
324 try:
328 try:
325 self.sa.delete(f)
329 self.sa.delete(f)
326 return
330 return
327 except:
331 except:
328 log.error(traceback.format_exc())
332 log.error(traceback.format_exc())
329 raise
333 raise
330
334
331 try:
335 try:
332 f = UserFollowing()
336 f = UserFollowing()
333 f.user_id = user_id
337 f.user_id = user_id
334 f.follows_user_id = follow_user_id
338 f.follows_user_id = follow_user_id
335 self.sa.add(f)
339 self.sa.add(f)
336 except:
340 except:
337 log.error(traceback.format_exc())
341 log.error(traceback.format_exc())
338 raise
342 raise
339
343
340 def is_following_repo(self, repo_name, user_id, cache=False):
344 def is_following_repo(self, repo_name, user_id, cache=False):
341 r = self.sa.query(Repository)\
345 r = self.sa.query(Repository)\
342 .filter(Repository.repo_name == repo_name).scalar()
346 .filter(Repository.repo_name == repo_name).scalar()
343
347
344 f = self.sa.query(UserFollowing)\
348 f = self.sa.query(UserFollowing)\
345 .filter(UserFollowing.follows_repository == r)\
349 .filter(UserFollowing.follows_repository == r)\
346 .filter(UserFollowing.user_id == user_id).scalar()
350 .filter(UserFollowing.user_id == user_id).scalar()
347
351
348 return f is not None
352 return f is not None
349
353
350 def is_following_user(self, username, user_id, cache=False):
354 def is_following_user(self, username, user_id, cache=False):
351 u = User.get_by_username(username)
355 u = User.get_by_username(username)
352
356
353 f = self.sa.query(UserFollowing)\
357 f = self.sa.query(UserFollowing)\
354 .filter(UserFollowing.follows_user == u)\
358 .filter(UserFollowing.follows_user == u)\
355 .filter(UserFollowing.user_id == user_id).scalar()
359 .filter(UserFollowing.user_id == user_id).scalar()
356
360
357 return f is not None
361 return f is not None
358
362
359 def get_followers(self, repo):
363 def get_followers(self, repo):
360 repo = self._get_repo(repo)
364 repo = self._get_repo(repo)
361
365
362 return self.sa.query(UserFollowing)\
366 return self.sa.query(UserFollowing)\
363 .filter(UserFollowing.follows_repository == repo).count()
367 .filter(UserFollowing.follows_repository == repo).count()
364
368
365 def get_forks(self, repo):
369 def get_forks(self, repo):
366 repo = self._get_repo(repo)
370 repo = self._get_repo(repo)
367 return self.sa.query(Repository)\
371 return self.sa.query(Repository)\
368 .filter(Repository.fork == repo).count()
372 .filter(Repository.fork == repo).count()
369
373
370 def get_pull_requests(self, repo):
374 def get_pull_requests(self, repo):
371 repo = self._get_repo(repo)
375 repo = self._get_repo(repo)
372 return self.sa.query(PullRequest)\
376 return self.sa.query(PullRequest)\
373 .filter(PullRequest.other_repo == repo).count()
377 .filter(PullRequest.other_repo == repo).count()
374
378
375 def mark_as_fork(self, repo, fork, user):
379 def mark_as_fork(self, repo, fork, user):
376 repo = self.__get_repo(repo)
380 repo = self.__get_repo(repo)
377 fork = self.__get_repo(fork)
381 fork = self.__get_repo(fork)
378 repo.fork = fork
382 repo.fork = fork
379 self.sa.add(repo)
383 self.sa.add(repo)
380 return repo
384 return repo
381
385
382 def pull_changes(self, repo, username):
386 def pull_changes(self, repo, username):
383 dbrepo = self.__get_repo(repo)
387 dbrepo = self.__get_repo(repo)
384 clone_uri = dbrepo.clone_uri
388 clone_uri = dbrepo.clone_uri
385 if not clone_uri:
389 if not clone_uri:
386 raise Exception("This repository doesn't have a clone uri")
390 raise Exception("This repository doesn't have a clone uri")
387
391
388 repo = dbrepo.scm_instance
392 repo = dbrepo.scm_instance
389 try:
393 try:
390 extras = {
394 extras = {
391 'ip': '',
395 'ip': '',
392 'username': username,
396 'username': username,
393 'action': 'push_remote',
397 'action': 'push_remote',
394 'repository': dbrepo.repo_name,
398 'repository': dbrepo.repo_name,
395 'scm': repo.alias,
399 'scm': repo.alias,
396 }
400 }
397 Repository.inject_ui(repo, extras=extras)
401 Repository.inject_ui(repo, extras=extras)
398
402
399 if repo.alias == 'git':
403 if repo.alias == 'git':
400 repo.fetch(clone_uri)
404 repo.fetch(clone_uri)
401 else:
405 else:
402 repo.pull(clone_uri)
406 repo.pull(clone_uri)
403 self.mark_for_invalidation(dbrepo.repo_name)
407 self.mark_for_invalidation(dbrepo.repo_name)
404 except:
408 except:
405 log.error(traceback.format_exc())
409 log.error(traceback.format_exc())
406 raise
410 raise
407
411
408 def commit_change(self, repo, repo_name, cs, user, author, message,
412 def commit_change(self, repo, repo_name, cs, user, author, message,
409 content, f_path):
413 content, f_path):
410
414
411 if repo.alias == 'hg':
415 if repo.alias == 'hg':
412 from rhodecode.lib.vcs.backends.hg import \
416 from rhodecode.lib.vcs.backends.hg import \
413 MercurialInMemoryChangeset as IMC
417 MercurialInMemoryChangeset as IMC
414 elif repo.alias == 'git':
418 elif repo.alias == 'git':
415 from rhodecode.lib.vcs.backends.git import \
419 from rhodecode.lib.vcs.backends.git import \
416 GitInMemoryChangeset as IMC
420 GitInMemoryChangeset as IMC
417
421
418 # decoding here will force that we have proper encoded values
422 # decoding here will force that we have proper encoded values
419 # in any other case this will throw exceptions and deny commit
423 # in any other case this will throw exceptions and deny commit
420 content = safe_str(content)
424 content = safe_str(content)
421 path = safe_str(f_path)
425 path = safe_str(f_path)
422 # message and author needs to be unicode
426 # message and author needs to be unicode
423 # proper backend should then translate that into required type
427 # proper backend should then translate that into required type
424 message = safe_unicode(message)
428 message = safe_unicode(message)
425 author = safe_unicode(author)
429 author = safe_unicode(author)
426 m = IMC(repo)
430 m = IMC(repo)
427 m.change(FileNode(path, content))
431 m.change(FileNode(path, content))
428 tip = m.commit(message=message,
432 tip = m.commit(message=message,
429 author=author,
433 author=author,
430 parents=[cs], branch=cs.branch)
434 parents=[cs], branch=cs.branch)
431
435
432 new_cs = tip.short_id
436 new_cs = tip.short_id
433 action = 'push_local:%s' % new_cs
437 action = 'push_local:%s' % new_cs
434
438
435 action_logger(user, action, repo_name)
439 action_logger(user, action, repo_name)
436
440
437 self.mark_for_invalidation(repo_name)
441 self.mark_for_invalidation(repo_name)
438
442
439 def create_node(self, repo, repo_name, cs, user, author, message, content,
443 def create_node(self, repo, repo_name, cs, user, author, message, content,
440 f_path):
444 f_path):
441 if repo.alias == 'hg':
445 if repo.alias == 'hg':
442 from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC
446 from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC
443 elif repo.alias == 'git':
447 elif repo.alias == 'git':
444 from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC
448 from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC
445 # decoding here will force that we have proper encoded values
449 # decoding here will force that we have proper encoded values
446 # in any other case this will throw exceptions and deny commit
450 # in any other case this will throw exceptions and deny commit
447
451
448 if isinstance(content, (basestring,)):
452 if isinstance(content, (basestring,)):
449 content = safe_str(content)
453 content = safe_str(content)
450 elif isinstance(content, (file, cStringIO.OutputType,)):
454 elif isinstance(content, (file, cStringIO.OutputType,)):
451 content = content.read()
455 content = content.read()
452 else:
456 else:
453 raise Exception('Content is of unrecognized type %s' % (
457 raise Exception('Content is of unrecognized type %s' % (
454 type(content)
458 type(content)
455 ))
459 ))
456
460
457 message = safe_unicode(message)
461 message = safe_unicode(message)
458 author = safe_unicode(author)
462 author = safe_unicode(author)
459 path = safe_str(f_path)
463 path = safe_str(f_path)
460 m = IMC(repo)
464 m = IMC(repo)
461
465
462 if isinstance(cs, EmptyChangeset):
466 if isinstance(cs, EmptyChangeset):
463 # EmptyChangeset means we we're editing empty repository
467 # EmptyChangeset means we we're editing empty repository
464 parents = None
468 parents = None
465 else:
469 else:
466 parents = [cs]
470 parents = [cs]
467
471
468 m.add(FileNode(path, content=content))
472 m.add(FileNode(path, content=content))
469 tip = m.commit(message=message,
473 tip = m.commit(message=message,
470 author=author,
474 author=author,
471 parents=parents, branch=cs.branch)
475 parents=parents, branch=cs.branch)
472 new_cs = tip.short_id
476 new_cs = tip.short_id
473 action = 'push_local:%s' % new_cs
477 action = 'push_local:%s' % new_cs
474
478
475 action_logger(user, action, repo_name)
479 action_logger(user, action, repo_name)
476
480
477 self.mark_for_invalidation(repo_name)
481 self.mark_for_invalidation(repo_name)
478
482
479 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
483 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
480 """
484 """
481 recursive walk in root dir and return a set of all path in that dir
485 recursive walk in root dir and return a set of all path in that dir
482 based on repository walk function
486 based on repository walk function
483
487
484 :param repo_name: name of repository
488 :param repo_name: name of repository
485 :param revision: revision for which to list nodes
489 :param revision: revision for which to list nodes
486 :param root_path: root path to list
490 :param root_path: root path to list
487 :param flat: return as a list, if False returns a dict with decription
491 :param flat: return as a list, if False returns a dict with decription
488
492
489 """
493 """
490 _files = list()
494 _files = list()
491 _dirs = list()
495 _dirs = list()
492 try:
496 try:
493 _repo = self.__get_repo(repo_name)
497 _repo = self.__get_repo(repo_name)
494 changeset = _repo.scm_instance.get_changeset(revision)
498 changeset = _repo.scm_instance.get_changeset(revision)
495 root_path = root_path.lstrip('/')
499 root_path = root_path.lstrip('/')
496 for topnode, dirs, files in changeset.walk(root_path):
500 for topnode, dirs, files in changeset.walk(root_path):
497 for f in files:
501 for f in files:
498 _files.append(f.path if flat else {"name": f.path,
502 _files.append(f.path if flat else {"name": f.path,
499 "type": "file"})
503 "type": "file"})
500 for d in dirs:
504 for d in dirs:
501 _dirs.append(d.path if flat else {"name": d.path,
505 _dirs.append(d.path if flat else {"name": d.path,
502 "type": "dir"})
506 "type": "dir"})
503 except RepositoryError:
507 except RepositoryError:
504 log.debug(traceback.format_exc())
508 log.debug(traceback.format_exc())
505 raise
509 raise
506
510
507 return _dirs, _files
511 return _dirs, _files
508
512
509 def get_unread_journal(self):
513 def get_unread_journal(self):
510 return self.sa.query(UserLog).count()
514 return self.sa.query(UserLog).count()
511
515
512 def get_repo_landing_revs(self, repo=None):
516 def get_repo_landing_revs(self, repo=None):
513 """
517 """
514 Generates select option with tags branches and bookmarks (for hg only)
518 Generates select option with tags branches and bookmarks (for hg only)
515 grouped by type
519 grouped by type
516
520
517 :param repo:
521 :param repo:
518 :type repo:
522 :type repo:
519 """
523 """
520
524
521 hist_l = []
525 hist_l = []
522 choices = []
526 choices = []
523 repo = self.__get_repo(repo)
527 repo = self.__get_repo(repo)
524 hist_l.append(['tip', _('latest tip')])
528 hist_l.append(['tip', _('latest tip')])
525 choices.append('tip')
529 choices.append('tip')
526 if not repo:
530 if not repo:
527 return choices, hist_l
531 return choices, hist_l
528
532
529 repo = repo.scm_instance
533 repo = repo.scm_instance
530
534
531 branches_group = ([(k, k) for k, v in
535 branches_group = ([(k, k) for k, v in
532 repo.branches.iteritems()], _("Branches"))
536 repo.branches.iteritems()], _("Branches"))
533 hist_l.append(branches_group)
537 hist_l.append(branches_group)
534 choices.extend([x[0] for x in branches_group[0]])
538 choices.extend([x[0] for x in branches_group[0]])
535
539
536 if repo.alias == 'hg':
540 if repo.alias == 'hg':
537 bookmarks_group = ([(k, k) for k, v in
541 bookmarks_group = ([(k, k) for k, v in
538 repo.bookmarks.iteritems()], _("Bookmarks"))
542 repo.bookmarks.iteritems()], _("Bookmarks"))
539 hist_l.append(bookmarks_group)
543 hist_l.append(bookmarks_group)
540 choices.extend([x[0] for x in bookmarks_group[0]])
544 choices.extend([x[0] for x in bookmarks_group[0]])
541
545
542 tags_group = ([(k, k) for k, v in
546 tags_group = ([(k, k) for k, v in
543 repo.tags.iteritems()], _("Tags"))
547 repo.tags.iteritems()], _("Tags"))
544 hist_l.append(tags_group)
548 hist_l.append(tags_group)
545 choices.extend([x[0] for x in tags_group[0]])
549 choices.extend([x[0] for x in tags_group[0]])
546
550
547 return choices, hist_l
551 return choices, hist_l
552
553 def install_git_hook(self, repo, force_create=False):
554 """
555 Creates a rhodecode hook inside a git repository
556
557 :param repo: Instance of VCS repo
558 :param force_create: Create even if same name hook exists
559 """
560
561 loc = jn(repo.path, 'hooks')
562 if not repo.bare:
563 loc = jn(repo.path, '.git', 'hooks')
564 if not os.path.isdir(loc):
565 os.makedirs(loc)
566
567 tmpl = pkg_resources.resource_string(
568 'rhodecode', jn('config', 'post_receive_tmpl.py')
569 )
570
571 _hook_file = jn(loc, 'post-receive')
572 _rhodecode_hook = False
573 log.debug('Installing git hook in repo %s' % repo)
574 if os.path.exists(_hook_file):
575 # let's take a look at this hook, maybe it's rhodecode ?
576 log.debug('hook exists, checking if it is from rhodecode')
577 _HOOK_VER_PAT = re.compile(r'^RC_HOOK_VER')
578 with open(_hook_file, 'rb') as f:
579 data = f.read()
580 matches = re.compile(r'(?:%s)\s*=\s*(.*)'
581 % 'RC_HOOK_VER').search(data)
582 if matches:
583 try:
584 ver = matches.groups()[0]
585 log.debug('got %s it is rhodecode' % (ver))
586 _rhodecode_hook = True
587 except:
588 log.error(traceback.format_exc())
589
590 if _rhodecode_hook or force_create:
591 log.debug('writing hook file !')
592 with open(_hook_file, 'wb') as f:
593 tmpl = tmpl.replace('_TMPL_', rhodecode.__version__)
594 f.write(tmpl)
595 os.chmod(_hook_file, 0755)
596 else:
597 log.debug('skipping writing hook file') No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now