##// END OF EJS Templates
There's no need to use special env variable for config file, it's already passed in in extras now
marcink -
r2870:cbf0775f beta
parent child Browse files
Show More
@@ -1,382 +1,382 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 from rhodecode.lib.compat import json
37 from rhodecode.lib.exceptions import HTTPLockedRC
37 from rhodecode.lib.exceptions import HTTPLockedRC
38 from rhodecode.lib.utils2 import safe_str
38 from rhodecode.lib.utils2 import safe_str
39 from rhodecode.model.db import Repository, User
39 from rhodecode.model.db import Repository, User
40
40
41 def _get_scm_size(alias, root_path):
41 def _get_scm_size(alias, root_path):
42
42
43 if not alias.startswith('.'):
43 if not alias.startswith('.'):
44 alias += '.'
44 alias += '.'
45
45
46 size_scm, size_root = 0, 0
46 size_scm, size_root = 0, 0
47 for path, dirs, files in os.walk(root_path):
47 for path, dirs, files in os.walk(root_path):
48 if path.find(alias) != -1:
48 if path.find(alias) != -1:
49 for f in files:
49 for f in files:
50 try:
50 try:
51 size_scm += os.path.getsize(os.path.join(path, f))
51 size_scm += os.path.getsize(os.path.join(path, f))
52 except OSError:
52 except OSError:
53 pass
53 pass
54 else:
54 else:
55 for f in files:
55 for f in files:
56 try:
56 try:
57 size_root += os.path.getsize(os.path.join(path, f))
57 size_root += os.path.getsize(os.path.join(path, f))
58 except OSError:
58 except OSError:
59 pass
59 pass
60
60
61 size_scm_f = h.format_byte_size(size_scm)
61 size_scm_f = h.format_byte_size(size_scm)
62 size_root_f = h.format_byte_size(size_root)
62 size_root_f = h.format_byte_size(size_root)
63 size_total_f = h.format_byte_size(size_root + size_scm)
63 size_total_f = h.format_byte_size(size_root + size_scm)
64
64
65 return size_scm_f, size_root_f, size_total_f
65 return size_scm_f, size_root_f, size_total_f
66
66
67
67
68 def repo_size(ui, repo, hooktype=None, **kwargs):
68 def repo_size(ui, repo, hooktype=None, **kwargs):
69 """
69 """
70 Presents size of repository after push
70 Presents size of repository after push
71
71
72 :param ui:
72 :param ui:
73 :param repo:
73 :param repo:
74 :param hooktype:
74 :param hooktype:
75 """
75 """
76
76
77 size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', repo.root)
77 size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', repo.root)
78
78
79 last_cs = repo[len(repo) - 1]
79 last_cs = repo[len(repo) - 1]
80
80
81 msg = ('Repository size .hg:%s repo:%s total:%s\n'
81 msg = ('Repository size .hg:%s repo:%s total:%s\n'
82 'Last revision is now r%s:%s\n') % (
82 'Last revision is now r%s:%s\n') % (
83 size_hg_f, size_root_f, size_total_f, last_cs.rev(), last_cs.hex()[:12]
83 size_hg_f, size_root_f, size_total_f, last_cs.rev(), last_cs.hex()[:12]
84 )
84 )
85
85
86 sys.stdout.write(msg)
86 sys.stdout.write(msg)
87
87
88
88
89 def pre_push(ui, repo, **kwargs):
89 def pre_push(ui, repo, **kwargs):
90 # pre push function, currently used to ban pushing when
90 # pre push function, currently used to ban pushing when
91 # repository is locked
91 # repository is locked
92 try:
92 try:
93 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
93 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
94 except:
94 except:
95 rc_extras = {}
95 rc_extras = {}
96 extras = dict(repo.ui.configitems('rhodecode_extras'))
96 extras = dict(repo.ui.configitems('rhodecode_extras'))
97
97
98 if 'username' in extras:
98 if 'username' in extras:
99 username = extras['username']
99 username = extras['username']
100 repository = extras['repository']
100 repository = extras['repository']
101 scm = extras['scm']
101 scm = extras['scm']
102 locked_by = extras['locked_by']
102 locked_by = extras['locked_by']
103 elif 'username' in rc_extras:
103 elif 'username' in rc_extras:
104 username = rc_extras['username']
104 username = rc_extras['username']
105 repository = rc_extras['repository']
105 repository = rc_extras['repository']
106 scm = rc_extras['scm']
106 scm = rc_extras['scm']
107 locked_by = rc_extras['locked_by']
107 locked_by = rc_extras['locked_by']
108 else:
108 else:
109 raise Exception('Missing data in repo.ui and os.environ')
109 raise Exception('Missing data in repo.ui and os.environ')
110
110
111 usr = User.get_by_username(username)
111 usr = User.get_by_username(username)
112 if locked_by[0] and usr.user_id != int(locked_by[0]):
112 if locked_by[0] and usr.user_id != int(locked_by[0]):
113 locked_by = User.get(locked_by[0]).username
113 locked_by = User.get(locked_by[0]).username
114 raise HTTPLockedRC(repository, locked_by)
114 raise HTTPLockedRC(repository, locked_by)
115
115
116
116
117 def pre_pull(ui, repo, **kwargs):
117 def pre_pull(ui, repo, **kwargs):
118 # pre push function, currently used to ban pushing when
118 # pre push function, currently used to ban pushing when
119 # repository is locked
119 # repository is locked
120 try:
120 try:
121 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
121 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
122 except:
122 except:
123 rc_extras = {}
123 rc_extras = {}
124 extras = dict(repo.ui.configitems('rhodecode_extras'))
124 extras = dict(repo.ui.configitems('rhodecode_extras'))
125 if 'username' in extras:
125 if 'username' in extras:
126 username = extras['username']
126 username = extras['username']
127 repository = extras['repository']
127 repository = extras['repository']
128 scm = extras['scm']
128 scm = extras['scm']
129 locked_by = extras['locked_by']
129 locked_by = extras['locked_by']
130 elif 'username' in rc_extras:
130 elif 'username' in rc_extras:
131 username = rc_extras['username']
131 username = rc_extras['username']
132 repository = rc_extras['repository']
132 repository = rc_extras['repository']
133 scm = rc_extras['scm']
133 scm = rc_extras['scm']
134 locked_by = rc_extras['locked_by']
134 locked_by = rc_extras['locked_by']
135 else:
135 else:
136 raise Exception('Missing data in repo.ui and os.environ')
136 raise Exception('Missing data in repo.ui and os.environ')
137
137
138 if locked_by[0]:
138 if locked_by[0]:
139 locked_by = User.get(locked_by[0]).username
139 locked_by = User.get(locked_by[0]).username
140 raise HTTPLockedRC(repository, locked_by)
140 raise HTTPLockedRC(repository, locked_by)
141
141
142
142
143 def log_pull_action(ui, repo, **kwargs):
143 def log_pull_action(ui, repo, **kwargs):
144 """
144 """
145 Logs user last pull action
145 Logs user last pull action
146
146
147 :param ui:
147 :param ui:
148 :param repo:
148 :param repo:
149 """
149 """
150 try:
150 try:
151 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
151 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
152 except:
152 except:
153 rc_extras = {}
153 rc_extras = {}
154 extras = dict(repo.ui.configitems('rhodecode_extras'))
154 extras = dict(repo.ui.configitems('rhodecode_extras'))
155 if 'username' in extras:
155 if 'username' in extras:
156 username = extras['username']
156 username = extras['username']
157 repository = extras['repository']
157 repository = extras['repository']
158 scm = extras['scm']
158 scm = extras['scm']
159 make_lock = extras['make_lock']
159 make_lock = extras['make_lock']
160 elif 'username' in rc_extras:
160 elif 'username' in rc_extras:
161 username = rc_extras['username']
161 username = rc_extras['username']
162 repository = rc_extras['repository']
162 repository = rc_extras['repository']
163 scm = rc_extras['scm']
163 scm = rc_extras['scm']
164 make_lock = rc_extras['make_lock']
164 make_lock = rc_extras['make_lock']
165 else:
165 else:
166 raise Exception('Missing data in repo.ui and os.environ')
166 raise Exception('Missing data in repo.ui and os.environ')
167 user = User.get_by_username(username)
167 user = User.get_by_username(username)
168 action = 'pull'
168 action = 'pull'
169 action_logger(user, action, repository, extras['ip'], commit=True)
169 action_logger(user, action, repository, extras['ip'], commit=True)
170 # extension hook call
170 # extension hook call
171 from rhodecode import EXTENSIONS
171 from rhodecode import EXTENSIONS
172 callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
172 callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
173
173
174 if isfunction(callback):
174 if isfunction(callback):
175 kw = {}
175 kw = {}
176 kw.update(extras)
176 kw.update(extras)
177 callback(**kw)
177 callback(**kw)
178
178
179 if make_lock is True:
179 if make_lock is True:
180 Repository.lock(Repository.get_by_repo_name(repository), user.user_id)
180 Repository.lock(Repository.get_by_repo_name(repository), user.user_id)
181 #msg = 'Made lock on repo `%s`' % repository
181 #msg = 'Made lock on repo `%s`' % repository
182 #sys.stdout.write(msg)
182 #sys.stdout.write(msg)
183
183
184 return 0
184 return 0
185
185
186
186
187 def log_push_action(ui, repo, **kwargs):
187 def log_push_action(ui, repo, **kwargs):
188 """
188 """
189 Maps user last push action to new changeset id, from mercurial
189 Maps user last push action to new changeset id, from mercurial
190
190
191 :param ui:
191 :param ui:
192 :param repo: repo object containing the `ui` object
192 :param repo: repo object containing the `ui` object
193 """
193 """
194
194
195 try:
195 try:
196 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
196 rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
197 except:
197 except:
198 rc_extras = {}
198 rc_extras = {}
199
199
200 extras = dict(repo.ui.configitems('rhodecode_extras'))
200 extras = dict(repo.ui.configitems('rhodecode_extras'))
201 if 'username' in extras:
201 if 'username' in extras:
202 username = extras['username']
202 username = extras['username']
203 repository = extras['repository']
203 repository = extras['repository']
204 scm = extras['scm']
204 scm = extras['scm']
205 make_lock = extras['make_lock']
205 make_lock = extras['make_lock']
206 elif 'username' in rc_extras:
206 elif 'username' in rc_extras:
207 username = rc_extras['username']
207 username = rc_extras['username']
208 repository = rc_extras['repository']
208 repository = rc_extras['repository']
209 scm = rc_extras['scm']
209 scm = rc_extras['scm']
210 make_lock = rc_extras['make_lock']
210 make_lock = rc_extras['make_lock']
211 else:
211 else:
212 raise Exception('Missing data in repo.ui and os.environ')
212 raise Exception('Missing data in repo.ui and os.environ')
213
213
214 action = 'push' + ':%s'
214 action = 'push' + ':%s'
215
215
216 if scm == 'hg':
216 if scm == 'hg':
217 node = kwargs['node']
217 node = kwargs['node']
218
218
219 def get_revs(repo, rev_opt):
219 def get_revs(repo, rev_opt):
220 if rev_opt:
220 if rev_opt:
221 revs = revrange(repo, rev_opt)
221 revs = revrange(repo, rev_opt)
222
222
223 if len(revs) == 0:
223 if len(revs) == 0:
224 return (nullrev, nullrev)
224 return (nullrev, nullrev)
225 return (max(revs), min(revs))
225 return (max(revs), min(revs))
226 else:
226 else:
227 return (len(repo) - 1, 0)
227 return (len(repo) - 1, 0)
228
228
229 stop, start = get_revs(repo, [node + ':'])
229 stop, start = get_revs(repo, [node + ':'])
230 h = binascii.hexlify
230 h = binascii.hexlify
231 revs = [h(repo[r].node()) for r in xrange(start, stop + 1)]
231 revs = [h(repo[r].node()) for r in xrange(start, stop + 1)]
232 elif scm == 'git':
232 elif scm == 'git':
233 revs = kwargs.get('_git_revs', [])
233 revs = kwargs.get('_git_revs', [])
234 if '_git_revs' in kwargs:
234 if '_git_revs' in kwargs:
235 kwargs.pop('_git_revs')
235 kwargs.pop('_git_revs')
236
236
237 action = action % ','.join(revs)
237 action = action % ','.join(revs)
238
238
239 action_logger(username, action, repository, extras['ip'], commit=True)
239 action_logger(username, action, repository, extras['ip'], commit=True)
240
240
241 # extension hook call
241 # extension hook call
242 from rhodecode import EXTENSIONS
242 from rhodecode import EXTENSIONS
243 callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
243 callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
244 if isfunction(callback):
244 if isfunction(callback):
245 kw = {'pushed_revs': revs}
245 kw = {'pushed_revs': revs}
246 kw.update(extras)
246 kw.update(extras)
247 callback(**kw)
247 callback(**kw)
248
248
249 if make_lock is False:
249 if make_lock is False:
250 Repository.unlock(Repository.get_by_repo_name(repository))
250 Repository.unlock(Repository.get_by_repo_name(repository))
251 msg = 'Released lock on repo `%s`\n' % repository
251 msg = 'Released lock on repo `%s`\n' % repository
252 sys.stdout.write(msg)
252 sys.stdout.write(msg)
253
253
254 return 0
254 return 0
255
255
256
256
257 def log_create_repository(repository_dict, created_by, **kwargs):
257 def log_create_repository(repository_dict, created_by, **kwargs):
258 """
258 """
259 Post create repository Hook. This is a dummy function for admins to re-use
259 Post create repository Hook. This is a dummy function for admins to re-use
260 if needed. It's taken from rhodecode-extensions module and executed
260 if needed. It's taken from rhodecode-extensions module and executed
261 if present
261 if present
262
262
263 :param repository: dict dump of repository object
263 :param repository: dict dump of repository object
264 :param created_by: username who created repository
264 :param created_by: username who created repository
265 :param created_date: date of creation
265 :param created_date: date of creation
266
266
267 available keys of repository_dict:
267 available keys of repository_dict:
268
268
269 'repo_type',
269 'repo_type',
270 'description',
270 'description',
271 'private',
271 'private',
272 'created_on',
272 'created_on',
273 'enable_downloads',
273 'enable_downloads',
274 'repo_id',
274 'repo_id',
275 'user_id',
275 'user_id',
276 'enable_statistics',
276 'enable_statistics',
277 'clone_uri',
277 'clone_uri',
278 'fork_id',
278 'fork_id',
279 'group_id',
279 'group_id',
280 'repo_name'
280 'repo_name'
281
281
282 """
282 """
283 from rhodecode import EXTENSIONS
283 from rhodecode import EXTENSIONS
284 callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
284 callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
285 if isfunction(callback):
285 if isfunction(callback):
286 kw = {}
286 kw = {}
287 kw.update(repository_dict)
287 kw.update(repository_dict)
288 kw.update({'created_by': created_by})
288 kw.update({'created_by': created_by})
289 kw.update(kwargs)
289 kw.update(kwargs)
290 return callback(**kw)
290 return callback(**kw)
291
291
292 return 0
292 return 0
293
293
294 handle_git_pre_receive = (lambda repo_path, revs, env:
294 handle_git_pre_receive = (lambda repo_path, revs, env:
295 handle_git_receive(repo_path, revs, env, hook_type='pre'))
295 handle_git_receive(repo_path, revs, env, hook_type='pre'))
296 handle_git_post_receive = (lambda repo_path, revs, env:
296 handle_git_post_receive = (lambda repo_path, revs, env:
297 handle_git_receive(repo_path, revs, env, hook_type='post'))
297 handle_git_receive(repo_path, revs, env, hook_type='post'))
298
298
299
299
300 def handle_git_receive(repo_path, revs, env, hook_type='post'):
300 def handle_git_receive(repo_path, revs, env, hook_type='post'):
301 """
301 """
302 A really hacky method that is runned by git post-receive hook and logs
302 A really hacky method that is runned by git post-receive hook and logs
303 an push action together with pushed revisions. It's executed by subprocess
303 an push action together with pushed revisions. It's executed by subprocess
304 thus needs all info to be able to create a on the fly pylons enviroment,
304 thus needs all info to be able to create a on the fly pylons enviroment,
305 connect to database and run the logging code. Hacky as sh*t but works.
305 connect to database and run the logging code. Hacky as sh*t but works.
306
306
307 :param repo_path:
307 :param repo_path:
308 :type repo_path:
308 :type repo_path:
309 :param revs:
309 :param revs:
310 :type revs:
310 :type revs:
311 :param env:
311 :param env:
312 :type env:
312 :type env:
313 """
313 """
314 from paste.deploy import appconfig
314 from paste.deploy import appconfig
315 from sqlalchemy import engine_from_config
315 from sqlalchemy import engine_from_config
316 from rhodecode.config.environment import load_environment
316 from rhodecode.config.environment import load_environment
317 from rhodecode.model import init_model
317 from rhodecode.model import init_model
318 from rhodecode.model.db import RhodeCodeUi
318 from rhodecode.model.db import RhodeCodeUi
319 from rhodecode.lib.utils import make_ui
319 from rhodecode.lib.utils import make_ui
320 extras = json.loads(env['RHODECODE_EXTRAS'])
320
321
321 path, ini_name = os.path.split(env['RHODECODE_CONFIG_FILE'])
322 path, ini_name = os.path.split(extras['config'])
322 conf = appconfig('config:%s' % ini_name, relative_to=path)
323 conf = appconfig('config:%s' % ini_name, relative_to=path)
323 load_environment(conf.global_conf, conf.local_conf)
324 load_environment(conf.global_conf, conf.local_conf)
324
325
325 engine = engine_from_config(conf, 'sqlalchemy.db1.')
326 engine = engine_from_config(conf, 'sqlalchemy.db1.')
326 init_model(engine)
327 init_model(engine)
327
328
328 baseui = make_ui('db')
329 baseui = make_ui('db')
329 # fix if it's not a bare repo
330 # fix if it's not a bare repo
330 if repo_path.endswith('.git'):
331 if repo_path.endswith('.git'):
331 repo_path = repo_path[:-4]
332 repo_path = repo_path[:-4]
332
333
333 repo = Repository.get_by_full_path(repo_path)
334 repo = Repository.get_by_full_path(repo_path)
334 if not repo:
335 if not repo:
335 raise OSError('Repository %s not found in database'
336 raise OSError('Repository %s not found in database'
336 % (safe_str(repo_path)))
337 % (safe_str(repo_path)))
337
338
338 _hooks = dict(baseui.configitems('hooks')) or {}
339 _hooks = dict(baseui.configitems('hooks')) or {}
339
340
340 extras = json.loads(env['RHODECODE_EXTRAS'])
341 for k, v in extras.items():
341 for k, v in extras.items():
342 baseui.setconfig('rhodecode_extras', k, v)
342 baseui.setconfig('rhodecode_extras', k, v)
343 repo = repo.scm_instance
343 repo = repo.scm_instance
344 repo.ui = baseui
344 repo.ui = baseui
345
345
346 if hook_type == 'pre':
346 if hook_type == 'pre':
347 pre_push(baseui, repo)
347 pre_push(baseui, repo)
348
348
349 # if push hook is enabled via web interface
349 # if push hook is enabled via web interface
350 elif hook_type == 'post' and _hooks.get(RhodeCodeUi.HOOK_PUSH):
350 elif hook_type == 'post' and _hooks.get(RhodeCodeUi.HOOK_PUSH):
351
351
352 rev_data = []
352 rev_data = []
353 for l in revs:
353 for l in revs:
354 old_rev, new_rev, ref = l.split(' ')
354 old_rev, new_rev, ref = l.split(' ')
355 _ref_data = ref.split('/')
355 _ref_data = ref.split('/')
356 if _ref_data[1] in ['tags', 'heads']:
356 if _ref_data[1] in ['tags', 'heads']:
357 rev_data.append({'old_rev': old_rev,
357 rev_data.append({'old_rev': old_rev,
358 'new_rev': new_rev,
358 'new_rev': new_rev,
359 'ref': ref,
359 'ref': ref,
360 'type': _ref_data[1],
360 'type': _ref_data[1],
361 'name': _ref_data[2].strip()})
361 'name': _ref_data[2].strip()})
362
362
363 git_revs = []
363 git_revs = []
364 for push_ref in rev_data:
364 for push_ref in rev_data:
365 _type = push_ref['type']
365 _type = push_ref['type']
366 if _type == 'heads':
366 if _type == 'heads':
367 if push_ref['old_rev'] == EmptyChangeset().raw_id:
367 if push_ref['old_rev'] == EmptyChangeset().raw_id:
368 cmd = "for-each-ref --format='%(refname)' 'refs/heads/*'"
368 cmd = "for-each-ref --format='%(refname)' 'refs/heads/*'"
369 heads = repo.run_git_command(cmd)[0]
369 heads = repo.run_git_command(cmd)[0]
370 heads = heads.replace(push_ref['ref'], '')
370 heads = heads.replace(push_ref['ref'], '')
371 heads = ' '.join(map(lambda c: c.strip('\n').strip(),
371 heads = ' '.join(map(lambda c: c.strip('\n').strip(),
372 heads.splitlines()))
372 heads.splitlines()))
373 cmd = (('log %(new_rev)s' % push_ref) +
373 cmd = (('log %(new_rev)s' % push_ref) +
374 ' --reverse --pretty=format:"%H" --not ' + heads)
374 ' --reverse --pretty=format:"%H" --not ' + heads)
375 else:
375 else:
376 cmd = (('log %(old_rev)s..%(new_rev)s' % push_ref) +
376 cmd = (('log %(old_rev)s..%(new_rev)s' % push_ref) +
377 ' --reverse --pretty=format:"%H"')
377 ' --reverse --pretty=format:"%H"')
378 git_revs += repo.run_git_command(cmd)[0].splitlines()
378 git_revs += repo.run_git_command(cmd)[0].splitlines()
379 elif _type == 'tags':
379 elif _type == 'tags':
380 git_revs += [push_ref['name']]
380 git_revs += [push_ref['name']]
381
381
382 log_push_action(baseui, repo, _git_revs=git_revs)
382 log_push_action(baseui, repo, _git_revs=git_revs)
@@ -1,200 +1,197 b''
1 import os
1 import os
2 import socket
2 import socket
3 import logging
3 import logging
4 import subprocess
4 import subprocess
5
5
6 from webob import Request, Response, exc
6 from webob import Request, Response, exc
7
7
8 from rhodecode.lib import subprocessio
8 from rhodecode.lib import subprocessio
9
9
10 log = logging.getLogger(__name__)
10 log = logging.getLogger(__name__)
11
11
12
12
13 class FileWrapper(object):
13 class FileWrapper(object):
14
14
15 def __init__(self, fd, content_length):
15 def __init__(self, fd, content_length):
16 self.fd = fd
16 self.fd = fd
17 self.content_length = content_length
17 self.content_length = content_length
18 self.remain = content_length
18 self.remain = content_length
19
19
20 def read(self, size):
20 def read(self, size):
21 if size <= self.remain:
21 if size <= self.remain:
22 try:
22 try:
23 data = self.fd.read(size)
23 data = self.fd.read(size)
24 except socket.error:
24 except socket.error:
25 raise IOError(self)
25 raise IOError(self)
26 self.remain -= size
26 self.remain -= size
27 elif self.remain:
27 elif self.remain:
28 data = self.fd.read(self.remain)
28 data = self.fd.read(self.remain)
29 self.remain = 0
29 self.remain = 0
30 else:
30 else:
31 data = None
31 data = None
32 return data
32 return data
33
33
34 def __repr__(self):
34 def __repr__(self):
35 return '<FileWrapper %s len: %s, read: %s>' % (
35 return '<FileWrapper %s len: %s, read: %s>' % (
36 self.fd, self.content_length, self.content_length - self.remain
36 self.fd, self.content_length, self.content_length - self.remain
37 )
37 )
38
38
39
39
40 class GitRepository(object):
40 class GitRepository(object):
41 git_folder_signature = set(['config', 'head', 'info', 'objects', 'refs'])
41 git_folder_signature = set(['config', 'head', 'info', 'objects', 'refs'])
42 commands = ['git-upload-pack', 'git-receive-pack']
42 commands = ['git-upload-pack', 'git-receive-pack']
43
43
44 def __init__(self, repo_name, content_path, extras):
44 def __init__(self, repo_name, content_path, extras):
45 files = set([f.lower() for f in os.listdir(content_path)])
45 files = set([f.lower() for f in os.listdir(content_path)])
46 if not (self.git_folder_signature.intersection(files)
46 if not (self.git_folder_signature.intersection(files)
47 == self.git_folder_signature):
47 == self.git_folder_signature):
48 raise OSError('%s missing git signature' % content_path)
48 raise OSError('%s missing git signature' % content_path)
49 self.content_path = content_path
49 self.content_path = content_path
50 self.valid_accepts = ['application/x-%s-result' %
50 self.valid_accepts = ['application/x-%s-result' %
51 c for c in self.commands]
51 c for c in self.commands]
52 self.repo_name = repo_name
52 self.repo_name = repo_name
53 self.extras = extras
53 self.extras = extras
54
54
55 def _get_fixedpath(self, path):
55 def _get_fixedpath(self, path):
56 """
56 """
57 Small fix for repo_path
57 Small fix for repo_path
58
58
59 :param path:
59 :param path:
60 :type path:
60 :type path:
61 """
61 """
62 return path.split(self.repo_name, 1)[-1].strip('/')
62 return path.split(self.repo_name, 1)[-1].strip('/')
63
63
64 def inforefs(self, request, environ):
64 def inforefs(self, request, environ):
65 """
65 """
66 WSGI Response producer for HTTP GET Git Smart
66 WSGI Response producer for HTTP GET Git Smart
67 HTTP /info/refs request.
67 HTTP /info/refs request.
68 """
68 """
69
69
70 git_command = request.GET.get('service')
70 git_command = request.GET.get('service')
71 if git_command not in self.commands:
71 if git_command not in self.commands:
72 log.debug('command %s not allowed' % git_command)
72 log.debug('command %s not allowed' % git_command)
73 return exc.HTTPMethodNotAllowed()
73 return exc.HTTPMethodNotAllowed()
74
74
75 # note to self:
75 # note to self:
76 # please, resist the urge to add '\n' to git capture and increment
76 # please, resist the urge to add '\n' to git capture and increment
77 # line count by 1.
77 # line count by 1.
78 # The code in Git client not only does NOT need '\n', but actually
78 # The code in Git client not only does NOT need '\n', but actually
79 # blows up if you sprinkle "flush" (0000) as "0001\n".
79 # blows up if you sprinkle "flush" (0000) as "0001\n".
80 # It reads binary, per number of bytes specified.
80 # It reads binary, per number of bytes specified.
81 # if you do add '\n' as part of data, count it.
81 # if you do add '\n' as part of data, count it.
82 server_advert = '# service=%s' % git_command
82 server_advert = '# service=%s' % git_command
83 packet_len = str(hex(len(server_advert) + 4)[2:].rjust(4, '0')).lower()
83 packet_len = str(hex(len(server_advert) + 4)[2:].rjust(4, '0')).lower()
84 try:
84 try:
85 out = subprocessio.SubprocessIOChunker(
85 out = subprocessio.SubprocessIOChunker(
86 r'git %s --stateless-rpc --advertise-refs "%s"' % (
86 r'git %s --stateless-rpc --advertise-refs "%s"' % (
87 git_command[4:], self.content_path),
87 git_command[4:], self.content_path),
88 starting_values=[
88 starting_values=[
89 packet_len + server_advert + '0000'
89 packet_len + server_advert + '0000'
90 ]
90 ]
91 )
91 )
92 except EnvironmentError, e:
92 except EnvironmentError, e:
93 log.exception(e)
93 log.exception(e)
94 raise exc.HTTPExpectationFailed()
94 raise exc.HTTPExpectationFailed()
95 resp = Response()
95 resp = Response()
96 resp.content_type = 'application/x-%s-advertisement' % str(git_command)
96 resp.content_type = 'application/x-%s-advertisement' % str(git_command)
97 resp.charset = None
97 resp.charset = None
98 resp.app_iter = out
98 resp.app_iter = out
99 return resp
99 return resp
100
100
101 def backend(self, request, environ):
101 def backend(self, request, environ):
102 """
102 """
103 WSGI Response producer for HTTP POST Git Smart HTTP requests.
103 WSGI Response producer for HTTP POST Git Smart HTTP requests.
104 Reads commands and data from HTTP POST's body.
104 Reads commands and data from HTTP POST's body.
105 returns an iterator obj with contents of git command's
105 returns an iterator obj with contents of git command's
106 response to stdout
106 response to stdout
107 """
107 """
108 git_command = self._get_fixedpath(request.path_info)
108 git_command = self._get_fixedpath(request.path_info)
109 if git_command not in self.commands:
109 if git_command not in self.commands:
110 log.debug('command %s not allowed' % git_command)
110 log.debug('command %s not allowed' % git_command)
111 return exc.HTTPMethodNotAllowed()
111 return exc.HTTPMethodNotAllowed()
112
112
113 if 'CONTENT_LENGTH' in environ:
113 if 'CONTENT_LENGTH' in environ:
114 inputstream = FileWrapper(environ['wsgi.input'],
114 inputstream = FileWrapper(environ['wsgi.input'],
115 request.content_length)
115 request.content_length)
116 else:
116 else:
117 inputstream = environ['wsgi.input']
117 inputstream = environ['wsgi.input']
118
118
119 try:
119 try:
120 gitenv = os.environ
120 gitenv = os.environ
121 from rhodecode import CONFIG
122 from rhodecode.lib.compat import json
121 from rhodecode.lib.compat import json
123 gitenv['RHODECODE_EXTRAS'] = json.dumps(self.extras)
122 gitenv['RHODECODE_EXTRAS'] = json.dumps(self.extras)
124 # forget all configs
123 # forget all configs
125 gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
124 gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
126 # we need current .ini file used to later initialize rhodecode
127 # env and connect to db
128 gitenv['RHODECODE_CONFIG_FILE'] = CONFIG['__file__']
129 opts = dict(
125 opts = dict(
130 env=gitenv,
126 env=gitenv,
131 cwd=os.getcwd()
127 cwd=os.getcwd()
132 )
128 )
129
133 out = subprocessio.SubprocessIOChunker(
130 out = subprocessio.SubprocessIOChunker(
134 r'git %s --stateless-rpc "%s"' % (git_command[4:],
131 r'git %s --stateless-rpc "%s"' % (git_command[4:],
135 self.content_path),
132 self.content_path),
136 inputstream=inputstream,
133 inputstream=inputstream,
137 **opts
134 **opts
138 )
135 )
139 except EnvironmentError, e:
136 except EnvironmentError, e:
140 log.exception(e)
137 log.exception(e)
141 raise exc.HTTPExpectationFailed()
138 raise exc.HTTPExpectationFailed()
142
139
143 if git_command in [u'git-receive-pack']:
140 if git_command in [u'git-receive-pack']:
144 # updating refs manually after each push.
141 # updating refs manually after each push.
145 # Needed for pre-1.7.0.4 git clients using regular HTTP mode.
142 # Needed for pre-1.7.0.4 git clients using regular HTTP mode.
146 subprocess.call(u'git --git-dir "%s" '
143 subprocess.call(u'git --git-dir "%s" '
147 'update-server-info' % self.content_path,
144 'update-server-info' % self.content_path,
148 shell=True)
145 shell=True)
149
146
150 resp = Response()
147 resp = Response()
151 resp.content_type = 'application/x-%s-result' % git_command.encode('utf8')
148 resp.content_type = 'application/x-%s-result' % git_command.encode('utf8')
152 resp.charset = None
149 resp.charset = None
153 resp.app_iter = out
150 resp.app_iter = out
154 return resp
151 return resp
155
152
156 def __call__(self, environ, start_response):
153 def __call__(self, environ, start_response):
157 request = Request(environ)
154 request = Request(environ)
158 _path = self._get_fixedpath(request.path_info)
155 _path = self._get_fixedpath(request.path_info)
159 if _path.startswith('info/refs'):
156 if _path.startswith('info/refs'):
160 app = self.inforefs
157 app = self.inforefs
161 elif [a for a in self.valid_accepts if a in request.accept]:
158 elif [a for a in self.valid_accepts if a in request.accept]:
162 app = self.backend
159 app = self.backend
163 try:
160 try:
164 resp = app(request, environ)
161 resp = app(request, environ)
165 except exc.HTTPException, e:
162 except exc.HTTPException, e:
166 resp = e
163 resp = e
167 log.exception(e)
164 log.exception(e)
168 except Exception, e:
165 except Exception, e:
169 log.exception(e)
166 log.exception(e)
170 resp = exc.HTTPInternalServerError()
167 resp = exc.HTTPInternalServerError()
171 return resp(environ, start_response)
168 return resp(environ, start_response)
172
169
173
170
174 class GitDirectory(object):
171 class GitDirectory(object):
175
172
176 def __init__(self, repo_root, repo_name, extras):
173 def __init__(self, repo_root, repo_name, extras):
177 repo_location = os.path.join(repo_root, repo_name)
174 repo_location = os.path.join(repo_root, repo_name)
178 if not os.path.isdir(repo_location):
175 if not os.path.isdir(repo_location):
179 raise OSError(repo_location)
176 raise OSError(repo_location)
180
177
181 self.content_path = repo_location
178 self.content_path = repo_location
182 self.repo_name = repo_name
179 self.repo_name = repo_name
183 self.repo_location = repo_location
180 self.repo_location = repo_location
184 self.extras = extras
181 self.extras = extras
185
182
186 def __call__(self, environ, start_response):
183 def __call__(self, environ, start_response):
187 content_path = self.content_path
184 content_path = self.content_path
188 try:
185 try:
189 app = GitRepository(self.repo_name, content_path, self.extras)
186 app = GitRepository(self.repo_name, content_path, self.extras)
190 except (AssertionError, OSError):
187 except (AssertionError, OSError):
191 content_path = os.path.join(content_path, '.git')
188 content_path = os.path.join(content_path, '.git')
192 if os.path.isdir(content_path):
189 if os.path.isdir(content_path):
193 app = GitRepository(self.repo_name, content_path, self.extras)
190 app = GitRepository(self.repo_name, content_path, self.extras)
194 else:
191 else:
195 return exc.HTTPNotFound()(environ, start_response)
192 return exc.HTTPNotFound()(environ, start_response)
196 return app(environ, start_response)
193 return app(environ, start_response)
197
194
198
195
199 def make_wsgi_app(repo_name, repo_root, extras):
196 def make_wsgi_app(repo_name, repo_root, extras):
200 return GitDirectory(repo_root, repo_name, extras)
197 return GitDirectory(repo_root, repo_name, extras)
General Comments 0
You need to be logged in to leave comments. Login now