##// END OF EJS Templates
Version bump,freeze of dependent libs....
marcink -
r639:45e1fdc0 default
parent child Browse files
Show More
@@ -1,35 +1,35 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # RhodeCode, a web based repository management based on pylons
3 # RhodeCode, a web based repository management based on pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 9, 2010
21 Created on April 9, 2010
22 RhodeCode, a web based repository management based on pylons
22 RhodeCode, a web based repository management based on pylons
23 versioning implementation: http://semver.org/
23 versioning implementation: http://semver.org/
24 @author: marcink
24 @author: marcink
25 """
25 """
26
26
27 VERSION = (1, 0, 0, 'rc4')
27 VERSION = (1, 0, 0,)
28
28
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
30
30
31 def get_version():
31 def get_version():
32 """
32 """
33 Returns shorter version (digit parts only) as string.
33 Returns shorter version (digit parts only) as string.
34 """
34 """
35 return '.'.join((str(each) for each in VERSION[:3]))
35 return '.'.join((str(each) for each in VERSION[:3]))
@@ -1,502 +1,507 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Utilities for RhodeCode
3 # Utilities for RhodeCode
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; version 2
7 # as published by the Free Software Foundation; version 2
8 # of the License or (at your opinion) any later version of the license.
8 # of the License or (at your opinion) any later version of the license.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # MA 02110-1301, USA.
18 # MA 02110-1301, USA.
19
19
20 """
20 """
21 Created on April 18, 2010
21 Created on April 18, 2010
22 Utilities for RhodeCode
22 Utilities for RhodeCode
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from beaker.cache import cache_region
25 from beaker.cache import cache_region
26 from mercurial import ui, config, hg
26 from mercurial import ui, config, hg
27 from mercurial.error import RepoError
27 from mercurial.error import RepoError
28 from rhodecode.model import meta
28 from rhodecode.model import meta
29 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, UserLog
29 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, UserLog
30 from vcs.backends.base import BaseChangeset
30 from vcs.backends.base import BaseChangeset
31 from vcs.utils.lazy import LazyProperty
31 from vcs.utils.lazy import LazyProperty
32 import logging
32 import logging
33 import datetime
33 import datetime
34 import os
34 import os
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38
38
39 def get_repo_slug(request):
39 def get_repo_slug(request):
40 return request.environ['pylons.routes_dict'].get('repo_name')
40 return request.environ['pylons.routes_dict'].get('repo_name')
41
41
42 def is_mercurial(environ):
42 def is_mercurial(environ):
43 """
43 """
44 Returns True if request's target is mercurial server - header
44 Returns True if request's target is mercurial server - header
45 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
45 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
46 """
46 """
47 http_accept = environ.get('HTTP_ACCEPT')
47 http_accept = environ.get('HTTP_ACCEPT')
48 if http_accept and http_accept.startswith('application/mercurial'):
48 if http_accept and http_accept.startswith('application/mercurial'):
49 return True
49 return True
50 return False
50 return False
51
51
52 def is_git(environ):
52 def is_git(environ):
53 """
53 """
54 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
54 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
55 then have git client version given.
55 then have git client version given.
56
56
57 :param environ:
57 :param environ:
58 """
58 """
59 http_user_agent = environ.get('HTTP_USER_AGENT')
59 http_user_agent = environ.get('HTTP_USER_AGENT')
60 if http_user_agent.startswith('git'):
60 if http_user_agent.startswith('git'):
61 return True
61 return True
62 return False
62 return False
63
63
64 def action_logger(user, action, repo, ipaddr, sa=None):
64 def action_logger(user, action, repo, ipaddr, sa=None):
65 """
65 """
66 Action logger for various action made by users
66 Action logger for various action made by users
67 """
67 """
68
68
69 if not sa:
69 if not sa:
70 sa = meta.Session
70 sa = meta.Session
71
71
72 try:
72 try:
73 if hasattr(user, 'user_id'):
73 if hasattr(user, 'user_id'):
74 user_id = user.user_id
74 user_id = user.user_id
75 elif isinstance(user, basestring):
75 elif isinstance(user, basestring):
76 user_id = sa.query(User).filter(User.username == user).one()
76 user_id = sa.query(User).filter(User.username == user).one()
77 else:
77 else:
78 raise Exception('You have to provide user object or username')
78 raise Exception('You have to provide user object or username')
79
79
80 repo_name = repo.lstrip('/')
80 repo_name = repo.lstrip('/')
81 user_log = UserLog()
81 user_log = UserLog()
82 user_log.user_id = user_id
82 user_log.user_id = user_id
83 user_log.action = action
83 user_log.action = action
84 user_log.repository_name = repo_name
84 user_log.repository_name = repo_name
85 user_log.repository = sa.query(Repository)\
85 user_log.repository = sa.query(Repository)\
86 .filter(Repository.repo_name == repo_name).one()
86 .filter(Repository.repo_name == repo_name).one()
87 user_log.action_date = datetime.datetime.now()
87 user_log.action_date = datetime.datetime.now()
88 user_log.user_ip = ipaddr
88 user_log.user_ip = ipaddr
89 sa.add(user_log)
89 sa.add(user_log)
90 sa.commit()
90 sa.commit()
91
91
92 log.info('Adding user %s, action %s on %s',
92 log.info('Adding user %s, action %s on %s',
93 user.username, action, repo)
93 user.username, action, repo)
94 except Exception, e:
94 except Exception, e:
95 sa.rollback()
95 sa.rollback()
96 log.error('could not log user action:%s', str(e))
96 log.error('could not log user action:%s', str(e))
97
97
98 def check_repo_dir(paths):
98 def check_repo_dir(paths):
99 repos_path = paths[0][1].split('/')
99 repos_path = paths[0][1].split('/')
100 if repos_path[-1] in ['*', '**']:
100 if repos_path[-1] in ['*', '**']:
101 repos_path = repos_path[:-1]
101 repos_path = repos_path[:-1]
102 if repos_path[0] != '/':
102 if repos_path[0] != '/':
103 repos_path[0] = '/'
103 repos_path[0] = '/'
104 if not os.path.isdir(os.path.join(*repos_path)):
104 if not os.path.isdir(os.path.join(*repos_path)):
105 raise Exception('Not a valid repository in %s' % paths[0][1])
105 raise Exception('Not a valid repository in %s' % paths[0][1])
106
106
107 def check_repo_fast(repo_name, base_path):
107 def check_repo_fast(repo_name, base_path):
108 if os.path.isdir(os.path.join(base_path, repo_name)):return False
108 if os.path.isdir(os.path.join(base_path, repo_name)):return False
109 return True
109 return True
110
110
111 def check_repo(repo_name, base_path, verify=True):
111 def check_repo(repo_name, base_path, verify=True):
112
112
113 repo_path = os.path.join(base_path, repo_name)
113 repo_path = os.path.join(base_path, repo_name)
114
114
115 try:
115 try:
116 if not check_repo_fast(repo_name, base_path):
116 if not check_repo_fast(repo_name, base_path):
117 return False
117 return False
118 r = hg.repository(ui.ui(), repo_path)
118 r = hg.repository(ui.ui(), repo_path)
119 if verify:
119 if verify:
120 hg.verify(r)
120 hg.verify(r)
121 #here we hnow that repo exists it was verified
121 #here we hnow that repo exists it was verified
122 log.info('%s repo is already created', repo_name)
122 log.info('%s repo is already created', repo_name)
123 return False
123 return False
124 except RepoError:
124 except RepoError:
125 #it means that there is no valid repo there...
125 #it means that there is no valid repo there...
126 log.info('%s repo is free for creation', repo_name)
126 log.info('%s repo is free for creation', repo_name)
127 return True
127 return True
128
128
129 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
129 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
130 while True:
130 while True:
131 ok = raw_input(prompt)
131 ok = raw_input(prompt)
132 if ok in ('y', 'ye', 'yes'): return True
132 if ok in ('y', 'ye', 'yes'): return True
133 if ok in ('n', 'no', 'nop', 'nope'): return False
133 if ok in ('n', 'no', 'nop', 'nope'): return False
134 retries = retries - 1
134 retries = retries - 1
135 if retries < 0: raise IOError
135 if retries < 0: raise IOError
136 print complaint
136 print complaint
137
137
138 @cache_region('super_short_term', 'cached_hg_ui')
138 @cache_region('super_short_term', 'cached_hg_ui')
139 def get_hg_ui_cached():
139 def get_hg_ui_cached():
140 try:
140 try:
141 sa = meta.Session
141 sa = meta.Session
142 ret = sa.query(RhodeCodeUi).all()
142 ret = sa.query(RhodeCodeUi).all()
143 finally:
143 finally:
144 meta.Session.remove()
144 meta.Session.remove()
145 return ret
145 return ret
146
146
147
147
148 def get_hg_settings():
148 def get_hg_settings():
149 try:
149 try:
150 sa = meta.Session
150 sa = meta.Session
151 ret = sa.query(RhodeCodeSettings).all()
151 ret = sa.query(RhodeCodeSettings).all()
152 finally:
152 finally:
153 meta.Session.remove()
153 meta.Session.remove()
154
154
155 if not ret:
155 if not ret:
156 raise Exception('Could not get application settings !')
156 raise Exception('Could not get application settings !')
157 settings = {}
157 settings = {}
158 for each in ret:
158 for each in ret:
159 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
159 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
160
160
161 return settings
161 return settings
162
162
163 def get_hg_ui_settings():
163 def get_hg_ui_settings():
164 try:
164 try:
165 sa = meta.Session
165 sa = meta.Session
166 ret = sa.query(RhodeCodeUi).all()
166 ret = sa.query(RhodeCodeUi).all()
167 finally:
167 finally:
168 meta.Session.remove()
168 meta.Session.remove()
169
169
170 if not ret:
170 if not ret:
171 raise Exception('Could not get application ui settings !')
171 raise Exception('Could not get application ui settings !')
172 settings = {}
172 settings = {}
173 for each in ret:
173 for each in ret:
174 k = each.ui_key
174 k = each.ui_key
175 v = each.ui_value
175 v = each.ui_value
176 if k == '/':
176 if k == '/':
177 k = 'root_path'
177 k = 'root_path'
178
178
179 if k.find('.') != -1:
179 if k.find('.') != -1:
180 k = k.replace('.', '_')
180 k = k.replace('.', '_')
181
181
182 if each.ui_section == 'hooks':
182 if each.ui_section == 'hooks':
183 v = each.ui_active
183 v = each.ui_active
184
184
185 settings[each.ui_section + '_' + k] = v
185 settings[each.ui_section + '_' + k] = v
186
186
187 return settings
187 return settings
188
188
189 #propagated from mercurial documentation
189 #propagated from mercurial documentation
190 ui_sections = ['alias', 'auth',
190 ui_sections = ['alias', 'auth',
191 'decode/encode', 'defaults',
191 'decode/encode', 'defaults',
192 'diff', 'email',
192 'diff', 'email',
193 'extensions', 'format',
193 'extensions', 'format',
194 'merge-patterns', 'merge-tools',
194 'merge-patterns', 'merge-tools',
195 'hooks', 'http_proxy',
195 'hooks', 'http_proxy',
196 'smtp', 'patch',
196 'smtp', 'patch',
197 'paths', 'profiling',
197 'paths', 'profiling',
198 'server', 'trusted',
198 'server', 'trusted',
199 'ui', 'web', ]
199 'ui', 'web', ]
200
200
201 def make_ui(read_from='file', path=None, checkpaths=True):
201 def make_ui(read_from='file', path=None, checkpaths=True):
202 """
202 """
203 A function that will read python rc files or database
203 A function that will read python rc files or database
204 and make an mercurial ui object from read options
204 and make an mercurial ui object from read options
205
205
206 :param path: path to mercurial config file
206 :param path: path to mercurial config file
207 :param checkpaths: check the path
207 :param checkpaths: check the path
208 :param read_from: read from 'file' or 'db'
208 :param read_from: read from 'file' or 'db'
209 """
209 """
210
210
211 baseui = ui.ui()
211 baseui = ui.ui()
212
212
213 if read_from == 'file':
213 if read_from == 'file':
214 if not os.path.isfile(path):
214 if not os.path.isfile(path):
215 log.warning('Unable to read config file %s' % path)
215 log.warning('Unable to read config file %s' % path)
216 return False
216 return False
217 log.debug('reading hgrc from %s', path)
217 log.debug('reading hgrc from %s', path)
218 cfg = config.config()
218 cfg = config.config()
219 cfg.read(path)
219 cfg.read(path)
220 for section in ui_sections:
220 for section in ui_sections:
221 for k, v in cfg.items(section):
221 for k, v in cfg.items(section):
222 baseui.setconfig(section, k, v)
222 baseui.setconfig(section, k, v)
223 log.debug('settings ui from file[%s]%s:%s', section, k, v)
223 log.debug('settings ui from file[%s]%s:%s', section, k, v)
224 if checkpaths:check_repo_dir(cfg.items('paths'))
224 if checkpaths:check_repo_dir(cfg.items('paths'))
225
225
226
226
227 elif read_from == 'db':
227 elif read_from == 'db':
228 hg_ui = get_hg_ui_cached()
228 hg_ui = get_hg_ui_cached()
229 for ui_ in hg_ui:
229 for ui_ in hg_ui:
230 if ui_.ui_active:
230 if ui_.ui_active:
231 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
231 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
232 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
232 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
233
233
234
234
235 return baseui
235 return baseui
236
236
237
237
238 def set_rhodecode_config(config):
238 def set_rhodecode_config(config):
239 hgsettings = get_hg_settings()
239 hgsettings = get_hg_settings()
240
240
241 for k, v in hgsettings.items():
241 for k, v in hgsettings.items():
242 config[k] = v
242 config[k] = v
243
243
244 def invalidate_cache(name, *args):
244 def invalidate_cache(name, *args):
245 """Invalidates given name cache"""
245 """Invalidates given name cache"""
246
246
247 from beaker.cache import region_invalidate
247 from beaker.cache import region_invalidate
248 log.info('INVALIDATING CACHE FOR %s', name)
248 log.info('INVALIDATING CACHE FOR %s', name)
249
249
250 """propagate our arguments to make sure invalidation works. First
250 """propagate our arguments to make sure invalidation works. First
251 argument has to be the name of cached func name give to cache decorator
251 argument has to be the name of cached func name give to cache decorator
252 without that the invalidation would not work"""
252 without that the invalidation would not work"""
253 tmp = [name]
253 tmp = [name]
254 tmp.extend(args)
254 tmp.extend(args)
255 args = tuple(tmp)
255 args = tuple(tmp)
256
256
257 if name == 'cached_repo_list':
257 if name == 'cached_repo_list':
258 from rhodecode.model.hg_model import _get_repos_cached
258 from rhodecode.model.hg_model import _get_repos_cached
259 region_invalidate(_get_repos_cached, None, *args)
259 region_invalidate(_get_repos_cached, None, *args)
260
260
261 if name == 'full_changelog':
261 if name == 'full_changelog':
262 from rhodecode.model.hg_model import _full_changelog_cached
262 from rhodecode.model.hg_model import _full_changelog_cached
263 region_invalidate(_full_changelog_cached, None, *args)
263 region_invalidate(_full_changelog_cached, None, *args)
264
264
265 class EmptyChangeset(BaseChangeset):
265 class EmptyChangeset(BaseChangeset):
266 """
266 """
267 An dummy empty changeset.
267 An dummy empty changeset.
268 """
268 """
269
269
270 revision = -1
270 revision = -1
271 message = ''
271 message = ''
272 author = ''
272 author = ''
273 date = ''
273 date = ''
274 @LazyProperty
274 @LazyProperty
275 def raw_id(self):
275 def raw_id(self):
276 """
276 """
277 Returns raw string identifing this changeset, useful for web
277 Returns raw string identifing this changeset, useful for web
278 representation.
278 representation.
279 """
279 """
280 return '0' * 40
280 return '0' * 40
281
281
282 @LazyProperty
282 @LazyProperty
283 def short_id(self):
283 def short_id(self):
284 return self.raw_id[:12]
284 return self.raw_id[:12]
285
285
286 def get_file_changeset(self, path):
286 def get_file_changeset(self, path):
287 return self
287 return self
288
288
289 def get_file_content(self, path):
289 def get_file_content(self, path):
290 return u''
290 return u''
291
291
292 def get_file_size(self, path):
292 def get_file_size(self, path):
293 return 0
293 return 0
294
294
295 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
295 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
296 """
296 """
297 maps all found repositories into db
297 maps all found repositories into db
298 """
298 """
299 from rhodecode.model.repo_model import RepoModel
299 from rhodecode.model.repo_model import RepoModel
300
300
301 sa = meta.Session
301 sa = meta.Session
302 user = sa.query(User).filter(User.admin == True).first()
302 user = sa.query(User).filter(User.admin == True).first()
303
303
304 rm = RepoModel()
304 rm = RepoModel()
305
305
306 for name, repo in initial_repo_list.items():
306 for name, repo in initial_repo_list.items():
307 if not sa.query(Repository).filter(Repository.repo_name == name).scalar():
307 if not sa.query(Repository).filter(Repository.repo_name == name).scalar():
308 log.info('repository %s not found creating default', name)
308 log.info('repository %s not found creating default', name)
309
309
310 form_data = {
310 form_data = {
311 'repo_name':name,
311 'repo_name':name,
312 'description':repo.description if repo.description != 'unknown' else \
312 'description':repo.description if repo.description != 'unknown' else \
313 'auto description for %s' % name,
313 'auto description for %s' % name,
314 'private':False
314 'private':False
315 }
315 }
316 rm.create(form_data, user, just_db=True)
316 rm.create(form_data, user, just_db=True)
317
317
318
318
319 if remove_obsolete:
319 if remove_obsolete:
320 #remove from database those repositories that are not in the filesystem
320 #remove from database those repositories that are not in the filesystem
321 for repo in sa.query(Repository).all():
321 for repo in sa.query(Repository).all():
322 if repo.repo_name not in initial_repo_list.keys():
322 if repo.repo_name not in initial_repo_list.keys():
323 sa.delete(repo)
323 sa.delete(repo)
324 sa.commit()
324 sa.commit()
325
325
326
326
327 meta.Session.remove()
327 meta.Session.remove()
328
328
329 from UserDict import DictMixin
329 from UserDict import DictMixin
330
330
331 class OrderedDict(dict, DictMixin):
331 class OrderedDict(dict, DictMixin):
332
332
333 def __init__(self, *args, **kwds):
333 def __init__(self, *args, **kwds):
334 if len(args) > 1:
334 if len(args) > 1:
335 raise TypeError('expected at most 1 arguments, got %d' % len(args))
335 raise TypeError('expected at most 1 arguments, got %d' % len(args))
336 try:
336 try:
337 self.__end
337 self.__end
338 except AttributeError:
338 except AttributeError:
339 self.clear()
339 self.clear()
340 self.update(*args, **kwds)
340 self.update(*args, **kwds)
341
341
342 def clear(self):
342 def clear(self):
343 self.__end = end = []
343 self.__end = end = []
344 end += [None, end, end] # sentinel node for doubly linked list
344 end += [None, end, end] # sentinel node for doubly linked list
345 self.__map = {} # key --> [key, prev, next]
345 self.__map = {} # key --> [key, prev, next]
346 dict.clear(self)
346 dict.clear(self)
347
347
348 def __setitem__(self, key, value):
348 def __setitem__(self, key, value):
349 if key not in self:
349 if key not in self:
350 end = self.__end
350 end = self.__end
351 curr = end[1]
351 curr = end[1]
352 curr[2] = end[1] = self.__map[key] = [key, curr, end]
352 curr[2] = end[1] = self.__map[key] = [key, curr, end]
353 dict.__setitem__(self, key, value)
353 dict.__setitem__(self, key, value)
354
354
355 def __delitem__(self, key):
355 def __delitem__(self, key):
356 dict.__delitem__(self, key)
356 dict.__delitem__(self, key)
357 key, prev, next = self.__map.pop(key)
357 key, prev, next = self.__map.pop(key)
358 prev[2] = next
358 prev[2] = next
359 next[1] = prev
359 next[1] = prev
360
360
361 def __iter__(self):
361 def __iter__(self):
362 end = self.__end
362 end = self.__end
363 curr = end[2]
363 curr = end[2]
364 while curr is not end:
364 while curr is not end:
365 yield curr[0]
365 yield curr[0]
366 curr = curr[2]
366 curr = curr[2]
367
367
368 def __reversed__(self):
368 def __reversed__(self):
369 end = self.__end
369 end = self.__end
370 curr = end[1]
370 curr = end[1]
371 while curr is not end:
371 while curr is not end:
372 yield curr[0]
372 yield curr[0]
373 curr = curr[1]
373 curr = curr[1]
374
374
375 def popitem(self, last=True):
375 def popitem(self, last=True):
376 if not self:
376 if not self:
377 raise KeyError('dictionary is empty')
377 raise KeyError('dictionary is empty')
378 if last:
378 if last:
379 key = reversed(self).next()
379 key = reversed(self).next()
380 else:
380 else:
381 key = iter(self).next()
381 key = iter(self).next()
382 value = self.pop(key)
382 value = self.pop(key)
383 return key, value
383 return key, value
384
384
385 def __reduce__(self):
385 def __reduce__(self):
386 items = [[k, self[k]] for k in self]
386 items = [[k, self[k]] for k in self]
387 tmp = self.__map, self.__end
387 tmp = self.__map, self.__end
388 del self.__map, self.__end
388 del self.__map, self.__end
389 inst_dict = vars(self).copy()
389 inst_dict = vars(self).copy()
390 self.__map, self.__end = tmp
390 self.__map, self.__end = tmp
391 if inst_dict:
391 if inst_dict:
392 return (self.__class__, (items,), inst_dict)
392 return (self.__class__, (items,), inst_dict)
393 return self.__class__, (items,)
393 return self.__class__, (items,)
394
394
395 def keys(self):
395 def keys(self):
396 return list(self)
396 return list(self)
397
397
398 setdefault = DictMixin.setdefault
398 setdefault = DictMixin.setdefault
399 update = DictMixin.update
399 update = DictMixin.update
400 pop = DictMixin.pop
400 pop = DictMixin.pop
401 values = DictMixin.values
401 values = DictMixin.values
402 items = DictMixin.items
402 items = DictMixin.items
403 iterkeys = DictMixin.iterkeys
403 iterkeys = DictMixin.iterkeys
404 itervalues = DictMixin.itervalues
404 itervalues = DictMixin.itervalues
405 iteritems = DictMixin.iteritems
405 iteritems = DictMixin.iteritems
406
406
407 def __repr__(self):
407 def __repr__(self):
408 if not self:
408 if not self:
409 return '%s()' % (self.__class__.__name__,)
409 return '%s()' % (self.__class__.__name__,)
410 return '%s(%r)' % (self.__class__.__name__, self.items())
410 return '%s(%r)' % (self.__class__.__name__, self.items())
411
411
412 def copy(self):
412 def copy(self):
413 return self.__class__(self)
413 return self.__class__(self)
414
414
415 @classmethod
415 @classmethod
416 def fromkeys(cls, iterable, value=None):
416 def fromkeys(cls, iterable, value=None):
417 d = cls()
417 d = cls()
418 for key in iterable:
418 for key in iterable:
419 d[key] = value
419 d[key] = value
420 return d
420 return d
421
421
422 def __eq__(self, other):
422 def __eq__(self, other):
423 if isinstance(other, OrderedDict):
423 if isinstance(other, OrderedDict):
424 return len(self) == len(other) and self.items() == other.items()
424 return len(self) == len(other) and self.items() == other.items()
425 return dict.__eq__(self, other)
425 return dict.__eq__(self, other)
426
426
427 def __ne__(self, other):
427 def __ne__(self, other):
428 return not self == other
428 return not self == other
429
429
430
430
431 #===============================================================================
431 #===============================================================================
432 # TEST FUNCTIONS
432 # TEST FUNCTIONS
433 #===============================================================================
433 #===============================================================================
434 def create_test_index(repo_location, full_index):
434 def create_test_index(repo_location, full_index):
435 """Makes default test index
435 """Makes default test index
436 :param repo_location:
436 :param repo_location:
437 :param full_index:
437 :param full_index:
438 """
438 """
439 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
439 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
440 from rhodecode.lib.pidlock import DaemonLock, LockHeld
440 from rhodecode.lib.pidlock import DaemonLock, LockHeld
441 from rhodecode.lib.indexers import IDX_LOCATION
441 from rhodecode.lib.indexers import IDX_LOCATION
442 import shutil
442 import shutil
443
443
444 if os.path.exists(IDX_LOCATION):
444 if os.path.exists(IDX_LOCATION):
445 shutil.rmtree(IDX_LOCATION)
445 shutil.rmtree(IDX_LOCATION)
446
446
447 try:
447 try:
448 l = DaemonLock()
448 l = DaemonLock()
449 WhooshIndexingDaemon(repo_location=repo_location)\
449 WhooshIndexingDaemon(repo_location=repo_location)\
450 .run(full_index=full_index)
450 .run(full_index=full_index)
451 l.release()
451 l.release()
452 except LockHeld:
452 except LockHeld:
453 pass
453 pass
454
454
455 def create_test_env(repos_test_path, config):
455 def create_test_env(repos_test_path, config):
456 """Makes a fresh database and
456 """Makes a fresh database and
457 install test repository into tmp dir
457 install test repository into tmp dir
458 """
458 """
459 from rhodecode.lib.db_manage import DbManage
459 from rhodecode.lib.db_manage import DbManage
460 import tarfile
460 import tarfile
461 import shutil
461 import shutil
462 from os.path import dirname as dn, join as jn, abspath
462 from os.path import dirname as dn, join as jn, abspath
463 from rhodecode.tests import REPO_PATH, NEW_REPO_PATH
463
464
464 log = logging.getLogger('TestEnvCreator')
465 log = logging.getLogger('TestEnvCreator')
465 # create logger
466 # create logger
466 log.setLevel(logging.DEBUG)
467 log.setLevel(logging.DEBUG)
467 log.propagate = True
468 log.propagate = True
468 # create console handler and set level to debug
469 # create console handler and set level to debug
469 ch = logging.StreamHandler()
470 ch = logging.StreamHandler()
470 ch.setLevel(logging.DEBUG)
471 ch.setLevel(logging.DEBUG)
471
472
472 # create formatter
473 # create formatter
473 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
474 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
474
475
475 # add formatter to ch
476 # add formatter to ch
476 ch.setFormatter(formatter)
477 ch.setFormatter(formatter)
477
478
478 # add ch to logger
479 # add ch to logger
479 log.addHandler(ch)
480 log.addHandler(ch)
480
481
481 #PART ONE create db
482 #PART ONE create db
482 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
483 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
483 log.debug('making test db %s', dbname)
484 log.debug('making test db %s', dbname)
484
485
485 dbmanage = DbManage(log_sql=True, dbname=dbname, root=config['here'],
486 dbmanage = DbManage(log_sql=True, dbname=dbname, root=config['here'],
486 tests=True)
487 tests=True)
487 dbmanage.create_tables(override=True)
488 dbmanage.create_tables(override=True)
488 dbmanage.config_prompt(repos_test_path)
489 dbmanage.config_prompt(repos_test_path)
489 dbmanage.create_default_user()
490 dbmanage.create_default_user()
490 dbmanage.admin_prompt()
491 dbmanage.admin_prompt()
491 dbmanage.create_permissions()
492 dbmanage.create_permissions()
492 dbmanage.populate_default_permissions()
493 dbmanage.populate_default_permissions()
493
494
494 #PART TWO make test repo
495 #PART TWO make test repo
495 log.debug('making test vcs repo')
496 log.debug('making test vcs repo')
496 if os.path.isdir('/tmp/vcs_test'):
497 if os.path.isdir(REPO_PATH):
497 shutil.rmtree('/tmp/vcs_test')
498 log.debug('REMOVING %s', REPO_PATH)
499 shutil.rmtree(REPO_PATH)
500 if os.path.isdir(NEW_REPO_PATH):
501 log.debug('REMOVING %s', NEW_REPO_PATH)
502 shutil.rmtree(NEW_REPO_PATH)
498
503
499 cur_dir = dn(dn(abspath(__file__)))
504 cur_dir = dn(dn(abspath(__file__)))
500 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test.tar.gz"))
505 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test.tar.gz"))
501 tar.extractall('/tmp')
506 tar.extractall('/tmp')
502 tar.close()
507 tar.close()
@@ -1,31 +1,31 b''
1 ##content highligthing
1 ##content highligthing
2
2
3 %for cnt,sr in enumerate(c.formated_results):
3 %for cnt,sr in enumerate(c.formated_results):
4 %if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(sr['repository'],'search results check'):
4 %if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(sr['repository'],'search results check'):
5 <div class="table">
5 <div class="table">
6 <div id="body${cnt}" class="codeblock">
6 <div id="body${cnt}" class="codeblock">
7 <div class="code-header">
7 <div class="code-header">
8 <div class="revision">${h.link_to(h.literal('%s &raquo; %s' % (sr['repository'],sr['f_path'])),
8 <div class="revision">${h.link_to(h.literal('%s &raquo; %s' % (sr['repository'],sr['f_path'])),
9 h.url('files_home',repo_name=sr['repository'],revision='tip',f_path=sr['f_path']))}</div>
9 h.url('files_home',repo_name=sr['repository'],revision='tip',f_path=sr['f_path']))}</div>
10 </div>
10 </div>
11 <div class="code-body">
11 <div class="code-body">
12 <pre>${h.literal(sr['content_short_hl'])}</pre>
12 <pre>${h.literal(sr['content_short_hl'])}</pre>
13 </div>
13 </div>
14 </div>
14 </div>
15 </div>
15 </div>
16 %else:
16 %else:
17 %if cnt == 0:
17 %if cnt == 0:
18 <div class="table">
18 <div class="table">
19 <div id="body${cnt}" class="codeblock">
19 <div id="body${cnt}" class="codeblock">
20 <div class="error">${_('Permission denied')}</div>
20 <div class="error">${_('Permission denied')}</div>
21 </div>
21 </div>
22 </div>
22 </div>
23 %endif
23 %endif
24
24
25 %endif
25 %endif
26 %endfor
26 %endfor
27 %if c.cur_query and c.formated_results:
27 %if c.cur_query and c.formated_results:
28 <div class="pagination-wh pagination-left">
28 <div class="pagination-wh pagination-left" style="padding-left:16px">
29 ${c.formated_results.pager('$link_previous ~2~ $link_next')}
29 ${c.formated_results.pager('$link_previous ~2~ $link_next')}
30 </div>
30 </div>
31 %endif No newline at end of file
31 %endif
@@ -1,58 +1,60 b''
1 """Pylons application test package
1 """Pylons application test package
2
2
3 This package assumes the Pylons environment is already loaded, such as
3 This package assumes the Pylons environment is already loaded, such as
4 when this script is imported from the `nosetests --with-pylons=test.ini`
4 when this script is imported from the `nosetests --with-pylons=test.ini`
5 command.
5 command.
6
6
7 This module initializes the application via ``websetup`` (`paster
7 This module initializes the application via ``websetup`` (`paster
8 setup-app`) and provides the base testing objects.
8 setup-app`) and provides the base testing objects.
9 """
9 """
10 from unittest import TestCase
10 from unittest import TestCase
11
11
12 from paste.deploy import loadapp
12 from paste.deploy import loadapp
13 from paste.script.appinstall import SetupCommand
13 from paste.script.appinstall import SetupCommand
14 from pylons import config, url
14 from pylons import config, url
15 from routes.util import URLGenerator
15 from routes.util import URLGenerator
16 from webtest import TestApp
16 from webtest import TestApp
17 import os
17 import os
18 from rhodecode.model import meta
18 from rhodecode.model import meta
19 import logging
19 import logging
20
20
21
21 log = logging.getLogger(__name__)
22 log = logging.getLogger(__name__)
23
22
24 import pylons.test
23 import pylons.test
25
24
26 __all__ = ['environ', 'url', 'TestController']
25 __all__ = ['environ', 'url', 'TestController']
27
26
28 # Invoke websetup with the current config file
27 # Invoke websetup with the current config file
29 #SetupCommand('setup-app').run([config_file])
28 #SetupCommand('setup-app').run([config_file])
30
29
31 ##RUNNING DESIRED TESTS
30 ##RUNNING DESIRED TESTS
32 #nosetests rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account
31 #nosetests rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account
33
32
34 environ = {}
33 environ = {}
34 TEST_DIR = '/tmp'
35 REPO_PATH = os.path.join(TEST_DIR, 'vcs_test')
36 NEW_REPO_PATH = os.path.join(TEST_DIR, 'vcs_test_new')
35
37
36 class TestController(TestCase):
38 class TestController(TestCase):
37
39
38 def __init__(self, *args, **kwargs):
40 def __init__(self, *args, **kwargs):
39 wsgiapp = pylons.test.pylonsapp
41 wsgiapp = pylons.test.pylonsapp
40 config = wsgiapp.config
42 config = wsgiapp.config
41 self.app = TestApp(wsgiapp)
43 self.app = TestApp(wsgiapp)
42 url._push_object(URLGenerator(config['routes.map'], environ))
44 url._push_object(URLGenerator(config['routes.map'], environ))
43 self.sa = meta.Session
45 self.sa = meta.Session
44
46
45 TestCase.__init__(self, *args, **kwargs)
47 TestCase.__init__(self, *args, **kwargs)
46
48
47 def log_user(self, username='test_admin', password='test12'):
49 def log_user(self, username='test_admin', password='test12'):
48 response = self.app.post(url(controller='login', action='index'),
50 response = self.app.post(url(controller='login', action='index'),
49 {'username':username,
51 {'username':username,
50 'password':password})
52 'password':password})
51 print response
53 print response
52
54
53 if 'invalid user name' in response.body:
55 if 'invalid user name' in response.body:
54 assert False, 'could not login using %s %s' % (username, password)
56 assert False, 'could not login using %s %s' % (username, password)
55
57
56 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
58 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
57 assert response.session['rhodecode_user'].username == username, 'wrong logged in user got %s expected %s' % (response.session['rhodecode_user'].username, username)
59 assert response.session['rhodecode_user'].username == username, 'wrong logged in user got %s expected %s' % (response.session['rhodecode_user'].username, username)
58 return response.follow()
60 return response.follow()
@@ -1,31 +1,30 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2
2
3 class TestChangelogController(TestController):
3 class TestChangelogController(TestController):
4
4
5 def test_index(self):
5 def test_index(self):
6 self.log_user()
6 self.log_user()
7 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'))
7 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'))
8
8
9 print response
9 assert """<div id="chg_20" class="container">""" in response.body, 'wrong info about number of changes'
10 assert """<div id="chg_20" class="container">""" in response.body, 'wrong info about number ofchanges'
11 assert """Small update at simplevcs app""" in response.body, 'missing info about commit message'
10 assert """Small update at simplevcs app""" in response.body, 'missing info about commit message'
12 assert """<span class="removed" title="removed">0</span>""" in response.body, 'wrong info about removed nodes'
11 assert """<span class="removed" title="removed">0</span>""" in response.body, 'wrong info about removed nodes'
13 assert """<span class="changed" title="changed">2</span>""" in response.body, 'wrong info about changed nodes'
12 assert """<span class="changed" title="changed">2</span>""" in response.body, 'wrong info about changed nodes'
14 assert """<span class="added" title="added">1</span>""" in response.body, 'wrong info about added nodes'
13 assert """<span class="added" title="added">1</span>""" in response.body, 'wrong info about added nodes'
15
14
16 #pagination
15 #pagination
17
16
18 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'), {'page':1})
17 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'), {'page':1})
19 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'), {'page':2})
18 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'), {'page':2})
20 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'), {'page':3})
19 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'), {'page':3})
21 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'), {'page':4})
20 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'), {'page':4})
22 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'), {'page':5})
21 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'), {'page':5})
23 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'), {'page':6})
22 response = self.app.get(url(controller='changelog', action='index', repo_name='vcs_test'), {'page':6})
24 # Test response after pagination...
23 # Test response after pagination...
25
24
26 assert """<span class="removed" title="removed">20</span>"""in response.body, 'wrong info about number of removed'
25 assert """<span class="removed" title="removed">20</span>"""in response.body, 'wrong info about number of removed'
27 assert """<span class="changed" title="changed">1</span>"""in response.body, 'wrong info about number of changes'
26 assert """<span class="changed" title="changed">1</span>"""in response.body, 'wrong info about number of changes'
28 assert """<span class="added" title="added">0</span>"""in response.body, 'wrong info about number of added'
27 assert """<span class="added" title="added">0</span>"""in response.body, 'wrong info about number of added'
29 assert """<div class="date">commit 64: 46ad32a4f974@2010-04-20 00:33:21</div>"""in response.body, 'wrong info about commit 64'
28 assert """<div class="date">commit 64: 46ad32a4f974@2010-04-20 00:33:21</div>"""in response.body, 'wrong info about commit 64'
29
30 assert """<div class="message"><a href="/vcs_test/changeset/46ad32a4f974">Merge with 2e6a2bf9356ca56df08807f4ad86d480da72a8f4</a></div>"""in response.body, 'wrong info about commit 64 is a merge'
30 assert """<div class="message"><a href="/vcs_test/changeset/46ad32a4f974">Merge with 2e6a2bf9356ca56df08807f4ad86d480da72a8f4</a></div>"""in response.body, 'wrong info about commit 64 is a merge'
31
@@ -1,42 +1,42 b''
1 [egg_info]
1 [egg_info]
2 tag_build = rc4
2 tag_build =
3 tag_svn_revision = true
3 tag_svn_revision = true
4
4
5 [easy_install]
5 [easy_install]
6 find_links = http://www.pylonshq.com/download/
6 find_links = http://www.pylonshq.com/download/
7
7
8 [nosetests]
8 [nosetests]
9 verbose=True
9 verbose=True
10 verbosity=2
10 verbosity=2
11 with-pylons=test.ini
11 with-pylons=test.ini
12 detailed-errors=1
12 detailed-errors=1
13
13
14 # Babel configuration
14 # Babel configuration
15 [compile_catalog]
15 [compile_catalog]
16 domain = rhodecode
16 domain = rhodecode
17 directory = rhodecode/i18n
17 directory = rhodecode/i18n
18 statistics = true
18 statistics = true
19
19
20 [extract_messages]
20 [extract_messages]
21 add_comments = TRANSLATORS:
21 add_comments = TRANSLATORS:
22 output_file = rhodecode/i18n/rhodecode.pot
22 output_file = rhodecode/i18n/rhodecode.pot
23 width = 80
23 width = 80
24
24
25 [init_catalog]
25 [init_catalog]
26 domain = rhodecode
26 domain = rhodecode
27 input_file = rhodecode/i18n/rhodecode.pot
27 input_file = rhodecode/i18n/rhodecode.pot
28 output_dir = rhodecode/i18n
28 output_dir = rhodecode/i18n
29
29
30 [update_catalog]
30 [update_catalog]
31 domain = rhodecode
31 domain = rhodecode
32 input_file = rhodecode/i18n/rhodecode.pot
32 input_file = rhodecode/i18n/rhodecode.pot
33 output_dir = rhodecode/i18n
33 output_dir = rhodecode/i18n
34 previous = true
34 previous = true
35
35
36 [build_sphinx]
36 [build_sphinx]
37 source-dir = docs/
37 source-dir = docs/
38 build-dir = docs/_build
38 build-dir = docs/_build
39 all_files = 1
39 all_files = 1
40
40
41 [upload_sphinx]
41 [upload_sphinx]
42 upload-dir = docs/_build/html No newline at end of file
42 upload-dir = docs/_build/html
@@ -1,88 +1,88 b''
1 from rhodecode import get_version
1 from rhodecode import get_version
2 import sys
2 import sys
3 py_version = sys.version_info
3 py_version = sys.version_info
4
4
5 requirements = [
5 requirements = [
6 "Pylons>=1.0.0",
6 "Pylons>=1.0.0",
7 "SQLAlchemy>=0.6",
7 "SQLAlchemy==0.6.4",
8 "Mako>=0.3.2",
8 "Mako>=0.3.2",
9 "vcs==0.1.8",
9 "vcs==0.1.8",
10 "pygments>=1.3.0",
10 "pygments>=1.3.0",
11 "mercurial>=1.6",
11 "mercurial==1.6.4",
12 "whoosh==1.0.0",
12 "whoosh==1.2.5",
13 "celery>=2.0.0",
13 "celery==2.1.2",
14 "py-bcrypt",
14 "py-bcrypt",
15 "babel",
15 "babel",
16 ]
16 ]
17
17
18 classifiers = ['Development Status :: 4 - Beta',
18 classifiers = ["Development Status :: 5 - Production/Stable",
19 'Environment :: Web Environment',
19 'Environment :: Web Environment',
20 'Framework :: Pylons',
20 'Framework :: Pylons',
21 'Intended Audience :: Developers',
21 'Intended Audience :: Developers',
22 'License :: OSI Approved :: BSD License',
22 'License :: OSI Approved :: BSD License',
23 'Operating System :: OS Independent',
23 'Operating System :: OS Independent',
24 'Programming Language :: Python', ]
24 'Programming Language :: Python', ]
25
25
26 if sys.version_info < (2, 6):
26 if sys.version_info < (2, 6):
27 requirements.append("simplejson")
27 requirements.append("simplejson")
28 requirements.append("pysqlite")
28 requirements.append("pysqlite")
29
29
30 #additional files from project that goes somewhere in the filesystem
30 #additional files from project that goes somewhere in the filesystem
31 #relative to sys.prefix
31 #relative to sys.prefix
32 data_files = []
32 data_files = []
33
33
34 #additional files that goes into package itself
34 #additional files that goes into package itself
35 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
35 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
36
36
37 description = 'Mercurial repository serving and browsing app'
37 description = 'Mercurial repository serving and browsing app'
38 #long description
38 #long description
39 try:
39 try:
40 readme_file = 'README.rst'
40 readme_file = 'README.rst'
41 long_description = open(readme_file).read()
41 long_description = open(readme_file).read()
42 except IOError, err:
42 except IOError, err:
43 sys.stderr.write("[WARNING] Cannot find file specified as "
43 sys.stderr.write("[WARNING] Cannot find file specified as "
44 "long_description (%s)\n skipping that file" % readme_file)
44 "long_description (%s)\n skipping that file" % readme_file)
45 long_description = description
45 long_description = description
46
46
47
47
48 try:
48 try:
49 from setuptools import setup, find_packages
49 from setuptools import setup, find_packages
50 except ImportError:
50 except ImportError:
51 from ez_setup import use_setuptools
51 from ez_setup import use_setuptools
52 use_setuptools()
52 use_setuptools()
53 from setuptools import setup, find_packages
53 from setuptools import setup, find_packages
54 #packages
54 #packages
55 packages = find_packages(exclude=['ez_setup'])
55 packages = find_packages(exclude=['ez_setup'])
56
56
57 setup(
57 setup(
58 name='RhodeCode',
58 name='RhodeCode',
59 version=get_version(),
59 version=get_version(),
60 description=description,
60 description=description,
61 long_description=long_description,
61 long_description=long_description,
62 keywords='rhodiumcode mercurial web hgwebdir replacement serving hgweb rhodecode',
62 keywords='rhodiumcode mercurial web hgwebdir replacement serving hgweb rhodecode',
63 license='BSD',
63 license='BSD',
64 author='Marcin Kuzminski',
64 author='Marcin Kuzminski',
65 author_email='marcin@python-works.com',
65 author_email='marcin@python-works.com',
66 url='http://hg.python-works.com',
66 url='http://hg.python-works.com',
67 install_requires=requirements,
67 install_requires=requirements,
68 classifiers=classifiers,
68 classifiers=classifiers,
69 setup_requires=["PasteScript>=1.6.3"],
69 setup_requires=["PasteScript>=1.6.3"],
70 data_files=data_files,
70 data_files=data_files,
71 packages=packages,
71 packages=packages,
72 include_package_data=True,
72 include_package_data=True,
73 test_suite='nose.collector',
73 test_suite='nose.collector',
74 package_data=package_data,
74 package_data=package_data,
75 message_extractors={'rhodecode': [
75 message_extractors={'rhodecode': [
76 ('**.py', 'python', None),
76 ('**.py', 'python', None),
77 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
77 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
78 ('public/**', 'ignore', None)]},
78 ('public/**', 'ignore', None)]},
79 zip_safe=False,
79 zip_safe=False,
80 paster_plugins=['PasteScript', 'Pylons'],
80 paster_plugins=['PasteScript', 'Pylons'],
81 entry_points="""
81 entry_points="""
82 [paste.app_factory]
82 [paste.app_factory]
83 main = rhodecode.config.middleware:make_app
83 main = rhodecode.config.middleware:make_app
84
84
85 [paste.app_install]
85 [paste.app_install]
86 main = pylons.util:PylonsInstaller
86 main = pylons.util:PylonsInstaller
87 """,
87 """,
88 )
88 )
General Comments 0
You need to be logged in to leave comments. Login now