repo.py
411 lines
| 14.7 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 | ||||
r1203 | :copyright: (C) 2009-2011 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 | ||||
r1045 | from sqlalchemy.orm import joinedload, make_transient | |||
r1036 | ||||
from vcs.utils.lazy import LazyProperty | ||||
from vcs.backends import get_backend | ||||
r1401 | from rhodecode.lib import safe_str | |||
r786 | from rhodecode.model import BaseModel | |||
from rhodecode.model.caching_query import FromCache | ||||
r708 | from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \ | |||
r1323 | Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, Group | |||
r629 | from rhodecode.model.user import UserModel | |||
r786 | ||||
r629 | log = logging.getLogger(__name__) | |||
r1271 | ||||
r752 | class RepoModel(BaseModel): | |||
r629 | ||||
r1036 | @LazyProperty | |||
def repos_path(self): | ||||
"""Get's the repositories root path from database | ||||
""" | ||||
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): | |||
""" | ||||
Get's information about repository, and returns a dict for | ||||
usage in forms | ||||
: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': | ||||
r2p = self.sa.query(RepoToPerm)\ | ||||
r1530 | .filter(RepoToPerm.user == User.get_by_username(member))\ | |||
r1014 | .filter(RepoToPerm.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': | ||||
r2p = RepoToPerm() | ||||
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': | |||
r1594 | cur_repo.group = Group.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 | ||||
self.sa.commit() | ||||
r1539 | return cur_repo | |||
r629 | except: | |||
log.error(traceback.format_exc()) | ||||
self.sa.rollback() | ||||
raise | ||||
def create(self, form_data, cur_user, just_db=False, fork=False): | ||||
r1366 | ||||
r629 | try: | |||
if fork: | ||||
r1401 | repo_name = form_data['fork_name'] | |||
org_name = form_data['repo_name'] | ||||
org_full_name = org_name | ||||
r629 | ||||
else: | ||||
r1401 | org_name = repo_name = form_data['repo_name'] | |||
r1361 | repo_name_full = form_data['repo_name_full'] | |||
r629 | new_repo = Repository() | |||
r1075 | new_repo.enable_statistics = False | |||
r629 | for k, v in form_data.items(): | |||
if k == 'repo_name': | ||||
r1366 | if fork: | |||
v = repo_name | ||||
else: | ||||
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: | ||||
parent_repo = self.sa.query(Repository)\ | ||||
r1366 | .filter(Repository.repo_name == org_full_name).one() | |||
r629 | new_repo.fork = parent_repo | |||
new_repo.user_id = cur_user.user_id | ||||
self.sa.add(new_repo) | ||||
#create default permission | ||||
repo_to_perm = RepoToPerm() | ||||
default = 'repository.read' | ||||
r1530 | for p in User.get_by_username('default').user_perms: | |||
r629 | 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 | ||||
r1028 | repo_to_perm.repository = new_repo | |||
r1530 | repo_to_perm.user_id = User.get_by_username('default').user_id | |||
r629 | ||||
self.sa.add(repo_to_perm) | ||||
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 | ||||
r629 | self.sa.commit() | |||
r786 | ||||
#now automatically start following this repository as owner | ||||
from rhodecode.model.scm import ScmModel | ||||
ScmModel(self.sa).toggle_following_repo(new_repo.repo_id, | ||||
cur_user.user_id) | ||||
r1539 | return new_repo | |||
r629 | except: | |||
log.error(traceback.format_exc()) | ||||
self.sa.rollback() | ||||
raise | ||||
def create_fork(self, form_data, cur_user): | ||||
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 | self.sa.commit() | |||
except: | ||||
log.error(traceback.format_exc()) | ||||
self.sa.rollback() | ||||
raise | ||||
def delete_perm_user(self, form_data, repo_name): | ||||
try: | ||||
self.sa.query(RepoToPerm)\ | ||||
r735 | .filter(RepoToPerm.repository \ | |||
== self.get_by_repo_name(repo_name))\ | ||||
r629 | .filter(RepoToPerm.user_id == form_data['user_id']).delete() | |||
self.sa.commit() | ||||
except: | ||||
log.error(traceback.format_exc()) | ||||
self.sa.rollback() | ||||
raise | ||||
r1015 | def delete_perm_users_group(self, form_data, repo_name): | |||
try: | ||||
r1271 | self.sa.query(UsersGroupRepoToPerm)\ | |||
.filter(UsersGroupRepoToPerm.repository \ | ||||
r1015 | == self.get_by_repo_name(repo_name))\ | |||
r1271 | .filter(UsersGroupRepoToPerm.users_group_id \ | |||
r1015 | == form_data['users_group_id']).delete() | |||
self.sa.commit() | ||||
except: | ||||
log.error(traceback.format_exc()) | ||||
self.sa.rollback() | ||||
raise | ||||
r708 | def delete_stats(self, repo_name): | |||
try: | ||||
self.sa.query(Statistics)\ | ||||
r735 | .filter(Statistics.repository == \ | |||
self.get_by_repo_name(repo_name)).delete() | ||||
r708 | self.sa.commit() | |||
except: | ||||
log.error(traceback.format_exc()) | ||||
self.sa.rollback() | ||||
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: | |||
paths = Group.get(new_parent_id).full_path.split(Group.url_sep()) | ||||
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) | |||
r668 | #disable hg/git | |||
alias = repo.repo_type | ||||
shutil.move(os.path.join(rm_path, '.%s' % alias), | ||||
os.path.join(rm_path, 'rm__.%s' % alias)) | ||||
r629 | #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))) | ||||
r1594 | ||||