##// END OF EJS Templates
Fixed remote pull command from todays code refactoring
marcink -
r1370:ef9a30e2 beta
parent child Browse files
Show More
@@ -1,412 +1,412 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) 2009-2011 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
30 30 from mercurial import ui
31 31
32 32 from sqlalchemy.exc import DatabaseError
33 33 from sqlalchemy.orm import make_transient
34 34
35 35 from beaker.cache import cache_region, region_invalidate
36 36
37 37 from vcs import get_backend
38 38 from vcs.utils.helpers import get_scm
39 39 from vcs.exceptions import RepositoryError, VCSError
40 40 from vcs.utils.lazy import LazyProperty
41 41 from vcs.nodes import FileNode
42 42
43 43 from rhodecode import BACKENDS
44 44 from rhodecode.lib import helpers as h
45 45 from rhodecode.lib.auth import HasRepoPermissionAny
46 46 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
47 47 action_logger
48 48 from rhodecode.model import BaseModel
49 49 from rhodecode.model.user import UserModel
50 50 from rhodecode.model.repo import RepoModel
51 51 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
52 52 UserFollowing, UserLog
53 53 from rhodecode.model.caching_query import FromCache
54 54
55 55 log = logging.getLogger(__name__)
56 56
57 57
58 58 class UserTemp(object):
59 59 def __init__(self, user_id):
60 60 self.user_id = user_id
61 61
62 62 def __repr__(self):
63 63 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
64 64
65 65
66 66 class RepoTemp(object):
67 67 def __init__(self, repo_id):
68 68 self.repo_id = repo_id
69 69
70 70 def __repr__(self):
71 71 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
72 72
73 73 class CachedRepoList(object):
74 74
75 75 def __init__(self, db_repo_list, invalidation_list, repos_path,
76 76 order_by=None):
77 77 self.db_repo_list = db_repo_list
78 78 self.invalidation_list = invalidation_list
79 79 self.repos_path = repos_path
80 80 self.order_by = order_by
81 81 self.reversed = (order_by or '').startswith('-')
82 82
83 83 def __len__(self):
84 84 return len(self.db_repo_list)
85 85
86 86 def __repr__(self):
87 87 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
88 88
89 89 def __iter__(self):
90 90 for db_repo in self.db_repo_list:
91 91 dbr = db_repo
92 92
93 93 # invalidate the repo cache if needed before getting the
94 94 # scm instance
95 95
96 96 scm_invalidate = False
97 97 if self.invalidation_list is not None:
98 98 scm_invalidate = dbr.repo_name in self.invalidation_list
99 99
100 100 if scm_invalidate:
101 101 log.info('invalidating cache for repository %s',
102 102 dbr.repo_name)
103 103 db_repo.set_invalidate
104 104
105 105 scmr = db_repo.scm_instance_cached
106 106
107 107 #check permission at this level
108 108 if not HasRepoPermissionAny('repository.read',
109 109 'repository.write',
110 110 'repository.admin')(dbr.repo_name,
111 111 'get repo check'):
112 112 continue
113 113
114 114
115 115
116 116
117 117
118 118 last_change = scmr.last_change
119 119 tip = h.get_changeset_safe(scmr, 'tip')
120 120
121 121 tmp_d = {}
122 122 tmp_d['name'] = dbr.repo_name
123 123 tmp_d['name_sort'] = tmp_d['name'].lower()
124 124 tmp_d['description'] = dbr.description
125 125 tmp_d['description_sort'] = tmp_d['description']
126 126 tmp_d['last_change'] = last_change
127 127 tmp_d['last_change_sort'] = time.mktime(last_change \
128 128 .timetuple())
129 129 tmp_d['tip'] = tip.raw_id
130 130 tmp_d['tip_sort'] = tip.revision
131 131 tmp_d['rev'] = tip.revision
132 132 tmp_d['contact'] = dbr.user.full_contact
133 133 tmp_d['contact_sort'] = tmp_d['contact']
134 134 tmp_d['owner_sort'] = tmp_d['contact']
135 135 tmp_d['repo_archives'] = list(scmr._get_archives())
136 136 tmp_d['last_msg'] = tip.message
137 137 tmp_d['repo'] = scmr
138 138 tmp_d['dbrepo'] = dbr.get_dict()
139 139 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork \
140 140 else {}
141 141 yield tmp_d
142 142
143 143 class ScmModel(BaseModel):
144 144 """Generic Scm Model
145 145 """
146 146
147 147 @LazyProperty
148 148 def repos_path(self):
149 149 """Get's the repositories root path from database
150 150 """
151 151
152 152 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
153 153
154 154 return q.ui_value
155 155
156 156 def repo_scan(self, repos_path=None):
157 157 """Listing of repositories in given path. This path should not be a
158 158 repository itself. Return a dictionary of repository objects
159 159
160 160 :param repos_path: path to directory containing repositories
161 161 """
162 162
163 163 log.info('scanning for repositories in %s', repos_path)
164 164
165 165 if repos_path is None:
166 166 repos_path = self.repos_path
167 167
168 168 baseui = make_ui('db')
169 169 repos_list = {}
170 170
171 171 for name, path in get_filesystem_repos(repos_path, recursive=True):
172 172 try:
173 173 if name in repos_list:
174 174 raise RepositoryError('Duplicate repository name %s '
175 175 'found in %s' % (name, path))
176 176 else:
177 177
178 178 klass = get_backend(path[0])
179 179
180 180 if path[0] == 'hg' and path[0] in BACKENDS.keys():
181 181 repos_list[name] = klass(path[1], baseui=baseui)
182 182
183 183 if path[0] == 'git' and path[0] in BACKENDS.keys():
184 184 repos_list[name] = klass(path[1])
185 185 except OSError:
186 186 continue
187 187
188 188 return repos_list
189 189
190 190 def get_repos(self, all_repos=None, sort_key=None):
191 191 """
192 192 Get all repos from db and for each repo create it's
193 193 backend instance and fill that backed with information from database
194 194
195 195 :param all_repos: list of repository names as strings
196 196 give specific repositories list, good for filtering
197 197 """
198 198 if all_repos is None:
199 199 all_repos = self.sa.query(Repository)\
200 200 .filter(Repository.group_id == None)\
201 201 .order_by(Repository.repo_name).all()
202 202
203 203 #get the repositories that should be invalidated
204 204 invalidation_list = [str(x.cache_key) for x in \
205 205 self.sa.query(CacheInvalidation.cache_key)\
206 206 .filter(CacheInvalidation.cache_active == False)\
207 207 .all()]
208 208
209 209 repo_iter = CachedRepoList(all_repos, invalidation_list,
210 210 repos_path=self.repos_path,
211 211 order_by=sort_key)
212 212
213 213 return repo_iter
214 214
215 215 def mark_for_invalidation(self, repo_name):
216 216 """Puts cache invalidation task into db for
217 217 further global cache invalidation
218 218
219 219 :param repo_name: this repo that should invalidation take place
220 220 """
221 221
222 222 log.debug('marking %s for invalidation', repo_name)
223 223 cache = self.sa.query(CacheInvalidation)\
224 224 .filter(CacheInvalidation.cache_key == repo_name).scalar()
225 225
226 226 if cache:
227 227 #mark this cache as inactive
228 228 cache.cache_active = False
229 229 else:
230 230 log.debug('cache key not found in invalidation db -> creating one')
231 231 cache = CacheInvalidation(repo_name)
232 232
233 233 try:
234 234 self.sa.add(cache)
235 235 self.sa.commit()
236 236 except (DatabaseError,):
237 237 log.error(traceback.format_exc())
238 238 self.sa.rollback()
239 239
240 240 def toggle_following_repo(self, follow_repo_id, user_id):
241 241
242 242 f = self.sa.query(UserFollowing)\
243 243 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
244 244 .filter(UserFollowing.user_id == user_id).scalar()
245 245
246 246 if f is not None:
247 247
248 248 try:
249 249 self.sa.delete(f)
250 250 self.sa.commit()
251 251 action_logger(UserTemp(user_id),
252 252 'stopped_following_repo',
253 253 RepoTemp(follow_repo_id))
254 254 return
255 255 except:
256 256 log.error(traceback.format_exc())
257 257 self.sa.rollback()
258 258 raise
259 259
260 260 try:
261 261 f = UserFollowing()
262 262 f.user_id = user_id
263 263 f.follows_repo_id = follow_repo_id
264 264 self.sa.add(f)
265 265 self.sa.commit()
266 266 action_logger(UserTemp(user_id),
267 267 'started_following_repo',
268 268 RepoTemp(follow_repo_id))
269 269 except:
270 270 log.error(traceback.format_exc())
271 271 self.sa.rollback()
272 272 raise
273 273
274 274 def toggle_following_user(self, follow_user_id, user_id):
275 275 f = self.sa.query(UserFollowing)\
276 276 .filter(UserFollowing.follows_user_id == follow_user_id)\
277 277 .filter(UserFollowing.user_id == user_id).scalar()
278 278
279 279 if f is not None:
280 280 try:
281 281 self.sa.delete(f)
282 282 self.sa.commit()
283 283 return
284 284 except:
285 285 log.error(traceback.format_exc())
286 286 self.sa.rollback()
287 287 raise
288 288
289 289 try:
290 290 f = UserFollowing()
291 291 f.user_id = user_id
292 292 f.follows_user_id = follow_user_id
293 293 self.sa.add(f)
294 294 self.sa.commit()
295 295 except:
296 296 log.error(traceback.format_exc())
297 297 self.sa.rollback()
298 298 raise
299 299
300 300 def is_following_repo(self, repo_name, user_id, cache=False):
301 301 r = self.sa.query(Repository)\
302 302 .filter(Repository.repo_name == repo_name).scalar()
303 303
304 304 f = self.sa.query(UserFollowing)\
305 305 .filter(UserFollowing.follows_repository == r)\
306 306 .filter(UserFollowing.user_id == user_id).scalar()
307 307
308 308 return f is not None
309 309
310 310 def is_following_user(self, username, user_id, cache=False):
311 311 u = UserModel(self.sa).get_by_username(username)
312 312
313 313 f = self.sa.query(UserFollowing)\
314 314 .filter(UserFollowing.follows_user == u)\
315 315 .filter(UserFollowing.user_id == user_id).scalar()
316 316
317 317 return f is not None
318 318
319 319 def get_followers(self, repo_id):
320 320 if not isinstance(repo_id, int):
321 321 repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
322 322
323 323 return self.sa.query(UserFollowing)\
324 324 .filter(UserFollowing.follows_repo_id == repo_id).count()
325 325
326 326 def get_forks(self, repo_id):
327 327 if not isinstance(repo_id, int):
328 328 repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
329 329
330 330 return self.sa.query(Repository)\
331 331 .filter(Repository.fork_id == repo_id).count()
332 332
333 333 def pull_changes(self, repo_name, username):
334 repo, dbrepo = self.get(repo_name, retval='all')
335
334 dbrepo = Repository.by_repo_name(repo_name)
335 repo = dbrepo.scm_instance
336 336 try:
337 337 extras = {'ip': '',
338 338 'username': username,
339 339 'action': 'push_remote',
340 340 'repository': repo_name}
341 341
342 342 #inject ui extra param to log this action via push logger
343 343 for k, v in extras.items():
344 344 repo._repo.ui.setconfig('rhodecode_extras', k, v)
345 345
346 346 repo.pull(dbrepo.clone_uri)
347 347 self.mark_for_invalidation(repo_name)
348 348 except:
349 349 log.error(traceback.format_exc())
350 350 raise
351 351
352 352
353 353 def commit_change(self, repo, repo_name, cs, user, author, message, content,
354 354 f_path):
355 355
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
361 361 # decoding here will force that we have proper encoded values
362 362 # in any other case this will throw exceptions and deny commit
363 363 content = content.encode('utf8')
364 364 message = message.encode('utf8')
365 365 path = f_path.encode('utf8')
366 366 author = author.encode('utf8')
367 367 m = IMC(repo)
368 368 m.change(FileNode(path, content))
369 369 tip = m.commit(message=message,
370 370 author=author,
371 371 parents=[cs], branch=cs.branch)
372 372
373 373 new_cs = tip.short_id
374 374 action = 'push_local:%s' % new_cs
375 375
376 376 action_logger(user, action, repo_name)
377 377
378 378 self.mark_for_invalidation(repo_name)
379 379
380 380
381 381 def get_unread_journal(self):
382 382 return self.sa.query(UserLog).count()
383 383
384 384 def _should_invalidate(self, repo_name):
385 385 """Looks up database for invalidation signals for this repo_name
386 386
387 387 :param repo_name:
388 388 """
389 389
390 390 ret = self.sa.query(CacheInvalidation)\
391 391 .filter(CacheInvalidation.cache_key == repo_name)\
392 392 .filter(CacheInvalidation.cache_active == False)\
393 393 .scalar()
394 394
395 395 return ret
396 396
397 397 def _mark_invalidated(self, cache_key):
398 398 """ Marks all occurrences of cache to invalidation as already
399 399 invalidated
400 400
401 401 :param cache_key:
402 402 """
403 403
404 404 if cache_key:
405 405 log.debug('marking %s as already invalidated', cache_key)
406 406 try:
407 407 cache_key.cache_active = True
408 408 self.sa.add(cache_key)
409 409 self.sa.commit()
410 410 except (DatabaseError,):
411 411 log.error(traceback.format_exc())
412 412 self.sa.rollback()
General Comments 0
You need to be logged in to leave comments. Login now