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