##// END OF EJS Templates
we must rollback if repo2db mapper cleanup fails ! Session blows up, and that code still throws an error without it
marcink -
r2635:d6fa7805 beta
parent child Browse files
Show More
@@ -1,702 +1,703 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import re
27 import re
28 import logging
28 import logging
29 import datetime
29 import datetime
30 import traceback
30 import traceback
31 import paste
31 import paste
32 import beaker
32 import beaker
33 import tarfile
33 import tarfile
34 import shutil
34 import shutil
35 from os.path import abspath
35 from os.path import abspath
36 from os.path import dirname as dn, join as jn
36 from os.path import dirname as dn, join as jn
37
37
38 from paste.script.command import Command, BadCommand
38 from paste.script.command import Command, BadCommand
39
39
40 from mercurial import ui, config
40 from mercurial import ui, config
41
41
42 from webhelpers.text import collapse, remove_formatting, strip_tags
42 from webhelpers.text import collapse, remove_formatting, strip_tags
43
43
44 from rhodecode.lib.vcs import get_backend
44 from rhodecode.lib.vcs import get_backend
45 from rhodecode.lib.vcs.backends.base import BaseChangeset
45 from rhodecode.lib.vcs.backends.base import BaseChangeset
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47 from rhodecode.lib.vcs.utils.helpers import get_scm
47 from rhodecode.lib.vcs.utils.helpers import get_scm
48 from rhodecode.lib.vcs.exceptions import VCSError
48 from rhodecode.lib.vcs.exceptions import VCSError
49
49
50 from rhodecode.lib.caching_query import FromCache
50 from rhodecode.lib.caching_query import FromCache
51
51
52 from rhodecode.model import meta
52 from rhodecode.model import meta
53 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
53 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
54 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation
54 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation
55 from rhodecode.model.meta import Session
55 from rhodecode.model.meta import Session
56 from rhodecode.model.repos_group import ReposGroupModel
56 from rhodecode.model.repos_group import ReposGroupModel
57 from rhodecode.lib.utils2 import safe_str, safe_unicode
57 from rhodecode.lib.utils2 import safe_str, safe_unicode
58 from rhodecode.lib.vcs.utils.fakemod import create_module
58 from rhodecode.lib.vcs.utils.fakemod import create_module
59
59
60 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
61
61
62 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
62 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
63
63
64
64
65 def recursive_replace(str_, replace=' '):
65 def recursive_replace(str_, replace=' '):
66 """
66 """
67 Recursive replace of given sign to just one instance
67 Recursive replace of given sign to just one instance
68
68
69 :param str_: given string
69 :param str_: given string
70 :param replace: char to find and replace multiple instances
70 :param replace: char to find and replace multiple instances
71
71
72 Examples::
72 Examples::
73 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
73 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
74 'Mighty-Mighty-Bo-sstones'
74 'Mighty-Mighty-Bo-sstones'
75 """
75 """
76
76
77 if str_.find(replace * 2) == -1:
77 if str_.find(replace * 2) == -1:
78 return str_
78 return str_
79 else:
79 else:
80 str_ = str_.replace(replace * 2, replace)
80 str_ = str_.replace(replace * 2, replace)
81 return recursive_replace(str_, replace)
81 return recursive_replace(str_, replace)
82
82
83
83
84 def repo_name_slug(value):
84 def repo_name_slug(value):
85 """
85 """
86 Return slug of name of repository
86 Return slug of name of repository
87 This function is called on each creation/modification
87 This function is called on each creation/modification
88 of repository to prevent bad names in repo
88 of repository to prevent bad names in repo
89 """
89 """
90
90
91 slug = remove_formatting(value)
91 slug = remove_formatting(value)
92 slug = strip_tags(slug)
92 slug = strip_tags(slug)
93
93
94 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
94 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
95 slug = slug.replace(c, '-')
95 slug = slug.replace(c, '-')
96 slug = recursive_replace(slug, '-')
96 slug = recursive_replace(slug, '-')
97 slug = collapse(slug, '-')
97 slug = collapse(slug, '-')
98 return slug
98 return slug
99
99
100
100
101 def get_repo_slug(request):
101 def get_repo_slug(request):
102 _repo = request.environ['pylons.routes_dict'].get('repo_name')
102 _repo = request.environ['pylons.routes_dict'].get('repo_name')
103 if _repo:
103 if _repo:
104 _repo = _repo.rstrip('/')
104 _repo = _repo.rstrip('/')
105 return _repo
105 return _repo
106
106
107
107
108 def get_repos_group_slug(request):
108 def get_repos_group_slug(request):
109 _group = request.environ['pylons.routes_dict'].get('group_name')
109 _group = request.environ['pylons.routes_dict'].get('group_name')
110 if _group:
110 if _group:
111 _group = _group.rstrip('/')
111 _group = _group.rstrip('/')
112 return _group
112 return _group
113
113
114
114
115 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
115 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
116 """
116 """
117 Action logger for various actions made by users
117 Action logger for various actions made by users
118
118
119 :param user: user that made this action, can be a unique username string or
119 :param user: user that made this action, can be a unique username string or
120 object containing user_id attribute
120 object containing user_id attribute
121 :param action: action to log, should be on of predefined unique actions for
121 :param action: action to log, should be on of predefined unique actions for
122 easy translations
122 easy translations
123 :param repo: string name of repository or object containing repo_id,
123 :param repo: string name of repository or object containing repo_id,
124 that action was made on
124 that action was made on
125 :param ipaddr: optional ip address from what the action was made
125 :param ipaddr: optional ip address from what the action was made
126 :param sa: optional sqlalchemy session
126 :param sa: optional sqlalchemy session
127
127
128 """
128 """
129
129
130 if not sa:
130 if not sa:
131 sa = meta.Session()
131 sa = meta.Session()
132
132
133 try:
133 try:
134 if hasattr(user, 'user_id'):
134 if hasattr(user, 'user_id'):
135 user_obj = user
135 user_obj = user
136 elif isinstance(user, basestring):
136 elif isinstance(user, basestring):
137 user_obj = User.get_by_username(user)
137 user_obj = User.get_by_username(user)
138 else:
138 else:
139 raise Exception('You have to provide user object or username')
139 raise Exception('You have to provide user object or username')
140
140
141 if hasattr(repo, 'repo_id'):
141 if hasattr(repo, 'repo_id'):
142 repo_obj = Repository.get(repo.repo_id)
142 repo_obj = Repository.get(repo.repo_id)
143 repo_name = repo_obj.repo_name
143 repo_name = repo_obj.repo_name
144 elif isinstance(repo, basestring):
144 elif isinstance(repo, basestring):
145 repo_name = repo.lstrip('/')
145 repo_name = repo.lstrip('/')
146 repo_obj = Repository.get_by_repo_name(repo_name)
146 repo_obj = Repository.get_by_repo_name(repo_name)
147 else:
147 else:
148 repo_obj = None
148 repo_obj = None
149 repo_name = ''
149 repo_name = ''
150
150
151 user_log = UserLog()
151 user_log = UserLog()
152 user_log.user_id = user_obj.user_id
152 user_log.user_id = user_obj.user_id
153 user_log.action = safe_unicode(action)
153 user_log.action = safe_unicode(action)
154
154
155 user_log.repository = repo_obj
155 user_log.repository = repo_obj
156 user_log.repository_name = repo_name
156 user_log.repository_name = repo_name
157
157
158 user_log.action_date = datetime.datetime.now()
158 user_log.action_date = datetime.datetime.now()
159 user_log.user_ip = ipaddr
159 user_log.user_ip = ipaddr
160 sa.add(user_log)
160 sa.add(user_log)
161
161
162 log.info(
162 log.info(
163 'Adding user %s, action %s on %s' % (user_obj, action,
163 'Adding user %s, action %s on %s' % (user_obj, action,
164 safe_unicode(repo))
164 safe_unicode(repo))
165 )
165 )
166 if commit:
166 if commit:
167 sa.commit()
167 sa.commit()
168 except:
168 except:
169 log.error(traceback.format_exc())
169 log.error(traceback.format_exc())
170 raise
170 raise
171
171
172
172
173 def get_repos(path, recursive=False):
173 def get_repos(path, recursive=False):
174 """
174 """
175 Scans given path for repos and return (name,(type,path)) tuple
175 Scans given path for repos and return (name,(type,path)) tuple
176
176
177 :param path: path to scan for repositories
177 :param path: path to scan for repositories
178 :param recursive: recursive search and return names with subdirs in front
178 :param recursive: recursive search and return names with subdirs in front
179 """
179 """
180
180
181 # remove ending slash for better results
181 # remove ending slash for better results
182 path = path.rstrip(os.sep)
182 path = path.rstrip(os.sep)
183
183
184 def _get_repos(p):
184 def _get_repos(p):
185 if not os.access(p, os.W_OK):
185 if not os.access(p, os.W_OK):
186 return
186 return
187 for dirpath in os.listdir(p):
187 for dirpath in os.listdir(p):
188 if os.path.isfile(os.path.join(p, dirpath)):
188 if os.path.isfile(os.path.join(p, dirpath)):
189 continue
189 continue
190 cur_path = os.path.join(p, dirpath)
190 cur_path = os.path.join(p, dirpath)
191 try:
191 try:
192 scm_info = get_scm(cur_path)
192 scm_info = get_scm(cur_path)
193 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
193 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
194 except VCSError:
194 except VCSError:
195 if not recursive:
195 if not recursive:
196 continue
196 continue
197 #check if this dir containts other repos for recursive scan
197 #check if this dir containts other repos for recursive scan
198 rec_path = os.path.join(p, dirpath)
198 rec_path = os.path.join(p, dirpath)
199 if os.path.isdir(rec_path):
199 if os.path.isdir(rec_path):
200 for inner_scm in _get_repos(rec_path):
200 for inner_scm in _get_repos(rec_path):
201 yield inner_scm
201 yield inner_scm
202
202
203 return _get_repos(path)
203 return _get_repos(path)
204
204
205
205
206 def is_valid_repo(repo_name, base_path):
206 def is_valid_repo(repo_name, base_path):
207 """
207 """
208 Returns True if given path is a valid repository False otherwise
208 Returns True if given path is a valid repository False otherwise
209
209
210 :param repo_name:
210 :param repo_name:
211 :param base_path:
211 :param base_path:
212
212
213 :return True: if given path is a valid repository
213 :return True: if given path is a valid repository
214 """
214 """
215 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
215 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
216
216
217 try:
217 try:
218 get_scm(full_path)
218 get_scm(full_path)
219 return True
219 return True
220 except VCSError:
220 except VCSError:
221 return False
221 return False
222
222
223
223
224 def is_valid_repos_group(repos_group_name, base_path):
224 def is_valid_repos_group(repos_group_name, base_path):
225 """
225 """
226 Returns True if given path is a repos group False otherwise
226 Returns True if given path is a repos group False otherwise
227
227
228 :param repo_name:
228 :param repo_name:
229 :param base_path:
229 :param base_path:
230 """
230 """
231 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
231 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
232
232
233 # check if it's not a repo
233 # check if it's not a repo
234 if is_valid_repo(repos_group_name, base_path):
234 if is_valid_repo(repos_group_name, base_path):
235 return False
235 return False
236
236
237 try:
237 try:
238 # we need to check bare git repos at higher level
238 # we need to check bare git repos at higher level
239 # since we might match branches/hooks/info/objects or possible
239 # since we might match branches/hooks/info/objects or possible
240 # other things inside bare git repo
240 # other things inside bare git repo
241 get_scm(os.path.dirname(full_path))
241 get_scm(os.path.dirname(full_path))
242 return False
242 return False
243 except VCSError:
243 except VCSError:
244 pass
244 pass
245
245
246 # check if it's a valid path
246 # check if it's a valid path
247 if os.path.isdir(full_path):
247 if os.path.isdir(full_path):
248 return True
248 return True
249
249
250 return False
250 return False
251
251
252
252
253 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
253 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
254 while True:
254 while True:
255 ok = raw_input(prompt)
255 ok = raw_input(prompt)
256 if ok in ('y', 'ye', 'yes'):
256 if ok in ('y', 'ye', 'yes'):
257 return True
257 return True
258 if ok in ('n', 'no', 'nop', 'nope'):
258 if ok in ('n', 'no', 'nop', 'nope'):
259 return False
259 return False
260 retries = retries - 1
260 retries = retries - 1
261 if retries < 0:
261 if retries < 0:
262 raise IOError
262 raise IOError
263 print complaint
263 print complaint
264
264
265 #propagated from mercurial documentation
265 #propagated from mercurial documentation
266 ui_sections = ['alias', 'auth',
266 ui_sections = ['alias', 'auth',
267 'decode/encode', 'defaults',
267 'decode/encode', 'defaults',
268 'diff', 'email',
268 'diff', 'email',
269 'extensions', 'format',
269 'extensions', 'format',
270 'merge-patterns', 'merge-tools',
270 'merge-patterns', 'merge-tools',
271 'hooks', 'http_proxy',
271 'hooks', 'http_proxy',
272 'smtp', 'patch',
272 'smtp', 'patch',
273 'paths', 'profiling',
273 'paths', 'profiling',
274 'server', 'trusted',
274 'server', 'trusted',
275 'ui', 'web', ]
275 'ui', 'web', ]
276
276
277
277
278 def make_ui(read_from='file', path=None, checkpaths=True):
278 def make_ui(read_from='file', path=None, checkpaths=True):
279 """
279 """
280 A function that will read python rc files or database
280 A function that will read python rc files or database
281 and make an mercurial ui object from read options
281 and make an mercurial ui object from read options
282
282
283 :param path: path to mercurial config file
283 :param path: path to mercurial config file
284 :param checkpaths: check the path
284 :param checkpaths: check the path
285 :param read_from: read from 'file' or 'db'
285 :param read_from: read from 'file' or 'db'
286 """
286 """
287
287
288 baseui = ui.ui()
288 baseui = ui.ui()
289
289
290 # clean the baseui object
290 # clean the baseui object
291 baseui._ocfg = config.config()
291 baseui._ocfg = config.config()
292 baseui._ucfg = config.config()
292 baseui._ucfg = config.config()
293 baseui._tcfg = config.config()
293 baseui._tcfg = config.config()
294
294
295 if read_from == 'file':
295 if read_from == 'file':
296 if not os.path.isfile(path):
296 if not os.path.isfile(path):
297 log.debug('hgrc file is not present at %s skipping...' % path)
297 log.debug('hgrc file is not present at %s skipping...' % path)
298 return False
298 return False
299 log.debug('reading hgrc from %s' % path)
299 log.debug('reading hgrc from %s' % path)
300 cfg = config.config()
300 cfg = config.config()
301 cfg.read(path)
301 cfg.read(path)
302 for section in ui_sections:
302 for section in ui_sections:
303 for k, v in cfg.items(section):
303 for k, v in cfg.items(section):
304 log.debug('settings ui from file[%s]%s:%s' % (section, k, v))
304 log.debug('settings ui from file[%s]%s:%s' % (section, k, v))
305 baseui.setconfig(section, k, v)
305 baseui.setconfig(section, k, v)
306
306
307 elif read_from == 'db':
307 elif read_from == 'db':
308 sa = meta.Session()
308 sa = meta.Session()
309 ret = sa.query(RhodeCodeUi)\
309 ret = sa.query(RhodeCodeUi)\
310 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
310 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
311 .all()
311 .all()
312
312
313 hg_ui = ret
313 hg_ui = ret
314 for ui_ in hg_ui:
314 for ui_ in hg_ui:
315 if ui_.ui_active:
315 if ui_.ui_active:
316 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
316 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
317 ui_.ui_key, ui_.ui_value)
317 ui_.ui_key, ui_.ui_value)
318 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
318 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
319
319
320 meta.Session.remove()
320 meta.Session.remove()
321 return baseui
321 return baseui
322
322
323
323
324 def set_rhodecode_config(config):
324 def set_rhodecode_config(config):
325 """
325 """
326 Updates pylons config with new settings from database
326 Updates pylons config with new settings from database
327
327
328 :param config:
328 :param config:
329 """
329 """
330 hgsettings = RhodeCodeSetting.get_app_settings()
330 hgsettings = RhodeCodeSetting.get_app_settings()
331
331
332 for k, v in hgsettings.items():
332 for k, v in hgsettings.items():
333 config[k] = v
333 config[k] = v
334
334
335
335
336 def invalidate_cache(cache_key, *args):
336 def invalidate_cache(cache_key, *args):
337 """
337 """
338 Puts cache invalidation task into db for
338 Puts cache invalidation task into db for
339 further global cache invalidation
339 further global cache invalidation
340 """
340 """
341
341
342 from rhodecode.model.scm import ScmModel
342 from rhodecode.model.scm import ScmModel
343
343
344 if cache_key.startswith('get_repo_cached_'):
344 if cache_key.startswith('get_repo_cached_'):
345 name = cache_key.split('get_repo_cached_')[-1]
345 name = cache_key.split('get_repo_cached_')[-1]
346 ScmModel().mark_for_invalidation(name)
346 ScmModel().mark_for_invalidation(name)
347
347
348
348
349 class EmptyChangeset(BaseChangeset):
349 class EmptyChangeset(BaseChangeset):
350 """
350 """
351 An dummy empty changeset. It's possible to pass hash when creating
351 An dummy empty changeset. It's possible to pass hash when creating
352 an EmptyChangeset
352 an EmptyChangeset
353 """
353 """
354
354
355 def __init__(self, cs='0' * 40, repo=None, requested_revision=None,
355 def __init__(self, cs='0' * 40, repo=None, requested_revision=None,
356 alias=None):
356 alias=None):
357 self._empty_cs = cs
357 self._empty_cs = cs
358 self.revision = -1
358 self.revision = -1
359 self.message = ''
359 self.message = ''
360 self.author = ''
360 self.author = ''
361 self.date = ''
361 self.date = ''
362 self.repository = repo
362 self.repository = repo
363 self.requested_revision = requested_revision
363 self.requested_revision = requested_revision
364 self.alias = alias
364 self.alias = alias
365
365
366 @LazyProperty
366 @LazyProperty
367 def raw_id(self):
367 def raw_id(self):
368 """
368 """
369 Returns raw string identifying this changeset, useful for web
369 Returns raw string identifying this changeset, useful for web
370 representation.
370 representation.
371 """
371 """
372
372
373 return self._empty_cs
373 return self._empty_cs
374
374
375 @LazyProperty
375 @LazyProperty
376 def branch(self):
376 def branch(self):
377 return get_backend(self.alias).DEFAULT_BRANCH_NAME
377 return get_backend(self.alias).DEFAULT_BRANCH_NAME
378
378
379 @LazyProperty
379 @LazyProperty
380 def short_id(self):
380 def short_id(self):
381 return self.raw_id[:12]
381 return self.raw_id[:12]
382
382
383 def get_file_changeset(self, path):
383 def get_file_changeset(self, path):
384 return self
384 return self
385
385
386 def get_file_content(self, path):
386 def get_file_content(self, path):
387 return u''
387 return u''
388
388
389 def get_file_size(self, path):
389 def get_file_size(self, path):
390 return 0
390 return 0
391
391
392
392
393 def map_groups(path):
393 def map_groups(path):
394 """
394 """
395 Given a full path to a repository, create all nested groups that this
395 Given a full path to a repository, create all nested groups that this
396 repo is inside. This function creates parent-child relationships between
396 repo is inside. This function creates parent-child relationships between
397 groups and creates default perms for all new groups.
397 groups and creates default perms for all new groups.
398
398
399 :param paths: full path to repository
399 :param paths: full path to repository
400 """
400 """
401 sa = meta.Session()
401 sa = meta.Session()
402 groups = path.split(Repository.url_sep())
402 groups = path.split(Repository.url_sep())
403 parent = None
403 parent = None
404 group = None
404 group = None
405
405
406 # last element is repo in nested groups structure
406 # last element is repo in nested groups structure
407 groups = groups[:-1]
407 groups = groups[:-1]
408 rgm = ReposGroupModel(sa)
408 rgm = ReposGroupModel(sa)
409 for lvl, group_name in enumerate(groups):
409 for lvl, group_name in enumerate(groups):
410 group_name = '/'.join(groups[:lvl] + [group_name])
410 group_name = '/'.join(groups[:lvl] + [group_name])
411 group = RepoGroup.get_by_group_name(group_name)
411 group = RepoGroup.get_by_group_name(group_name)
412 desc = '%s group' % group_name
412 desc = '%s group' % group_name
413
413
414 # skip folders that are now removed repos
414 # skip folders that are now removed repos
415 if REMOVED_REPO_PAT.match(group_name):
415 if REMOVED_REPO_PAT.match(group_name):
416 break
416 break
417
417
418 if group is None:
418 if group is None:
419 log.debug('creating group level: %s group_name: %s' % (lvl,
419 log.debug('creating group level: %s group_name: %s' % (lvl,
420 group_name))
420 group_name))
421 group = RepoGroup(group_name, parent)
421 group = RepoGroup(group_name, parent)
422 group.group_description = desc
422 group.group_description = desc
423 sa.add(group)
423 sa.add(group)
424 rgm._create_default_perms(group)
424 rgm._create_default_perms(group)
425 sa.flush()
425 sa.flush()
426 parent = group
426 parent = group
427 return group
427 return group
428
428
429
429
430 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
430 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
431 install_git_hook=False):
431 install_git_hook=False):
432 """
432 """
433 maps all repos given in initial_repo_list, non existing repositories
433 maps all repos given in initial_repo_list, non existing repositories
434 are created, if remove_obsolete is True it also check for db entries
434 are created, if remove_obsolete is True it also check for db entries
435 that are not in initial_repo_list and removes them.
435 that are not in initial_repo_list and removes them.
436
436
437 :param initial_repo_list: list of repositories found by scanning methods
437 :param initial_repo_list: list of repositories found by scanning methods
438 :param remove_obsolete: check for obsolete entries in database
438 :param remove_obsolete: check for obsolete entries in database
439 :param install_git_hook: if this is True, also check and install githook
439 :param install_git_hook: if this is True, also check and install githook
440 for a repo if missing
440 for a repo if missing
441 """
441 """
442 from rhodecode.model.repo import RepoModel
442 from rhodecode.model.repo import RepoModel
443 from rhodecode.model.scm import ScmModel
443 from rhodecode.model.scm import ScmModel
444 sa = meta.Session()
444 sa = meta.Session()
445 rm = RepoModel()
445 rm = RepoModel()
446 user = sa.query(User).filter(User.admin == True).first()
446 user = sa.query(User).filter(User.admin == True).first()
447 if user is None:
447 if user is None:
448 raise Exception('Missing administrative account !')
448 raise Exception('Missing administrative account !')
449 added = []
449 added = []
450
450
451 for name, repo in initial_repo_list.items():
451 for name, repo in initial_repo_list.items():
452 group = map_groups(name)
452 group = map_groups(name)
453 db_repo = rm.get_by_repo_name(name)
453 db_repo = rm.get_by_repo_name(name)
454 # found repo that is on filesystem not in RhodeCode database
454 # found repo that is on filesystem not in RhodeCode database
455 if not db_repo:
455 if not db_repo:
456 log.info('repository %s not found creating now' % name)
456 log.info('repository %s not found creating now' % name)
457 added.append(name)
457 added.append(name)
458 desc = (repo.description
458 desc = (repo.description
459 if repo.description != 'unknown'
459 if repo.description != 'unknown'
460 else '%s repository' % name)
460 else '%s repository' % name)
461 new_repo = rm.create_repo(
461 new_repo = rm.create_repo(
462 repo_name=name,
462 repo_name=name,
463 repo_type=repo.alias,
463 repo_type=repo.alias,
464 description=desc,
464 description=desc,
465 repos_group=getattr(group, 'group_id', None),
465 repos_group=getattr(group, 'group_id', None),
466 owner=user,
466 owner=user,
467 just_db=True
467 just_db=True
468 )
468 )
469 # we added that repo just now, and make sure it has githook
469 # we added that repo just now, and make sure it has githook
470 # installed
470 # installed
471 if new_repo.repo_type == 'git':
471 if new_repo.repo_type == 'git':
472 ScmModel().install_git_hook(new_repo.scm_instance)
472 ScmModel().install_git_hook(new_repo.scm_instance)
473 elif install_git_hook:
473 elif install_git_hook:
474 if db_repo.repo_type == 'git':
474 if db_repo.repo_type == 'git':
475 ScmModel().install_git_hook(db_repo.scm_instance)
475 ScmModel().install_git_hook(db_repo.scm_instance)
476 sa.commit()
476 sa.commit()
477 removed = []
477 removed = []
478 if remove_obsolete:
478 if remove_obsolete:
479 # remove from database those repositories that are not in the filesystem
479 # remove from database those repositories that are not in the filesystem
480 for repo in sa.query(Repository).all():
480 for repo in sa.query(Repository).all():
481 if repo.repo_name not in initial_repo_list.keys():
481 if repo.repo_name not in initial_repo_list.keys():
482 log.debug("Removing non existing repository found in db %s" %
482 log.debug("Removing non existing repository found in db `%s`" %
483 repo.repo_name)
483 repo.repo_name)
484 try:
484 try:
485 sa.delete(repo)
485 sa.delete(repo)
486 sa.commit()
486 sa.commit()
487 removed.append(repo.repo_name)
487 removed.append(repo.repo_name)
488 except:
488 except:
489 #don't hold further removals on error
489 #don't hold further removals on error
490 log.error(traceback.format_exc())
490 log.error(traceback.format_exc())
491 sa.rollback()
491
492
492 # clear cache keys
493 # clear cache keys
493 log.debug("Clearing cache keys now...")
494 log.debug("Clearing cache keys now...")
494 CacheInvalidation.clear_cache()
495 CacheInvalidation.clear_cache()
495 sa.commit()
496 sa.commit()
496 return added, removed
497 return added, removed
497
498
498
499
499 # set cache regions for beaker so celery can utilise it
500 # set cache regions for beaker so celery can utilise it
500 def add_cache(settings):
501 def add_cache(settings):
501 cache_settings = {'regions': None}
502 cache_settings = {'regions': None}
502 for key in settings.keys():
503 for key in settings.keys():
503 for prefix in ['beaker.cache.', 'cache.']:
504 for prefix in ['beaker.cache.', 'cache.']:
504 if key.startswith(prefix):
505 if key.startswith(prefix):
505 name = key.split(prefix)[1].strip()
506 name = key.split(prefix)[1].strip()
506 cache_settings[name] = settings[key].strip()
507 cache_settings[name] = settings[key].strip()
507 if cache_settings['regions']:
508 if cache_settings['regions']:
508 for region in cache_settings['regions'].split(','):
509 for region in cache_settings['regions'].split(','):
509 region = region.strip()
510 region = region.strip()
510 region_settings = {}
511 region_settings = {}
511 for key, value in cache_settings.items():
512 for key, value in cache_settings.items():
512 if key.startswith(region):
513 if key.startswith(region):
513 region_settings[key.split('.')[1]] = value
514 region_settings[key.split('.')[1]] = value
514 region_settings['expire'] = int(region_settings.get('expire',
515 region_settings['expire'] = int(region_settings.get('expire',
515 60))
516 60))
516 region_settings.setdefault('lock_dir',
517 region_settings.setdefault('lock_dir',
517 cache_settings.get('lock_dir'))
518 cache_settings.get('lock_dir'))
518 region_settings.setdefault('data_dir',
519 region_settings.setdefault('data_dir',
519 cache_settings.get('data_dir'))
520 cache_settings.get('data_dir'))
520
521
521 if 'type' not in region_settings:
522 if 'type' not in region_settings:
522 region_settings['type'] = cache_settings.get('type',
523 region_settings['type'] = cache_settings.get('type',
523 'memory')
524 'memory')
524 beaker.cache.cache_regions[region] = region_settings
525 beaker.cache.cache_regions[region] = region_settings
525
526
526
527
527 def load_rcextensions(root_path):
528 def load_rcextensions(root_path):
528 import rhodecode
529 import rhodecode
529 from rhodecode.config import conf
530 from rhodecode.config import conf
530
531
531 path = os.path.join(root_path, 'rcextensions', '__init__.py')
532 path = os.path.join(root_path, 'rcextensions', '__init__.py')
532 if os.path.isfile(path):
533 if os.path.isfile(path):
533 rcext = create_module('rc', path)
534 rcext = create_module('rc', path)
534 EXT = rhodecode.EXTENSIONS = rcext
535 EXT = rhodecode.EXTENSIONS = rcext
535 log.debug('Found rcextensions now loading %s...' % rcext)
536 log.debug('Found rcextensions now loading %s...' % rcext)
536
537
537 # Additional mappings that are not present in the pygments lexers
538 # Additional mappings that are not present in the pygments lexers
538 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
539 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
539
540
540 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
541 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
541
542
542 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
543 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
543 log.debug('settings custom INDEX_EXTENSIONS')
544 log.debug('settings custom INDEX_EXTENSIONS')
544 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
545 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
545
546
546 #ADDITIONAL MAPPINGS
547 #ADDITIONAL MAPPINGS
547 log.debug('adding extra into INDEX_EXTENSIONS')
548 log.debug('adding extra into INDEX_EXTENSIONS')
548 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
549 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
549
550
550
551
551 #==============================================================================
552 #==============================================================================
552 # TEST FUNCTIONS AND CREATORS
553 # TEST FUNCTIONS AND CREATORS
553 #==============================================================================
554 #==============================================================================
554 def create_test_index(repo_location, config, full_index):
555 def create_test_index(repo_location, config, full_index):
555 """
556 """
556 Makes default test index
557 Makes default test index
557
558
558 :param config: test config
559 :param config: test config
559 :param full_index:
560 :param full_index:
560 """
561 """
561
562
562 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
563 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
563 from rhodecode.lib.pidlock import DaemonLock, LockHeld
564 from rhodecode.lib.pidlock import DaemonLock, LockHeld
564
565
565 repo_location = repo_location
566 repo_location = repo_location
566
567
567 index_location = os.path.join(config['app_conf']['index_dir'])
568 index_location = os.path.join(config['app_conf']['index_dir'])
568 if not os.path.exists(index_location):
569 if not os.path.exists(index_location):
569 os.makedirs(index_location)
570 os.makedirs(index_location)
570
571
571 try:
572 try:
572 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
573 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
573 WhooshIndexingDaemon(index_location=index_location,
574 WhooshIndexingDaemon(index_location=index_location,
574 repo_location=repo_location)\
575 repo_location=repo_location)\
575 .run(full_index=full_index)
576 .run(full_index=full_index)
576 l.release()
577 l.release()
577 except LockHeld:
578 except LockHeld:
578 pass
579 pass
579
580
580
581
581 def create_test_env(repos_test_path, config):
582 def create_test_env(repos_test_path, config):
582 """
583 """
583 Makes a fresh database and
584 Makes a fresh database and
584 install test repository into tmp dir
585 install test repository into tmp dir
585 """
586 """
586 from rhodecode.lib.db_manage import DbManage
587 from rhodecode.lib.db_manage import DbManage
587 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
588 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
588
589
589 # PART ONE create db
590 # PART ONE create db
590 dbconf = config['sqlalchemy.db1.url']
591 dbconf = config['sqlalchemy.db1.url']
591 log.debug('making test db %s' % dbconf)
592 log.debug('making test db %s' % dbconf)
592
593
593 # create test dir if it doesn't exist
594 # create test dir if it doesn't exist
594 if not os.path.isdir(repos_test_path):
595 if not os.path.isdir(repos_test_path):
595 log.debug('Creating testdir %s' % repos_test_path)
596 log.debug('Creating testdir %s' % repos_test_path)
596 os.makedirs(repos_test_path)
597 os.makedirs(repos_test_path)
597
598
598 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
599 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
599 tests=True)
600 tests=True)
600 dbmanage.create_tables(override=True)
601 dbmanage.create_tables(override=True)
601 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
602 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
602 dbmanage.create_default_user()
603 dbmanage.create_default_user()
603 dbmanage.admin_prompt()
604 dbmanage.admin_prompt()
604 dbmanage.create_permissions()
605 dbmanage.create_permissions()
605 dbmanage.populate_default_permissions()
606 dbmanage.populate_default_permissions()
606 Session().commit()
607 Session().commit()
607 # PART TWO make test repo
608 # PART TWO make test repo
608 log.debug('making test vcs repositories')
609 log.debug('making test vcs repositories')
609
610
610 idx_path = config['app_conf']['index_dir']
611 idx_path = config['app_conf']['index_dir']
611 data_path = config['app_conf']['cache_dir']
612 data_path = config['app_conf']['cache_dir']
612
613
613 #clean index and data
614 #clean index and data
614 if idx_path and os.path.exists(idx_path):
615 if idx_path and os.path.exists(idx_path):
615 log.debug('remove %s' % idx_path)
616 log.debug('remove %s' % idx_path)
616 shutil.rmtree(idx_path)
617 shutil.rmtree(idx_path)
617
618
618 if data_path and os.path.exists(data_path):
619 if data_path and os.path.exists(data_path):
619 log.debug('remove %s' % data_path)
620 log.debug('remove %s' % data_path)
620 shutil.rmtree(data_path)
621 shutil.rmtree(data_path)
621
622
622 #CREATE DEFAULT TEST REPOS
623 #CREATE DEFAULT TEST REPOS
623 cur_dir = dn(dn(abspath(__file__)))
624 cur_dir = dn(dn(abspath(__file__)))
624 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
625 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
625 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
626 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
626 tar.close()
627 tar.close()
627
628
628 cur_dir = dn(dn(abspath(__file__)))
629 cur_dir = dn(dn(abspath(__file__)))
629 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
630 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
630 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
631 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
631 tar.close()
632 tar.close()
632
633
633 #LOAD VCS test stuff
634 #LOAD VCS test stuff
634 from rhodecode.tests.vcs import setup_package
635 from rhodecode.tests.vcs import setup_package
635 setup_package()
636 setup_package()
636
637
637
638
638 #==============================================================================
639 #==============================================================================
639 # PASTER COMMANDS
640 # PASTER COMMANDS
640 #==============================================================================
641 #==============================================================================
641 class BasePasterCommand(Command):
642 class BasePasterCommand(Command):
642 """
643 """
643 Abstract Base Class for paster commands.
644 Abstract Base Class for paster commands.
644
645
645 The celery commands are somewhat aggressive about loading
646 The celery commands are somewhat aggressive about loading
646 celery.conf, and since our module sets the `CELERY_LOADER`
647 celery.conf, and since our module sets the `CELERY_LOADER`
647 environment variable to our loader, we have to bootstrap a bit and
648 environment variable to our loader, we have to bootstrap a bit and
648 make sure we've had a chance to load the pylons config off of the
649 make sure we've had a chance to load the pylons config off of the
649 command line, otherwise everything fails.
650 command line, otherwise everything fails.
650 """
651 """
651 min_args = 1
652 min_args = 1
652 min_args_error = "Please provide a paster config file as an argument."
653 min_args_error = "Please provide a paster config file as an argument."
653 takes_config_file = 1
654 takes_config_file = 1
654 requires_config_file = True
655 requires_config_file = True
655
656
656 def notify_msg(self, msg, log=False):
657 def notify_msg(self, msg, log=False):
657 """Make a notification to user, additionally if logger is passed
658 """Make a notification to user, additionally if logger is passed
658 it logs this action using given logger
659 it logs this action using given logger
659
660
660 :param msg: message that will be printed to user
661 :param msg: message that will be printed to user
661 :param log: logging instance, to use to additionally log this message
662 :param log: logging instance, to use to additionally log this message
662
663
663 """
664 """
664 if log and isinstance(log, logging):
665 if log and isinstance(log, logging):
665 log(msg)
666 log(msg)
666
667
667 def run(self, args):
668 def run(self, args):
668 """
669 """
669 Overrides Command.run
670 Overrides Command.run
670
671
671 Checks for a config file argument and loads it.
672 Checks for a config file argument and loads it.
672 """
673 """
673 if len(args) < self.min_args:
674 if len(args) < self.min_args:
674 raise BadCommand(
675 raise BadCommand(
675 self.min_args_error % {'min_args': self.min_args,
676 self.min_args_error % {'min_args': self.min_args,
676 'actual_args': len(args)})
677 'actual_args': len(args)})
677
678
678 # Decrement because we're going to lob off the first argument.
679 # Decrement because we're going to lob off the first argument.
679 # @@ This is hacky
680 # @@ This is hacky
680 self.min_args -= 1
681 self.min_args -= 1
681 self.bootstrap_config(args[0])
682 self.bootstrap_config(args[0])
682 self.update_parser()
683 self.update_parser()
683 return super(BasePasterCommand, self).run(args[1:])
684 return super(BasePasterCommand, self).run(args[1:])
684
685
685 def update_parser(self):
686 def update_parser(self):
686 """
687 """
687 Abstract method. Allows for the class's parser to be updated
688 Abstract method. Allows for the class's parser to be updated
688 before the superclass's `run` method is called. Necessary to
689 before the superclass's `run` method is called. Necessary to
689 allow options/arguments to be passed through to the underlying
690 allow options/arguments to be passed through to the underlying
690 celery command.
691 celery command.
691 """
692 """
692 raise NotImplementedError("Abstract Method.")
693 raise NotImplementedError("Abstract Method.")
693
694
694 def bootstrap_config(self, conf):
695 def bootstrap_config(self, conf):
695 """
696 """
696 Loads the pylons configuration.
697 Loads the pylons configuration.
697 """
698 """
698 from pylons import config as pylonsconfig
699 from pylons import config as pylonsconfig
699
700
700 self.path_to_ini_file = os.path.realpath(conf)
701 self.path_to_ini_file = os.path.realpath(conf)
701 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
702 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
702 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
703 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
General Comments 0
You need to be logged in to leave comments. Login now