Mercurial > kallithea
changeset 2402:2eeb2ed72e55 beta
Added handling of git hooks, extract pushed revisions and store them inside
rhodecode journal. F.I.N.A.L.Y !
author | Marcin Kuzminski <marcin@python-works.com> |
---|---|
date | Wed, 06 Jun 2012 19:51:16 +0200 |
parents | e2af60e480ce |
children | 6418fdb7d807 |
files | rhodecode/config/pre-receive.tmpl rhodecode/lib/hooks.py rhodecode/lib/middleware/pygrack.py rhodecode/lib/middleware/simplegit.py |
diffstat | 4 files changed, 124 insertions(+), 20 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rhodecode/config/pre-receive.tmpl Wed Jun 06 19:51:16 2012 +0200 @@ -0,0 +1,29 @@ +#!/usr/bin/env python +import os +import sys + +try: + import rhodecode + from rhodecode.lib.hooks import handle_git_post_receive +except ImportError: + rhodecode = None + + +def main(): + if rhodecode is None: + # exit with success if we cannot import rhodecode !! + # this allows simply push to this repo even without + # rhodecode + sys.exit(0) + + repo_path = os.path.abspath('.') + push_data = sys.stdin.read().strip().split(' ') + # os.environ is modified here by a subprocess call that + # runs git and later git executes this hook. + # Environ get's some additional info from rhodecode system + # like IP or username from basic-auth + handle_git_post_receive(repo_path, push_data, os.environ) + sys.exit(0) + +if __name__ == '__main__': + main() \ No newline at end of file
--- a/rhodecode/lib/hooks.py Wed Jun 06 19:19:21 2012 +0200 +++ b/rhodecode/lib/hooks.py Wed Jun 06 19:51:16 2012 +0200 @@ -139,7 +139,9 @@ h = binascii.hexlify revs = (h(repo[r].node()) for r in xrange(start, stop + 1)) elif scm == 'git': - revs = [] + revs = kwargs.get('_git_revs', []) + if '_git_revs' in kwargs: + kwargs.pop('_git_revs') action = action % ','.join(revs) @@ -190,3 +192,59 @@ return callback(**kw) return 0 + + +def handle_git_post_receive(repo_path, revs, env): + """ + A really hacky method that is runned by git pre-receive hook and logs + an push action together with pushed revisions. It's runned by subprocess + thus needs all info to be able to create a temp pylons enviroment, connect + to database and run the logging code. Hacky as sh**t but works. ps. + GIT SUCKS + + :param repo_path: + :type repo_path: + :param revs: + :type revs: + :param env: + :type env: + """ + from paste.deploy import appconfig + from sqlalchemy import engine_from_config + from rhodecode.config.environment import load_environment + from rhodecode.model import init_model + from rhodecode.model.db import RhodeCodeUi + from rhodecode.lib.utils import make_ui + from rhodecode.model.db import Repository + + path, ini_name = os.path.split(env['RHODECODE_CONFIG_FILE']) + conf = appconfig('config:%s' % ini_name, relative_to=path) + load_environment(conf.global_conf, conf.local_conf) + + engine = engine_from_config(conf, 'sqlalchemy.db1.') + init_model(engine) + + baseui = make_ui('db') + repo = Repository.get_by_full_path(repo_path) + + _hooks = dict(baseui.configitems('hooks')) or {} + # if push hook is enabled via web interface + if _hooks.get(RhodeCodeUi.HOOK_PUSH): + + extras = { + 'username': env['RHODECODE_USER'], + 'repository': repo.repo_name, + 'scm': 'git', + 'action': 'push', + 'ip': env['RHODECODE_CONFIG_IP'], + } + for k, v in extras.items(): + baseui.setconfig('rhodecode_extras', k, v) + repo = repo.scm_instance + repo.ui = baseui + old_rev, new_rev = revs[0:-1] + + cmd = 'log ' + old_rev + '..' + new_rev + ' --reverse --pretty=format:"%H"' + git_revs = repo.run_git_command(cmd)[0].splitlines() + + log_push_action(baseui, repo, _git_revs=git_revs)
--- a/rhodecode/lib/middleware/pygrack.py Wed Jun 06 19:19:21 2012 +0200 +++ b/rhodecode/lib/middleware/pygrack.py Wed Jun 06 19:51:16 2012 +0200 @@ -41,7 +41,7 @@ git_folder_signature = set(['config', 'head', 'info', 'objects', 'refs']) commands = ['git-upload-pack', 'git-receive-pack'] - def __init__(self, repo_name, content_path): + def __init__(self, repo_name, content_path, username): files = set([f.lower() for f in os.listdir(content_path)]) if not (self.git_folder_signature.intersection(files) == self.git_folder_signature): @@ -50,6 +50,7 @@ self.valid_accepts = ['application/x-%s-result' % c for c in self.commands] self.repo_name = repo_name + self.username = username def _get_fixedpath(self, path): """ @@ -115,11 +116,25 @@ inputstream = environ['wsgi.input'] try: + gitenv = os.environ + from rhodecode import CONFIG + from rhodecode.lib.base import _get_ip_addr + gitenv['RHODECODE_USER'] = self.username + gitenv['RHODECODE_CONFIG_IP'] = _get_ip_addr(environ) + # forget all configs + gitenv['GIT_CONFIG_NOGLOBAL'] = '1' + # we need current .ini file used to later initialize rhodecode + # env and connect to db + gitenv['RHODECODE_CONFIG_FILE'] = CONFIG['__file__'] + opts = dict( + env=gitenv + ) out = subprocessio.SubprocessIOChunker( r'git %s --stateless-rpc "%s"' % (git_command[4:], self.content_path), - inputstream=inputstream - ) + inputstream=inputstream, + **opts + ) except EnvironmentError, e: log.exception(e) raise exc.HTTPExpectationFailed() @@ -156,7 +171,7 @@ class GitDirectory(object): - def __init__(self, repo_root, repo_name): + def __init__(self, repo_root, repo_name, username): repo_location = os.path.join(repo_root, repo_name) if not os.path.isdir(repo_location): raise OSError(repo_location) @@ -164,19 +179,20 @@ self.content_path = repo_location self.repo_name = repo_name self.repo_location = repo_location + self.username = username def __call__(self, environ, start_response): content_path = self.content_path try: - app = GitRepository(self.repo_name, content_path) + app = GitRepository(self.repo_name, content_path, self.username) except (AssertionError, OSError): if os.path.isdir(os.path.join(content_path, '.git')): app = GitRepository(self.repo_name, os.path.join(content_path, '.git')) else: - return exc.HTTPNotFound()(environ, start_response) + return exc.HTTPNotFound()(environ, start_response, self.username) return app(environ, start_response) -def make_wsgi_app(repo_name, repo_root): - return GitDirectory(repo_root, repo_name) +def make_wsgi_app(repo_name, repo_root, username): + return GitDirectory(repo_root, repo_name, username)
--- a/rhodecode/lib/middleware/simplegit.py Wed Jun 06 19:19:21 2012 +0200 +++ b/rhodecode/lib/middleware/simplegit.py Wed Jun 06 19:51:16 2012 +0200 @@ -68,8 +68,9 @@ 'git-receive-pack': dulserver.ReceivePackHandler, } -from dulwich.repo import Repo -from dulwich.web import make_wsgi_chain +# not used for now until dulwich get's fixed +#from dulwich.repo import Repo +#from dulwich.web import make_wsgi_chain from paste.httpheaders import REMOTE_USER, AUTH_TYPE @@ -77,7 +78,7 @@ from rhodecode.lib.base import BaseVCSController from rhodecode.lib.auth import get_container_username from rhodecode.lib.utils import is_valid_repo, make_ui -from rhodecode.model.db import User +from rhodecode.model.db import User, RhodeCodeUi from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError @@ -205,13 +206,13 @@ self._handle_githooks(repo_name, action, baseui, environ) log.info('%s action on GIT repo "%s"' % (action, repo_name)) - app = self.__make_app(repo_name, repo_path) + app = self.__make_app(repo_name, repo_path, username) return app(environ, start_response) except Exception: log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response) - def __make_app(self, repo_name, repo_path): + def __make_app(self, repo_name, repo_path, username): """ Make an wsgi application using dulserver @@ -223,6 +224,7 @@ app = make_wsgi_app( repo_root=os.path.dirname(repo_path), repo_name=repo_name, + username=username, ) return app @@ -268,7 +270,10 @@ return op def _handle_githooks(self, repo_name, action, baseui, environ): - from rhodecode.lib.hooks import log_pull_action, log_push_action + """ + Handles pull action, push is handled by pre-receive hook + """ + from rhodecode.lib.hooks import log_pull_action service = environ['QUERY_STRING'].split('=') if len(service) < 2: return @@ -278,12 +283,8 @@ _repo = _repo.scm_instance _repo._repo.ui = baseui - push_hook = 'pretxnchangegroup.push_logger' - pull_hook = 'preoutgoing.pull_logger' _hooks = dict(baseui.configitems('hooks')) or {} - if action == 'push' and _hooks.get(push_hook): - log_push_action(ui=baseui, repo=_repo._repo) - elif action == 'pull' and _hooks.get(pull_hook): + if action == 'pull' and _hooks.get(RhodeCodeUi.HOOK_PULL): log_pull_action(ui=baseui, repo=_repo._repo) def __inject_extras(self, repo_path, baseui, extras={}):