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