Refactored logic of matching rules
authorAlexander Kanevskiy <alexander.kanevskiy@intel.com>
Thu, 15 Aug 2013 22:57:24 +0000 (01:57 +0300)
committerAlexander Kanevskiy <alexander.kanevskiy@intel.com>
Thu, 15 Aug 2013 22:57:24 +0000 (01:57 +0300)
plugins/add_reviewers.py

index d3c979d81f92d8757cdc866452b07f07a6a474bf..2ea6527f20c742d3437a90f7fd6a79d038786f61 100644 (file)
@@ -35,13 +35,35 @@ def to_bool(arg):
         return bool(arg)
 
 
+def gerrit_client(config):
+    """
+    Returns tuple: GerritREST instance / error message
+    """
+    if not config.has_option("gerrit", "url"):
+        return (None, 'Gerrit URL is not defined in config!')
+
+    gerrit_url = config.get("gerrit", "url")
+    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:
+            return (None, "Unable to find credentials in ~/.netrc")
+
+    return (GerritREST(gerrit_url, user, password), None)
+
+
 def patchset_created(hook = None, config = None, logger = None, params = None,
                         extra_params = None):
+    """Main entry on add_reviewers plugin"""
 
     section_name = 'add_reviewers'
     error_message = ''
-    if not config.has_option("gerrit", "url"):
-        error_message = 'Gerrit URL is not defined in config!'
     if not params.change:
         error_message = 'Change not defined'
     if not params.project:
@@ -52,29 +74,16 @@ def patchset_created(hook = None, config = None, logger = None, params = None,
         logger.error(error_message)
         return 1
 
-    gerrit_url = config.get("gerrit", "url")
-
-
-    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:
-            logger.error("Unable to find credentials in ~/.netrc")
-            return -1
-
-    rules, rop = get_rules(config, section_name, logger)
+    rules, rop = get_rules(config, section_name)
 
     if not rop['enabled']:
         logger.warning("No rules enabled")
         return 0
 
-    gerrit = GerritREST(gerrit_url, user, password)
+    gerrit, error_message = gerrit_client(config)
+    if error_message:
+        logger.error(error_message)
+        return 1
 
     # get change information from gerrit
     change_query = "change:%s+project:%s+branch:%s" % \
@@ -100,67 +109,16 @@ def patchset_created(hook = None, config = None, logger = None, params = None,
     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(r'\s*[,;]\s*',
-                                        rules[rule].get('reviewer',"")):
-                    reviewers.append(reviewer.strip())
-                for group in re.split(r'\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(r'\s*[,;]\s*',
-                                        rules[rule].get('reviewer',"")):
-                    reviewers.append(reviewer.strip())
-                for group in re.split(r'\s*[,;]\s*',
-                                        rules[rule].get('reviewer_group',"")):
-                    reviewer_groups.append(group.strip())
+        if rules[rule]['enabled'] and \
+            match_branch(rules[rule], params.branch) and \
+            match_project(rules[rule], params.project) and \
+            match_change_author(rule, rules[rule], change_info, logger) and \
+            match_change_file(rule, rules[rule], change_info, params.commit,
+                logger):
+
+            reviewers, reviewer_groups = return_reviewers(rules[rule])
+            reviewer_groups.extend(
+                match_project_group(rule, rules[rule], project_groups, logger))
 
     try:
         dry_run = config.getboolean("add_reviewers", "dry_run")
@@ -173,7 +131,85 @@ def patchset_created(hook = None, config = None, logger = None, params = None,
         add_reviewers(reviewer_groups, gerrit, change_info, dry_run, logger)
 
 
-def get_rules(config, section_name, logger):
+def match_project(rule, project):
+    """Checks if project rule is present and project matches it."""
+    if project and rule.get('project_regexp', None):
+        project_match = re.compile(rule['project_regexp'])
+        if not project_match.search(project):
+            return False
+    return True
+
+def match_branch(rule, branch):
+    """Checks if branch rule is present and branch matches it."""
+    if branch and rule.get('branch_regexp', None):
+        branch_match = re.compile(rule['branch_regexp'])
+        if not branch_match.search(branch):
+            return False
+    return True
+
+def match_change_file(name, rule, change, commit, logger):
+    """Matches files changed in this change"""
+
+    result = True
+    if rule.get('file_regexp', None):
+        file_match = re.compile(rule['file_regexp'])
+        if commit and commit in change['revisions']:
+            files = change['revisions'][commit].get('files', {})
+        else:
+            files = change['revisions']\
+                    [change['current_revision']].get('files',{})
+        result = False
+        for change_file in files:
+            if file_match.search(change_file):
+                result = True
+                logger.debug("[%s] Matched file: %s", name, change_file)
+                break
+    return result
+
+def match_change_author(name, rule, change, logger):
+    """Matches change author"""
+    result = True
+    if rule.get('author_regexp', None):
+        owner_match = re.compile(rule['author_regexp'], re.IGNORECASE)
+        owner_info = []
+        owner_email = change['owner'].get('email', None)
+        if owner_email:
+            owner_info.append(owner_email)
+        owner_name = change['owner'].get('name', None)
+        if owner_name:
+            owner_info.append(owner_name)
+        logger.debug("[%s] Author info %s", name, owner_info)
+        result = False
+        for info in owner_info:
+            if owner_match.search(info):
+                result = True
+                logger.debug("[%s] Matched owner: %s", rule, info)
+                break
+    return result
+
+def match_project_group(name, rule, project_groups, logger):
+    """Matches group associated with project"""
+    result = []
+    if rule.get('project_groups_regexp', None):
+        group_match = re.compile(rule['project_groups_regexp'])
+        for group in project_groups:
+            if group_match.search(group):
+                logger.debug("[%s] Matched group %s", name, group)
+                result.append(group)
+    return result
+
+def return_reviewers(rule):
+    """Returns reviewers and reviewer groups from rule"""
+    reviewers = set()
+    reviewer_groups = set()
+    for reviewer in re.split(r'\s*[,;]\s*', rule.get('reviewer',"")):
+        reviewers.add(reviewer.strip())
+    for group in re.split(r'\s*[,;]\s*', rule.get('reviewer_group',"")):
+        reviewer_groups.add(group.strip())
+    return list(reviewers), list(reviewer_groups)
+
+
+def get_rules(config, section_name):
     """
     Parses config options and returns rules + options
     """
@@ -195,15 +231,12 @@ def get_rules(config, section_name, logger):
         rules[rule]['enabled'] = to_bool(rules[rule].get('enabled', None))
         if rules[rule]['enabled']:
             options['enabled'] = True
-            rule_type = rules[rule].get('type', None)
-            if rule_type == 'project_group':
+            if rules[rule].get('project_groups_regexp', None):
                 options['project_groups'] = True
-            elif rule_type == 'change_file':
+            if rules[rule].get('file_regexp', None):
                 options['files'] = True
-            elif rule_type == 'author':
+            if rules[rule].get('author_regexp', None):
                 options['owners'] = True
-            else:
-                logger.warning("Unknown type(%s) in %s", rule_type, section)
     return rules, options