##// END OF EJS Templates
fix strict version of git check if we have 1.7.4 it's ok !
marcink -
r2997:da3926d9 beta
parent child Browse files
Show More
@@ -1,714 +1,714 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 a user object or a username')
139 raise Exception('You have to provide a user object or a 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, scm=None):
206 def is_valid_repo(repo_name, base_path, scm=None):
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 If scm param is given also compare if given scm is the same as expected
209 If scm param is given also compare if given scm is the same as expected
210 from scm parameter
210 from scm parameter
211
211
212 :param repo_name:
212 :param repo_name:
213 :param base_path:
213 :param base_path:
214 :param scm:
214 :param scm:
215
215
216 :return True: if given path is a valid repository
216 :return True: if given path is a valid repository
217 """
217 """
218 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
218 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
219
219
220 try:
220 try:
221 scm_ = get_scm(full_path)
221 scm_ = get_scm(full_path)
222 if scm:
222 if scm:
223 return scm_[0] == scm
223 return scm_[0] == scm
224 return True
224 return True
225 except VCSError:
225 except VCSError:
226 return False
226 return False
227
227
228
228
229 def is_valid_repos_group(repos_group_name, base_path):
229 def is_valid_repos_group(repos_group_name, base_path):
230 """
230 """
231 Returns True if given path is a repos group False otherwise
231 Returns True if given path is a repos group False otherwise
232
232
233 :param repo_name:
233 :param repo_name:
234 :param base_path:
234 :param base_path:
235 """
235 """
236 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
236 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
237
237
238 # check if it's not a repo
238 # check if it's not a repo
239 if is_valid_repo(repos_group_name, base_path):
239 if is_valid_repo(repos_group_name, base_path):
240 return False
240 return False
241
241
242 try:
242 try:
243 # we need to check bare git repos at higher level
243 # we need to check bare git repos at higher level
244 # since we might match branches/hooks/info/objects or possible
244 # since we might match branches/hooks/info/objects or possible
245 # other things inside bare git repo
245 # other things inside bare git repo
246 get_scm(os.path.dirname(full_path))
246 get_scm(os.path.dirname(full_path))
247 return False
247 return False
248 except VCSError:
248 except VCSError:
249 pass
249 pass
250
250
251 # check if it's a valid path
251 # check if it's a valid path
252 if os.path.isdir(full_path):
252 if os.path.isdir(full_path):
253 return True
253 return True
254
254
255 return False
255 return False
256
256
257
257
258 def ask_ok(prompt, retries=4, complaint='Yes or no please!'):
258 def ask_ok(prompt, retries=4, complaint='Yes or no please!'):
259 while True:
259 while True:
260 ok = raw_input(prompt)
260 ok = raw_input(prompt)
261 if ok in ('y', 'ye', 'yes'):
261 if ok in ('y', 'ye', 'yes'):
262 return True
262 return True
263 if ok in ('n', 'no', 'nop', 'nope'):
263 if ok in ('n', 'no', 'nop', 'nope'):
264 return False
264 return False
265 retries = retries - 1
265 retries = retries - 1
266 if retries < 0:
266 if retries < 0:
267 raise IOError
267 raise IOError
268 print complaint
268 print complaint
269
269
270 #propagated from mercurial documentation
270 #propagated from mercurial documentation
271 ui_sections = ['alias', 'auth',
271 ui_sections = ['alias', 'auth',
272 'decode/encode', 'defaults',
272 'decode/encode', 'defaults',
273 'diff', 'email',
273 'diff', 'email',
274 'extensions', 'format',
274 'extensions', 'format',
275 'merge-patterns', 'merge-tools',
275 'merge-patterns', 'merge-tools',
276 'hooks', 'http_proxy',
276 'hooks', 'http_proxy',
277 'smtp', 'patch',
277 'smtp', 'patch',
278 'paths', 'profiling',
278 'paths', 'profiling',
279 'server', 'trusted',
279 'server', 'trusted',
280 'ui', 'web', ]
280 'ui', 'web', ]
281
281
282
282
283 def make_ui(read_from='file', path=None, checkpaths=True, clear_session=True):
283 def make_ui(read_from='file', path=None, checkpaths=True, clear_session=True):
284 """
284 """
285 A function that will read python rc files or database
285 A function that will read python rc files or database
286 and make an mercurial ui object from read options
286 and make an mercurial ui object from read options
287
287
288 :param path: path to mercurial config file
288 :param path: path to mercurial config file
289 :param checkpaths: check the path
289 :param checkpaths: check the path
290 :param read_from: read from 'file' or 'db'
290 :param read_from: read from 'file' or 'db'
291 """
291 """
292
292
293 baseui = ui.ui()
293 baseui = ui.ui()
294
294
295 # clean the baseui object
295 # clean the baseui object
296 baseui._ocfg = config.config()
296 baseui._ocfg = config.config()
297 baseui._ucfg = config.config()
297 baseui._ucfg = config.config()
298 baseui._tcfg = config.config()
298 baseui._tcfg = config.config()
299
299
300 if read_from == 'file':
300 if read_from == 'file':
301 if not os.path.isfile(path):
301 if not os.path.isfile(path):
302 log.debug('hgrc file is not present at %s, skipping...' % path)
302 log.debug('hgrc file is not present at %s, skipping...' % path)
303 return False
303 return False
304 log.debug('reading hgrc from %s' % path)
304 log.debug('reading hgrc from %s' % path)
305 cfg = config.config()
305 cfg = config.config()
306 cfg.read(path)
306 cfg.read(path)
307 for section in ui_sections:
307 for section in ui_sections:
308 for k, v in cfg.items(section):
308 for k, v in cfg.items(section):
309 log.debug('settings ui from file[%s]%s:%s' % (section, k, v))
309 log.debug('settings ui from file[%s]%s:%s' % (section, k, v))
310 baseui.setconfig(safe_str(section), safe_str(k), safe_str(v))
310 baseui.setconfig(safe_str(section), safe_str(k), safe_str(v))
311
311
312 elif read_from == 'db':
312 elif read_from == 'db':
313 sa = meta.Session()
313 sa = meta.Session()
314 ret = sa.query(RhodeCodeUi)\
314 ret = sa.query(RhodeCodeUi)\
315 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
315 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
316 .all()
316 .all()
317
317
318 hg_ui = ret
318 hg_ui = ret
319 for ui_ in hg_ui:
319 for ui_ in hg_ui:
320 if ui_.ui_active:
320 if ui_.ui_active:
321 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
321 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
322 ui_.ui_key, ui_.ui_value)
322 ui_.ui_key, ui_.ui_value)
323 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
323 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
324 safe_str(ui_.ui_value))
324 safe_str(ui_.ui_value))
325 if ui_.ui_key == 'push_ssl':
325 if ui_.ui_key == 'push_ssl':
326 # force set push_ssl requirement to False, rhodecode
326 # force set push_ssl requirement to False, rhodecode
327 # handles that
327 # handles that
328 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
328 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
329 False)
329 False)
330 if clear_session:
330 if clear_session:
331 meta.Session.remove()
331 meta.Session.remove()
332 return baseui
332 return baseui
333
333
334
334
335 def set_rhodecode_config(config):
335 def set_rhodecode_config(config):
336 """
336 """
337 Updates pylons config with new settings from database
337 Updates pylons config with new settings from database
338
338
339 :param config:
339 :param config:
340 """
340 """
341 hgsettings = RhodeCodeSetting.get_app_settings()
341 hgsettings = RhodeCodeSetting.get_app_settings()
342
342
343 for k, v in hgsettings.items():
343 for k, v in hgsettings.items():
344 config[k] = v
344 config[k] = v
345
345
346
346
347 def invalidate_cache(cache_key, *args):
347 def invalidate_cache(cache_key, *args):
348 """
348 """
349 Puts cache invalidation task into db for
349 Puts cache invalidation task into db for
350 further global cache invalidation
350 further global cache invalidation
351 """
351 """
352
352
353 from rhodecode.model.scm import ScmModel
353 from rhodecode.model.scm import ScmModel
354
354
355 if cache_key.startswith('get_repo_cached_'):
355 if cache_key.startswith('get_repo_cached_'):
356 name = cache_key.split('get_repo_cached_')[-1]
356 name = cache_key.split('get_repo_cached_')[-1]
357 ScmModel().mark_for_invalidation(name)
357 ScmModel().mark_for_invalidation(name)
358
358
359
359
360 def map_groups(path):
360 def map_groups(path):
361 """
361 """
362 Given a full path to a repository, create all nested groups that this
362 Given a full path to a repository, create all nested groups that this
363 repo is inside. This function creates parent-child relationships between
363 repo is inside. This function creates parent-child relationships between
364 groups and creates default perms for all new groups.
364 groups and creates default perms for all new groups.
365
365
366 :param paths: full path to repository
366 :param paths: full path to repository
367 """
367 """
368 sa = meta.Session()
368 sa = meta.Session()
369 groups = path.split(Repository.url_sep())
369 groups = path.split(Repository.url_sep())
370 parent = None
370 parent = None
371 group = None
371 group = None
372
372
373 # last element is repo in nested groups structure
373 # last element is repo in nested groups structure
374 groups = groups[:-1]
374 groups = groups[:-1]
375 rgm = ReposGroupModel(sa)
375 rgm = ReposGroupModel(sa)
376 for lvl, group_name in enumerate(groups):
376 for lvl, group_name in enumerate(groups):
377 group_name = '/'.join(groups[:lvl] + [group_name])
377 group_name = '/'.join(groups[:lvl] + [group_name])
378 group = RepoGroup.get_by_group_name(group_name)
378 group = RepoGroup.get_by_group_name(group_name)
379 desc = '%s group' % group_name
379 desc = '%s group' % group_name
380
380
381 # skip folders that are now removed repos
381 # skip folders that are now removed repos
382 if REMOVED_REPO_PAT.match(group_name):
382 if REMOVED_REPO_PAT.match(group_name):
383 break
383 break
384
384
385 if group is None:
385 if group is None:
386 log.debug('creating group level: %s group_name: %s' % (lvl,
386 log.debug('creating group level: %s group_name: %s' % (lvl,
387 group_name))
387 group_name))
388 group = RepoGroup(group_name, parent)
388 group = RepoGroup(group_name, parent)
389 group.group_description = desc
389 group.group_description = desc
390 sa.add(group)
390 sa.add(group)
391 rgm._create_default_perms(group)
391 rgm._create_default_perms(group)
392 sa.flush()
392 sa.flush()
393 parent = group
393 parent = group
394 return group
394 return group
395
395
396
396
397 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
397 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
398 install_git_hook=False):
398 install_git_hook=False):
399 """
399 """
400 maps all repos given in initial_repo_list, non existing repositories
400 maps all repos given in initial_repo_list, non existing repositories
401 are created, if remove_obsolete is True it also check for db entries
401 are created, if remove_obsolete is True it also check for db entries
402 that are not in initial_repo_list and removes them.
402 that are not in initial_repo_list and removes them.
403
403
404 :param initial_repo_list: list of repositories found by scanning methods
404 :param initial_repo_list: list of repositories found by scanning methods
405 :param remove_obsolete: check for obsolete entries in database
405 :param remove_obsolete: check for obsolete entries in database
406 :param install_git_hook: if this is True, also check and install githook
406 :param install_git_hook: if this is True, also check and install githook
407 for a repo if missing
407 for a repo if missing
408 """
408 """
409 from rhodecode.model.repo import RepoModel
409 from rhodecode.model.repo import RepoModel
410 from rhodecode.model.scm import ScmModel
410 from rhodecode.model.scm import ScmModel
411 sa = meta.Session()
411 sa = meta.Session()
412 rm = RepoModel()
412 rm = RepoModel()
413 user = sa.query(User).filter(User.admin == True).first()
413 user = sa.query(User).filter(User.admin == True).first()
414 if user is None:
414 if user is None:
415 raise Exception('Missing administrative account!')
415 raise Exception('Missing administrative account!')
416 added = []
416 added = []
417
417
418 # # clear cache keys
418 # # clear cache keys
419 # log.debug("Clearing cache keys now...")
419 # log.debug("Clearing cache keys now...")
420 # CacheInvalidation.clear_cache()
420 # CacheInvalidation.clear_cache()
421 # sa.commit()
421 # sa.commit()
422
422
423 for name, repo in initial_repo_list.items():
423 for name, repo in initial_repo_list.items():
424 group = map_groups(name)
424 group = map_groups(name)
425 db_repo = rm.get_by_repo_name(name)
425 db_repo = rm.get_by_repo_name(name)
426 # found repo that is on filesystem not in RhodeCode database
426 # found repo that is on filesystem not in RhodeCode database
427 if not db_repo:
427 if not db_repo:
428 log.info('repository %s not found, creating now' % name)
428 log.info('repository %s not found, creating now' % name)
429 added.append(name)
429 added.append(name)
430 desc = (repo.description
430 desc = (repo.description
431 if repo.description != 'unknown'
431 if repo.description != 'unknown'
432 else '%s repository' % name)
432 else '%s repository' % name)
433 new_repo = rm.create_repo(
433 new_repo = rm.create_repo(
434 repo_name=name,
434 repo_name=name,
435 repo_type=repo.alias,
435 repo_type=repo.alias,
436 description=desc,
436 description=desc,
437 repos_group=getattr(group, 'group_id', None),
437 repos_group=getattr(group, 'group_id', None),
438 owner=user,
438 owner=user,
439 just_db=True
439 just_db=True
440 )
440 )
441 # we added that repo just now, and make sure it has githook
441 # we added that repo just now, and make sure it has githook
442 # installed
442 # installed
443 if new_repo.repo_type == 'git':
443 if new_repo.repo_type == 'git':
444 ScmModel().install_git_hook(new_repo.scm_instance)
444 ScmModel().install_git_hook(new_repo.scm_instance)
445 elif install_git_hook:
445 elif install_git_hook:
446 if db_repo.repo_type == 'git':
446 if db_repo.repo_type == 'git':
447 ScmModel().install_git_hook(db_repo.scm_instance)
447 ScmModel().install_git_hook(db_repo.scm_instance)
448 # during starting install all cache keys for all repositories in the
448 # during starting install all cache keys for all repositories in the
449 # system, this will register all repos and multiple instances
449 # system, this will register all repos and multiple instances
450 key, _prefix, _org_key = CacheInvalidation._get_key(name)
450 key, _prefix, _org_key = CacheInvalidation._get_key(name)
451 log.debug("Creating a cache key for %s instance_id:`%s`" % (name, _prefix))
451 log.debug("Creating a cache key for %s instance_id:`%s`" % (name, _prefix))
452 CacheInvalidation._get_or_create_key(key, _prefix, _org_key, commit=False)
452 CacheInvalidation._get_or_create_key(key, _prefix, _org_key, commit=False)
453 sa.commit()
453 sa.commit()
454 removed = []
454 removed = []
455 if remove_obsolete:
455 if remove_obsolete:
456 # remove from database those repositories that are not in the filesystem
456 # remove from database those repositories that are not in the filesystem
457 for repo in sa.query(Repository).all():
457 for repo in sa.query(Repository).all():
458 if repo.repo_name not in initial_repo_list.keys():
458 if repo.repo_name not in initial_repo_list.keys():
459 log.debug("Removing non-existing repository found in db `%s`" %
459 log.debug("Removing non-existing repository found in db `%s`" %
460 repo.repo_name)
460 repo.repo_name)
461 try:
461 try:
462 sa.delete(repo)
462 sa.delete(repo)
463 sa.commit()
463 sa.commit()
464 removed.append(repo.repo_name)
464 removed.append(repo.repo_name)
465 except:
465 except:
466 #don't hold further removals on error
466 #don't hold further removals on error
467 log.error(traceback.format_exc())
467 log.error(traceback.format_exc())
468 sa.rollback()
468 sa.rollback()
469
469
470 return added, removed
470 return added, removed
471
471
472
472
473 # set cache regions for beaker so celery can utilise it
473 # set cache regions for beaker so celery can utilise it
474 def add_cache(settings):
474 def add_cache(settings):
475 cache_settings = {'regions': None}
475 cache_settings = {'regions': None}
476 for key in settings.keys():
476 for key in settings.keys():
477 for prefix in ['beaker.cache.', 'cache.']:
477 for prefix in ['beaker.cache.', 'cache.']:
478 if key.startswith(prefix):
478 if key.startswith(prefix):
479 name = key.split(prefix)[1].strip()
479 name = key.split(prefix)[1].strip()
480 cache_settings[name] = settings[key].strip()
480 cache_settings[name] = settings[key].strip()
481 if cache_settings['regions']:
481 if cache_settings['regions']:
482 for region in cache_settings['regions'].split(','):
482 for region in cache_settings['regions'].split(','):
483 region = region.strip()
483 region = region.strip()
484 region_settings = {}
484 region_settings = {}
485 for key, value in cache_settings.items():
485 for key, value in cache_settings.items():
486 if key.startswith(region):
486 if key.startswith(region):
487 region_settings[key.split('.')[1]] = value
487 region_settings[key.split('.')[1]] = value
488 region_settings['expire'] = int(region_settings.get('expire',
488 region_settings['expire'] = int(region_settings.get('expire',
489 60))
489 60))
490 region_settings.setdefault('lock_dir',
490 region_settings.setdefault('lock_dir',
491 cache_settings.get('lock_dir'))
491 cache_settings.get('lock_dir'))
492 region_settings.setdefault('data_dir',
492 region_settings.setdefault('data_dir',
493 cache_settings.get('data_dir'))
493 cache_settings.get('data_dir'))
494
494
495 if 'type' not in region_settings:
495 if 'type' not in region_settings:
496 region_settings['type'] = cache_settings.get('type',
496 region_settings['type'] = cache_settings.get('type',
497 'memory')
497 'memory')
498 beaker.cache.cache_regions[region] = region_settings
498 beaker.cache.cache_regions[region] = region_settings
499
499
500
500
501 def load_rcextensions(root_path):
501 def load_rcextensions(root_path):
502 import rhodecode
502 import rhodecode
503 from rhodecode.config import conf
503 from rhodecode.config import conf
504
504
505 path = os.path.join(root_path, 'rcextensions', '__init__.py')
505 path = os.path.join(root_path, 'rcextensions', '__init__.py')
506 if os.path.isfile(path):
506 if os.path.isfile(path):
507 rcext = create_module('rc', path)
507 rcext = create_module('rc', path)
508 EXT = rhodecode.EXTENSIONS = rcext
508 EXT = rhodecode.EXTENSIONS = rcext
509 log.debug('Found rcextensions now loading %s...' % rcext)
509 log.debug('Found rcextensions now loading %s...' % rcext)
510
510
511 # Additional mappings that are not present in the pygments lexers
511 # Additional mappings that are not present in the pygments lexers
512 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
512 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
513
513
514 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
514 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
515
515
516 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
516 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
517 log.debug('settings custom INDEX_EXTENSIONS')
517 log.debug('settings custom INDEX_EXTENSIONS')
518 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
518 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
519
519
520 #ADDITIONAL MAPPINGS
520 #ADDITIONAL MAPPINGS
521 log.debug('adding extra into INDEX_EXTENSIONS')
521 log.debug('adding extra into INDEX_EXTENSIONS')
522 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
522 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
523
523
524
524
525 #==============================================================================
525 #==============================================================================
526 # TEST FUNCTIONS AND CREATORS
526 # TEST FUNCTIONS AND CREATORS
527 #==============================================================================
527 #==============================================================================
528 def create_test_index(repo_location, config, full_index):
528 def create_test_index(repo_location, config, full_index):
529 """
529 """
530 Makes default test index
530 Makes default test index
531
531
532 :param config: test config
532 :param config: test config
533 :param full_index:
533 :param full_index:
534 """
534 """
535
535
536 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
536 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
537 from rhodecode.lib.pidlock import DaemonLock, LockHeld
537 from rhodecode.lib.pidlock import DaemonLock, LockHeld
538
538
539 repo_location = repo_location
539 repo_location = repo_location
540
540
541 index_location = os.path.join(config['app_conf']['index_dir'])
541 index_location = os.path.join(config['app_conf']['index_dir'])
542 if not os.path.exists(index_location):
542 if not os.path.exists(index_location):
543 os.makedirs(index_location)
543 os.makedirs(index_location)
544
544
545 try:
545 try:
546 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
546 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
547 WhooshIndexingDaemon(index_location=index_location,
547 WhooshIndexingDaemon(index_location=index_location,
548 repo_location=repo_location)\
548 repo_location=repo_location)\
549 .run(full_index=full_index)
549 .run(full_index=full_index)
550 l.release()
550 l.release()
551 except LockHeld:
551 except LockHeld:
552 pass
552 pass
553
553
554
554
555 def create_test_env(repos_test_path, config):
555 def create_test_env(repos_test_path, config):
556 """
556 """
557 Makes a fresh database and
557 Makes a fresh database and
558 install test repository into tmp dir
558 install test repository into tmp dir
559 """
559 """
560 from rhodecode.lib.db_manage import DbManage
560 from rhodecode.lib.db_manage import DbManage
561 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
561 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
562
562
563 # PART ONE create db
563 # PART ONE create db
564 dbconf = config['sqlalchemy.db1.url']
564 dbconf = config['sqlalchemy.db1.url']
565 log.debug('making test db %s' % dbconf)
565 log.debug('making test db %s' % dbconf)
566
566
567 # create test dir if it doesn't exist
567 # create test dir if it doesn't exist
568 if not os.path.isdir(repos_test_path):
568 if not os.path.isdir(repos_test_path):
569 log.debug('Creating testdir %s' % repos_test_path)
569 log.debug('Creating testdir %s' % repos_test_path)
570 os.makedirs(repos_test_path)
570 os.makedirs(repos_test_path)
571
571
572 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
572 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
573 tests=True)
573 tests=True)
574 dbmanage.create_tables(override=True)
574 dbmanage.create_tables(override=True)
575 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
575 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
576 dbmanage.create_default_user()
576 dbmanage.create_default_user()
577 dbmanage.admin_prompt()
577 dbmanage.admin_prompt()
578 dbmanage.create_permissions()
578 dbmanage.create_permissions()
579 dbmanage.populate_default_permissions()
579 dbmanage.populate_default_permissions()
580 Session().commit()
580 Session().commit()
581 # PART TWO make test repo
581 # PART TWO make test repo
582 log.debug('making test vcs repositories')
582 log.debug('making test vcs repositories')
583
583
584 idx_path = config['app_conf']['index_dir']
584 idx_path = config['app_conf']['index_dir']
585 data_path = config['app_conf']['cache_dir']
585 data_path = config['app_conf']['cache_dir']
586
586
587 #clean index and data
587 #clean index and data
588 if idx_path and os.path.exists(idx_path):
588 if idx_path and os.path.exists(idx_path):
589 log.debug('remove %s' % idx_path)
589 log.debug('remove %s' % idx_path)
590 shutil.rmtree(idx_path)
590 shutil.rmtree(idx_path)
591
591
592 if data_path and os.path.exists(data_path):
592 if data_path and os.path.exists(data_path):
593 log.debug('remove %s' % data_path)
593 log.debug('remove %s' % data_path)
594 shutil.rmtree(data_path)
594 shutil.rmtree(data_path)
595
595
596 #CREATE DEFAULT TEST REPOS
596 #CREATE DEFAULT TEST REPOS
597 cur_dir = dn(dn(abspath(__file__)))
597 cur_dir = dn(dn(abspath(__file__)))
598 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
598 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
599 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
599 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
600 tar.close()
600 tar.close()
601
601
602 cur_dir = dn(dn(abspath(__file__)))
602 cur_dir = dn(dn(abspath(__file__)))
603 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
603 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
604 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
604 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
605 tar.close()
605 tar.close()
606
606
607 #LOAD VCS test stuff
607 #LOAD VCS test stuff
608 from rhodecode.tests.vcs import setup_package
608 from rhodecode.tests.vcs import setup_package
609 setup_package()
609 setup_package()
610
610
611
611
612 #==============================================================================
612 #==============================================================================
613 # PASTER COMMANDS
613 # PASTER COMMANDS
614 #==============================================================================
614 #==============================================================================
615 class BasePasterCommand(Command):
615 class BasePasterCommand(Command):
616 """
616 """
617 Abstract Base Class for paster commands.
617 Abstract Base Class for paster commands.
618
618
619 The celery commands are somewhat aggressive about loading
619 The celery commands are somewhat aggressive about loading
620 celery.conf, and since our module sets the `CELERY_LOADER`
620 celery.conf, and since our module sets the `CELERY_LOADER`
621 environment variable to our loader, we have to bootstrap a bit and
621 environment variable to our loader, we have to bootstrap a bit and
622 make sure we've had a chance to load the pylons config off of the
622 make sure we've had a chance to load the pylons config off of the
623 command line, otherwise everything fails.
623 command line, otherwise everything fails.
624 """
624 """
625 min_args = 1
625 min_args = 1
626 min_args_error = "Please provide a paster config file as an argument."
626 min_args_error = "Please provide a paster config file as an argument."
627 takes_config_file = 1
627 takes_config_file = 1
628 requires_config_file = True
628 requires_config_file = True
629
629
630 def notify_msg(self, msg, log=False):
630 def notify_msg(self, msg, log=False):
631 """Make a notification to user, additionally if logger is passed
631 """Make a notification to user, additionally if logger is passed
632 it logs this action using given logger
632 it logs this action using given logger
633
633
634 :param msg: message that will be printed to user
634 :param msg: message that will be printed to user
635 :param log: logging instance, to use to additionally log this message
635 :param log: logging instance, to use to additionally log this message
636
636
637 """
637 """
638 if log and isinstance(log, logging):
638 if log and isinstance(log, logging):
639 log(msg)
639 log(msg)
640
640
641 def run(self, args):
641 def run(self, args):
642 """
642 """
643 Overrides Command.run
643 Overrides Command.run
644
644
645 Checks for a config file argument and loads it.
645 Checks for a config file argument and loads it.
646 """
646 """
647 if len(args) < self.min_args:
647 if len(args) < self.min_args:
648 raise BadCommand(
648 raise BadCommand(
649 self.min_args_error % {'min_args': self.min_args,
649 self.min_args_error % {'min_args': self.min_args,
650 'actual_args': len(args)})
650 'actual_args': len(args)})
651
651
652 # Decrement because we're going to lob off the first argument.
652 # Decrement because we're going to lob off the first argument.
653 # @@ This is hacky
653 # @@ This is hacky
654 self.min_args -= 1
654 self.min_args -= 1
655 self.bootstrap_config(args[0])
655 self.bootstrap_config(args[0])
656 self.update_parser()
656 self.update_parser()
657 return super(BasePasterCommand, self).run(args[1:])
657 return super(BasePasterCommand, self).run(args[1:])
658
658
659 def update_parser(self):
659 def update_parser(self):
660 """
660 """
661 Abstract method. Allows for the class's parser to be updated
661 Abstract method. Allows for the class's parser to be updated
662 before the superclass's `run` method is called. Necessary to
662 before the superclass's `run` method is called. Necessary to
663 allow options/arguments to be passed through to the underlying
663 allow options/arguments to be passed through to the underlying
664 celery command.
664 celery command.
665 """
665 """
666 raise NotImplementedError("Abstract Method.")
666 raise NotImplementedError("Abstract Method.")
667
667
668 def bootstrap_config(self, conf):
668 def bootstrap_config(self, conf):
669 """
669 """
670 Loads the pylons configuration.
670 Loads the pylons configuration.
671 """
671 """
672 from pylons import config as pylonsconfig
672 from pylons import config as pylonsconfig
673
673
674 self.path_to_ini_file = os.path.realpath(conf)
674 self.path_to_ini_file = os.path.realpath(conf)
675 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
675 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
676 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
676 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
677
677
678
678
679 def check_git_version():
679 def check_git_version():
680 """
680 """
681 Checks what version of git is installed in system, and issues a warning
681 Checks what version of git is installed in system, and issues a warning
682 if it's too old for RhodeCode to properly work.
682 if it's too old for RhodeCode to properly work.
683 """
683 """
684 import subprocess
684 import subprocess
685 from distutils.version import StrictVersion
685 from distutils.version import StrictVersion
686 from rhodecode import BACKENDS
686 from rhodecode import BACKENDS
687
687
688 p = subprocess.Popen('git --version', shell=True,
688 p = subprocess.Popen('git --version', shell=True,
689 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
689 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
690 stdout, stderr = p.communicate()
690 stdout, stderr = p.communicate()
691 ver = (stdout.split(' ')[-1] or '').strip() or '0.0.0'
691 ver = (stdout.split(' ')[-1] or '').strip() or '0.0.0'
692 if len(ver.split('.')) > 3:
692 if len(ver.split('.')) > 3:
693 #StrictVersion needs to be only 3 element type
693 #StrictVersion needs to be only 3 element type
694 ver = '.'.join(ver.split('.')[:3])
694 ver = '.'.join(ver.split('.')[:3])
695 try:
695 try:
696 _ver = StrictVersion(ver)
696 _ver = StrictVersion(ver)
697 except:
697 except:
698 _ver = StrictVersion('0.0.0')
698 _ver = StrictVersion('0.0.0')
699 stderr = traceback.format_exc()
699 stderr = traceback.format_exc()
700
700
701 req_ver = '1.7.4'
701 req_ver = '1.7.4'
702 to_old_git = False
702 to_old_git = False
703 if _ver <= StrictVersion(req_ver):
703 if _ver < StrictVersion(req_ver):
704 to_old_git = True
704 to_old_git = True
705
705
706 if 'git' in BACKENDS:
706 if 'git' in BACKENDS:
707 log.debug('GIT version detected: %s' % stdout)
707 log.debug('GIT version detected: %s' % stdout)
708 if stderr:
708 if stderr:
709 log.warning('Unable to detect git version org error was:%r' % stderr)
709 log.warning('Unable to detect git version org error was:%r' % stderr)
710 elif to_old_git:
710 elif to_old_git:
711 log.warning('RhodeCode detected git version %s, which is too old '
711 log.warning('RhodeCode detected git version %s, which is too old '
712 'for the system to function properly. Make sure '
712 'for the system to function properly. Make sure '
713 'its version is at least %s' % (ver, req_ver))
713 'its version is at least %s' % (ver, req_ver))
714 return _ver
714 return _ver
General Comments 0
You need to be logged in to leave comments. Login now