##// END OF EJS Templates
diff-cache: remove diff cache dir on repository removal.
marcink -
r2689:33739db8 default
parent child Browse files
Show More
@@ -1,1036 +1,1041 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import re
23 23 import shutil
24 24 import time
25 25 import logging
26 26 import traceback
27 27 import datetime
28 28
29 29 from pyramid.threadlocal import get_current_request
30 30 from zope.cachedescriptors.property import Lazy as LazyProperty
31 31
32 32 from rhodecode import events
33 33 from rhodecode.lib.auth import HasUserGroupPermissionAny
34 34 from rhodecode.lib.caching_query import FromCache
35 35 from rhodecode.lib.exceptions import AttachedForksError
36 36 from rhodecode.lib.hooks_base import log_delete_repository
37 37 from rhodecode.lib.user_log_filter import user_log_filter
38 38 from rhodecode.lib.utils import make_db_config
39 39 from rhodecode.lib.utils2 import (
40 40 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
41 41 get_current_rhodecode_user, safe_int, datetime_to_time,
42 42 action_logger_generic)
43 43 from rhodecode.lib.vcs.backends import get_backend
44 44 from rhodecode.model import BaseModel
45 45 from rhodecode.model.db import (
46 46 _hash_key, joinedload, or_, Repository, UserRepoToPerm, UserGroupRepoToPerm,
47 47 UserRepoGroupToPerm, UserGroupRepoGroupToPerm, User, Permission,
48 48 Statistics, UserGroup, RepoGroup, RepositoryField, UserLog)
49 49
50 50 from rhodecode.model.settings import VcsSettingsModel
51 51
52 52
53 53 log = logging.getLogger(__name__)
54 54
55 55
56 56 class RepoModel(BaseModel):
57 57
58 58 cls = Repository
59 59
60 60 def _get_user_group(self, users_group):
61 61 return self._get_instance(UserGroup, users_group,
62 62 callback=UserGroup.get_by_group_name)
63 63
64 64 def _get_repo_group(self, repo_group):
65 65 return self._get_instance(RepoGroup, repo_group,
66 66 callback=RepoGroup.get_by_group_name)
67 67
68 68 def _create_default_perms(self, repository, private):
69 69 # create default permission
70 70 default = 'repository.read'
71 71 def_user = User.get_default_user()
72 72 for p in def_user.user_perms:
73 73 if p.permission.permission_name.startswith('repository.'):
74 74 default = p.permission.permission_name
75 75 break
76 76
77 77 default_perm = 'repository.none' if private else default
78 78
79 79 repo_to_perm = UserRepoToPerm()
80 80 repo_to_perm.permission = Permission.get_by_key(default_perm)
81 81
82 82 repo_to_perm.repository = repository
83 83 repo_to_perm.user_id = def_user.user_id
84 84
85 85 return repo_to_perm
86 86
87 87 @LazyProperty
88 88 def repos_path(self):
89 89 """
90 90 Gets the repositories root path from database
91 91 """
92 92 settings_model = VcsSettingsModel(sa=self.sa)
93 93 return settings_model.get_repos_location()
94 94
95 95 def get(self, repo_id, cache=False):
96 96 repo = self.sa.query(Repository) \
97 97 .filter(Repository.repo_id == repo_id)
98 98
99 99 if cache:
100 100 repo = repo.options(
101 101 FromCache("sql_cache_short", "get_repo_%s" % repo_id))
102 102 return repo.scalar()
103 103
104 104 def get_repo(self, repository):
105 105 return self._get_repo(repository)
106 106
107 107 def get_by_repo_name(self, repo_name, cache=False):
108 108 repo = self.sa.query(Repository) \
109 109 .filter(Repository.repo_name == repo_name)
110 110
111 111 if cache:
112 112 name_key = _hash_key(repo_name)
113 113 repo = repo.options(
114 114 FromCache("sql_cache_short", "get_repo_%s" % name_key))
115 115 return repo.scalar()
116 116
117 117 def _extract_id_from_repo_name(self, repo_name):
118 118 if repo_name.startswith('/'):
119 119 repo_name = repo_name.lstrip('/')
120 120 by_id_match = re.match(r'^_(\d{1,})', repo_name)
121 121 if by_id_match:
122 122 return by_id_match.groups()[0]
123 123
124 124 def get_repo_by_id(self, repo_name):
125 125 """
126 126 Extracts repo_name by id from special urls.
127 127 Example url is _11/repo_name
128 128
129 129 :param repo_name:
130 130 :return: repo object if matched else None
131 131 """
132 132
133 133 try:
134 134 _repo_id = self._extract_id_from_repo_name(repo_name)
135 135 if _repo_id:
136 136 return self.get(_repo_id)
137 137 except Exception:
138 138 log.exception('Failed to extract repo_name from URL')
139 139
140 140 return None
141 141
142 142 def get_repos_for_root(self, root, traverse=False):
143 143 if traverse:
144 144 like_expression = u'{}%'.format(safe_unicode(root))
145 145 repos = Repository.query().filter(
146 146 Repository.repo_name.like(like_expression)).all()
147 147 else:
148 148 if root and not isinstance(root, RepoGroup):
149 149 raise ValueError(
150 150 'Root must be an instance '
151 151 'of RepoGroup, got:{} instead'.format(type(root)))
152 152 repos = Repository.query().filter(Repository.group == root).all()
153 153 return repos
154 154
155 155 def get_url(self, repo, request=None, permalink=False):
156 156 if not request:
157 157 request = get_current_request()
158 158
159 159 if not request:
160 160 return
161 161
162 162 if permalink:
163 163 return request.route_url(
164 164 'repo_summary', repo_name='_{}'.format(safe_str(repo.repo_id)))
165 165 else:
166 166 return request.route_url(
167 167 'repo_summary', repo_name=safe_str(repo.repo_name))
168 168
169 169 def get_commit_url(self, repo, commit_id, request=None, permalink=False):
170 170 if not request:
171 171 request = get_current_request()
172 172
173 173 if not request:
174 174 return
175 175
176 176 if permalink:
177 177 return request.route_url(
178 178 'repo_commit', repo_name=safe_str(repo.repo_id),
179 179 commit_id=commit_id)
180 180
181 181 else:
182 182 return request.route_url(
183 183 'repo_commit', repo_name=safe_str(repo.repo_name),
184 184 commit_id=commit_id)
185 185
186 186 def get_repo_log(self, repo, filter_term):
187 187 repo_log = UserLog.query()\
188 188 .filter(or_(UserLog.repository_id == repo.repo_id,
189 189 UserLog.repository_name == repo.repo_name))\
190 190 .options(joinedload(UserLog.user))\
191 191 .options(joinedload(UserLog.repository))\
192 192 .order_by(UserLog.action_date.desc())
193 193
194 194 repo_log = user_log_filter(repo_log, filter_term)
195 195 return repo_log
196 196
197 197 @classmethod
198 198 def update_repoinfo(cls, repositories=None):
199 199 if not repositories:
200 200 repositories = Repository.getAll()
201 201 for repo in repositories:
202 202 repo.update_commit_cache()
203 203
204 204 def get_repos_as_dict(self, repo_list=None, admin=False,
205 205 super_user_actions=False):
206 206 _render = get_current_request().get_partial_renderer(
207 207 'rhodecode:templates/data_table/_dt_elements.mako')
208 208 c = _render.get_call_context()
209 209
210 210 def quick_menu(repo_name):
211 211 return _render('quick_menu', repo_name)
212 212
213 213 def repo_lnk(name, rtype, rstate, private, fork_of):
214 214 return _render('repo_name', name, rtype, rstate, private, fork_of,
215 215 short_name=not admin, admin=False)
216 216
217 217 def last_change(last_change):
218 218 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
219 219 last_change = last_change + datetime.timedelta(seconds=
220 220 (datetime.datetime.now() - datetime.datetime.utcnow()).seconds)
221 221 return _render("last_change", last_change)
222 222
223 223 def rss_lnk(repo_name):
224 224 return _render("rss", repo_name)
225 225
226 226 def atom_lnk(repo_name):
227 227 return _render("atom", repo_name)
228 228
229 229 def last_rev(repo_name, cs_cache):
230 230 return _render('revision', repo_name, cs_cache.get('revision'),
231 231 cs_cache.get('raw_id'), cs_cache.get('author'),
232 232 cs_cache.get('message'), cs_cache.get('date'))
233 233
234 234 def desc(desc):
235 235 return _render('repo_desc', desc, c.visual.stylify_metatags)
236 236
237 237 def state(repo_state):
238 238 return _render("repo_state", repo_state)
239 239
240 240 def repo_actions(repo_name):
241 241 return _render('repo_actions', repo_name, super_user_actions)
242 242
243 243 def user_profile(username):
244 244 return _render('user_profile', username)
245 245
246 246 repos_data = []
247 247 for repo in repo_list:
248 248 cs_cache = repo.changeset_cache
249 249 row = {
250 250 "menu": quick_menu(repo.repo_name),
251 251
252 252 "name": repo_lnk(repo.repo_name, repo.repo_type,
253 253 repo.repo_state, repo.private, repo.fork),
254 254 "name_raw": repo.repo_name.lower(),
255 255
256 256 "last_change": last_change(repo.last_db_change),
257 257 "last_change_raw": datetime_to_time(repo.last_db_change),
258 258
259 259 "last_changeset": last_rev(repo.repo_name, cs_cache),
260 260 "last_changeset_raw": cs_cache.get('revision'),
261 261
262 262 "desc": desc(repo.description_safe),
263 263 "owner": user_profile(repo.user.username),
264 264
265 265 "state": state(repo.repo_state),
266 266 "rss": rss_lnk(repo.repo_name),
267 267
268 268 "atom": atom_lnk(repo.repo_name),
269 269 }
270 270 if admin:
271 271 row.update({
272 272 "action": repo_actions(repo.repo_name),
273 273 })
274 274 repos_data.append(row)
275 275
276 276 return repos_data
277 277
278 278 def _get_defaults(self, repo_name):
279 279 """
280 280 Gets information about repository, and returns a dict for
281 281 usage in forms
282 282
283 283 :param repo_name:
284 284 """
285 285
286 286 repo_info = Repository.get_by_repo_name(repo_name)
287 287
288 288 if repo_info is None:
289 289 return None
290 290
291 291 defaults = repo_info.get_dict()
292 292 defaults['repo_name'] = repo_info.just_name
293 293
294 294 groups = repo_info.groups_with_parents
295 295 parent_group = groups[-1] if groups else None
296 296
297 297 # we use -1 as this is how in HTML, we mark an empty group
298 298 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
299 299
300 300 keys_to_process = (
301 301 {'k': 'repo_type', 'strip': False},
302 302 {'k': 'repo_enable_downloads', 'strip': True},
303 303 {'k': 'repo_description', 'strip': True},
304 304 {'k': 'repo_enable_locking', 'strip': True},
305 305 {'k': 'repo_landing_rev', 'strip': True},
306 306 {'k': 'clone_uri', 'strip': False},
307 307 {'k': 'push_uri', 'strip': False},
308 308 {'k': 'repo_private', 'strip': True},
309 309 {'k': 'repo_enable_statistics', 'strip': True}
310 310 )
311 311
312 312 for item in keys_to_process:
313 313 attr = item['k']
314 314 if item['strip']:
315 315 attr = remove_prefix(item['k'], 'repo_')
316 316
317 317 val = defaults[attr]
318 318 if item['k'] == 'repo_landing_rev':
319 319 val = ':'.join(defaults[attr])
320 320 defaults[item['k']] = val
321 321 if item['k'] == 'clone_uri':
322 322 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
323 323 if item['k'] == 'push_uri':
324 324 defaults['push_uri_hidden'] = repo_info.push_uri_hidden
325 325
326 326 # fill owner
327 327 if repo_info.user:
328 328 defaults.update({'user': repo_info.user.username})
329 329 else:
330 330 replacement_user = User.get_first_super_admin().username
331 331 defaults.update({'user': replacement_user})
332 332
333 333 return defaults
334 334
335 335 def update(self, repo, **kwargs):
336 336 try:
337 337 cur_repo = self._get_repo(repo)
338 338 source_repo_name = cur_repo.repo_name
339 339 if 'user' in kwargs:
340 340 cur_repo.user = User.get_by_username(kwargs['user'])
341 341
342 342 if 'repo_group' in kwargs:
343 343 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
344 344 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
345 345
346 346 update_keys = [
347 347 (1, 'repo_description'),
348 348 (1, 'repo_landing_rev'),
349 349 (1, 'repo_private'),
350 350 (1, 'repo_enable_downloads'),
351 351 (1, 'repo_enable_locking'),
352 352 (1, 'repo_enable_statistics'),
353 353 (0, 'clone_uri'),
354 354 (0, 'push_uri'),
355 355 (0, 'fork_id')
356 356 ]
357 357 for strip, k in update_keys:
358 358 if k in kwargs:
359 359 val = kwargs[k]
360 360 if strip:
361 361 k = remove_prefix(k, 'repo_')
362 362
363 363 setattr(cur_repo, k, val)
364 364
365 365 new_name = cur_repo.get_new_name(kwargs['repo_name'])
366 366 cur_repo.repo_name = new_name
367 367
368 368 # if private flag is set, reset default permission to NONE
369 369 if kwargs.get('repo_private'):
370 370 EMPTY_PERM = 'repository.none'
371 371 RepoModel().grant_user_permission(
372 372 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
373 373 )
374 374
375 375 # handle extra fields
376 376 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX),
377 377 kwargs):
378 378 k = RepositoryField.un_prefix_key(field)
379 379 ex_field = RepositoryField.get_by_key_name(
380 380 key=k, repo=cur_repo)
381 381 if ex_field:
382 382 ex_field.field_value = kwargs[field]
383 383 self.sa.add(ex_field)
384 384 cur_repo.updated_on = datetime.datetime.now()
385 385 self.sa.add(cur_repo)
386 386
387 387 if source_repo_name != new_name:
388 388 # rename repository
389 389 self._rename_filesystem_repo(
390 390 old=source_repo_name, new=new_name)
391 391
392 392 return cur_repo
393 393 except Exception:
394 394 log.error(traceback.format_exc())
395 395 raise
396 396
397 397 def _create_repo(self, repo_name, repo_type, description, owner,
398 398 private=False, clone_uri=None, repo_group=None,
399 399 landing_rev='rev:tip', fork_of=None,
400 400 copy_fork_permissions=False, enable_statistics=False,
401 401 enable_locking=False, enable_downloads=False,
402 402 copy_group_permissions=False,
403 403 state=Repository.STATE_PENDING):
404 404 """
405 405 Create repository inside database with PENDING state, this should be
406 406 only executed by create() repo. With exception of importing existing
407 407 repos
408 408 """
409 409 from rhodecode.model.scm import ScmModel
410 410
411 411 owner = self._get_user(owner)
412 412 fork_of = self._get_repo(fork_of)
413 413 repo_group = self._get_repo_group(safe_int(repo_group))
414 414
415 415 try:
416 416 repo_name = safe_unicode(repo_name)
417 417 description = safe_unicode(description)
418 418 # repo name is just a name of repository
419 419 # while repo_name_full is a full qualified name that is combined
420 420 # with name and path of group
421 421 repo_name_full = repo_name
422 422 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
423 423
424 424 new_repo = Repository()
425 425 new_repo.repo_state = state
426 426 new_repo.enable_statistics = False
427 427 new_repo.repo_name = repo_name_full
428 428 new_repo.repo_type = repo_type
429 429 new_repo.user = owner
430 430 new_repo.group = repo_group
431 431 new_repo.description = description or repo_name
432 432 new_repo.private = private
433 433 new_repo.clone_uri = clone_uri
434 434 new_repo.landing_rev = landing_rev
435 435
436 436 new_repo.enable_statistics = enable_statistics
437 437 new_repo.enable_locking = enable_locking
438 438 new_repo.enable_downloads = enable_downloads
439 439
440 440 if repo_group:
441 441 new_repo.enable_locking = repo_group.enable_locking
442 442
443 443 if fork_of:
444 444 parent_repo = fork_of
445 445 new_repo.fork = parent_repo
446 446
447 447 events.trigger(events.RepoPreCreateEvent(new_repo))
448 448
449 449 self.sa.add(new_repo)
450 450
451 451 EMPTY_PERM = 'repository.none'
452 452 if fork_of and copy_fork_permissions:
453 453 repo = fork_of
454 454 user_perms = UserRepoToPerm.query() \
455 455 .filter(UserRepoToPerm.repository == repo).all()
456 456 group_perms = UserGroupRepoToPerm.query() \
457 457 .filter(UserGroupRepoToPerm.repository == repo).all()
458 458
459 459 for perm in user_perms:
460 460 UserRepoToPerm.create(
461 461 perm.user, new_repo, perm.permission)
462 462
463 463 for perm in group_perms:
464 464 UserGroupRepoToPerm.create(
465 465 perm.users_group, new_repo, perm.permission)
466 466 # in case we copy permissions and also set this repo to private
467 467 # override the default user permission to make it a private
468 468 # repo
469 469 if private:
470 470 RepoModel(self.sa).grant_user_permission(
471 471 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
472 472
473 473 elif repo_group and copy_group_permissions:
474 474 user_perms = UserRepoGroupToPerm.query() \
475 475 .filter(UserRepoGroupToPerm.group == repo_group).all()
476 476
477 477 group_perms = UserGroupRepoGroupToPerm.query() \
478 478 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
479 479
480 480 for perm in user_perms:
481 481 perm_name = perm.permission.permission_name.replace(
482 482 'group.', 'repository.')
483 483 perm_obj = Permission.get_by_key(perm_name)
484 484 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
485 485
486 486 for perm in group_perms:
487 487 perm_name = perm.permission.permission_name.replace(
488 488 'group.', 'repository.')
489 489 perm_obj = Permission.get_by_key(perm_name)
490 490 UserGroupRepoToPerm.create(
491 491 perm.users_group, new_repo, perm_obj)
492 492
493 493 if private:
494 494 RepoModel(self.sa).grant_user_permission(
495 495 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
496 496
497 497 else:
498 498 perm_obj = self._create_default_perms(new_repo, private)
499 499 self.sa.add(perm_obj)
500 500
501 501 # now automatically start following this repository as owner
502 502 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
503 503 owner.user_id)
504 504
505 505 # we need to flush here, in order to check if database won't
506 506 # throw any exceptions, create filesystem dirs at the very end
507 507 self.sa.flush()
508 508 events.trigger(events.RepoCreateEvent(new_repo))
509 509 return new_repo
510 510
511 511 except Exception:
512 512 log.error(traceback.format_exc())
513 513 raise
514 514
515 515 def create(self, form_data, cur_user):
516 516 """
517 517 Create repository using celery tasks
518 518
519 519 :param form_data:
520 520 :param cur_user:
521 521 """
522 522 from rhodecode.lib.celerylib import tasks, run_task
523 523 return run_task(tasks.create_repo, form_data, cur_user)
524 524
525 525 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
526 526 perm_deletions=None, check_perms=True,
527 527 cur_user=None):
528 528 if not perm_additions:
529 529 perm_additions = []
530 530 if not perm_updates:
531 531 perm_updates = []
532 532 if not perm_deletions:
533 533 perm_deletions = []
534 534
535 535 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
536 536
537 537 changes = {
538 538 'added': [],
539 539 'updated': [],
540 540 'deleted': []
541 541 }
542 542 # update permissions
543 543 for member_id, perm, member_type in perm_updates:
544 544 member_id = int(member_id)
545 545 if member_type == 'user':
546 546 member_name = User.get(member_id).username
547 547 # this updates also current one if found
548 548 self.grant_user_permission(
549 549 repo=repo, user=member_id, perm=perm)
550 550 else: # set for user group
551 551 # check if we have permissions to alter this usergroup
552 552 member_name = UserGroup.get(member_id).users_group_name
553 553 if not check_perms or HasUserGroupPermissionAny(
554 554 *req_perms)(member_name, user=cur_user):
555 555 self.grant_user_group_permission(
556 556 repo=repo, group_name=member_id, perm=perm)
557 557
558 558 changes['updated'].append({'type': member_type, 'id': member_id,
559 559 'name': member_name, 'new_perm': perm})
560 560
561 561 # set new permissions
562 562 for member_id, perm, member_type in perm_additions:
563 563 member_id = int(member_id)
564 564 if member_type == 'user':
565 565 member_name = User.get(member_id).username
566 566 self.grant_user_permission(
567 567 repo=repo, user=member_id, perm=perm)
568 568 else: # set for user group
569 569 # check if we have permissions to alter this usergroup
570 570 member_name = UserGroup.get(member_id).users_group_name
571 571 if not check_perms or HasUserGroupPermissionAny(
572 572 *req_perms)(member_name, user=cur_user):
573 573 self.grant_user_group_permission(
574 574 repo=repo, group_name=member_id, perm=perm)
575 575 changes['added'].append({'type': member_type, 'id': member_id,
576 576 'name': member_name, 'new_perm': perm})
577 577 # delete permissions
578 578 for member_id, perm, member_type in perm_deletions:
579 579 member_id = int(member_id)
580 580 if member_type == 'user':
581 581 member_name = User.get(member_id).username
582 582 self.revoke_user_permission(repo=repo, user=member_id)
583 583 else: # set for user group
584 584 # check if we have permissions to alter this usergroup
585 585 member_name = UserGroup.get(member_id).users_group_name
586 586 if not check_perms or HasUserGroupPermissionAny(
587 587 *req_perms)(member_name, user=cur_user):
588 588 self.revoke_user_group_permission(
589 589 repo=repo, group_name=member_id)
590 590
591 591 changes['deleted'].append({'type': member_type, 'id': member_id,
592 592 'name': member_name, 'new_perm': perm})
593 593 return changes
594 594
595 595 def create_fork(self, form_data, cur_user):
596 596 """
597 597 Simple wrapper into executing celery task for fork creation
598 598
599 599 :param form_data:
600 600 :param cur_user:
601 601 """
602 602 from rhodecode.lib.celerylib import tasks, run_task
603 603 return run_task(tasks.create_repo_fork, form_data, cur_user)
604 604
605 605 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
606 606 """
607 607 Delete given repository, forks parameter defines what do do with
608 608 attached forks. Throws AttachedForksError if deleted repo has attached
609 609 forks
610 610
611 611 :param repo:
612 612 :param forks: str 'delete' or 'detach'
613 613 :param fs_remove: remove(archive) repo from filesystem
614 614 """
615 615 if not cur_user:
616 616 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
617 617 repo = self._get_repo(repo)
618 618 if repo:
619 619 if forks == 'detach':
620 620 for r in repo.forks:
621 621 r.fork = None
622 622 self.sa.add(r)
623 623 elif forks == 'delete':
624 624 for r in repo.forks:
625 625 self.delete(r, forks='delete')
626 626 elif [f for f in repo.forks]:
627 627 raise AttachedForksError()
628 628
629 629 old_repo_dict = repo.get_dict()
630 630 events.trigger(events.RepoPreDeleteEvent(repo))
631 631 try:
632 632 self.sa.delete(repo)
633 633 if fs_remove:
634 634 self._delete_filesystem_repo(repo)
635 635 else:
636 636 log.debug('skipping removal from filesystem')
637 637 old_repo_dict.update({
638 638 'deleted_by': cur_user,
639 639 'deleted_on': time.time(),
640 640 })
641 641 log_delete_repository(**old_repo_dict)
642 642 events.trigger(events.RepoDeleteEvent(repo))
643 643 except Exception:
644 644 log.error(traceback.format_exc())
645 645 raise
646 646
647 647 def grant_user_permission(self, repo, user, perm):
648 648 """
649 649 Grant permission for user on given repository, or update existing one
650 650 if found
651 651
652 652 :param repo: Instance of Repository, repository_id, or repository name
653 653 :param user: Instance of User, user_id or username
654 654 :param perm: Instance of Permission, or permission_name
655 655 """
656 656 user = self._get_user(user)
657 657 repo = self._get_repo(repo)
658 658 permission = self._get_perm(perm)
659 659
660 660 # check if we have that permission already
661 661 obj = self.sa.query(UserRepoToPerm) \
662 662 .filter(UserRepoToPerm.user == user) \
663 663 .filter(UserRepoToPerm.repository == repo) \
664 664 .scalar()
665 665 if obj is None:
666 666 # create new !
667 667 obj = UserRepoToPerm()
668 668 obj.repository = repo
669 669 obj.user = user
670 670 obj.permission = permission
671 671 self.sa.add(obj)
672 672 log.debug('Granted perm %s to %s on %s', perm, user, repo)
673 673 action_logger_generic(
674 674 'granted permission: {} to user: {} on repo: {}'.format(
675 675 perm, user, repo), namespace='security.repo')
676 676 return obj
677 677
678 678 def revoke_user_permission(self, repo, user):
679 679 """
680 680 Revoke permission for user on given repository
681 681
682 682 :param repo: Instance of Repository, repository_id, or repository name
683 683 :param user: Instance of User, user_id or username
684 684 """
685 685
686 686 user = self._get_user(user)
687 687 repo = self._get_repo(repo)
688 688
689 689 obj = self.sa.query(UserRepoToPerm) \
690 690 .filter(UserRepoToPerm.repository == repo) \
691 691 .filter(UserRepoToPerm.user == user) \
692 692 .scalar()
693 693 if obj:
694 694 self.sa.delete(obj)
695 695 log.debug('Revoked perm on %s on %s', repo, user)
696 696 action_logger_generic(
697 697 'revoked permission from user: {} on repo: {}'.format(
698 698 user, repo), namespace='security.repo')
699 699
700 700 def grant_user_group_permission(self, repo, group_name, perm):
701 701 """
702 702 Grant permission for user group on given repository, or update
703 703 existing one if found
704 704
705 705 :param repo: Instance of Repository, repository_id, or repository name
706 706 :param group_name: Instance of UserGroup, users_group_id,
707 707 or user group name
708 708 :param perm: Instance of Permission, or permission_name
709 709 """
710 710 repo = self._get_repo(repo)
711 711 group_name = self._get_user_group(group_name)
712 712 permission = self._get_perm(perm)
713 713
714 714 # check if we have that permission already
715 715 obj = self.sa.query(UserGroupRepoToPerm) \
716 716 .filter(UserGroupRepoToPerm.users_group == group_name) \
717 717 .filter(UserGroupRepoToPerm.repository == repo) \
718 718 .scalar()
719 719
720 720 if obj is None:
721 721 # create new
722 722 obj = UserGroupRepoToPerm()
723 723
724 724 obj.repository = repo
725 725 obj.users_group = group_name
726 726 obj.permission = permission
727 727 self.sa.add(obj)
728 728 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
729 729 action_logger_generic(
730 730 'granted permission: {} to usergroup: {} on repo: {}'.format(
731 731 perm, group_name, repo), namespace='security.repo')
732 732
733 733 return obj
734 734
735 735 def revoke_user_group_permission(self, repo, group_name):
736 736 """
737 737 Revoke permission for user group on given repository
738 738
739 739 :param repo: Instance of Repository, repository_id, or repository name
740 740 :param group_name: Instance of UserGroup, users_group_id,
741 741 or user group name
742 742 """
743 743 repo = self._get_repo(repo)
744 744 group_name = self._get_user_group(group_name)
745 745
746 746 obj = self.sa.query(UserGroupRepoToPerm) \
747 747 .filter(UserGroupRepoToPerm.repository == repo) \
748 748 .filter(UserGroupRepoToPerm.users_group == group_name) \
749 749 .scalar()
750 750 if obj:
751 751 self.sa.delete(obj)
752 752 log.debug('Revoked perm to %s on %s', repo, group_name)
753 753 action_logger_generic(
754 754 'revoked permission from usergroup: {} on repo: {}'.format(
755 755 group_name, repo), namespace='security.repo')
756 756
757 757 def delete_stats(self, repo_name):
758 758 """
759 759 removes stats for given repo
760 760
761 761 :param repo_name:
762 762 """
763 763 repo = self._get_repo(repo_name)
764 764 try:
765 765 obj = self.sa.query(Statistics) \
766 766 .filter(Statistics.repository == repo).scalar()
767 767 if obj:
768 768 self.sa.delete(obj)
769 769 except Exception:
770 770 log.error(traceback.format_exc())
771 771 raise
772 772
773 773 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
774 774 field_type='str', field_desc=''):
775 775
776 776 repo = self._get_repo(repo_name)
777 777
778 778 new_field = RepositoryField()
779 779 new_field.repository = repo
780 780 new_field.field_key = field_key
781 781 new_field.field_type = field_type # python type
782 782 new_field.field_value = field_value
783 783 new_field.field_desc = field_desc
784 784 new_field.field_label = field_label
785 785 self.sa.add(new_field)
786 786 return new_field
787 787
788 788 def delete_repo_field(self, repo_name, field_key):
789 789 repo = self._get_repo(repo_name)
790 790 field = RepositoryField.get_by_key_name(field_key, repo)
791 791 if field:
792 792 self.sa.delete(field)
793 793
794 794 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
795 795 clone_uri=None, repo_store_location=None,
796 796 use_global_config=False):
797 797 """
798 798 makes repository on filesystem. It's group aware means it'll create
799 799 a repository within a group, and alter the paths accordingly of
800 800 group location
801 801
802 802 :param repo_name:
803 803 :param alias:
804 804 :param parent:
805 805 :param clone_uri:
806 806 :param repo_store_location:
807 807 """
808 808 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
809 809 from rhodecode.model.scm import ScmModel
810 810
811 811 if Repository.NAME_SEP in repo_name:
812 812 raise ValueError(
813 813 'repo_name must not contain groups got `%s`' % repo_name)
814 814
815 815 if isinstance(repo_group, RepoGroup):
816 816 new_parent_path = os.sep.join(repo_group.full_path_splitted)
817 817 else:
818 818 new_parent_path = repo_group or ''
819 819
820 820 if repo_store_location:
821 821 _paths = [repo_store_location]
822 822 else:
823 823 _paths = [self.repos_path, new_parent_path, repo_name]
824 824 # we need to make it str for mercurial
825 825 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
826 826
827 827 # check if this path is not a repository
828 828 if is_valid_repo(repo_path, self.repos_path):
829 829 raise Exception('This path %s is a valid repository' % repo_path)
830 830
831 831 # check if this path is a group
832 832 if is_valid_repo_group(repo_path, self.repos_path):
833 833 raise Exception('This path %s is a valid group' % repo_path)
834 834
835 835 log.info('creating repo %s in %s from url: `%s`',
836 836 repo_name, safe_unicode(repo_path),
837 837 obfuscate_url_pw(clone_uri))
838 838
839 839 backend = get_backend(repo_type)
840 840
841 841 config_repo = None if use_global_config else repo_name
842 842 if config_repo and new_parent_path:
843 843 config_repo = Repository.NAME_SEP.join(
844 844 (new_parent_path, config_repo))
845 845 config = make_db_config(clear_session=False, repo=config_repo)
846 846 config.set('extensions', 'largefiles', '')
847 847
848 848 # patch and reset hooks section of UI config to not run any
849 849 # hooks on creating remote repo
850 850 config.clear_section('hooks')
851 851
852 852 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
853 853 if repo_type == 'git':
854 854 repo = backend(
855 855 repo_path, config=config, create=True, src_url=clone_uri,
856 856 bare=True)
857 857 else:
858 858 repo = backend(
859 859 repo_path, config=config, create=True, src_url=clone_uri)
860 860
861 861 repo.install_hooks()
862 862
863 863 log.debug('Created repo %s with %s backend',
864 864 safe_unicode(repo_name), safe_unicode(repo_type))
865 865 return repo
866 866
867 867 def _rename_filesystem_repo(self, old, new):
868 868 """
869 869 renames repository on filesystem
870 870
871 871 :param old: old name
872 872 :param new: new name
873 873 """
874 874 log.info('renaming repo from %s to %s', old, new)
875 875
876 876 old_path = os.path.join(self.repos_path, old)
877 877 new_path = os.path.join(self.repos_path, new)
878 878 if os.path.isdir(new_path):
879 879 raise Exception(
880 880 'Was trying to rename to already existing dir %s' % new_path
881 881 )
882 882 shutil.move(old_path, new_path)
883 883
884 884 def _delete_filesystem_repo(self, repo):
885 885 """
886 886 removes repo from filesystem, the removal is acctually made by
887 887 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
888 888 repository is no longer valid for rhodecode, can be undeleted later on
889 889 by reverting the renames on this repository
890 890
891 891 :param repo: repo object
892 892 """
893 893 rm_path = os.path.join(self.repos_path, repo.repo_name)
894 894 repo_group = repo.group
895 895 log.info("Removing repository %s", rm_path)
896 896 # disable hg/git internal that it doesn't get detected as repo
897 897 alias = repo.repo_type
898 898
899 899 config = make_db_config(clear_session=False)
900 900 config.set('extensions', 'largefiles', '')
901 901 bare = getattr(repo.scm_instance(config=config), 'bare', False)
902 902
903 903 # skip this for bare git repos
904 904 if not bare:
905 905 # disable VCS repo
906 906 vcs_path = os.path.join(rm_path, '.%s' % alias)
907 907 if os.path.exists(vcs_path):
908 908 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
909 909
910 910 _now = datetime.datetime.now()
911 911 _ms = str(_now.microsecond).rjust(6, '0')
912 912 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
913 913 repo.just_name)
914 914 if repo_group:
915 915 # if repository is in group, prefix the removal path with the group
916 916 args = repo_group.full_path_splitted + [_d]
917 917 _d = os.path.join(*args)
918 918
919 919 if os.path.isdir(rm_path):
920 920 shutil.move(rm_path, os.path.join(self.repos_path, _d))
921 921
922 # finally cleanup diff-cache if it exists
923 cached_diffs_dir = repo.cached_diffs_dir
924 if os.path.isdir(cached_diffs_dir):
925 shutil.rmtree(cached_diffs_dir)
926
922 927
923 928 class ReadmeFinder:
924 929 """
925 930 Utility which knows how to find a readme for a specific commit.
926 931
927 932 The main idea is that this is a configurable algorithm. When creating an
928 933 instance you can define parameters, currently only the `default_renderer`.
929 934 Based on this configuration the method :meth:`search` behaves slightly
930 935 different.
931 936 """
932 937
933 938 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
934 939 path_re = re.compile(r'^docs?', re.IGNORECASE)
935 940
936 941 default_priorities = {
937 942 None: 0,
938 943 '.text': 2,
939 944 '.txt': 3,
940 945 '.rst': 1,
941 946 '.rest': 2,
942 947 '.md': 1,
943 948 '.mkdn': 2,
944 949 '.mdown': 3,
945 950 '.markdown': 4,
946 951 }
947 952
948 953 path_priority = {
949 954 'doc': 0,
950 955 'docs': 1,
951 956 }
952 957
953 958 FALLBACK_PRIORITY = 99
954 959
955 960 RENDERER_TO_EXTENSION = {
956 961 'rst': ['.rst', '.rest'],
957 962 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
958 963 }
959 964
960 965 def __init__(self, default_renderer=None):
961 966 self._default_renderer = default_renderer
962 967 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
963 968 default_renderer, [])
964 969
965 970 def search(self, commit, path='/'):
966 971 """
967 972 Find a readme in the given `commit`.
968 973 """
969 974 nodes = commit.get_nodes(path)
970 975 matches = self._match_readmes(nodes)
971 976 matches = self._sort_according_to_priority(matches)
972 977 if matches:
973 978 return matches[0].node
974 979
975 980 paths = self._match_paths(nodes)
976 981 paths = self._sort_paths_according_to_priority(paths)
977 982 for path in paths:
978 983 match = self.search(commit, path=path)
979 984 if match:
980 985 return match
981 986
982 987 return None
983 988
984 989 def _match_readmes(self, nodes):
985 990 for node in nodes:
986 991 if not node.is_file():
987 992 continue
988 993 path = node.path.rsplit('/', 1)[-1]
989 994 match = self.readme_re.match(path)
990 995 if match:
991 996 extension = match.group(1)
992 997 yield ReadmeMatch(node, match, self._priority(extension))
993 998
994 999 def _match_paths(self, nodes):
995 1000 for node in nodes:
996 1001 if not node.is_dir():
997 1002 continue
998 1003 match = self.path_re.match(node.path)
999 1004 if match:
1000 1005 yield node.path
1001 1006
1002 1007 def _priority(self, extension):
1003 1008 renderer_priority = (
1004 1009 0 if extension in self._renderer_extensions else 1)
1005 1010 extension_priority = self.default_priorities.get(
1006 1011 extension, self.FALLBACK_PRIORITY)
1007 1012 return (renderer_priority, extension_priority)
1008 1013
1009 1014 def _sort_according_to_priority(self, matches):
1010 1015
1011 1016 def priority_and_path(match):
1012 1017 return (match.priority, match.path)
1013 1018
1014 1019 return sorted(matches, key=priority_and_path)
1015 1020
1016 1021 def _sort_paths_according_to_priority(self, paths):
1017 1022
1018 1023 def priority_and_path(path):
1019 1024 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1020 1025
1021 1026 return sorted(paths, key=priority_and_path)
1022 1027
1023 1028
1024 1029 class ReadmeMatch:
1025 1030
1026 1031 def __init__(self, node, match, priority):
1027 1032 self.node = node
1028 1033 self._match = match
1029 1034 self.priority = priority
1030 1035
1031 1036 @property
1032 1037 def path(self):
1033 1038 return self.node.path
1034 1039
1035 1040 def __repr__(self):
1036 1041 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
General Comments 0
You need to be logged in to leave comments. Login now