repo.py
429 lines
| 15.5 KiB
| text/x-python
|
PythonLexer
r786 | # -*- coding: utf-8 -*- | |||
""" | ||||
r811 | rhodecode.model.repo | |||
~~~~~~~~~~~~~~~~~~~~ | ||||
r786 | ||||
Repository model for rhodecode | ||||
r1203 | ||||
r786 | :created_on: Jun 5, 2010 | |||
:author: marcink | ||||
r1824 | :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com> | |||
r786 | :license: GPLv3, see COPYING for more details. | |||
""" | ||||
r1206 | # This program is free software: you can redistribute it and/or modify | |||
# it under the terms of the GNU General Public License as published by | ||||
# the Free Software Foundation, either version 3 of the License, or | ||||
# (at your option) any later version. | ||||
r1203 | # | |||
r629 | # This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
# GNU General Public License for more details. | ||||
r1203 | # | |||
r629 | # You should have received a copy of the GNU General Public License | |||
r1206 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
r786 | import os | |||
import shutil | ||||
import logging | ||||
import traceback | ||||
r629 | from datetime import datetime | |||
r786 | ||||
r1036 | from vcs.utils.lazy import LazyProperty | |||
from vcs.backends import get_backend | ||||
r1401 | from rhodecode.lib import safe_str | |||
r1669 | from rhodecode.lib.caching_query import FromCache | |||
r1401 | ||||
r786 | from rhodecode.model import BaseModel | |||
r1633 | from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \ | |||
Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup | ||||
r786 | ||||
r629 | log = logging.getLogger(__name__) | |||
r1271 | ||||
r752 | class RepoModel(BaseModel): | |||
r629 | ||||
r1036 | @LazyProperty | |||
def repos_path(self): | ||||
r1716 | """ | |||
Get's the repositories root path from database | ||||
r1036 | """ | |||
r1017 | ||||
r1036 | q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one() | |||
return q.ui_value | ||||
r1013 | ||||
r629 | def get(self, repo_id, cache=False): | |||
repo = self.sa.query(Repository)\ | ||||
r735 | .filter(Repository.repo_id == repo_id) | |||
r629 | ||||
if cache: | ||||
repo = repo.options(FromCache("sql_cache_short", | ||||
r735 | "get_repo_%s" % repo_id)) | |||
r629 | return repo.scalar() | |||
r735 | def get_by_repo_name(self, repo_name, cache=False): | |||
repo = self.sa.query(Repository)\ | ||||
.filter(Repository.repo_name == repo_name) | ||||
if cache: | ||||
repo = repo.options(FromCache("sql_cache_short", | ||||
"get_repo_%s" % repo_name)) | ||||
r752 | return repo.scalar() | |||
r735 | ||||
r1036 | ||||
r629 | def get_users_js(self): | |||
users = self.sa.query(User).filter(User.active == True).all() | ||||
u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},''' | ||||
r1013 | users_array = '[%s]' % '\n'.join([u_tmpl % (u.user_id, u.name, | |||
r629 | u.lastname, u.username) | |||
for u in users]) | ||||
return users_array | ||||
r1013 | def get_users_groups_js(self): | |||
users_groups = self.sa.query(UsersGroup)\ | ||||
.filter(UsersGroup.users_group_active == True).all() | ||||
g_tmpl = '''{id:%s, grname:"%s",grmembers:"%s"},''' | ||||
users_groups_array = '[%s]' % '\n'.join([g_tmpl % \ | ||||
(gr.users_group_id, gr.users_group_name, | ||||
len(gr.members)) | ||||
for gr in users_groups]) | ||||
return users_groups_array | ||||
r1594 | def _get_defaults(self, repo_name): | |||
""" | ||||
r1818 | Get's information about repository, and returns a dict for | |||
r1594 | usage in forms | |||
r1818 | ||||
r1594 | :param repo_name: | |||
""" | ||||
repo_info = Repository.get_by_repo_name(repo_name) | ||||
if repo_info is None: | ||||
return None | ||||
defaults = repo_info.get_dict() | ||||
group, repo_name = repo_info.groups_and_repo | ||||
defaults['repo_name'] = repo_name | ||||
defaults['repo_group'] = getattr(group[-1] if group else None, | ||||
'group_id', None) | ||||
# fill owner | ||||
if repo_info.user: | ||||
defaults.update({'user': repo_info.user.username}) | ||||
else: | ||||
replacement_user = User.query().filter(User.admin == | ||||
True).first().username | ||||
defaults.update({'user': replacement_user}) | ||||
# fill repository users | ||||
for p in repo_info.repo_to_perm: | ||||
defaults.update({'u_perm_%s' % p.user.username: | ||||
p.permission.permission_name}) | ||||
# fill repository groups | ||||
for p in repo_info.users_group_to_perm: | ||||
defaults.update({'g_perm_%s' % p.users_group.users_group_name: | ||||
p.permission.permission_name}) | ||||
return defaults | ||||
r629 | def update(self, repo_name, form_data): | |||
try: | ||||
r822 | cur_repo = self.get_by_repo_name(repo_name, cache=False) | |||
r629 | ||||
r1539 | # update permissions | |||
r1014 | for member, perm, member_type in form_data['perms_updates']: | |||
if member_type == 'user': | ||||
r1716 | _member = User.get_by_username(member) | |||
r1633 | r2p = self.sa.query(UserRepoToPerm)\ | |||
r1716 | .filter(UserRepoToPerm.user == _member)\ | |||
.filter(UserRepoToPerm.repository == cur_repo)\ | ||||
.one() | ||||
r629 | ||||
r1014 | r2p.permission = self.sa.query(Permission)\ | |||
r1271 | .filter(Permission.permission_name == | |||
perm).scalar() | ||||
r1014 | self.sa.add(r2p) | |||
else: | ||||
r1271 | g2p = self.sa.query(UsersGroupRepoToPerm)\ | |||
.filter(UsersGroupRepoToPerm.users_group == | ||||
UsersGroup.get_by_group_name(member))\ | ||||
.filter(UsersGroupRepoToPerm.repository == | ||||
cur_repo).one() | ||||
r1014 | ||||
g2p.permission = self.sa.query(Permission)\ | ||||
r1271 | .filter(Permission.permission_name == | |||
perm).scalar() | ||||
r1014 | self.sa.add(g2p) | |||
r629 | ||||
r1539 | # set new permissions | |||
r1014 | for member, perm, member_type in form_data['perms_new']: | |||
if member_type == 'user': | ||||
r1633 | r2p = UserRepoToPerm() | |||
r1014 | r2p.repository = cur_repo | |||
r1530 | r2p.user = User.get_by_username(member) | |||
r629 | ||||
r1014 | r2p.permission = self.sa.query(Permission)\ | |||
r1271 | .filter(Permission. | |||
permission_name == perm)\ | ||||
.scalar() | ||||
r1014 | self.sa.add(r2p) | |||
else: | ||||
r1271 | g2p = UsersGroupRepoToPerm() | |||
r1014 | g2p.repository = cur_repo | |||
r1271 | g2p.users_group = UsersGroup.get_by_group_name(member) | |||
r1014 | g2p.permission = self.sa.query(Permission)\ | |||
r1271 | .filter(Permission. | |||
permission_name == perm)\ | ||||
.scalar() | ||||
r1014 | self.sa.add(g2p) | |||
r629 | ||||
r1539 | # update current repo | |||
r629 | for k, v in form_data.items(): | |||
if k == 'user': | ||||
r1530 | cur_repo.user = User.get_by_username(v) | |||
r1323 | elif k == 'repo_name': | |||
r1539 | pass | |||
r1379 | elif k == 'repo_group': | |||
r1633 | cur_repo.group = RepoGroup.get(v) | |||
r1323 | ||||
r629 | else: | |||
setattr(cur_repo, k, v) | ||||
r1539 | new_name = cur_repo.get_new_name(form_data['repo_name']) | |||
cur_repo.repo_name = new_name | ||||
r629 | self.sa.add(cur_repo) | |||
r1539 | if repo_name != new_name: | |||
r1323 | # rename repository | |||
r1539 | self.__rename_repo(old=repo_name, new=new_name) | |||
r629 | ||||
r1539 | return cur_repo | |||
r629 | except: | |||
log.error(traceback.format_exc()) | ||||
raise | ||||
def create(self, form_data, cur_user, just_db=False, fork=False): | ||||
r1722 | from rhodecode.model.scm import ScmModel | |||
r1366 | ||||
r629 | try: | |||
if fork: | ||||
r1722 | fork_parent_id = form_data['fork_parent_id'] | |||
r629 | ||||
r1722 | # repo name is just a name of repository | |||
# while repo_name_full is a full qualified name that is combined | ||||
# with name and path of group | ||||
repo_name = form_data['repo_name'] | ||||
repo_name_full = form_data['repo_name_full'] | ||||
r1361 | ||||
r629 | new_repo = Repository() | |||
r1075 | new_repo.enable_statistics = False | |||
r1722 | ||||
r629 | for k, v in form_data.items(): | |||
if k == 'repo_name': | ||||
r1722 | v = repo_name_full | |||
r1350 | if k == 'repo_group': | |||
k = 'group_id' | ||||
r1517 | if k == 'description': | |||
v = v or repo_name | ||||
r629 | setattr(new_repo, k, v) | |||
if fork: | ||||
r1722 | parent_repo = Repository.get(fork_parent_id) | |||
r629 | new_repo.fork = parent_repo | |||
new_repo.user_id = cur_user.user_id | ||||
self.sa.add(new_repo) | ||||
r1729 | def _create_default_perms(): | |||
# create default permission | ||||
repo_to_perm = UserRepoToPerm() | ||||
default = 'repository.read' | ||||
for p in User.get_by_username('default').user_perms: | ||||
if p.permission.permission_name.startswith('repository.'): | ||||
default = p.permission.permission_name | ||||
break | ||||
default_perm = 'repository.none' if form_data['private'] else default | ||||
repo_to_perm.permission_id = self.sa.query(Permission)\ | ||||
.filter(Permission.permission_name == default_perm)\ | ||||
.one().permission_id | ||||
repo_to_perm.repository = new_repo | ||||
repo_to_perm.user_id = User.get_by_username('default').user_id | ||||
self.sa.add(repo_to_perm) | ||||
r629 | ||||
r1729 | ||||
if fork: | ||||
if form_data.get('copy_permissions'): | ||||
repo = Repository.get(fork_parent_id) | ||||
user_perms = UserRepoToPerm.query()\ | ||||
.filter(UserRepoToPerm.repository == repo).all() | ||||
group_perms = UsersGroupRepoToPerm.query()\ | ||||
.filter(UsersGroupRepoToPerm.repository == repo).all() | ||||
r629 | ||||
r1729 | for perm in user_perms: | |||
UserRepoToPerm.create(perm.user, new_repo, | ||||
perm.permission) | ||||
r629 | ||||
r1729 | for perm in group_perms: | |||
UsersGroupRepoToPerm.create(perm.users_group, new_repo, | ||||
perm.permission) | ||||
else: | ||||
_create_default_perms() | ||||
else: | ||||
_create_default_perms() | ||||
r1028 | ||||
if not just_db: | ||||
r1112 | self.__create_repo(repo_name, form_data['repo_type'], | |||
r1350 | form_data['repo_group'], | |||
r1112 | form_data['clone_uri']) | |||
r1028 | ||||
r1722 | # now automatically start following this repository as owner | |||
r786 | ScmModel(self.sa).toggle_following_repo(new_repo.repo_id, | |||
r1722 | cur_user.user_id) | |||
r1539 | return new_repo | |||
r629 | except: | |||
log.error(traceback.format_exc()) | ||||
raise | ||||
def create_fork(self, form_data, cur_user): | ||||
r1722 | """ | |||
Simple wrapper into executing celery task for fork creation | ||||
r1818 | ||||
r1722 | :param form_data: | |||
:param cur_user: | ||||
""" | ||||
r629 | from rhodecode.lib.celerylib import tasks, run_task | |||
run_task(tasks.create_repo_fork, form_data, cur_user) | ||||
def delete(self, repo): | ||||
try: | ||||
self.sa.delete(repo) | ||||
r668 | self.__delete_repo(repo) | |||
r629 | except: | |||
log.error(traceback.format_exc()) | ||||
raise | ||||
def delete_perm_user(self, form_data, repo_name): | ||||
try: | ||||
r1713 | obj = self.sa.query(UserRepoToPerm)\ | |||
r1633 | .filter(UserRepoToPerm.repository \ | |||
r735 | == self.get_by_repo_name(repo_name))\ | |||
r1713 | .filter(UserRepoToPerm.user_id == form_data['user_id']).one() | |||
self.sa.delete(obj) | ||||
r629 | except: | |||
log.error(traceback.format_exc()) | ||||
raise | ||||
r1015 | def delete_perm_users_group(self, form_data, repo_name): | |||
try: | ||||
r1713 | obj = self.sa.query(UsersGroupRepoToPerm)\ | |||
r1271 | .filter(UsersGroupRepoToPerm.repository \ | |||
r1015 | == self.get_by_repo_name(repo_name))\ | |||
r1716 | .filter(UsersGroupRepoToPerm.users_group_id | |||
== form_data['users_group_id']).one() | ||||
r1713 | self.sa.delete(obj) | |||
r1015 | except: | |||
log.error(traceback.format_exc()) | ||||
raise | ||||
r708 | def delete_stats(self, repo_name): | |||
r1722 | """ | |||
removes stats for given repo | ||||
r1818 | ||||
r1722 | :param repo_name: | |||
""" | ||||
r708 | try: | |||
r1713 | obj = self.sa.query(Statistics)\ | |||
.filter(Statistics.repository == \ | ||||
self.get_by_repo_name(repo_name)).one() | ||||
self.sa.delete(obj) | ||||
r708 | except: | |||
log.error(traceback.format_exc()) | ||||
raise | ||||
r1350 | def __create_repo(self, repo_name, alias, new_parent_id, clone_uri=False): | |||
r787 | """ | |||
r1361 | makes repository on filesystem. It's group aware means it'll create | |||
a repository within a group, and alter the paths accordingly of | ||||
group location | ||||
r1203 | ||||
r787 | :param repo_name: | |||
:param alias: | ||||
r1350 | :param parent_id: | |||
:param clone_uri: | ||||
r787 | """ | |||
r1594 | from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group | |||
r1517 | ||||
r1350 | if new_parent_id: | |||
r1716 | paths = RepoGroup.get(new_parent_id)\ | |||
.full_path.split(RepoGroup.url_sep()) | ||||
r1350 | new_parent_path = os.sep.join(paths) | |||
else: | ||||
new_parent_path = '' | ||||
r1401 | repo_path = os.path.join(*map(lambda x:safe_str(x), | |||
[self.repos_path, new_parent_path, repo_name])) | ||||
r1350 | ||||
r1594 | ||||
r1550 | # check if this path is not a repository | |||
if is_valid_repo(repo_path, self.repos_path): | ||||
raise Exception('This path %s is a valid repository' % repo_path) | ||||
r1401 | ||||
r1550 | # check if this path is a group | |||
if is_valid_repos_group(repo_path, self.repos_path): | ||||
raise Exception('This path %s is a valid group' % repo_path) | ||||
r1594 | ||||
r1550 | log.info('creating repo %s in %s @ %s', repo_name, repo_path, | |||
clone_uri) | ||||
backend = get_backend(alias) | ||||
backend(repo_path, create=True, src_url=clone_uri) | ||||
r629 | ||||
r1401 | ||||
r629 | def __rename_repo(self, old, new): | |||
r787 | """ | |||
renames repository on filesystem | ||||
r1203 | ||||
r787 | :param old: old name | |||
:param new: new name | ||||
""" | ||||
r629 | log.info('renaming repo from %s to %s', old, new) | |||
r1036 | old_path = os.path.join(self.repos_path, old) | |||
new_path = os.path.join(self.repos_path, new) | ||||
r629 | if os.path.isdir(new_path): | |||
r1492 | raise Exception('Was trying to rename to already existing dir %s' \ | |||
% new_path) | ||||
r629 | shutil.move(old_path, new_path) | |||
r668 | def __delete_repo(self, repo): | |||
r787 | """ | |||
removes repo from filesystem, the removal is acctually made by | ||||
added rm__ prefix into dir, and rename internat .hg/.git dirs so this | ||||
repository is no longer valid for rhodecode, can be undeleted later on | ||||
by reverting the renames on this repository | ||||
r1203 | ||||
r787 | :param repo: repo object | |||
""" | ||||
r1036 | rm_path = os.path.join(self.repos_path, repo.repo_name) | |||
r629 | log.info("Removing %s", rm_path) | |||
r1749 | # disable hg/git | |||
r668 | alias = repo.repo_type | |||
shutil.move(os.path.join(rm_path, '.%s' % alias), | ||||
os.path.join(rm_path, 'rm__.%s' % alias)) | ||||
r1749 | # disable repo | |||
r1036 | shutil.move(rm_path, os.path.join(self.repos_path, 'rm__%s__%s' \ | |||
r1119 | % (datetime.today()\ | |||
.strftime('%Y%m%d_%H%M%S_%f'), | ||||
repo.repo_name))) | ||||