##// END OF EJS Templates
repo rescann should detach forks of zombie repos,...
marcink -
r3694:34093903 beta
parent child Browse files
Show More
@@ -1,779 +1,779 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import re
27 import re
28 import logging
28 import logging
29 import datetime
29 import datetime
30 import traceback
30 import traceback
31 import paste
31 import paste
32 import beaker
32 import beaker
33 import tarfile
33 import tarfile
34 import shutil
34 import shutil
35 import decorator
35 import decorator
36 import warnings
36 import warnings
37 from os.path import abspath
37 from os.path import abspath
38 from os.path import dirname as dn, join as jn
38 from os.path import dirname as dn, join as jn
39
39
40 from paste.script.command import Command, BadCommand
40 from paste.script.command import Command, BadCommand
41
41
42 from mercurial import ui, config
42 from mercurial import ui, config
43
43
44 from webhelpers.text import collapse, remove_formatting, strip_tags
44 from webhelpers.text import collapse, remove_formatting, strip_tags
45
45
46 from rhodecode.lib.vcs import get_backend
46 from rhodecode.lib.vcs import get_backend
47 from rhodecode.lib.vcs.backends.base import BaseChangeset
47 from rhodecode.lib.vcs.backends.base import BaseChangeset
48 from rhodecode.lib.vcs.utils.lazy import LazyProperty
48 from rhodecode.lib.vcs.utils.lazy import LazyProperty
49 from rhodecode.lib.vcs.utils.helpers import get_scm
49 from rhodecode.lib.vcs.utils.helpers import get_scm
50 from rhodecode.lib.vcs.exceptions import VCSError
50 from rhodecode.lib.vcs.exceptions import VCSError
51
51
52 from rhodecode.lib.caching_query import FromCache
52 from rhodecode.lib.caching_query import FromCache
53
53
54 from rhodecode.model import meta
54 from rhodecode.model import meta
55 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
55 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
56 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation
56 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation
57 from rhodecode.model.meta import Session
57 from rhodecode.model.meta import Session
58 from rhodecode.model.repos_group import ReposGroupModel
58 from rhodecode.model.repos_group import ReposGroupModel
59 from rhodecode.lib.utils2 import safe_str, safe_unicode
59 from rhodecode.lib.utils2 import safe_str, safe_unicode
60 from rhodecode.lib.vcs.utils.fakemod import create_module
60 from rhodecode.lib.vcs.utils.fakemod import create_module
61
61
62 log = logging.getLogger(__name__)
62 log = logging.getLogger(__name__)
63
63
64 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
64 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
65
65
66
66
67 def recursive_replace(str_, replace=' '):
67 def recursive_replace(str_, replace=' '):
68 """
68 """
69 Recursive replace of given sign to just one instance
69 Recursive replace of given sign to just one instance
70
70
71 :param str_: given string
71 :param str_: given string
72 :param replace: char to find and replace multiple instances
72 :param replace: char to find and replace multiple instances
73
73
74 Examples::
74 Examples::
75 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
75 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
76 'Mighty-Mighty-Bo-sstones'
76 'Mighty-Mighty-Bo-sstones'
77 """
77 """
78
78
79 if str_.find(replace * 2) == -1:
79 if str_.find(replace * 2) == -1:
80 return str_
80 return str_
81 else:
81 else:
82 str_ = str_.replace(replace * 2, replace)
82 str_ = str_.replace(replace * 2, replace)
83 return recursive_replace(str_, replace)
83 return recursive_replace(str_, replace)
84
84
85
85
86 def repo_name_slug(value):
86 def repo_name_slug(value):
87 """
87 """
88 Return slug of name of repository
88 Return slug of name of repository
89 This function is called on each creation/modification
89 This function is called on each creation/modification
90 of repository to prevent bad names in repo
90 of repository to prevent bad names in repo
91 """
91 """
92
92
93 slug = remove_formatting(value)
93 slug = remove_formatting(value)
94 slug = strip_tags(slug)
94 slug = strip_tags(slug)
95
95
96 for c in """`?=[]\;'"<>,/~!@#$%^&*()+{}|: """:
96 for c in """`?=[]\;'"<>,/~!@#$%^&*()+{}|: """:
97 slug = slug.replace(c, '-')
97 slug = slug.replace(c, '-')
98 slug = recursive_replace(slug, '-')
98 slug = recursive_replace(slug, '-')
99 slug = collapse(slug, '-')
99 slug = collapse(slug, '-')
100 return slug
100 return slug
101
101
102
102
103 def get_repo_slug(request):
103 def get_repo_slug(request):
104 _repo = request.environ['pylons.routes_dict'].get('repo_name')
104 _repo = request.environ['pylons.routes_dict'].get('repo_name')
105 if _repo:
105 if _repo:
106 _repo = _repo.rstrip('/')
106 _repo = _repo.rstrip('/')
107 return _repo
107 return _repo
108
108
109
109
110 def get_repos_group_slug(request):
110 def get_repos_group_slug(request):
111 _group = request.environ['pylons.routes_dict'].get('group_name')
111 _group = request.environ['pylons.routes_dict'].get('group_name')
112 if _group:
112 if _group:
113 _group = _group.rstrip('/')
113 _group = _group.rstrip('/')
114 return _group
114 return _group
115
115
116
116
117 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
117 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
118 """
118 """
119 Action logger for various actions made by users
119 Action logger for various actions made by users
120
120
121 :param user: user that made this action, can be a unique username string or
121 :param user: user that made this action, can be a unique username string or
122 object containing user_id attribute
122 object containing user_id attribute
123 :param action: action to log, should be on of predefined unique actions for
123 :param action: action to log, should be on of predefined unique actions for
124 easy translations
124 easy translations
125 :param repo: string name of repository or object containing repo_id,
125 :param repo: string name of repository or object containing repo_id,
126 that action was made on
126 that action was made on
127 :param ipaddr: optional ip address from what the action was made
127 :param ipaddr: optional ip address from what the action was made
128 :param sa: optional sqlalchemy session
128 :param sa: optional sqlalchemy session
129
129
130 """
130 """
131
131
132 if not sa:
132 if not sa:
133 sa = meta.Session()
133 sa = meta.Session()
134
134
135 try:
135 try:
136 if hasattr(user, 'user_id'):
136 if hasattr(user, 'user_id'):
137 user_obj = User.get(user.user_id)
137 user_obj = User.get(user.user_id)
138 elif isinstance(user, basestring):
138 elif isinstance(user, basestring):
139 user_obj = User.get_by_username(user)
139 user_obj = User.get_by_username(user)
140 else:
140 else:
141 raise Exception('You have to provide a user object or a username')
141 raise Exception('You have to provide a user object or a username')
142
142
143 if hasattr(repo, 'repo_id'):
143 if hasattr(repo, 'repo_id'):
144 repo_obj = Repository.get(repo.repo_id)
144 repo_obj = Repository.get(repo.repo_id)
145 repo_name = repo_obj.repo_name
145 repo_name = repo_obj.repo_name
146 elif isinstance(repo, basestring):
146 elif isinstance(repo, basestring):
147 repo_name = repo.lstrip('/')
147 repo_name = repo.lstrip('/')
148 repo_obj = Repository.get_by_repo_name(repo_name)
148 repo_obj = Repository.get_by_repo_name(repo_name)
149 else:
149 else:
150 repo_obj = None
150 repo_obj = None
151 repo_name = ''
151 repo_name = ''
152
152
153 user_log = UserLog()
153 user_log = UserLog()
154 user_log.user_id = user_obj.user_id
154 user_log.user_id = user_obj.user_id
155 user_log.username = user_obj.username
155 user_log.username = user_obj.username
156 user_log.action = safe_unicode(action)
156 user_log.action = safe_unicode(action)
157
157
158 user_log.repository = repo_obj
158 user_log.repository = repo_obj
159 user_log.repository_name = repo_name
159 user_log.repository_name = repo_name
160
160
161 user_log.action_date = datetime.datetime.now()
161 user_log.action_date = datetime.datetime.now()
162 user_log.user_ip = ipaddr
162 user_log.user_ip = ipaddr
163 sa.add(user_log)
163 sa.add(user_log)
164
164
165 log.info('Logging action:%s on %s by user:%s ip:%s' %
165 log.info('Logging action:%s on %s by user:%s ip:%s' %
166 (action, safe_unicode(repo), user_obj, ipaddr))
166 (action, safe_unicode(repo), user_obj, ipaddr))
167 if commit:
167 if commit:
168 sa.commit()
168 sa.commit()
169 except Exception:
169 except Exception:
170 log.error(traceback.format_exc())
170 log.error(traceback.format_exc())
171 raise
171 raise
172
172
173
173
174 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
174 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
175 """
175 """
176 Scans given path for repos and return (name,(type,path)) tuple
176 Scans given path for repos and return (name,(type,path)) tuple
177
177
178 :param path: path to scan for repositories
178 :param path: path to scan for repositories
179 :param recursive: recursive search and return names with subdirs in front
179 :param recursive: recursive search and return names with subdirs in front
180 """
180 """
181
181
182 # remove ending slash for better results
182 # remove ending slash for better results
183 path = path.rstrip(os.sep)
183 path = path.rstrip(os.sep)
184 log.debug('now scanning in %s location recursive:%s...' % (path, recursive))
184 log.debug('now scanning in %s location recursive:%s...' % (path, recursive))
185
185
186 def _get_repos(p):
186 def _get_repos(p):
187 if not os.access(p, os.W_OK):
187 if not os.access(p, os.W_OK):
188 log.warn('ignoring repo path without write access: %s', p)
188 log.warn('ignoring repo path without write access: %s', p)
189 return
189 return
190 for dirpath in os.listdir(p):
190 for dirpath in os.listdir(p):
191 if os.path.isfile(os.path.join(p, dirpath)):
191 if os.path.isfile(os.path.join(p, dirpath)):
192 continue
192 continue
193 cur_path = os.path.join(p, dirpath)
193 cur_path = os.path.join(p, dirpath)
194
194
195 # skip removed repos
195 # skip removed repos
196 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
196 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
197 continue
197 continue
198
198
199 #skip .<somethin> dirs
199 #skip .<somethin> dirs
200 if dirpath.startswith('.'):
200 if dirpath.startswith('.'):
201 continue
201 continue
202
202
203 try:
203 try:
204 scm_info = get_scm(cur_path)
204 scm_info = get_scm(cur_path)
205 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
205 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
206 except VCSError:
206 except VCSError:
207 if not recursive:
207 if not recursive:
208 continue
208 continue
209 #check if this dir containts other repos for recursive scan
209 #check if this dir containts other repos for recursive scan
210 rec_path = os.path.join(p, dirpath)
210 rec_path = os.path.join(p, dirpath)
211 if os.path.isdir(rec_path):
211 if os.path.isdir(rec_path):
212 for inner_scm in _get_repos(rec_path):
212 for inner_scm in _get_repos(rec_path):
213 yield inner_scm
213 yield inner_scm
214
214
215 return _get_repos(path)
215 return _get_repos(path)
216
216
217
217
218 def is_valid_repo(repo_name, base_path, scm=None):
218 def is_valid_repo(repo_name, base_path, scm=None):
219 """
219 """
220 Returns True if given path is a valid repository False otherwise.
220 Returns True if given path is a valid repository False otherwise.
221 If scm param is given also compare if given scm is the same as expected
221 If scm param is given also compare if given scm is the same as expected
222 from scm parameter
222 from scm parameter
223
223
224 :param repo_name:
224 :param repo_name:
225 :param base_path:
225 :param base_path:
226 :param scm:
226 :param scm:
227
227
228 :return True: if given path is a valid repository
228 :return True: if given path is a valid repository
229 """
229 """
230 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
230 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
231
231
232 try:
232 try:
233 scm_ = get_scm(full_path)
233 scm_ = get_scm(full_path)
234 if scm:
234 if scm:
235 return scm_[0] == scm
235 return scm_[0] == scm
236 return True
236 return True
237 except VCSError:
237 except VCSError:
238 return False
238 return False
239
239
240
240
241 def is_valid_repos_group(repos_group_name, base_path, skip_path_check=False):
241 def is_valid_repos_group(repos_group_name, base_path, skip_path_check=False):
242 """
242 """
243 Returns True if given path is a repository group False otherwise
243 Returns True if given path is a repository group False otherwise
244
244
245 :param repo_name:
245 :param repo_name:
246 :param base_path:
246 :param base_path:
247 """
247 """
248 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
248 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
249
249
250 # check if it's not a repo
250 # check if it's not a repo
251 if is_valid_repo(repos_group_name, base_path):
251 if is_valid_repo(repos_group_name, base_path):
252 return False
252 return False
253
253
254 try:
254 try:
255 # we need to check bare git repos at higher level
255 # we need to check bare git repos at higher level
256 # since we might match branches/hooks/info/objects or possible
256 # since we might match branches/hooks/info/objects or possible
257 # other things inside bare git repo
257 # other things inside bare git repo
258 get_scm(os.path.dirname(full_path))
258 get_scm(os.path.dirname(full_path))
259 return False
259 return False
260 except VCSError:
260 except VCSError:
261 pass
261 pass
262
262
263 # check if it's a valid path
263 # check if it's a valid path
264 if skip_path_check or os.path.isdir(full_path):
264 if skip_path_check or os.path.isdir(full_path):
265 return True
265 return True
266
266
267 return False
267 return False
268
268
269
269
270 def ask_ok(prompt, retries=4, complaint='Yes or no please!'):
270 def ask_ok(prompt, retries=4, complaint='Yes or no please!'):
271 while True:
271 while True:
272 ok = raw_input(prompt)
272 ok = raw_input(prompt)
273 if ok in ('y', 'ye', 'yes'):
273 if ok in ('y', 'ye', 'yes'):
274 return True
274 return True
275 if ok in ('n', 'no', 'nop', 'nope'):
275 if ok in ('n', 'no', 'nop', 'nope'):
276 return False
276 return False
277 retries = retries - 1
277 retries = retries - 1
278 if retries < 0:
278 if retries < 0:
279 raise IOError
279 raise IOError
280 print complaint
280 print complaint
281
281
282 #propagated from mercurial documentation
282 #propagated from mercurial documentation
283 ui_sections = ['alias', 'auth',
283 ui_sections = ['alias', 'auth',
284 'decode/encode', 'defaults',
284 'decode/encode', 'defaults',
285 'diff', 'email',
285 'diff', 'email',
286 'extensions', 'format',
286 'extensions', 'format',
287 'merge-patterns', 'merge-tools',
287 'merge-patterns', 'merge-tools',
288 'hooks', 'http_proxy',
288 'hooks', 'http_proxy',
289 'smtp', 'patch',
289 'smtp', 'patch',
290 'paths', 'profiling',
290 'paths', 'profiling',
291 'server', 'trusted',
291 'server', 'trusted',
292 'ui', 'web', ]
292 'ui', 'web', ]
293
293
294
294
295 def make_ui(read_from='file', path=None, checkpaths=True, clear_session=True):
295 def make_ui(read_from='file', path=None, checkpaths=True, clear_session=True):
296 """
296 """
297 A function that will read python rc files or database
297 A function that will read python rc files or database
298 and make an mercurial ui object from read options
298 and make an mercurial ui object from read options
299
299
300 :param path: path to mercurial config file
300 :param path: path to mercurial config file
301 :param checkpaths: check the path
301 :param checkpaths: check the path
302 :param read_from: read from 'file' or 'db'
302 :param read_from: read from 'file' or 'db'
303 """
303 """
304
304
305 baseui = ui.ui()
305 baseui = ui.ui()
306
306
307 # clean the baseui object
307 # clean the baseui object
308 baseui._ocfg = config.config()
308 baseui._ocfg = config.config()
309 baseui._ucfg = config.config()
309 baseui._ucfg = config.config()
310 baseui._tcfg = config.config()
310 baseui._tcfg = config.config()
311
311
312 if read_from == 'file':
312 if read_from == 'file':
313 if not os.path.isfile(path):
313 if not os.path.isfile(path):
314 log.debug('hgrc file is not present at %s, skipping...' % path)
314 log.debug('hgrc file is not present at %s, skipping...' % path)
315 return False
315 return False
316 log.debug('reading hgrc from %s' % path)
316 log.debug('reading hgrc from %s' % path)
317 cfg = config.config()
317 cfg = config.config()
318 cfg.read(path)
318 cfg.read(path)
319 for section in ui_sections:
319 for section in ui_sections:
320 for k, v in cfg.items(section):
320 for k, v in cfg.items(section):
321 log.debug('settings ui from file: [%s] %s=%s' % (section, k, v))
321 log.debug('settings ui from file: [%s] %s=%s' % (section, k, v))
322 baseui.setconfig(safe_str(section), safe_str(k), safe_str(v))
322 baseui.setconfig(safe_str(section), safe_str(k), safe_str(v))
323
323
324 elif read_from == 'db':
324 elif read_from == 'db':
325 sa = meta.Session()
325 sa = meta.Session()
326 ret = sa.query(RhodeCodeUi)\
326 ret = sa.query(RhodeCodeUi)\
327 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
327 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
328 .all()
328 .all()
329
329
330 hg_ui = ret
330 hg_ui = ret
331 for ui_ in hg_ui:
331 for ui_ in hg_ui:
332 if ui_.ui_active:
332 if ui_.ui_active:
333 log.debug('settings ui from db: [%s] %s=%s', ui_.ui_section,
333 log.debug('settings ui from db: [%s] %s=%s', ui_.ui_section,
334 ui_.ui_key, ui_.ui_value)
334 ui_.ui_key, ui_.ui_value)
335 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
335 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
336 safe_str(ui_.ui_value))
336 safe_str(ui_.ui_value))
337 if ui_.ui_key == 'push_ssl':
337 if ui_.ui_key == 'push_ssl':
338 # force set push_ssl requirement to False, rhodecode
338 # force set push_ssl requirement to False, rhodecode
339 # handles that
339 # handles that
340 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
340 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
341 False)
341 False)
342 if clear_session:
342 if clear_session:
343 meta.Session.remove()
343 meta.Session.remove()
344 return baseui
344 return baseui
345
345
346
346
347 def set_rhodecode_config(config):
347 def set_rhodecode_config(config):
348 """
348 """
349 Updates pylons config with new settings from database
349 Updates pylons config with new settings from database
350
350
351 :param config:
351 :param config:
352 """
352 """
353 hgsettings = RhodeCodeSetting.get_app_settings()
353 hgsettings = RhodeCodeSetting.get_app_settings()
354
354
355 for k, v in hgsettings.items():
355 for k, v in hgsettings.items():
356 config[k] = v
356 config[k] = v
357
357
358
358
359 def map_groups(path):
359 def map_groups(path):
360 """
360 """
361 Given a full path to a repository, create all nested groups that this
361 Given a full path to a repository, create all nested groups that this
362 repo is inside. This function creates parent-child relationships between
362 repo is inside. This function creates parent-child relationships between
363 groups and creates default perms for all new groups.
363 groups and creates default perms for all new groups.
364
364
365 :param paths: full path to repository
365 :param paths: full path to repository
366 """
366 """
367 sa = meta.Session()
367 sa = meta.Session()
368 groups = path.split(Repository.url_sep())
368 groups = path.split(Repository.url_sep())
369 parent = None
369 parent = None
370 group = None
370 group = None
371
371
372 # last element is repo in nested groups structure
372 # last element is repo in nested groups structure
373 groups = groups[:-1]
373 groups = groups[:-1]
374 rgm = ReposGroupModel(sa)
374 rgm = ReposGroupModel(sa)
375 for lvl, group_name in enumerate(groups):
375 for lvl, group_name in enumerate(groups):
376 group_name = '/'.join(groups[:lvl] + [group_name])
376 group_name = '/'.join(groups[:lvl] + [group_name])
377 group = RepoGroup.get_by_group_name(group_name)
377 group = RepoGroup.get_by_group_name(group_name)
378 desc = '%s group' % group_name
378 desc = '%s group' % group_name
379
379
380 # skip folders that are now removed repos
380 # skip folders that are now removed repos
381 if REMOVED_REPO_PAT.match(group_name):
381 if REMOVED_REPO_PAT.match(group_name):
382 break
382 break
383
383
384 if group is None:
384 if group is None:
385 log.debug('creating group level: %s group_name: %s' % (lvl,
385 log.debug('creating group level: %s group_name: %s' % (lvl,
386 group_name))
386 group_name))
387 group = RepoGroup(group_name, parent)
387 group = RepoGroup(group_name, parent)
388 group.group_description = desc
388 group.group_description = desc
389 sa.add(group)
389 sa.add(group)
390 rgm._create_default_perms(group)
390 rgm._create_default_perms(group)
391 sa.flush()
391 sa.flush()
392 parent = group
392 parent = group
393 return group
393 return group
394
394
395
395
396 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
396 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
397 install_git_hook=False):
397 install_git_hook=False):
398 """
398 """
399 maps all repos given in initial_repo_list, non existing repositories
399 maps all repos given in initial_repo_list, non existing repositories
400 are created, if remove_obsolete is True it also check for db entries
400 are created, if remove_obsolete is True it also check for db entries
401 that are not in initial_repo_list and removes them.
401 that are not in initial_repo_list and removes them.
402
402
403 :param initial_repo_list: list of repositories found by scanning methods
403 :param initial_repo_list: list of repositories found by scanning methods
404 :param remove_obsolete: check for obsolete entries in database
404 :param remove_obsolete: check for obsolete entries in database
405 :param install_git_hook: if this is True, also check and install githook
405 :param install_git_hook: if this is True, also check and install githook
406 for a repo if missing
406 for a repo if missing
407 """
407 """
408 from rhodecode.model.repo import RepoModel
408 from rhodecode.model.repo import RepoModel
409 from rhodecode.model.scm import ScmModel
409 from rhodecode.model.scm import ScmModel
410 sa = meta.Session()
410 sa = meta.Session()
411 rm = RepoModel()
411 rm = RepoModel()
412 user = sa.query(User).filter(User.admin == True).first()
412 user = sa.query(User).filter(User.admin == True).first()
413 if user is None:
413 if user is None:
414 raise Exception('Missing administrative account!')
414 raise Exception('Missing administrative account!')
415 added = []
415 added = []
416
416
417 ##creation defaults
417 ##creation defaults
418 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
418 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
419 enable_statistics = defs.get('repo_enable_statistics')
419 enable_statistics = defs.get('repo_enable_statistics')
420 enable_locking = defs.get('repo_enable_locking')
420 enable_locking = defs.get('repo_enable_locking')
421 enable_downloads = defs.get('repo_enable_downloads')
421 enable_downloads = defs.get('repo_enable_downloads')
422 private = defs.get('repo_private')
422 private = defs.get('repo_private')
423
423
424 for name, repo in initial_repo_list.items():
424 for name, repo in initial_repo_list.items():
425 group = map_groups(name)
425 group = map_groups(name)
426 db_repo = rm.get_by_repo_name(name)
426 db_repo = rm.get_by_repo_name(name)
427 # found repo that is on filesystem not in RhodeCode database
427 # found repo that is on filesystem not in RhodeCode database
428 if not db_repo:
428 if not db_repo:
429 log.info('repository %s not found, creating now' % name)
429 log.info('repository %s not found, creating now' % name)
430 added.append(name)
430 added.append(name)
431 desc = (repo.description
431 desc = (repo.description
432 if repo.description != 'unknown'
432 if repo.description != 'unknown'
433 else '%s repository' % name)
433 else '%s repository' % name)
434
434
435 new_repo = rm.create_repo(
435 new_repo = rm.create_repo(
436 repo_name=name,
436 repo_name=name,
437 repo_type=repo.alias,
437 repo_type=repo.alias,
438 description=desc,
438 description=desc,
439 repos_group=getattr(group, 'group_id', None),
439 repos_group=getattr(group, 'group_id', None),
440 owner=user,
440 owner=user,
441 just_db=True,
441 just_db=True,
442 enable_locking=enable_locking,
442 enable_locking=enable_locking,
443 enable_downloads=enable_downloads,
443 enable_downloads=enable_downloads,
444 enable_statistics=enable_statistics,
444 enable_statistics=enable_statistics,
445 private=private
445 private=private
446 )
446 )
447 # we added that repo just now, and make sure it has githook
447 # we added that repo just now, and make sure it has githook
448 # installed
448 # installed
449 if new_repo.repo_type == 'git':
449 if new_repo.repo_type == 'git':
450 ScmModel().install_git_hook(new_repo.scm_instance)
450 ScmModel().install_git_hook(new_repo.scm_instance)
451 new_repo.update_changeset_cache()
451 new_repo.update_changeset_cache()
452 elif install_git_hook:
452 elif install_git_hook:
453 if db_repo.repo_type == 'git':
453 if db_repo.repo_type == 'git':
454 ScmModel().install_git_hook(db_repo.scm_instance)
454 ScmModel().install_git_hook(db_repo.scm_instance)
455 # during starting install all cache keys for all repositories in the
455 # during starting install all cache keys for all repositories in the
456 # system, this will register all repos and multiple instances
456 # system, this will register all repos and multiple instances
457 cache_key = CacheInvalidation._get_cache_key(name)
457 cache_key = CacheInvalidation._get_cache_key(name)
458 log.debug("Creating invalidation cache key for %s: %s", name, cache_key)
458 log.debug("Creating invalidation cache key for %s: %s", name, cache_key)
459 CacheInvalidation.invalidate(name)
459 CacheInvalidation.invalidate(name)
460
460
461 sa.commit()
461 sa.commit()
462 removed = []
462 removed = []
463 if remove_obsolete:
463 if remove_obsolete:
464 # remove from database those repositories that are not in the filesystem
464 # remove from database those repositories that are not in the filesystem
465 for repo in sa.query(Repository).all():
465 for repo in sa.query(Repository).all():
466 if repo.repo_name not in initial_repo_list.keys():
466 if repo.repo_name not in initial_repo_list.keys():
467 log.debug("Removing non-existing repository found in db `%s`" %
467 log.debug("Removing non-existing repository found in db `%s`" %
468 repo.repo_name)
468 repo.repo_name)
469 try:
469 try:
470 sa.delete(repo)
470 removed.append(repo.repo_name)
471 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
471 sa.commit()
472 sa.commit()
472 removed.append(repo.repo_name)
473 except Exception:
473 except Exception:
474 #don't hold further removals on error
474 #don't hold further removals on error
475 log.error(traceback.format_exc())
475 log.error(traceback.format_exc())
476 sa.rollback()
476 sa.rollback()
477 return added, removed
477 return added, removed
478
478
479
479
480 # set cache regions for beaker so celery can utilise it
480 # set cache regions for beaker so celery can utilise it
481 def add_cache(settings):
481 def add_cache(settings):
482 cache_settings = {'regions': None}
482 cache_settings = {'regions': None}
483 for key in settings.keys():
483 for key in settings.keys():
484 for prefix in ['beaker.cache.', 'cache.']:
484 for prefix in ['beaker.cache.', 'cache.']:
485 if key.startswith(prefix):
485 if key.startswith(prefix):
486 name = key.split(prefix)[1].strip()
486 name = key.split(prefix)[1].strip()
487 cache_settings[name] = settings[key].strip()
487 cache_settings[name] = settings[key].strip()
488 if cache_settings['regions']:
488 if cache_settings['regions']:
489 for region in cache_settings['regions'].split(','):
489 for region in cache_settings['regions'].split(','):
490 region = region.strip()
490 region = region.strip()
491 region_settings = {}
491 region_settings = {}
492 for key, value in cache_settings.items():
492 for key, value in cache_settings.items():
493 if key.startswith(region):
493 if key.startswith(region):
494 region_settings[key.split('.')[1]] = value
494 region_settings[key.split('.')[1]] = value
495 region_settings['expire'] = int(region_settings.get('expire',
495 region_settings['expire'] = int(region_settings.get('expire',
496 60))
496 60))
497 region_settings.setdefault('lock_dir',
497 region_settings.setdefault('lock_dir',
498 cache_settings.get('lock_dir'))
498 cache_settings.get('lock_dir'))
499 region_settings.setdefault('data_dir',
499 region_settings.setdefault('data_dir',
500 cache_settings.get('data_dir'))
500 cache_settings.get('data_dir'))
501
501
502 if 'type' not in region_settings:
502 if 'type' not in region_settings:
503 region_settings['type'] = cache_settings.get('type',
503 region_settings['type'] = cache_settings.get('type',
504 'memory')
504 'memory')
505 beaker.cache.cache_regions[region] = region_settings
505 beaker.cache.cache_regions[region] = region_settings
506
506
507
507
508 def load_rcextensions(root_path):
508 def load_rcextensions(root_path):
509 import rhodecode
509 import rhodecode
510 from rhodecode.config import conf
510 from rhodecode.config import conf
511
511
512 path = os.path.join(root_path, 'rcextensions', '__init__.py')
512 path = os.path.join(root_path, 'rcextensions', '__init__.py')
513 if os.path.isfile(path):
513 if os.path.isfile(path):
514 rcext = create_module('rc', path)
514 rcext = create_module('rc', path)
515 EXT = rhodecode.EXTENSIONS = rcext
515 EXT = rhodecode.EXTENSIONS = rcext
516 log.debug('Found rcextensions now loading %s...' % rcext)
516 log.debug('Found rcextensions now loading %s...' % rcext)
517
517
518 # Additional mappings that are not present in the pygments lexers
518 # Additional mappings that are not present in the pygments lexers
519 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
519 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
520
520
521 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
521 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
522
522
523 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
523 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
524 log.debug('settings custom INDEX_EXTENSIONS')
524 log.debug('settings custom INDEX_EXTENSIONS')
525 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
525 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
526
526
527 #ADDITIONAL MAPPINGS
527 #ADDITIONAL MAPPINGS
528 log.debug('adding extra into INDEX_EXTENSIONS')
528 log.debug('adding extra into INDEX_EXTENSIONS')
529 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
529 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
530
530
531 # auto check if the module is not missing any data, set to default if is
531 # auto check if the module is not missing any data, set to default if is
532 # this will help autoupdate new feature of rcext module
532 # this will help autoupdate new feature of rcext module
533 from rhodecode.config import rcextensions
533 from rhodecode.config import rcextensions
534 for k in dir(rcextensions):
534 for k in dir(rcextensions):
535 if not k.startswith('_') and not hasattr(EXT, k):
535 if not k.startswith('_') and not hasattr(EXT, k):
536 setattr(EXT, k, getattr(rcextensions, k))
536 setattr(EXT, k, getattr(rcextensions, k))
537
537
538
538
539 def get_custom_lexer(extension):
539 def get_custom_lexer(extension):
540 """
540 """
541 returns a custom lexer if it's defined in rcextensions module, or None
541 returns a custom lexer if it's defined in rcextensions module, or None
542 if there's no custom lexer defined
542 if there's no custom lexer defined
543 """
543 """
544 import rhodecode
544 import rhodecode
545 from pygments import lexers
545 from pygments import lexers
546 #check if we didn't define this extension as other lexer
546 #check if we didn't define this extension as other lexer
547 if rhodecode.EXTENSIONS and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
547 if rhodecode.EXTENSIONS and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
548 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
548 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
549 return lexers.get_lexer_by_name(_lexer_name)
549 return lexers.get_lexer_by_name(_lexer_name)
550
550
551
551
552 #==============================================================================
552 #==============================================================================
553 # TEST FUNCTIONS AND CREATORS
553 # TEST FUNCTIONS AND CREATORS
554 #==============================================================================
554 #==============================================================================
555 def create_test_index(repo_location, config, full_index):
555 def create_test_index(repo_location, config, full_index):
556 """
556 """
557 Makes default test index
557 Makes default test index
558
558
559 :param config: test config
559 :param config: test config
560 :param full_index:
560 :param full_index:
561 """
561 """
562
562
563 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
563 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
564 from rhodecode.lib.pidlock import DaemonLock, LockHeld
564 from rhodecode.lib.pidlock import DaemonLock, LockHeld
565
565
566 repo_location = repo_location
566 repo_location = repo_location
567
567
568 index_location = os.path.join(config['app_conf']['index_dir'])
568 index_location = os.path.join(config['app_conf']['index_dir'])
569 if not os.path.exists(index_location):
569 if not os.path.exists(index_location):
570 os.makedirs(index_location)
570 os.makedirs(index_location)
571
571
572 try:
572 try:
573 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
573 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
574 WhooshIndexingDaemon(index_location=index_location,
574 WhooshIndexingDaemon(index_location=index_location,
575 repo_location=repo_location)\
575 repo_location=repo_location)\
576 .run(full_index=full_index)
576 .run(full_index=full_index)
577 l.release()
577 l.release()
578 except LockHeld:
578 except LockHeld:
579 pass
579 pass
580
580
581
581
582 def create_test_env(repos_test_path, config):
582 def create_test_env(repos_test_path, config):
583 """
583 """
584 Makes a fresh database and
584 Makes a fresh database and
585 install test repository into tmp dir
585 install test repository into tmp dir
586 """
586 """
587 from rhodecode.lib.db_manage import DbManage
587 from rhodecode.lib.db_manage import DbManage
588 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
588 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
589
589
590 # PART ONE create db
590 # PART ONE create db
591 dbconf = config['sqlalchemy.db1.url']
591 dbconf = config['sqlalchemy.db1.url']
592 log.debug('making test db %s' % dbconf)
592 log.debug('making test db %s' % dbconf)
593
593
594 # create test dir if it doesn't exist
594 # create test dir if it doesn't exist
595 if not os.path.isdir(repos_test_path):
595 if not os.path.isdir(repos_test_path):
596 log.debug('Creating testdir %s' % repos_test_path)
596 log.debug('Creating testdir %s' % repos_test_path)
597 os.makedirs(repos_test_path)
597 os.makedirs(repos_test_path)
598
598
599 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
599 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
600 tests=True)
600 tests=True)
601 dbmanage.create_tables(override=True)
601 dbmanage.create_tables(override=True)
602 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
602 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
603 dbmanage.create_default_user()
603 dbmanage.create_default_user()
604 dbmanage.admin_prompt()
604 dbmanage.admin_prompt()
605 dbmanage.create_permissions()
605 dbmanage.create_permissions()
606 dbmanage.populate_default_permissions()
606 dbmanage.populate_default_permissions()
607 Session().commit()
607 Session().commit()
608 # PART TWO make test repo
608 # PART TWO make test repo
609 log.debug('making test vcs repositories')
609 log.debug('making test vcs repositories')
610
610
611 idx_path = config['app_conf']['index_dir']
611 idx_path = config['app_conf']['index_dir']
612 data_path = config['app_conf']['cache_dir']
612 data_path = config['app_conf']['cache_dir']
613
613
614 #clean index and data
614 #clean index and data
615 if idx_path and os.path.exists(idx_path):
615 if idx_path and os.path.exists(idx_path):
616 log.debug('remove %s' % idx_path)
616 log.debug('remove %s' % idx_path)
617 shutil.rmtree(idx_path)
617 shutil.rmtree(idx_path)
618
618
619 if data_path and os.path.exists(data_path):
619 if data_path and os.path.exists(data_path):
620 log.debug('remove %s' % data_path)
620 log.debug('remove %s' % data_path)
621 shutil.rmtree(data_path)
621 shutil.rmtree(data_path)
622
622
623 #CREATE DEFAULT TEST REPOS
623 #CREATE DEFAULT TEST REPOS
624 cur_dir = dn(dn(abspath(__file__)))
624 cur_dir = dn(dn(abspath(__file__)))
625 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
625 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
626 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
626 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
627 tar.close()
627 tar.close()
628
628
629 cur_dir = dn(dn(abspath(__file__)))
629 cur_dir = dn(dn(abspath(__file__)))
630 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
630 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
631 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
631 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
632 tar.close()
632 tar.close()
633
633
634 #LOAD VCS test stuff
634 #LOAD VCS test stuff
635 from rhodecode.tests.vcs import setup_package
635 from rhodecode.tests.vcs import setup_package
636 setup_package()
636 setup_package()
637
637
638
638
639 #==============================================================================
639 #==============================================================================
640 # PASTER COMMANDS
640 # PASTER COMMANDS
641 #==============================================================================
641 #==============================================================================
642 class BasePasterCommand(Command):
642 class BasePasterCommand(Command):
643 """
643 """
644 Abstract Base Class for paster commands.
644 Abstract Base Class for paster commands.
645
645
646 The celery commands are somewhat aggressive about loading
646 The celery commands are somewhat aggressive about loading
647 celery.conf, and since our module sets the `CELERY_LOADER`
647 celery.conf, and since our module sets the `CELERY_LOADER`
648 environment variable to our loader, we have to bootstrap a bit and
648 environment variable to our loader, we have to bootstrap a bit and
649 make sure we've had a chance to load the pylons config off of the
649 make sure we've had a chance to load the pylons config off of the
650 command line, otherwise everything fails.
650 command line, otherwise everything fails.
651 """
651 """
652 min_args = 1
652 min_args = 1
653 min_args_error = "Please provide a paster config file as an argument."
653 min_args_error = "Please provide a paster config file as an argument."
654 takes_config_file = 1
654 takes_config_file = 1
655 requires_config_file = True
655 requires_config_file = True
656
656
657 def notify_msg(self, msg, log=False):
657 def notify_msg(self, msg, log=False):
658 """Make a notification to user, additionally if logger is passed
658 """Make a notification to user, additionally if logger is passed
659 it logs this action using given logger
659 it logs this action using given logger
660
660
661 :param msg: message that will be printed to user
661 :param msg: message that will be printed to user
662 :param log: logging instance, to use to additionally log this message
662 :param log: logging instance, to use to additionally log this message
663
663
664 """
664 """
665 if log and isinstance(log, logging):
665 if log and isinstance(log, logging):
666 log(msg)
666 log(msg)
667
667
668 def run(self, args):
668 def run(self, args):
669 """
669 """
670 Overrides Command.run
670 Overrides Command.run
671
671
672 Checks for a config file argument and loads it.
672 Checks for a config file argument and loads it.
673 """
673 """
674 if len(args) < self.min_args:
674 if len(args) < self.min_args:
675 raise BadCommand(
675 raise BadCommand(
676 self.min_args_error % {'min_args': self.min_args,
676 self.min_args_error % {'min_args': self.min_args,
677 'actual_args': len(args)})
677 'actual_args': len(args)})
678
678
679 # Decrement because we're going to lob off the first argument.
679 # Decrement because we're going to lob off the first argument.
680 # @@ This is hacky
680 # @@ This is hacky
681 self.min_args -= 1
681 self.min_args -= 1
682 self.bootstrap_config(args[0])
682 self.bootstrap_config(args[0])
683 self.update_parser()
683 self.update_parser()
684 return super(BasePasterCommand, self).run(args[1:])
684 return super(BasePasterCommand, self).run(args[1:])
685
685
686 def update_parser(self):
686 def update_parser(self):
687 """
687 """
688 Abstract method. Allows for the class's parser to be updated
688 Abstract method. Allows for the class's parser to be updated
689 before the superclass's `run` method is called. Necessary to
689 before the superclass's `run` method is called. Necessary to
690 allow options/arguments to be passed through to the underlying
690 allow options/arguments to be passed through to the underlying
691 celery command.
691 celery command.
692 """
692 """
693 raise NotImplementedError("Abstract Method.")
693 raise NotImplementedError("Abstract Method.")
694
694
695 def bootstrap_config(self, conf):
695 def bootstrap_config(self, conf):
696 """
696 """
697 Loads the pylons configuration.
697 Loads the pylons configuration.
698 """
698 """
699 from pylons import config as pylonsconfig
699 from pylons import config as pylonsconfig
700
700
701 self.path_to_ini_file = os.path.realpath(conf)
701 self.path_to_ini_file = os.path.realpath(conf)
702 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
702 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
703 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
703 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
704
704
705 def _init_session(self):
705 def _init_session(self):
706 """
706 """
707 Inits SqlAlchemy Session
707 Inits SqlAlchemy Session
708 """
708 """
709 logging.config.fileConfig(self.path_to_ini_file)
709 logging.config.fileConfig(self.path_to_ini_file)
710 from pylons import config
710 from pylons import config
711 from rhodecode.model import init_model
711 from rhodecode.model import init_model
712 from rhodecode.lib.utils2 import engine_from_config
712 from rhodecode.lib.utils2 import engine_from_config
713
713
714 #get to remove repos !!
714 #get to remove repos !!
715 add_cache(config)
715 add_cache(config)
716 engine = engine_from_config(config, 'sqlalchemy.db1.')
716 engine = engine_from_config(config, 'sqlalchemy.db1.')
717 init_model(engine)
717 init_model(engine)
718
718
719
719
720 def check_git_version():
720 def check_git_version():
721 """
721 """
722 Checks what version of git is installed in system, and issues a warning
722 Checks what version of git is installed in system, and issues a warning
723 if it's too old for RhodeCode to properly work.
723 if it's too old for RhodeCode to properly work.
724 """
724 """
725 from rhodecode import BACKENDS
725 from rhodecode import BACKENDS
726 from rhodecode.lib.vcs.backends.git.repository import GitRepository
726 from rhodecode.lib.vcs.backends.git.repository import GitRepository
727 from distutils.version import StrictVersion
727 from distutils.version import StrictVersion
728
728
729 stdout, stderr = GitRepository._run_git_command('--version', _bare=True,
729 stdout, stderr = GitRepository._run_git_command('--version', _bare=True,
730 _safe=True)
730 _safe=True)
731
731
732 ver = (stdout.split(' ')[-1] or '').strip() or '0.0.0'
732 ver = (stdout.split(' ')[-1] or '').strip() or '0.0.0'
733 if len(ver.split('.')) > 3:
733 if len(ver.split('.')) > 3:
734 #StrictVersion needs to be only 3 element type
734 #StrictVersion needs to be only 3 element type
735 ver = '.'.join(ver.split('.')[:3])
735 ver = '.'.join(ver.split('.')[:3])
736 try:
736 try:
737 _ver = StrictVersion(ver)
737 _ver = StrictVersion(ver)
738 except Exception:
738 except Exception:
739 _ver = StrictVersion('0.0.0')
739 _ver = StrictVersion('0.0.0')
740 stderr = traceback.format_exc()
740 stderr = traceback.format_exc()
741
741
742 req_ver = '1.7.4'
742 req_ver = '1.7.4'
743 to_old_git = False
743 to_old_git = False
744 if _ver < StrictVersion(req_ver):
744 if _ver < StrictVersion(req_ver):
745 to_old_git = True
745 to_old_git = True
746
746
747 if 'git' in BACKENDS:
747 if 'git' in BACKENDS:
748 log.debug('GIT version detected: %s' % stdout)
748 log.debug('GIT version detected: %s' % stdout)
749 if stderr:
749 if stderr:
750 log.warning('Unable to detect git version, org error was: %r' % stderr)
750 log.warning('Unable to detect git version, org error was: %r' % stderr)
751 elif to_old_git:
751 elif to_old_git:
752 log.warning('RhodeCode detected git version %s, which is too old '
752 log.warning('RhodeCode detected git version %s, which is too old '
753 'for the system to function properly. Make sure '
753 'for the system to function properly. Make sure '
754 'its version is at least %s' % (ver, req_ver))
754 'its version is at least %s' % (ver, req_ver))
755 return _ver
755 return _ver
756
756
757
757
758 @decorator.decorator
758 @decorator.decorator
759 def jsonify(func, *args, **kwargs):
759 def jsonify(func, *args, **kwargs):
760 """Action decorator that formats output for JSON
760 """Action decorator that formats output for JSON
761
761
762 Given a function that will return content, this decorator will turn
762 Given a function that will return content, this decorator will turn
763 the result into JSON, with a content-type of 'application/json' and
763 the result into JSON, with a content-type of 'application/json' and
764 output it.
764 output it.
765
765
766 """
766 """
767 from pylons.decorators.util import get_pylons
767 from pylons.decorators.util import get_pylons
768 from rhodecode.lib.ext_json import json
768 from rhodecode.lib.ext_json import json
769 pylons = get_pylons(args)
769 pylons = get_pylons(args)
770 pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
770 pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
771 data = func(*args, **kwargs)
771 data = func(*args, **kwargs)
772 if isinstance(data, (list, tuple)):
772 if isinstance(data, (list, tuple)):
773 msg = "JSON responses with Array envelopes are susceptible to " \
773 msg = "JSON responses with Array envelopes are susceptible to " \
774 "cross-site data leak attacks, see " \
774 "cross-site data leak attacks, see " \
775 "http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
775 "http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
776 warnings.warn(msg, Warning, 2)
776 warnings.warn(msg, Warning, 2)
777 log.warning(msg)
777 log.warning(msg)
778 log.debug("Returning JSON wrapped action output")
778 log.debug("Returning JSON wrapped action output")
779 return json.dumps(data, encoding='utf-8')
779 return json.dumps(data, encoding='utf-8')
@@ -1,703 +1,707 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.repo
3 rhodecode.model.repo
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 Repository model for rhodecode
6 Repository model for rhodecode
7
7
8 :created_on: Jun 5, 2010
8 :created_on: Jun 5, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import with_statement
25 from __future__ import with_statement
26 import os
26 import os
27 import shutil
27 import shutil
28 import logging
28 import logging
29 import traceback
29 import traceback
30 from datetime import datetime
30 from datetime import datetime
31
31
32 from rhodecode.lib.vcs.backends import get_backend
32 from rhodecode.lib.vcs.backends import get_backend
33 from rhodecode.lib.compat import json
33 from rhodecode.lib.compat import json
34 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode,\
34 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode,\
35 remove_prefix, obfuscate_url_pw
35 remove_prefix, obfuscate_url_pw
36 from rhodecode.lib.caching_query import FromCache
36 from rhodecode.lib.caching_query import FromCache
37 from rhodecode.lib.hooks import log_create_repository, log_delete_repository
37 from rhodecode.lib.hooks import log_create_repository, log_delete_repository
38
38
39 from rhodecode.model import BaseModel
39 from rhodecode.model import BaseModel
40 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
40 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
41 Statistics, UserGroup, UserGroupRepoToPerm, RhodeCodeUi, RepoGroup,\
41 Statistics, UserGroup, UserGroupRepoToPerm, RhodeCodeUi, RepoGroup,\
42 RhodeCodeSetting, RepositoryField
42 RhodeCodeSetting, RepositoryField
43 from rhodecode.lib import helpers as h
43 from rhodecode.lib import helpers as h
44 from rhodecode.lib.auth import HasRepoPermissionAny
44 from rhodecode.lib.auth import HasRepoPermissionAny
45 from rhodecode.lib.exceptions import AttachedForksError
45 from rhodecode.lib.exceptions import AttachedForksError
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 class RepoModel(BaseModel):
50 class RepoModel(BaseModel):
51
51
52 cls = Repository
52 cls = Repository
53 URL_SEPARATOR = Repository.url_sep()
53 URL_SEPARATOR = Repository.url_sep()
54
54
55 def __get_users_group(self, users_group):
55 def __get_users_group(self, users_group):
56 return self._get_instance(UserGroup, users_group,
56 return self._get_instance(UserGroup, users_group,
57 callback=UserGroup.get_by_group_name)
57 callback=UserGroup.get_by_group_name)
58
58
59 def _get_repos_group(self, repos_group):
59 def _get_repos_group(self, repos_group):
60 return self._get_instance(RepoGroup, repos_group,
60 return self._get_instance(RepoGroup, repos_group,
61 callback=RepoGroup.get_by_group_name)
61 callback=RepoGroup.get_by_group_name)
62
62
63 @LazyProperty
63 @LazyProperty
64 def repos_path(self):
64 def repos_path(self):
65 """
65 """
66 Get's the repositories root path from database
66 Get's the repositories root path from database
67 """
67 """
68
68
69 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
69 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
70 return q.ui_value
70 return q.ui_value
71
71
72 def get(self, repo_id, cache=False):
72 def get(self, repo_id, cache=False):
73 repo = self.sa.query(Repository)\
73 repo = self.sa.query(Repository)\
74 .filter(Repository.repo_id == repo_id)
74 .filter(Repository.repo_id == repo_id)
75
75
76 if cache:
76 if cache:
77 repo = repo.options(FromCache("sql_cache_short",
77 repo = repo.options(FromCache("sql_cache_short",
78 "get_repo_%s" % repo_id))
78 "get_repo_%s" % repo_id))
79 return repo.scalar()
79 return repo.scalar()
80
80
81 def get_repo(self, repository):
81 def get_repo(self, repository):
82 return self._get_repo(repository)
82 return self._get_repo(repository)
83
83
84 def get_by_repo_name(self, repo_name, cache=False):
84 def get_by_repo_name(self, repo_name, cache=False):
85 repo = self.sa.query(Repository)\
85 repo = self.sa.query(Repository)\
86 .filter(Repository.repo_name == repo_name)
86 .filter(Repository.repo_name == repo_name)
87
87
88 if cache:
88 if cache:
89 repo = repo.options(FromCache("sql_cache_short",
89 repo = repo.options(FromCache("sql_cache_short",
90 "get_repo_%s" % repo_name))
90 "get_repo_%s" % repo_name))
91 return repo.scalar()
91 return repo.scalar()
92
92
93 def get_all_user_repos(self, user):
93 def get_all_user_repos(self, user):
94 """
94 """
95 Get's all repositories that user have at least read access
95 Get's all repositories that user have at least read access
96
96
97 :param user:
97 :param user:
98 :type user:
98 :type user:
99 """
99 """
100 from rhodecode.lib.auth import AuthUser
100 from rhodecode.lib.auth import AuthUser
101 user = self._get_user(user)
101 user = self._get_user(user)
102 repos = AuthUser(user_id=user.user_id).permissions['repositories']
102 repos = AuthUser(user_id=user.user_id).permissions['repositories']
103 access_check = lambda r: r[1] in ['repository.read',
103 access_check = lambda r: r[1] in ['repository.read',
104 'repository.write',
104 'repository.write',
105 'repository.admin']
105 'repository.admin']
106 repos = [x[0] for x in filter(access_check, repos.items())]
106 repos = [x[0] for x in filter(access_check, repos.items())]
107 return Repository.query().filter(Repository.repo_name.in_(repos))
107 return Repository.query().filter(Repository.repo_name.in_(repos))
108
108
109 def get_users_js(self):
109 def get_users_js(self):
110 users = self.sa.query(User).filter(User.active == True).all()
110 users = self.sa.query(User).filter(User.active == True).all()
111 return json.dumps([
111 return json.dumps([
112 {
112 {
113 'id': u.user_id,
113 'id': u.user_id,
114 'fname': u.name,
114 'fname': u.name,
115 'lname': u.lastname,
115 'lname': u.lastname,
116 'nname': u.username,
116 'nname': u.username,
117 'gravatar_lnk': h.gravatar_url(u.email, 14)
117 'gravatar_lnk': h.gravatar_url(u.email, 14)
118 } for u in users]
118 } for u in users]
119 )
119 )
120
120
121 def get_users_groups_js(self):
121 def get_users_groups_js(self):
122 users_groups = self.sa.query(UserGroup)\
122 users_groups = self.sa.query(UserGroup)\
123 .filter(UserGroup.users_group_active == True).all()
123 .filter(UserGroup.users_group_active == True).all()
124
124
125 return json.dumps([
125 return json.dumps([
126 {
126 {
127 'id': gr.users_group_id,
127 'id': gr.users_group_id,
128 'grname': gr.users_group_name,
128 'grname': gr.users_group_name,
129 'grmembers': len(gr.members),
129 'grmembers': len(gr.members),
130 } for gr in users_groups]
130 } for gr in users_groups]
131 )
131 )
132
132
133 @classmethod
133 @classmethod
134 def _render_datatable(cls, tmpl, *args, **kwargs):
134 def _render_datatable(cls, tmpl, *args, **kwargs):
135 import rhodecode
135 import rhodecode
136 from pylons import tmpl_context as c
136 from pylons import tmpl_context as c
137 from pylons.i18n.translation import _
137 from pylons.i18n.translation import _
138
138
139 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
139 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
140 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
140 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
141
141
142 tmpl = template.get_def(tmpl)
142 tmpl = template.get_def(tmpl)
143 kwargs.update(dict(_=_, h=h, c=c))
143 kwargs.update(dict(_=_, h=h, c=c))
144 return tmpl.render(*args, **kwargs)
144 return tmpl.render(*args, **kwargs)
145
145
146 @classmethod
146 @classmethod
147 def update_repoinfo(cls, repositories=None):
147 def update_repoinfo(cls, repositories=None):
148 if not repositories:
148 if not repositories:
149 repositories = Repository.getAll()
149 repositories = Repository.getAll()
150 for repo in repositories:
150 for repo in repositories:
151 repo.update_changeset_cache()
151 repo.update_changeset_cache()
152
152
153 def get_repos_as_dict(self, repos_list=None, admin=False, perm_check=True,
153 def get_repos_as_dict(self, repos_list=None, admin=False, perm_check=True,
154 super_user_actions=False):
154 super_user_actions=False):
155 _render = self._render_datatable
155 _render = self._render_datatable
156
156
157 def quick_menu(repo_name):
157 def quick_menu(repo_name):
158 return _render('quick_menu', repo_name)
158 return _render('quick_menu', repo_name)
159
159
160 def repo_lnk(name, rtype, private, fork_of):
160 def repo_lnk(name, rtype, private, fork_of):
161 return _render('repo_name', name, rtype, private, fork_of,
161 return _render('repo_name', name, rtype, private, fork_of,
162 short_name=not admin, admin=False)
162 short_name=not admin, admin=False)
163
163
164 def last_change(last_change):
164 def last_change(last_change):
165 return _render("last_change", last_change)
165 return _render("last_change", last_change)
166
166
167 def rss_lnk(repo_name):
167 def rss_lnk(repo_name):
168 return _render("rss", repo_name)
168 return _render("rss", repo_name)
169
169
170 def atom_lnk(repo_name):
170 def atom_lnk(repo_name):
171 return _render("atom", repo_name)
171 return _render("atom", repo_name)
172
172
173 def last_rev(repo_name, cs_cache):
173 def last_rev(repo_name, cs_cache):
174 return _render('revision', repo_name, cs_cache.get('revision'),
174 return _render('revision', repo_name, cs_cache.get('revision'),
175 cs_cache.get('raw_id'), cs_cache.get('author'),
175 cs_cache.get('raw_id'), cs_cache.get('author'),
176 cs_cache.get('message'))
176 cs_cache.get('message'))
177
177
178 def desc(desc):
178 def desc(desc):
179 from pylons import tmpl_context as c
179 from pylons import tmpl_context as c
180 if c.visual.stylify_metatags:
180 if c.visual.stylify_metatags:
181 return h.urlify_text(h.desc_stylize(h.truncate(desc, 60)))
181 return h.urlify_text(h.desc_stylize(h.truncate(desc, 60)))
182 else:
182 else:
183 return h.urlify_text(h.truncate(desc, 60))
183 return h.urlify_text(h.truncate(desc, 60))
184
184
185 def repo_actions(repo_name):
185 def repo_actions(repo_name):
186 return _render('repo_actions', repo_name, super_user_actions)
186 return _render('repo_actions', repo_name, super_user_actions)
187
187
188 def owner_actions(user_id, username):
188 def owner_actions(user_id, username):
189 return _render('user_name', user_id, username)
189 return _render('user_name', user_id, username)
190
190
191 repos_data = []
191 repos_data = []
192 for repo in repos_list:
192 for repo in repos_list:
193 if perm_check:
193 if perm_check:
194 # check permission at this level
194 # check permission at this level
195 if not HasRepoPermissionAny(
195 if not HasRepoPermissionAny(
196 'repository.read', 'repository.write', 'repository.admin'
196 'repository.read', 'repository.write', 'repository.admin'
197 )(repo.repo_name, 'get_repos_as_dict check'):
197 )(repo.repo_name, 'get_repos_as_dict check'):
198 continue
198 continue
199 cs_cache = repo.changeset_cache
199 cs_cache = repo.changeset_cache
200 row = {
200 row = {
201 "menu": quick_menu(repo.repo_name),
201 "menu": quick_menu(repo.repo_name),
202 "raw_name": repo.repo_name.lower(),
202 "raw_name": repo.repo_name.lower(),
203 "name": repo_lnk(repo.repo_name, repo.repo_type,
203 "name": repo_lnk(repo.repo_name, repo.repo_type,
204 repo.private, repo.fork),
204 repo.private, repo.fork),
205 "last_change": last_change(repo.last_db_change),
205 "last_change": last_change(repo.last_db_change),
206 "last_changeset": last_rev(repo.repo_name, cs_cache),
206 "last_changeset": last_rev(repo.repo_name, cs_cache),
207 "raw_tip": cs_cache.get('revision'),
207 "raw_tip": cs_cache.get('revision'),
208 "desc": desc(repo.description),
208 "desc": desc(repo.description),
209 "owner": h.person(repo.user.username),
209 "owner": h.person(repo.user.username),
210 "rss": rss_lnk(repo.repo_name),
210 "rss": rss_lnk(repo.repo_name),
211 "atom": atom_lnk(repo.repo_name),
211 "atom": atom_lnk(repo.repo_name),
212
212
213 }
213 }
214 if admin:
214 if admin:
215 row.update({
215 row.update({
216 "action": repo_actions(repo.repo_name),
216 "action": repo_actions(repo.repo_name),
217 "owner": owner_actions(repo.user.user_id,
217 "owner": owner_actions(repo.user.user_id,
218 h.person(repo.user.username))
218 h.person(repo.user.username))
219 })
219 })
220 repos_data.append(row)
220 repos_data.append(row)
221
221
222 return {
222 return {
223 "totalRecords": len(repos_list),
223 "totalRecords": len(repos_list),
224 "startIndex": 0,
224 "startIndex": 0,
225 "sort": "name",
225 "sort": "name",
226 "dir": "asc",
226 "dir": "asc",
227 "records": repos_data
227 "records": repos_data
228 }
228 }
229
229
230 def _get_defaults(self, repo_name):
230 def _get_defaults(self, repo_name):
231 """
231 """
232 Get's information about repository, and returns a dict for
232 Get's information about repository, and returns a dict for
233 usage in forms
233 usage in forms
234
234
235 :param repo_name:
235 :param repo_name:
236 """
236 """
237
237
238 repo_info = Repository.get_by_repo_name(repo_name)
238 repo_info = Repository.get_by_repo_name(repo_name)
239
239
240 if repo_info is None:
240 if repo_info is None:
241 return None
241 return None
242
242
243 defaults = repo_info.get_dict()
243 defaults = repo_info.get_dict()
244 group, repo_name, repo_name_full = repo_info.groups_and_repo
244 group, repo_name, repo_name_full = repo_info.groups_and_repo
245 defaults['repo_name'] = repo_name
245 defaults['repo_name'] = repo_name
246 defaults['repo_group'] = getattr(group[-1] if group else None,
246 defaults['repo_group'] = getattr(group[-1] if group else None,
247 'group_id', None)
247 'group_id', None)
248
248
249 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
249 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
250 (1, 'repo_description'), (1, 'repo_enable_locking'),
250 (1, 'repo_description'), (1, 'repo_enable_locking'),
251 (1, 'repo_landing_rev'), (0, 'clone_uri'),
251 (1, 'repo_landing_rev'), (0, 'clone_uri'),
252 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
252 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
253 attr = k
253 attr = k
254 if strip:
254 if strip:
255 attr = remove_prefix(k, 'repo_')
255 attr = remove_prefix(k, 'repo_')
256
256
257 defaults[k] = defaults[attr]
257 defaults[k] = defaults[attr]
258
258
259 # fill owner
259 # fill owner
260 if repo_info.user:
260 if repo_info.user:
261 defaults.update({'user': repo_info.user.username})
261 defaults.update({'user': repo_info.user.username})
262 else:
262 else:
263 replacement_user = User.query().filter(User.admin ==
263 replacement_user = User.query().filter(User.admin ==
264 True).first().username
264 True).first().username
265 defaults.update({'user': replacement_user})
265 defaults.update({'user': replacement_user})
266
266
267 # fill repository users
267 # fill repository users
268 for p in repo_info.repo_to_perm:
268 for p in repo_info.repo_to_perm:
269 defaults.update({'u_perm_%s' % p.user.username:
269 defaults.update({'u_perm_%s' % p.user.username:
270 p.permission.permission_name})
270 p.permission.permission_name})
271
271
272 # fill repository groups
272 # fill repository groups
273 for p in repo_info.users_group_to_perm:
273 for p in repo_info.users_group_to_perm:
274 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
274 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
275 p.permission.permission_name})
275 p.permission.permission_name})
276
276
277 return defaults
277 return defaults
278
278
279 def update(self, org_repo_name, **kwargs):
279 def update(self, org_repo_name, **kwargs):
280 try:
280 try:
281 cur_repo = self.get_by_repo_name(org_repo_name, cache=False)
281 cur_repo = self.get_by_repo_name(org_repo_name, cache=False)
282
282
283 if 'user' in kwargs:
283 if 'user' in kwargs:
284 cur_repo.user = User.get_by_username(kwargs['user'])
284 cur_repo.user = User.get_by_username(kwargs['user'])
285
285
286 if 'repo_group' in kwargs:
286 if 'repo_group' in kwargs:
287 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
287 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
288
288
289 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
289 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
290 (1, 'repo_description'), (1, 'repo_enable_locking'),
290 (1, 'repo_description'), (1, 'repo_enable_locking'),
291 (1, 'repo_landing_rev'), (0, 'clone_uri'),
291 (1, 'repo_landing_rev'), (0, 'clone_uri'),
292 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
292 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
293 if k in kwargs:
293 if k in kwargs:
294 val = kwargs[k]
294 val = kwargs[k]
295 if strip:
295 if strip:
296 k = remove_prefix(k, 'repo_')
296 k = remove_prefix(k, 'repo_')
297 setattr(cur_repo, k, val)
297 setattr(cur_repo, k, val)
298
298
299 new_name = cur_repo.get_new_name(kwargs['repo_name'])
299 new_name = cur_repo.get_new_name(kwargs['repo_name'])
300 cur_repo.repo_name = new_name
300 cur_repo.repo_name = new_name
301 #if private flag is set, reset default permission to NONE
301 #if private flag is set, reset default permission to NONE
302
302
303 if kwargs.get('repo_private'):
303 if kwargs.get('repo_private'):
304 EMPTY_PERM = 'repository.none'
304 EMPTY_PERM = 'repository.none'
305 RepoModel().grant_user_permission(
305 RepoModel().grant_user_permission(
306 repo=cur_repo, user='default', perm=EMPTY_PERM
306 repo=cur_repo, user='default', perm=EMPTY_PERM
307 )
307 )
308 #handle extra fields
308 #handle extra fields
309 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs):
309 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs):
310 k = RepositoryField.un_prefix_key(field)
310 k = RepositoryField.un_prefix_key(field)
311 ex_field = RepositoryField.get_by_key_name(key=k, repo=cur_repo)
311 ex_field = RepositoryField.get_by_key_name(key=k, repo=cur_repo)
312 if ex_field:
312 if ex_field:
313 ex_field.field_value = kwargs[field]
313 ex_field.field_value = kwargs[field]
314 self.sa.add(ex_field)
314 self.sa.add(ex_field)
315 self.sa.add(cur_repo)
315 self.sa.add(cur_repo)
316
316
317 if org_repo_name != new_name:
317 if org_repo_name != new_name:
318 # rename repository
318 # rename repository
319 self.__rename_repo(old=org_repo_name, new=new_name)
319 self.__rename_repo(old=org_repo_name, new=new_name)
320
320
321 return cur_repo
321 return cur_repo
322 except Exception:
322 except Exception:
323 log.error(traceback.format_exc())
323 log.error(traceback.format_exc())
324 raise
324 raise
325
325
326 def create_repo(self, repo_name, repo_type, description, owner,
326 def create_repo(self, repo_name, repo_type, description, owner,
327 private=False, clone_uri=None, repos_group=None,
327 private=False, clone_uri=None, repos_group=None,
328 landing_rev='tip', just_db=False, fork_of=None,
328 landing_rev='tip', just_db=False, fork_of=None,
329 copy_fork_permissions=False, enable_statistics=False,
329 copy_fork_permissions=False, enable_statistics=False,
330 enable_locking=False, enable_downloads=False):
330 enable_locking=False, enable_downloads=False):
331 """
331 """
332 Create repository
332 Create repository
333
333
334 """
334 """
335 from rhodecode.model.scm import ScmModel
335 from rhodecode.model.scm import ScmModel
336
336
337 owner = self._get_user(owner)
337 owner = self._get_user(owner)
338 fork_of = self._get_repo(fork_of)
338 fork_of = self._get_repo(fork_of)
339 repos_group = self._get_repos_group(repos_group)
339 repos_group = self._get_repos_group(repos_group)
340 try:
340 try:
341
341
342 # repo name is just a name of repository
342 # repo name is just a name of repository
343 # while repo_name_full is a full qualified name that is combined
343 # while repo_name_full is a full qualified name that is combined
344 # with name and path of group
344 # with name and path of group
345 repo_name_full = repo_name
345 repo_name_full = repo_name
346 repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
346 repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
347
347
348 new_repo = Repository()
348 new_repo = Repository()
349 new_repo.enable_statistics = False
349 new_repo.enable_statistics = False
350 new_repo.repo_name = repo_name_full
350 new_repo.repo_name = repo_name_full
351 new_repo.repo_type = repo_type
351 new_repo.repo_type = repo_type
352 new_repo.user = owner
352 new_repo.user = owner
353 new_repo.group = repos_group
353 new_repo.group = repos_group
354 new_repo.description = description or repo_name
354 new_repo.description = description or repo_name
355 new_repo.private = private
355 new_repo.private = private
356 new_repo.clone_uri = clone_uri
356 new_repo.clone_uri = clone_uri
357 new_repo.landing_rev = landing_rev
357 new_repo.landing_rev = landing_rev
358
358
359 new_repo.enable_statistics = enable_statistics
359 new_repo.enable_statistics = enable_statistics
360 new_repo.enable_locking = enable_locking
360 new_repo.enable_locking = enable_locking
361 new_repo.enable_downloads = enable_downloads
361 new_repo.enable_downloads = enable_downloads
362
362
363 if repos_group:
363 if repos_group:
364 new_repo.enable_locking = repos_group.enable_locking
364 new_repo.enable_locking = repos_group.enable_locking
365
365
366 if fork_of:
366 if fork_of:
367 parent_repo = fork_of
367 parent_repo = fork_of
368 new_repo.fork = parent_repo
368 new_repo.fork = parent_repo
369
369
370 self.sa.add(new_repo)
370 self.sa.add(new_repo)
371
371
372 def _create_default_perms():
372 def _create_default_perms():
373 # create default permission
373 # create default permission
374 repo_to_perm = UserRepoToPerm()
374 repo_to_perm = UserRepoToPerm()
375 default = 'repository.read'
375 default = 'repository.read'
376 for p in User.get_by_username('default').user_perms:
376 for p in User.get_by_username('default').user_perms:
377 if p.permission.permission_name.startswith('repository.'):
377 if p.permission.permission_name.startswith('repository.'):
378 default = p.permission.permission_name
378 default = p.permission.permission_name
379 break
379 break
380
380
381 default_perm = 'repository.none' if private else default
381 default_perm = 'repository.none' if private else default
382
382
383 repo_to_perm.permission_id = self.sa.query(Permission)\
383 repo_to_perm.permission_id = self.sa.query(Permission)\
384 .filter(Permission.permission_name == default_perm)\
384 .filter(Permission.permission_name == default_perm)\
385 .one().permission_id
385 .one().permission_id
386
386
387 repo_to_perm.repository = new_repo
387 repo_to_perm.repository = new_repo
388 repo_to_perm.user_id = User.get_by_username('default').user_id
388 repo_to_perm.user_id = User.get_by_username('default').user_id
389
389
390 self.sa.add(repo_to_perm)
390 self.sa.add(repo_to_perm)
391
391
392 if fork_of:
392 if fork_of:
393 if copy_fork_permissions:
393 if copy_fork_permissions:
394 repo = fork_of
394 repo = fork_of
395 user_perms = UserRepoToPerm.query()\
395 user_perms = UserRepoToPerm.query()\
396 .filter(UserRepoToPerm.repository == repo).all()
396 .filter(UserRepoToPerm.repository == repo).all()
397 group_perms = UserGroupRepoToPerm.query()\
397 group_perms = UserGroupRepoToPerm.query()\
398 .filter(UserGroupRepoToPerm.repository == repo).all()
398 .filter(UserGroupRepoToPerm.repository == repo).all()
399
399
400 for perm in user_perms:
400 for perm in user_perms:
401 UserRepoToPerm.create(perm.user, new_repo,
401 UserRepoToPerm.create(perm.user, new_repo,
402 perm.permission)
402 perm.permission)
403
403
404 for perm in group_perms:
404 for perm in group_perms:
405 UserGroupRepoToPerm.create(perm.users_group, new_repo,
405 UserGroupRepoToPerm.create(perm.users_group, new_repo,
406 perm.permission)
406 perm.permission)
407 else:
407 else:
408 _create_default_perms()
408 _create_default_perms()
409 else:
409 else:
410 _create_default_perms()
410 _create_default_perms()
411
411
412 if not just_db:
412 if not just_db:
413 self.__create_repo(repo_name, repo_type,
413 self.__create_repo(repo_name, repo_type,
414 repos_group,
414 repos_group,
415 clone_uri)
415 clone_uri)
416 log_create_repository(new_repo.get_dict(),
416 log_create_repository(new_repo.get_dict(),
417 created_by=owner.username)
417 created_by=owner.username)
418
418
419 # now automatically start following this repository as owner
419 # now automatically start following this repository as owner
420 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
420 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
421 owner.user_id)
421 owner.user_id)
422 return new_repo
422 return new_repo
423 except Exception:
423 except Exception:
424 log.error(traceback.format_exc())
424 log.error(traceback.format_exc())
425 raise
425 raise
426
426
427 def create(self, form_data, cur_user, just_db=False, fork=None):
427 def create(self, form_data, cur_user, just_db=False, fork=None):
428 """
428 """
429 Backward compatibility function, just a wrapper on top of create_repo
429 Backward compatibility function, just a wrapper on top of create_repo
430
430
431 :param form_data:
431 :param form_data:
432 :param cur_user:
432 :param cur_user:
433 :param just_db:
433 :param just_db:
434 :param fork:
434 :param fork:
435 """
435 """
436 owner = cur_user
436 owner = cur_user
437 repo_name = form_data['repo_name_full']
437 repo_name = form_data['repo_name_full']
438 repo_type = form_data['repo_type']
438 repo_type = form_data['repo_type']
439 description = form_data['repo_description']
439 description = form_data['repo_description']
440 private = form_data['repo_private']
440 private = form_data['repo_private']
441 clone_uri = form_data.get('clone_uri')
441 clone_uri = form_data.get('clone_uri')
442 repos_group = form_data['repo_group']
442 repos_group = form_data['repo_group']
443 landing_rev = form_data['repo_landing_rev']
443 landing_rev = form_data['repo_landing_rev']
444 copy_fork_permissions = form_data.get('copy_permissions')
444 copy_fork_permissions = form_data.get('copy_permissions')
445 fork_of = form_data.get('fork_parent_id')
445 fork_of = form_data.get('fork_parent_id')
446
446
447 ## repo creation defaults, private and repo_type are filled in form
447 ## repo creation defaults, private and repo_type are filled in form
448 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
448 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
449 enable_statistics = defs.get('repo_enable_statistics')
449 enable_statistics = defs.get('repo_enable_statistics')
450 enable_locking = defs.get('repo_enable_locking')
450 enable_locking = defs.get('repo_enable_locking')
451 enable_downloads = defs.get('repo_enable_downloads')
451 enable_downloads = defs.get('repo_enable_downloads')
452
452
453 return self.create_repo(
453 return self.create_repo(
454 repo_name, repo_type, description, owner, private, clone_uri,
454 repo_name, repo_type, description, owner, private, clone_uri,
455 repos_group, landing_rev, just_db, fork_of, copy_fork_permissions,
455 repos_group, landing_rev, just_db, fork_of, copy_fork_permissions,
456 enable_statistics, enable_locking, enable_downloads
456 enable_statistics, enable_locking, enable_downloads
457 )
457 )
458
458
459 def create_fork(self, form_data, cur_user):
459 def create_fork(self, form_data, cur_user):
460 """
460 """
461 Simple wrapper into executing celery task for fork creation
461 Simple wrapper into executing celery task for fork creation
462
462
463 :param form_data:
463 :param form_data:
464 :param cur_user:
464 :param cur_user:
465 """
465 """
466 from rhodecode.lib.celerylib import tasks, run_task
466 from rhodecode.lib.celerylib import tasks, run_task
467 run_task(tasks.create_repo_fork, form_data, cur_user)
467 run_task(tasks.create_repo_fork, form_data, cur_user)
468
468
469 def delete(self, repo, forks=None):
469 def delete(self, repo, forks=None, fs_remove=True):
470 """
470 """
471 Delete given repository, forks parameter defines what do do with
471 Delete given repository, forks parameter defines what do do with
472 attached forks. Throws AttachedForksError if deleted repo has attached
472 attached forks. Throws AttachedForksError if deleted repo has attached
473 forks
473 forks
474
474
475 :param repo:
475 :param repo:
476 :param forks: str 'delete' or 'detach'
476 :param forks: str 'delete' or 'detach'
477 :param fs_remove: remove(archive) repo from filesystem
477 """
478 """
478 repo = self._get_repo(repo)
479 repo = self._get_repo(repo)
479 if repo:
480 if repo:
480 if forks == 'detach':
481 if forks == 'detach':
481 for r in repo.forks:
482 for r in repo.forks:
482 r.fork = None
483 r.fork = None
483 self.sa.add(r)
484 self.sa.add(r)
484 elif forks == 'delete':
485 elif forks == 'delete':
485 for r in repo.forks:
486 for r in repo.forks:
486 self.delete(r, forks='delete')
487 self.delete(r, forks='delete')
487 elif [f for f in repo.forks]:
488 elif [f for f in repo.forks]:
488 raise AttachedForksError()
489 raise AttachedForksError()
489
490
490 old_repo_dict = repo.get_dict()
491 old_repo_dict = repo.get_dict()
491 owner = repo.user
492 owner = repo.user
492 try:
493 try:
493 self.sa.delete(repo)
494 self.sa.delete(repo)
495 if fs_remove:
494 self.__delete_repo(repo)
496 self.__delete_repo(repo)
497 else:
498 log.debug('skipping removal from filesystem')
495 log_delete_repository(old_repo_dict,
499 log_delete_repository(old_repo_dict,
496 deleted_by=owner.username)
500 deleted_by=owner.username)
497 except Exception:
501 except Exception:
498 log.error(traceback.format_exc())
502 log.error(traceback.format_exc())
499 raise
503 raise
500
504
501 def grant_user_permission(self, repo, user, perm):
505 def grant_user_permission(self, repo, user, perm):
502 """
506 """
503 Grant permission for user on given repository, or update existing one
507 Grant permission for user on given repository, or update existing one
504 if found
508 if found
505
509
506 :param repo: Instance of Repository, repository_id, or repository name
510 :param repo: Instance of Repository, repository_id, or repository name
507 :param user: Instance of User, user_id or username
511 :param user: Instance of User, user_id or username
508 :param perm: Instance of Permission, or permission_name
512 :param perm: Instance of Permission, or permission_name
509 """
513 """
510 user = self._get_user(user)
514 user = self._get_user(user)
511 repo = self._get_repo(repo)
515 repo = self._get_repo(repo)
512 permission = self._get_perm(perm)
516 permission = self._get_perm(perm)
513
517
514 # check if we have that permission already
518 # check if we have that permission already
515 obj = self.sa.query(UserRepoToPerm)\
519 obj = self.sa.query(UserRepoToPerm)\
516 .filter(UserRepoToPerm.user == user)\
520 .filter(UserRepoToPerm.user == user)\
517 .filter(UserRepoToPerm.repository == repo)\
521 .filter(UserRepoToPerm.repository == repo)\
518 .scalar()
522 .scalar()
519 if obj is None:
523 if obj is None:
520 # create new !
524 # create new !
521 obj = UserRepoToPerm()
525 obj = UserRepoToPerm()
522 obj.repository = repo
526 obj.repository = repo
523 obj.user = user
527 obj.user = user
524 obj.permission = permission
528 obj.permission = permission
525 self.sa.add(obj)
529 self.sa.add(obj)
526 log.debug('Granted perm %s to %s on %s' % (perm, user, repo))
530 log.debug('Granted perm %s to %s on %s' % (perm, user, repo))
527
531
528 def revoke_user_permission(self, repo, user):
532 def revoke_user_permission(self, repo, user):
529 """
533 """
530 Revoke permission for user on given repository
534 Revoke permission for user on given repository
531
535
532 :param repo: Instance of Repository, repository_id, or repository name
536 :param repo: Instance of Repository, repository_id, or repository name
533 :param user: Instance of User, user_id or username
537 :param user: Instance of User, user_id or username
534 """
538 """
535
539
536 user = self._get_user(user)
540 user = self._get_user(user)
537 repo = self._get_repo(repo)
541 repo = self._get_repo(repo)
538
542
539 obj = self.sa.query(UserRepoToPerm)\
543 obj = self.sa.query(UserRepoToPerm)\
540 .filter(UserRepoToPerm.repository == repo)\
544 .filter(UserRepoToPerm.repository == repo)\
541 .filter(UserRepoToPerm.user == user)\
545 .filter(UserRepoToPerm.user == user)\
542 .scalar()
546 .scalar()
543 if obj:
547 if obj:
544 self.sa.delete(obj)
548 self.sa.delete(obj)
545 log.debug('Revoked perm on %s on %s' % (repo, user))
549 log.debug('Revoked perm on %s on %s' % (repo, user))
546
550
547 def grant_users_group_permission(self, repo, group_name, perm):
551 def grant_users_group_permission(self, repo, group_name, perm):
548 """
552 """
549 Grant permission for user group on given repository, or update
553 Grant permission for user group on given repository, or update
550 existing one if found
554 existing one if found
551
555
552 :param repo: Instance of Repository, repository_id, or repository name
556 :param repo: Instance of Repository, repository_id, or repository name
553 :param group_name: Instance of UserGroup, users_group_id,
557 :param group_name: Instance of UserGroup, users_group_id,
554 or user group name
558 or user group name
555 :param perm: Instance of Permission, or permission_name
559 :param perm: Instance of Permission, or permission_name
556 """
560 """
557 repo = self._get_repo(repo)
561 repo = self._get_repo(repo)
558 group_name = self.__get_users_group(group_name)
562 group_name = self.__get_users_group(group_name)
559 permission = self._get_perm(perm)
563 permission = self._get_perm(perm)
560
564
561 # check if we have that permission already
565 # check if we have that permission already
562 obj = self.sa.query(UserGroupRepoToPerm)\
566 obj = self.sa.query(UserGroupRepoToPerm)\
563 .filter(UserGroupRepoToPerm.users_group == group_name)\
567 .filter(UserGroupRepoToPerm.users_group == group_name)\
564 .filter(UserGroupRepoToPerm.repository == repo)\
568 .filter(UserGroupRepoToPerm.repository == repo)\
565 .scalar()
569 .scalar()
566
570
567 if obj is None:
571 if obj is None:
568 # create new
572 # create new
569 obj = UserGroupRepoToPerm()
573 obj = UserGroupRepoToPerm()
570
574
571 obj.repository = repo
575 obj.repository = repo
572 obj.users_group = group_name
576 obj.users_group = group_name
573 obj.permission = permission
577 obj.permission = permission
574 self.sa.add(obj)
578 self.sa.add(obj)
575 log.debug('Granted perm %s to %s on %s' % (perm, group_name, repo))
579 log.debug('Granted perm %s to %s on %s' % (perm, group_name, repo))
576
580
577 def revoke_users_group_permission(self, repo, group_name):
581 def revoke_users_group_permission(self, repo, group_name):
578 """
582 """
579 Revoke permission for user group on given repository
583 Revoke permission for user group on given repository
580
584
581 :param repo: Instance of Repository, repository_id, or repository name
585 :param repo: Instance of Repository, repository_id, or repository name
582 :param group_name: Instance of UserGroup, users_group_id,
586 :param group_name: Instance of UserGroup, users_group_id,
583 or user group name
587 or user group name
584 """
588 """
585 repo = self._get_repo(repo)
589 repo = self._get_repo(repo)
586 group_name = self.__get_users_group(group_name)
590 group_name = self.__get_users_group(group_name)
587
591
588 obj = self.sa.query(UserGroupRepoToPerm)\
592 obj = self.sa.query(UserGroupRepoToPerm)\
589 .filter(UserGroupRepoToPerm.repository == repo)\
593 .filter(UserGroupRepoToPerm.repository == repo)\
590 .filter(UserGroupRepoToPerm.users_group == group_name)\
594 .filter(UserGroupRepoToPerm.users_group == group_name)\
591 .scalar()
595 .scalar()
592 if obj:
596 if obj:
593 self.sa.delete(obj)
597 self.sa.delete(obj)
594 log.debug('Revoked perm to %s on %s' % (repo, group_name))
598 log.debug('Revoked perm to %s on %s' % (repo, group_name))
595
599
596 def delete_stats(self, repo_name):
600 def delete_stats(self, repo_name):
597 """
601 """
598 removes stats for given repo
602 removes stats for given repo
599
603
600 :param repo_name:
604 :param repo_name:
601 """
605 """
602 repo = self._get_repo(repo_name)
606 repo = self._get_repo(repo_name)
603 try:
607 try:
604 obj = self.sa.query(Statistics)\
608 obj = self.sa.query(Statistics)\
605 .filter(Statistics.repository == repo).scalar()
609 .filter(Statistics.repository == repo).scalar()
606 if obj:
610 if obj:
607 self.sa.delete(obj)
611 self.sa.delete(obj)
608 except Exception:
612 except Exception:
609 log.error(traceback.format_exc())
613 log.error(traceback.format_exc())
610 raise
614 raise
611
615
612 def __create_repo(self, repo_name, alias, parent, clone_uri=False):
616 def __create_repo(self, repo_name, alias, parent, clone_uri=False):
613 """
617 """
614 makes repository on filesystem. It's group aware means it'll create
618 makes repository on filesystem. It's group aware means it'll create
615 a repository within a group, and alter the paths accordingly of
619 a repository within a group, and alter the paths accordingly of
616 group location
620 group location
617
621
618 :param repo_name:
622 :param repo_name:
619 :param alias:
623 :param alias:
620 :param parent_id:
624 :param parent_id:
621 :param clone_uri:
625 :param clone_uri:
622 """
626 """
623 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
627 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
624 from rhodecode.model.scm import ScmModel
628 from rhodecode.model.scm import ScmModel
625
629
626 if parent:
630 if parent:
627 new_parent_path = os.sep.join(parent.full_path_splitted)
631 new_parent_path = os.sep.join(parent.full_path_splitted)
628 else:
632 else:
629 new_parent_path = ''
633 new_parent_path = ''
630
634
631 # we need to make it str for mercurial
635 # we need to make it str for mercurial
632 repo_path = os.path.join(*map(lambda x: safe_str(x),
636 repo_path = os.path.join(*map(lambda x: safe_str(x),
633 [self.repos_path, new_parent_path, repo_name]))
637 [self.repos_path, new_parent_path, repo_name]))
634
638
635 # check if this path is not a repository
639 # check if this path is not a repository
636 if is_valid_repo(repo_path, self.repos_path):
640 if is_valid_repo(repo_path, self.repos_path):
637 raise Exception('This path %s is a valid repository' % repo_path)
641 raise Exception('This path %s is a valid repository' % repo_path)
638
642
639 # check if this path is a group
643 # check if this path is a group
640 if is_valid_repos_group(repo_path, self.repos_path):
644 if is_valid_repos_group(repo_path, self.repos_path):
641 raise Exception('This path %s is a valid group' % repo_path)
645 raise Exception('This path %s is a valid group' % repo_path)
642
646
643 log.info('creating repo %s in %s @ %s' % (
647 log.info('creating repo %s in %s @ %s' % (
644 repo_name, safe_unicode(repo_path),
648 repo_name, safe_unicode(repo_path),
645 obfuscate_url_pw(clone_uri)
649 obfuscate_url_pw(clone_uri)
646 )
650 )
647 )
651 )
648 backend = get_backend(alias)
652 backend = get_backend(alias)
649 if alias == 'hg':
653 if alias == 'hg':
650 backend(repo_path, create=True, src_url=clone_uri)
654 backend(repo_path, create=True, src_url=clone_uri)
651 elif alias == 'git':
655 elif alias == 'git':
652 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
656 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
653 # add rhodecode hook into this repo
657 # add rhodecode hook into this repo
654 ScmModel().install_git_hook(repo=r)
658 ScmModel().install_git_hook(repo=r)
655 else:
659 else:
656 raise Exception('Undefined alias %s' % alias)
660 raise Exception('Undefined alias %s' % alias)
657
661
658 def __rename_repo(self, old, new):
662 def __rename_repo(self, old, new):
659 """
663 """
660 renames repository on filesystem
664 renames repository on filesystem
661
665
662 :param old: old name
666 :param old: old name
663 :param new: new name
667 :param new: new name
664 """
668 """
665 log.info('renaming repo from %s to %s' % (old, new))
669 log.info('renaming repo from %s to %s' % (old, new))
666
670
667 old_path = os.path.join(self.repos_path, old)
671 old_path = os.path.join(self.repos_path, old)
668 new_path = os.path.join(self.repos_path, new)
672 new_path = os.path.join(self.repos_path, new)
669 if os.path.isdir(new_path):
673 if os.path.isdir(new_path):
670 raise Exception(
674 raise Exception(
671 'Was trying to rename to already existing dir %s' % new_path
675 'Was trying to rename to already existing dir %s' % new_path
672 )
676 )
673 shutil.move(old_path, new_path)
677 shutil.move(old_path, new_path)
674
678
675 def __delete_repo(self, repo):
679 def __delete_repo(self, repo):
676 """
680 """
677 removes repo from filesystem, the removal is acctually made by
681 removes repo from filesystem, the removal is acctually made by
678 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
682 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
679 repository is no longer valid for rhodecode, can be undeleted later on
683 repository is no longer valid for rhodecode, can be undeleted later on
680 by reverting the renames on this repository
684 by reverting the renames on this repository
681
685
682 :param repo: repo object
686 :param repo: repo object
683 """
687 """
684 rm_path = os.path.join(self.repos_path, repo.repo_name)
688 rm_path = os.path.join(self.repos_path, repo.repo_name)
685 log.info("Removing %s" % (rm_path))
689 log.info("Removing %s" % (rm_path))
686 # disable hg/git internal that it doesn't get detected as repo
690 # disable hg/git internal that it doesn't get detected as repo
687 alias = repo.repo_type
691 alias = repo.repo_type
688
692
689 bare = getattr(repo.scm_instance, 'bare', False)
693 bare = getattr(repo.scm_instance, 'bare', False)
690
694
691 if not bare:
695 if not bare:
692 # skip this for bare git repos
696 # skip this for bare git repos
693 shutil.move(os.path.join(rm_path, '.%s' % alias),
697 shutil.move(os.path.join(rm_path, '.%s' % alias),
694 os.path.join(rm_path, 'rm__.%s' % alias))
698 os.path.join(rm_path, 'rm__.%s' % alias))
695 # disable repo
699 # disable repo
696 _now = datetime.now()
700 _now = datetime.now()
697 _ms = str(_now.microsecond).rjust(6, '0')
701 _ms = str(_now.microsecond).rjust(6, '0')
698 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
702 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
699 repo.just_name)
703 repo.just_name)
700 if repo.group:
704 if repo.group:
701 args = repo.group.full_path_splitted + [_d]
705 args = repo.group.full_path_splitted + [_d]
702 _d = os.path.join(*args)
706 _d = os.path.join(*args)
703 shutil.move(rm_path, os.path.join(self.repos_path, _d))
707 shutil.move(rm_path, os.path.join(self.repos_path, _d))
General Comments 0
You need to be logged in to leave comments. Login now