David Caro has uploaded a new change for review. Change subject: Added first python hooks and libs ......................................................................
Added first python hooks and libs * Added basic config.py lib * Added basic gerrit.py lib * Added hook to check if the patch is merged on all newer branches Change-Id: I985113f5a228ccc128eaad3dbf45c918b7fc093a Signed-off-by: David Caro <[email protected]> --- A hooks/bz/patchset-created.warn_if_not_merged_to_previous_branch M hooks/hook-dispatcher A hooks/lib/config.py A hooks/lib/config.pyc A hooks/lib/gerrit.py A hooks/lib/gerrit.pyc 6 files changed, 231 insertions(+), 1 deletion(-) git pull ssh://gerrit.ovirt.org:29418/gerrit-admin refs/changes/08/24208/1 diff --git a/hooks/bz/patchset-created.warn_if_not_merged_to_previous_branch b/hooks/bz/patchset-created.warn_if_not_merged_to_previous_branch new file mode 100755 index 0000000..20b035d --- /dev/null +++ b/hooks/bz/patchset-created.warn_if_not_merged_to_previous_branch @@ -0,0 +1,86 @@ +#!/usr/bin/env python + +import argparse +import logging +import sys +import os +from config import load_config +from gerrit import Gerrit + + +def is_newer(branch1, branch2): + try: + b1_ver_num = branch1.rsplit('-', 1)[1] + b2_ver_num = branch2.rsplit('-', 1)[1] + b1_tuple = tuple(map(int, (b1_ver_num.split(".")))) + b2_tuple = tuple(map(int, (b2_ver_num.split(".")))) + except (IndexError, ValueError): + raise Exception("Unable to parse version strings (%s, %s)" + % (branch1, branch2)) + return b1_tuple > b2_tuple + + +def get_unmerged_newer_branches(gerrit, change_id, cur_branch): + branches = [] + patches = gerrit.query(change_id, out_format='json') + for patch in patches: + if 'branch' not in patch: + continue + logging.debug("Checking patch %s" % str(patch)) + if is_newer(patch['branch'], cur_branch) and patch['open']: + logging.error('patch with id {id} still open for branch ' + '{branch}'.format(**patch)) + branches.append(patch['branch']) + return branches + + +def get_parser(): + """ + Build the parser for patchset-created hook type + """ + parser = argparse.ArgumentParser( + description=('This dispatcher handles the' + ' special tags and hook' + ' execution'), + ) + for arg in ('change', 'project', 'branch', 'commit', 'is-draft', + 'change-url', 'author', 'comment'): + parser.add_argument('--' + arg, + action='store', + required=True) + parser.add_argument('-v', '--verbose', action='store_true', default=False) + return parser + + +def give_warning(gerrit, commit, project, branches): + msg = ("WARNING: This patch was not merged yet on all the newer " + "branches\n Missing on: %s" % ', '.join(branches)) + gerrit.review(commit=commit, project=project, message=msg) + + +def main(): + parser = get_parser() + args = parser.parse_args() + if args.verbose: + loglevel = logging.DEBUG + else: + loglevel = logging.INFO + logging.basicConfig(stream=sys.stdout, + level=loglevel, + format='%(asctime)s::' + + str(os.getpid()) + + '::%(levelname)s::%(message)s') + config = load_config() + logging.debug("STARTING::PARAMS %s" % sys.argv) + gerrit = Gerrit(config['GERRIT_SRV']) + unmerged_branches = get_unmerged_newer_branches( + gerrit, + args.change, + args.branch) + if unmerged_branches: + give_warning(gerrit, args.commit, args.project, unmerged_branches) + logging.info("FINISHED") + + +if __name__ == '__main__': + main() diff --git a/hooks/hook-dispatcher b/hooks/hook-dispatcher index 93595f9..fc09c76 100755 --- a/hooks/hook-dispatcher +++ b/hooks/hook-dispatcher @@ -235,6 +235,8 @@ ## add the hooks lib dir to the path os.environ["PATH"] = os.environ["PATH"] + ':' \ + os.path.dirname(os.path.realpath(__file__)) + '/lib' + os.environ["PYTHONPATH"] = os.environ.get("PYTHONPATH", "") \ + + ':' + os.path.dirname(os.path.realpath(__file__)) + '/lib' hooks.sort() params = sys.argv[1:] for hook in hooks: @@ -297,7 +299,7 @@ def main(): logpath = os.path.join(os.path.dirname(__file__), '..', 'logs') logging.basicConfig(filename=os.path.join(logpath, 'gerrit.hooks.log'), - level=logging.INFO, + level=logging.DEBUG, format='%(asctime)s::' + str(os.getpid()) + '::%(levelname)s::%(message)s') diff --git a/hooks/lib/config.py b/hooks/lib/config.py new file mode 100644 index 0000000..5d28a17 --- /dev/null +++ b/hooks/lib/config.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +#encoding: utf-8 +import re +import os +from os.path import ( + abspath, + dirname, + join as pjoin, + exists as fexists, +) +import logging + + +logger = logging.getLogger(__name__) + + +CONF_FILES = [ + pjoin(dirname(abspath(__file__)), '..', 'config'), + pjoin(os.environ.get('GIT_DIR', ''), 'hooks', 'config'), +] + + +def unquote(string): + if re.match(r'^(\'|").*', string): + return string[1:-1] + else: + return string + + +class Config(dict): + def __init__(self, files=None): + files = files or [] + self.files = [] + for fname in files: + if not fname: + continue + if fexists(fname): + self.read(fname) + else: + logger.warn('Unable to read config file %s' % fname) + + def read(self, filename): + linenum = 0 + for line in open(filename, 'r'): + if not line.strip() or line.strip().startswith('#'): + continue + if '=' not in line: + logger.warn('Malformed config entry at %s line %d :\n%s' + % (filename, linenum, line)) + key, val = map(lambda x: unquote(str.strip(x)), + line.split('=', 1)) + self[key] = val + linenum += 1 + self.files.append(filename) + + def __getitem__(self, item): + try: + return dict.__getitem__(self, item) + except KeyError: + logger.error('Unable to get config value %s from any of the ' + 'config files [%s]' % (item,', '.join(self.files))) + raise + + +def load_config(): + return Config(CONF_FILES) diff --git a/hooks/lib/config.pyc b/hooks/lib/config.pyc new file mode 100644 index 0000000..e7a6f0f --- /dev/null +++ b/hooks/lib/config.pyc Binary files differ diff --git a/hooks/lib/gerrit.py b/hooks/lib/gerrit.py new file mode 100644 index 0000000..3b09a42 --- /dev/null +++ b/hooks/lib/gerrit.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +#encoding: utf-8 +import subprocess +import json +import logging + +logger = logging.getLogger(__name__) + + +class Gerrit(object): + def __init__(self, server): + self.server = server + self.cmd = ( + 'ssh', + '-o', 'UserKnownHostsFile=/dev/null', + '-o', 'StrictHostKeyChecking=no', + self.server, + '-p', '29418', + 'gerrit', + ) + + def generate_cmd(self, action, *options): + cmd = list(self.cmd) + cmd.append(action) + cmd.extend(options) + return cmd + + def review(self, commit, message, project, verify=None, review=None): + gerrit_cmd = self.generate_cmd( + 'review', + commit, + '--message="%s"' % message, + '--project=%s' % project, + ) + if verify is not None: + gerrit_cmd.append('--verify=%s' % str(verify)) + if review is not None: + gerrit_cmd.append('--code-review=%s' % str(review)) + cmd = subprocess.Popen(gerrit_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, err = cmd.communicate() + if cmd.returncode: + raise Exception("Execution of %s returned %d:\n%s" + % (' '.join(gerrit_cmd), + cmd.returncode, + err)) + return 0 + + def query(self, query, out_format='json'): + gerrit_cmd = self.generate_cmd( + 'query', + '--format=%s' % out_format, + query, + ) + logger.debug("Executing %s" % ' '.join(gerrit_cmd)) + cmd = subprocess.Popen(gerrit_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, err = cmd.communicate() + if cmd.returncode: + raise Exception("Execution of %s returned %d:\n%s" + % (' '.join(gerrit_cmd), + cmd.returncode, + err)) + if out_format == 'json': + res = [] + try: + for line in out.splitlines(): + res.append(json.loads(line)) + except ValueError: + logger.error("Unable to decode json from:\n%s" % line) + raise + else: + res = out + return res diff --git a/hooks/lib/gerrit.pyc b/hooks/lib/gerrit.pyc new file mode 100644 index 0000000..9f762aa --- /dev/null +++ b/hooks/lib/gerrit.pyc Binary files differ -- To view, visit http://gerrit.ovirt.org/24208 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I985113f5a228ccc128eaad3dbf45c918b7fc093a Gerrit-PatchSet: 1 Gerrit-Project: gerrit-admin Gerrit-Branch: master Gerrit-Owner: David Caro <[email protected]> _______________________________________________ Engine-patches mailing list [email protected] http://lists.ovirt.org/mailman/listinfo/engine-patches
