revset and template keywords for finding reviewers
authorGregory Szorc <gps@mozilla.com>
Fri, 08 Nov 2013 09:08:41 -0800
changeset 70 cbc08bc115936d4ccfb766aa518782db83f855f7
parent 69 1af548d52f11c1da60f242b7c44571b186e1f4cb
child 71 fb1e21435cdffb77c492547c82d22521363546a8
push id50
push usergszorc@mozilla.com
push dateFri, 08 Nov 2013 17:08:44 +0000
revset and template keywords for finding reviewers
__init__.py
mozautomation/commitparser.py
--- a/__init__.py
+++ b/__init__.py
@@ -119,29 +119,34 @@ bug(BUG)
    Retreive changesets that reference a specific bug. e.g. ``bug(784841)``.
 
 dontbuild()
    Retrieve changesets that are marked as DONTBUILD.
 
 me()
    Retrieve changesets that you are involved with.
 
-   Currently, this only retrieves changesets you authored (via ui.username).
-   In the future, this extension will index review syntax in commit messages
-   and return changesets that you reviewed.
+   This retrieves changesets you authored (via ui.username) as well as
+   changesets you reviewed (via mozext.ircnick).
 
 nobug()
    Retrieve changesets that don't reference a bug in the commit message.
 
 pushhead([TREE])
    Retrieve changesets that are push heads.
 
 firstpushtree(TREE)
    Retrieve changesets that initially landed on the specified tree.
 
+reviewer(REVIEWER)
+   Retrieve changesets there were reviewed by the specified person.
+
+   The reviewer string matches the *r=* string specified in the commit. In
+   the future, we may consult a database of known aliases, etc.
+
 tree(TREE)
    Retrieve changesets that are currently in the specified tree.
 
    Trees are specified with a known alias. e.g. ``tree(central)``.
 
    It's possible to see which changesets are in some tree but not others.
    e.g. to find changesets in *inbound* that haven't merged to *central*
    yet, do ``tree(inbound) - tree(central)``.
@@ -275,17 +280,20 @@ from mercurial import (
     hg,
     util,
 )
 
 from mozautomation.changetracker import (
     ChangeTracker,
 )
 
-from mozautomation.commitparser import parse_bugs
+from mozautomation.commitparser import (
+    parse_bugs,
+    parse_reviewers,
+)
 
 from mozautomation.repository import (
     MercurialRepository,
     RELEASE_TREES,
     REPOS,
     resolve_trees_to_official,
     resolve_trees_to_uris,
     resolve_uri_to_tree,
@@ -719,25 +727,31 @@ def revset_me(repo, subset, x):
     """
     if x:
         raise ParseError(_('me() does not take any arguments'))
 
     me = repo.ui.config('ui', 'username')
     if not me:
         raise util.Abort(_('"[ui] username" must be set to use me()'))
 
+    ircnick = repo.ui.config('mozext', 'ircnick')
+
     n = encoding.lower(me)
     kind, pattern, matcher = revset._substringmatcher(n)
 
     for r in subset:
-        if matcher(encoding.lower(repo[r].user())):
+        ctx = repo[r]
+        if matcher(encoding.lower(ctx.user())):
             yield r
             continue
 
-        # TODO check reviewer blocks.
+        if ircnick:
+            if ircnick in parse_reviewers(ctx.description()):
+                yield r
+                continue
 
 
 def revset_nobug(repo, subset, x):
     if x:
         raise ParseError(_('nobug() does not take any arguments'))
 
     return [r for r in subset if not parse_bugs(repo[r].description())]
 
@@ -821,27 +835,48 @@ def revset_pushhead(repo, subset, x):
         node = repo[r].node()
 
         for push in repo.changetracker.pushes_for_changeset(node):
             if str(push[4]) == node:
                 yield r
                 break
 
 
+def revset_reviewer(repo, subset, x):
+    """``reviewer(REVIEWER)``
+    Changesets reviewed by a specific person.
+    """
+    n = revset.getstring(x, _('reviewer() requires a string argument.'))
+
+    return [r for r in subset if n in parse_reviewers(repo[r].description())]
+
+
 def template_bug(repo, ctx, **args):
     """:bug: String. The bug this changeset is most associated with."""
     bugs = parse_bugs(ctx.description())
     return bugs[0] if bugs else None
 
 
 def template_bugs(repo, ctx, **args):
     """:bugs: List of ints. The bugs associated with this changeset."""
     return parse_bugs(ctx.description())
 
 
+def template_reviewer(repo, ctx, **args):
+    """:reviewer: String. The first reviewer of this changeset."""
+    reviewers = parse_reviewers(ctx.description())
+    return reviewers[0] if reviewers else None
+
+
+def template_reviewers(repo, ctx, **args):
+    """:reviewers: List of strings. The reviewers associated with tis
+    changeset."""
+    return parse_reviewers(ctx.description())
+
+
 def _compute_first_version(repo, ctx, what, cache):
     rev = ctx.rev()
     cache_key = '%s_ancestors' % what
 
     if cache_key not in cache:
         versions = getattr(repo, '_%s_releases' % what)()
         cache[cache_key] = repo._earliest_version_ancestors(versions)
 
@@ -1006,21 +1041,24 @@ def extsetup(ui):
 
     extensions.wrapcommand(commands.table, 'pull', pullexpand)
 
     revset.symbols['bug'] = revset_bug
     revset.symbols['dontbuild'] = revset_dontbuild
     revset.symbols['me'] = revset_me
     revset.symbols['nobug'] = revset_nobug
     revset.symbols['pushhead'] = revset_pushhead
+    revset.symbols['reviewer'] = revset_reviewer
     revset.symbols['tree'] = revset_tree
     revset.symbols['firstpushtree'] = revset_firstpushtree
 
     templatekw.keywords['bug'] = template_bug
     templatekw.keywords['bugs'] = template_bugs
+    templatekw.keywords['reviewer'] = template_reviewer
+    templatekw.keywords['reviewers'] = template_reviewers
     templatekw.keywords['firstrelease'] = template_firstrelease
     templatekw.keywords['firstbeta'] = template_firstbeta
     templatekw.keywords['firstaurora'] = template_firstaurora
     templatekw.keywords['firstnightly'] = template_firstnightly
     templatekw.keywords['auroradate'] = template_auroradate
     templatekw.keywords['nightlydate'] = template_nightlydate
     templatekw.keywords['firstpushuser'] = template_firstpushuser
     templatekw.keywords['firstpushtree'] = template_firstpushtree
--- a/mozautomation/commitparser.py
+++ b/mozautomation/commitparser.py
@@ -1,25 +1,37 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # This module contains utilities for parsing commit messages.
 
 import re
 
+# These regular expressions are not very robust. Specifically, they fail to
+# handle lists well.
+
 BUG_RE = re.compile(
     r'''# bug followed by any sequence of numbers, or
         # a standalone sequence of numbers
          (
            (?:
              bug |
              b= |
              # a sequence of 5+ numbers preceded by whitespace
              (?=\b\#?\d{5,}) |
              # numbers at the very beginning
              ^(?=\d)
            )
            (?:\s*\#?)(\d+)
          )''', re.I | re.X)
 
+REVIEW_RE = re.compile(r'[ra][=?]+(\w[^ ]+)')
+
+LIST_RE = re.compile(r'[;\.,\+\/\\]')
+
 def parse_bugs(s):
     return [int(m[1]) for m in BUG_RE.findall(s)]
+
+def parse_reviewers(s):
+    for r in REVIEW_RE.findall(s):
+        for part in LIST_RE.split(r):
+            yield part.strip('[](){}')