#!/usr/bin/env python3
# Copyright 2021 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
Upload comments to gerrit.
mph = "make Peter happy".
Previously called mch ("make Casey happy").
"""
import optparse
import re
import sys
import codereview_parser
try:
import gerrit_util
import git_cl
except:
print('depot_tools not found; try appending the module path to your' +
' python path')
sys.exit(1)
def DieWithError(message):
print(message, file=sys.stderr)
sys.exit(1)
# These are additions to gerrit_util.py from depot_tools not yet ready to send
# out for review.
def CreateDraft(host, change, revision, path, line, msg=''):
"""Create a draft gerrit comment."""
path = 'changes/%s/revisions/%s/drafts' % (change, revision)
body = {'path': path, 'line': line, 'message': msg, 'unresolved': True}
conn = gerrit_util.CreateHttpConn(host, path, reqtype='PUT', body=body)
return gerrit_util.ReadHttpJsonResponse(conn)
def SetReview(host, change, revision, msg, lgtm, comments):
"""Sets a review in gerrit."""
path = 'changes/%s/revisions/%s/review' % (change, revision)
body = {'message': msg, 'comments': comments}
if lgtm:
body['labels'] = {'code-review': 1}
conn = gerrit_util.CreateHttpConn(host, path, reqtype='POST', body=body)
return gerrit_util.ReadHttpJsonResponse(conn)
class GerritParser(codereview_parser.Parser):
def __init__(self, file):
codereview_parser.Parser.__init__(self, file)
self._HOST = 'chromium-review.googlesource.com'
self._issue_number = 0
self._patchset = 0
self._change_id = ''
self._revision_id = ""
self._overall_comment = ''
self._comments = {}
def OnError(self, msg):
DieWithError(msg)
def OnPreambleLine(self, line):
matcher = re.match('Issue: (\d+), patchset: (\d+)', line)
if matcher:
self._issue_number = int(matcher.groups()[0])
self._patchset = int(matcher.groups()[1])
self._change_id = gerrit_util.GetChange(self._HOST,
self._issue_number)['change_id']
self._revision_id = gerrit_util.GetChangeCurrentRevision(
self._HOST, self._change_id)[0]['current_revision']
def OnFinishPreamble(self):
pass
def OnOverallComment(self, comment):
self._overall_comment = comment
def OnFileComment(self, path, line, text, comment):
if not path in self._comments:
self._comments[path] = []
self._comments[path].append({
'message': comment,
'line': line,
'unresolved': True
})
def OnParseFinished(self):
is_lg = re.match(".*lgtm.*", self._overall_comment, re.IGNORECASE)
SetReview(self._HOST, self._change_id, self._revision_id,
self._overall_comment, is_lg, self._comments)
print('Done')
def Parse(self):
codereview_parser.Parser.Parse(self)
self.OnParseFinished()
def add_comment(self, issue, message, add_as_reviewer=False):
max_message = 10000
tail = '...\n(message too large)'
if len(message) > max_message:
message = message[:max_message - len(tail)] + tail
issue_props = self._rd.get_issue_properties(self._issue_number, None)
reviewers = ','.join(issue_props['reviewers'])
cc = ','.join(issue_props['cc'])
self._rd.post('/%d/publish' % issue,
[('xsrf_token', self._rd.xsrf_token()), ('message', message),
('message_only', 'False'),
('add_as_reviewer', str(bool(add_as_reviewer))),
('reviewers', reviewers), ('cc', cc), ('send_mail', 'True'),
('no_redirect', 'True')])
def ProcessFile(file):
parser = GerritParser(file)
parser.Parse()
def main(argv):
parser = optparse.OptionParser()
parser.add_option('-f', '--file')
options, args = parser.parse_args(argv)
file_name = options.file
if file_name is None:
if len(argv) != 1:
parser.print_help()
DieWithError('Review file not specified')
file_name = argv[0]
with open(file_name) as file:
ProcessFile(file)
if __name__ == '__main__':
main(sys.argv[1:])