##// END OF EJS Templates
vcs: Add method to return the base path to the repo store.
Martin Bornhold -
r555:9193199d default
parent child Browse files
Show More
@@ -1,1001 +1,1011 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Utilities library for RhodeCode
22 Utilities library for RhodeCode
23 """
23 """
24
24
25 import datetime
25 import datetime
26 import decorator
26 import decorator
27 import json
27 import json
28 import logging
28 import logging
29 import os
29 import os
30 import re
30 import re
31 import shutil
31 import shutil
32 import tempfile
32 import tempfile
33 import traceback
33 import traceback
34 import tarfile
34 import tarfile
35 import warnings
35 import warnings
36 from os.path import join as jn
36 from os.path import join as jn
37
37
38 import paste
38 import paste
39 import pkg_resources
39 import pkg_resources
40 from paste.script.command import Command, BadCommand
40 from paste.script.command import Command, BadCommand
41 from webhelpers.text import collapse, remove_formatting, strip_tags
41 from webhelpers.text import collapse, remove_formatting, strip_tags
42 from mako import exceptions
42 from mako import exceptions
43 from pyramid.threadlocal import get_current_registry
43 from pyramid.threadlocal import get_current_registry
44
44
45 from rhodecode.lib.fakemod import create_module
45 from rhodecode.lib.fakemod import create_module
46 from rhodecode.lib.vcs.backends.base import Config
46 from rhodecode.lib.vcs.backends.base import Config
47 from rhodecode.lib.vcs.exceptions import VCSError
47 from rhodecode.lib.vcs.exceptions import VCSError
48 from rhodecode.lib.vcs.utils.helpers import get_scm, get_scm_backend
48 from rhodecode.lib.vcs.utils.helpers import get_scm, get_scm_backend
49 from rhodecode.lib.utils2 import (
49 from rhodecode.lib.utils2 import (
50 safe_str, safe_unicode, get_current_rhodecode_user, md5)
50 safe_str, safe_unicode, get_current_rhodecode_user, md5)
51 from rhodecode.model import meta
51 from rhodecode.model import meta
52 from rhodecode.model.db import (
52 from rhodecode.model.db import (
53 Repository, User, RhodeCodeUi, UserLog, RepoGroup, UserGroup)
53 Repository, User, RhodeCodeUi, UserLog, RepoGroup, UserGroup)
54 from rhodecode.model.meta import Session
54 from rhodecode.model.meta import Session
55
55
56
56
57 log = logging.getLogger(__name__)
57 log = logging.getLogger(__name__)
58
58
59 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
59 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
60
60
61 _license_cache = None
61 _license_cache = None
62
62
63
63
64 def recursive_replace(str_, replace=' '):
64 def recursive_replace(str_, replace=' '):
65 """
65 """
66 Recursive replace of given sign to just one instance
66 Recursive replace of given sign to just one instance
67
67
68 :param str_: given string
68 :param str_: given string
69 :param replace: char to find and replace multiple instances
69 :param replace: char to find and replace multiple instances
70
70
71 Examples::
71 Examples::
72 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
72 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
73 'Mighty-Mighty-Bo-sstones'
73 'Mighty-Mighty-Bo-sstones'
74 """
74 """
75
75
76 if str_.find(replace * 2) == -1:
76 if str_.find(replace * 2) == -1:
77 return str_
77 return str_
78 else:
78 else:
79 str_ = str_.replace(replace * 2, replace)
79 str_ = str_.replace(replace * 2, replace)
80 return recursive_replace(str_, replace)
80 return recursive_replace(str_, replace)
81
81
82
82
83 def repo_name_slug(value):
83 def repo_name_slug(value):
84 """
84 """
85 Return slug of name of repository
85 Return slug of name of repository
86 This function is called on each creation/modification
86 This function is called on each creation/modification
87 of repository to prevent bad names in repo
87 of repository to prevent bad names in repo
88 """
88 """
89
89
90 slug = remove_formatting(value)
90 slug = remove_formatting(value)
91 slug = strip_tags(slug)
91 slug = strip_tags(slug)
92
92
93 for c in """`?=[]\;'"<>,/~!@#$%^&*()+{}|: """:
93 for c in """`?=[]\;'"<>,/~!@#$%^&*()+{}|: """:
94 slug = slug.replace(c, '-')
94 slug = slug.replace(c, '-')
95 slug = recursive_replace(slug, '-')
95 slug = recursive_replace(slug, '-')
96 slug = collapse(slug, '-')
96 slug = collapse(slug, '-')
97 return slug
97 return slug
98
98
99
99
100 #==============================================================================
100 #==============================================================================
101 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
101 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
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_repo_group_slug(request):
110 def get_repo_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 get_user_group_slug(request):
117 def get_user_group_slug(request):
118 _group = request.environ['pylons.routes_dict'].get('user_group_id')
118 _group = request.environ['pylons.routes_dict'].get('user_group_id')
119 try:
119 try:
120 _group = UserGroup.get(_group)
120 _group = UserGroup.get(_group)
121 if _group:
121 if _group:
122 _group = _group.users_group_name
122 _group = _group.users_group_name
123 except Exception:
123 except Exception:
124 log.debug(traceback.format_exc())
124 log.debug(traceback.format_exc())
125 #catch all failures here
125 #catch all failures here
126 pass
126 pass
127
127
128 return _group
128 return _group
129
129
130
130
131 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
131 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
132 """
132 """
133 Action logger for various actions made by users
133 Action logger for various actions made by users
134
134
135 :param user: user that made this action, can be a unique username string or
135 :param user: user that made this action, can be a unique username string or
136 object containing user_id attribute
136 object containing user_id attribute
137 :param action: action to log, should be on of predefined unique actions for
137 :param action: action to log, should be on of predefined unique actions for
138 easy translations
138 easy translations
139 :param repo: string name of repository or object containing repo_id,
139 :param repo: string name of repository or object containing repo_id,
140 that action was made on
140 that action was made on
141 :param ipaddr: optional ip address from what the action was made
141 :param ipaddr: optional ip address from what the action was made
142 :param sa: optional sqlalchemy session
142 :param sa: optional sqlalchemy session
143
143
144 """
144 """
145
145
146 if not sa:
146 if not sa:
147 sa = meta.Session()
147 sa = meta.Session()
148 # if we don't get explicit IP address try to get one from registered user
148 # if we don't get explicit IP address try to get one from registered user
149 # in tmpl context var
149 # in tmpl context var
150 if not ipaddr:
150 if not ipaddr:
151 ipaddr = getattr(get_current_rhodecode_user(), 'ip_addr', '')
151 ipaddr = getattr(get_current_rhodecode_user(), 'ip_addr', '')
152
152
153 try:
153 try:
154 if getattr(user, 'user_id', None):
154 if getattr(user, 'user_id', None):
155 user_obj = User.get(user.user_id)
155 user_obj = User.get(user.user_id)
156 elif isinstance(user, basestring):
156 elif isinstance(user, basestring):
157 user_obj = User.get_by_username(user)
157 user_obj = User.get_by_username(user)
158 else:
158 else:
159 raise Exception('You have to provide a user object or a username')
159 raise Exception('You have to provide a user object or a username')
160
160
161 if getattr(repo, 'repo_id', None):
161 if getattr(repo, 'repo_id', None):
162 repo_obj = Repository.get(repo.repo_id)
162 repo_obj = Repository.get(repo.repo_id)
163 repo_name = repo_obj.repo_name
163 repo_name = repo_obj.repo_name
164 elif isinstance(repo, basestring):
164 elif isinstance(repo, basestring):
165 repo_name = repo.lstrip('/')
165 repo_name = repo.lstrip('/')
166 repo_obj = Repository.get_by_repo_name(repo_name)
166 repo_obj = Repository.get_by_repo_name(repo_name)
167 else:
167 else:
168 repo_obj = None
168 repo_obj = None
169 repo_name = ''
169 repo_name = ''
170
170
171 user_log = UserLog()
171 user_log = UserLog()
172 user_log.user_id = user_obj.user_id
172 user_log.user_id = user_obj.user_id
173 user_log.username = user_obj.username
173 user_log.username = user_obj.username
174 action = safe_unicode(action)
174 action = safe_unicode(action)
175 user_log.action = action[:1200000]
175 user_log.action = action[:1200000]
176
176
177 user_log.repository = repo_obj
177 user_log.repository = repo_obj
178 user_log.repository_name = repo_name
178 user_log.repository_name = repo_name
179
179
180 user_log.action_date = datetime.datetime.now()
180 user_log.action_date = datetime.datetime.now()
181 user_log.user_ip = ipaddr
181 user_log.user_ip = ipaddr
182 sa.add(user_log)
182 sa.add(user_log)
183
183
184 log.info('Logging action:`%s` on repo:`%s` by user:%s ip:%s',
184 log.info('Logging action:`%s` on repo:`%s` by user:%s ip:%s',
185 action, safe_unicode(repo), user_obj, ipaddr)
185 action, safe_unicode(repo), user_obj, ipaddr)
186 if commit:
186 if commit:
187 sa.commit()
187 sa.commit()
188 except Exception:
188 except Exception:
189 log.error(traceback.format_exc())
189 log.error(traceback.format_exc())
190 raise
190 raise
191
191
192
192
193 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
193 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
194 """
194 """
195 Scans given path for repos and return (name,(type,path)) tuple
195 Scans given path for repos and return (name,(type,path)) tuple
196
196
197 :param path: path to scan for repositories
197 :param path: path to scan for repositories
198 :param recursive: recursive search and return names with subdirs in front
198 :param recursive: recursive search and return names with subdirs in front
199 """
199 """
200
200
201 # remove ending slash for better results
201 # remove ending slash for better results
202 path = path.rstrip(os.sep)
202 path = path.rstrip(os.sep)
203 log.debug('now scanning in %s location recursive:%s...', path, recursive)
203 log.debug('now scanning in %s location recursive:%s...', path, recursive)
204
204
205 def _get_repos(p):
205 def _get_repos(p):
206 dirpaths = _get_dirpaths(p)
206 dirpaths = _get_dirpaths(p)
207 if not _is_dir_writable(p):
207 if not _is_dir_writable(p):
208 log.warning('repo path without write access: %s', p)
208 log.warning('repo path without write access: %s', p)
209
209
210 for dirpath in dirpaths:
210 for dirpath in dirpaths:
211 if os.path.isfile(os.path.join(p, dirpath)):
211 if os.path.isfile(os.path.join(p, dirpath)):
212 continue
212 continue
213 cur_path = os.path.join(p, dirpath)
213 cur_path = os.path.join(p, dirpath)
214
214
215 # skip removed repos
215 # skip removed repos
216 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
216 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
217 continue
217 continue
218
218
219 #skip .<somethin> dirs
219 #skip .<somethin> dirs
220 if dirpath.startswith('.'):
220 if dirpath.startswith('.'):
221 continue
221 continue
222
222
223 try:
223 try:
224 scm_info = get_scm(cur_path)
224 scm_info = get_scm(cur_path)
225 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
225 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
226 except VCSError:
226 except VCSError:
227 if not recursive:
227 if not recursive:
228 continue
228 continue
229 #check if this dir containts other repos for recursive scan
229 #check if this dir containts other repos for recursive scan
230 rec_path = os.path.join(p, dirpath)
230 rec_path = os.path.join(p, dirpath)
231 if os.path.isdir(rec_path):
231 if os.path.isdir(rec_path):
232 for inner_scm in _get_repos(rec_path):
232 for inner_scm in _get_repos(rec_path):
233 yield inner_scm
233 yield inner_scm
234
234
235 return _get_repos(path)
235 return _get_repos(path)
236
236
237
237
238 def _get_dirpaths(p):
238 def _get_dirpaths(p):
239 try:
239 try:
240 # OS-independable way of checking if we have at least read-only
240 # OS-independable way of checking if we have at least read-only
241 # access or not.
241 # access or not.
242 dirpaths = os.listdir(p)
242 dirpaths = os.listdir(p)
243 except OSError:
243 except OSError:
244 log.warning('ignoring repo path without read access: %s', p)
244 log.warning('ignoring repo path without read access: %s', p)
245 return []
245 return []
246
246
247 # os.listpath has a tweak: If a unicode is passed into it, then it tries to
247 # os.listpath has a tweak: If a unicode is passed into it, then it tries to
248 # decode paths and suddenly returns unicode objects itself. The items it
248 # decode paths and suddenly returns unicode objects itself. The items it
249 # cannot decode are returned as strings and cause issues.
249 # cannot decode are returned as strings and cause issues.
250 #
250 #
251 # Those paths are ignored here until a solid solution for path handling has
251 # Those paths are ignored here until a solid solution for path handling has
252 # been built.
252 # been built.
253 expected_type = type(p)
253 expected_type = type(p)
254
254
255 def _has_correct_type(item):
255 def _has_correct_type(item):
256 if type(item) is not expected_type:
256 if type(item) is not expected_type:
257 log.error(
257 log.error(
258 u"Ignoring path %s since it cannot be decoded into unicode.",
258 u"Ignoring path %s since it cannot be decoded into unicode.",
259 # Using "repr" to make sure that we see the byte value in case
259 # Using "repr" to make sure that we see the byte value in case
260 # of support.
260 # of support.
261 repr(item))
261 repr(item))
262 return False
262 return False
263 return True
263 return True
264
264
265 dirpaths = [item for item in dirpaths if _has_correct_type(item)]
265 dirpaths = [item for item in dirpaths if _has_correct_type(item)]
266
266
267 return dirpaths
267 return dirpaths
268
268
269
269
270 def _is_dir_writable(path):
270 def _is_dir_writable(path):
271 """
271 """
272 Probe if `path` is writable.
272 Probe if `path` is writable.
273
273
274 Due to trouble on Cygwin / Windows, this is actually probing if it is
274 Due to trouble on Cygwin / Windows, this is actually probing if it is
275 possible to create a file inside of `path`, stat does not produce reliable
275 possible to create a file inside of `path`, stat does not produce reliable
276 results in this case.
276 results in this case.
277 """
277 """
278 try:
278 try:
279 with tempfile.TemporaryFile(dir=path):
279 with tempfile.TemporaryFile(dir=path):
280 pass
280 pass
281 except OSError:
281 except OSError:
282 return False
282 return False
283 return True
283 return True
284
284
285
285
286 def is_valid_repo(repo_name, base_path, expect_scm=None, explicit_scm=None):
286 def is_valid_repo(repo_name, base_path, expect_scm=None, explicit_scm=None):
287 """
287 """
288 Returns True if given path is a valid repository False otherwise.
288 Returns True if given path is a valid repository False otherwise.
289 If expect_scm param is given also, compare if given scm is the same
289 If expect_scm param is given also, compare if given scm is the same
290 as expected from scm parameter. If explicit_scm is given don't try to
290 as expected from scm parameter. If explicit_scm is given don't try to
291 detect the scm, just use the given one to check if repo is valid
291 detect the scm, just use the given one to check if repo is valid
292
292
293 :param repo_name:
293 :param repo_name:
294 :param base_path:
294 :param base_path:
295 :param expect_scm:
295 :param expect_scm:
296 :param explicit_scm:
296 :param explicit_scm:
297
297
298 :return True: if given path is a valid repository
298 :return True: if given path is a valid repository
299 """
299 """
300 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
300 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
301 log.debug('Checking if `%s` is a valid path for repository', repo_name)
301 log.debug('Checking if `%s` is a valid path for repository', repo_name)
302
302
303 try:
303 try:
304 if explicit_scm:
304 if explicit_scm:
305 detected_scms = [get_scm_backend(explicit_scm)]
305 detected_scms = [get_scm_backend(explicit_scm)]
306 else:
306 else:
307 detected_scms = get_scm(full_path)
307 detected_scms = get_scm(full_path)
308
308
309 if expect_scm:
309 if expect_scm:
310 return detected_scms[0] == expect_scm
310 return detected_scms[0] == expect_scm
311 log.debug('path: %s is an vcs object:%s', full_path, detected_scms)
311 log.debug('path: %s is an vcs object:%s', full_path, detected_scms)
312 return True
312 return True
313 except VCSError:
313 except VCSError:
314 log.debug('path: %s is not a valid repo !', full_path)
314 log.debug('path: %s is not a valid repo !', full_path)
315 return False
315 return False
316
316
317
317
318 def is_valid_repo_group(repo_group_name, base_path, skip_path_check=False):
318 def is_valid_repo_group(repo_group_name, base_path, skip_path_check=False):
319 """
319 """
320 Returns True if given path is a repository group, False otherwise
320 Returns True if given path is a repository group, False otherwise
321
321
322 :param repo_name:
322 :param repo_name:
323 :param base_path:
323 :param base_path:
324 """
324 """
325 full_path = os.path.join(safe_str(base_path), safe_str(repo_group_name))
325 full_path = os.path.join(safe_str(base_path), safe_str(repo_group_name))
326 log.debug('Checking if `%s` is a valid path for repository group',
326 log.debug('Checking if `%s` is a valid path for repository group',
327 repo_group_name)
327 repo_group_name)
328
328
329 # check if it's not a repo
329 # check if it's not a repo
330 if is_valid_repo(repo_group_name, base_path):
330 if is_valid_repo(repo_group_name, base_path):
331 log.debug('Repo called %s exist, it is not a valid '
331 log.debug('Repo called %s exist, it is not a valid '
332 'repo group' % repo_group_name)
332 'repo group' % repo_group_name)
333 return False
333 return False
334
334
335 try:
335 try:
336 # we need to check bare git repos at higher level
336 # we need to check bare git repos at higher level
337 # since we might match branches/hooks/info/objects or possible
337 # since we might match branches/hooks/info/objects or possible
338 # other things inside bare git repo
338 # other things inside bare git repo
339 scm_ = get_scm(os.path.dirname(full_path))
339 scm_ = get_scm(os.path.dirname(full_path))
340 log.debug('path: %s is a vcs object:%s, not valid '
340 log.debug('path: %s is a vcs object:%s, not valid '
341 'repo group' % (full_path, scm_))
341 'repo group' % (full_path, scm_))
342 return False
342 return False
343 except VCSError:
343 except VCSError:
344 pass
344 pass
345
345
346 # check if it's a valid path
346 # check if it's a valid path
347 if skip_path_check or os.path.isdir(full_path):
347 if skip_path_check or os.path.isdir(full_path):
348 log.debug('path: %s is a valid repo group !', full_path)
348 log.debug('path: %s is a valid repo group !', full_path)
349 return True
349 return True
350
350
351 log.debug('path: %s is not a valid repo group !', full_path)
351 log.debug('path: %s is not a valid repo group !', full_path)
352 return False
352 return False
353
353
354
354
355 def ask_ok(prompt, retries=4, complaint='[y]es or [n]o please!'):
355 def ask_ok(prompt, retries=4, complaint='[y]es or [n]o please!'):
356 while True:
356 while True:
357 ok = raw_input(prompt)
357 ok = raw_input(prompt)
358 if ok.lower() in ('y', 'ye', 'yes'):
358 if ok.lower() in ('y', 'ye', 'yes'):
359 return True
359 return True
360 if ok.lower() in ('n', 'no', 'nop', 'nope'):
360 if ok.lower() in ('n', 'no', 'nop', 'nope'):
361 return False
361 return False
362 retries = retries - 1
362 retries = retries - 1
363 if retries < 0:
363 if retries < 0:
364 raise IOError
364 raise IOError
365 print(complaint)
365 print(complaint)
366
366
367 # propagated from mercurial documentation
367 # propagated from mercurial documentation
368 ui_sections = [
368 ui_sections = [
369 'alias', 'auth',
369 'alias', 'auth',
370 'decode/encode', 'defaults',
370 'decode/encode', 'defaults',
371 'diff', 'email',
371 'diff', 'email',
372 'extensions', 'format',
372 'extensions', 'format',
373 'merge-patterns', 'merge-tools',
373 'merge-patterns', 'merge-tools',
374 'hooks', 'http_proxy',
374 'hooks', 'http_proxy',
375 'smtp', 'patch',
375 'smtp', 'patch',
376 'paths', 'profiling',
376 'paths', 'profiling',
377 'server', 'trusted',
377 'server', 'trusted',
378 'ui', 'web', ]
378 'ui', 'web', ]
379
379
380
380
381 def config_data_from_db(clear_session=True, repo=None):
381 def config_data_from_db(clear_session=True, repo=None):
382 """
382 """
383 Read the configuration data from the database and return configuration
383 Read the configuration data from the database and return configuration
384 tuples.
384 tuples.
385 """
385 """
386 from rhodecode.model.settings import VcsSettingsModel
386 from rhodecode.model.settings import VcsSettingsModel
387
387
388 config = []
388 config = []
389
389
390 sa = meta.Session()
390 sa = meta.Session()
391 settings_model = VcsSettingsModel(repo=repo, sa=sa)
391 settings_model = VcsSettingsModel(repo=repo, sa=sa)
392
392
393 ui_settings = settings_model.get_ui_settings()
393 ui_settings = settings_model.get_ui_settings()
394
394
395 for setting in ui_settings:
395 for setting in ui_settings:
396 if setting.active:
396 if setting.active:
397 log.debug(
397 log.debug(
398 'settings ui from db: [%s] %s=%s',
398 'settings ui from db: [%s] %s=%s',
399 setting.section, setting.key, setting.value)
399 setting.section, setting.key, setting.value)
400 config.append((
400 config.append((
401 safe_str(setting.section), safe_str(setting.key),
401 safe_str(setting.section), safe_str(setting.key),
402 safe_str(setting.value)))
402 safe_str(setting.value)))
403 if setting.key == 'push_ssl':
403 if setting.key == 'push_ssl':
404 # force set push_ssl requirement to False, rhodecode
404 # force set push_ssl requirement to False, rhodecode
405 # handles that
405 # handles that
406 config.append((
406 config.append((
407 safe_str(setting.section), safe_str(setting.key), False))
407 safe_str(setting.section), safe_str(setting.key), False))
408 if clear_session:
408 if clear_session:
409 meta.Session.remove()
409 meta.Session.remove()
410
410
411 # TODO: mikhail: probably it makes no sense to re-read hooks information.
411 # TODO: mikhail: probably it makes no sense to re-read hooks information.
412 # It's already there and activated/deactivated
412 # It's already there and activated/deactivated
413 skip_entries = []
413 skip_entries = []
414 enabled_hook_classes = get_enabled_hook_classes(ui_settings)
414 enabled_hook_classes = get_enabled_hook_classes(ui_settings)
415 if 'pull' not in enabled_hook_classes:
415 if 'pull' not in enabled_hook_classes:
416 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PULL))
416 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PULL))
417 if 'push' not in enabled_hook_classes:
417 if 'push' not in enabled_hook_classes:
418 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PUSH))
418 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PUSH))
419
419
420 config = [entry for entry in config if entry[:2] not in skip_entries]
420 config = [entry for entry in config if entry[:2] not in skip_entries]
421
421
422 return config
422 return config
423
423
424
424
425 def make_db_config(clear_session=True, repo=None):
425 def make_db_config(clear_session=True, repo=None):
426 """
426 """
427 Create a :class:`Config` instance based on the values in the database.
427 Create a :class:`Config` instance based on the values in the database.
428 """
428 """
429 config = Config()
429 config = Config()
430 config_data = config_data_from_db(clear_session=clear_session, repo=repo)
430 config_data = config_data_from_db(clear_session=clear_session, repo=repo)
431 for section, option, value in config_data:
431 for section, option, value in config_data:
432 config.set(section, option, value)
432 config.set(section, option, value)
433 return config
433 return config
434
434
435
435
436 def get_enabled_hook_classes(ui_settings):
436 def get_enabled_hook_classes(ui_settings):
437 """
437 """
438 Return the enabled hook classes.
438 Return the enabled hook classes.
439
439
440 :param ui_settings: List of ui_settings as returned
440 :param ui_settings: List of ui_settings as returned
441 by :meth:`VcsSettingsModel.get_ui_settings`
441 by :meth:`VcsSettingsModel.get_ui_settings`
442
442
443 :return: a list with the enabled hook classes. The order is not guaranteed.
443 :return: a list with the enabled hook classes. The order is not guaranteed.
444 :rtype: list
444 :rtype: list
445 """
445 """
446 enabled_hooks = []
446 enabled_hooks = []
447 active_hook_keys = [
447 active_hook_keys = [
448 key for section, key, value, active in ui_settings
448 key for section, key, value, active in ui_settings
449 if section == 'hooks' and active]
449 if section == 'hooks' and active]
450
450
451 hook_names = {
451 hook_names = {
452 RhodeCodeUi.HOOK_PUSH: 'push',
452 RhodeCodeUi.HOOK_PUSH: 'push',
453 RhodeCodeUi.HOOK_PULL: 'pull',
453 RhodeCodeUi.HOOK_PULL: 'pull',
454 RhodeCodeUi.HOOK_REPO_SIZE: 'repo_size'
454 RhodeCodeUi.HOOK_REPO_SIZE: 'repo_size'
455 }
455 }
456
456
457 for key in active_hook_keys:
457 for key in active_hook_keys:
458 hook = hook_names.get(key)
458 hook = hook_names.get(key)
459 if hook:
459 if hook:
460 enabled_hooks.append(hook)
460 enabled_hooks.append(hook)
461
461
462 return enabled_hooks
462 return enabled_hooks
463
463
464
464
465 def set_rhodecode_config(config):
465 def set_rhodecode_config(config):
466 """
466 """
467 Updates pylons config with new settings from database
467 Updates pylons config with new settings from database
468
468
469 :param config:
469 :param config:
470 """
470 """
471 from rhodecode.model.settings import SettingsModel
471 from rhodecode.model.settings import SettingsModel
472 app_settings = SettingsModel().get_all_settings()
472 app_settings = SettingsModel().get_all_settings()
473
473
474 for k, v in app_settings.items():
474 for k, v in app_settings.items():
475 config[k] = v
475 config[k] = v
476
476
477
477
478 def get_rhodecode_realm():
478 def get_rhodecode_realm():
479 """
479 """
480 Return the rhodecode realm from database.
480 Return the rhodecode realm from database.
481 """
481 """
482 from rhodecode.model.settings import SettingsModel
482 from rhodecode.model.settings import SettingsModel
483 realm = SettingsModel().get_setting_by_name('realm')
483 realm = SettingsModel().get_setting_by_name('realm')
484 return safe_str(realm.app_settings_value)
484 return safe_str(realm.app_settings_value)
485
485
486
486
487 def get_rhodecode_base_path():
488 """
489 Returns the base path. The base path is the filesystem path which points
490 to the repository store.
491 """
492 from rhodecode.model.settings import SettingsModel
493 paths_ui = SettingsModel().get_ui_by_section_and_key('paths', '/')
494 return safe_str(paths_ui.ui_value)
495
496
487 def map_groups(path):
497 def map_groups(path):
488 """
498 """
489 Given a full path to a repository, create all nested groups that this
499 Given a full path to a repository, create all nested groups that this
490 repo is inside. This function creates parent-child relationships between
500 repo is inside. This function creates parent-child relationships between
491 groups and creates default perms for all new groups.
501 groups and creates default perms for all new groups.
492
502
493 :param paths: full path to repository
503 :param paths: full path to repository
494 """
504 """
495 from rhodecode.model.repo_group import RepoGroupModel
505 from rhodecode.model.repo_group import RepoGroupModel
496 sa = meta.Session()
506 sa = meta.Session()
497 groups = path.split(Repository.NAME_SEP)
507 groups = path.split(Repository.NAME_SEP)
498 parent = None
508 parent = None
499 group = None
509 group = None
500
510
501 # last element is repo in nested groups structure
511 # last element is repo in nested groups structure
502 groups = groups[:-1]
512 groups = groups[:-1]
503 rgm = RepoGroupModel(sa)
513 rgm = RepoGroupModel(sa)
504 owner = User.get_first_super_admin()
514 owner = User.get_first_super_admin()
505 for lvl, group_name in enumerate(groups):
515 for lvl, group_name in enumerate(groups):
506 group_name = '/'.join(groups[:lvl] + [group_name])
516 group_name = '/'.join(groups[:lvl] + [group_name])
507 group = RepoGroup.get_by_group_name(group_name)
517 group = RepoGroup.get_by_group_name(group_name)
508 desc = '%s group' % group_name
518 desc = '%s group' % group_name
509
519
510 # skip folders that are now removed repos
520 # skip folders that are now removed repos
511 if REMOVED_REPO_PAT.match(group_name):
521 if REMOVED_REPO_PAT.match(group_name):
512 break
522 break
513
523
514 if group is None:
524 if group is None:
515 log.debug('creating group level: %s group_name: %s',
525 log.debug('creating group level: %s group_name: %s',
516 lvl, group_name)
526 lvl, group_name)
517 group = RepoGroup(group_name, parent)
527 group = RepoGroup(group_name, parent)
518 group.group_description = desc
528 group.group_description = desc
519 group.user = owner
529 group.user = owner
520 sa.add(group)
530 sa.add(group)
521 perm_obj = rgm._create_default_perms(group)
531 perm_obj = rgm._create_default_perms(group)
522 sa.add(perm_obj)
532 sa.add(perm_obj)
523 sa.flush()
533 sa.flush()
524
534
525 parent = group
535 parent = group
526 return group
536 return group
527
537
528
538
529 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
539 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
530 """
540 """
531 maps all repos given in initial_repo_list, non existing repositories
541 maps all repos given in initial_repo_list, non existing repositories
532 are created, if remove_obsolete is True it also checks for db entries
542 are created, if remove_obsolete is True it also checks for db entries
533 that are not in initial_repo_list and removes them.
543 that are not in initial_repo_list and removes them.
534
544
535 :param initial_repo_list: list of repositories found by scanning methods
545 :param initial_repo_list: list of repositories found by scanning methods
536 :param remove_obsolete: check for obsolete entries in database
546 :param remove_obsolete: check for obsolete entries in database
537 """
547 """
538 from rhodecode.model.repo import RepoModel
548 from rhodecode.model.repo import RepoModel
539 from rhodecode.model.scm import ScmModel
549 from rhodecode.model.scm import ScmModel
540 from rhodecode.model.repo_group import RepoGroupModel
550 from rhodecode.model.repo_group import RepoGroupModel
541 from rhodecode.model.settings import SettingsModel
551 from rhodecode.model.settings import SettingsModel
542
552
543 sa = meta.Session()
553 sa = meta.Session()
544 repo_model = RepoModel()
554 repo_model = RepoModel()
545 user = User.get_first_super_admin()
555 user = User.get_first_super_admin()
546 added = []
556 added = []
547
557
548 # creation defaults
558 # creation defaults
549 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
559 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
550 enable_statistics = defs.get('repo_enable_statistics')
560 enable_statistics = defs.get('repo_enable_statistics')
551 enable_locking = defs.get('repo_enable_locking')
561 enable_locking = defs.get('repo_enable_locking')
552 enable_downloads = defs.get('repo_enable_downloads')
562 enable_downloads = defs.get('repo_enable_downloads')
553 private = defs.get('repo_private')
563 private = defs.get('repo_private')
554
564
555 for name, repo in initial_repo_list.items():
565 for name, repo in initial_repo_list.items():
556 group = map_groups(name)
566 group = map_groups(name)
557 unicode_name = safe_unicode(name)
567 unicode_name = safe_unicode(name)
558 db_repo = repo_model.get_by_repo_name(unicode_name)
568 db_repo = repo_model.get_by_repo_name(unicode_name)
559 # found repo that is on filesystem not in RhodeCode database
569 # found repo that is on filesystem not in RhodeCode database
560 if not db_repo:
570 if not db_repo:
561 log.info('repository %s not found, creating now', name)
571 log.info('repository %s not found, creating now', name)
562 added.append(name)
572 added.append(name)
563 desc = (repo.description
573 desc = (repo.description
564 if repo.description != 'unknown'
574 if repo.description != 'unknown'
565 else '%s repository' % name)
575 else '%s repository' % name)
566
576
567 db_repo = repo_model._create_repo(
577 db_repo = repo_model._create_repo(
568 repo_name=name,
578 repo_name=name,
569 repo_type=repo.alias,
579 repo_type=repo.alias,
570 description=desc,
580 description=desc,
571 repo_group=getattr(group, 'group_id', None),
581 repo_group=getattr(group, 'group_id', None),
572 owner=user,
582 owner=user,
573 enable_locking=enable_locking,
583 enable_locking=enable_locking,
574 enable_downloads=enable_downloads,
584 enable_downloads=enable_downloads,
575 enable_statistics=enable_statistics,
585 enable_statistics=enable_statistics,
576 private=private,
586 private=private,
577 state=Repository.STATE_CREATED
587 state=Repository.STATE_CREATED
578 )
588 )
579 sa.commit()
589 sa.commit()
580 # we added that repo just now, and make sure we updated server info
590 # we added that repo just now, and make sure we updated server info
581 if db_repo.repo_type == 'git':
591 if db_repo.repo_type == 'git':
582 git_repo = db_repo.scm_instance()
592 git_repo = db_repo.scm_instance()
583 # update repository server-info
593 # update repository server-info
584 log.debug('Running update server info')
594 log.debug('Running update server info')
585 git_repo._update_server_info()
595 git_repo._update_server_info()
586
596
587 db_repo.update_commit_cache()
597 db_repo.update_commit_cache()
588
598
589 config = db_repo._config
599 config = db_repo._config
590 config.set('extensions', 'largefiles', '')
600 config.set('extensions', 'largefiles', '')
591 ScmModel().install_hooks(
601 ScmModel().install_hooks(
592 db_repo.scm_instance(config=config),
602 db_repo.scm_instance(config=config),
593 repo_type=db_repo.repo_type)
603 repo_type=db_repo.repo_type)
594
604
595 removed = []
605 removed = []
596 if remove_obsolete:
606 if remove_obsolete:
597 # remove from database those repositories that are not in the filesystem
607 # remove from database those repositories that are not in the filesystem
598 for repo in sa.query(Repository).all():
608 for repo in sa.query(Repository).all():
599 if repo.repo_name not in initial_repo_list.keys():
609 if repo.repo_name not in initial_repo_list.keys():
600 log.debug("Removing non-existing repository found in db `%s`",
610 log.debug("Removing non-existing repository found in db `%s`",
601 repo.repo_name)
611 repo.repo_name)
602 try:
612 try:
603 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
613 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
604 sa.commit()
614 sa.commit()
605 removed.append(repo.repo_name)
615 removed.append(repo.repo_name)
606 except Exception:
616 except Exception:
607 # don't hold further removals on error
617 # don't hold further removals on error
608 log.error(traceback.format_exc())
618 log.error(traceback.format_exc())
609 sa.rollback()
619 sa.rollback()
610
620
611 def splitter(full_repo_name):
621 def splitter(full_repo_name):
612 _parts = full_repo_name.rsplit(RepoGroup.url_sep(), 1)
622 _parts = full_repo_name.rsplit(RepoGroup.url_sep(), 1)
613 gr_name = None
623 gr_name = None
614 if len(_parts) == 2:
624 if len(_parts) == 2:
615 gr_name = _parts[0]
625 gr_name = _parts[0]
616 return gr_name
626 return gr_name
617
627
618 initial_repo_group_list = [splitter(x) for x in
628 initial_repo_group_list = [splitter(x) for x in
619 initial_repo_list.keys() if splitter(x)]
629 initial_repo_list.keys() if splitter(x)]
620
630
621 # remove from database those repository groups that are not in the
631 # remove from database those repository groups that are not in the
622 # filesystem due to parent child relationships we need to delete them
632 # filesystem due to parent child relationships we need to delete them
623 # in a specific order of most nested first
633 # in a specific order of most nested first
624 all_groups = [x.group_name for x in sa.query(RepoGroup).all()]
634 all_groups = [x.group_name for x in sa.query(RepoGroup).all()]
625 nested_sort = lambda gr: len(gr.split('/'))
635 nested_sort = lambda gr: len(gr.split('/'))
626 for group_name in sorted(all_groups, key=nested_sort, reverse=True):
636 for group_name in sorted(all_groups, key=nested_sort, reverse=True):
627 if group_name not in initial_repo_group_list:
637 if group_name not in initial_repo_group_list:
628 repo_group = RepoGroup.get_by_group_name(group_name)
638 repo_group = RepoGroup.get_by_group_name(group_name)
629 if (repo_group.children.all() or
639 if (repo_group.children.all() or
630 not RepoGroupModel().check_exist_filesystem(
640 not RepoGroupModel().check_exist_filesystem(
631 group_name=group_name, exc_on_failure=False)):
641 group_name=group_name, exc_on_failure=False)):
632 continue
642 continue
633
643
634 log.info(
644 log.info(
635 'Removing non-existing repository group found in db `%s`',
645 'Removing non-existing repository group found in db `%s`',
636 group_name)
646 group_name)
637 try:
647 try:
638 RepoGroupModel(sa).delete(group_name, fs_remove=False)
648 RepoGroupModel(sa).delete(group_name, fs_remove=False)
639 sa.commit()
649 sa.commit()
640 removed.append(group_name)
650 removed.append(group_name)
641 except Exception:
651 except Exception:
642 # don't hold further removals on error
652 # don't hold further removals on error
643 log.exception(
653 log.exception(
644 'Unable to remove repository group `%s`',
654 'Unable to remove repository group `%s`',
645 group_name)
655 group_name)
646 sa.rollback()
656 sa.rollback()
647 raise
657 raise
648
658
649 return added, removed
659 return added, removed
650
660
651
661
652 def get_default_cache_settings(settings):
662 def get_default_cache_settings(settings):
653 cache_settings = {}
663 cache_settings = {}
654 for key in settings.keys():
664 for key in settings.keys():
655 for prefix in ['beaker.cache.', 'cache.']:
665 for prefix in ['beaker.cache.', 'cache.']:
656 if key.startswith(prefix):
666 if key.startswith(prefix):
657 name = key.split(prefix)[1].strip()
667 name = key.split(prefix)[1].strip()
658 cache_settings[name] = settings[key].strip()
668 cache_settings[name] = settings[key].strip()
659 return cache_settings
669 return cache_settings
660
670
661
671
662 # set cache regions for beaker so celery can utilise it
672 # set cache regions for beaker so celery can utilise it
663 def add_cache(settings):
673 def add_cache(settings):
664 from rhodecode.lib import caches
674 from rhodecode.lib import caches
665 cache_settings = {'regions': None}
675 cache_settings = {'regions': None}
666 # main cache settings used as default ...
676 # main cache settings used as default ...
667 cache_settings.update(get_default_cache_settings(settings))
677 cache_settings.update(get_default_cache_settings(settings))
668
678
669 if cache_settings['regions']:
679 if cache_settings['regions']:
670 for region in cache_settings['regions'].split(','):
680 for region in cache_settings['regions'].split(','):
671 region = region.strip()
681 region = region.strip()
672 region_settings = {}
682 region_settings = {}
673 for key, value in cache_settings.items():
683 for key, value in cache_settings.items():
674 if key.startswith(region):
684 if key.startswith(region):
675 region_settings[key.split('.')[1]] = value
685 region_settings[key.split('.')[1]] = value
676
686
677 caches.configure_cache_region(
687 caches.configure_cache_region(
678 region, region_settings, cache_settings)
688 region, region_settings, cache_settings)
679
689
680
690
681 def load_rcextensions(root_path):
691 def load_rcextensions(root_path):
682 import rhodecode
692 import rhodecode
683 from rhodecode.config import conf
693 from rhodecode.config import conf
684
694
685 path = os.path.join(root_path, 'rcextensions', '__init__.py')
695 path = os.path.join(root_path, 'rcextensions', '__init__.py')
686 if os.path.isfile(path):
696 if os.path.isfile(path):
687 rcext = create_module('rc', path)
697 rcext = create_module('rc', path)
688 EXT = rhodecode.EXTENSIONS = rcext
698 EXT = rhodecode.EXTENSIONS = rcext
689 log.debug('Found rcextensions now loading %s...', rcext)
699 log.debug('Found rcextensions now loading %s...', rcext)
690
700
691 # Additional mappings that are not present in the pygments lexers
701 # Additional mappings that are not present in the pygments lexers
692 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
702 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
693
703
694 # auto check if the module is not missing any data, set to default if is
704 # auto check if the module is not missing any data, set to default if is
695 # this will help autoupdate new feature of rcext module
705 # this will help autoupdate new feature of rcext module
696 #from rhodecode.config import rcextensions
706 #from rhodecode.config import rcextensions
697 #for k in dir(rcextensions):
707 #for k in dir(rcextensions):
698 # if not k.startswith('_') and not hasattr(EXT, k):
708 # if not k.startswith('_') and not hasattr(EXT, k):
699 # setattr(EXT, k, getattr(rcextensions, k))
709 # setattr(EXT, k, getattr(rcextensions, k))
700
710
701
711
702 def get_custom_lexer(extension):
712 def get_custom_lexer(extension):
703 """
713 """
704 returns a custom lexer if it is defined in rcextensions module, or None
714 returns a custom lexer if it is defined in rcextensions module, or None
705 if there's no custom lexer defined
715 if there's no custom lexer defined
706 """
716 """
707 import rhodecode
717 import rhodecode
708 from pygments import lexers
718 from pygments import lexers
709 # check if we didn't define this extension as other lexer
719 # check if we didn't define this extension as other lexer
710 extensions = rhodecode.EXTENSIONS and getattr(rhodecode.EXTENSIONS, 'EXTRA_LEXERS', None)
720 extensions = rhodecode.EXTENSIONS and getattr(rhodecode.EXTENSIONS, 'EXTRA_LEXERS', None)
711 if extensions and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
721 if extensions and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
712 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
722 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
713 return lexers.get_lexer_by_name(_lexer_name)
723 return lexers.get_lexer_by_name(_lexer_name)
714
724
715
725
716 #==============================================================================
726 #==============================================================================
717 # TEST FUNCTIONS AND CREATORS
727 # TEST FUNCTIONS AND CREATORS
718 #==============================================================================
728 #==============================================================================
719 def create_test_index(repo_location, config):
729 def create_test_index(repo_location, config):
720 """
730 """
721 Makes default test index.
731 Makes default test index.
722 """
732 """
723 import rc_testdata
733 import rc_testdata
724
734
725 rc_testdata.extract_search_index(
735 rc_testdata.extract_search_index(
726 'vcs_search_index', os.path.dirname(config['search.location']))
736 'vcs_search_index', os.path.dirname(config['search.location']))
727
737
728
738
729 def create_test_directory(test_path):
739 def create_test_directory(test_path):
730 """
740 """
731 Create test directory if it doesn't exist.
741 Create test directory if it doesn't exist.
732 """
742 """
733 if not os.path.isdir(test_path):
743 if not os.path.isdir(test_path):
734 log.debug('Creating testdir %s', test_path)
744 log.debug('Creating testdir %s', test_path)
735 os.makedirs(test_path)
745 os.makedirs(test_path)
736
746
737
747
738 def create_test_database(test_path, config):
748 def create_test_database(test_path, config):
739 """
749 """
740 Makes a fresh database.
750 Makes a fresh database.
741 """
751 """
742 from rhodecode.lib.db_manage import DbManage
752 from rhodecode.lib.db_manage import DbManage
743
753
744 # PART ONE create db
754 # PART ONE create db
745 dbconf = config['sqlalchemy.db1.url']
755 dbconf = config['sqlalchemy.db1.url']
746 log.debug('making test db %s', dbconf)
756 log.debug('making test db %s', dbconf)
747
757
748 dbmanage = DbManage(log_sql=False, dbconf=dbconf, root=config['here'],
758 dbmanage = DbManage(log_sql=False, dbconf=dbconf, root=config['here'],
749 tests=True, cli_args={'force_ask': True})
759 tests=True, cli_args={'force_ask': True})
750 dbmanage.create_tables(override=True)
760 dbmanage.create_tables(override=True)
751 dbmanage.set_db_version()
761 dbmanage.set_db_version()
752 # for tests dynamically set new root paths based on generated content
762 # for tests dynamically set new root paths based on generated content
753 dbmanage.create_settings(dbmanage.config_prompt(test_path))
763 dbmanage.create_settings(dbmanage.config_prompt(test_path))
754 dbmanage.create_default_user()
764 dbmanage.create_default_user()
755 dbmanage.create_test_admin_and_users()
765 dbmanage.create_test_admin_and_users()
756 dbmanage.create_permissions()
766 dbmanage.create_permissions()
757 dbmanage.populate_default_permissions()
767 dbmanage.populate_default_permissions()
758 Session().commit()
768 Session().commit()
759
769
760
770
761 def create_test_repositories(test_path, config):
771 def create_test_repositories(test_path, config):
762 """
772 """
763 Creates test repositories in the temporary directory. Repositories are
773 Creates test repositories in the temporary directory. Repositories are
764 extracted from archives within the rc_testdata package.
774 extracted from archives within the rc_testdata package.
765 """
775 """
766 import rc_testdata
776 import rc_testdata
767 from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO
777 from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO
768
778
769 log.debug('making test vcs repositories')
779 log.debug('making test vcs repositories')
770
780
771 idx_path = config['search.location']
781 idx_path = config['search.location']
772 data_path = config['cache_dir']
782 data_path = config['cache_dir']
773
783
774 # clean index and data
784 # clean index and data
775 if idx_path and os.path.exists(idx_path):
785 if idx_path and os.path.exists(idx_path):
776 log.debug('remove %s', idx_path)
786 log.debug('remove %s', idx_path)
777 shutil.rmtree(idx_path)
787 shutil.rmtree(idx_path)
778
788
779 if data_path and os.path.exists(data_path):
789 if data_path and os.path.exists(data_path):
780 log.debug('remove %s', data_path)
790 log.debug('remove %s', data_path)
781 shutil.rmtree(data_path)
791 shutil.rmtree(data_path)
782
792
783 rc_testdata.extract_hg_dump('vcs_test_hg', jn(test_path, HG_REPO))
793 rc_testdata.extract_hg_dump('vcs_test_hg', jn(test_path, HG_REPO))
784 rc_testdata.extract_git_dump('vcs_test_git', jn(test_path, GIT_REPO))
794 rc_testdata.extract_git_dump('vcs_test_git', jn(test_path, GIT_REPO))
785
795
786 # Note: Subversion is in the process of being integrated with the system,
796 # Note: Subversion is in the process of being integrated with the system,
787 # until we have a properly packed version of the test svn repository, this
797 # until we have a properly packed version of the test svn repository, this
788 # tries to copy over the repo from a package "rc_testdata"
798 # tries to copy over the repo from a package "rc_testdata"
789 svn_repo_path = rc_testdata.get_svn_repo_archive()
799 svn_repo_path = rc_testdata.get_svn_repo_archive()
790 with tarfile.open(svn_repo_path) as tar:
800 with tarfile.open(svn_repo_path) as tar:
791 tar.extractall(jn(test_path, SVN_REPO))
801 tar.extractall(jn(test_path, SVN_REPO))
792
802
793
803
794 #==============================================================================
804 #==============================================================================
795 # PASTER COMMANDS
805 # PASTER COMMANDS
796 #==============================================================================
806 #==============================================================================
797 class BasePasterCommand(Command):
807 class BasePasterCommand(Command):
798 """
808 """
799 Abstract Base Class for paster commands.
809 Abstract Base Class for paster commands.
800
810
801 The celery commands are somewhat aggressive about loading
811 The celery commands are somewhat aggressive about loading
802 celery.conf, and since our module sets the `CELERY_LOADER`
812 celery.conf, and since our module sets the `CELERY_LOADER`
803 environment variable to our loader, we have to bootstrap a bit and
813 environment variable to our loader, we have to bootstrap a bit and
804 make sure we've had a chance to load the pylons config off of the
814 make sure we've had a chance to load the pylons config off of the
805 command line, otherwise everything fails.
815 command line, otherwise everything fails.
806 """
816 """
807 min_args = 1
817 min_args = 1
808 min_args_error = "Please provide a paster config file as an argument."
818 min_args_error = "Please provide a paster config file as an argument."
809 takes_config_file = 1
819 takes_config_file = 1
810 requires_config_file = True
820 requires_config_file = True
811
821
812 def notify_msg(self, msg, log=False):
822 def notify_msg(self, msg, log=False):
813 """Make a notification to user, additionally if logger is passed
823 """Make a notification to user, additionally if logger is passed
814 it logs this action using given logger
824 it logs this action using given logger
815
825
816 :param msg: message that will be printed to user
826 :param msg: message that will be printed to user
817 :param log: logging instance, to use to additionally log this message
827 :param log: logging instance, to use to additionally log this message
818
828
819 """
829 """
820 if log and isinstance(log, logging):
830 if log and isinstance(log, logging):
821 log(msg)
831 log(msg)
822
832
823 def run(self, args):
833 def run(self, args):
824 """
834 """
825 Overrides Command.run
835 Overrides Command.run
826
836
827 Checks for a config file argument and loads it.
837 Checks for a config file argument and loads it.
828 """
838 """
829 if len(args) < self.min_args:
839 if len(args) < self.min_args:
830 raise BadCommand(
840 raise BadCommand(
831 self.min_args_error % {'min_args': self.min_args,
841 self.min_args_error % {'min_args': self.min_args,
832 'actual_args': len(args)})
842 'actual_args': len(args)})
833
843
834 # Decrement because we're going to lob off the first argument.
844 # Decrement because we're going to lob off the first argument.
835 # @@ This is hacky
845 # @@ This is hacky
836 self.min_args -= 1
846 self.min_args -= 1
837 self.bootstrap_config(args[0])
847 self.bootstrap_config(args[0])
838 self.update_parser()
848 self.update_parser()
839 return super(BasePasterCommand, self).run(args[1:])
849 return super(BasePasterCommand, self).run(args[1:])
840
850
841 def update_parser(self):
851 def update_parser(self):
842 """
852 """
843 Abstract method. Allows for the class' parser to be updated
853 Abstract method. Allows for the class' parser to be updated
844 before the superclass' `run` method is called. Necessary to
854 before the superclass' `run` method is called. Necessary to
845 allow options/arguments to be passed through to the underlying
855 allow options/arguments to be passed through to the underlying
846 celery command.
856 celery command.
847 """
857 """
848 raise NotImplementedError("Abstract Method.")
858 raise NotImplementedError("Abstract Method.")
849
859
850 def bootstrap_config(self, conf):
860 def bootstrap_config(self, conf):
851 """
861 """
852 Loads the pylons configuration.
862 Loads the pylons configuration.
853 """
863 """
854 from pylons import config as pylonsconfig
864 from pylons import config as pylonsconfig
855
865
856 self.path_to_ini_file = os.path.realpath(conf)
866 self.path_to_ini_file = os.path.realpath(conf)
857 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
867 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
858 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
868 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
859
869
860 def _init_session(self):
870 def _init_session(self):
861 """
871 """
862 Inits SqlAlchemy Session
872 Inits SqlAlchemy Session
863 """
873 """
864 logging.config.fileConfig(self.path_to_ini_file)
874 logging.config.fileConfig(self.path_to_ini_file)
865 from pylons import config
875 from pylons import config
866 from rhodecode.config.utils import initialize_database
876 from rhodecode.config.utils import initialize_database
867
877
868 # get to remove repos !!
878 # get to remove repos !!
869 add_cache(config)
879 add_cache(config)
870 initialize_database(config)
880 initialize_database(config)
871
881
872
882
873 @decorator.decorator
883 @decorator.decorator
874 def jsonify(func, *args, **kwargs):
884 def jsonify(func, *args, **kwargs):
875 """Action decorator that formats output for JSON
885 """Action decorator that formats output for JSON
876
886
877 Given a function that will return content, this decorator will turn
887 Given a function that will return content, this decorator will turn
878 the result into JSON, with a content-type of 'application/json' and
888 the result into JSON, with a content-type of 'application/json' and
879 output it.
889 output it.
880
890
881 """
891 """
882 from pylons.decorators.util import get_pylons
892 from pylons.decorators.util import get_pylons
883 from rhodecode.lib.ext_json import json
893 from rhodecode.lib.ext_json import json
884 pylons = get_pylons(args)
894 pylons = get_pylons(args)
885 pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
895 pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
886 data = func(*args, **kwargs)
896 data = func(*args, **kwargs)
887 if isinstance(data, (list, tuple)):
897 if isinstance(data, (list, tuple)):
888 msg = "JSON responses with Array envelopes are susceptible to " \
898 msg = "JSON responses with Array envelopes are susceptible to " \
889 "cross-site data leak attacks, see " \
899 "cross-site data leak attacks, see " \
890 "http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
900 "http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
891 warnings.warn(msg, Warning, 2)
901 warnings.warn(msg, Warning, 2)
892 log.warning(msg)
902 log.warning(msg)
893 log.debug("Returning JSON wrapped action output")
903 log.debug("Returning JSON wrapped action output")
894 return json.dumps(data, encoding='utf-8')
904 return json.dumps(data, encoding='utf-8')
895
905
896
906
897 class PartialRenderer(object):
907 class PartialRenderer(object):
898 """
908 """
899 Partial renderer used to render chunks of html used in datagrids
909 Partial renderer used to render chunks of html used in datagrids
900 use like::
910 use like::
901
911
902 _render = PartialRenderer('data_table/_dt_elements.html')
912 _render = PartialRenderer('data_table/_dt_elements.html')
903 _render('quick_menu', args, kwargs)
913 _render('quick_menu', args, kwargs)
904 PartialRenderer.h,
914 PartialRenderer.h,
905 c,
915 c,
906 _,
916 _,
907 ungettext
917 ungettext
908 are the template stuff initialized inside and can be re-used later
918 are the template stuff initialized inside and can be re-used later
909
919
910 :param tmpl_name: template path relate to /templates/ dir
920 :param tmpl_name: template path relate to /templates/ dir
911 """
921 """
912
922
913 def __init__(self, tmpl_name):
923 def __init__(self, tmpl_name):
914 import rhodecode
924 import rhodecode
915 from pylons import request, tmpl_context as c
925 from pylons import request, tmpl_context as c
916 from pylons.i18n.translation import _, ungettext
926 from pylons.i18n.translation import _, ungettext
917 from rhodecode.lib import helpers as h
927 from rhodecode.lib import helpers as h
918
928
919 self.tmpl_name = tmpl_name
929 self.tmpl_name = tmpl_name
920 self.rhodecode = rhodecode
930 self.rhodecode = rhodecode
921 self.c = c
931 self.c = c
922 self._ = _
932 self._ = _
923 self.ungettext = ungettext
933 self.ungettext = ungettext
924 self.h = h
934 self.h = h
925 self.request = request
935 self.request = request
926
936
927 def _mako_lookup(self):
937 def _mako_lookup(self):
928 _tmpl_lookup = self.rhodecode.CONFIG['pylons.app_globals'].mako_lookup
938 _tmpl_lookup = self.rhodecode.CONFIG['pylons.app_globals'].mako_lookup
929 return _tmpl_lookup.get_template(self.tmpl_name)
939 return _tmpl_lookup.get_template(self.tmpl_name)
930
940
931 def _update_kwargs_for_render(self, kwargs):
941 def _update_kwargs_for_render(self, kwargs):
932 """
942 """
933 Inject params required for Mako rendering
943 Inject params required for Mako rendering
934 """
944 """
935 _kwargs = {
945 _kwargs = {
936 '_': self._,
946 '_': self._,
937 'h': self.h,
947 'h': self.h,
938 'c': self.c,
948 'c': self.c,
939 'request': self.request,
949 'request': self.request,
940 'ungettext': self.ungettext,
950 'ungettext': self.ungettext,
941 }
951 }
942 _kwargs.update(kwargs)
952 _kwargs.update(kwargs)
943 return _kwargs
953 return _kwargs
944
954
945 def _render_with_exc(self, render_func, args, kwargs):
955 def _render_with_exc(self, render_func, args, kwargs):
946 try:
956 try:
947 return render_func.render(*args, **kwargs)
957 return render_func.render(*args, **kwargs)
948 except:
958 except:
949 log.error(exceptions.text_error_template().render())
959 log.error(exceptions.text_error_template().render())
950 raise
960 raise
951
961
952 def _get_template(self, template_obj, def_name):
962 def _get_template(self, template_obj, def_name):
953 if def_name:
963 if def_name:
954 tmpl = template_obj.get_def(def_name)
964 tmpl = template_obj.get_def(def_name)
955 else:
965 else:
956 tmpl = template_obj
966 tmpl = template_obj
957 return tmpl
967 return tmpl
958
968
959 def render(self, def_name, *args, **kwargs):
969 def render(self, def_name, *args, **kwargs):
960 lookup_obj = self._mako_lookup()
970 lookup_obj = self._mako_lookup()
961 tmpl = self._get_template(lookup_obj, def_name=def_name)
971 tmpl = self._get_template(lookup_obj, def_name=def_name)
962 kwargs = self._update_kwargs_for_render(kwargs)
972 kwargs = self._update_kwargs_for_render(kwargs)
963 return self._render_with_exc(tmpl, args, kwargs)
973 return self._render_with_exc(tmpl, args, kwargs)
964
974
965 def __call__(self, tmpl, *args, **kwargs):
975 def __call__(self, tmpl, *args, **kwargs):
966 return self.render(tmpl, *args, **kwargs)
976 return self.render(tmpl, *args, **kwargs)
967
977
968
978
969 def password_changed(auth_user, session):
979 def password_changed(auth_user, session):
970 # Never report password change in case of default user or anonymous user.
980 # Never report password change in case of default user or anonymous user.
971 if auth_user.username == User.DEFAULT_USER or auth_user.user_id is None:
981 if auth_user.username == User.DEFAULT_USER or auth_user.user_id is None:
972 return False
982 return False
973
983
974 password_hash = md5(auth_user.password) if auth_user.password else None
984 password_hash = md5(auth_user.password) if auth_user.password else None
975 rhodecode_user = session.get('rhodecode_user', {})
985 rhodecode_user = session.get('rhodecode_user', {})
976 session_password_hash = rhodecode_user.get('password', '')
986 session_password_hash = rhodecode_user.get('password', '')
977 return password_hash != session_password_hash
987 return password_hash != session_password_hash
978
988
979
989
980 def read_opensource_licenses():
990 def read_opensource_licenses():
981 global _license_cache
991 global _license_cache
982
992
983 if not _license_cache:
993 if not _license_cache:
984 licenses = pkg_resources.resource_string(
994 licenses = pkg_resources.resource_string(
985 'rhodecode', 'config/licenses.json')
995 'rhodecode', 'config/licenses.json')
986 _license_cache = json.loads(licenses)
996 _license_cache = json.loads(licenses)
987
997
988 return _license_cache
998 return _license_cache
989
999
990
1000
991 def get_registry(request):
1001 def get_registry(request):
992 """
1002 """
993 Utility to get the pyramid registry from a request. During migration to
1003 Utility to get the pyramid registry from a request. During migration to
994 pyramid we sometimes want to use the pyramid registry from pylons context.
1004 pyramid we sometimes want to use the pyramid registry from pylons context.
995 Therefore this utility returns `request.registry` for pyramid requests and
1005 Therefore this utility returns `request.registry` for pyramid requests and
996 uses `get_current_registry()` for pylons requests.
1006 uses `get_current_registry()` for pylons requests.
997 """
1007 """
998 try:
1008 try:
999 return request.registry
1009 return request.registry
1000 except AttributeError:
1010 except AttributeError:
1001 return get_current_registry()
1011 return get_current_registry()
General Comments 0
You need to be logged in to leave comments. Login now