##// END OF EJS Templates
fixes rhodecode upgrade problem that caused setuptool to crash on importing sqlalchemy models
marcink -
r1541:70e646b2 beta
parent child Browse files
Show More
@@ -1,56 +1,56 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.__init__
3 rhodecode.__init__
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 RhodeCode, a web based repository management based on pylons
6 RhodeCode, a web based repository management based on pylons
7 versioning implementation: http://semver.org/
7 versioning implementation: http://semver.org/
8
8
9 :created_on: Apr 9, 2010
9 :created_on: Apr 9, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 import platform
26 import platform
27
27
28 VERSION = (1, 3, 0, 'beta')
28 VERSION = (1, 3, 0, 'beta')
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
30 __dbversion__ = 4 #defines current db version for migrations
30 __dbversion__ = 4 #defines current db version for migrations
31 __platform__ = platform.system()
31 __platform__ = platform.system()
32 __license__ = 'GPLv3'
32 __license__ = 'GPLv3'
33
33
34 PLATFORM_WIN = ('Windows')
34 PLATFORM_WIN = ('Windows')
35 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS')
35 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS')
36
36
37 try:
37 try:
38 from rhodecode.lib.utils import get_current_revision
38 from rhodecode.lib import get_current_revision
39 _rev = get_current_revision()
39 _rev = get_current_revision()
40 except ImportError:
40 except ImportError:
41 #this is needed when doing some setup.py operations
41 #this is needed when doing some setup.py operations
42 _rev = False
42 _rev = False
43
43
44 if len(VERSION) > 3 and _rev:
44 if len(VERSION) > 3 and _rev:
45 __version__ += ' [rev:%s]' % _rev[0]
45 __version__ += ' [rev:%s]' % _rev[0]
46
46
47
47
48 def get_version():
48 def get_version():
49 """Returns shorter version (digit parts only) as string."""
49 """Returns shorter version (digit parts only) as string."""
50
50
51 return '.'.join((str(each) for each in VERSION[:3]))
51 return '.'.join((str(each) for each in VERSION[:3]))
52
52
53 BACKENDS = {
53 BACKENDS = {
54 'hg': 'Mercurial repository',
54 'hg': 'Mercurial repository',
55 #'git': 'Git repository',
55 #'git': 'Git repository',
56 }
56 }
@@ -1,381 +1,404 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.__init__
3 rhodecode.lib.__init__
4 ~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Some simple helper functions
6 Some simple helper functions
7
7
8 :created_on: Jan 5, 2011
8 :created_on: Jan 5, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 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
27
26 def __get_lem():
28 def __get_lem():
27 from pygments import lexers
29 from pygments import lexers
28 from string import lower
30 from string import lower
29 from collections import defaultdict
31 from collections import defaultdict
30
32
31 d = defaultdict(lambda: [])
33 d = defaultdict(lambda: [])
32
34
33 def __clean(s):
35 def __clean(s):
34 s = s.lstrip('*')
36 s = s.lstrip('*')
35 s = s.lstrip('.')
37 s = s.lstrip('.')
36
38
37 if s.find('[') != -1:
39 if s.find('[') != -1:
38 exts = []
40 exts = []
39 start, stop = s.find('['), s.find(']')
41 start, stop = s.find('['), s.find(']')
40
42
41 for suffix in s[start + 1:stop]:
43 for suffix in s[start + 1:stop]:
42 exts.append(s[:s.find('[')] + suffix)
44 exts.append(s[:s.find('[')] + suffix)
43 return map(lower, exts)
45 return map(lower, exts)
44 else:
46 else:
45 return map(lower, [s])
47 return map(lower, [s])
46
48
47 for lx, t in sorted(lexers.LEXERS.items()):
49 for lx, t in sorted(lexers.LEXERS.items()):
48 m = map(__clean, t[-2])
50 m = map(__clean, t[-2])
49 if m:
51 if m:
50 m = reduce(lambda x, y: x + y, m)
52 m = reduce(lambda x, y: x + y, m)
51 for ext in m:
53 for ext in m:
52 desc = lx.replace('Lexer', '')
54 desc = lx.replace('Lexer', '')
53 d[ext].append(desc)
55 d[ext].append(desc)
54
56
55 return dict(d)
57 return dict(d)
56
58
57 # language map is also used by whoosh indexer, which for those specified
59 # language map is also used by whoosh indexer, which for those specified
58 # extensions will index it's content
60 # extensions will index it's content
59 LANGUAGES_EXTENSIONS_MAP = __get_lem()
61 LANGUAGES_EXTENSIONS_MAP = __get_lem()
60
62
61 # Additional mappings that are not present in the pygments lexers
63 # Additional mappings that are not present in the pygments lexers
62 # NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
64 # NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
63 ADDITIONAL_MAPPINGS = {'xaml': 'XAML'}
65 ADDITIONAL_MAPPINGS = {'xaml': 'XAML'}
64
66
65 LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS)
67 LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS)
66
68
67
69
68 def str2bool(_str):
70 def str2bool(_str):
69 """
71 """
70 returs True/False value from given string, it tries to translate the
72 returs True/False value from given string, it tries to translate the
71 string into boolean
73 string into boolean
72
74
73 :param _str: string value to translate into boolean
75 :param _str: string value to translate into boolean
74 :rtype: boolean
76 :rtype: boolean
75 :returns: boolean from given string
77 :returns: boolean from given string
76 """
78 """
77 if _str is None:
79 if _str is None:
78 return False
80 return False
79 if _str in (True, False):
81 if _str in (True, False):
80 return _str
82 return _str
81 _str = str(_str).strip().lower()
83 _str = str(_str).strip().lower()
82 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
84 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
83
85
84
86
85 def convert_line_endings(line, mode):
87 def convert_line_endings(line, mode):
86 """
88 """
87 Converts a given line "line end" accordingly to given mode
89 Converts a given line "line end" accordingly to given mode
88
90
89 Available modes are::
91 Available modes are::
90 0 - Unix
92 0 - Unix
91 1 - Mac
93 1 - Mac
92 2 - DOS
94 2 - DOS
93
95
94 :param line: given line to convert
96 :param line: given line to convert
95 :param mode: mode to convert to
97 :param mode: mode to convert to
96 :rtype: str
98 :rtype: str
97 :return: converted line according to mode
99 :return: converted line according to mode
98 """
100 """
99 from string import replace
101 from string import replace
100
102
101 if mode == 0:
103 if mode == 0:
102 line = replace(line, '\r\n', '\n')
104 line = replace(line, '\r\n', '\n')
103 line = replace(line, '\r', '\n')
105 line = replace(line, '\r', '\n')
104 elif mode == 1:
106 elif mode == 1:
105 line = replace(line, '\r\n', '\r')
107 line = replace(line, '\r\n', '\r')
106 line = replace(line, '\n', '\r')
108 line = replace(line, '\n', '\r')
107 elif mode == 2:
109 elif mode == 2:
108 import re
110 import re
109 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
111 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
110 return line
112 return line
111
113
112
114
113 def detect_mode(line, default):
115 def detect_mode(line, default):
114 """
116 """
115 Detects line break for given line, if line break couldn't be found
117 Detects line break for given line, if line break couldn't be found
116 given default value is returned
118 given default value is returned
117
119
118 :param line: str line
120 :param line: str line
119 :param default: default
121 :param default: default
120 :rtype: int
122 :rtype: int
121 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
123 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
122 """
124 """
123 if line.endswith('\r\n'):
125 if line.endswith('\r\n'):
124 return 2
126 return 2
125 elif line.endswith('\n'):
127 elif line.endswith('\n'):
126 return 0
128 return 0
127 elif line.endswith('\r'):
129 elif line.endswith('\r'):
128 return 1
130 return 1
129 else:
131 else:
130 return default
132 return default
131
133
132
134
133 def generate_api_key(username, salt=None):
135 def generate_api_key(username, salt=None):
134 """
136 """
135 Generates unique API key for given username, if salt is not given
137 Generates unique API key for given username, if salt is not given
136 it'll be generated from some random string
138 it'll be generated from some random string
137
139
138 :param username: username as string
140 :param username: username as string
139 :param salt: salt to hash generate KEY
141 :param salt: salt to hash generate KEY
140 :rtype: str
142 :rtype: str
141 :returns: sha1 hash from username+salt
143 :returns: sha1 hash from username+salt
142 """
144 """
143 from tempfile import _RandomNameSequence
145 from tempfile import _RandomNameSequence
144 import hashlib
146 import hashlib
145
147
146 if salt is None:
148 if salt is None:
147 salt = _RandomNameSequence().next()
149 salt = _RandomNameSequence().next()
148
150
149 return hashlib.sha1(username + salt).hexdigest()
151 return hashlib.sha1(username + salt).hexdigest()
150
152
151
153
152 def safe_unicode(str_, from_encoding='utf8'):
154 def safe_unicode(str_, from_encoding='utf8'):
153 """
155 """
154 safe unicode function. Does few trick to turn str_ into unicode
156 safe unicode function. Does few trick to turn str_ into unicode
155
157
156 In case of UnicodeDecode error we try to return it with encoding detected
158 In case of UnicodeDecode error we try to return it with encoding detected
157 by chardet library if it fails fallback to unicode with errors replaced
159 by chardet library if it fails fallback to unicode with errors replaced
158
160
159 :param str_: string to decode
161 :param str_: string to decode
160 :rtype: unicode
162 :rtype: unicode
161 :returns: unicode object
163 :returns: unicode object
162 """
164 """
163 if isinstance(str_, unicode):
165 if isinstance(str_, unicode):
164 return str_
166 return str_
165
167
166 try:
168 try:
167 return unicode(str_)
169 return unicode(str_)
168 except UnicodeDecodeError:
170 except UnicodeDecodeError:
169 pass
171 pass
170
172
171 try:
173 try:
172 return unicode(str_, from_encoding)
174 return unicode(str_, from_encoding)
173 except UnicodeDecodeError:
175 except UnicodeDecodeError:
174 pass
176 pass
175
177
176 try:
178 try:
177 import chardet
179 import chardet
178 encoding = chardet.detect(str_)['encoding']
180 encoding = chardet.detect(str_)['encoding']
179 if encoding is None:
181 if encoding is None:
180 raise Exception()
182 raise Exception()
181 return str_.decode(encoding)
183 return str_.decode(encoding)
182 except (ImportError, UnicodeDecodeError, Exception):
184 except (ImportError, UnicodeDecodeError, Exception):
183 return unicode(str_, from_encoding, 'replace')
185 return unicode(str_, from_encoding, 'replace')
184
186
185 def safe_str(unicode_, to_encoding='utf8'):
187 def safe_str(unicode_, to_encoding='utf8'):
186 """
188 """
187 safe str function. Does few trick to turn unicode_ into string
189 safe str function. Does few trick to turn unicode_ into string
188
190
189 In case of UnicodeEncodeError we try to return it with encoding detected
191 In case of UnicodeEncodeError we try to return it with encoding detected
190 by chardet library if it fails fallback to string with errors replaced
192 by chardet library if it fails fallback to string with errors replaced
191
193
192 :param unicode_: unicode to encode
194 :param unicode_: unicode to encode
193 :rtype: str
195 :rtype: str
194 :returns: str object
196 :returns: str object
195 """
197 """
196
198
197 if isinstance(unicode_, str):
199 if isinstance(unicode_, str):
198 return unicode_
200 return unicode_
199
201
200 try:
202 try:
201 return unicode_.encode(to_encoding)
203 return unicode_.encode(to_encoding)
202 except UnicodeEncodeError:
204 except UnicodeEncodeError:
203 pass
205 pass
204
206
205 try:
207 try:
206 import chardet
208 import chardet
207 encoding = chardet.detect(unicode_)['encoding']
209 encoding = chardet.detect(unicode_)['encoding']
208 print encoding
210 print encoding
209 if encoding is None:
211 if encoding is None:
210 raise UnicodeEncodeError()
212 raise UnicodeEncodeError()
211
213
212 return unicode_.encode(encoding)
214 return unicode_.encode(encoding)
213 except (ImportError, UnicodeEncodeError):
215 except (ImportError, UnicodeEncodeError):
214 return unicode_.encode(to_encoding, 'replace')
216 return unicode_.encode(to_encoding, 'replace')
215
217
216 return safe_str
218 return safe_str
217
219
218
220
219
221
220 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
222 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
221 """
223 """
222 Custom engine_from_config functions that makes sure we use NullPool for
224 Custom engine_from_config functions that makes sure we use NullPool for
223 file based sqlite databases. This prevents errors on sqlite. This only
225 file based sqlite databases. This prevents errors on sqlite. This only
224 applies to sqlalchemy versions < 0.7.0
226 applies to sqlalchemy versions < 0.7.0
225
227
226 """
228 """
227 import sqlalchemy
229 import sqlalchemy
228 from sqlalchemy import engine_from_config as efc
230 from sqlalchemy import engine_from_config as efc
229 import logging
231 import logging
230
232
231 if int(sqlalchemy.__version__.split('.')[1]) < 7:
233 if int(sqlalchemy.__version__.split('.')[1]) < 7:
232
234
233 # This solution should work for sqlalchemy < 0.7.0, and should use
235 # This solution should work for sqlalchemy < 0.7.0, and should use
234 # proxy=TimerProxy() for execution time profiling
236 # proxy=TimerProxy() for execution time profiling
235
237
236 from sqlalchemy.pool import NullPool
238 from sqlalchemy.pool import NullPool
237 url = configuration[prefix + 'url']
239 url = configuration[prefix + 'url']
238
240
239 if url.startswith('sqlite'):
241 if url.startswith('sqlite'):
240 kwargs.update({'poolclass': NullPool})
242 kwargs.update({'poolclass': NullPool})
241 return efc(configuration, prefix, **kwargs)
243 return efc(configuration, prefix, **kwargs)
242 else:
244 else:
243 import time
245 import time
244 from sqlalchemy import event
246 from sqlalchemy import event
245 from sqlalchemy.engine import Engine
247 from sqlalchemy.engine import Engine
246
248
247 log = logging.getLogger('sqlalchemy.engine')
249 log = logging.getLogger('sqlalchemy.engine')
248 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
250 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
249 engine = efc(configuration, prefix, **kwargs)
251 engine = efc(configuration, prefix, **kwargs)
250
252
251 def color_sql(sql):
253 def color_sql(sql):
252 COLOR_SEQ = "\033[1;%dm"
254 COLOR_SEQ = "\033[1;%dm"
253 COLOR_SQL = YELLOW
255 COLOR_SQL = YELLOW
254 normal = '\x1b[0m'
256 normal = '\x1b[0m'
255 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
257 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
256
258
257 if configuration['debug']:
259 if configuration['debug']:
258 #attach events only for debug configuration
260 #attach events only for debug configuration
259
261
260 def before_cursor_execute(conn, cursor, statement,
262 def before_cursor_execute(conn, cursor, statement,
261 parameters, context, executemany):
263 parameters, context, executemany):
262 context._query_start_time = time.time()
264 context._query_start_time = time.time()
263 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
265 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
264
266
265
267
266 def after_cursor_execute(conn, cursor, statement,
268 def after_cursor_execute(conn, cursor, statement,
267 parameters, context, executemany):
269 parameters, context, executemany):
268 total = time.time() - context._query_start_time
270 total = time.time() - context._query_start_time
269 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
271 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
270
272
271 event.listen(engine, "before_cursor_execute",
273 event.listen(engine, "before_cursor_execute",
272 before_cursor_execute)
274 before_cursor_execute)
273 event.listen(engine, "after_cursor_execute",
275 event.listen(engine, "after_cursor_execute",
274 after_cursor_execute)
276 after_cursor_execute)
275
277
276 return engine
278 return engine
277
279
278
280
279 def age(curdate):
281 def age(curdate):
280 """
282 """
281 turns a datetime into an age string.
283 turns a datetime into an age string.
282
284
283 :param curdate: datetime object
285 :param curdate: datetime object
284 :rtype: unicode
286 :rtype: unicode
285 :returns: unicode words describing age
287 :returns: unicode words describing age
286 """
288 """
287
289
288 from datetime import datetime
290 from datetime import datetime
289 from webhelpers.date import time_ago_in_words
291 from webhelpers.date import time_ago_in_words
290
292
291 _ = lambda s:s
293 _ = lambda s:s
292
294
293 if not curdate:
295 if not curdate:
294 return ''
296 return ''
295
297
296 agescales = [(_(u"year"), 3600 * 24 * 365),
298 agescales = [(_(u"year"), 3600 * 24 * 365),
297 (_(u"month"), 3600 * 24 * 30),
299 (_(u"month"), 3600 * 24 * 30),
298 (_(u"day"), 3600 * 24),
300 (_(u"day"), 3600 * 24),
299 (_(u"hour"), 3600),
301 (_(u"hour"), 3600),
300 (_(u"minute"), 60),
302 (_(u"minute"), 60),
301 (_(u"second"), 1), ]
303 (_(u"second"), 1), ]
302
304
303 age = datetime.now() - curdate
305 age = datetime.now() - curdate
304 age_seconds = (age.days * agescales[2][1]) + age.seconds
306 age_seconds = (age.days * agescales[2][1]) + age.seconds
305 pos = 1
307 pos = 1
306 for scale in agescales:
308 for scale in agescales:
307 if scale[1] <= age_seconds:
309 if scale[1] <= age_seconds:
308 if pos == 6:pos = 5
310 if pos == 6:pos = 5
309 return '%s %s' % (time_ago_in_words(curdate,
311 return '%s %s' % (time_ago_in_words(curdate,
310 agescales[pos][0]), _('ago'))
312 agescales[pos][0]), _('ago'))
311 pos += 1
313 pos += 1
312
314
313 return _(u'just now')
315 return _(u'just now')
314
316
315
317
316 def uri_filter(uri):
318 def uri_filter(uri):
317 """
319 """
318 Removes user:password from given url string
320 Removes user:password from given url string
319
321
320 :param uri:
322 :param uri:
321 :rtype: unicode
323 :rtype: unicode
322 :returns: filtered list of strings
324 :returns: filtered list of strings
323 """
325 """
324 if not uri:
326 if not uri:
325 return ''
327 return ''
326
328
327 proto = ''
329 proto = ''
328
330
329 for pat in ('https://', 'http://'):
331 for pat in ('https://', 'http://'):
330 if uri.startswith(pat):
332 if uri.startswith(pat):
331 uri = uri[len(pat):]
333 uri = uri[len(pat):]
332 proto = pat
334 proto = pat
333 break
335 break
334
336
335 # remove passwords and username
337 # remove passwords and username
336 uri = uri[uri.find('@') + 1:]
338 uri = uri[uri.find('@') + 1:]
337
339
338 # get the port
340 # get the port
339 cred_pos = uri.find(':')
341 cred_pos = uri.find(':')
340 if cred_pos == -1:
342 if cred_pos == -1:
341 host, port = uri, None
343 host, port = uri, None
342 else:
344 else:
343 host, port = uri[:cred_pos], uri[cred_pos + 1:]
345 host, port = uri[:cred_pos], uri[cred_pos + 1:]
344
346
345 return filter(None, [proto, host, port])
347 return filter(None, [proto, host, port])
346
348
347
349
348 def credentials_filter(uri):
350 def credentials_filter(uri):
349 """
351 """
350 Returns a url with removed credentials
352 Returns a url with removed credentials
351
353
352 :param uri:
354 :param uri:
353 """
355 """
354
356
355 uri = uri_filter(uri)
357 uri = uri_filter(uri)
356 #check if we have port
358 #check if we have port
357 if len(uri) > 2 and uri[2]:
359 if len(uri) > 2 and uri[2]:
358 uri[2] = ':' + uri[2]
360 uri[2] = ':' + uri[2]
359
361
360 return ''.join(uri)
362 return ''.join(uri)
361
363
362 def get_changeset_safe(repo, rev):
364 def get_changeset_safe(repo, rev):
363 """
365 """
364 Safe version of get_changeset if this changeset doesn't exists for a
366 Safe version of get_changeset if this changeset doesn't exists for a
365 repo it returns a Dummy one instead
367 repo it returns a Dummy one instead
366
368
367 :param repo:
369 :param repo:
368 :param rev:
370 :param rev:
369 """
371 """
370 from vcs.backends.base import BaseRepository
372 from vcs.backends.base import BaseRepository
371 from vcs.exceptions import RepositoryError
373 from vcs.exceptions import RepositoryError
372 if not isinstance(repo, BaseRepository):
374 if not isinstance(repo, BaseRepository):
373 raise Exception('You must pass an Repository '
375 raise Exception('You must pass an Repository '
374 'object as first argument got %s', type(repo))
376 'object as first argument got %s', type(repo))
375
377
376 try:
378 try:
377 cs = repo.get_changeset(rev)
379 cs = repo.get_changeset(rev)
378 except RepositoryError:
380 except RepositoryError:
379 from rhodecode.lib.utils import EmptyChangeset
381 from rhodecode.lib.utils import EmptyChangeset
380 cs = EmptyChangeset(requested_revision=rev)
382 cs = EmptyChangeset(requested_revision=rev)
381 return cs
383 return cs
384
385
386 def get_current_revision():
387 """
388 Returns tuple of (number, id) from repository containing this package
389 or None if repository could not be found.
390 """
391
392 try:
393 from vcs import get_repo
394 from vcs.utils.helpers import get_scm
395 from vcs.exceptions import RepositoryError, VCSError
396 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
397 scm = get_scm(repopath)[0]
398 repo = get_repo(path=repopath, alias=scm)
399 tip = repo.get_changeset()
400 return (tip.revision, tip.short_id)
401 except (ImportError, RepositoryError, VCSError), err:
402 print ("Cannot retrieve rhodecode's revision. Original error "
403 "was: %s" % err)
404 return None
@@ -1,614 +1,594 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import paste
30 import paste
31 import beaker
31 import beaker
32 from os.path import dirname as dn, join as jn
32 from os.path import dirname as dn, join as jn
33
33
34 from paste.script.command import Command, BadCommand
34 from paste.script.command import Command, BadCommand
35
35
36 from mercurial import ui, config
36 from mercurial import ui, config
37
37
38 from webhelpers.text import collapse, remove_formatting, strip_tags
38 from webhelpers.text import collapse, remove_formatting, strip_tags
39
39
40 from vcs import get_backend
40 from vcs import get_backend
41 from vcs.backends.base import BaseChangeset
41 from vcs.backends.base import BaseChangeset
42 from vcs.utils.lazy import LazyProperty
42 from vcs.utils.lazy import LazyProperty
43 from vcs.utils.helpers import get_scm
43 from vcs.utils.helpers import get_scm
44 from vcs.exceptions import VCSError
44 from vcs.exceptions import VCSError
45
45
46 from rhodecode.model import meta
46 from rhodecode.model import meta
47 from rhodecode.model.caching_query import FromCache
47 from rhodecode.model.caching_query import FromCache
48 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group, \
48 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group, \
49 RhodeCodeSettings
49 RhodeCodeSettings
50 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 def recursive_replace(str, replace=' '):
55 def recursive_replace(str, replace=' '):
56 """Recursive replace of given sign to just one instance
56 """Recursive replace of given sign to just one instance
57
57
58 :param str: given string
58 :param str: given string
59 :param replace: char to find and replace multiple instances
59 :param replace: char to find and replace multiple instances
60
60
61 Examples::
61 Examples::
62 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
62 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
63 'Mighty-Mighty-Bo-sstones'
63 'Mighty-Mighty-Bo-sstones'
64 """
64 """
65
65
66 if str.find(replace * 2) == -1:
66 if str.find(replace * 2) == -1:
67 return str
67 return str
68 else:
68 else:
69 str = str.replace(replace * 2, replace)
69 str = str.replace(replace * 2, replace)
70 return recursive_replace(str, replace)
70 return recursive_replace(str, replace)
71
71
72
72
73 def repo_name_slug(value):
73 def repo_name_slug(value):
74 """Return slug of name of repository
74 """Return slug of name of repository
75 This function is called on each creation/modification
75 This function is called on each creation/modification
76 of repository to prevent bad names in repo
76 of repository to prevent bad names in repo
77 """
77 """
78
78
79 slug = remove_formatting(value)
79 slug = remove_formatting(value)
80 slug = strip_tags(slug)
80 slug = strip_tags(slug)
81
81
82 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
82 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
83 slug = slug.replace(c, '-')
83 slug = slug.replace(c, '-')
84 slug = recursive_replace(slug, '-')
84 slug = recursive_replace(slug, '-')
85 slug = collapse(slug, '-')
85 slug = collapse(slug, '-')
86 return slug
86 return slug
87
87
88
88
89 def get_repo_slug(request):
89 def get_repo_slug(request):
90 return request.environ['pylons.routes_dict'].get('repo_name')
90 return request.environ['pylons.routes_dict'].get('repo_name')
91
91
92
92
93 def action_logger(user, action, repo, ipaddr='', sa=None):
93 def action_logger(user, action, repo, ipaddr='', sa=None):
94 """
94 """
95 Action logger for various actions made by users
95 Action logger for various actions made by users
96
96
97 :param user: user that made this action, can be a unique username string or
97 :param user: user that made this action, can be a unique username string or
98 object containing user_id attribute
98 object containing user_id attribute
99 :param action: action to log, should be on of predefined unique actions for
99 :param action: action to log, should be on of predefined unique actions for
100 easy translations
100 easy translations
101 :param repo: string name of repository or object containing repo_id,
101 :param repo: string name of repository or object containing repo_id,
102 that action was made on
102 that action was made on
103 :param ipaddr: optional ip address from what the action was made
103 :param ipaddr: optional ip address from what the action was made
104 :param sa: optional sqlalchemy session
104 :param sa: optional sqlalchemy session
105
105
106 """
106 """
107
107
108 if not sa:
108 if not sa:
109 sa = meta.Session()
109 sa = meta.Session()
110
110
111 try:
111 try:
112 if hasattr(user, 'user_id'):
112 if hasattr(user, 'user_id'):
113 user_obj = user
113 user_obj = user
114 elif isinstance(user, basestring):
114 elif isinstance(user, basestring):
115 user_obj = User.get_by_username(user)
115 user_obj = User.get_by_username(user)
116 else:
116 else:
117 raise Exception('You have to provide user object or username')
117 raise Exception('You have to provide user object or username')
118
118
119 rm = RepoModel()
119 rm = RepoModel()
120 if hasattr(repo, 'repo_id'):
120 if hasattr(repo, 'repo_id'):
121 repo_obj = rm.get(repo.repo_id, cache=False)
121 repo_obj = rm.get(repo.repo_id, cache=False)
122 repo_name = repo_obj.repo_name
122 repo_name = repo_obj.repo_name
123 elif isinstance(repo, basestring):
123 elif isinstance(repo, basestring):
124 repo_name = repo.lstrip('/')
124 repo_name = repo.lstrip('/')
125 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
125 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
126 else:
126 else:
127 raise Exception('You have to provide repository to action logger')
127 raise Exception('You have to provide repository to action logger')
128
128
129 user_log = UserLog()
129 user_log = UserLog()
130 user_log.user_id = user_obj.user_id
130 user_log.user_id = user_obj.user_id
131 user_log.action = action
131 user_log.action = action
132
132
133 user_log.repository_id = repo_obj.repo_id
133 user_log.repository_id = repo_obj.repo_id
134 user_log.repository_name = repo_name
134 user_log.repository_name = repo_name
135
135
136 user_log.action_date = datetime.datetime.now()
136 user_log.action_date = datetime.datetime.now()
137 user_log.user_ip = ipaddr
137 user_log.user_ip = ipaddr
138 sa.add(user_log)
138 sa.add(user_log)
139 sa.commit()
139 sa.commit()
140
140
141 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
141 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
142 except:
142 except:
143 log.error(traceback.format_exc())
143 log.error(traceback.format_exc())
144 sa.rollback()
144 sa.rollback()
145
145
146
146
147 def get_repos(path, recursive=False):
147 def get_repos(path, recursive=False):
148 """
148 """
149 Scans given path for repos and return (name,(type,path)) tuple
149 Scans given path for repos and return (name,(type,path)) tuple
150
150
151 :param path: path to scann for repositories
151 :param path: path to scann for repositories
152 :param recursive: recursive search and return names with subdirs in front
152 :param recursive: recursive search and return names with subdirs in front
153 """
153 """
154 from vcs.utils.helpers import get_scm
154 from vcs.utils.helpers import get_scm
155 from vcs.exceptions import VCSError
155 from vcs.exceptions import VCSError
156
156
157 if path.endswith(os.sep):
157 if path.endswith(os.sep):
158 #remove ending slash for better results
158 #remove ending slash for better results
159 path = path[:-1]
159 path = path[:-1]
160
160
161 def _get_repos(p):
161 def _get_repos(p):
162 if not os.access(p, os.W_OK):
162 if not os.access(p, os.W_OK):
163 return
163 return
164 for dirpath in os.listdir(p):
164 for dirpath in os.listdir(p):
165 if os.path.isfile(os.path.join(p, dirpath)):
165 if os.path.isfile(os.path.join(p, dirpath)):
166 continue
166 continue
167 cur_path = os.path.join(p, dirpath)
167 cur_path = os.path.join(p, dirpath)
168 try:
168 try:
169 scm_info = get_scm(cur_path)
169 scm_info = get_scm(cur_path)
170 yield scm_info[1].split(path)[-1].lstrip(os.sep), scm_info
170 yield scm_info[1].split(path)[-1].lstrip(os.sep), scm_info
171 except VCSError:
171 except VCSError:
172 if not recursive:
172 if not recursive:
173 continue
173 continue
174 #check if this dir containts other repos for recursive scan
174 #check if this dir containts other repos for recursive scan
175 rec_path = os.path.join(p, dirpath)
175 rec_path = os.path.join(p, dirpath)
176 if os.path.isdir(rec_path):
176 if os.path.isdir(rec_path):
177 for inner_scm in _get_repos(rec_path):
177 for inner_scm in _get_repos(rec_path):
178 yield inner_scm
178 yield inner_scm
179
179
180 return _get_repos(path)
180 return _get_repos(path)
181
181
182
182
183 def is_valid_repo(repo_name, base_path):
183 def is_valid_repo(repo_name, base_path):
184 """
184 """
185 Returns True if given path is a valid repository False otherwise
185 Returns True if given path is a valid repository False otherwise
186 :param repo_name:
186 :param repo_name:
187 :param base_path:
187 :param base_path:
188
188
189 :return True: if given path is a valid repository
189 :return True: if given path is a valid repository
190 """
190 """
191 full_path = os.path.join(base_path, repo_name)
191 full_path = os.path.join(base_path, repo_name)
192
192
193 try:
193 try:
194 get_scm(full_path)
194 get_scm(full_path)
195 return True
195 return True
196 except VCSError:
196 except VCSError:
197 return False
197 return False
198
198
199 def is_valid_repos_group(repos_group_name, base_path):
199 def is_valid_repos_group(repos_group_name, base_path):
200 """
200 """
201 Returns True if given path is a repos group False otherwise
201 Returns True if given path is a repos group False otherwise
202
202
203 :param repo_name:
203 :param repo_name:
204 :param base_path:
204 :param base_path:
205 """
205 """
206 full_path = os.path.join(base_path, repos_group_name)
206 full_path = os.path.join(base_path, repos_group_name)
207
207
208 # check if it's not a repo
208 # check if it's not a repo
209 if is_valid_repo(repos_group_name, base_path):
209 if is_valid_repo(repos_group_name, base_path):
210 return False
210 return False
211
211
212 # check if it's a valid path
212 # check if it's a valid path
213 if os.path.isdir(full_path):
213 if os.path.isdir(full_path):
214 return True
214 return True
215
215
216 return False
216 return False
217
217
218 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
218 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
219 while True:
219 while True:
220 ok = raw_input(prompt)
220 ok = raw_input(prompt)
221 if ok in ('y', 'ye', 'yes'):
221 if ok in ('y', 'ye', 'yes'):
222 return True
222 return True
223 if ok in ('n', 'no', 'nop', 'nope'):
223 if ok in ('n', 'no', 'nop', 'nope'):
224 return False
224 return False
225 retries = retries - 1
225 retries = retries - 1
226 if retries < 0:
226 if retries < 0:
227 raise IOError
227 raise IOError
228 print complaint
228 print complaint
229
229
230 #propagated from mercurial documentation
230 #propagated from mercurial documentation
231 ui_sections = ['alias', 'auth',
231 ui_sections = ['alias', 'auth',
232 'decode/encode', 'defaults',
232 'decode/encode', 'defaults',
233 'diff', 'email',
233 'diff', 'email',
234 'extensions', 'format',
234 'extensions', 'format',
235 'merge-patterns', 'merge-tools',
235 'merge-patterns', 'merge-tools',
236 'hooks', 'http_proxy',
236 'hooks', 'http_proxy',
237 'smtp', 'patch',
237 'smtp', 'patch',
238 'paths', 'profiling',
238 'paths', 'profiling',
239 'server', 'trusted',
239 'server', 'trusted',
240 'ui', 'web', ]
240 'ui', 'web', ]
241
241
242
242
243 def make_ui(read_from='file', path=None, checkpaths=True):
243 def make_ui(read_from='file', path=None, checkpaths=True):
244 """A function that will read python rc files or database
244 """A function that will read python rc files or database
245 and make an mercurial ui object from read options
245 and make an mercurial ui object from read options
246
246
247 :param path: path to mercurial config file
247 :param path: path to mercurial config file
248 :param checkpaths: check the path
248 :param checkpaths: check the path
249 :param read_from: read from 'file' or 'db'
249 :param read_from: read from 'file' or 'db'
250 """
250 """
251
251
252 baseui = ui.ui()
252 baseui = ui.ui()
253
253
254 #clean the baseui object
254 #clean the baseui object
255 baseui._ocfg = config.config()
255 baseui._ocfg = config.config()
256 baseui._ucfg = config.config()
256 baseui._ucfg = config.config()
257 baseui._tcfg = config.config()
257 baseui._tcfg = config.config()
258
258
259 if read_from == 'file':
259 if read_from == 'file':
260 if not os.path.isfile(path):
260 if not os.path.isfile(path):
261 log.warning('Unable to read config file %s' % path)
261 log.warning('Unable to read config file %s' % path)
262 return False
262 return False
263 log.debug('reading hgrc from %s', path)
263 log.debug('reading hgrc from %s', path)
264 cfg = config.config()
264 cfg = config.config()
265 cfg.read(path)
265 cfg.read(path)
266 for section in ui_sections:
266 for section in ui_sections:
267 for k, v in cfg.items(section):
267 for k, v in cfg.items(section):
268 log.debug('settings ui from file[%s]%s:%s', section, k, v)
268 log.debug('settings ui from file[%s]%s:%s', section, k, v)
269 baseui.setconfig(section, k, v)
269 baseui.setconfig(section, k, v)
270
270
271 elif read_from == 'db':
271 elif read_from == 'db':
272 sa = meta.Session()
272 sa = meta.Session()
273 ret = sa.query(RhodeCodeUi)\
273 ret = sa.query(RhodeCodeUi)\
274 .options(FromCache("sql_cache_short",
274 .options(FromCache("sql_cache_short",
275 "get_hg_ui_settings")).all()
275 "get_hg_ui_settings")).all()
276
276
277 hg_ui = ret
277 hg_ui = ret
278 for ui_ in hg_ui:
278 for ui_ in hg_ui:
279 if ui_.ui_active:
279 if ui_.ui_active:
280 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
280 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
281 ui_.ui_key, ui_.ui_value)
281 ui_.ui_key, ui_.ui_value)
282 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
282 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
283
283
284 meta.Session.remove()
284 meta.Session.remove()
285 return baseui
285 return baseui
286
286
287
287
288 def set_rhodecode_config(config):
288 def set_rhodecode_config(config):
289 """Updates pylons config with new settings from database
289 """Updates pylons config with new settings from database
290
290
291 :param config:
291 :param config:
292 """
292 """
293 hgsettings = RhodeCodeSettings.get_app_settings()
293 hgsettings = RhodeCodeSettings.get_app_settings()
294
294
295 for k, v in hgsettings.items():
295 for k, v in hgsettings.items():
296 config[k] = v
296 config[k] = v
297
297
298
298
299 def invalidate_cache(cache_key, *args):
299 def invalidate_cache(cache_key, *args):
300 """Puts cache invalidation task into db for
300 """Puts cache invalidation task into db for
301 further global cache invalidation
301 further global cache invalidation
302 """
302 """
303
303
304 from rhodecode.model.scm import ScmModel
304 from rhodecode.model.scm import ScmModel
305
305
306 if cache_key.startswith('get_repo_cached_'):
306 if cache_key.startswith('get_repo_cached_'):
307 name = cache_key.split('get_repo_cached_')[-1]
307 name = cache_key.split('get_repo_cached_')[-1]
308 ScmModel().mark_for_invalidation(name)
308 ScmModel().mark_for_invalidation(name)
309
309
310
310
311 class EmptyChangeset(BaseChangeset):
311 class EmptyChangeset(BaseChangeset):
312 """
312 """
313 An dummy empty changeset. It's possible to pass hash when creating
313 An dummy empty changeset. It's possible to pass hash when creating
314 an EmptyChangeset
314 an EmptyChangeset
315 """
315 """
316
316
317 def __init__(self, cs='0' * 40, repo=None, requested_revision=None, alias=None):
317 def __init__(self, cs='0' * 40, repo=None, requested_revision=None, alias=None):
318 self._empty_cs = cs
318 self._empty_cs = cs
319 self.revision = -1
319 self.revision = -1
320 self.message = ''
320 self.message = ''
321 self.author = ''
321 self.author = ''
322 self.date = ''
322 self.date = ''
323 self.repository = repo
323 self.repository = repo
324 self.requested_revision = requested_revision
324 self.requested_revision = requested_revision
325 self.alias = alias
325 self.alias = alias
326
326
327 @LazyProperty
327 @LazyProperty
328 def raw_id(self):
328 def raw_id(self):
329 """Returns raw string identifying this changeset, useful for web
329 """Returns raw string identifying this changeset, useful for web
330 representation.
330 representation.
331 """
331 """
332
332
333 return self._empty_cs
333 return self._empty_cs
334
334
335 @LazyProperty
335 @LazyProperty
336 def branch(self):
336 def branch(self):
337 return get_backend(self.alias).DEFAULT_BRANCH_NAME
337 return get_backend(self.alias).DEFAULT_BRANCH_NAME
338
338
339 @LazyProperty
339 @LazyProperty
340 def short_id(self):
340 def short_id(self):
341 return self.raw_id[:12]
341 return self.raw_id[:12]
342
342
343 def get_file_changeset(self, path):
343 def get_file_changeset(self, path):
344 return self
344 return self
345
345
346 def get_file_content(self, path):
346 def get_file_content(self, path):
347 return u''
347 return u''
348
348
349 def get_file_size(self, path):
349 def get_file_size(self, path):
350 return 0
350 return 0
351
351
352
352
353 def map_groups(groups):
353 def map_groups(groups):
354 """Checks for groups existence, and creates groups structures.
354 """Checks for groups existence, and creates groups structures.
355 It returns last group in structure
355 It returns last group in structure
356
356
357 :param groups: list of groups structure
357 :param groups: list of groups structure
358 """
358 """
359 sa = meta.Session()
359 sa = meta.Session()
360
360
361 parent = None
361 parent = None
362 group = None
362 group = None
363
363
364 # last element is repo in nested groups structure
364 # last element is repo in nested groups structure
365 groups = groups[:-1]
365 groups = groups[:-1]
366
366
367 for lvl, group_name in enumerate(groups):
367 for lvl, group_name in enumerate(groups):
368 group_name = '/'.join(groups[:lvl] + [group_name])
368 group_name = '/'.join(groups[:lvl] + [group_name])
369 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
369 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
370
370
371 if group is None:
371 if group is None:
372 group = Group(group_name, parent)
372 group = Group(group_name, parent)
373 sa.add(group)
373 sa.add(group)
374 sa.commit()
374 sa.commit()
375 parent = group
375 parent = group
376 return group
376 return group
377
377
378
378
379 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
379 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
380 """maps all repos given in initial_repo_list, non existing repositories
380 """maps all repos given in initial_repo_list, non existing repositories
381 are created, if remove_obsolete is True it also check for db entries
381 are created, if remove_obsolete is True it also check for db entries
382 that are not in initial_repo_list and removes them.
382 that are not in initial_repo_list and removes them.
383
383
384 :param initial_repo_list: list of repositories found by scanning methods
384 :param initial_repo_list: list of repositories found by scanning methods
385 :param remove_obsolete: check for obsolete entries in database
385 :param remove_obsolete: check for obsolete entries in database
386 """
386 """
387
387
388 sa = meta.Session()
388 sa = meta.Session()
389 rm = RepoModel()
389 rm = RepoModel()
390 user = sa.query(User).filter(User.admin == True).first()
390 user = sa.query(User).filter(User.admin == True).first()
391 added = []
391 added = []
392 for name, repo in initial_repo_list.items():
392 for name, repo in initial_repo_list.items():
393 group = map_groups(name.split(os.sep))
393 group = map_groups(name.split(os.sep))
394 if not rm.get_by_repo_name(name, cache=False):
394 if not rm.get_by_repo_name(name, cache=False):
395 log.info('repository %s not found creating default', name)
395 log.info('repository %s not found creating default', name)
396 added.append(name)
396 added.append(name)
397 form_data = {
397 form_data = {
398 'repo_name': name,
398 'repo_name': name,
399 'repo_name_full': name,
399 'repo_name_full': name,
400 'repo_type': repo.alias,
400 'repo_type': repo.alias,
401 'description': repo.description \
401 'description': repo.description \
402 if repo.description != 'unknown' else \
402 if repo.description != 'unknown' else \
403 '%s repository' % name,
403 '%s repository' % name,
404 'private': False,
404 'private': False,
405 'group_id': getattr(group, 'group_id', None)
405 'group_id': getattr(group, 'group_id', None)
406 }
406 }
407 rm.create(form_data, user, just_db=True)
407 rm.create(form_data, user, just_db=True)
408
408
409 removed = []
409 removed = []
410 if remove_obsolete:
410 if remove_obsolete:
411 #remove from database those repositories that are not in the filesystem
411 #remove from database those repositories that are not in the filesystem
412 for repo in sa.query(Repository).all():
412 for repo in sa.query(Repository).all():
413 if repo.repo_name not in initial_repo_list.keys():
413 if repo.repo_name not in initial_repo_list.keys():
414 removed.append(repo.repo_name)
414 removed.append(repo.repo_name)
415 sa.delete(repo)
415 sa.delete(repo)
416 sa.commit()
416 sa.commit()
417
417
418 return added, removed
418 return added, removed
419
419
420 #set cache regions for beaker so celery can utilise it
420 #set cache regions for beaker so celery can utilise it
421 def add_cache(settings):
421 def add_cache(settings):
422 cache_settings = {'regions': None}
422 cache_settings = {'regions': None}
423 for key in settings.keys():
423 for key in settings.keys():
424 for prefix in ['beaker.cache.', 'cache.']:
424 for prefix in ['beaker.cache.', 'cache.']:
425 if key.startswith(prefix):
425 if key.startswith(prefix):
426 name = key.split(prefix)[1].strip()
426 name = key.split(prefix)[1].strip()
427 cache_settings[name] = settings[key].strip()
427 cache_settings[name] = settings[key].strip()
428 if cache_settings['regions']:
428 if cache_settings['regions']:
429 for region in cache_settings['regions'].split(','):
429 for region in cache_settings['regions'].split(','):
430 region = region.strip()
430 region = region.strip()
431 region_settings = {}
431 region_settings = {}
432 for key, value in cache_settings.items():
432 for key, value in cache_settings.items():
433 if key.startswith(region):
433 if key.startswith(region):
434 region_settings[key.split('.')[1]] = value
434 region_settings[key.split('.')[1]] = value
435 region_settings['expire'] = int(region_settings.get('expire',
435 region_settings['expire'] = int(region_settings.get('expire',
436 60))
436 60))
437 region_settings.setdefault('lock_dir',
437 region_settings.setdefault('lock_dir',
438 cache_settings.get('lock_dir'))
438 cache_settings.get('lock_dir'))
439 region_settings.setdefault('data_dir',
439 region_settings.setdefault('data_dir',
440 cache_settings.get('data_dir'))
440 cache_settings.get('data_dir'))
441
441
442 if 'type' not in region_settings:
442 if 'type' not in region_settings:
443 region_settings['type'] = cache_settings.get('type',
443 region_settings['type'] = cache_settings.get('type',
444 'memory')
444 'memory')
445 beaker.cache.cache_regions[region] = region_settings
445 beaker.cache.cache_regions[region] = region_settings
446
446
447
447
448 def get_current_revision():
449 """Returns tuple of (number, id) from repository containing this package
450 or None if repository could not be found.
451 """
452
453 try:
454 from vcs import get_repo
455 from vcs.utils.helpers import get_scm
456 from vcs.exceptions import RepositoryError, VCSError
457 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
458 scm = get_scm(repopath)[0]
459 repo = get_repo(path=repopath, alias=scm)
460 tip = repo.get_changeset()
461 return (tip.revision, tip.short_id)
462 except (ImportError, RepositoryError, VCSError), err:
463 logging.debug("Cannot retrieve rhodecode's revision. Original error "
464 "was: %s" % err)
465 return None
466
467
468 #==============================================================================
448 #==============================================================================
469 # TEST FUNCTIONS AND CREATORS
449 # TEST FUNCTIONS AND CREATORS
470 #==============================================================================
450 #==============================================================================
471 def create_test_index(repo_location, config, full_index):
451 def create_test_index(repo_location, config, full_index):
472 """
452 """
473 Makes default test index
453 Makes default test index
474
454
475 :param config: test config
455 :param config: test config
476 :param full_index:
456 :param full_index:
477 """
457 """
478
458
479 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
459 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
480 from rhodecode.lib.pidlock import DaemonLock, LockHeld
460 from rhodecode.lib.pidlock import DaemonLock, LockHeld
481
461
482 repo_location = repo_location
462 repo_location = repo_location
483
463
484 index_location = os.path.join(config['app_conf']['index_dir'])
464 index_location = os.path.join(config['app_conf']['index_dir'])
485 if not os.path.exists(index_location):
465 if not os.path.exists(index_location):
486 os.makedirs(index_location)
466 os.makedirs(index_location)
487
467
488 try:
468 try:
489 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
469 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
490 WhooshIndexingDaemon(index_location=index_location,
470 WhooshIndexingDaemon(index_location=index_location,
491 repo_location=repo_location)\
471 repo_location=repo_location)\
492 .run(full_index=full_index)
472 .run(full_index=full_index)
493 l.release()
473 l.release()
494 except LockHeld:
474 except LockHeld:
495 pass
475 pass
496
476
497
477
498 def create_test_env(repos_test_path, config):
478 def create_test_env(repos_test_path, config):
499 """Makes a fresh database and
479 """Makes a fresh database and
500 install test repository into tmp dir
480 install test repository into tmp dir
501 """
481 """
502 from rhodecode.lib.db_manage import DbManage
482 from rhodecode.lib.db_manage import DbManage
503 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
483 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
504 HG_FORK, GIT_FORK, TESTS_TMP_PATH
484 HG_FORK, GIT_FORK, TESTS_TMP_PATH
505 import tarfile
485 import tarfile
506 import shutil
486 import shutil
507 from os.path import abspath
487 from os.path import abspath
508
488
509 # PART ONE create db
489 # PART ONE create db
510 dbconf = config['sqlalchemy.db1.url']
490 dbconf = config['sqlalchemy.db1.url']
511 log.debug('making test db %s', dbconf)
491 log.debug('making test db %s', dbconf)
512
492
513 # create test dir if it doesn't exist
493 # create test dir if it doesn't exist
514 if not os.path.isdir(repos_test_path):
494 if not os.path.isdir(repos_test_path):
515 log.debug('Creating testdir %s' % repos_test_path)
495 log.debug('Creating testdir %s' % repos_test_path)
516 os.makedirs(repos_test_path)
496 os.makedirs(repos_test_path)
517
497
518 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
498 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
519 tests=True)
499 tests=True)
520 dbmanage.create_tables(override=True)
500 dbmanage.create_tables(override=True)
521 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
501 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
522 dbmanage.create_default_user()
502 dbmanage.create_default_user()
523 dbmanage.admin_prompt()
503 dbmanage.admin_prompt()
524 dbmanage.create_permissions()
504 dbmanage.create_permissions()
525 dbmanage.populate_default_permissions()
505 dbmanage.populate_default_permissions()
526
506
527 # PART TWO make test repo
507 # PART TWO make test repo
528 log.debug('making test vcs repositories')
508 log.debug('making test vcs repositories')
529
509
530 idx_path = config['app_conf']['index_dir']
510 idx_path = config['app_conf']['index_dir']
531 data_path = config['app_conf']['cache_dir']
511 data_path = config['app_conf']['cache_dir']
532
512
533 #clean index and data
513 #clean index and data
534 if idx_path and os.path.exists(idx_path):
514 if idx_path and os.path.exists(idx_path):
535 log.debug('remove %s' % idx_path)
515 log.debug('remove %s' % idx_path)
536 shutil.rmtree(idx_path)
516 shutil.rmtree(idx_path)
537
517
538 if data_path and os.path.exists(data_path):
518 if data_path and os.path.exists(data_path):
539 log.debug('remove %s' % data_path)
519 log.debug('remove %s' % data_path)
540 shutil.rmtree(data_path)
520 shutil.rmtree(data_path)
541
521
542 #CREATE DEFAULT HG REPOSITORY
522 #CREATE DEFAULT HG REPOSITORY
543 cur_dir = dn(dn(abspath(__file__)))
523 cur_dir = dn(dn(abspath(__file__)))
544 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
524 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
545 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
525 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
546 tar.close()
526 tar.close()
547
527
548
528
549 #==============================================================================
529 #==============================================================================
550 # PASTER COMMANDS
530 # PASTER COMMANDS
551 #==============================================================================
531 #==============================================================================
552 class BasePasterCommand(Command):
532 class BasePasterCommand(Command):
553 """
533 """
554 Abstract Base Class for paster commands.
534 Abstract Base Class for paster commands.
555
535
556 The celery commands are somewhat aggressive about loading
536 The celery commands are somewhat aggressive about loading
557 celery.conf, and since our module sets the `CELERY_LOADER`
537 celery.conf, and since our module sets the `CELERY_LOADER`
558 environment variable to our loader, we have to bootstrap a bit and
538 environment variable to our loader, we have to bootstrap a bit and
559 make sure we've had a chance to load the pylons config off of the
539 make sure we've had a chance to load the pylons config off of the
560 command line, otherwise everything fails.
540 command line, otherwise everything fails.
561 """
541 """
562 min_args = 1
542 min_args = 1
563 min_args_error = "Please provide a paster config file as an argument."
543 min_args_error = "Please provide a paster config file as an argument."
564 takes_config_file = 1
544 takes_config_file = 1
565 requires_config_file = True
545 requires_config_file = True
566
546
567 def notify_msg(self, msg, log=False):
547 def notify_msg(self, msg, log=False):
568 """Make a notification to user, additionally if logger is passed
548 """Make a notification to user, additionally if logger is passed
569 it logs this action using given logger
549 it logs this action using given logger
570
550
571 :param msg: message that will be printed to user
551 :param msg: message that will be printed to user
572 :param log: logging instance, to use to additionally log this message
552 :param log: logging instance, to use to additionally log this message
573
553
574 """
554 """
575 if log and isinstance(log, logging):
555 if log and isinstance(log, logging):
576 log(msg)
556 log(msg)
577
557
578 def run(self, args):
558 def run(self, args):
579 """
559 """
580 Overrides Command.run
560 Overrides Command.run
581
561
582 Checks for a config file argument and loads it.
562 Checks for a config file argument and loads it.
583 """
563 """
584 if len(args) < self.min_args:
564 if len(args) < self.min_args:
585 raise BadCommand(
565 raise BadCommand(
586 self.min_args_error % {'min_args': self.min_args,
566 self.min_args_error % {'min_args': self.min_args,
587 'actual_args': len(args)})
567 'actual_args': len(args)})
588
568
589 # Decrement because we're going to lob off the first argument.
569 # Decrement because we're going to lob off the first argument.
590 # @@ This is hacky
570 # @@ This is hacky
591 self.min_args -= 1
571 self.min_args -= 1
592 self.bootstrap_config(args[0])
572 self.bootstrap_config(args[0])
593 self.update_parser()
573 self.update_parser()
594 return super(BasePasterCommand, self).run(args[1:])
574 return super(BasePasterCommand, self).run(args[1:])
595
575
596 def update_parser(self):
576 def update_parser(self):
597 """
577 """
598 Abstract method. Allows for the class's parser to be updated
578 Abstract method. Allows for the class's parser to be updated
599 before the superclass's `run` method is called. Necessary to
579 before the superclass's `run` method is called. Necessary to
600 allow options/arguments to be passed through to the underlying
580 allow options/arguments to be passed through to the underlying
601 celery command.
581 celery command.
602 """
582 """
603 raise NotImplementedError("Abstract Method.")
583 raise NotImplementedError("Abstract Method.")
604
584
605 def bootstrap_config(self, conf):
585 def bootstrap_config(self, conf):
606 """
586 """
607 Loads the pylons configuration.
587 Loads the pylons configuration.
608 """
588 """
609 from pylons import config as pylonsconfig
589 from pylons import config as pylonsconfig
610
590
611 path_to_ini_file = os.path.realpath(conf)
591 path_to_ini_file = os.path.realpath(conf)
612 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
592 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
613 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
593 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
614
594
General Comments 0
You need to be logged in to leave comments. Login now