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