##// END OF EJS Templates
use os.environ as a fallback for getting special info from hooks, this will allow...
marcink -
r2716:4c716671 beta
parent child Browse files
Show More
@@ -1,278 +1,303 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.hooks
3 rhodecode.lib.hooks
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Hooks runned by rhodecode
6 Hooks runned by rhodecode
7
7
8 :created_on: Aug 6, 2010
8 :created_on: Aug 6, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import os
25 import os
26 import sys
26 import sys
27 import binascii
27 import binascii
28 from inspect import isfunction
28 from inspect import isfunction
29
29
30 from mercurial.scmutil import revrange
30 from mercurial.scmutil import revrange
31 from mercurial.node import nullrev
31 from mercurial.node import nullrev
32
32
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
34 from rhodecode.lib.utils import action_logger
34 from rhodecode.lib.utils import action_logger
35 from rhodecode.lib.vcs.backends.base import EmptyChangeset
35 from rhodecode.lib.vcs.backends.base import EmptyChangeset
36 from rhodecode.lib.compat import json
36
37
37
38
38 def _get_scm_size(alias, root_path):
39 def _get_scm_size(alias, root_path):
39
40
40 if not alias.startswith('.'):
41 if not alias.startswith('.'):
41 alias += '.'
42 alias += '.'
42
43
43 size_scm, size_root = 0, 0
44 size_scm, size_root = 0, 0
44 for path, dirs, files in os.walk(root_path):
45 for path, dirs, files in os.walk(root_path):
45 if path.find(alias) != -1:
46 if path.find(alias) != -1:
46 for f in files:
47 for f in files:
47 try:
48 try:
48 size_scm += os.path.getsize(os.path.join(path, f))
49 size_scm += os.path.getsize(os.path.join(path, f))
49 except OSError:
50 except OSError:
50 pass
51 pass
51 else:
52 else:
52 for f in files:
53 for f in files:
53 try:
54 try:
54 size_root += os.path.getsize(os.path.join(path, f))
55 size_root += os.path.getsize(os.path.join(path, f))
55 except OSError:
56 except OSError:
56 pass
57 pass
57
58
58 size_scm_f = h.format_byte_size(size_scm)
59 size_scm_f = h.format_byte_size(size_scm)
59 size_root_f = h.format_byte_size(size_root)
60 size_root_f = h.format_byte_size(size_root)
60 size_total_f = h.format_byte_size(size_root + size_scm)
61 size_total_f = h.format_byte_size(size_root + size_scm)
61
62
62 return size_scm_f, size_root_f, size_total_f
63 return size_scm_f, size_root_f, size_total_f
63
64
64
65
65 def repo_size(ui, repo, hooktype=None, **kwargs):
66 def repo_size(ui, repo, hooktype=None, **kwargs):
66 """
67 """
67 Presents size of repository after push
68 Presents size of repository after push
68
69
69 :param ui:
70 :param ui:
70 :param repo:
71 :param repo:
71 :param hooktype:
72 :param hooktype:
72 """
73 """
73
74
74 size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', repo.root)
75 size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', repo.root)
75
76
76 last_cs = repo[len(repo) - 1]
77 last_cs = repo[len(repo) - 1]
77
78
78 msg = ('Repository size .hg:%s repo:%s total:%s\n'
79 msg = ('Repository size .hg:%s repo:%s total:%s\n'
79 'Last revision is now r%s:%s\n') % (
80 'Last revision is now r%s:%s\n') % (
80 size_hg_f, size_root_f, size_total_f, last_cs.rev(), last_cs.hex()[:12]
81 size_hg_f, size_root_f, size_total_f, last_cs.rev(), last_cs.hex()[:12]
81 )
82 )
82
83
83 sys.stdout.write(msg)
84 sys.stdout.write(msg)
84
85
85
86
86 def log_pull_action(ui, repo, **kwargs):
87 def log_pull_action(ui, repo, **kwargs):
87 """
88 """
88 Logs user last pull action
89 Logs user last pull action
89
90
90 :param ui:
91 :param ui:
91 :param repo:
92 :param repo:
92 """
93 """
94 try:
95 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
96 except:
97 rc_extras = {}
93 extras = dict(repo.ui.configitems('rhodecode_extras'))
98 extras = dict(repo.ui.configitems('rhodecode_extras'))
94 username = extras['username']
99 if 'username' in extras:
95 repository = extras['repository']
100 username = extras['username']
96 scm = extras['scm']
101 repository = extras['repository']
102 scm = extras['scm']
103 elif 'username' in rc_extras:
104 username = rc_extras['username']
105 repository = rc_extras['repository']
106 scm = rc_extras['scm']
107 else:
108 raise Exception('Missing data in repo.ui and os.environ')
109
97 action = 'pull'
110 action = 'pull'
98
99 action_logger(username, action, repository, extras['ip'], commit=True)
111 action_logger(username, action, repository, extras['ip'], commit=True)
100 # extension hook call
112 # extension hook call
101 from rhodecode import EXTENSIONS
113 from rhodecode import EXTENSIONS
102 callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
114 callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
103
115
104 if isfunction(callback):
116 if isfunction(callback):
105 kw = {}
117 kw = {}
106 kw.update(extras)
118 kw.update(extras)
107 callback(**kw)
119 callback(**kw)
108 return 0
120 return 0
109
121
110
122
111 def log_push_action(ui, repo, **kwargs):
123 def log_push_action(ui, repo, **kwargs):
112 """
124 """
113 Maps user last push action to new changeset id, from mercurial
125 Maps user last push action to new changeset id, from mercurial
114
126
115 :param ui:
127 :param ui:
116 :param repo: repo object containing the `ui` object
128 :param repo: repo object containing the `ui` object
117 """
129 """
118
130
131 try:
132 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
133 except:
134 rc_extras = {}
135
119 extras = dict(repo.ui.configitems('rhodecode_extras'))
136 extras = dict(repo.ui.configitems('rhodecode_extras'))
120 username = extras['username']
137 if 'username' in extras:
121 repository = extras['repository']
138 username = extras['username']
122 action = extras['action'] + ':%s'
139 repository = extras['repository']
123 scm = extras['scm']
140 scm = extras['scm']
141 elif 'username' in rc_extras:
142 username = rc_extras['username']
143 repository = rc_extras['repository']
144 scm = rc_extras['scm']
145 else:
146 raise Exception('Missing data in repo.ui and os.environ')
147
148 action = 'push' + ':%s'
124
149
125 if scm == 'hg':
150 if scm == 'hg':
126 node = kwargs['node']
151 node = kwargs['node']
127
152
128 def get_revs(repo, rev_opt):
153 def get_revs(repo, rev_opt):
129 if rev_opt:
154 if rev_opt:
130 revs = revrange(repo, rev_opt)
155 revs = revrange(repo, rev_opt)
131
156
132 if len(revs) == 0:
157 if len(revs) == 0:
133 return (nullrev, nullrev)
158 return (nullrev, nullrev)
134 return (max(revs), min(revs))
159 return (max(revs), min(revs))
135 else:
160 else:
136 return (len(repo) - 1, 0)
161 return (len(repo) - 1, 0)
137
162
138 stop, start = get_revs(repo, [node + ':'])
163 stop, start = get_revs(repo, [node + ':'])
139 h = binascii.hexlify
164 h = binascii.hexlify
140 revs = [h(repo[r].node()) for r in xrange(start, stop + 1)]
165 revs = [h(repo[r].node()) for r in xrange(start, stop + 1)]
141 elif scm == 'git':
166 elif scm == 'git':
142 revs = kwargs.get('_git_revs', [])
167 revs = kwargs.get('_git_revs', [])
143 if '_git_revs' in kwargs:
168 if '_git_revs' in kwargs:
144 kwargs.pop('_git_revs')
169 kwargs.pop('_git_revs')
145
170
146 action = action % ','.join(revs)
171 action = action % ','.join(revs)
147
172
148 action_logger(username, action, repository, extras['ip'], commit=True)
173 action_logger(username, action, repository, extras['ip'], commit=True)
149
174
150 # extension hook call
175 # extension hook call
151 from rhodecode import EXTENSIONS
176 from rhodecode import EXTENSIONS
152 callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
177 callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
153 if isfunction(callback):
178 if isfunction(callback):
154 kw = {'pushed_revs': revs}
179 kw = {'pushed_revs': revs}
155 kw.update(extras)
180 kw.update(extras)
156 callback(**kw)
181 callback(**kw)
157 return 0
182 return 0
158
183
159
184
160 def log_create_repository(repository_dict, created_by, **kwargs):
185 def log_create_repository(repository_dict, created_by, **kwargs):
161 """
186 """
162 Post create repository Hook. This is a dummy function for admins to re-use
187 Post create repository Hook. This is a dummy function for admins to re-use
163 if needed. It's taken from rhodecode-extensions module and executed
188 if needed. It's taken from rhodecode-extensions module and executed
164 if present
189 if present
165
190
166 :param repository: dict dump of repository object
191 :param repository: dict dump of repository object
167 :param created_by: username who created repository
192 :param created_by: username who created repository
168 :param created_date: date of creation
193 :param created_date: date of creation
169
194
170 available keys of repository_dict:
195 available keys of repository_dict:
171
196
172 'repo_type',
197 'repo_type',
173 'description',
198 'description',
174 'private',
199 'private',
175 'created_on',
200 'created_on',
176 'enable_downloads',
201 'enable_downloads',
177 'repo_id',
202 'repo_id',
178 'user_id',
203 'user_id',
179 'enable_statistics',
204 'enable_statistics',
180 'clone_uri',
205 'clone_uri',
181 'fork_id',
206 'fork_id',
182 'group_id',
207 'group_id',
183 'repo_name'
208 'repo_name'
184
209
185 """
210 """
186 from rhodecode import EXTENSIONS
211 from rhodecode import EXTENSIONS
187 callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
212 callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
188 if isfunction(callback):
213 if isfunction(callback):
189 kw = {}
214 kw = {}
190 kw.update(repository_dict)
215 kw.update(repository_dict)
191 kw.update({'created_by': created_by})
216 kw.update({'created_by': created_by})
192 kw.update(kwargs)
217 kw.update(kwargs)
193 return callback(**kw)
218 return callback(**kw)
194
219
195 return 0
220 return 0
196
221
197
222
198 def handle_git_post_receive(repo_path, revs, env):
223 def handle_git_post_receive(repo_path, revs, env):
199 """
224 """
200 A really hacky method that is runned by git post-receive hook and logs
225 A really hacky method that is runned by git post-receive hook and logs
201 an push action together with pushed revisions. It's executed by subprocess
226 an push action together with pushed revisions. It's executed by subprocess
202 thus needs all info to be able to create a on the fly pylons enviroment,
227 thus needs all info to be able to create a on the fly pylons enviroment,
203 connect to database and run the logging code. Hacky as sh*t but works.
228 connect to database and run the logging code. Hacky as sh*t but works.
204
229
205 :param repo_path:
230 :param repo_path:
206 :type repo_path:
231 :type repo_path:
207 :param revs:
232 :param revs:
208 :type revs:
233 :type revs:
209 :param env:
234 :param env:
210 :type env:
235 :type env:
211 """
236 """
212 from paste.deploy import appconfig
237 from paste.deploy import appconfig
213 from sqlalchemy import engine_from_config
238 from sqlalchemy import engine_from_config
214 from rhodecode.config.environment import load_environment
239 from rhodecode.config.environment import load_environment
215 from rhodecode.model import init_model
240 from rhodecode.model import init_model
216 from rhodecode.model.db import RhodeCodeUi
241 from rhodecode.model.db import RhodeCodeUi
217 from rhodecode.lib.utils import make_ui
242 from rhodecode.lib.utils import make_ui
218 from rhodecode.model.db import Repository
243 from rhodecode.model.db import Repository
219
244
220 path, ini_name = os.path.split(env['RHODECODE_CONFIG_FILE'])
245 path, ini_name = os.path.split(env['RHODECODE_CONFIG_FILE'])
221 conf = appconfig('config:%s' % ini_name, relative_to=path)
246 conf = appconfig('config:%s' % ini_name, relative_to=path)
222 load_environment(conf.global_conf, conf.local_conf)
247 load_environment(conf.global_conf, conf.local_conf)
223
248
224 engine = engine_from_config(conf, 'sqlalchemy.db1.')
249 engine = engine_from_config(conf, 'sqlalchemy.db1.')
225 init_model(engine)
250 init_model(engine)
226
251
227 baseui = make_ui('db')
252 baseui = make_ui('db')
228 # fix if it's not a bare repo
253 # fix if it's not a bare repo
229 if repo_path.endswith('.git'):
254 if repo_path.endswith('.git'):
230 repo_path = repo_path[:-4]
255 repo_path = repo_path[:-4]
231 repo = Repository.get_by_full_path(repo_path)
256 repo = Repository.get_by_full_path(repo_path)
232 _hooks = dict(baseui.configitems('hooks')) or {}
257 _hooks = dict(baseui.configitems('hooks')) or {}
233 # if push hook is enabled via web interface
258 # if push hook is enabled via web interface
234 if repo and _hooks.get(RhodeCodeUi.HOOK_PUSH):
259 if repo and _hooks.get(RhodeCodeUi.HOOK_PUSH):
235
260
236 extras = {
261 extras = {
237 'username': env['RHODECODE_USER'],
262 'username': env['RHODECODE_USER'],
238 'repository': repo.repo_name,
263 'repository': repo.repo_name,
239 'scm': 'git',
264 'scm': 'git',
240 'action': 'push',
265 'action': 'push',
241 'ip': env['RHODECODE_CONFIG_IP'],
266 'ip': env['RHODECODE_CONFIG_IP'],
242 }
267 }
243 for k, v in extras.items():
268 for k, v in extras.items():
244 baseui.setconfig('rhodecode_extras', k, v)
269 baseui.setconfig('rhodecode_extras', k, v)
245 repo = repo.scm_instance
270 repo = repo.scm_instance
246 repo.ui = baseui
271 repo.ui = baseui
247
272
248 rev_data = []
273 rev_data = []
249 for l in revs:
274 for l in revs:
250 old_rev, new_rev, ref = l.split(' ')
275 old_rev, new_rev, ref = l.split(' ')
251 _ref_data = ref.split('/')
276 _ref_data = ref.split('/')
252 if _ref_data[1] in ['tags', 'heads']:
277 if _ref_data[1] in ['tags', 'heads']:
253 rev_data.append({'old_rev': old_rev,
278 rev_data.append({'old_rev': old_rev,
254 'new_rev': new_rev,
279 'new_rev': new_rev,
255 'ref': ref,
280 'ref': ref,
256 'type': _ref_data[1],
281 'type': _ref_data[1],
257 'name': _ref_data[2].strip()})
282 'name': _ref_data[2].strip()})
258
283
259 git_revs = []
284 git_revs = []
260 for push_ref in rev_data:
285 for push_ref in rev_data:
261 _type = push_ref['type']
286 _type = push_ref['type']
262 if _type == 'heads':
287 if _type == 'heads':
263 if push_ref['old_rev'] == EmptyChangeset().raw_id:
288 if push_ref['old_rev'] == EmptyChangeset().raw_id:
264 cmd = "for-each-ref --format='%(refname)' 'refs/heads/*'"
289 cmd = "for-each-ref --format='%(refname)' 'refs/heads/*'"
265 heads = repo.run_git_command(cmd)[0]
290 heads = repo.run_git_command(cmd)[0]
266 heads = heads.replace(push_ref['ref'], '')
291 heads = heads.replace(push_ref['ref'], '')
267 heads = ' '.join(map(lambda c: c.strip('\n').strip(),
292 heads = ' '.join(map(lambda c: c.strip('\n').strip(),
268 heads.splitlines()))
293 heads.splitlines()))
269 cmd = (('log %(new_rev)s' % push_ref) +
294 cmd = (('log %(new_rev)s' % push_ref) +
270 ' --reverse --pretty=format:"%H" --not ' + heads)
295 ' --reverse --pretty=format:"%H" --not ' + heads)
271 else:
296 else:
272 cmd = (('log %(old_rev)s..%(new_rev)s' % push_ref) +
297 cmd = (('log %(old_rev)s..%(new_rev)s' % push_ref) +
273 ' --reverse --pretty=format:"%H"')
298 ' --reverse --pretty=format:"%H"')
274 git_revs += repo.run_git_command(cmd)[0].splitlines()
299 git_revs += repo.run_git_command(cmd)[0].splitlines()
275 elif _type == 'tags':
300 elif _type == 'tags':
276 git_revs += [push_ref['name']]
301 git_revs += [push_ref['name']]
277
302
278 log_push_action(baseui, repo, _git_revs=git_revs)
303 log_push_action(baseui, repo, _git_revs=git_revs)
@@ -1,306 +1,308 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.middleware.simplegit
3 rhodecode.lib.middleware.simplegit
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 SimpleGit middleware for handling git protocol request (push/clone etc.)
6 SimpleGit middleware for handling git protocol request (push/clone etc.)
7 It's implemented with basic auth function
7 It's implemented with basic auth function
8
8
9 :created_on: Apr 28, 2010
9 :created_on: Apr 28, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import os
27 import os
28 import re
28 import re
29 import logging
29 import logging
30 import traceback
30 import traceback
31
31
32 from dulwich import server as dulserver
32 from dulwich import server as dulserver
33 from dulwich.web import LimitedInputFilter, GunzipFilter
33 from dulwich.web import LimitedInputFilter, GunzipFilter
34
34
35
35
36 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
36 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
37
37
38 def handle(self):
38 def handle(self):
39 write = lambda x: self.proto.write_sideband(1, x)
39 write = lambda x: self.proto.write_sideband(1, x)
40
40
41 graph_walker = dulserver.ProtocolGraphWalker(self,
41 graph_walker = dulserver.ProtocolGraphWalker(self,
42 self.repo.object_store,
42 self.repo.object_store,
43 self.repo.get_peeled)
43 self.repo.get_peeled)
44 objects_iter = self.repo.fetch_objects(
44 objects_iter = self.repo.fetch_objects(
45 graph_walker.determine_wants, graph_walker, self.progress,
45 graph_walker.determine_wants, graph_walker, self.progress,
46 get_tagged=self.get_tagged)
46 get_tagged=self.get_tagged)
47
47
48 # Did the process short-circuit (e.g. in a stateless RPC call)? Note
48 # Did the process short-circuit (e.g. in a stateless RPC call)? Note
49 # that the client still expects a 0-object pack in most cases.
49 # that the client still expects a 0-object pack in most cases.
50 if objects_iter is None:
50 if objects_iter is None:
51 return
51 return
52
52
53 self.progress("counting objects: %d, done.\n" % len(objects_iter))
53 self.progress("counting objects: %d, done.\n" % len(objects_iter))
54 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
54 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
55 objects_iter)
55 objects_iter)
56 messages = []
56 messages = []
57 messages.append('thank you for using rhodecode')
57 messages.append('thank you for using rhodecode')
58
58
59 for msg in messages:
59 for msg in messages:
60 self.progress(msg + "\n")
60 self.progress(msg + "\n")
61 # we are done
61 # we are done
62 self.proto.write("0000")
62 self.proto.write("0000")
63
63
64
64
65 dulserver.DEFAULT_HANDLERS = {
65 dulserver.DEFAULT_HANDLERS = {
66 #git-ls-remote, git-clone, git-fetch and git-pull
66 #git-ls-remote, git-clone, git-fetch and git-pull
67 'git-upload-pack': SimpleGitUploadPackHandler,
67 'git-upload-pack': SimpleGitUploadPackHandler,
68 #git-push
68 #git-push
69 'git-receive-pack': dulserver.ReceivePackHandler,
69 'git-receive-pack': dulserver.ReceivePackHandler,
70 }
70 }
71
71
72 # not used for now until dulwich get's fixed
72 # not used for now until dulwich get's fixed
73 #from dulwich.repo import Repo
73 #from dulwich.repo import Repo
74 #from dulwich.web import make_wsgi_chain
74 #from dulwich.web import make_wsgi_chain
75
75
76 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
76 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
77 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
77 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
78 HTTPBadRequest, HTTPNotAcceptable
78 HTTPBadRequest, HTTPNotAcceptable
79
79
80 from rhodecode.lib.utils2 import safe_str
80 from rhodecode.lib.utils2 import safe_str
81 from rhodecode.lib.base import BaseVCSController
81 from rhodecode.lib.base import BaseVCSController
82 from rhodecode.lib.auth import get_container_username
82 from rhodecode.lib.auth import get_container_username
83 from rhodecode.lib.utils import is_valid_repo, make_ui
83 from rhodecode.lib.utils import is_valid_repo, make_ui
84 from rhodecode.lib.compat import json
84 from rhodecode.model.db import User, RhodeCodeUi
85 from rhodecode.model.db import User, RhodeCodeUi
85
86
86 log = logging.getLogger(__name__)
87 log = logging.getLogger(__name__)
87
88
88
89
89 GIT_PROTO_PAT = re.compile(r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)')
90 GIT_PROTO_PAT = re.compile(r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)')
90
91
91
92
92 def is_git(environ):
93 def is_git(environ):
93 path_info = environ['PATH_INFO']
94 path_info = environ['PATH_INFO']
94 isgit_path = GIT_PROTO_PAT.match(path_info)
95 isgit_path = GIT_PROTO_PAT.match(path_info)
95 log.debug('pathinfo: %s detected as GIT %s' % (
96 log.debug('pathinfo: %s detected as GIT %s' % (
96 path_info, isgit_path != None)
97 path_info, isgit_path != None)
97 )
98 )
98 return isgit_path
99 return isgit_path
99
100
100
101
101 class SimpleGit(BaseVCSController):
102 class SimpleGit(BaseVCSController):
102
103
103 def _handle_request(self, environ, start_response):
104 def _handle_request(self, environ, start_response):
104
105
105 if not is_git(environ):
106 if not is_git(environ):
106 return self.application(environ, start_response)
107 return self.application(environ, start_response)
107 if not self._check_ssl(environ, start_response):
108 if not self._check_ssl(environ, start_response):
108 return HTTPNotAcceptable('SSL REQUIRED !')(environ, start_response)
109 return HTTPNotAcceptable('SSL REQUIRED !')(environ, start_response)
109 ipaddr = self._get_ip_addr(environ)
110 ipaddr = self._get_ip_addr(environ)
110 username = None
111 username = None
111 self._git_first_op = False
112 self._git_first_op = False
112 # skip passing error to error controller
113 # skip passing error to error controller
113 environ['pylons.status_code_redirect'] = True
114 environ['pylons.status_code_redirect'] = True
114
115
115 #======================================================================
116 #======================================================================
116 # EXTRACT REPOSITORY NAME FROM ENV
117 # EXTRACT REPOSITORY NAME FROM ENV
117 #======================================================================
118 #======================================================================
118 try:
119 try:
119 repo_name = self.__get_repository(environ)
120 repo_name = self.__get_repository(environ)
120 log.debug('Extracted repo name is %s' % repo_name)
121 log.debug('Extracted repo name is %s' % repo_name)
121 except:
122 except:
122 return HTTPInternalServerError()(environ, start_response)
123 return HTTPInternalServerError()(environ, start_response)
123
124
124 # quick check if that dir exists...
125 # quick check if that dir exists...
125 if is_valid_repo(repo_name, self.basepath) is False:
126 if is_valid_repo(repo_name, self.basepath, 'git') is False:
126 return HTTPNotFound()(environ, start_response)
127 return HTTPNotFound()(environ, start_response)
127
128
128 #======================================================================
129 #======================================================================
129 # GET ACTION PULL or PUSH
130 # GET ACTION PULL or PUSH
130 #======================================================================
131 #======================================================================
131 action = self.__get_action(environ)
132 action = self.__get_action(environ)
132
133
133 #======================================================================
134 #======================================================================
134 # CHECK ANONYMOUS PERMISSION
135 # CHECK ANONYMOUS PERMISSION
135 #======================================================================
136 #======================================================================
136 if action in ['pull', 'push']:
137 if action in ['pull', 'push']:
137 anonymous_user = self.__get_user('default')
138 anonymous_user = self.__get_user('default')
138 username = anonymous_user.username
139 username = anonymous_user.username
139 anonymous_perm = self._check_permission(action, anonymous_user,
140 anonymous_perm = self._check_permission(action, anonymous_user,
140 repo_name)
141 repo_name)
141
142
142 if anonymous_perm is not True or anonymous_user.active is False:
143 if anonymous_perm is not True or anonymous_user.active is False:
143 if anonymous_perm is not True:
144 if anonymous_perm is not True:
144 log.debug('Not enough credentials to access this '
145 log.debug('Not enough credentials to access this '
145 'repository as anonymous user')
146 'repository as anonymous user')
146 if anonymous_user.active is False:
147 if anonymous_user.active is False:
147 log.debug('Anonymous access is disabled, running '
148 log.debug('Anonymous access is disabled, running '
148 'authentication')
149 'authentication')
149 #==============================================================
150 #==============================================================
150 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
151 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
151 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
152 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
152 #==============================================================
153 #==============================================================
153
154
154 # Attempting to retrieve username from the container
155 # Attempting to retrieve username from the container
155 username = get_container_username(environ, self.config)
156 username = get_container_username(environ, self.config)
156
157
157 # If not authenticated by the container, running basic auth
158 # If not authenticated by the container, running basic auth
158 if not username:
159 if not username:
159 self.authenticate.realm = \
160 self.authenticate.realm = \
160 safe_str(self.config['rhodecode_realm'])
161 safe_str(self.config['rhodecode_realm'])
161 result = self.authenticate(environ)
162 result = self.authenticate(environ)
162 if isinstance(result, str):
163 if isinstance(result, str):
163 AUTH_TYPE.update(environ, 'basic')
164 AUTH_TYPE.update(environ, 'basic')
164 REMOTE_USER.update(environ, result)
165 REMOTE_USER.update(environ, result)
165 username = result
166 username = result
166 else:
167 else:
167 return result.wsgi_application(environ, start_response)
168 return result.wsgi_application(environ, start_response)
168
169
169 #==============================================================
170 #==============================================================
170 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
171 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
171 #==============================================================
172 #==============================================================
172 try:
173 try:
173 user = self.__get_user(username)
174 user = self.__get_user(username)
174 if user is None or not user.active:
175 if user is None or not user.active:
175 return HTTPForbidden()(environ, start_response)
176 return HTTPForbidden()(environ, start_response)
176 username = user.username
177 username = user.username
177 except:
178 except:
178 log.error(traceback.format_exc())
179 log.error(traceback.format_exc())
179 return HTTPInternalServerError()(environ, start_response)
180 return HTTPInternalServerError()(environ, start_response)
180
181
181 #check permissions for this repository
182 #check permissions for this repository
182 perm = self._check_permission(action, user, repo_name)
183 perm = self._check_permission(action, user, repo_name)
183 if perm is not True:
184 if perm is not True:
184 return HTTPForbidden()(environ, start_response)
185 return HTTPForbidden()(environ, start_response)
185
186
186 extras = {
187 extras = {
187 'ip': ipaddr,
188 'ip': ipaddr,
188 'username': username,
189 'username': username,
189 'action': action,
190 'action': action,
190 'repository': repo_name,
191 'repository': repo_name,
191 'scm': 'git',
192 'scm': 'git',
192 }
193 }
193
194 # set the environ variables for this request
195 os.environ['RC_SCM_DATA'] = json.dumps(extras)
194 #===================================================================
196 #===================================================================
195 # GIT REQUEST HANDLING
197 # GIT REQUEST HANDLING
196 #===================================================================
198 #===================================================================
197 repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
199 repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
198 log.debug('Repository path is %s' % repo_path)
200 log.debug('Repository path is %s' % repo_path)
199
201
200 baseui = make_ui('db')
202 baseui = make_ui('db')
201 self.__inject_extras(repo_path, baseui, extras)
203 self.__inject_extras(repo_path, baseui, extras)
202
204
203 try:
205 try:
204 # invalidate cache on push
206 # invalidate cache on push
205 if action == 'push':
207 if action == 'push':
206 self._invalidate_cache(repo_name)
208 self._invalidate_cache(repo_name)
207 self._handle_githooks(repo_name, action, baseui, environ)
209 self._handle_githooks(repo_name, action, baseui, environ)
208
210
209 log.info('%s action on GIT repo "%s"' % (action, repo_name))
211 log.info('%s action on GIT repo "%s"' % (action, repo_name))
210 app = self.__make_app(repo_name, repo_path, username)
212 app = self.__make_app(repo_name, repo_path, username)
211 return app(environ, start_response)
213 return app(environ, start_response)
212 except Exception:
214 except Exception:
213 log.error(traceback.format_exc())
215 log.error(traceback.format_exc())
214 return HTTPInternalServerError()(environ, start_response)
216 return HTTPInternalServerError()(environ, start_response)
215
217
216 def __make_app(self, repo_name, repo_path, username):
218 def __make_app(self, repo_name, repo_path, username):
217 """
219 """
218 Make an wsgi application using dulserver
220 Make an wsgi application using dulserver
219
221
220 :param repo_name: name of the repository
222 :param repo_name: name of the repository
221 :param repo_path: full path to the repository
223 :param repo_path: full path to the repository
222 """
224 """
223
225
224 from rhodecode.lib.middleware.pygrack import make_wsgi_app
226 from rhodecode.lib.middleware.pygrack import make_wsgi_app
225 app = make_wsgi_app(
227 app = make_wsgi_app(
226 repo_root=safe_str(self.basepath),
228 repo_root=safe_str(self.basepath),
227 repo_name=repo_name,
229 repo_name=repo_name,
228 username=username,
230 username=username,
229 )
231 )
230 app = GunzipFilter(LimitedInputFilter(app))
232 app = GunzipFilter(LimitedInputFilter(app))
231 return app
233 return app
232
234
233 def __get_repository(self, environ):
235 def __get_repository(self, environ):
234 """
236 """
235 Get's repository name out of PATH_INFO header
237 Get's repository name out of PATH_INFO header
236
238
237 :param environ: environ where PATH_INFO is stored
239 :param environ: environ where PATH_INFO is stored
238 """
240 """
239 try:
241 try:
240 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
242 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
241 repo_name = GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1)
243 repo_name = GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1)
242 except:
244 except:
243 log.error(traceback.format_exc())
245 log.error(traceback.format_exc())
244 raise
246 raise
245
247
246 return repo_name
248 return repo_name
247
249
248 def __get_user(self, username):
250 def __get_user(self, username):
249 return User.get_by_username(username)
251 return User.get_by_username(username)
250
252
251 def __get_action(self, environ):
253 def __get_action(self, environ):
252 """
254 """
253 Maps git request commands into a pull or push command.
255 Maps git request commands into a pull or push command.
254
256
255 :param environ:
257 :param environ:
256 """
258 """
257 service = environ['QUERY_STRING'].split('=')
259 service = environ['QUERY_STRING'].split('=')
258
260
259 if len(service) > 1:
261 if len(service) > 1:
260 service_cmd = service[1]
262 service_cmd = service[1]
261 mapping = {
263 mapping = {
262 'git-receive-pack': 'push',
264 'git-receive-pack': 'push',
263 'git-upload-pack': 'pull',
265 'git-upload-pack': 'pull',
264 }
266 }
265 op = mapping[service_cmd]
267 op = mapping[service_cmd]
266 self._git_stored_op = op
268 self._git_stored_op = op
267 return op
269 return op
268 else:
270 else:
269 # try to fallback to stored variable as we don't know if the last
271 # try to fallback to stored variable as we don't know if the last
270 # operation is pull/push
272 # operation is pull/push
271 op = getattr(self, '_git_stored_op', 'pull')
273 op = getattr(self, '_git_stored_op', 'pull')
272 return op
274 return op
273
275
274 def _handle_githooks(self, repo_name, action, baseui, environ):
276 def _handle_githooks(self, repo_name, action, baseui, environ):
275 """
277 """
276 Handles pull action, push is handled by post-receive hook
278 Handles pull action, push is handled by post-receive hook
277 """
279 """
278 from rhodecode.lib.hooks import log_pull_action
280 from rhodecode.lib.hooks import log_pull_action
279 service = environ['QUERY_STRING'].split('=')
281 service = environ['QUERY_STRING'].split('=')
280 if len(service) < 2:
282 if len(service) < 2:
281 return
283 return
282
284
283 from rhodecode.model.db import Repository
285 from rhodecode.model.db import Repository
284 _repo = Repository.get_by_repo_name(repo_name)
286 _repo = Repository.get_by_repo_name(repo_name)
285 _repo = _repo.scm_instance
287 _repo = _repo.scm_instance
286 _repo._repo.ui = baseui
288 _repo._repo.ui = baseui
287
289
288 _hooks = dict(baseui.configitems('hooks')) or {}
290 _hooks = dict(baseui.configitems('hooks')) or {}
289 if action == 'pull' and _hooks.get(RhodeCodeUi.HOOK_PULL):
291 if action == 'pull' and _hooks.get(RhodeCodeUi.HOOK_PULL):
290 log_pull_action(ui=baseui, repo=_repo._repo)
292 log_pull_action(ui=baseui, repo=_repo._repo)
291
293
292 def __inject_extras(self, repo_path, baseui, extras={}):
294 def __inject_extras(self, repo_path, baseui, extras={}):
293 """
295 """
294 Injects some extra params into baseui instance
296 Injects some extra params into baseui instance
295
297
296 :param baseui: baseui instance
298 :param baseui: baseui instance
297 :param extras: dict with extra params to put into baseui
299 :param extras: dict with extra params to put into baseui
298 """
300 """
299
301
300 # make our hgweb quiet so it doesn't print output
302 # make our hgweb quiet so it doesn't print output
301 baseui.setconfig('ui', 'quiet', 'true')
303 baseui.setconfig('ui', 'quiet', 'true')
302
304
303 #inject some additional parameters that will be available in ui
305 #inject some additional parameters that will be available in ui
304 #for hooks
306 #for hooks
305 for k, v in extras.items():
307 for k, v in extras.items():
306 baseui.setconfig('rhodecode_extras', k, v)
308 baseui.setconfig('rhodecode_extras', k, v)
@@ -1,261 +1,263 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.middleware.simplehg
3 rhodecode.lib.middleware.simplehg
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 SimpleHG middleware for handling mercurial protocol request
6 SimpleHG middleware for handling mercurial protocol request
7 (push/clone etc.). It's implemented with basic auth function
7 (push/clone etc.). It's implemented with basic auth function
8
8
9 :created_on: Apr 28, 2010
9 :created_on: Apr 28, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import os
27 import os
28 import logging
28 import logging
29 import traceback
29 import traceback
30 import urllib
30 import urllib
31
31
32 from mercurial.error import RepoError
32 from mercurial.error import RepoError
33 from mercurial.hgweb import hgweb_mod
33 from mercurial.hgweb import hgweb_mod
34
34
35 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
35 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
36 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
36 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
37 HTTPBadRequest, HTTPNotAcceptable
37 HTTPBadRequest, HTTPNotAcceptable
38
38
39 from rhodecode.lib.utils2 import safe_str
39 from rhodecode.lib.utils2 import safe_str
40 from rhodecode.lib.base import BaseVCSController
40 from rhodecode.lib.base import BaseVCSController
41 from rhodecode.lib.auth import get_container_username
41 from rhodecode.lib.auth import get_container_username
42 from rhodecode.lib.utils import make_ui, is_valid_repo, ui_sections
42 from rhodecode.lib.utils import make_ui, is_valid_repo, ui_sections
43 from rhodecode.lib.compat import json
43 from rhodecode.model.db import User
44 from rhodecode.model.db import User
44
45
45
46
46 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
47
48
48
49
49 def is_mercurial(environ):
50 def is_mercurial(environ):
50 """
51 """
51 Returns True if request's target is mercurial server - header
52 Returns True if request's target is mercurial server - header
52 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
53 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
53 """
54 """
54 http_accept = environ.get('HTTP_ACCEPT')
55 http_accept = environ.get('HTTP_ACCEPT')
55 path_info = environ['PATH_INFO']
56 path_info = environ['PATH_INFO']
56 if http_accept and http_accept.startswith('application/mercurial'):
57 if http_accept and http_accept.startswith('application/mercurial'):
57 ishg_path = True
58 ishg_path = True
58 else:
59 else:
59 ishg_path = False
60 ishg_path = False
60
61
61 log.debug('pathinfo: %s detected as HG %s' % (
62 log.debug('pathinfo: %s detected as HG %s' % (
62 path_info, ishg_path)
63 path_info, ishg_path)
63 )
64 )
64 return ishg_path
65 return ishg_path
65
66
66
67
67 class SimpleHg(BaseVCSController):
68 class SimpleHg(BaseVCSController):
68
69
69 def _handle_request(self, environ, start_response):
70 def _handle_request(self, environ, start_response):
70 if not is_mercurial(environ):
71 if not is_mercurial(environ):
71 return self.application(environ, start_response)
72 return self.application(environ, start_response)
72 if not self._check_ssl(environ, start_response):
73 if not self._check_ssl(environ, start_response):
73 return HTTPNotAcceptable('SSL REQUIRED !')(environ, start_response)
74 return HTTPNotAcceptable('SSL REQUIRED !')(environ, start_response)
74
75
75 ipaddr = self._get_ip_addr(environ)
76 ipaddr = self._get_ip_addr(environ)
76 username = None
77 username = None
77 # skip passing error to error controller
78 # skip passing error to error controller
78 environ['pylons.status_code_redirect'] = True
79 environ['pylons.status_code_redirect'] = True
79
80
80 #======================================================================
81 #======================================================================
81 # EXTRACT REPOSITORY NAME FROM ENV
82 # EXTRACT REPOSITORY NAME FROM ENV
82 #======================================================================
83 #======================================================================
83 try:
84 try:
84 repo_name = environ['REPO_NAME'] = self.__get_repository(environ)
85 repo_name = environ['REPO_NAME'] = self.__get_repository(environ)
85 log.debug('Extracted repo name is %s' % repo_name)
86 log.debug('Extracted repo name is %s' % repo_name)
86 except:
87 except:
87 return HTTPInternalServerError()(environ, start_response)
88 return HTTPInternalServerError()(environ, start_response)
88
89
89 # quick check if that dir exists...
90 # quick check if that dir exists...
90 if is_valid_repo(repo_name, self.basepath) is False:
91 if is_valid_repo(repo_name, self.basepath, 'hg') is False:
91 return HTTPNotFound()(environ, start_response)
92 return HTTPNotFound()(environ, start_response)
92
93
93 #======================================================================
94 #======================================================================
94 # GET ACTION PULL or PUSH
95 # GET ACTION PULL or PUSH
95 #======================================================================
96 #======================================================================
96 action = self.__get_action(environ)
97 action = self.__get_action(environ)
97
98
98 #======================================================================
99 #======================================================================
99 # CHECK ANONYMOUS PERMISSION
100 # CHECK ANONYMOUS PERMISSION
100 #======================================================================
101 #======================================================================
101 if action in ['pull', 'push']:
102 if action in ['pull', 'push']:
102 anonymous_user = self.__get_user('default')
103 anonymous_user = self.__get_user('default')
103 username = anonymous_user.username
104 username = anonymous_user.username
104 anonymous_perm = self._check_permission(action, anonymous_user,
105 anonymous_perm = self._check_permission(action, anonymous_user,
105 repo_name)
106 repo_name)
106
107
107 if anonymous_perm is not True or anonymous_user.active is False:
108 if anonymous_perm is not True or anonymous_user.active is False:
108 if anonymous_perm is not True:
109 if anonymous_perm is not True:
109 log.debug('Not enough credentials to access this '
110 log.debug('Not enough credentials to access this '
110 'repository as anonymous user')
111 'repository as anonymous user')
111 if anonymous_user.active is False:
112 if anonymous_user.active is False:
112 log.debug('Anonymous access is disabled, running '
113 log.debug('Anonymous access is disabled, running '
113 'authentication')
114 'authentication')
114 #==============================================================
115 #==============================================================
115 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
116 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
116 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
117 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
117 #==============================================================
118 #==============================================================
118
119
119 # Attempting to retrieve username from the container
120 # Attempting to retrieve username from the container
120 username = get_container_username(environ, self.config)
121 username = get_container_username(environ, self.config)
121
122
122 # If not authenticated by the container, running basic auth
123 # If not authenticated by the container, running basic auth
123 if not username:
124 if not username:
124 self.authenticate.realm = \
125 self.authenticate.realm = \
125 safe_str(self.config['rhodecode_realm'])
126 safe_str(self.config['rhodecode_realm'])
126 result = self.authenticate(environ)
127 result = self.authenticate(environ)
127 if isinstance(result, str):
128 if isinstance(result, str):
128 AUTH_TYPE.update(environ, 'basic')
129 AUTH_TYPE.update(environ, 'basic')
129 REMOTE_USER.update(environ, result)
130 REMOTE_USER.update(environ, result)
130 username = result
131 username = result
131 else:
132 else:
132 return result.wsgi_application(environ, start_response)
133 return result.wsgi_application(environ, start_response)
133
134
134 #==============================================================
135 #==============================================================
135 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
136 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
136 #==============================================================
137 #==============================================================
137 try:
138 try:
138 user = self.__get_user(username)
139 user = self.__get_user(username)
139 if user is None or not user.active:
140 if user is None or not user.active:
140 return HTTPForbidden()(environ, start_response)
141 return HTTPForbidden()(environ, start_response)
141 username = user.username
142 username = user.username
142 except:
143 except:
143 log.error(traceback.format_exc())
144 log.error(traceback.format_exc())
144 return HTTPInternalServerError()(environ, start_response)
145 return HTTPInternalServerError()(environ, start_response)
145
146
146 #check permissions for this repository
147 #check permissions for this repository
147 perm = self._check_permission(action, user, repo_name)
148 perm = self._check_permission(action, user, repo_name)
148 if perm is not True:
149 if perm is not True:
149 return HTTPForbidden()(environ, start_response)
150 return HTTPForbidden()(environ, start_response)
150
151
151 # extras are injected into mercurial UI object and later available
152 # extras are injected into mercurial UI object and later available
152 # in hg hooks executed by rhodecode
153 # in hg hooks executed by rhodecode
153 extras = {
154 extras = {
154 'ip': ipaddr,
155 'ip': ipaddr,
155 'username': username,
156 'username': username,
156 'action': action,
157 'action': action,
157 'repository': repo_name,
158 'repository': repo_name,
158 'scm': 'hg',
159 'scm': 'hg',
159 }
160 }
160
161 # set the environ variables for this request
162 os.environ['RC_SCM_DATA'] = json.dumps(extras)
161 #======================================================================
163 #======================================================================
162 # MERCURIAL REQUEST HANDLING
164 # MERCURIAL REQUEST HANDLING
163 #======================================================================
165 #======================================================================
164 repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
166 repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
165 log.debug('Repository path is %s' % repo_path)
167 log.debug('Repository path is %s' % repo_path)
166
168
167 baseui = make_ui('db')
169 baseui = make_ui('db')
168 self.__inject_extras(repo_path, baseui, extras)
170 self.__inject_extras(repo_path, baseui, extras)
169
171
170 try:
172 try:
171 # invalidate cache on push
173 # invalidate cache on push
172 if action == 'push':
174 if action == 'push':
173 self._invalidate_cache(repo_name)
175 self._invalidate_cache(repo_name)
174 log.info('%s action on HG repo "%s"' % (action, repo_name))
176 log.info('%s action on HG repo "%s"' % (action, repo_name))
175 app = self.__make_app(repo_path, baseui, extras)
177 app = self.__make_app(repo_path, baseui, extras)
176 return app(environ, start_response)
178 return app(environ, start_response)
177 except RepoError, e:
179 except RepoError, e:
178 if str(e).find('not found') != -1:
180 if str(e).find('not found') != -1:
179 return HTTPNotFound()(environ, start_response)
181 return HTTPNotFound()(environ, start_response)
180 except Exception:
182 except Exception:
181 log.error(traceback.format_exc())
183 log.error(traceback.format_exc())
182 return HTTPInternalServerError()(environ, start_response)
184 return HTTPInternalServerError()(environ, start_response)
183
185
184 def __make_app(self, repo_name, baseui, extras):
186 def __make_app(self, repo_name, baseui, extras):
185 """
187 """
186 Make an wsgi application using hgweb, and inject generated baseui
188 Make an wsgi application using hgweb, and inject generated baseui
187 instance, additionally inject some extras into ui object
189 instance, additionally inject some extras into ui object
188 """
190 """
189 return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
191 return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
190
192
191 def __get_repository(self, environ):
193 def __get_repository(self, environ):
192 """
194 """
193 Get's repository name out of PATH_INFO header
195 Get's repository name out of PATH_INFO header
194
196
195 :param environ: environ where PATH_INFO is stored
197 :param environ: environ where PATH_INFO is stored
196 """
198 """
197 try:
199 try:
198 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
200 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
199 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
201 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
200 if repo_name.endswith('/'):
202 if repo_name.endswith('/'):
201 repo_name = repo_name.rstrip('/')
203 repo_name = repo_name.rstrip('/')
202 except:
204 except:
203 log.error(traceback.format_exc())
205 log.error(traceback.format_exc())
204 raise
206 raise
205
207
206 return repo_name
208 return repo_name
207
209
208 def __get_user(self, username):
210 def __get_user(self, username):
209 return User.get_by_username(username)
211 return User.get_by_username(username)
210
212
211 def __get_action(self, environ):
213 def __get_action(self, environ):
212 """
214 """
213 Maps mercurial request commands into a clone,pull or push command.
215 Maps mercurial request commands into a clone,pull or push command.
214 This should always return a valid command string
216 This should always return a valid command string
215
217
216 :param environ:
218 :param environ:
217 """
219 """
218 mapping = {'changegroup': 'pull',
220 mapping = {'changegroup': 'pull',
219 'changegroupsubset': 'pull',
221 'changegroupsubset': 'pull',
220 'stream_out': 'pull',
222 'stream_out': 'pull',
221 'listkeys': 'pull',
223 'listkeys': 'pull',
222 'unbundle': 'push',
224 'unbundle': 'push',
223 'pushkey': 'push', }
225 'pushkey': 'push', }
224 for qry in environ['QUERY_STRING'].split('&'):
226 for qry in environ['QUERY_STRING'].split('&'):
225 if qry.startswith('cmd'):
227 if qry.startswith('cmd'):
226 cmd = qry.split('=')[-1]
228 cmd = qry.split('=')[-1]
227 if cmd in mapping:
229 if cmd in mapping:
228 return mapping[cmd]
230 return mapping[cmd]
229
231
230 return 'pull'
232 return 'pull'
231
233
232 raise Exception('Unable to detect pull/push action !!'
234 raise Exception('Unable to detect pull/push action !!'
233 'Are you using non standard command or client ?')
235 'Are you using non standard command or client ?')
234
236
235 def __inject_extras(self, repo_path, baseui, extras={}):
237 def __inject_extras(self, repo_path, baseui, extras={}):
236 """
238 """
237 Injects some extra params into baseui instance
239 Injects some extra params into baseui instance
238
240
239 also overwrites global settings with those takes from local hgrc file
241 also overwrites global settings with those takes from local hgrc file
240
242
241 :param baseui: baseui instance
243 :param baseui: baseui instance
242 :param extras: dict with extra params to put into baseui
244 :param extras: dict with extra params to put into baseui
243 """
245 """
244
246
245 hgrc = os.path.join(repo_path, '.hg', 'hgrc')
247 hgrc = os.path.join(repo_path, '.hg', 'hgrc')
246
248
247 # make our hgweb quiet so it doesn't print output
249 # make our hgweb quiet so it doesn't print output
248 baseui.setconfig('ui', 'quiet', 'true')
250 baseui.setconfig('ui', 'quiet', 'true')
249
251
250 #inject some additional parameters that will be available in ui
252 #inject some additional parameters that will be available in ui
251 #for hooks
253 #for hooks
252 for k, v in extras.items():
254 for k, v in extras.items():
253 baseui.setconfig('rhodecode_extras', k, v)
255 baseui.setconfig('rhodecode_extras', k, v)
254
256
255 repoui = make_ui('file', hgrc, False)
257 repoui = make_ui('file', hgrc, False)
256
258
257 if repoui:
259 if repoui:
258 #overwrite our ui instance with the section from hgrc file
260 #overwrite our ui instance with the section from hgrc file
259 for section in ui_sections:
261 for section in ui_sections:
260 for k, v in repoui.configitems(section):
262 for k, v in repoui.configitems(section):
261 baseui.setconfig(section, k, v)
263 baseui.setconfig(section, k, v)
@@ -1,663 +1,668 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import re
27 import re
28 import logging
28 import logging
29 import datetime
29 import datetime
30 import traceback
30 import traceback
31 import paste
31 import paste
32 import beaker
32 import beaker
33 import tarfile
33 import tarfile
34 import shutil
34 import shutil
35 from os.path import abspath
35 from os.path import abspath
36 from os.path import dirname as dn, join as jn
36 from os.path import dirname as dn, join as jn
37
37
38 from paste.script.command import Command, BadCommand
38 from paste.script.command import Command, BadCommand
39
39
40 from mercurial import ui, config
40 from mercurial import ui, config
41
41
42 from webhelpers.text import collapse, remove_formatting, strip_tags
42 from webhelpers.text import collapse, remove_formatting, strip_tags
43
43
44 from rhodecode.lib.vcs import get_backend
44 from rhodecode.lib.vcs import get_backend
45 from rhodecode.lib.vcs.backends.base import BaseChangeset
45 from rhodecode.lib.vcs.backends.base import BaseChangeset
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47 from rhodecode.lib.vcs.utils.helpers import get_scm
47 from rhodecode.lib.vcs.utils.helpers import get_scm
48 from rhodecode.lib.vcs.exceptions import VCSError
48 from rhodecode.lib.vcs.exceptions import VCSError
49
49
50 from rhodecode.lib.caching_query import FromCache
50 from rhodecode.lib.caching_query import FromCache
51
51
52 from rhodecode.model import meta
52 from rhodecode.model import meta
53 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
53 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
54 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation
54 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation
55 from rhodecode.model.meta import Session
55 from rhodecode.model.meta import Session
56 from rhodecode.model.repos_group import ReposGroupModel
56 from rhodecode.model.repos_group import ReposGroupModel
57 from rhodecode.lib.utils2 import safe_str, safe_unicode
57 from rhodecode.lib.utils2 import safe_str, safe_unicode
58 from rhodecode.lib.vcs.utils.fakemod import create_module
58 from rhodecode.lib.vcs.utils.fakemod import create_module
59
59
60 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
61
61
62 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
62 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
63
63
64
64
65 def recursive_replace(str_, replace=' '):
65 def recursive_replace(str_, replace=' '):
66 """
66 """
67 Recursive replace of given sign to just one instance
67 Recursive replace of given sign to just one instance
68
68
69 :param str_: given string
69 :param str_: given string
70 :param replace: char to find and replace multiple instances
70 :param replace: char to find and replace multiple instances
71
71
72 Examples::
72 Examples::
73 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
73 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
74 'Mighty-Mighty-Bo-sstones'
74 'Mighty-Mighty-Bo-sstones'
75 """
75 """
76
76
77 if str_.find(replace * 2) == -1:
77 if str_.find(replace * 2) == -1:
78 return str_
78 return str_
79 else:
79 else:
80 str_ = str_.replace(replace * 2, replace)
80 str_ = str_.replace(replace * 2, replace)
81 return recursive_replace(str_, replace)
81 return recursive_replace(str_, replace)
82
82
83
83
84 def repo_name_slug(value):
84 def repo_name_slug(value):
85 """
85 """
86 Return slug of name of repository
86 Return slug of name of repository
87 This function is called on each creation/modification
87 This function is called on each creation/modification
88 of repository to prevent bad names in repo
88 of repository to prevent bad names in repo
89 """
89 """
90
90
91 slug = remove_formatting(value)
91 slug = remove_formatting(value)
92 slug = strip_tags(slug)
92 slug = strip_tags(slug)
93
93
94 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
94 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
95 slug = slug.replace(c, '-')
95 slug = slug.replace(c, '-')
96 slug = recursive_replace(slug, '-')
96 slug = recursive_replace(slug, '-')
97 slug = collapse(slug, '-')
97 slug = collapse(slug, '-')
98 return slug
98 return slug
99
99
100
100
101 def get_repo_slug(request):
101 def get_repo_slug(request):
102 _repo = request.environ['pylons.routes_dict'].get('repo_name')
102 _repo = request.environ['pylons.routes_dict'].get('repo_name')
103 if _repo:
103 if _repo:
104 _repo = _repo.rstrip('/')
104 _repo = _repo.rstrip('/')
105 return _repo
105 return _repo
106
106
107
107
108 def get_repos_group_slug(request):
108 def get_repos_group_slug(request):
109 _group = request.environ['pylons.routes_dict'].get('group_name')
109 _group = request.environ['pylons.routes_dict'].get('group_name')
110 if _group:
110 if _group:
111 _group = _group.rstrip('/')
111 _group = _group.rstrip('/')
112 return _group
112 return _group
113
113
114
114
115 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
115 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
116 """
116 """
117 Action logger for various actions made by users
117 Action logger for various actions made by users
118
118
119 :param user: user that made this action, can be a unique username string or
119 :param user: user that made this action, can be a unique username string or
120 object containing user_id attribute
120 object containing user_id attribute
121 :param action: action to log, should be on of predefined unique actions for
121 :param action: action to log, should be on of predefined unique actions for
122 easy translations
122 easy translations
123 :param repo: string name of repository or object containing repo_id,
123 :param repo: string name of repository or object containing repo_id,
124 that action was made on
124 that action was made on
125 :param ipaddr: optional ip address from what the action was made
125 :param ipaddr: optional ip address from what the action was made
126 :param sa: optional sqlalchemy session
126 :param sa: optional sqlalchemy session
127
127
128 """
128 """
129
129
130 if not sa:
130 if not sa:
131 sa = meta.Session()
131 sa = meta.Session()
132
132
133 try:
133 try:
134 if hasattr(user, 'user_id'):
134 if hasattr(user, 'user_id'):
135 user_obj = user
135 user_obj = user
136 elif isinstance(user, basestring):
136 elif isinstance(user, basestring):
137 user_obj = User.get_by_username(user)
137 user_obj = User.get_by_username(user)
138 else:
138 else:
139 raise Exception('You have to provide user object or username')
139 raise Exception('You have to provide user object or username')
140
140
141 if hasattr(repo, 'repo_id'):
141 if hasattr(repo, 'repo_id'):
142 repo_obj = Repository.get(repo.repo_id)
142 repo_obj = Repository.get(repo.repo_id)
143 repo_name = repo_obj.repo_name
143 repo_name = repo_obj.repo_name
144 elif isinstance(repo, basestring):
144 elif isinstance(repo, basestring):
145 repo_name = repo.lstrip('/')
145 repo_name = repo.lstrip('/')
146 repo_obj = Repository.get_by_repo_name(repo_name)
146 repo_obj = Repository.get_by_repo_name(repo_name)
147 else:
147 else:
148 repo_obj = None
148 repo_obj = None
149 repo_name = ''
149 repo_name = ''
150
150
151 user_log = UserLog()
151 user_log = UserLog()
152 user_log.user_id = user_obj.user_id
152 user_log.user_id = user_obj.user_id
153 user_log.action = safe_unicode(action)
153 user_log.action = safe_unicode(action)
154
154
155 user_log.repository = repo_obj
155 user_log.repository = repo_obj
156 user_log.repository_name = repo_name
156 user_log.repository_name = repo_name
157
157
158 user_log.action_date = datetime.datetime.now()
158 user_log.action_date = datetime.datetime.now()
159 user_log.user_ip = ipaddr
159 user_log.user_ip = ipaddr
160 sa.add(user_log)
160 sa.add(user_log)
161
161
162 log.info(
162 log.info(
163 'Adding user %s, action %s on %s' % (user_obj, action,
163 'Adding user %s, action %s on %s' % (user_obj, action,
164 safe_unicode(repo))
164 safe_unicode(repo))
165 )
165 )
166 if commit:
166 if commit:
167 sa.commit()
167 sa.commit()
168 except:
168 except:
169 log.error(traceback.format_exc())
169 log.error(traceback.format_exc())
170 raise
170 raise
171
171
172
172
173 def get_repos(path, recursive=False):
173 def get_repos(path, recursive=False):
174 """
174 """
175 Scans given path for repos and return (name,(type,path)) tuple
175 Scans given path for repos and return (name,(type,path)) tuple
176
176
177 :param path: path to scan for repositories
177 :param path: path to scan for repositories
178 :param recursive: recursive search and return names with subdirs in front
178 :param recursive: recursive search and return names with subdirs in front
179 """
179 """
180
180
181 # remove ending slash for better results
181 # remove ending slash for better results
182 path = path.rstrip(os.sep)
182 path = path.rstrip(os.sep)
183
183
184 def _get_repos(p):
184 def _get_repos(p):
185 if not os.access(p, os.W_OK):
185 if not os.access(p, os.W_OK):
186 return
186 return
187 for dirpath in os.listdir(p):
187 for dirpath in os.listdir(p):
188 if os.path.isfile(os.path.join(p, dirpath)):
188 if os.path.isfile(os.path.join(p, dirpath)):
189 continue
189 continue
190 cur_path = os.path.join(p, dirpath)
190 cur_path = os.path.join(p, dirpath)
191 try:
191 try:
192 scm_info = get_scm(cur_path)
192 scm_info = get_scm(cur_path)
193 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
193 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
194 except VCSError:
194 except VCSError:
195 if not recursive:
195 if not recursive:
196 continue
196 continue
197 #check if this dir containts other repos for recursive scan
197 #check if this dir containts other repos for recursive scan
198 rec_path = os.path.join(p, dirpath)
198 rec_path = os.path.join(p, dirpath)
199 if os.path.isdir(rec_path):
199 if os.path.isdir(rec_path):
200 for inner_scm in _get_repos(rec_path):
200 for inner_scm in _get_repos(rec_path):
201 yield inner_scm
201 yield inner_scm
202
202
203 return _get_repos(path)
203 return _get_repos(path)
204
204
205
205
206 def is_valid_repo(repo_name, base_path):
206 def is_valid_repo(repo_name, base_path, scm=None):
207 """
207 """
208 Returns True if given path is a valid repository False otherwise
208 Returns True if given path is a valid repository False otherwise.
209 If scm param is given also compare if given scm is the same as expected
210 from scm parameter
209
211
210 :param repo_name:
212 :param repo_name:
211 :param base_path:
213 :param base_path:
214 :param scm:
212
215
213 :return True: if given path is a valid repository
216 :return True: if given path is a valid repository
214 """
217 """
215 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
218 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
216
219
217 try:
220 try:
218 get_scm(full_path)
221 scm_ = get_scm(full_path)
222 if scm:
223 return scm_[0] == scm
219 return True
224 return True
220 except VCSError:
225 except VCSError:
221 return False
226 return False
222
227
223
228
224 def is_valid_repos_group(repos_group_name, base_path):
229 def is_valid_repos_group(repos_group_name, base_path):
225 """
230 """
226 Returns True if given path is a repos group False otherwise
231 Returns True if given path is a repos group False otherwise
227
232
228 :param repo_name:
233 :param repo_name:
229 :param base_path:
234 :param base_path:
230 """
235 """
231 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
236 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
232
237
233 # check if it's not a repo
238 # check if it's not a repo
234 if is_valid_repo(repos_group_name, base_path):
239 if is_valid_repo(repos_group_name, base_path):
235 return False
240 return False
236
241
237 try:
242 try:
238 # we need to check bare git repos at higher level
243 # we need to check bare git repos at higher level
239 # since we might match branches/hooks/info/objects or possible
244 # since we might match branches/hooks/info/objects or possible
240 # other things inside bare git repo
245 # other things inside bare git repo
241 get_scm(os.path.dirname(full_path))
246 get_scm(os.path.dirname(full_path))
242 return False
247 return False
243 except VCSError:
248 except VCSError:
244 pass
249 pass
245
250
246 # check if it's a valid path
251 # check if it's a valid path
247 if os.path.isdir(full_path):
252 if os.path.isdir(full_path):
248 return True
253 return True
249
254
250 return False
255 return False
251
256
252
257
253 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
258 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
254 while True:
259 while True:
255 ok = raw_input(prompt)
260 ok = raw_input(prompt)
256 if ok in ('y', 'ye', 'yes'):
261 if ok in ('y', 'ye', 'yes'):
257 return True
262 return True
258 if ok in ('n', 'no', 'nop', 'nope'):
263 if ok in ('n', 'no', 'nop', 'nope'):
259 return False
264 return False
260 retries = retries - 1
265 retries = retries - 1
261 if retries < 0:
266 if retries < 0:
262 raise IOError
267 raise IOError
263 print complaint
268 print complaint
264
269
265 #propagated from mercurial documentation
270 #propagated from mercurial documentation
266 ui_sections = ['alias', 'auth',
271 ui_sections = ['alias', 'auth',
267 'decode/encode', 'defaults',
272 'decode/encode', 'defaults',
268 'diff', 'email',
273 'diff', 'email',
269 'extensions', 'format',
274 'extensions', 'format',
270 'merge-patterns', 'merge-tools',
275 'merge-patterns', 'merge-tools',
271 'hooks', 'http_proxy',
276 'hooks', 'http_proxy',
272 'smtp', 'patch',
277 'smtp', 'patch',
273 'paths', 'profiling',
278 'paths', 'profiling',
274 'server', 'trusted',
279 'server', 'trusted',
275 'ui', 'web', ]
280 'ui', 'web', ]
276
281
277
282
278 def make_ui(read_from='file', path=None, checkpaths=True):
283 def make_ui(read_from='file', path=None, checkpaths=True):
279 """
284 """
280 A function that will read python rc files or database
285 A function that will read python rc files or database
281 and make an mercurial ui object from read options
286 and make an mercurial ui object from read options
282
287
283 :param path: path to mercurial config file
288 :param path: path to mercurial config file
284 :param checkpaths: check the path
289 :param checkpaths: check the path
285 :param read_from: read from 'file' or 'db'
290 :param read_from: read from 'file' or 'db'
286 """
291 """
287
292
288 baseui = ui.ui()
293 baseui = ui.ui()
289
294
290 # clean the baseui object
295 # clean the baseui object
291 baseui._ocfg = config.config()
296 baseui._ocfg = config.config()
292 baseui._ucfg = config.config()
297 baseui._ucfg = config.config()
293 baseui._tcfg = config.config()
298 baseui._tcfg = config.config()
294
299
295 if read_from == 'file':
300 if read_from == 'file':
296 if not os.path.isfile(path):
301 if not os.path.isfile(path):
297 log.debug('hgrc file is not present at %s skipping...' % path)
302 log.debug('hgrc file is not present at %s skipping...' % path)
298 return False
303 return False
299 log.debug('reading hgrc from %s' % path)
304 log.debug('reading hgrc from %s' % path)
300 cfg = config.config()
305 cfg = config.config()
301 cfg.read(path)
306 cfg.read(path)
302 for section in ui_sections:
307 for section in ui_sections:
303 for k, v in cfg.items(section):
308 for k, v in cfg.items(section):
304 log.debug('settings ui from file[%s]%s:%s' % (section, k, v))
309 log.debug('settings ui from file[%s]%s:%s' % (section, k, v))
305 baseui.setconfig(section, k, v)
310 baseui.setconfig(section, k, v)
306
311
307 elif read_from == 'db':
312 elif read_from == 'db':
308 sa = meta.Session()
313 sa = meta.Session()
309 ret = sa.query(RhodeCodeUi)\
314 ret = sa.query(RhodeCodeUi)\
310 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
315 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
311 .all()
316 .all()
312
317
313 hg_ui = ret
318 hg_ui = ret
314 for ui_ in hg_ui:
319 for ui_ in hg_ui:
315 if ui_.ui_active:
320 if ui_.ui_active:
316 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
321 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
317 ui_.ui_key, ui_.ui_value)
322 ui_.ui_key, ui_.ui_value)
318 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
323 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
319 if ui_.ui_key == 'push_ssl':
324 if ui_.ui_key == 'push_ssl':
320 # force set push_ssl requirement to False, rhodecode
325 # force set push_ssl requirement to False, rhodecode
321 # handles that
326 # handles that
322 baseui.setconfig(ui_.ui_section, ui_.ui_key, False)
327 baseui.setconfig(ui_.ui_section, ui_.ui_key, False)
323
328
324 meta.Session.remove()
329 meta.Session.remove()
325 return baseui
330 return baseui
326
331
327
332
328 def set_rhodecode_config(config):
333 def set_rhodecode_config(config):
329 """
334 """
330 Updates pylons config with new settings from database
335 Updates pylons config with new settings from database
331
336
332 :param config:
337 :param config:
333 """
338 """
334 hgsettings = RhodeCodeSetting.get_app_settings()
339 hgsettings = RhodeCodeSetting.get_app_settings()
335
340
336 for k, v in hgsettings.items():
341 for k, v in hgsettings.items():
337 config[k] = v
342 config[k] = v
338
343
339
344
340 def invalidate_cache(cache_key, *args):
345 def invalidate_cache(cache_key, *args):
341 """
346 """
342 Puts cache invalidation task into db for
347 Puts cache invalidation task into db for
343 further global cache invalidation
348 further global cache invalidation
344 """
349 """
345
350
346 from rhodecode.model.scm import ScmModel
351 from rhodecode.model.scm import ScmModel
347
352
348 if cache_key.startswith('get_repo_cached_'):
353 if cache_key.startswith('get_repo_cached_'):
349 name = cache_key.split('get_repo_cached_')[-1]
354 name = cache_key.split('get_repo_cached_')[-1]
350 ScmModel().mark_for_invalidation(name)
355 ScmModel().mark_for_invalidation(name)
351
356
352
357
353 def map_groups(path):
358 def map_groups(path):
354 """
359 """
355 Given a full path to a repository, create all nested groups that this
360 Given a full path to a repository, create all nested groups that this
356 repo is inside. This function creates parent-child relationships between
361 repo is inside. This function creates parent-child relationships between
357 groups and creates default perms for all new groups.
362 groups and creates default perms for all new groups.
358
363
359 :param paths: full path to repository
364 :param paths: full path to repository
360 """
365 """
361 sa = meta.Session()
366 sa = meta.Session()
362 groups = path.split(Repository.url_sep())
367 groups = path.split(Repository.url_sep())
363 parent = None
368 parent = None
364 group = None
369 group = None
365
370
366 # last element is repo in nested groups structure
371 # last element is repo in nested groups structure
367 groups = groups[:-1]
372 groups = groups[:-1]
368 rgm = ReposGroupModel(sa)
373 rgm = ReposGroupModel(sa)
369 for lvl, group_name in enumerate(groups):
374 for lvl, group_name in enumerate(groups):
370 group_name = '/'.join(groups[:lvl] + [group_name])
375 group_name = '/'.join(groups[:lvl] + [group_name])
371 group = RepoGroup.get_by_group_name(group_name)
376 group = RepoGroup.get_by_group_name(group_name)
372 desc = '%s group' % group_name
377 desc = '%s group' % group_name
373
378
374 # skip folders that are now removed repos
379 # skip folders that are now removed repos
375 if REMOVED_REPO_PAT.match(group_name):
380 if REMOVED_REPO_PAT.match(group_name):
376 break
381 break
377
382
378 if group is None:
383 if group is None:
379 log.debug('creating group level: %s group_name: %s' % (lvl,
384 log.debug('creating group level: %s group_name: %s' % (lvl,
380 group_name))
385 group_name))
381 group = RepoGroup(group_name, parent)
386 group = RepoGroup(group_name, parent)
382 group.group_description = desc
387 group.group_description = desc
383 sa.add(group)
388 sa.add(group)
384 rgm._create_default_perms(group)
389 rgm._create_default_perms(group)
385 sa.flush()
390 sa.flush()
386 parent = group
391 parent = group
387 return group
392 return group
388
393
389
394
390 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
395 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
391 install_git_hook=False):
396 install_git_hook=False):
392 """
397 """
393 maps all repos given in initial_repo_list, non existing repositories
398 maps all repos given in initial_repo_list, non existing repositories
394 are created, if remove_obsolete is True it also check for db entries
399 are created, if remove_obsolete is True it also check for db entries
395 that are not in initial_repo_list and removes them.
400 that are not in initial_repo_list and removes them.
396
401
397 :param initial_repo_list: list of repositories found by scanning methods
402 :param initial_repo_list: list of repositories found by scanning methods
398 :param remove_obsolete: check for obsolete entries in database
403 :param remove_obsolete: check for obsolete entries in database
399 :param install_git_hook: if this is True, also check and install githook
404 :param install_git_hook: if this is True, also check and install githook
400 for a repo if missing
405 for a repo if missing
401 """
406 """
402 from rhodecode.model.repo import RepoModel
407 from rhodecode.model.repo import RepoModel
403 from rhodecode.model.scm import ScmModel
408 from rhodecode.model.scm import ScmModel
404 sa = meta.Session()
409 sa = meta.Session()
405 rm = RepoModel()
410 rm = RepoModel()
406 user = sa.query(User).filter(User.admin == True).first()
411 user = sa.query(User).filter(User.admin == True).first()
407 if user is None:
412 if user is None:
408 raise Exception('Missing administrative account !')
413 raise Exception('Missing administrative account !')
409 added = []
414 added = []
410
415
411 for name, repo in initial_repo_list.items():
416 for name, repo in initial_repo_list.items():
412 group = map_groups(name)
417 group = map_groups(name)
413 db_repo = rm.get_by_repo_name(name)
418 db_repo = rm.get_by_repo_name(name)
414 # found repo that is on filesystem not in RhodeCode database
419 # found repo that is on filesystem not in RhodeCode database
415 if not db_repo:
420 if not db_repo:
416 log.info('repository %s not found creating now' % name)
421 log.info('repository %s not found creating now' % name)
417 added.append(name)
422 added.append(name)
418 desc = (repo.description
423 desc = (repo.description
419 if repo.description != 'unknown'
424 if repo.description != 'unknown'
420 else '%s repository' % name)
425 else '%s repository' % name)
421 new_repo = rm.create_repo(
426 new_repo = rm.create_repo(
422 repo_name=name,
427 repo_name=name,
423 repo_type=repo.alias,
428 repo_type=repo.alias,
424 description=desc,
429 description=desc,
425 repos_group=getattr(group, 'group_id', None),
430 repos_group=getattr(group, 'group_id', None),
426 owner=user,
431 owner=user,
427 just_db=True
432 just_db=True
428 )
433 )
429 # we added that repo just now, and make sure it has githook
434 # we added that repo just now, and make sure it has githook
430 # installed
435 # installed
431 if new_repo.repo_type == 'git':
436 if new_repo.repo_type == 'git':
432 ScmModel().install_git_hook(new_repo.scm_instance)
437 ScmModel().install_git_hook(new_repo.scm_instance)
433 elif install_git_hook:
438 elif install_git_hook:
434 if db_repo.repo_type == 'git':
439 if db_repo.repo_type == 'git':
435 ScmModel().install_git_hook(db_repo.scm_instance)
440 ScmModel().install_git_hook(db_repo.scm_instance)
436 sa.commit()
441 sa.commit()
437 removed = []
442 removed = []
438 if remove_obsolete:
443 if remove_obsolete:
439 # remove from database those repositories that are not in the filesystem
444 # remove from database those repositories that are not in the filesystem
440 for repo in sa.query(Repository).all():
445 for repo in sa.query(Repository).all():
441 if repo.repo_name not in initial_repo_list.keys():
446 if repo.repo_name not in initial_repo_list.keys():
442 log.debug("Removing non existing repository found in db `%s`" %
447 log.debug("Removing non existing repository found in db `%s`" %
443 repo.repo_name)
448 repo.repo_name)
444 try:
449 try:
445 sa.delete(repo)
450 sa.delete(repo)
446 sa.commit()
451 sa.commit()
447 removed.append(repo.repo_name)
452 removed.append(repo.repo_name)
448 except:
453 except:
449 #don't hold further removals on error
454 #don't hold further removals on error
450 log.error(traceback.format_exc())
455 log.error(traceback.format_exc())
451 sa.rollback()
456 sa.rollback()
452
457
453 # clear cache keys
458 # clear cache keys
454 log.debug("Clearing cache keys now...")
459 log.debug("Clearing cache keys now...")
455 CacheInvalidation.clear_cache()
460 CacheInvalidation.clear_cache()
456 sa.commit()
461 sa.commit()
457 return added, removed
462 return added, removed
458
463
459
464
460 # set cache regions for beaker so celery can utilise it
465 # set cache regions for beaker so celery can utilise it
461 def add_cache(settings):
466 def add_cache(settings):
462 cache_settings = {'regions': None}
467 cache_settings = {'regions': None}
463 for key in settings.keys():
468 for key in settings.keys():
464 for prefix in ['beaker.cache.', 'cache.']:
469 for prefix in ['beaker.cache.', 'cache.']:
465 if key.startswith(prefix):
470 if key.startswith(prefix):
466 name = key.split(prefix)[1].strip()
471 name = key.split(prefix)[1].strip()
467 cache_settings[name] = settings[key].strip()
472 cache_settings[name] = settings[key].strip()
468 if cache_settings['regions']:
473 if cache_settings['regions']:
469 for region in cache_settings['regions'].split(','):
474 for region in cache_settings['regions'].split(','):
470 region = region.strip()
475 region = region.strip()
471 region_settings = {}
476 region_settings = {}
472 for key, value in cache_settings.items():
477 for key, value in cache_settings.items():
473 if key.startswith(region):
478 if key.startswith(region):
474 region_settings[key.split('.')[1]] = value
479 region_settings[key.split('.')[1]] = value
475 region_settings['expire'] = int(region_settings.get('expire',
480 region_settings['expire'] = int(region_settings.get('expire',
476 60))
481 60))
477 region_settings.setdefault('lock_dir',
482 region_settings.setdefault('lock_dir',
478 cache_settings.get('lock_dir'))
483 cache_settings.get('lock_dir'))
479 region_settings.setdefault('data_dir',
484 region_settings.setdefault('data_dir',
480 cache_settings.get('data_dir'))
485 cache_settings.get('data_dir'))
481
486
482 if 'type' not in region_settings:
487 if 'type' not in region_settings:
483 region_settings['type'] = cache_settings.get('type',
488 region_settings['type'] = cache_settings.get('type',
484 'memory')
489 'memory')
485 beaker.cache.cache_regions[region] = region_settings
490 beaker.cache.cache_regions[region] = region_settings
486
491
487
492
488 def load_rcextensions(root_path):
493 def load_rcextensions(root_path):
489 import rhodecode
494 import rhodecode
490 from rhodecode.config import conf
495 from rhodecode.config import conf
491
496
492 path = os.path.join(root_path, 'rcextensions', '__init__.py')
497 path = os.path.join(root_path, 'rcextensions', '__init__.py')
493 if os.path.isfile(path):
498 if os.path.isfile(path):
494 rcext = create_module('rc', path)
499 rcext = create_module('rc', path)
495 EXT = rhodecode.EXTENSIONS = rcext
500 EXT = rhodecode.EXTENSIONS = rcext
496 log.debug('Found rcextensions now loading %s...' % rcext)
501 log.debug('Found rcextensions now loading %s...' % rcext)
497
502
498 # Additional mappings that are not present in the pygments lexers
503 # Additional mappings that are not present in the pygments lexers
499 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
504 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
500
505
501 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
506 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
502
507
503 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
508 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
504 log.debug('settings custom INDEX_EXTENSIONS')
509 log.debug('settings custom INDEX_EXTENSIONS')
505 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
510 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
506
511
507 #ADDITIONAL MAPPINGS
512 #ADDITIONAL MAPPINGS
508 log.debug('adding extra into INDEX_EXTENSIONS')
513 log.debug('adding extra into INDEX_EXTENSIONS')
509 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
514 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
510
515
511
516
512 #==============================================================================
517 #==============================================================================
513 # TEST FUNCTIONS AND CREATORS
518 # TEST FUNCTIONS AND CREATORS
514 #==============================================================================
519 #==============================================================================
515 def create_test_index(repo_location, config, full_index):
520 def create_test_index(repo_location, config, full_index):
516 """
521 """
517 Makes default test index
522 Makes default test index
518
523
519 :param config: test config
524 :param config: test config
520 :param full_index:
525 :param full_index:
521 """
526 """
522
527
523 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
528 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
524 from rhodecode.lib.pidlock import DaemonLock, LockHeld
529 from rhodecode.lib.pidlock import DaemonLock, LockHeld
525
530
526 repo_location = repo_location
531 repo_location = repo_location
527
532
528 index_location = os.path.join(config['app_conf']['index_dir'])
533 index_location = os.path.join(config['app_conf']['index_dir'])
529 if not os.path.exists(index_location):
534 if not os.path.exists(index_location):
530 os.makedirs(index_location)
535 os.makedirs(index_location)
531
536
532 try:
537 try:
533 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
538 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
534 WhooshIndexingDaemon(index_location=index_location,
539 WhooshIndexingDaemon(index_location=index_location,
535 repo_location=repo_location)\
540 repo_location=repo_location)\
536 .run(full_index=full_index)
541 .run(full_index=full_index)
537 l.release()
542 l.release()
538 except LockHeld:
543 except LockHeld:
539 pass
544 pass
540
545
541
546
542 def create_test_env(repos_test_path, config):
547 def create_test_env(repos_test_path, config):
543 """
548 """
544 Makes a fresh database and
549 Makes a fresh database and
545 install test repository into tmp dir
550 install test repository into tmp dir
546 """
551 """
547 from rhodecode.lib.db_manage import DbManage
552 from rhodecode.lib.db_manage import DbManage
548 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
553 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
549
554
550 # PART ONE create db
555 # PART ONE create db
551 dbconf = config['sqlalchemy.db1.url']
556 dbconf = config['sqlalchemy.db1.url']
552 log.debug('making test db %s' % dbconf)
557 log.debug('making test db %s' % dbconf)
553
558
554 # create test dir if it doesn't exist
559 # create test dir if it doesn't exist
555 if not os.path.isdir(repos_test_path):
560 if not os.path.isdir(repos_test_path):
556 log.debug('Creating testdir %s' % repos_test_path)
561 log.debug('Creating testdir %s' % repos_test_path)
557 os.makedirs(repos_test_path)
562 os.makedirs(repos_test_path)
558
563
559 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
564 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
560 tests=True)
565 tests=True)
561 dbmanage.create_tables(override=True)
566 dbmanage.create_tables(override=True)
562 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
567 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
563 dbmanage.create_default_user()
568 dbmanage.create_default_user()
564 dbmanage.admin_prompt()
569 dbmanage.admin_prompt()
565 dbmanage.create_permissions()
570 dbmanage.create_permissions()
566 dbmanage.populate_default_permissions()
571 dbmanage.populate_default_permissions()
567 Session().commit()
572 Session().commit()
568 # PART TWO make test repo
573 # PART TWO make test repo
569 log.debug('making test vcs repositories')
574 log.debug('making test vcs repositories')
570
575
571 idx_path = config['app_conf']['index_dir']
576 idx_path = config['app_conf']['index_dir']
572 data_path = config['app_conf']['cache_dir']
577 data_path = config['app_conf']['cache_dir']
573
578
574 #clean index and data
579 #clean index and data
575 if idx_path and os.path.exists(idx_path):
580 if idx_path and os.path.exists(idx_path):
576 log.debug('remove %s' % idx_path)
581 log.debug('remove %s' % idx_path)
577 shutil.rmtree(idx_path)
582 shutil.rmtree(idx_path)
578
583
579 if data_path and os.path.exists(data_path):
584 if data_path and os.path.exists(data_path):
580 log.debug('remove %s' % data_path)
585 log.debug('remove %s' % data_path)
581 shutil.rmtree(data_path)
586 shutil.rmtree(data_path)
582
587
583 #CREATE DEFAULT TEST REPOS
588 #CREATE DEFAULT TEST REPOS
584 cur_dir = dn(dn(abspath(__file__)))
589 cur_dir = dn(dn(abspath(__file__)))
585 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
590 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
586 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
591 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
587 tar.close()
592 tar.close()
588
593
589 cur_dir = dn(dn(abspath(__file__)))
594 cur_dir = dn(dn(abspath(__file__)))
590 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
595 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
591 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
596 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
592 tar.close()
597 tar.close()
593
598
594 #LOAD VCS test stuff
599 #LOAD VCS test stuff
595 from rhodecode.tests.vcs import setup_package
600 from rhodecode.tests.vcs import setup_package
596 setup_package()
601 setup_package()
597
602
598
603
599 #==============================================================================
604 #==============================================================================
600 # PASTER COMMANDS
605 # PASTER COMMANDS
601 #==============================================================================
606 #==============================================================================
602 class BasePasterCommand(Command):
607 class BasePasterCommand(Command):
603 """
608 """
604 Abstract Base Class for paster commands.
609 Abstract Base Class for paster commands.
605
610
606 The celery commands are somewhat aggressive about loading
611 The celery commands are somewhat aggressive about loading
607 celery.conf, and since our module sets the `CELERY_LOADER`
612 celery.conf, and since our module sets the `CELERY_LOADER`
608 environment variable to our loader, we have to bootstrap a bit and
613 environment variable to our loader, we have to bootstrap a bit and
609 make sure we've had a chance to load the pylons config off of the
614 make sure we've had a chance to load the pylons config off of the
610 command line, otherwise everything fails.
615 command line, otherwise everything fails.
611 """
616 """
612 min_args = 1
617 min_args = 1
613 min_args_error = "Please provide a paster config file as an argument."
618 min_args_error = "Please provide a paster config file as an argument."
614 takes_config_file = 1
619 takes_config_file = 1
615 requires_config_file = True
620 requires_config_file = True
616
621
617 def notify_msg(self, msg, log=False):
622 def notify_msg(self, msg, log=False):
618 """Make a notification to user, additionally if logger is passed
623 """Make a notification to user, additionally if logger is passed
619 it logs this action using given logger
624 it logs this action using given logger
620
625
621 :param msg: message that will be printed to user
626 :param msg: message that will be printed to user
622 :param log: logging instance, to use to additionally log this message
627 :param log: logging instance, to use to additionally log this message
623
628
624 """
629 """
625 if log and isinstance(log, logging):
630 if log and isinstance(log, logging):
626 log(msg)
631 log(msg)
627
632
628 def run(self, args):
633 def run(self, args):
629 """
634 """
630 Overrides Command.run
635 Overrides Command.run
631
636
632 Checks for a config file argument and loads it.
637 Checks for a config file argument and loads it.
633 """
638 """
634 if len(args) < self.min_args:
639 if len(args) < self.min_args:
635 raise BadCommand(
640 raise BadCommand(
636 self.min_args_error % {'min_args': self.min_args,
641 self.min_args_error % {'min_args': self.min_args,
637 'actual_args': len(args)})
642 'actual_args': len(args)})
638
643
639 # Decrement because we're going to lob off the first argument.
644 # Decrement because we're going to lob off the first argument.
640 # @@ This is hacky
645 # @@ This is hacky
641 self.min_args -= 1
646 self.min_args -= 1
642 self.bootstrap_config(args[0])
647 self.bootstrap_config(args[0])
643 self.update_parser()
648 self.update_parser()
644 return super(BasePasterCommand, self).run(args[1:])
649 return super(BasePasterCommand, self).run(args[1:])
645
650
646 def update_parser(self):
651 def update_parser(self):
647 """
652 """
648 Abstract method. Allows for the class's parser to be updated
653 Abstract method. Allows for the class's parser to be updated
649 before the superclass's `run` method is called. Necessary to
654 before the superclass's `run` method is called. Necessary to
650 allow options/arguments to be passed through to the underlying
655 allow options/arguments to be passed through to the underlying
651 celery command.
656 celery command.
652 """
657 """
653 raise NotImplementedError("Abstract Method.")
658 raise NotImplementedError("Abstract Method.")
654
659
655 def bootstrap_config(self, conf):
660 def bootstrap_config(self, conf):
656 """
661 """
657 Loads the pylons configuration.
662 Loads the pylons configuration.
658 """
663 """
659 from pylons import config as pylonsconfig
664 from pylons import config as pylonsconfig
660
665
661 self.path_to_ini_file = os.path.realpath(conf)
666 self.path_to_ini_file = os.path.realpath(conf)
662 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
667 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
663 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
668 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
General Comments 0
You need to be logged in to leave comments. Login now