repo.py
371 lines
| 13.6 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 | ||||
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 | def get_full(self, repo_name, cache=False, invalidate=False): | |||
repo = self.sa.query(Repository)\ | ||||
.options(joinedload(Repository.fork))\ | ||||
.options(joinedload(Repository.user))\ | ||||
r1351 | .options(joinedload(Repository.group))\ | |||
r1036 | .filter(Repository.repo_name == repo_name)\ | |||
if cache: | ||||
repo = repo.options(FromCache("sql_cache_long", | ||||
"get_repo_full_%s" % repo_name)) | ||||
r1038 | if invalidate and cache: | |||
r1036 | repo.invalidate() | |||
r1039 | ret = repo.scalar() | |||
r1045 | ||||
#make transient for sake of errors | ||||
make_transient(ret) | ||||
r1351 | for k in ['fork', 'user', 'group']: | |||
r1045 | attr = getattr(ret, k, False) | |||
if attr: | ||||
make_transient(attr) | ||||
r1039 | return ret | |||
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 | ||||
r629 | def update(self, repo_name, form_data): | |||
try: | ||||
r822 | cur_repo = self.get_by_repo_name(repo_name, cache=False) | |||
user_model = UserModel(self.sa) | ||||
r629 | ||||
#update permissions | ||||
r1014 | for member, perm, member_type in form_data['perms_updates']: | |||
if member_type == 'user': | ||||
r2p = self.sa.query(RepoToPerm)\ | ||||
r1271 | .filter(RepoToPerm.user == user_model. | |||
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 | ||||
#set new permissions | ||||
r1014 | for member, perm, member_type in form_data['perms_new']: | |||
if member_type == 'user': | ||||
r2p = RepoToPerm() | ||||
r2p.repository = cur_repo | ||||
r2p.user = user_model.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 | ||||
#update current repo | ||||
for k, v in form_data.items(): | ||||
if k == 'user': | ||||
r1323 | cur_repo.user = user_model.get_by_username(v) | |||
elif k == 'repo_name': | ||||
cur_repo.repo_name = form_data['repo_name_full'] | ||||
elif k == 'repo_group' and v: | ||||
cur_repo.group_id = v | ||||
r629 | else: | |||
setattr(cur_repo, k, v) | ||||
self.sa.add(cur_repo) | ||||
r1323 | if repo_name != form_data['repo_name_full']: | |||
# rename repository | ||||
self.__rename_repo(old=repo_name, | ||||
new=form_data['repo_name_full']) | ||||
r629 | ||||
self.sa.commit() | ||||
except: | ||||
log.error(traceback.format_exc()) | ||||
self.sa.rollback() | ||||
raise | ||||
def create(self, form_data, cur_user, just_db=False, fork=False): | ||||
try: | ||||
if fork: | ||||
r651 | #force str since hg doesn't go with unicode | |||
r629 | repo_name = str(form_data['fork_name']) | |||
org_name = str(form_data['repo_name']) | ||||
else: | ||||
org_name = repo_name = str(form_data['repo_name']) | ||||
new_repo = Repository() | ||||
r1075 | new_repo.enable_statistics = False | |||
r629 | for k, v in form_data.items(): | |||
if k == 'repo_name': | ||||
v = repo_name | ||||
r1350 | if k == 'repo_group': | |||
k = 'group_id' | ||||
r629 | setattr(new_repo, k, v) | |||
if fork: | ||||
parent_repo = self.sa.query(Repository)\ | ||||
.filter(Repository.repo_name == org_name).scalar() | ||||
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' | ||||
r1119 | for p in UserModel(self.sa).get_by_username('default', | |||
cache=False).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 | |||
r752 | repo_to_perm.user_id = UserModel(self.sa)\ | |||
.get_by_username('default', cache=False).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) | ||||
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 | """ | |||
r1350 | makes repository on filesystem it's group aware | |||
r1203 | ||||
r787 | :param repo_name: | |||
:param alias: | ||||
r1350 | :param parent_id: | |||
:param clone_uri: | ||||
r787 | """ | |||
r629 | from rhodecode.lib.utils import check_repo | |||
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 = '' | ||||
repo_path = os.path.join(self.repos_path, new_parent_path, repo_name) | ||||
r1036 | if check_repo(repo_name, self.repos_path): | |||
r1112 | log.info('creating repo %s in %s @ %s', repo_name, repo_path, | |||
clone_uri) | ||||
r659 | backend = get_backend(alias) | |||
r1112 | backend(repo_path, create=True, src_url=clone_uri) | |||
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): | |||
raise Exception('Was trying to rename to already existing dir %s', | ||||
new_path) | ||||
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))) | ||||