Commit d975b30c authored by bradnelson's avatar bradnelson Committed by Commit bot

Automatically CC folks listed in CC= lines.

Detect CC=<users> lines the way we detect R= and TBR=.
Add these as CC'ed users for rietveld and gerrit.

R=iannucci@chromium.org,hinoka@chromium.org,dnj@chromium.org

Review-Url: https://codereview.chromium.org/2433323004
parent 41e3a6c5
...@@ -2125,6 +2125,8 @@ class _RietveldChangelistImpl(_ChangelistCodereviewBase): ...@@ -2125,6 +2125,8 @@ class _RietveldChangelistImpl(_ChangelistCodereviewBase):
else: else:
cc = self.GetCCList() cc = self.GetCCList()
cc = ','.join(filter(None, (cc, ','.join(options.cc)))) cc = ','.join(filter(None, (cc, ','.join(options.cc))))
if change_desc.get_cced():
cc = ','.join(filter(None, (cc, ','.join(change_desc.get_cced()))))
if cc: if cc:
upload_args.extend(['--cc', cc]) upload_args.extend(['--cc', cc])
...@@ -2772,6 +2774,8 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase): ...@@ -2772,6 +2774,8 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
if options.cc: if options.cc:
cc.extend(options.cc) cc.extend(options.cc)
cc = filter(None, [email.strip() for email in cc]) cc = filter(None, [email.strip() for email in cc])
if change_desc.get_cced():
cc.extend(change_desc.get_cced())
if cc: if cc:
gerrit_util.AddReviewers( gerrit_util.AddReviewers(
self._GetGerritHost(), self.GetIssue(), cc, is_reviewer=False) self._GetGerritHost(), self.GetIssue(), cc, is_reviewer=False)
...@@ -2898,6 +2902,7 @@ def _get_bug_line_values(default_project, bugs): ...@@ -2898,6 +2902,7 @@ def _get_bug_line_values(default_project, bugs):
class ChangeDescription(object): class ChangeDescription(object):
"""Contains a parsed form of the change description.""" """Contains a parsed form of the change description."""
R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$' R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$'
CC_LINE = r'^[ \t]*(CC)[ \t]*=[ \t]*(.*?)[ \t]*$'
BUG_LINE = r'^[ \t]*(BUG)[ \t]*=[ \t]*(.*?)[ \t]*$' BUG_LINE = r'^[ \t]*(BUG)[ \t]*=[ \t]*(.*?)[ \t]*$'
def __init__(self, description): def __init__(self, description):
...@@ -3045,6 +3050,12 @@ class ChangeDescription(object): ...@@ -3045,6 +3050,12 @@ class ChangeDescription(object):
if match and (not tbr_only or match.group(1).upper() == 'TBR')] if match and (not tbr_only or match.group(1).upper() == 'TBR')]
return cleanup_list(reviewers) return cleanup_list(reviewers)
def get_cced(self):
"""Retrieves the list of reviewers."""
matches = [re.match(self.CC_LINE, line) for line in self._description_lines]
cced = [match.group(2).strip() for match in matches if match]
return cleanup_list(cced)
def get_approving_reviewers(props): def get_approving_reviewers(props):
"""Retrieves the reviewers that approved a CL from the issue properties with """Retrieves the reviewers that approved a CL from the issue properties with
......
...@@ -599,14 +599,14 @@ class TestGitCl(TestCase): ...@@ -599,14 +599,14 @@ class TestGitCl(TestCase):
] ]
@staticmethod @staticmethod
def _cmd_line(description, args, similarity, find_copies, private): def _cmd_line(description, args, similarity, find_copies, private, cc):
"""Returns the upload command line passed to upload.RealMain().""" """Returns the upload command line passed to upload.RealMain()."""
return [ return [
'upload', '--assume_yes', '--server', 'upload', '--assume_yes', '--server',
'https://codereview.example.com', 'https://codereview.example.com',
'--message', description '--message', description
] + args + [ ] + args + [
'--cc', 'joe@example.com', '--cc', ','.join(['joe@example.com'] + cc),
] + (['--private'] if private else []) + [ ] + (['--private'] if private else []) + [
'--git_similarity', similarity or '50' '--git_similarity', similarity or '50'
] + (['--git_no_find_copies'] if find_copies == False else []) + [ ] + (['--git_no_find_copies'] if find_copies == False else []) + [
...@@ -620,7 +620,8 @@ class TestGitCl(TestCase): ...@@ -620,7 +620,8 @@ class TestGitCl(TestCase):
returned_description, returned_description,
final_description, final_description,
reviewers, reviewers,
private=False): private=False,
cc=None):
"""Generic reviewer test framework.""" """Generic reviewer test framework."""
self.mock(git_cl.sys, 'stdout', StringIO.StringIO()) self.mock(git_cl.sys, 'stdout', StringIO.StringIO())
try: try:
...@@ -636,6 +637,7 @@ class TestGitCl(TestCase): ...@@ -636,6 +637,7 @@ class TestGitCl(TestCase):
find_copies = None find_copies = None
private = '--private' in upload_args private = '--private' in upload_args
cc = cc or []
self.calls = self._upload_calls(similarity, find_copies, private) self.calls = self._upload_calls(similarity, find_copies, private)
...@@ -653,7 +655,7 @@ class TestGitCl(TestCase): ...@@ -653,7 +655,7 @@ class TestGitCl(TestCase):
def check_upload(args): def check_upload(args):
cmd_line = self._cmd_line(final_description, reviewers, similarity, cmd_line = self._cmd_line(final_description, reviewers, similarity,
find_copies, private) find_copies, private, cc)
self.assertEquals(cmd_line, args) self.assertEquals(cmd_line, args)
return 1, 2 return 1, 2
self.mock(git_cl.upload, 'RealMain', check_upload) self.mock(git_cl.upload, 'RealMain', check_upload)
...@@ -716,13 +718,15 @@ class TestGitCl(TestCase): ...@@ -716,13 +718,15 @@ class TestGitCl(TestCase):
def test_reviewer_multiple(self): def test_reviewer_multiple(self):
# Handles multiple R= or TBR= lines. # Handles multiple R= or TBR= lines.
description = ( description = (
'Foo Bar\nTBR=reviewer@example.com\nBUG=\nR=another@example.com') 'Foo Bar\nTBR=reviewer@example.com\nBUG=\nR=another@example.com\n'
'CC=more@example.com,people@example.com')
self._run_reviewer_test( self._run_reviewer_test(
[], [],
'desc\n\nBUG=', 'desc\n\nBUG=',
description, description,
description, description,
['--reviewers=another@example.com,reviewer@example.com']) ['--reviewers=another@example.com,reviewer@example.com'],
cc=['more@example.com', 'people@example.com'])
def test_reviewer_send_mail(self): def test_reviewer_send_mail(self):
# --send-mail can be used without -r if R= is used # --send-mail can be used without -r if R= is used
...@@ -848,10 +852,11 @@ class TestGitCl(TestCase): ...@@ -848,10 +852,11 @@ class TestGitCl(TestCase):
squash_mode='default', squash_mode='default',
expected_upstream_ref='origin/refs/heads/master', expected_upstream_ref='origin/refs/heads/master',
ref_suffix='', notify=False, ref_suffix='', notify=False,
post_amend_description=None, issue=None): post_amend_description=None, issue=None, cc=None):
if post_amend_description is None: if post_amend_description is None:
post_amend_description = description post_amend_description = description
calls = [] calls = []
cc = cc or []
if squash_mode == 'default': if squash_mode == 'default':
calls.extend([ calls.extend([
...@@ -956,7 +961,7 @@ class TestGitCl(TestCase): ...@@ -956,7 +961,7 @@ class TestGitCl(TestCase):
((['git', 'config', 'rietveld.cc'],), ''), ((['git', 'config', 'rietveld.cc'],), ''),
((['AddReviewers', 'chromium-review.googlesource.com', ((['AddReviewers', 'chromium-review.googlesource.com',
123456 if squash else None, 123456 if squash else None,
['joe@example.com'], False],), ''), ['joe@example.com'] + cc, False],), ''),
] ]
calls += cls._git_post_upload_calls() calls += cls._git_post_upload_calls()
return calls return calls
...@@ -972,7 +977,8 @@ class TestGitCl(TestCase): ...@@ -972,7 +977,8 @@ class TestGitCl(TestCase):
ref_suffix='', ref_suffix='',
notify=False, notify=False,
post_amend_description=None, post_amend_description=None,
issue=None): issue=None,
cc=None):
"""Generic gerrit upload test framework.""" """Generic gerrit upload test framework."""
if squash_mode is None: if squash_mode is None:
if '--no-squash' in upload_args: if '--no-squash' in upload_args:
...@@ -983,6 +989,7 @@ class TestGitCl(TestCase): ...@@ -983,6 +989,7 @@ class TestGitCl(TestCase):
squash_mode = 'default' squash_mode = 'default'
reviewers = reviewers or [] reviewers = reviewers or []
cc = cc or []
self.mock(git_cl.sys, 'stdout', StringIO.StringIO()) self.mock(git_cl.sys, 'stdout', StringIO.StringIO())
self.mock(git_cl.gerrit_util, 'CookiesAuthenticator', self.mock(git_cl.gerrit_util, 'CookiesAuthenticator',
CookiesAuthenticatorMockFactory(same_cookie='same_cred')) CookiesAuthenticatorMockFactory(same_cookie='same_cred'))
...@@ -1002,7 +1009,7 @@ class TestGitCl(TestCase): ...@@ -1002,7 +1009,7 @@ class TestGitCl(TestCase):
expected_upstream_ref=expected_upstream_ref, expected_upstream_ref=expected_upstream_ref,
ref_suffix=ref_suffix, notify=notify, ref_suffix=ref_suffix, notify=notify,
post_amend_description=post_amend_description, post_amend_description=post_amend_description,
issue=issue) issue=issue, cc=cc)
# Uncomment when debugging. # Uncomment when debugging.
# print '\n'.join(map(lambda x: '%2i: %s' % x, enumerate(self.calls))) # print '\n'.join(map(lambda x: '%2i: %s' % x, enumerate(self.calls)))
git_cl.main(['upload'] + upload_args) git_cl.main(['upload'] + upload_args)
...@@ -1058,12 +1065,14 @@ class TestGitCl(TestCase): ...@@ -1058,12 +1065,14 @@ class TestGitCl(TestCase):
def test_gerrit_reviewer_multiple(self): def test_gerrit_reviewer_multiple(self):
self._run_gerrit_upload_test( self._run_gerrit_upload_test(
[], [],
'desc\nTBR=reviewer@example.com\nBUG=\nR=another@example.com\n\n' 'desc\nTBR=reviewer@example.com\nBUG=\nR=another@example.com\n'
'CC=more@example.com,people@example.com\n\n'
'Change-Id: 123456789\n', 'Change-Id: 123456789\n',
['reviewer@example.com', 'another@example.com'], ['reviewer@example.com', 'another@example.com'],
squash=False, squash=False,
squash_mode='override_nosquash', squash_mode='override_nosquash',
ref_suffix='%l=Code-Review+1') ref_suffix='%l=Code-Review+1',
cc=['more@example.com', 'people@example.com'])
def test_gerrit_upload_squash_first_is_default(self): def test_gerrit_upload_squash_first_is_default(self):
# Mock Gerrit CL description to indicate the first upload. # Mock Gerrit CL description to indicate the first upload.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment