##// END OF EJS Templates
Added handling of git hooks, extract pushed revisions and store them inside...
marcink -
r2402:2eeb2ed7 beta
parent child Browse files
Show More
@@ -0,0 +1,29
1 #!/usr/bin/env python
2 import os
3 import sys
4
5 try:
6 import rhodecode
7 from rhodecode.lib.hooks import handle_git_post_receive
8 except ImportError:
9 rhodecode = None
10
11
12 def main():
13 if rhodecode is None:
14 # exit with success if we cannot import rhodecode !!
15 # this allows simply push to this repo even without
16 # rhodecode
17 sys.exit(0)
18
19 repo_path = os.path.abspath('.')
20 push_data = sys.stdin.read().strip().split(' ')
21 # os.environ is modified here by a subprocess call that
22 # runs git and later git executes this hook.
23 # Environ get's some additional info from rhodecode system
24 # like IP or username from basic-auth
25 handle_git_post_receive(repo_path, push_data, os.environ)
26 sys.exit(0)
27
28 if __name__ == '__main__':
29 main() No newline at end of file
@@ -1,192 +1,250
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.hooks
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Hooks runned by rhodecode
7 7
8 8 :created_on: Aug 6, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 import os
26 26 import sys
27 27 import binascii
28 28 from inspect import isfunction
29 29
30 30 from mercurial.scmutil import revrange
31 31 from mercurial.node import nullrev
32 32
33 33 from rhodecode import EXTENSIONS
34 34 from rhodecode.lib import helpers as h
35 35 from rhodecode.lib.utils import action_logger
36 36
37 37
38 38 def _get_scm_size(alias, root_path):
39 39
40 40 if not alias.startswith('.'):
41 41 alias += '.'
42 42
43 43 size_scm, size_root = 0, 0
44 44 for path, dirs, files in os.walk(root_path):
45 45 if path.find(alias) != -1:
46 46 for f in files:
47 47 try:
48 48 size_scm += os.path.getsize(os.path.join(path, f))
49 49 except OSError:
50 50 pass
51 51 else:
52 52 for f in files:
53 53 try:
54 54 size_root += os.path.getsize(os.path.join(path, f))
55 55 except OSError:
56 56 pass
57 57
58 58 size_scm_f = h.format_byte_size(size_scm)
59 59 size_root_f = h.format_byte_size(size_root)
60 60 size_total_f = h.format_byte_size(size_root + size_scm)
61 61
62 62 return size_scm_f, size_root_f, size_total_f
63 63
64 64
65 65 def repo_size(ui, repo, hooktype=None, **kwargs):
66 66 """
67 67 Presents size of repository after push
68 68
69 69 :param ui:
70 70 :param repo:
71 71 :param hooktype:
72 72 """
73 73
74 74 size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', repo.root)
75 75
76 76 last_cs = repo[len(repo) - 1]
77 77
78 78 msg = ('Repository size .hg:%s repo:%s total:%s\n'
79 79 'Last revision is now r%s:%s\n') % (
80 80 size_hg_f, size_root_f, size_total_f, last_cs.rev(), last_cs.hex()[:12]
81 81 )
82 82
83 83 sys.stdout.write(msg)
84 84
85 85
86 86 def log_pull_action(ui, repo, **kwargs):
87 87 """
88 88 Logs user last pull action
89 89
90 90 :param ui:
91 91 :param repo:
92 92 """
93 93
94 94 extras = dict(repo.ui.configitems('rhodecode_extras'))
95 95 username = extras['username']
96 96 repository = extras['repository']
97 97 scm = extras['scm']
98 98 action = 'pull'
99 99
100 100 action_logger(username, action, repository, extras['ip'], commit=True)
101 101 # extension hook call
102 102 callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
103 103
104 104 if isfunction(callback):
105 105 kw = {}
106 106 kw.update(extras)
107 107 callback(**kw)
108 108 return 0
109 109
110 110
111 111 def log_push_action(ui, repo, **kwargs):
112 112 """
113 113 Maps user last push action to new changeset id, from mercurial
114 114
115 115 :param ui:
116 116 :param repo: repo object containing the `ui` object
117 117 """
118 118
119 119 extras = dict(repo.ui.configitems('rhodecode_extras'))
120 120 username = extras['username']
121 121 repository = extras['repository']
122 122 action = extras['action'] + ':%s'
123 123 scm = extras['scm']
124 124
125 125 if scm == 'hg':
126 126 node = kwargs['node']
127 127
128 128 def get_revs(repo, rev_opt):
129 129 if rev_opt:
130 130 revs = revrange(repo, rev_opt)
131 131
132 132 if len(revs) == 0:
133 133 return (nullrev, nullrev)
134 134 return (max(revs), min(revs))
135 135 else:
136 136 return (len(repo) - 1, 0)
137 137
138 138 stop, start = get_revs(repo, [node + ':'])
139 139 h = binascii.hexlify
140 140 revs = (h(repo[r].node()) for r in xrange(start, stop + 1))
141 141 elif scm == 'git':
142 revs = []
142 revs = kwargs.get('_git_revs', [])
143 if '_git_revs' in kwargs:
144 kwargs.pop('_git_revs')
143 145
144 146 action = action % ','.join(revs)
145 147
146 148 action_logger(username, action, repository, extras['ip'], commit=True)
147 149
148 150 # extension hook call
149 151 callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
150 152 if isfunction(callback):
151 153 kw = {'pushed_revs': revs}
152 154 kw.update(extras)
153 155 callback(**kw)
154 156 return 0
155 157
156 158
157 159 def log_create_repository(repository_dict, created_by, **kwargs):
158 160 """
159 161 Post create repository Hook. This is a dummy function for admins to re-use
160 162 if needed. It's taken from rhodecode-extensions module and executed
161 163 if present
162 164
163 165 :param repository: dict dump of repository object
164 166 :param created_by: username who created repository
165 167 :param created_date: date of creation
166 168
167 169 available keys of repository_dict:
168 170
169 171 'repo_type',
170 172 'description',
171 173 'private',
172 174 'created_on',
173 175 'enable_downloads',
174 176 'repo_id',
175 177 'user_id',
176 178 'enable_statistics',
177 179 'clone_uri',
178 180 'fork_id',
179 181 'group_id',
180 182 'repo_name'
181 183
182 184 """
183 185
184 186 callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
185 187 if isfunction(callback):
186 188 kw = {}
187 189 kw.update(repository_dict)
188 190 kw.update({'created_by': created_by})
189 191 kw.update(kwargs)
190 192 return callback(**kw)
191 193
192 194 return 0
195
196
197 def handle_git_post_receive(repo_path, revs, env):
198 """
199 A really hacky method that is runned by git pre-receive hook and logs
200 an push action together with pushed revisions. It's runned by subprocess
201 thus needs all info to be able to create a temp pylons enviroment, connect
202 to database and run the logging code. Hacky as sh**t but works. ps.
203 GIT SUCKS
204
205 :param repo_path:
206 :type repo_path:
207 :param revs:
208 :type revs:
209 :param env:
210 :type env:
211 """
212 from paste.deploy import appconfig
213 from sqlalchemy import engine_from_config
214 from rhodecode.config.environment import load_environment
215 from rhodecode.model import init_model
216 from rhodecode.model.db import RhodeCodeUi
217 from rhodecode.lib.utils import make_ui
218 from rhodecode.model.db import Repository
219
220 path, ini_name = os.path.split(env['RHODECODE_CONFIG_FILE'])
221 conf = appconfig('config:%s' % ini_name, relative_to=path)
222 load_environment(conf.global_conf, conf.local_conf)
223
224 engine = engine_from_config(conf, 'sqlalchemy.db1.')
225 init_model(engine)
226
227 baseui = make_ui('db')
228 repo = Repository.get_by_full_path(repo_path)
229
230 _hooks = dict(baseui.configitems('hooks')) or {}
231 # if push hook is enabled via web interface
232 if _hooks.get(RhodeCodeUi.HOOK_PUSH):
233
234 extras = {
235 'username': env['RHODECODE_USER'],
236 'repository': repo.repo_name,
237 'scm': 'git',
238 'action': 'push',
239 'ip': env['RHODECODE_CONFIG_IP'],
240 }
241 for k, v in extras.items():
242 baseui.setconfig('rhodecode_extras', k, v)
243 repo = repo.scm_instance
244 repo.ui = baseui
245 old_rev, new_rev = revs[0:-1]
246
247 cmd = 'log ' + old_rev + '..' + new_rev + ' --reverse --pretty=format:"%H"'
248 git_revs = repo.run_git_command(cmd)[0].splitlines()
249
250 log_push_action(baseui, repo, _git_revs=git_revs)
@@ -1,182 +1,198
1 1 import os
2 2 import socket
3 3 import logging
4 4 import subprocess
5 5
6 6 from webob import Request, Response, exc
7 7
8 8 from rhodecode.lib import subprocessio
9 9
10 10 log = logging.getLogger(__name__)
11 11
12 12
13 13 class FileWrapper(object):
14 14
15 15 def __init__(self, fd, content_length):
16 16 self.fd = fd
17 17 self.content_length = content_length
18 18 self.remain = content_length
19 19
20 20 def read(self, size):
21 21 if size <= self.remain:
22 22 try:
23 23 data = self.fd.read(size)
24 24 except socket.error:
25 25 raise IOError(self)
26 26 self.remain -= size
27 27 elif self.remain:
28 28 data = self.fd.read(self.remain)
29 29 self.remain = 0
30 30 else:
31 31 data = None
32 32 return data
33 33
34 34 def __repr__(self):
35 35 return '<FileWrapper %s len: %s, read: %s>' % (
36 36 self.fd, self.content_length, self.content_length - self.remain
37 37 )
38 38
39 39
40 40 class GitRepository(object):
41 41 git_folder_signature = set(['config', 'head', 'info', 'objects', 'refs'])
42 42 commands = ['git-upload-pack', 'git-receive-pack']
43 43
44 def __init__(self, repo_name, content_path):
44 def __init__(self, repo_name, content_path, username):
45 45 files = set([f.lower() for f in os.listdir(content_path)])
46 46 if not (self.git_folder_signature.intersection(files)
47 47 == self.git_folder_signature):
48 48 raise OSError('%s missing git signature' % content_path)
49 49 self.content_path = content_path
50 50 self.valid_accepts = ['application/x-%s-result' %
51 51 c for c in self.commands]
52 52 self.repo_name = repo_name
53 self.username = username
53 54
54 55 def _get_fixedpath(self, path):
55 56 """
56 57 Small fix for repo_path
57 58
58 59 :param path:
59 60 :type path:
60 61 """
61 62 return path.split(self.repo_name, 1)[-1].strip('/')
62 63
63 64 def inforefs(self, request, environ):
64 65 """
65 66 WSGI Response producer for HTTP GET Git Smart
66 67 HTTP /info/refs request.
67 68 """
68 69
69 70 git_command = request.GET['service']
70 71 if git_command not in self.commands:
71 72 log.debug('command %s not allowed' % git_command)
72 73 return exc.HTTPMethodNotAllowed()
73 74
74 75 # note to self:
75 76 # please, resist the urge to add '\n' to git capture and increment
76 77 # line count by 1.
77 78 # The code in Git client not only does NOT need '\n', but actually
78 79 # blows up if you sprinkle "flush" (0000) as "0001\n".
79 80 # It reads binary, per number of bytes specified.
80 81 # if you do add '\n' as part of data, count it.
81 82 smart_server_advert = '# service=%s' % git_command
82 83 try:
83 84 out = subprocessio.SubprocessIOChunker(
84 85 r'git %s --stateless-rpc --advertise-refs "%s"' % (
85 86 git_command[4:], self.content_path),
86 87 starting_values=[
87 88 str(hex(len(smart_server_advert) + 4)[2:]
88 89 .rjust(4, '0') + smart_server_advert + '0000')
89 90 ]
90 91 )
91 92 except EnvironmentError, e:
92 93 log.exception(e)
93 94 raise exc.HTTPExpectationFailed()
94 95 resp = Response()
95 96 resp.content_type = 'application/x-%s-advertisement' % str(git_command)
96 97 resp.app_iter = out
97 98 return resp
98 99
99 100 def backend(self, request, environ):
100 101 """
101 102 WSGI Response producer for HTTP POST Git Smart HTTP requests.
102 103 Reads commands and data from HTTP POST's body.
103 104 returns an iterator obj with contents of git command's
104 105 response to stdout
105 106 """
106 107 git_command = self._get_fixedpath(request.path_info)
107 108 if git_command not in self.commands:
108 109 log.debug('command %s not allowed' % git_command)
109 110 return exc.HTTPMethodNotAllowed()
110 111
111 112 if 'CONTENT_LENGTH' in environ:
112 113 inputstream = FileWrapper(environ['wsgi.input'],
113 114 request.content_length)
114 115 else:
115 116 inputstream = environ['wsgi.input']
116 117
117 118 try:
119 gitenv = os.environ
120 from rhodecode import CONFIG
121 from rhodecode.lib.base import _get_ip_addr
122 gitenv['RHODECODE_USER'] = self.username
123 gitenv['RHODECODE_CONFIG_IP'] = _get_ip_addr(environ)
124 # forget all configs
125 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(
130 env=gitenv
131 )
118 132 out = subprocessio.SubprocessIOChunker(
119 133 r'git %s --stateless-rpc "%s"' % (git_command[4:],
120 134 self.content_path),
121 inputstream=inputstream
135 inputstream=inputstream,
136 **opts
122 137 )
123 138 except EnvironmentError, e:
124 139 log.exception(e)
125 140 raise exc.HTTPExpectationFailed()
126 141
127 142 if git_command in [u'git-receive-pack']:
128 143 # updating refs manually after each push.
129 144 # Needed for pre-1.7.0.4 git clients using regular HTTP mode.
130 145 subprocess.call(u'git --git-dir "%s" '
131 146 'update-server-info' % self.content_path,
132 147 shell=True)
133 148
134 149 resp = Response()
135 150 resp.content_type = 'application/x-%s-result' % git_command.encode('utf8')
136 151 resp.app_iter = out
137 152 return resp
138 153
139 154 def __call__(self, environ, start_response):
140 155 request = Request(environ)
141 156 _path = self._get_fixedpath(request.path_info)
142 157 if _path.startswith('info/refs'):
143 158 app = self.inforefs
144 159 elif [a for a in self.valid_accepts if a in request.accept]:
145 160 app = self.backend
146 161 try:
147 162 resp = app(request, environ)
148 163 except exc.HTTPException, e:
149 164 resp = e
150 165 log.exception(e)
151 166 except Exception, e:
152 167 log.exception(e)
153 168 resp = exc.HTTPInternalServerError()
154 169 return resp(environ, start_response)
155 170
156 171
157 172 class GitDirectory(object):
158 173
159 def __init__(self, repo_root, repo_name):
174 def __init__(self, repo_root, repo_name, username):
160 175 repo_location = os.path.join(repo_root, repo_name)
161 176 if not os.path.isdir(repo_location):
162 177 raise OSError(repo_location)
163 178
164 179 self.content_path = repo_location
165 180 self.repo_name = repo_name
166 181 self.repo_location = repo_location
182 self.username = username
167 183
168 184 def __call__(self, environ, start_response):
169 185 content_path = self.content_path
170 186 try:
171 app = GitRepository(self.repo_name, content_path)
187 app = GitRepository(self.repo_name, content_path, self.username)
172 188 except (AssertionError, OSError):
173 189 if os.path.isdir(os.path.join(content_path, '.git')):
174 190 app = GitRepository(self.repo_name,
175 191 os.path.join(content_path, '.git'))
176 192 else:
177 return exc.HTTPNotFound()(environ, start_response)
193 return exc.HTTPNotFound()(environ, start_response, self.username)
178 194 return app(environ, start_response)
179 195
180 196
181 def make_wsgi_app(repo_name, repo_root):
182 return GitDirectory(repo_root, repo_name)
197 def make_wsgi_app(repo_name, repo_root, username):
198 return GitDirectory(repo_root, repo_name, username)
@@ -1,303 +1,304
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.middleware.simplegit
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 SimpleGit middleware for handling git protocol request (push/clone etc.)
7 7 It's implemented with basic auth function
8 8
9 9 :created_on: Apr 28, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26
27 27 import os
28 28 import re
29 29 import logging
30 30 import traceback
31 31
32 32 from dulwich import server as dulserver
33 33
34 34
35 35 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
36 36
37 37 def handle(self):
38 38 write = lambda x: self.proto.write_sideband(1, x)
39 39
40 40 graph_walker = dulserver.ProtocolGraphWalker(self,
41 41 self.repo.object_store,
42 42 self.repo.get_peeled)
43 43 objects_iter = self.repo.fetch_objects(
44 44 graph_walker.determine_wants, graph_walker, self.progress,
45 45 get_tagged=self.get_tagged)
46 46
47 47 # Did the process short-circuit (e.g. in a stateless RPC call)? Note
48 48 # that the client still expects a 0-object pack in most cases.
49 49 if objects_iter is None:
50 50 return
51 51
52 52 self.progress("counting objects: %d, done.\n" % len(objects_iter))
53 53 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
54 54 objects_iter)
55 55 messages = []
56 56 messages.append('thank you for using rhodecode')
57 57
58 58 for msg in messages:
59 59 self.progress(msg + "\n")
60 60 # we are done
61 61 self.proto.write("0000")
62 62
63 63
64 64 dulserver.DEFAULT_HANDLERS = {
65 65 #git-ls-remote, git-clone, git-fetch and git-pull
66 66 'git-upload-pack': SimpleGitUploadPackHandler,
67 67 #git-push
68 68 'git-receive-pack': dulserver.ReceivePackHandler,
69 69 }
70 70
71 from dulwich.repo import Repo
72 from dulwich.web import make_wsgi_chain
71 # not used for now until dulwich get's fixed
72 #from dulwich.repo import Repo
73 #from dulwich.web import make_wsgi_chain
73 74
74 75 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
75 76
76 77 from rhodecode.lib.utils2 import safe_str
77 78 from rhodecode.lib.base import BaseVCSController
78 79 from rhodecode.lib.auth import get_container_username
79 80 from rhodecode.lib.utils import is_valid_repo, make_ui
80 from rhodecode.model.db import User
81 from rhodecode.model.db import User, RhodeCodeUi
81 82
82 83 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
83 84
84 85 log = logging.getLogger(__name__)
85 86
86 87
87 88 GIT_PROTO_PAT = re.compile(r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)')
88 89
89 90
90 91 def is_git(environ):
91 92 path_info = environ['PATH_INFO']
92 93 isgit_path = GIT_PROTO_PAT.match(path_info)
93 94 log.debug('pathinfo: %s detected as GIT %s' % (
94 95 path_info, isgit_path != None)
95 96 )
96 97 return isgit_path
97 98
98 99
99 100 class SimpleGit(BaseVCSController):
100 101
101 102 def _handle_request(self, environ, start_response):
102 103
103 104 if not is_git(environ):
104 105 return self.application(environ, start_response)
105 106
106 107 ipaddr = self._get_ip_addr(environ)
107 108 username = None
108 109 self._git_first_op = False
109 110 # skip passing error to error controller
110 111 environ['pylons.status_code_redirect'] = True
111 112
112 113 #======================================================================
113 114 # EXTRACT REPOSITORY NAME FROM ENV
114 115 #======================================================================
115 116 try:
116 117 repo_name = self.__get_repository(environ)
117 118 log.debug('Extracted repo name is %s' % repo_name)
118 119 except:
119 120 return HTTPInternalServerError()(environ, start_response)
120 121
121 122 # quick check if that dir exists...
122 123 if is_valid_repo(repo_name, self.basepath) is False:
123 124 return HTTPNotFound()(environ, start_response)
124 125
125 126 #======================================================================
126 127 # GET ACTION PULL or PUSH
127 128 #======================================================================
128 129 action = self.__get_action(environ)
129 130
130 131 #======================================================================
131 132 # CHECK ANONYMOUS PERMISSION
132 133 #======================================================================
133 134 if action in ['pull', 'push']:
134 135 anonymous_user = self.__get_user('default')
135 136 username = anonymous_user.username
136 137 anonymous_perm = self._check_permission(action, anonymous_user,
137 138 repo_name)
138 139
139 140 if anonymous_perm is not True or anonymous_user.active is False:
140 141 if anonymous_perm is not True:
141 142 log.debug('Not enough credentials to access this '
142 143 'repository as anonymous user')
143 144 if anonymous_user.active is False:
144 145 log.debug('Anonymous access is disabled, running '
145 146 'authentication')
146 147 #==============================================================
147 148 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
148 149 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
149 150 #==============================================================
150 151
151 152 # Attempting to retrieve username from the container
152 153 username = get_container_username(environ, self.config)
153 154
154 155 # If not authenticated by the container, running basic auth
155 156 if not username:
156 157 self.authenticate.realm = \
157 158 safe_str(self.config['rhodecode_realm'])
158 159 result = self.authenticate(environ)
159 160 if isinstance(result, str):
160 161 AUTH_TYPE.update(environ, 'basic')
161 162 REMOTE_USER.update(environ, result)
162 163 username = result
163 164 else:
164 165 return result.wsgi_application(environ, start_response)
165 166
166 167 #==============================================================
167 168 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
168 169 #==============================================================
169 170 if action in ['pull', 'push']:
170 171 try:
171 172 user = self.__get_user(username)
172 173 if user is None or not user.active:
173 174 return HTTPForbidden()(environ, start_response)
174 175 username = user.username
175 176 except:
176 177 log.error(traceback.format_exc())
177 178 return HTTPInternalServerError()(environ,
178 179 start_response)
179 180
180 181 #check permissions for this repository
181 182 perm = self._check_permission(action, user, repo_name)
182 183 if perm is not True:
183 184 return HTTPForbidden()(environ, start_response)
184 185 extras = {
185 186 'ip': ipaddr,
186 187 'username': username,
187 188 'action': action,
188 189 'repository': repo_name,
189 190 'scm': 'git',
190 191 }
191 192
192 193 #===================================================================
193 194 # GIT REQUEST HANDLING
194 195 #===================================================================
195 196 repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
196 197 log.debug('Repository path is %s' % repo_path)
197 198
198 199 baseui = make_ui('db')
199 200 self.__inject_extras(repo_path, baseui, extras)
200 201
201 202 try:
202 203 # invalidate cache on push
203 204 if action == 'push':
204 205 self._invalidate_cache(repo_name)
205 206 self._handle_githooks(repo_name, action, baseui, environ)
206 207
207 208 log.info('%s action on GIT repo "%s"' % (action, repo_name))
208 app = self.__make_app(repo_name, repo_path)
209 app = self.__make_app(repo_name, repo_path, username)
209 210 return app(environ, start_response)
210 211 except Exception:
211 212 log.error(traceback.format_exc())
212 213 return HTTPInternalServerError()(environ, start_response)
213 214
214 def __make_app(self, repo_name, repo_path):
215 def __make_app(self, repo_name, repo_path, username):
215 216 """
216 217 Make an wsgi application using dulserver
217 218
218 219 :param repo_name: name of the repository
219 220 :param repo_path: full path to the repository
220 221 """
221 222
222 223 from rhodecode.lib.middleware.pygrack import make_wsgi_app
223 224 app = make_wsgi_app(
224 225 repo_root=os.path.dirname(repo_path),
225 226 repo_name=repo_name,
227 username=username,
226 228 )
227 229 return app
228 230
229 231 def __get_repository(self, environ):
230 232 """
231 233 Get's repository name out of PATH_INFO header
232 234
233 235 :param environ: environ where PATH_INFO is stored
234 236 """
235 237 try:
236 238 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
237 239 repo_name = GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1)
238 240 except:
239 241 log.error(traceback.format_exc())
240 242 raise
241 243
242 244 return repo_name
243 245
244 246 def __get_user(self, username):
245 247 return User.get_by_username(username)
246 248
247 249 def __get_action(self, environ):
248 250 """
249 251 Maps git request commands into a pull or push command.
250 252
251 253 :param environ:
252 254 """
253 255 service = environ['QUERY_STRING'].split('=')
254 256
255 257 if len(service) > 1:
256 258 service_cmd = service[1]
257 259 mapping = {
258 260 'git-receive-pack': 'push',
259 261 'git-upload-pack': 'pull',
260 262 }
261 263 op = mapping[service_cmd]
262 264 self._git_stored_op = op
263 265 return op
264 266 else:
265 267 # try to fallback to stored variable as we don't know if the last
266 268 # operation is pull/push
267 269 op = getattr(self, '_git_stored_op', 'pull')
268 270 return op
269 271
270 272 def _handle_githooks(self, repo_name, action, baseui, environ):
271 from rhodecode.lib.hooks import log_pull_action, log_push_action
273 """
274 Handles pull action, push is handled by pre-receive hook
275 """
276 from rhodecode.lib.hooks import log_pull_action
272 277 service = environ['QUERY_STRING'].split('=')
273 278 if len(service) < 2:
274 279 return
275 280
276 281 from rhodecode.model.db import Repository
277 282 _repo = Repository.get_by_repo_name(repo_name)
278 283 _repo = _repo.scm_instance
279 284 _repo._repo.ui = baseui
280 285
281 push_hook = 'pretxnchangegroup.push_logger'
282 pull_hook = 'preoutgoing.pull_logger'
283 286 _hooks = dict(baseui.configitems('hooks')) or {}
284 if action == 'push' and _hooks.get(push_hook):
285 log_push_action(ui=baseui, repo=_repo._repo)
286 elif action == 'pull' and _hooks.get(pull_hook):
287 if action == 'pull' and _hooks.get(RhodeCodeUi.HOOK_PULL):
287 288 log_pull_action(ui=baseui, repo=_repo._repo)
288 289
289 290 def __inject_extras(self, repo_path, baseui, extras={}):
290 291 """
291 292 Injects some extra params into baseui instance
292 293
293 294 :param baseui: baseui instance
294 295 :param extras: dict with extra params to put into baseui
295 296 """
296 297
297 298 # make our hgweb quiet so it doesn't print output
298 299 baseui.setconfig('ui', 'quiet', 'true')
299 300
300 301 #inject some additional parameters that will be available in ui
301 302 #for hooks
302 303 for k, v in extras.items():
303 304 baseui.setconfig('rhodecode_extras', k, v)
General Comments 0
You need to be logged in to leave comments. Login now