##// END OF EJS Templates
fixes issue #341, logger outputed invalid path name
marcink -
r1925:9d400b58 beta
parent child Browse files
Show More
@@ -1,425 +1,425 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.scm
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Scm model for RhodeCode
7 7
8 8 :created_on: Apr 9, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 import os
26 26 import time
27 27 import traceback
28 28 import logging
29 29 import cStringIO
30 30
31 31 from vcs import get_backend
32 32 from vcs.exceptions import RepositoryError
33 33 from vcs.utils.lazy import LazyProperty
34 34 from vcs.nodes import FileNode
35 35
36 36 from rhodecode import BACKENDS
37 37 from rhodecode.lib import helpers as h
38 38 from rhodecode.lib import safe_str
39 39 from rhodecode.lib.auth import HasRepoPermissionAny
40 40 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
41 41 action_logger, EmptyChangeset
42 42 from rhodecode.model import BaseModel
43 43 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
44 44 UserFollowing, UserLog, User
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48
49 49 class UserTemp(object):
50 50 def __init__(self, user_id):
51 51 self.user_id = user_id
52 52
53 53 def __repr__(self):
54 54 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
55 55
56 56
57 57 class RepoTemp(object):
58 58 def __init__(self, repo_id):
59 59 self.repo_id = repo_id
60 60
61 61 def __repr__(self):
62 62 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
63 63
64 64
65 65 class CachedRepoList(object):
66 66
67 67 def __init__(self, db_repo_list, repos_path, order_by=None):
68 68 self.db_repo_list = db_repo_list
69 69 self.repos_path = repos_path
70 70 self.order_by = order_by
71 71 self.reversed = (order_by or '').startswith('-')
72 72
73 73 def __len__(self):
74 74 return len(self.db_repo_list)
75 75
76 76 def __repr__(self):
77 77 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
78 78
79 79 def __iter__(self):
80 80 for dbr in self.db_repo_list:
81 81 scmr = dbr.scm_instance_cached
82 82 # check permission at this level
83 83 if not HasRepoPermissionAny('repository.read', 'repository.write',
84 84 'repository.admin')(dbr.repo_name,
85 85 'get repo check'):
86 86 continue
87 87
88 88 if scmr is None:
89 89 log.error('%s this repository is present in database but it '
90 90 'cannot be created as an scm instance',
91 91 dbr.repo_name)
92 92 continue
93 93
94 94 last_change = scmr.last_change
95 95 tip = h.get_changeset_safe(scmr, 'tip')
96 96
97 97 tmp_d = {}
98 98 tmp_d['name'] = dbr.repo_name
99 99 tmp_d['name_sort'] = tmp_d['name'].lower()
100 100 tmp_d['description'] = dbr.description
101 101 tmp_d['description_sort'] = tmp_d['description']
102 102 tmp_d['last_change'] = last_change
103 103 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
104 104 tmp_d['tip'] = tip.raw_id
105 105 tmp_d['tip_sort'] = tip.revision
106 106 tmp_d['rev'] = tip.revision
107 107 tmp_d['contact'] = dbr.user.full_contact
108 108 tmp_d['contact_sort'] = tmp_d['contact']
109 109 tmp_d['owner_sort'] = tmp_d['contact']
110 110 tmp_d['repo_archives'] = list(scmr._get_archives())
111 111 tmp_d['last_msg'] = tip.message
112 112 tmp_d['author'] = tip.author
113 113 tmp_d['dbrepo'] = dbr.get_dict()
114 114 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
115 115 yield tmp_d
116 116
117 117
118 118 class ScmModel(BaseModel):
119 119 """
120 120 Generic Scm Model
121 121 """
122 122
123 123 def __get_repo(self, instance):
124 124 cls = Repository
125 125 if isinstance(instance, cls):
126 126 return instance
127 127 elif isinstance(instance, int) or str(instance).isdigit():
128 128 return cls.get(instance)
129 129 elif isinstance(instance, basestring):
130 130 return cls.get_by_repo_name(instance)
131 131 elif instance:
132 132 raise Exception('given object must be int, basestr or Instance'
133 133 ' of %s got %s' % (type(cls), type(instance)))
134 134
135 135 @LazyProperty
136 136 def repos_path(self):
137 137 """
138 138 Get's the repositories root path from database
139 139 """
140 140
141 141 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
142 142
143 143 return q.ui_value
144 144
145 145 def repo_scan(self, repos_path=None):
146 146 """
147 147 Listing of repositories in given path. This path should not be a
148 148 repository itself. Return a dictionary of repository objects
149 149
150 150 :param repos_path: path to directory containing repositories
151 151 """
152 152
153 log.info('scanning for repositories in %s', repos_path)
154
155 153 if repos_path is None:
156 154 repos_path = self.repos_path
157 155
156 log.info('scanning for repositories in %s' % repos_path)
157
158 158 baseui = make_ui('db')
159 159 repos = {}
160 160
161 161 for name, path in get_filesystem_repos(repos_path, recursive=True):
162 162
163 163 # name need to be decomposed and put back together using the /
164 164 # since this is internal storage separator for rhodecode
165 165 name = Repository.url_sep().join(name.split(os.sep))
166 166
167 167 try:
168 168 if name in repos:
169 169 raise RepositoryError('Duplicate repository name %s '
170 170 'found in %s' % (name, path))
171 171 else:
172 172
173 173 klass = get_backend(path[0])
174 174
175 175 if path[0] == 'hg' and path[0] in BACKENDS.keys():
176 176 repos[name] = klass(safe_str(path[1]), baseui=baseui)
177 177
178 178 if path[0] == 'git' and path[0] in BACKENDS.keys():
179 179 repos[name] = klass(path[1])
180 180 except OSError:
181 181 continue
182 182
183 183 return repos
184 184
185 185 def get_repos(self, all_repos=None, sort_key=None):
186 186 """
187 187 Get all repos from db and for each repo create it's
188 188 backend instance and fill that backed with information from database
189 189
190 190 :param all_repos: list of repository names as strings
191 191 give specific repositories list, good for filtering
192 192 """
193 193 if all_repos is None:
194 194 all_repos = self.sa.query(Repository)\
195 195 .filter(Repository.group_id == None)\
196 196 .order_by(Repository.repo_name).all()
197 197
198 198 repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
199 199 order_by=sort_key)
200 200
201 201 return repo_iter
202 202
203 203 def mark_for_invalidation(self, repo_name):
204 204 """Puts cache invalidation task into db for
205 205 further global cache invalidation
206 206
207 207 :param repo_name: this repo that should invalidation take place
208 208 """
209 209 CacheInvalidation.set_invalidate(repo_name)
210 210 CacheInvalidation.set_invalidate(repo_name + "_README")
211 211
212 212 def toggle_following_repo(self, follow_repo_id, user_id):
213 213
214 214 f = self.sa.query(UserFollowing)\
215 215 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
216 216 .filter(UserFollowing.user_id == user_id).scalar()
217 217
218 218 if f is not None:
219 219 try:
220 220 self.sa.delete(f)
221 221 action_logger(UserTemp(user_id),
222 222 'stopped_following_repo',
223 223 RepoTemp(follow_repo_id))
224 224 return
225 225 except:
226 226 log.error(traceback.format_exc())
227 227 raise
228 228
229 229 try:
230 230 f = UserFollowing()
231 231 f.user_id = user_id
232 232 f.follows_repo_id = follow_repo_id
233 233 self.sa.add(f)
234 234
235 235 action_logger(UserTemp(user_id),
236 236 'started_following_repo',
237 237 RepoTemp(follow_repo_id))
238 238 except:
239 239 log.error(traceback.format_exc())
240 240 raise
241 241
242 242 def toggle_following_user(self, follow_user_id, user_id):
243 243 f = self.sa.query(UserFollowing)\
244 244 .filter(UserFollowing.follows_user_id == follow_user_id)\
245 245 .filter(UserFollowing.user_id == user_id).scalar()
246 246
247 247 if f is not None:
248 248 try:
249 249 self.sa.delete(f)
250 250 return
251 251 except:
252 252 log.error(traceback.format_exc())
253 253 raise
254 254
255 255 try:
256 256 f = UserFollowing()
257 257 f.user_id = user_id
258 258 f.follows_user_id = follow_user_id
259 259 self.sa.add(f)
260 260 except:
261 261 log.error(traceback.format_exc())
262 262 raise
263 263
264 264 def is_following_repo(self, repo_name, user_id, cache=False):
265 265 r = self.sa.query(Repository)\
266 266 .filter(Repository.repo_name == repo_name).scalar()
267 267
268 268 f = self.sa.query(UserFollowing)\
269 269 .filter(UserFollowing.follows_repository == r)\
270 270 .filter(UserFollowing.user_id == user_id).scalar()
271 271
272 272 return f is not None
273 273
274 274 def is_following_user(self, username, user_id, cache=False):
275 275 u = User.get_by_username(username)
276 276
277 277 f = self.sa.query(UserFollowing)\
278 278 .filter(UserFollowing.follows_user == u)\
279 279 .filter(UserFollowing.user_id == user_id).scalar()
280 280
281 281 return f is not None
282 282
283 283 def get_followers(self, repo_id):
284 284 if not isinstance(repo_id, int):
285 285 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
286 286
287 287 return self.sa.query(UserFollowing)\
288 288 .filter(UserFollowing.follows_repo_id == repo_id).count()
289 289
290 290 def get_forks(self, repo_id):
291 291 if not isinstance(repo_id, int):
292 292 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
293 293
294 294 return self.sa.query(Repository)\
295 295 .filter(Repository.fork_id == repo_id).count()
296 296
297 297 def mark_as_fork(self, repo, fork, user):
298 298 repo = self.__get_repo(repo)
299 299 fork = self.__get_repo(fork)
300 300 repo.fork = fork
301 301 self.sa.add(repo)
302 302 return repo
303 303
304 304 def pull_changes(self, repo_name, username):
305 305 dbrepo = Repository.get_by_repo_name(repo_name)
306 306 clone_uri = dbrepo.clone_uri
307 307 if not clone_uri:
308 308 raise Exception("This repository doesn't have a clone uri")
309 309
310 310 repo = dbrepo.scm_instance
311 311 try:
312 312 extras = {'ip': '',
313 313 'username': username,
314 314 'action': 'push_remote',
315 315 'repository': repo_name}
316 316
317 317 #inject ui extra param to log this action via push logger
318 318 for k, v in extras.items():
319 319 repo._repo.ui.setconfig('rhodecode_extras', k, v)
320 320
321 321 repo.pull(clone_uri)
322 322 self.mark_for_invalidation(repo_name)
323 323 except:
324 324 log.error(traceback.format_exc())
325 325 raise
326 326
327 327 def commit_change(self, repo, repo_name, cs, user, author, message,
328 328 content, f_path):
329 329
330 330 if repo.alias == 'hg':
331 331 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
332 332 elif repo.alias == 'git':
333 333 from vcs.backends.git import GitInMemoryChangeset as IMC
334 334
335 335 # decoding here will force that we have proper encoded values
336 336 # in any other case this will throw exceptions and deny commit
337 337 content = safe_str(content)
338 338 message = safe_str(message)
339 339 path = safe_str(f_path)
340 340 author = safe_str(author)
341 341 m = IMC(repo)
342 342 m.change(FileNode(path, content))
343 343 tip = m.commit(message=message,
344 344 author=author,
345 345 parents=[cs], branch=cs.branch)
346 346
347 347 new_cs = tip.short_id
348 348 action = 'push_local:%s' % new_cs
349 349
350 350 action_logger(user, action, repo_name)
351 351
352 352 self.mark_for_invalidation(repo_name)
353 353
354 354 def create_node(self, repo, repo_name, cs, user, author, message, content,
355 355 f_path):
356 356 if repo.alias == 'hg':
357 357 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
358 358 elif repo.alias == 'git':
359 359 from vcs.backends.git import GitInMemoryChangeset as IMC
360 360 # decoding here will force that we have proper encoded values
361 361 # in any other case this will throw exceptions and deny commit
362 362
363 363 if isinstance(content, (basestring,)):
364 364 content = safe_str(content)
365 365 elif isinstance(content, (file, cStringIO.OutputType,)):
366 366 content = content.read()
367 367 else:
368 368 raise Exception('Content is of unrecognized type %s' % (
369 369 type(content)
370 370 ))
371 371
372 372 message = safe_str(message)
373 373 path = safe_str(f_path)
374 374 author = safe_str(author)
375 375 m = IMC(repo)
376 376
377 377 if isinstance(cs, EmptyChangeset):
378 378 # Emptychangeset means we we're editing empty repository
379 379 parents = None
380 380 else:
381 381 parents = [cs]
382 382
383 383 m.add(FileNode(path, content=content))
384 384 tip = m.commit(message=message,
385 385 author=author,
386 386 parents=parents, branch=cs.branch)
387 387 new_cs = tip.short_id
388 388 action = 'push_local:%s' % new_cs
389 389
390 390 action_logger(user, action, repo_name)
391 391
392 392 self.mark_for_invalidation(repo_name)
393 393
394 394 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
395 395 """
396 396 recursive walk in root dir and return a set of all path in that dir
397 397 based on repository walk function
398 398
399 399 :param repo_name: name of repository
400 400 :param revision: revision for which to list nodes
401 401 :param root_path: root path to list
402 402 :param flat: return as a list, if False returns a dict with decription
403 403
404 404 """
405 405 _files = list()
406 406 _dirs = list()
407 407 try:
408 408 _repo = self.__get_repo(repo_name)
409 409 changeset = _repo.scm_instance.get_changeset(revision)
410 410 root_path = root_path.lstrip('/')
411 411 for topnode, dirs, files in changeset.walk(root_path):
412 412 for f in files:
413 413 _files.append(f.path if flat else {"name": f.path,
414 414 "type": "file"})
415 415 for d in dirs:
416 416 _dirs.append(d.path if flat else {"name": d.path,
417 417 "type": "dir"})
418 418 except RepositoryError:
419 419 log.debug(traceback.format_exc())
420 420 raise
421 421
422 422 return _dirs, _files
423 423
424 424 def get_unread_journal(self):
425 425 return self.sa.query(UserLog).count()
General Comments 0
You need to be logged in to leave comments. Login now