--- /dev/null
+#!/usr/bin/env
+# -*- coding: UTF-8 -*-
+# vim: sw=4 ts=4 expandtab ai
+#
+# Copyright (c) 2013 Intel, Inc.
+# License: GPLv2
+# Author: Alexander Kanevskiy <alexander.kanevskiy@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License, version 2,
+# as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+
+
+"""
+Add reviewers
+"""
+
+import netrc
+import urlparse
+import types
+import re
+from gerritrest import GerritREST
+
+from pprint import pformat
+
+def to_bool(arg):
+ """Convert string to bool"""
+ if isinstance(arg, types.StringTypes):
+ return bool(arg.lower() in ("true", "yes", "1"))
+ else:
+ return bool(arg)
+
+
+def patchset_created(hook = None, config = None, logger = None, params = None,
+ extra_params = None):
+
+ if not config.has_option("gerrit", "url"):
+ logger.error("Gerrit URL is not defined in config!")
+ return 1
+
+ gerrit_url = config.get("gerrit", "url")
+
+ if not params.change:
+ logger.warning("Change not defined")
+ return 1
+ if not params.project:
+ logger.warning("Project not defined")
+ return 1
+ if not params.branch:
+ logger.warning("Branch not defined")
+ return 1
+
+ if config.has_option("gerrit", "username") \
+ and config.has_option("gerrit", "password"):
+ user = config.get("gerrit", "username")
+ password = config.get("gerrit", "password")
+ else:
+ # Fallback to ~/.netrc
+ try:
+ user, _, password = \
+ netrc.netrc().hosts[urlparse.urlparse(gerrit_url).netloc]
+ except KeyError, exc:
+ logger.error("Unable to find credentials in ~/.netrc")
+ return -1
+
+ rules = {}
+ need_project_groups = False
+ need_files = False
+ need_owners = False
+ rules_enabled = False
+
+ for section in config.sections():
+ if len(section) < 17 or section[:16] not in \
+ ("patchset_created", "patchset-created"):
+ continue
+ rule = section[17:]
+ rules[rule] = dict(config.items(section))
+ rules[rule]['enabled'] = to_bool(rules[rule].get('enabled', None))
+ if rules[rule]['enabled']:
+ rules_enabled = True
+ rule_type = rules[rule].get('type', None)
+ if rule_type == 'project_group':
+ need_project_groups = True
+ elif rule_type == 'change_file':
+ need_files = True
+ elif rule_type == 'author':
+ need_owners = True
+ else:
+ logger.warning("Unknown type(%s) in %s", rule_type, section)
+
+ if not rules_enabled:
+ logger.warning("No rules enabled")
+ return 0
+
+ gerrit = GerritREST(gerrit_url, user, password)
+
+ # get change information from gerrit
+ change_query = "change:%s+project:%s+branch:%s" % \
+ (params.change, params.project, params.branch)
+
+ logger.debug("Getting information about change: %s", change_query)
+ change_info = gerrit.get_changes(query=change_query, all_files=need_files,
+ all_revisions=True, detailed_accounts=need_owners, detailed_labels=True)
+ if not change_info:
+ logger.error("Change not found!")
+ return 2
+ elif len(change_info) > 1:
+ logger.warning("More than one change like that found! Using first one.")
+ change_info = change_info[0]
+
+ if need_project_groups:
+ logger.debug("Getting groups info for project %s", params.project)
+ project_groups = gerrit.get_groups(project=params.project)
+
+ reviewers = []
+ reviewer_groups = []
+ for rule in rules:
+ if not rules[rule]['enabled']:
+ continue
+ rule_type = rules[rule].get('type', None)
+ if rule_type == 'project_group':
+ if not rules[rule].get('match', None):
+ logger.warning("No match defined for %s", rule)
+ continue
+ group_match = re.compile(rules[rule]['match'])
+ for group in project_groups:
+ if group_match.search(group):
+ logger.debug("[%s] Matched group %s", rule, group)
+ reviewer_groups.append(group)
+ elif rule_type == 'change_file':
+ if not rules[rule].get('match', None):
+ logger.warning("No match defined for %s", rule)
+ continue
+ file_match = re.compile(rules[rule]['match'])
+ if params.commit and params.commit in change_info['revisions']:
+ files = change_info['revisions'][params.commit].get('files', {})
+ else:
+ files = change_info['revisions']\
+ [change_info['current_revision']].get('files',{})
+ matched = False
+ for change_file in files:
+ if file_match.search(change_file):
+ matched = True
+ logger.debug("[%s] Matched file: %s", rule, change_file)
+ break
+ if matched:
+ for reviewer in re.split('\s*[,;]\s*',
+ rules[rule].get('reviewer',"")):
+ reviewers.append(reviewer.strip())
+ for group in re.split('\s*[,;]\s*',
+ rules[rule].get('reviewer_group',"")):
+ reviewer_groups.append(group.strip())
+ elif rule_type == 'author':
+ if not rules[rule].get('match', None):
+ logger.warning("No match defined for %s", rule)
+ continue
+ owner_match = re.compile(rules[rule]['match'], re.IGNORECASE)
+ owner_info = []
+ owner_email = change_info['owner'].get('email', None)
+ if owner_email:
+ owner_info.append(owner_email)
+ owner_name = change_info['owner'].get('name', None)
+ if owner_name:
+ owner_info.append(owner_name)
+ logger.debug("match: %s Author: %s", rules[rule]['match'], owner_info)
+ matched = False
+ for info in owner_info:
+ if owner_match.search(info):
+ matched = True
+ logger.debug("[%s] Matched owner: %s", rule, info)
+ break
+ if matched:
+ for reviewer in re.split('\s*[,;]\s*',
+ rules[rule].get('reviewer',"")):
+ reviewers.append(reviewer.strip())
+ for group in re.split('\s*[,;]\s*',
+ rules[rule].get('reviewer_group',"")):
+ reviewer_groups.append(group.strip())
+
+ if reviewers:
+ for reviewer in reviewers:
+ logger.debug("Adding reviewer %s", reviewer)
+ if reviewer_groups:
+ for reviewer_group in reviewer_groups:
+ logger.debug("Adding reviewer group %s", reviewer_group)
+
+
+
+