##// END OF EJS Templates
fixes #220 repos groups are not detected on Windows
marcink -
r1406:a773087f beta
parent child Browse files
Show More
@@ -1,628 +1,628 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) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import paste
30 import paste
31 import beaker
31 import beaker
32 from os.path import dirname as dn, join as jn
32 from os.path import dirname as dn, join as jn
33
33
34 from paste.script.command import Command, BadCommand
34 from paste.script.command import Command, BadCommand
35
35
36 from UserDict import DictMixin
36 from UserDict import DictMixin
37
37
38 from mercurial import ui, config, hg
38 from mercurial import ui, config, hg
39 from mercurial.error import RepoError
39 from mercurial.error import RepoError
40
40
41 from webhelpers.text import collapse, remove_formatting, strip_tags
41 from webhelpers.text import collapse, remove_formatting, strip_tags
42
42
43 from vcs.backends.base import BaseChangeset
43 from vcs.backends.base import BaseChangeset
44 from vcs.utils.lazy import LazyProperty
44 from vcs.utils.lazy import LazyProperty
45
45
46 from rhodecode.model import meta
46 from rhodecode.model import meta
47 from rhodecode.model.caching_query import FromCache
47 from rhodecode.model.caching_query import FromCache
48 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group, \
48 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group, \
49 RhodeCodeSettings
49 RhodeCodeSettings
50 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
51 from rhodecode.model.user import UserModel
51 from rhodecode.model.user import UserModel
52
52
53 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
54
54
55
55
56 def recursive_replace(str, replace=' '):
56 def recursive_replace(str, replace=' '):
57 """Recursive replace of given sign to just one instance
57 """Recursive replace of given sign to just one instance
58
58
59 :param str: given string
59 :param str: given string
60 :param replace: char to find and replace multiple instances
60 :param replace: char to find and replace multiple instances
61
61
62 Examples::
62 Examples::
63 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
63 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
64 'Mighty-Mighty-Bo-sstones'
64 'Mighty-Mighty-Bo-sstones'
65 """
65 """
66
66
67 if str.find(replace * 2) == -1:
67 if str.find(replace * 2) == -1:
68 return str
68 return str
69 else:
69 else:
70 str = str.replace(replace * 2, replace)
70 str = str.replace(replace * 2, replace)
71 return recursive_replace(str, replace)
71 return recursive_replace(str, replace)
72
72
73
73
74 def repo_name_slug(value):
74 def repo_name_slug(value):
75 """Return slug of name of repository
75 """Return slug of name of repository
76 This function is called on each creation/modification
76 This function is called on each creation/modification
77 of repository to prevent bad names in repo
77 of repository to prevent bad names in repo
78 """
78 """
79
79
80 slug = remove_formatting(value)
80 slug = remove_formatting(value)
81 slug = strip_tags(slug)
81 slug = strip_tags(slug)
82
82
83 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
83 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
84 slug = slug.replace(c, '-')
84 slug = slug.replace(c, '-')
85 slug = recursive_replace(slug, '-')
85 slug = recursive_replace(slug, '-')
86 slug = collapse(slug, '-')
86 slug = collapse(slug, '-')
87 return slug
87 return slug
88
88
89
89
90 def get_repo_slug(request):
90 def get_repo_slug(request):
91 return request.environ['pylons.routes_dict'].get('repo_name')
91 return request.environ['pylons.routes_dict'].get('repo_name')
92
92
93
93
94 def action_logger(user, action, repo, ipaddr='', sa=None):
94 def action_logger(user, action, repo, ipaddr='', sa=None):
95 """
95 """
96 Action logger for various actions made by users
96 Action logger for various actions made by users
97
97
98 :param user: user that made this action, can be a unique username string or
98 :param user: user that made this action, can be a unique username string or
99 object containing user_id attribute
99 object containing user_id attribute
100 :param action: action to log, should be on of predefined unique actions for
100 :param action: action to log, should be on of predefined unique actions for
101 easy translations
101 easy translations
102 :param repo: string name of repository or object containing repo_id,
102 :param repo: string name of repository or object containing repo_id,
103 that action was made on
103 that action was made on
104 :param ipaddr: optional ip address from what the action was made
104 :param ipaddr: optional ip address from what the action was made
105 :param sa: optional sqlalchemy session
105 :param sa: optional sqlalchemy session
106
106
107 """
107 """
108
108
109 if not sa:
109 if not sa:
110 sa = meta.Session()
110 sa = meta.Session()
111
111
112 try:
112 try:
113 um = UserModel()
113 um = UserModel()
114 if hasattr(user, 'user_id'):
114 if hasattr(user, 'user_id'):
115 user_obj = user
115 user_obj = user
116 elif isinstance(user, basestring):
116 elif isinstance(user, basestring):
117 user_obj = um.get_by_username(user, cache=False)
117 user_obj = um.get_by_username(user, cache=False)
118 else:
118 else:
119 raise Exception('You have to provide user object or username')
119 raise Exception('You have to provide user object or username')
120
120
121 rm = RepoModel()
121 rm = RepoModel()
122 if hasattr(repo, 'repo_id'):
122 if hasattr(repo, 'repo_id'):
123 repo_obj = rm.get(repo.repo_id, cache=False)
123 repo_obj = rm.get(repo.repo_id, cache=False)
124 repo_name = repo_obj.repo_name
124 repo_name = repo_obj.repo_name
125 elif isinstance(repo, basestring):
125 elif isinstance(repo, basestring):
126 repo_name = repo.lstrip('/')
126 repo_name = repo.lstrip('/')
127 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
127 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
128 else:
128 else:
129 raise Exception('You have to provide repository to action logger')
129 raise Exception('You have to provide repository to action logger')
130
130
131 user_log = UserLog()
131 user_log = UserLog()
132 user_log.user_id = user_obj.user_id
132 user_log.user_id = user_obj.user_id
133 user_log.action = action
133 user_log.action = action
134
134
135 user_log.repository_id = repo_obj.repo_id
135 user_log.repository_id = repo_obj.repo_id
136 user_log.repository_name = repo_name
136 user_log.repository_name = repo_name
137
137
138 user_log.action_date = datetime.datetime.now()
138 user_log.action_date = datetime.datetime.now()
139 user_log.user_ip = ipaddr
139 user_log.user_ip = ipaddr
140 sa.add(user_log)
140 sa.add(user_log)
141 sa.commit()
141 sa.commit()
142
142
143 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
143 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
144 except:
144 except:
145 log.error(traceback.format_exc())
145 log.error(traceback.format_exc())
146 sa.rollback()
146 sa.rollback()
147
147
148
148
149 def get_repos(path, recursive=False):
149 def get_repos(path, recursive=False):
150 """
150 """
151 Scans given path for repos and return (name,(type,path)) tuple
151 Scans given path for repos and return (name,(type,path)) tuple
152
152
153 :param path: path to scann for repositories
153 :param path: path to scann for repositories
154 :param recursive: recursive search and return names with subdirs in front
154 :param recursive: recursive search and return names with subdirs in front
155 """
155 """
156 from vcs.utils.helpers import get_scm
156 from vcs.utils.helpers import get_scm
157 from vcs.exceptions import VCSError
157 from vcs.exceptions import VCSError
158
158
159 if path.endswith(os.sep):
159 if path.endswith(os.sep):
160 #remove ending slash for better results
160 #remove ending slash for better results
161 path = path[:-1]
161 path = path[:-1]
162
162
163 def _get_repos(p):
163 def _get_repos(p):
164 if not os.access(p, os.W_OK):
164 if not os.access(p, os.W_OK):
165 return
165 return
166 for dirpath in os.listdir(p):
166 for dirpath in os.listdir(p):
167 if os.path.isfile(os.path.join(p, dirpath)):
167 if os.path.isfile(os.path.join(p, dirpath)):
168 continue
168 continue
169 cur_path = os.path.join(p, dirpath)
169 cur_path = os.path.join(p, dirpath)
170 try:
170 try:
171 scm_info = get_scm(cur_path)
171 scm_info = get_scm(cur_path)
172 yield scm_info[1].split(path)[-1].lstrip(os.sep), scm_info
172 yield scm_info[1].split(path)[-1].lstrip(os.sep), scm_info
173 except VCSError:
173 except VCSError:
174 if not recursive:
174 if not recursive:
175 continue
175 continue
176 #check if this dir containts other repos for recursive scan
176 #check if this dir containts other repos for recursive scan
177 rec_path = os.path.join(p, dirpath)
177 rec_path = os.path.join(p, dirpath)
178 if os.path.isdir(rec_path):
178 if os.path.isdir(rec_path):
179 for inner_scm in _get_repos(rec_path):
179 for inner_scm in _get_repos(rec_path):
180 yield inner_scm
180 yield inner_scm
181
181
182 return _get_repos(path)
182 return _get_repos(path)
183
183
184
184
185 def check_repo_fast(repo_name, base_path):
185 def check_repo_fast(repo_name, base_path):
186 """
186 """
187 Check given path for existence of directory
187 Check given path for existence of directory
188 :param repo_name:
188 :param repo_name:
189 :param base_path:
189 :param base_path:
190
190
191 :return False: if this directory is present
191 :return False: if this directory is present
192 """
192 """
193 if os.path.isdir(os.path.join(base_path, repo_name)):
193 if os.path.isdir(os.path.join(base_path, repo_name)):
194 return False
194 return False
195 return True
195 return True
196
196
197
197
198 def check_repo(repo_name, base_path, verify=True):
198 def check_repo(repo_name, base_path, verify=True):
199
199
200 repo_path = os.path.join(base_path, repo_name)
200 repo_path = os.path.join(base_path, repo_name)
201
201
202 try:
202 try:
203 if not check_repo_fast(repo_name, base_path):
203 if not check_repo_fast(repo_name, base_path):
204 return False
204 return False
205 r = hg.repository(ui.ui(), repo_path)
205 r = hg.repository(ui.ui(), repo_path)
206 if verify:
206 if verify:
207 hg.verify(r)
207 hg.verify(r)
208 #here we hnow that repo exists it was verified
208 #here we hnow that repo exists it was verified
209 log.info('%s repo is already created', repo_name)
209 log.info('%s repo is already created', repo_name)
210 return False
210 return False
211 except RepoError:
211 except RepoError:
212 #it means that there is no valid repo there...
212 #it means that there is no valid repo there...
213 log.info('%s repo is free for creation', repo_name)
213 log.info('%s repo is free for creation', repo_name)
214 return True
214 return True
215
215
216
216
217 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
217 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
218 while True:
218 while True:
219 ok = raw_input(prompt)
219 ok = raw_input(prompt)
220 if ok in ('y', 'ye', 'yes'):
220 if ok in ('y', 'ye', 'yes'):
221 return True
221 return True
222 if ok in ('n', 'no', 'nop', 'nope'):
222 if ok in ('n', 'no', 'nop', 'nope'):
223 return False
223 return False
224 retries = retries - 1
224 retries = retries - 1
225 if retries < 0:
225 if retries < 0:
226 raise IOError
226 raise IOError
227 print complaint
227 print complaint
228
228
229 #propagated from mercurial documentation
229 #propagated from mercurial documentation
230 ui_sections = ['alias', 'auth',
230 ui_sections = ['alias', 'auth',
231 'decode/encode', 'defaults',
231 'decode/encode', 'defaults',
232 'diff', 'email',
232 'diff', 'email',
233 'extensions', 'format',
233 'extensions', 'format',
234 'merge-patterns', 'merge-tools',
234 'merge-patterns', 'merge-tools',
235 'hooks', 'http_proxy',
235 'hooks', 'http_proxy',
236 'smtp', 'patch',
236 'smtp', 'patch',
237 'paths', 'profiling',
237 'paths', 'profiling',
238 'server', 'trusted',
238 'server', 'trusted',
239 'ui', 'web', ]
239 'ui', 'web', ]
240
240
241
241
242 def make_ui(read_from='file', path=None, checkpaths=True):
242 def make_ui(read_from='file', path=None, checkpaths=True):
243 """A function that will read python rc files or database
243 """A function that will read python rc files or database
244 and make an mercurial ui object from read options
244 and make an mercurial ui object from read options
245
245
246 :param path: path to mercurial config file
246 :param path: path to mercurial config file
247 :param checkpaths: check the path
247 :param checkpaths: check the path
248 :param read_from: read from 'file' or 'db'
248 :param read_from: read from 'file' or 'db'
249 """
249 """
250
250
251 baseui = ui.ui()
251 baseui = ui.ui()
252
252
253 #clean the baseui object
253 #clean the baseui object
254 baseui._ocfg = config.config()
254 baseui._ocfg = config.config()
255 baseui._ucfg = config.config()
255 baseui._ucfg = config.config()
256 baseui._tcfg = config.config()
256 baseui._tcfg = config.config()
257
257
258 if read_from == 'file':
258 if read_from == 'file':
259 if not os.path.isfile(path):
259 if not os.path.isfile(path):
260 log.warning('Unable to read config file %s' % path)
260 log.warning('Unable to read config file %s' % path)
261 return False
261 return False
262 log.debug('reading hgrc from %s', path)
262 log.debug('reading hgrc from %s', path)
263 cfg = config.config()
263 cfg = config.config()
264 cfg.read(path)
264 cfg.read(path)
265 for section in ui_sections:
265 for section in ui_sections:
266 for k, v in cfg.items(section):
266 for k, v in cfg.items(section):
267 log.debug('settings ui from file[%s]%s:%s', section, k, v)
267 log.debug('settings ui from file[%s]%s:%s', section, k, v)
268 baseui.setconfig(section, k, v)
268 baseui.setconfig(section, k, v)
269
269
270 elif read_from == 'db':
270 elif read_from == 'db':
271 sa = meta.Session()
271 sa = meta.Session()
272 ret = sa.query(RhodeCodeUi)\
272 ret = sa.query(RhodeCodeUi)\
273 .options(FromCache("sql_cache_short",
273 .options(FromCache("sql_cache_short",
274 "get_hg_ui_settings")).all()
274 "get_hg_ui_settings")).all()
275
275
276 hg_ui = ret
276 hg_ui = ret
277 for ui_ in hg_ui:
277 for ui_ in hg_ui:
278 if ui_.ui_active:
278 if ui_.ui_active:
279 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
279 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
280 ui_.ui_key, ui_.ui_value)
280 ui_.ui_key, ui_.ui_value)
281 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
281 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
282
282
283 meta.Session.remove()
283 meta.Session.remove()
284 return baseui
284 return baseui
285
285
286
286
287 def set_rhodecode_config(config):
287 def set_rhodecode_config(config):
288 """Updates pylons config with new settings from database
288 """Updates pylons config with new settings from database
289
289
290 :param config:
290 :param config:
291 """
291 """
292 hgsettings = RhodeCodeSettings.get_app_settings()
292 hgsettings = RhodeCodeSettings.get_app_settings()
293
293
294 for k, v in hgsettings.items():
294 for k, v in hgsettings.items():
295 config[k] = v
295 config[k] = v
296
296
297
297
298 def invalidate_cache(cache_key, *args):
298 def invalidate_cache(cache_key, *args):
299 """Puts cache invalidation task into db for
299 """Puts cache invalidation task into db for
300 further global cache invalidation
300 further global cache invalidation
301 """
301 """
302
302
303 from rhodecode.model.scm import ScmModel
303 from rhodecode.model.scm import ScmModel
304
304
305 if cache_key.startswith('get_repo_cached_'):
305 if cache_key.startswith('get_repo_cached_'):
306 name = cache_key.split('get_repo_cached_')[-1]
306 name = cache_key.split('get_repo_cached_')[-1]
307 ScmModel().mark_for_invalidation(name)
307 ScmModel().mark_for_invalidation(name)
308
308
309
309
310 class EmptyChangeset(BaseChangeset):
310 class EmptyChangeset(BaseChangeset):
311 """
311 """
312 An dummy empty changeset. It's possible to pass hash when creating
312 An dummy empty changeset. It's possible to pass hash when creating
313 an EmptyChangeset
313 an EmptyChangeset
314 """
314 """
315
315
316 def __init__(self, cs='0' * 40, repo=None):
316 def __init__(self, cs='0' * 40, repo=None):
317 self._empty_cs = cs
317 self._empty_cs = cs
318 self.revision = -1
318 self.revision = -1
319 self.message = ''
319 self.message = ''
320 self.author = ''
320 self.author = ''
321 self.date = ''
321 self.date = ''
322 self.repository = repo
322 self.repository = repo
323
323
324 @LazyProperty
324 @LazyProperty
325 def raw_id(self):
325 def raw_id(self):
326 """Returns raw string identifying this changeset, useful for web
326 """Returns raw string identifying this changeset, useful for web
327 representation.
327 representation.
328 """
328 """
329
329
330 return self._empty_cs
330 return self._empty_cs
331
331
332 @LazyProperty
332 @LazyProperty
333 def short_id(self):
333 def short_id(self):
334 return self.raw_id[:12]
334 return self.raw_id[:12]
335
335
336 def get_file_changeset(self, path):
336 def get_file_changeset(self, path):
337 return self
337 return self
338
338
339 def get_file_content(self, path):
339 def get_file_content(self, path):
340 return u''
340 return u''
341
341
342 def get_file_size(self, path):
342 def get_file_size(self, path):
343 return 0
343 return 0
344
344
345
345
346 def map_groups(groups):
346 def map_groups(groups):
347 """Checks for groups existence, and creates groups structures.
347 """Checks for groups existence, and creates groups structures.
348 It returns last group in structure
348 It returns last group in structure
349
349
350 :param groups: list of groups structure
350 :param groups: list of groups structure
351 """
351 """
352 sa = meta.Session()
352 sa = meta.Session()
353
353
354 parent = None
354 parent = None
355 group = None
355 group = None
356 for lvl, group_name in enumerate(groups[:-1]):
356 for lvl, group_name in enumerate(groups[:-1]):
357 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
357 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
358
358
359 if group is None:
359 if group is None:
360 group = Group(group_name, parent)
360 group = Group(group_name, parent)
361 sa.add(group)
361 sa.add(group)
362 sa.commit()
362 sa.commit()
363
363
364 parent = group
364 parent = group
365
365
366 return group
366 return group
367
367
368
368
369 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
369 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
370 """maps all repos given in initial_repo_list, non existing repositories
370 """maps all repos given in initial_repo_list, non existing repositories
371 are created, if remove_obsolete is True it also check for db entries
371 are created, if remove_obsolete is True it also check for db entries
372 that are not in initial_repo_list and removes them.
372 that are not in initial_repo_list and removes them.
373
373
374 :param initial_repo_list: list of repositories found by scanning methods
374 :param initial_repo_list: list of repositories found by scanning methods
375 :param remove_obsolete: check for obsolete entries in database
375 :param remove_obsolete: check for obsolete entries in database
376 """
376 """
377
377
378 sa = meta.Session()
378 sa = meta.Session()
379 rm = RepoModel()
379 rm = RepoModel()
380 user = sa.query(User).filter(User.admin == True).first()
380 user = sa.query(User).filter(User.admin == True).first()
381 added = []
381 added = []
382 for name, repo in initial_repo_list.items():
382 for name, repo in initial_repo_list.items():
383 group = map_groups(name.split('/'))
383 group = map_groups(name.split(os.sep))
384 if not rm.get_by_repo_name(name, cache=False):
384 if not rm.get_by_repo_name(name, cache=False):
385 log.info('repository %s not found creating default', name)
385 log.info('repository %s not found creating default', name)
386 added.append(name)
386 added.append(name)
387 form_data = {
387 form_data = {
388 'repo_name': name,
388 'repo_name': name,
389 'repo_name_full': name,
389 'repo_name_full': name,
390 'repo_type': repo.alias,
390 'repo_type': repo.alias,
391 'description': repo.description \
391 'description': repo.description \
392 if repo.description != 'unknown' else \
392 if repo.description != 'unknown' else \
393 '%s repository' % name,
393 '%s repository' % name,
394 'private': False,
394 'private': False,
395 'group_id': getattr(group, 'group_id', None)
395 'group_id': getattr(group, 'group_id', None)
396 }
396 }
397 rm.create(form_data, user, just_db=True)
397 rm.create(form_data, user, just_db=True)
398
398
399 removed = []
399 removed = []
400 if remove_obsolete:
400 if remove_obsolete:
401 #remove from database those repositories that are not in the filesystem
401 #remove from database those repositories that are not in the filesystem
402 for repo in sa.query(Repository).all():
402 for repo in sa.query(Repository).all():
403 if repo.repo_name not in initial_repo_list.keys():
403 if repo.repo_name not in initial_repo_list.keys():
404 removed.append(repo.repo_name)
404 removed.append(repo.repo_name)
405 sa.delete(repo)
405 sa.delete(repo)
406 sa.commit()
406 sa.commit()
407
407
408 return added, removed
408 return added, removed
409
409
410 #set cache regions for beaker so celery can utilise it
410 #set cache regions for beaker so celery can utilise it
411 def add_cache(settings):
411 def add_cache(settings):
412 cache_settings = {'regions': None}
412 cache_settings = {'regions': None}
413 for key in settings.keys():
413 for key in settings.keys():
414 for prefix in ['beaker.cache.', 'cache.']:
414 for prefix in ['beaker.cache.', 'cache.']:
415 if key.startswith(prefix):
415 if key.startswith(prefix):
416 name = key.split(prefix)[1].strip()
416 name = key.split(prefix)[1].strip()
417 cache_settings[name] = settings[key].strip()
417 cache_settings[name] = settings[key].strip()
418 if cache_settings['regions']:
418 if cache_settings['regions']:
419 for region in cache_settings['regions'].split(','):
419 for region in cache_settings['regions'].split(','):
420 region = region.strip()
420 region = region.strip()
421 region_settings = {}
421 region_settings = {}
422 for key, value in cache_settings.items():
422 for key, value in cache_settings.items():
423 if key.startswith(region):
423 if key.startswith(region):
424 region_settings[key.split('.')[1]] = value
424 region_settings[key.split('.')[1]] = value
425 region_settings['expire'] = int(region_settings.get('expire',
425 region_settings['expire'] = int(region_settings.get('expire',
426 60))
426 60))
427 region_settings.setdefault('lock_dir',
427 region_settings.setdefault('lock_dir',
428 cache_settings.get('lock_dir'))
428 cache_settings.get('lock_dir'))
429 region_settings.setdefault('data_dir',
429 region_settings.setdefault('data_dir',
430 cache_settings.get('data_dir'))
430 cache_settings.get('data_dir'))
431
431
432 if 'type' not in region_settings:
432 if 'type' not in region_settings:
433 region_settings['type'] = cache_settings.get('type',
433 region_settings['type'] = cache_settings.get('type',
434 'memory')
434 'memory')
435 beaker.cache.cache_regions[region] = region_settings
435 beaker.cache.cache_regions[region] = region_settings
436
436
437
437
438 def get_current_revision():
438 def get_current_revision():
439 """Returns tuple of (number, id) from repository containing this package
439 """Returns tuple of (number, id) from repository containing this package
440 or None if repository could not be found.
440 or None if repository could not be found.
441 """
441 """
442
442
443 try:
443 try:
444 from vcs import get_repo
444 from vcs import get_repo
445 from vcs.utils.helpers import get_scm
445 from vcs.utils.helpers import get_scm
446 from vcs.exceptions import RepositoryError, VCSError
446 from vcs.exceptions import RepositoryError, VCSError
447 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
447 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
448 scm = get_scm(repopath)[0]
448 scm = get_scm(repopath)[0]
449 repo = get_repo(path=repopath, alias=scm)
449 repo = get_repo(path=repopath, alias=scm)
450 tip = repo.get_changeset()
450 tip = repo.get_changeset()
451 return (tip.revision, tip.short_id)
451 return (tip.revision, tip.short_id)
452 except (ImportError, RepositoryError, VCSError), err:
452 except (ImportError, RepositoryError, VCSError), err:
453 logging.debug("Cannot retrieve rhodecode's revision. Original error "
453 logging.debug("Cannot retrieve rhodecode's revision. Original error "
454 "was: %s" % err)
454 "was: %s" % err)
455 return None
455 return None
456
456
457
457
458 #==============================================================================
458 #==============================================================================
459 # TEST FUNCTIONS AND CREATORS
459 # TEST FUNCTIONS AND CREATORS
460 #==============================================================================
460 #==============================================================================
461 def create_test_index(repo_location, config, full_index):
461 def create_test_index(repo_location, config, full_index):
462 """
462 """
463 Makes default test index
463 Makes default test index
464
464
465 :param config: test config
465 :param config: test config
466 :param full_index:
466 :param full_index:
467 """
467 """
468
468
469 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
469 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
470 from rhodecode.lib.pidlock import DaemonLock, LockHeld
470 from rhodecode.lib.pidlock import DaemonLock, LockHeld
471
471
472 repo_location = repo_location
472 repo_location = repo_location
473
473
474 index_location = os.path.join(config['app_conf']['index_dir'], 'index')
474 index_location = os.path.join(config['app_conf']['index_dir'], 'index')
475 if not os.path.exists(index_location):
475 if not os.path.exists(index_location):
476 os.makedirs(index_location)
476 os.makedirs(index_location)
477
477
478 try:
478 try:
479 l = DaemonLock(file=jn(dn(index_location), 'make_index.lock'))
479 l = DaemonLock(file=jn(dn(index_location), 'make_index.lock'))
480 WhooshIndexingDaemon(index_location=index_location,
480 WhooshIndexingDaemon(index_location=index_location,
481 repo_location=repo_location)\
481 repo_location=repo_location)\
482 .run(full_index=full_index)
482 .run(full_index=full_index)
483 l.release()
483 l.release()
484 except LockHeld:
484 except LockHeld:
485 pass
485 pass
486
486
487
487
488 def create_test_env(repos_test_path, config):
488 def create_test_env(repos_test_path, config):
489 """Makes a fresh database and
489 """Makes a fresh database and
490 install test repository into tmp dir
490 install test repository into tmp dir
491 """
491 """
492 from rhodecode.lib.db_manage import DbManage
492 from rhodecode.lib.db_manage import DbManage
493 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
493 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
494 HG_FORK, GIT_FORK, TESTS_TMP_PATH
494 HG_FORK, GIT_FORK, TESTS_TMP_PATH
495 import tarfile
495 import tarfile
496 import shutil
496 import shutil
497 from os.path import dirname as dn, join as jn, abspath
497 from os.path import dirname as dn, join as jn, abspath
498
498
499 log = logging.getLogger('TestEnvCreator')
499 log = logging.getLogger('TestEnvCreator')
500 # create logger
500 # create logger
501 log.setLevel(logging.DEBUG)
501 log.setLevel(logging.DEBUG)
502 log.propagate = True
502 log.propagate = True
503 # create console handler and set level to debug
503 # create console handler and set level to debug
504 ch = logging.StreamHandler()
504 ch = logging.StreamHandler()
505 ch.setLevel(logging.DEBUG)
505 ch.setLevel(logging.DEBUG)
506
506
507 # create formatter
507 # create formatter
508 formatter = logging.Formatter("%(asctime)s - %(name)s -"
508 formatter = logging.Formatter("%(asctime)s - %(name)s -"
509 " %(levelname)s - %(message)s")
509 " %(levelname)s - %(message)s")
510
510
511 # add formatter to ch
511 # add formatter to ch
512 ch.setFormatter(formatter)
512 ch.setFormatter(formatter)
513
513
514 # add ch to logger
514 # add ch to logger
515 log.addHandler(ch)
515 log.addHandler(ch)
516
516
517 #PART ONE create db
517 #PART ONE create db
518 dbconf = config['sqlalchemy.db1.url']
518 dbconf = config['sqlalchemy.db1.url']
519 log.debug('making test db %s', dbconf)
519 log.debug('making test db %s', dbconf)
520
520
521 # create test dir if it doesn't exist
521 # create test dir if it doesn't exist
522 if not os.path.isdir(repos_test_path):
522 if not os.path.isdir(repos_test_path):
523 log.debug('Creating testdir %s' % repos_test_path)
523 log.debug('Creating testdir %s' % repos_test_path)
524 os.makedirs(repos_test_path)
524 os.makedirs(repos_test_path)
525
525
526 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
526 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
527 tests=True)
527 tests=True)
528 dbmanage.create_tables(override=True)
528 dbmanage.create_tables(override=True)
529 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
529 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
530 dbmanage.create_default_user()
530 dbmanage.create_default_user()
531 dbmanage.admin_prompt()
531 dbmanage.admin_prompt()
532 dbmanage.create_permissions()
532 dbmanage.create_permissions()
533 dbmanage.populate_default_permissions()
533 dbmanage.populate_default_permissions()
534
534
535 #PART TWO make test repo
535 #PART TWO make test repo
536 log.debug('making test vcs repositories')
536 log.debug('making test vcs repositories')
537
537
538 #remove old one from previos tests
538 #remove old one from previos tests
539 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
539 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
540
540
541 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
541 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
542 log.debug('removing %s', r)
542 log.debug('removing %s', r)
543 shutil.rmtree(jn(TESTS_TMP_PATH, r))
543 shutil.rmtree(jn(TESTS_TMP_PATH, r))
544
544
545 idx_path = config['app_conf']['index_dir']
545 idx_path = config['app_conf']['index_dir']
546 data_path = config['app_conf']['cache_dir']
546 data_path = config['app_conf']['cache_dir']
547
547
548 #clean index and data
548 #clean index and data
549 if idx_path and os.path.exists(idx_path):
549 if idx_path and os.path.exists(idx_path):
550 log.debug('remove %s' % idx_path)
550 log.debug('remove %s' % idx_path)
551 shutil.rmtree(idx_path)
551 shutil.rmtree(idx_path)
552
552
553 if data_path and os.path.exists(data_path):
553 if data_path and os.path.exists(data_path):
554 log.debug('remove %s' % data_path)
554 log.debug('remove %s' % data_path)
555 shutil.rmtree(data_path)
555 shutil.rmtree(data_path)
556
556
557 #CREATE DEFAULT HG REPOSITORY
557 #CREATE DEFAULT HG REPOSITORY
558 cur_dir = dn(dn(abspath(__file__)))
558 cur_dir = dn(dn(abspath(__file__)))
559 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
559 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
560 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
560 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
561 tar.close()
561 tar.close()
562
562
563
563
564 #==============================================================================
564 #==============================================================================
565 # PASTER COMMANDS
565 # PASTER COMMANDS
566 #==============================================================================
566 #==============================================================================
567 class BasePasterCommand(Command):
567 class BasePasterCommand(Command):
568 """
568 """
569 Abstract Base Class for paster commands.
569 Abstract Base Class for paster commands.
570
570
571 The celery commands are somewhat aggressive about loading
571 The celery commands are somewhat aggressive about loading
572 celery.conf, and since our module sets the `CELERY_LOADER`
572 celery.conf, and since our module sets the `CELERY_LOADER`
573 environment variable to our loader, we have to bootstrap a bit and
573 environment variable to our loader, we have to bootstrap a bit and
574 make sure we've had a chance to load the pylons config off of the
574 make sure we've had a chance to load the pylons config off of the
575 command line, otherwise everything fails.
575 command line, otherwise everything fails.
576 """
576 """
577 min_args = 1
577 min_args = 1
578 min_args_error = "Please provide a paster config file as an argument."
578 min_args_error = "Please provide a paster config file as an argument."
579 takes_config_file = 1
579 takes_config_file = 1
580 requires_config_file = True
580 requires_config_file = True
581
581
582 def notify_msg(self, msg, log=False):
582 def notify_msg(self, msg, log=False):
583 """Make a notification to user, additionally if logger is passed
583 """Make a notification to user, additionally if logger is passed
584 it logs this action using given logger
584 it logs this action using given logger
585
585
586 :param msg: message that will be printed to user
586 :param msg: message that will be printed to user
587 :param log: logging instance, to use to additionally log this message
587 :param log: logging instance, to use to additionally log this message
588
588
589 """
589 """
590 if log and isinstance(log, logging):
590 if log and isinstance(log, logging):
591 log(msg)
591 log(msg)
592
592
593 def run(self, args):
593 def run(self, args):
594 """
594 """
595 Overrides Command.run
595 Overrides Command.run
596
596
597 Checks for a config file argument and loads it.
597 Checks for a config file argument and loads it.
598 """
598 """
599 if len(args) < self.min_args:
599 if len(args) < self.min_args:
600 raise BadCommand(
600 raise BadCommand(
601 self.min_args_error % {'min_args': self.min_args,
601 self.min_args_error % {'min_args': self.min_args,
602 'actual_args': len(args)})
602 'actual_args': len(args)})
603
603
604 # Decrement because we're going to lob off the first argument.
604 # Decrement because we're going to lob off the first argument.
605 # @@ This is hacky
605 # @@ This is hacky
606 self.min_args -= 1
606 self.min_args -= 1
607 self.bootstrap_config(args[0])
607 self.bootstrap_config(args[0])
608 self.update_parser()
608 self.update_parser()
609 return super(BasePasterCommand, self).run(args[1:])
609 return super(BasePasterCommand, self).run(args[1:])
610
610
611 def update_parser(self):
611 def update_parser(self):
612 """
612 """
613 Abstract method. Allows for the class's parser to be updated
613 Abstract method. Allows for the class's parser to be updated
614 before the superclass's `run` method is called. Necessary to
614 before the superclass's `run` method is called. Necessary to
615 allow options/arguments to be passed through to the underlying
615 allow options/arguments to be passed through to the underlying
616 celery command.
616 celery command.
617 """
617 """
618 raise NotImplementedError("Abstract Method.")
618 raise NotImplementedError("Abstract Method.")
619
619
620 def bootstrap_config(self, conf):
620 def bootstrap_config(self, conf):
621 """
621 """
622 Loads the pylons configuration.
622 Loads the pylons configuration.
623 """
623 """
624 from pylons import config as pylonsconfig
624 from pylons import config as pylonsconfig
625
625
626 path_to_ini_file = os.path.realpath(conf)
626 path_to_ini_file = os.path.realpath(conf)
627 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
627 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
628 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
628 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
General Comments 0
You need to be logged in to leave comments. Login now