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