chromium/tools/accessibility/codereview/mph.py

#!/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:])