##// END OF EJS Templates
#48 rewrite action loggers into hooks with all changesets that are inside a push
marcink -
r654:7f5976da beta
parent child Browse files
Show More
@@ -1,78 +1,99 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # custom hooks for application
3 # custom hooks for application
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 Aug 6, 2010
21 Created on Aug 6, 2010
22
22
23 @author: marcink
23 @author: marcink
24 """
24 """
25
25 from mercurial.cmdutil import revrange
26 import sys
26 from mercurial.node import nullrev
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib.utils import action_logger
27 import os
29 import os
28 from rhodecode.lib import helpers as h
30 import sys
29 from rhodecode.model import meta
30 from rhodecode.model.db import UserLog, User
31
31
32 def repo_size(ui, repo, hooktype=None, **kwargs):
32 def repo_size(ui, repo, hooktype=None, **kwargs):
33
33
34 if hooktype != 'changegroup':
34 if hooktype != 'changegroup':
35 return False
35 return False
36 size_hg, size_root = 0, 0
36 size_hg, size_root = 0, 0
37 for path, dirs, files in os.walk(repo.root):
37 for path, dirs, files in os.walk(repo.root):
38 if path.find('.hg') != -1:
38 if path.find('.hg') != -1:
39 for f in files:
39 for f in files:
40 size_hg += os.path.getsize(os.path.join(path, f))
40 size_hg += os.path.getsize(os.path.join(path, f))
41 else:
41 else:
42 for f in files:
42 for f in files:
43 size_root += os.path.getsize(os.path.join(path, f))
43 size_root += os.path.getsize(os.path.join(path, f))
44
44
45 size_hg_f = h.format_byte_size(size_hg)
45 size_hg_f = h.format_byte_size(size_hg)
46 size_root_f = h.format_byte_size(size_root)
46 size_root_f = h.format_byte_size(size_root)
47 size_total_f = h.format_byte_size(size_root + size_hg)
47 size_total_f = h.format_byte_size(size_root + size_hg)
48 sys.stdout.write('Repository size .hg:%s repo:%s total:%s\n' \
48 sys.stdout.write('Repository size .hg:%s repo:%s total:%s\n' \
49 % (size_hg_f, size_root_f, size_total_f))
49 % (size_hg_f, size_root_f, size_total_f))
50
50
51 user_action_mapper(ui, repo, hooktype, **kwargs)
51 def log_pull_action(ui, repo, **kwargs):
52 """
53 Logs user last pull action
54 :param ui:
55 :param repo:
56 """
57
58 extra_params = dict(repo.ui.configitems('rhodecode_extras'))
59 username = extra_params['username']
60 repository = extra_params['repository']
61 action = 'pull'
62
63 action_logger(username, action, repository, extra_params['ip'])
64
65 return 0
52
66
53 def user_action_mapper(ui, repo, hooktype=None, **kwargs):
67 def log_push_action(ui, repo, **kwargs):
54 """
68 """
55 Maps user last push action to new changeset id, from mercurial
69 Maps user last push action to new changeset id, from mercurial
56 :param ui:
70 :param ui:
57 :param repo:
71 :param repo:
58 :param hooktype:
59 """
72 """
60
73
61 try:
74 extra_params = dict(repo.ui.configitems('rhodecode_extras'))
62 sa = meta.Session()
75 username = extra_params['username']
63 username = kwargs['url'].split(':')[-1]
76 repository = extra_params['repository']
64 user_log = sa.query(UserLog)\
77 action = 'push:%s'
65 .filter(UserLog.user == sa.query(User)\
78 node = kwargs['node']
66 .filter(User.username == username).one())\
79
67 .order_by(UserLog.user_log_id.desc()).first()
80 def get_revs(repo, rev_opt):
81 if rev_opt:
82 revs = revrange(repo, rev_opt)
83
84 if len(revs) == 0:
85 return (nullrev, nullrev)
86 return (max(revs), min(revs))
87 else:
88 return (len(repo) - 1, 0)
89
90 stop, start = get_revs(repo, [node + ':'])
91
92 revs = (str(repo[r]) for r in xrange(start, stop + 1))
93
94 action = action % ','.join(revs)
68
95
69 if user_log and not user_log.revision:
96 action_logger(username, action, repository, extra_params['ip'])
70 user_log.revision = str(repo['tip'])
97
71 sa.add(user_log)
98 return 0
72 sa.commit()
99
73
74 except Exception, e:
75 sa.rollback()
76 raise
77 finally:
78 meta.Session.remove()
@@ -1,227 +1,241 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # middleware to handle mercurial api calls
3 # middleware to handle mercurial api calls
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 2010-04-28
21 Created on 2010-04-28
22
22
23 @author: marcink
23 @author: marcink
24 SimpleHG middleware for handling mercurial protocol request (push/clone etc.)
24 SimpleHG middleware for handling mercurial protocol request (push/clone etc.)
25 It's implemented with basic auth function
25 It's implemented with basic auth function
26 """
26 """
27 from itertools import chain
27 from itertools import chain
28 from mercurial.error import RepoError
28 from mercurial.error import RepoError
29 from mercurial.hgweb import hgweb
29 from mercurial.hgweb import hgweb
30 from mercurial.hgweb.request import wsgiapplication
30 from mercurial.hgweb.request import wsgiapplication
31 from paste.auth.basic import AuthBasicAuthenticator
31 from paste.auth.basic import AuthBasicAuthenticator
32 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
32 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
33 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
33 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
34 from rhodecode.lib.utils import is_mercurial, make_ui, invalidate_cache, \
34 from rhodecode.lib.utils import is_mercurial, make_ui, invalidate_cache, \
35 check_repo_fast, ui_sections
35 check_repo_fast, ui_sections
36 from rhodecode.model.user import UserModel
36 from rhodecode.model.user import UserModel
37 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
37 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
38 from rhodecode.lib.utils import action_logger
38 from rhodecode.lib.utils import action_logger
39 import logging
39 import logging
40 import os
40 import os
41 import traceback
41 import traceback
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45 class SimpleHg(object):
45 class SimpleHg(object):
46
46
47 def __init__(self, application, config):
47 def __init__(self, application, config):
48 self.application = application
48 self.application = application
49 self.config = config
49 self.config = config
50 #authenticate this mercurial request using
50 #authenticate this mercurial request using
51 self.authenticate = AuthBasicAuthenticator('', authfunc)
51 self.authenticate = AuthBasicAuthenticator('', authfunc)
52
52 self.ipaddr = '0.0.0.0'
53 self.repository = None
54 self.username = None
55 self.action = None
56
53 def __call__(self, environ, start_response):
57 def __call__(self, environ, start_response):
54 if not is_mercurial(environ):
58 if not is_mercurial(environ):
55 return self.application(environ, start_response)
59 return self.application(environ, start_response)
56
60
61 proxy_key = 'HTTP_X_REAL_IP'
62 def_key = 'REMOTE_ADDR'
63 self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
64
57 #===================================================================
65 #===================================================================
58 # AUTHENTICATE THIS MERCURIAL REQUEST
66 # AUTHENTICATE THIS MERCURIAL REQUEST
59 #===================================================================
67 #===================================================================
60 username = REMOTE_USER(environ)
68 username = REMOTE_USER(environ)
69
61 if not username:
70 if not username:
62 self.authenticate.realm = self.config['rhodecode_realm']
71 self.authenticate.realm = self.config['rhodecode_realm']
63 result = self.authenticate(environ)
72 result = self.authenticate(environ)
64 if isinstance(result, str):
73 if isinstance(result, str):
65 AUTH_TYPE.update(environ, 'basic')
74 AUTH_TYPE.update(environ, 'basic')
66 REMOTE_USER.update(environ, result)
75 REMOTE_USER.update(environ, result)
67 else:
76 else:
68 return result.wsgi_application(environ, start_response)
77 return result.wsgi_application(environ, start_response)
69
78
79 #=======================================================================
80 # GET REPOSITORY
81 #=======================================================================
70 try:
82 try:
71 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
83 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
72 if repo_name.endswith('/'):
84 if repo_name.endswith('/'):
73 repo_name = repo_name.rstrip('/')
85 repo_name = repo_name.rstrip('/')
86 self.repository = repo_name
74 except:
87 except:
75 log.error(traceback.format_exc())
88 log.error(traceback.format_exc())
76 return HTTPInternalServerError()(environ, start_response)
89 return HTTPInternalServerError()(environ, start_response)
77
90
78 #===================================================================
91 #===================================================================
79 # CHECK PERMISSIONS FOR THIS REQUEST
92 # CHECK PERMISSIONS FOR THIS REQUEST
80 #===================================================================
93 #===================================================================
81 action = self.__get_action(environ)
94 self.action = self.__get_action(environ)
82 if action:
95 if self.action:
83 username = self.__get_environ_user(environ)
96 username = self.__get_environ_user(environ)
84 try:
97 try:
85 user = self.__get_user(username)
98 user = self.__get_user(username)
99 self.username = user.username
86 except:
100 except:
87 log.error(traceback.format_exc())
101 log.error(traceback.format_exc())
88 return HTTPInternalServerError()(environ, start_response)
102 return HTTPInternalServerError()(environ, start_response)
89
103
90 #check permissions for this repository
104 #check permissions for this repository
91 if action == 'push':
105 if self.action == 'push':
92 if not HasPermissionAnyMiddleware('repository.write',
106 if not HasPermissionAnyMiddleware('repository.write',
93 'repository.admin')\
107 'repository.admin')\
94 (user, repo_name):
108 (user, repo_name):
95 return HTTPForbidden()(environ, start_response)
109 return HTTPForbidden()(environ, start_response)
96
110
97 else:
111 else:
98 #any other action need at least read permission
112 #any other action need at least read permission
99 if not HasPermissionAnyMiddleware('repository.read',
113 if not HasPermissionAnyMiddleware('repository.read',
100 'repository.write',
114 'repository.write',
101 'repository.admin')\
115 'repository.admin')\
102 (user, repo_name):
116 (user, repo_name):
103 return HTTPForbidden()(environ, start_response)
117 return HTTPForbidden()(environ, start_response)
104
118
105 #log action
119 self.extras = {'ip':self.ipaddr,
106 if action in ('push', 'pull', 'clone'):
120 'username':self.username,
107 proxy_key = 'HTTP_X_REAL_IP'
121 'action':self.action,
108 def_key = 'REMOTE_ADDR'
122 'repository':self.repository}
109 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
123 print self.extras
110 self.__log_user_action(user, action, repo_name, ipaddr)
111
112 #===================================================================
124 #===================================================================
113 # MERCURIAL REQUEST HANDLING
125 # MERCURIAL REQUEST HANDLING
114 #===================================================================
126 #===================================================================
115 environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
127 environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
116 self.baseui = make_ui('db')
128 self.baseui = make_ui('db')
117 self.basepath = self.config['base_path']
129 self.basepath = self.config['base_path']
118 self.repo_path = os.path.join(self.basepath, repo_name)
130 self.repo_path = os.path.join(self.basepath, repo_name)
119
131
120 #quick check if that dir exists...
132 #quick check if that dir exists...
121 if check_repo_fast(repo_name, self.basepath):
133 if check_repo_fast(repo_name, self.basepath):
122 return HTTPNotFound()(environ, start_response)
134 return HTTPNotFound()(environ, start_response)
123 try:
135 try:
124 app = wsgiapplication(self.__make_app)
136 app = wsgiapplication(self.__make_app)
125 except RepoError, e:
137 except RepoError, e:
126 if str(e).find('not found') != -1:
138 if str(e).find('not found') != -1:
127 return HTTPNotFound()(environ, start_response)
139 return HTTPNotFound()(environ, start_response)
128 except Exception:
140 except Exception:
129 log.error(traceback.format_exc())
141 log.error(traceback.format_exc())
130 return HTTPInternalServerError()(environ, start_response)
142 return HTTPInternalServerError()(environ, start_response)
131
143
132 #invalidate cache on push
144 #invalidate cache on push
133 if action == 'push':
145 if self.action == 'push':
134 self.__invalidate_cache(repo_name)
146 self.__invalidate_cache(repo_name)
135 messages = []
147 messages = []
136 messages.append('thank you for using rhodecode')
148 messages.append('thank you for using rhodecode')
137
149
138 return self.msg_wrapper(app, environ, start_response, messages)
150 return self.msg_wrapper(app, environ, start_response, messages)
139 else:
151 else:
140 return app(environ, start_response)
152 return app(environ, start_response)
141
153
142
154
143 def msg_wrapper(self, app, environ, start_response, messages=[]):
155 def msg_wrapper(self, app, environ, start_response, messages=[]):
144 """
156 """
145 Wrapper for custom messages that come out of mercurial respond messages
157 Wrapper for custom messages that come out of mercurial respond messages
146 is a list of messages that the user will see at the end of response
158 is a list of messages that the user will see at the end of response
147 from merurial protocol actions that involves remote answers
159 from merurial protocol actions that involves remote answers
148 :param app:
160 :param app:
149 :param environ:
161 :param environ:
150 :param start_response:
162 :param start_response:
151 """
163 """
152 def custom_messages(msg_list):
164 def custom_messages(msg_list):
153 for msg in msg_list:
165 for msg in msg_list:
154 yield msg + '\n'
166 yield msg + '\n'
155 org_response = app(environ, start_response)
167 org_response = app(environ, start_response)
156 return chain(org_response, custom_messages(messages))
168 return chain(org_response, custom_messages(messages))
157
169
158 def __make_app(self):
170 def __make_app(self):
159 hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
171 hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
160 return self.__load_web_settings(hgserve)
172 return self.__load_web_settings(hgserve, self.extras)
161
173
162 def __get_environ_user(self, environ):
174 def __get_environ_user(self, environ):
163 return environ.get('REMOTE_USER')
175 return environ.get('REMOTE_USER')
164
176
165 def __get_user(self, username):
177 def __get_user(self, username):
166 return UserModel().get_by_username(username, cache=True)
178 return UserModel().get_by_username(username, cache=True)
167
179
168 def __get_action(self, environ):
180 def __get_action(self, environ):
169 """
181 """
170 Maps mercurial request commands into a clone,pull or push command.
182 Maps mercurial request commands into a clone,pull or push command.
171 This should always return a valid command string
183 This should always return a valid command string
172 :param environ:
184 :param environ:
173 """
185 """
174 mapping = {'changegroup': 'pull',
186 mapping = {'changegroup': 'pull',
175 'changegroupsubset': 'pull',
187 'changegroupsubset': 'pull',
176 'stream_out': 'pull',
188 'stream_out': 'pull',
177 #'listkeys': 'pull',
189 'listkeys': 'pull',
178 'unbundle': 'push',
190 'unbundle': 'push',
179 'pushkey': 'push', }
191 'pushkey': 'push', }
180 for qry in environ['QUERY_STRING'].split('&'):
192 for qry in environ['QUERY_STRING'].split('&'):
181 if qry.startswith('cmd'):
193 if qry.startswith('cmd'):
182 cmd = qry.split('=')[-1]
194 cmd = qry.split('=')[-1]
183 if mapping.has_key(cmd):
195 if mapping.has_key(cmd):
184 return mapping[cmd]
196 return mapping[cmd]
185 else:
197 else:
186 return cmd
198 return cmd
187
199
188 def __log_user_action(self, user, action, repo, ipaddr):
189 action_logger(user, action, repo, ipaddr)
190
191 def __invalidate_cache(self, repo_name):
200 def __invalidate_cache(self, repo_name):
192 """we know that some change was made to repositories and we should
201 """we know that some change was made to repositories and we should
193 invalidate the cache to see the changes right away but only for
202 invalidate the cache to see the changes right away but only for
194 push requests"""
203 push requests"""
195 invalidate_cache('cached_repo_list')
204 invalidate_cache('cached_repo_list')
196 invalidate_cache('full_changelog', repo_name)
205 invalidate_cache('full_changelog', repo_name)
197
206
198
207
199 def __load_web_settings(self, hgserve):
208 def __load_web_settings(self, hgserve, extras={}):
200 #set the global ui for hgserve instance passed
209 #set the global ui for hgserve instance passed
201 hgserve.repo.ui = self.baseui
210 hgserve.repo.ui = self.baseui
202
211
203 hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
212 hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
213
214 #inject some additional parameters that will be available in ui
215 #for hooks
216 for k, v in extras.items():
217 hgserve.repo.ui.setconfig('rhodecode_extras', k, v)
218
204 repoui = make_ui('file', hgrc, False)
219 repoui = make_ui('file', hgrc, False)
205
220
206
207 if repoui:
221 if repoui:
208 #overwrite our ui instance with the section from hgrc file
222 #overwrite our ui instance with the section from hgrc file
209 for section in ui_sections:
223 for section in ui_sections:
210 for k, v in repoui.configitems(section):
224 for k, v in repoui.configitems(section):
211 hgserve.repo.ui.setconfig(section, k, v)
225 hgserve.repo.ui.setconfig(section, k, v)
212
226
213 return hgserve
227 return hgserve
214
228
215
229
216
230
217
231
218
232
219
233
220
234
221
235
222
236
223
237
224
238
225
239
226
240
227
241
@@ -1,541 +1,542 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 Created on April 18, 2010
20 Created on April 18, 2010
21 Utilities for RhodeCode
21 Utilities for RhodeCode
22 @author: marcink
22 @author: marcink
23 """
23 """
24
24
25 from UserDict import DictMixin
25 from UserDict import DictMixin
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.caching_query import FromCache
29 from rhodecode.model.caching_query import FromCache
30 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, \
30 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, \
31 UserLog
31 UserLog
32 from rhodecode.model.repo import RepoModel
32 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.user import UserModel
33 from rhodecode.model.user import UserModel
34 from vcs.backends.base import BaseChangeset
34 from vcs.backends.base import BaseChangeset
35 from vcs.backends.git import GitRepository
35 from vcs.backends.git import GitRepository
36 from vcs.backends.hg import MercurialRepository
36 from vcs.backends.hg import MercurialRepository
37 from vcs.utils.lazy import LazyProperty
37 from vcs.utils.lazy import LazyProperty
38 import traceback
38 import datetime
39 import datetime
39 import logging
40 import logging
40 import os
41 import os
41
42
42 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
43
44
44
45
45 def get_repo_slug(request):
46 def get_repo_slug(request):
46 return request.environ['pylons.routes_dict'].get('repo_name')
47 return request.environ['pylons.routes_dict'].get('repo_name')
47
48
48 def is_mercurial(environ):
49 def is_mercurial(environ):
49 """
50 """
50 Returns True if request's target is mercurial server - header
51 Returns True if request's target is mercurial server - header
51 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
52 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
52 """
53 """
53 http_accept = environ.get('HTTP_ACCEPT')
54 http_accept = environ.get('HTTP_ACCEPT')
54 if http_accept and http_accept.startswith('application/mercurial'):
55 if http_accept and http_accept.startswith('application/mercurial'):
55 return True
56 return True
56 return False
57 return False
57
58
58 def is_git(environ):
59 def is_git(environ):
59 """
60 """
60 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
61 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
61 then have git client version given.
62 then have git client version given.
62
63
63 :param environ:
64 :param environ:
64 """
65 """
65 http_user_agent = environ.get('HTTP_USER_AGENT')
66 http_user_agent = environ.get('HTTP_USER_AGENT')
66 if http_user_agent.startswith('git'):
67 if http_user_agent.startswith('git'):
67 return True
68 return True
68 return False
69 return False
69
70
70 def action_logger(user, action, repo, ipaddr, sa=None):
71 def action_logger(user, action, repo, ipaddr, sa=None):
71 """
72 """
72 Action logger for various action made by users
73 Action logger for various action made by users
73 """
74 """
74
75
75 if not sa:
76 if not sa:
76 sa = meta.Session()
77 sa = meta.Session()
77
78
78 try:
79 try:
79 if hasattr(user, 'user_id'):
80 if hasattr(user, 'user_id'):
80 user_id = user.user_id
81 user_obj = user
81 elif isinstance(user, basestring):
82 elif isinstance(user, basestring):
82 user_id = UserModel(sa).get_by_username(user, cache=False).user_id
83 user_obj = UserModel(sa).get_by_username(user, cache=False)
83 else:
84 else:
84 raise Exception('You have to provide user object or username')
85 raise Exception('You have to provide user object or username')
85
86
86 repo_name = repo.lstrip('/')
87 repo_name = repo.lstrip('/')
87 user_log = UserLog()
88 user_log = UserLog()
88 user_log.user_id = user_id
89 user_log.user_id = user_obj.user_id
89 user_log.action = action
90 user_log.action = action
90 user_log.repository_name = repo_name
91 user_log.repository_name = repo_name
91 user_log.repository = RepoModel(sa).get(repo_name, cache=False)
92 user_log.repository = RepoModel(sa).get(repo_name, cache=False)
92 user_log.action_date = datetime.datetime.now()
93 user_log.action_date = datetime.datetime.now()
93 user_log.user_ip = ipaddr
94 user_log.user_ip = ipaddr
94 sa.add(user_log)
95 sa.add(user_log)
95 sa.commit()
96 sa.commit()
96
97
97 log.info('Adding user %s, action %s on %s',
98 log.info('Adding user %s, action %s on %s',
98 user.username, action, repo)
99 user_obj.username, action, repo)
99 except Exception, e:
100 except:
101 log.error(traceback.format_exc())
100 sa.rollback()
102 sa.rollback()
101 log.error('could not log user action:%s', str(e))
102
103
103 def get_repos(path, recursive=False, initial=False):
104 def get_repos(path, recursive=False, initial=False):
104 """
105 """
105 Scans given path for repos and return (name,(type,path)) tuple
106 Scans given path for repos and return (name,(type,path)) tuple
106 :param prefix:
107 :param prefix:
107 :param path:
108 :param path:
108 :param recursive:
109 :param recursive:
109 :param initial:
110 :param initial:
110 """
111 """
111 from vcs.utils.helpers import get_scm
112 from vcs.utils.helpers import get_scm
112 from vcs.exceptions import VCSError
113 from vcs.exceptions import VCSError
113
114
114 try:
115 try:
115 scm = get_scm(path)
116 scm = get_scm(path)
116 except:
117 except:
117 pass
118 pass
118 else:
119 else:
119 raise Exception('The given path %s should not be a repository got %s',
120 raise Exception('The given path %s should not be a repository got %s',
120 path, scm)
121 path, scm)
121
122
122 for dirpath in os.listdir(path):
123 for dirpath in os.listdir(path):
123 try:
124 try:
124 yield dirpath, get_scm(os.path.join(path, dirpath))
125 yield dirpath, get_scm(os.path.join(path, dirpath))
125 except VCSError:
126 except VCSError:
126 pass
127 pass
127
128
128 if __name__ == '__main__':
129 if __name__ == '__main__':
129 get_repos('', '/home/marcink/workspace-python')
130 get_repos('', '/home/marcink/workspace-python')
130
131
131
132
132 def check_repo_fast(repo_name, base_path):
133 def check_repo_fast(repo_name, base_path):
133 if os.path.isdir(os.path.join(base_path, repo_name)):return False
134 if os.path.isdir(os.path.join(base_path, repo_name)):return False
134 return True
135 return True
135
136
136 def check_repo(repo_name, base_path, verify=True):
137 def check_repo(repo_name, base_path, verify=True):
137
138
138 repo_path = os.path.join(base_path, repo_name)
139 repo_path = os.path.join(base_path, repo_name)
139
140
140 try:
141 try:
141 if not check_repo_fast(repo_name, base_path):
142 if not check_repo_fast(repo_name, base_path):
142 return False
143 return False
143 r = hg.repository(ui.ui(), repo_path)
144 r = hg.repository(ui.ui(), repo_path)
144 if verify:
145 if verify:
145 hg.verify(r)
146 hg.verify(r)
146 #here we hnow that repo exists it was verified
147 #here we hnow that repo exists it was verified
147 log.info('%s repo is already created', repo_name)
148 log.info('%s repo is already created', repo_name)
148 return False
149 return False
149 except RepoError:
150 except RepoError:
150 #it means that there is no valid repo there...
151 #it means that there is no valid repo there...
151 log.info('%s repo is free for creation', repo_name)
152 log.info('%s repo is free for creation', repo_name)
152 return True
153 return True
153
154
154 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
155 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
155 while True:
156 while True:
156 ok = raw_input(prompt)
157 ok = raw_input(prompt)
157 if ok in ('y', 'ye', 'yes'): return True
158 if ok in ('y', 'ye', 'yes'): return True
158 if ok in ('n', 'no', 'nop', 'nope'): return False
159 if ok in ('n', 'no', 'nop', 'nope'): return False
159 retries = retries - 1
160 retries = retries - 1
160 if retries < 0: raise IOError
161 if retries < 0: raise IOError
161 print complaint
162 print complaint
162
163
163 def get_hg_ui_cached():
164 def get_hg_ui_cached():
164 try:
165 try:
165 sa = meta.Session
166 sa = meta.Session
166 ret = sa.query(RhodeCodeUi)\
167 ret = sa.query(RhodeCodeUi)\
167 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
168 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
168 .all()
169 .all()
169 except:
170 except:
170 pass
171 pass
171 finally:
172 finally:
172 meta.Session.remove()
173 meta.Session.remove()
173 return ret
174 return ret
174
175
175
176
176 def get_hg_settings():
177 def get_hg_settings():
177 try:
178 try:
178 sa = meta.Session()
179 sa = meta.Session()
179 ret = sa.query(RhodeCodeSettings)\
180 ret = sa.query(RhodeCodeSettings)\
180 .options(FromCache("sql_cache_short", "get_hg_settings"))\
181 .options(FromCache("sql_cache_short", "get_hg_settings"))\
181 .all()
182 .all()
182 except:
183 except:
183 pass
184 pass
184 finally:
185 finally:
185 meta.Session.remove()
186 meta.Session.remove()
186
187
187 if not ret:
188 if not ret:
188 raise Exception('Could not get application settings !')
189 raise Exception('Could not get application settings !')
189 settings = {}
190 settings = {}
190 for each in ret:
191 for each in ret:
191 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
192 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
192
193
193 return settings
194 return settings
194
195
195 def get_hg_ui_settings():
196 def get_hg_ui_settings():
196 try:
197 try:
197 sa = meta.Session()
198 sa = meta.Session()
198 ret = sa.query(RhodeCodeUi).all()
199 ret = sa.query(RhodeCodeUi).all()
199 except:
200 except:
200 pass
201 pass
201 finally:
202 finally:
202 meta.Session.remove()
203 meta.Session.remove()
203
204
204 if not ret:
205 if not ret:
205 raise Exception('Could not get application ui settings !')
206 raise Exception('Could not get application ui settings !')
206 settings = {}
207 settings = {}
207 for each in ret:
208 for each in ret:
208 k = each.ui_key
209 k = each.ui_key
209 v = each.ui_value
210 v = each.ui_value
210 if k == '/':
211 if k == '/':
211 k = 'root_path'
212 k = 'root_path'
212
213
213 if k.find('.') != -1:
214 if k.find('.') != -1:
214 k = k.replace('.', '_')
215 k = k.replace('.', '_')
215
216
216 if each.ui_section == 'hooks':
217 if each.ui_section == 'hooks':
217 v = each.ui_active
218 v = each.ui_active
218
219
219 settings[each.ui_section + '_' + k] = v
220 settings[each.ui_section + '_' + k] = v
220
221
221 return settings
222 return settings
222
223
223 #propagated from mercurial documentation
224 #propagated from mercurial documentation
224 ui_sections = ['alias', 'auth',
225 ui_sections = ['alias', 'auth',
225 'decode/encode', 'defaults',
226 'decode/encode', 'defaults',
226 'diff', 'email',
227 'diff', 'email',
227 'extensions', 'format',
228 'extensions', 'format',
228 'merge-patterns', 'merge-tools',
229 'merge-patterns', 'merge-tools',
229 'hooks', 'http_proxy',
230 'hooks', 'http_proxy',
230 'smtp', 'patch',
231 'smtp', 'patch',
231 'paths', 'profiling',
232 'paths', 'profiling',
232 'server', 'trusted',
233 'server', 'trusted',
233 'ui', 'web', ]
234 'ui', 'web', ]
234
235
235 def make_ui(read_from='file', path=None, checkpaths=True):
236 def make_ui(read_from='file', path=None, checkpaths=True):
236 """
237 """
237 A function that will read python rc files or database
238 A function that will read python rc files or database
238 and make an mercurial ui object from read options
239 and make an mercurial ui object from read options
239
240
240 :param path: path to mercurial config file
241 :param path: path to mercurial config file
241 :param checkpaths: check the path
242 :param checkpaths: check the path
242 :param read_from: read from 'file' or 'db'
243 :param read_from: read from 'file' or 'db'
243 """
244 """
244
245
245 baseui = ui.ui()
246 baseui = ui.ui()
246
247
247 if read_from == 'file':
248 if read_from == 'file':
248 if not os.path.isfile(path):
249 if not os.path.isfile(path):
249 log.warning('Unable to read config file %s' % path)
250 log.warning('Unable to read config file %s' % path)
250 return False
251 return False
251 log.debug('reading hgrc from %s', path)
252 log.debug('reading hgrc from %s', path)
252 cfg = config.config()
253 cfg = config.config()
253 cfg.read(path)
254 cfg.read(path)
254 for section in ui_sections:
255 for section in ui_sections:
255 for k, v in cfg.items(section):
256 for k, v in cfg.items(section):
256 baseui.setconfig(section, k, v)
257 baseui.setconfig(section, k, v)
257 log.debug('settings ui from file[%s]%s:%s', section, k, v)
258 log.debug('settings ui from file[%s]%s:%s', section, k, v)
258
259
259 elif read_from == 'db':
260 elif read_from == 'db':
260 hg_ui = get_hg_ui_cached()
261 hg_ui = get_hg_ui_cached()
261 for ui_ in hg_ui:
262 for ui_ in hg_ui:
262 if ui_.ui_active:
263 if ui_.ui_active:
263 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
264 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
264 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
265 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
265
266
266
267
267 return baseui
268 return baseui
268
269
269
270
270 def set_rhodecode_config(config):
271 def set_rhodecode_config(config):
271 hgsettings = get_hg_settings()
272 hgsettings = get_hg_settings()
272
273
273 for k, v in hgsettings.items():
274 for k, v in hgsettings.items():
274 config[k] = v
275 config[k] = v
275
276
276 def invalidate_cache(name, *args):
277 def invalidate_cache(name, *args):
277 """Invalidates given name cache"""
278 """Invalidates given name cache"""
278
279
279 from beaker.cache import region_invalidate
280 from beaker.cache import region_invalidate
280 log.info('INVALIDATING CACHE FOR %s', name)
281 log.info('INVALIDATING CACHE FOR %s', name)
281
282
282 """propagate our arguments to make sure invalidation works. First
283 """propagate our arguments to make sure invalidation works. First
283 argument has to be the name of cached func name give to cache decorator
284 argument has to be the name of cached func name give to cache decorator
284 without that the invalidation would not work"""
285 without that the invalidation would not work"""
285 tmp = [name]
286 tmp = [name]
286 tmp.extend(args)
287 tmp.extend(args)
287 args = tuple(tmp)
288 args = tuple(tmp)
288
289
289 if name == 'cached_repo_list':
290 if name == 'cached_repo_list':
290 from rhodecode.model.hg import _get_repos_cached
291 from rhodecode.model.hg import _get_repos_cached
291 region_invalidate(_get_repos_cached, None, *args)
292 region_invalidate(_get_repos_cached, None, *args)
292
293
293 if name == 'full_changelog':
294 if name == 'full_changelog':
294 from rhodecode.model.hg import _full_changelog_cached
295 from rhodecode.model.hg import _full_changelog_cached
295 region_invalidate(_full_changelog_cached, None, *args)
296 region_invalidate(_full_changelog_cached, None, *args)
296
297
297 class EmptyChangeset(BaseChangeset):
298 class EmptyChangeset(BaseChangeset):
298 """
299 """
299 An dummy empty changeset. It's possible to pass hash when creating
300 An dummy empty changeset. It's possible to pass hash when creating
300 an EmptyChangeset
301 an EmptyChangeset
301 """
302 """
302
303
303 def __init__(self, cs='0' * 40):
304 def __init__(self, cs='0' * 40):
304 self._empty_cs = cs
305 self._empty_cs = cs
305 self.revision = -1
306 self.revision = -1
306 self.message = ''
307 self.message = ''
307 self.author = ''
308 self.author = ''
308 self.date = ''
309 self.date = ''
309
310
310 @LazyProperty
311 @LazyProperty
311 def raw_id(self):
312 def raw_id(self):
312 """
313 """
313 Returns raw string identifying this changeset, useful for web
314 Returns raw string identifying this changeset, useful for web
314 representation.
315 representation.
315 """
316 """
316 return self._empty_cs
317 return self._empty_cs
317
318
318 @LazyProperty
319 @LazyProperty
319 def short_id(self):
320 def short_id(self):
320 return self.raw_id[:12]
321 return self.raw_id[:12]
321
322
322 def get_file_changeset(self, path):
323 def get_file_changeset(self, path):
323 return self
324 return self
324
325
325 def get_file_content(self, path):
326 def get_file_content(self, path):
326 return u''
327 return u''
327
328
328 def get_file_size(self, path):
329 def get_file_size(self, path):
329 return 0
330 return 0
330
331
331 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
332 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
332 """
333 """
333 maps all found repositories into db
334 maps all found repositories into db
334 """
335 """
335
336
336 sa = meta.Session()
337 sa = meta.Session()
337 rm = RepoModel(sa)
338 rm = RepoModel(sa)
338 user = sa.query(User).filter(User.admin == True).first()
339 user = sa.query(User).filter(User.admin == True).first()
339
340
340 for name, repo in initial_repo_list.items():
341 for name, repo in initial_repo_list.items():
341 if not rm.get(name, cache=False):
342 if not rm.get(name, cache=False):
342 log.info('repository %s not found creating default', name)
343 log.info('repository %s not found creating default', name)
343
344
344 if isinstance(repo, MercurialRepository):
345 if isinstance(repo, MercurialRepository):
345 repo_type = 'hg'
346 repo_type = 'hg'
346 if isinstance(repo, GitRepository):
347 if isinstance(repo, GitRepository):
347 repo_type = 'git'
348 repo_type = 'git'
348
349
349 form_data = {
350 form_data = {
350 'repo_name':name,
351 'repo_name':name,
351 'repo_type':repo_type,
352 'repo_type':repo_type,
352 'description':repo.description if repo.description != 'unknown' else \
353 'description':repo.description if repo.description != 'unknown' else \
353 'auto description for %s' % name,
354 'auto description for %s' % name,
354 'private':False
355 'private':False
355 }
356 }
356 rm.create(form_data, user, just_db=True)
357 rm.create(form_data, user, just_db=True)
357
358
358
359
359 if remove_obsolete:
360 if remove_obsolete:
360 #remove from database those repositories that are not in the filesystem
361 #remove from database those repositories that are not in the filesystem
361 for repo in sa.query(Repository).all():
362 for repo in sa.query(Repository).all():
362 if repo.repo_name not in initial_repo_list.keys():
363 if repo.repo_name not in initial_repo_list.keys():
363 sa.delete(repo)
364 sa.delete(repo)
364 sa.commit()
365 sa.commit()
365
366
366
367
367 meta.Session.remove()
368 meta.Session.remove()
368
369
369
370
370 class OrderedDict(dict, DictMixin):
371 class OrderedDict(dict, DictMixin):
371
372
372 def __init__(self, *args, **kwds):
373 def __init__(self, *args, **kwds):
373 if len(args) > 1:
374 if len(args) > 1:
374 raise TypeError('expected at most 1 arguments, got %d' % len(args))
375 raise TypeError('expected at most 1 arguments, got %d' % len(args))
375 try:
376 try:
376 self.__end
377 self.__end
377 except AttributeError:
378 except AttributeError:
378 self.clear()
379 self.clear()
379 self.update(*args, **kwds)
380 self.update(*args, **kwds)
380
381
381 def clear(self):
382 def clear(self):
382 self.__end = end = []
383 self.__end = end = []
383 end += [None, end, end] # sentinel node for doubly linked list
384 end += [None, end, end] # sentinel node for doubly linked list
384 self.__map = {} # key --> [key, prev, next]
385 self.__map = {} # key --> [key, prev, next]
385 dict.clear(self)
386 dict.clear(self)
386
387
387 def __setitem__(self, key, value):
388 def __setitem__(self, key, value):
388 if key not in self:
389 if key not in self:
389 end = self.__end
390 end = self.__end
390 curr = end[1]
391 curr = end[1]
391 curr[2] = end[1] = self.__map[key] = [key, curr, end]
392 curr[2] = end[1] = self.__map[key] = [key, curr, end]
392 dict.__setitem__(self, key, value)
393 dict.__setitem__(self, key, value)
393
394
394 def __delitem__(self, key):
395 def __delitem__(self, key):
395 dict.__delitem__(self, key)
396 dict.__delitem__(self, key)
396 key, prev, next = self.__map.pop(key)
397 key, prev, next = self.__map.pop(key)
397 prev[2] = next
398 prev[2] = next
398 next[1] = prev
399 next[1] = prev
399
400
400 def __iter__(self):
401 def __iter__(self):
401 end = self.__end
402 end = self.__end
402 curr = end[2]
403 curr = end[2]
403 while curr is not end:
404 while curr is not end:
404 yield curr[0]
405 yield curr[0]
405 curr = curr[2]
406 curr = curr[2]
406
407
407 def __reversed__(self):
408 def __reversed__(self):
408 end = self.__end
409 end = self.__end
409 curr = end[1]
410 curr = end[1]
410 while curr is not end:
411 while curr is not end:
411 yield curr[0]
412 yield curr[0]
412 curr = curr[1]
413 curr = curr[1]
413
414
414 def popitem(self, last=True):
415 def popitem(self, last=True):
415 if not self:
416 if not self:
416 raise KeyError('dictionary is empty')
417 raise KeyError('dictionary is empty')
417 if last:
418 if last:
418 key = reversed(self).next()
419 key = reversed(self).next()
419 else:
420 else:
420 key = iter(self).next()
421 key = iter(self).next()
421 value = self.pop(key)
422 value = self.pop(key)
422 return key, value
423 return key, value
423
424
424 def __reduce__(self):
425 def __reduce__(self):
425 items = [[k, self[k]] for k in self]
426 items = [[k, self[k]] for k in self]
426 tmp = self.__map, self.__end
427 tmp = self.__map, self.__end
427 del self.__map, self.__end
428 del self.__map, self.__end
428 inst_dict = vars(self).copy()
429 inst_dict = vars(self).copy()
429 self.__map, self.__end = tmp
430 self.__map, self.__end = tmp
430 if inst_dict:
431 if inst_dict:
431 return (self.__class__, (items,), inst_dict)
432 return (self.__class__, (items,), inst_dict)
432 return self.__class__, (items,)
433 return self.__class__, (items,)
433
434
434 def keys(self):
435 def keys(self):
435 return list(self)
436 return list(self)
436
437
437 setdefault = DictMixin.setdefault
438 setdefault = DictMixin.setdefault
438 update = DictMixin.update
439 update = DictMixin.update
439 pop = DictMixin.pop
440 pop = DictMixin.pop
440 values = DictMixin.values
441 values = DictMixin.values
441 items = DictMixin.items
442 items = DictMixin.items
442 iterkeys = DictMixin.iterkeys
443 iterkeys = DictMixin.iterkeys
443 itervalues = DictMixin.itervalues
444 itervalues = DictMixin.itervalues
444 iteritems = DictMixin.iteritems
445 iteritems = DictMixin.iteritems
445
446
446 def __repr__(self):
447 def __repr__(self):
447 if not self:
448 if not self:
448 return '%s()' % (self.__class__.__name__,)
449 return '%s()' % (self.__class__.__name__,)
449 return '%s(%r)' % (self.__class__.__name__, self.items())
450 return '%s(%r)' % (self.__class__.__name__, self.items())
450
451
451 def copy(self):
452 def copy(self):
452 return self.__class__(self)
453 return self.__class__(self)
453
454
454 @classmethod
455 @classmethod
455 def fromkeys(cls, iterable, value=None):
456 def fromkeys(cls, iterable, value=None):
456 d = cls()
457 d = cls()
457 for key in iterable:
458 for key in iterable:
458 d[key] = value
459 d[key] = value
459 return d
460 return d
460
461
461 def __eq__(self, other):
462 def __eq__(self, other):
462 if isinstance(other, OrderedDict):
463 if isinstance(other, OrderedDict):
463 return len(self) == len(other) and self.items() == other.items()
464 return len(self) == len(other) and self.items() == other.items()
464 return dict.__eq__(self, other)
465 return dict.__eq__(self, other)
465
466
466 def __ne__(self, other):
467 def __ne__(self, other):
467 return not self == other
468 return not self == other
468
469
469
470
470 #===============================================================================
471 #===============================================================================
471 # TEST FUNCTIONS AND CREATORS
472 # TEST FUNCTIONS AND CREATORS
472 #===============================================================================
473 #===============================================================================
473 def create_test_index(repo_location, full_index):
474 def create_test_index(repo_location, full_index):
474 """Makes default test index
475 """Makes default test index
475 :param repo_location:
476 :param repo_location:
476 :param full_index:
477 :param full_index:
477 """
478 """
478 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
479 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
479 from rhodecode.lib.pidlock import DaemonLock, LockHeld
480 from rhodecode.lib.pidlock import DaemonLock, LockHeld
480 from rhodecode.lib.indexers import IDX_LOCATION
481 from rhodecode.lib.indexers import IDX_LOCATION
481 import shutil
482 import shutil
482
483
483 if os.path.exists(IDX_LOCATION):
484 if os.path.exists(IDX_LOCATION):
484 shutil.rmtree(IDX_LOCATION)
485 shutil.rmtree(IDX_LOCATION)
485
486
486 try:
487 try:
487 l = DaemonLock()
488 l = DaemonLock()
488 WhooshIndexingDaemon(repo_location=repo_location)\
489 WhooshIndexingDaemon(repo_location=repo_location)\
489 .run(full_index=full_index)
490 .run(full_index=full_index)
490 l.release()
491 l.release()
491 except LockHeld:
492 except LockHeld:
492 pass
493 pass
493
494
494 def create_test_env(repos_test_path, config):
495 def create_test_env(repos_test_path, config):
495 """Makes a fresh database and
496 """Makes a fresh database and
496 install test repository into tmp dir
497 install test repository into tmp dir
497 """
498 """
498 from rhodecode.lib.db_manage import DbManage
499 from rhodecode.lib.db_manage import DbManage
499 import tarfile
500 import tarfile
500 import shutil
501 import shutil
501 from os.path import dirname as dn, join as jn, abspath
502 from os.path import dirname as dn, join as jn, abspath
502
503
503 log = logging.getLogger('TestEnvCreator')
504 log = logging.getLogger('TestEnvCreator')
504 # create logger
505 # create logger
505 log.setLevel(logging.DEBUG)
506 log.setLevel(logging.DEBUG)
506 log.propagate = True
507 log.propagate = True
507 # create console handler and set level to debug
508 # create console handler and set level to debug
508 ch = logging.StreamHandler()
509 ch = logging.StreamHandler()
509 ch.setLevel(logging.DEBUG)
510 ch.setLevel(logging.DEBUG)
510
511
511 # create formatter
512 # create formatter
512 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
513 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
513
514
514 # add formatter to ch
515 # add formatter to ch
515 ch.setFormatter(formatter)
516 ch.setFormatter(formatter)
516
517
517 # add ch to logger
518 # add ch to logger
518 log.addHandler(ch)
519 log.addHandler(ch)
519
520
520 #PART ONE create db
521 #PART ONE create db
521 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
522 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
522 log.debug('making test db %s', dbname)
523 log.debug('making test db %s', dbname)
523
524
524 dbmanage = DbManage(log_sql=True, dbname=dbname, root=config['here'],
525 dbmanage = DbManage(log_sql=True, dbname=dbname, root=config['here'],
525 tests=True)
526 tests=True)
526 dbmanage.create_tables(override=True)
527 dbmanage.create_tables(override=True)
527 dbmanage.config_prompt(repos_test_path)
528 dbmanage.config_prompt(repos_test_path)
528 dbmanage.create_default_user()
529 dbmanage.create_default_user()
529 dbmanage.admin_prompt()
530 dbmanage.admin_prompt()
530 dbmanage.create_permissions()
531 dbmanage.create_permissions()
531 dbmanage.populate_default_permissions()
532 dbmanage.populate_default_permissions()
532
533
533 #PART TWO make test repo
534 #PART TWO make test repo
534 log.debug('making test vcs repo')
535 log.debug('making test vcs repo')
535 if os.path.isdir('/tmp/vcs_test'):
536 if os.path.isdir('/tmp/vcs_test'):
536 shutil.rmtree('/tmp/vcs_test')
537 shutil.rmtree('/tmp/vcs_test')
537
538
538 cur_dir = dn(dn(abspath(__file__)))
539 cur_dir = dn(dn(abspath(__file__)))
539 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test.tar.gz"))
540 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test.tar.gz"))
540 tar.extractall('/tmp')
541 tar.extractall('/tmp')
541 tar.close()
542 tar.close()
General Comments 0
You need to be logged in to leave comments. Login now