def _ProcessDeps(cl, deps, required): """Yields matching dependencies for a patch""" # We need to query the change to guarantee that we have a .gerrit_number for dep in deps: if not dep.remote in opts.gerrit: opts.gerrit[dep.remote] = gerrit.GetGerritHelper( remote=dep.remote, print_cmd=opts.debug) helper = opts.gerrit[dep.remote] # TODO(phobbs) this should maybe catch network errors. changes = _QueryChange(dep.ToGerritQueryText(), helper=helper) # Handle empty results. If we found a commit that was pushed directly # (e.g. a bot commit), then gerrit won't know about it. if not changes: if required: logging.error('CL %s depends on %s which cannot be found', cl, dep.ToGerritQueryText()) continue # Our query might have matched more than one result. This can come up # when CQ-DEPEND uses a Gerrit Change-Id, but that Change-Id shows up # across multiple repos/branches. We blindly check all of them in the # hopes that all open ones are what the user wants, but then again the # CQ-DEPEND syntax itself is unable to differeniate. *shrug* if len(changes) > 1: logging.warning('CL %s has an ambiguous CQ dependency %s', cl, dep.ToGerritQueryText()) for change in changes: if change.status == 'NEW': yield change
def GetGerrit(opts, cl=None): """Auto pick the right gerrit instance based on the |cl| Args: opts: The general options object. cl: A CL taking one of the forms: 1234 *1234 chromium:1234 Returns: A tuple of a gerrit object and a sanitized CL #. """ gob = opts.gob if cl is not None: if cl.startswith('*') or cl.startswith('chrome-internal:'): gob = config_lib.GetSiteParams().INTERNAL_GOB_INSTANCE if cl.startswith('*'): cl = cl[1:] else: cl = cl[16:] elif ':' in cl: gob, cl = cl.split(':', 1) if not gob in opts.gerrit: opts.gerrit[gob] = gerrit.GetGerritHelper(gob=gob, print_cmd=opts.debug) return (opts.gerrit[gob], cl)
def SimpleCreate(cls, cros_internal=True, cros=True): """Classmethod helper for creating a HelperPool from boolean options. Args: cros_internal: If True, allow access to a GerritHelper for internal. cros: If True, allow access to a GerritHelper for external. Returns: An appropriately configured HelperPool instance. """ site_params = config_lib.GetSiteParams() if cros: cros = gerrit.GetGerritHelper(site_params.EXTERNAL_REMOTE) else: cros = None if cros_internal: cros_internal = gerrit.GetGerritHelper(site_params.INTERNAL_REMOTE) else: cros_internal = None return cls(cros_internal=cros_internal, cros=cros)
def _GetHelper(self, remote=config_lib.GetSiteParams().EXTERNAL_REMOTE): return gerrit.GetGerritHelper(remote)
def _GetHelper(self, remote=site_config.params.EXTERNAL_REMOTE): return gerrit.GetGerritHelper(remote)
def main(argv): # Locate actions that are exposed to the user. All functions that start # with "UserAct" are fair game. act_pfx = 'UserAct' actions = [x for x in globals() if x.startswith(act_pfx)] usage = """%prog [options] <action> [action args] There is no support for doing line-by-line code review via the command line. This helps you manage various bits and CL status. Example: $ gerrit todo # List all the CLs that await your review. $ gerrit mine # List all of your open CLs. $ gerrit inspect 28123 # Inspect CL 28123 on the public gerrit. $ gerrit inspect *28123 # Inspect CL 28123 on the internal gerrit. $ gerrit verify 28123 1 # Mark CL 28123 as verified (+1). Actions:""" indent = max([len(x) - len(act_pfx) for x in actions]) for a in sorted(actions): usage += '\n %-*s: %s' % (indent, a[len(act_pfx):].lower(), globals()[a].__doc__) parser = commandline.OptionParser(usage=usage) parser.add_option('-i', '--internal', default=None, action='store_true', help='Query gerrit-int') parser.add_option('-e', '--external', dest='internal', action='store_false', help='Query gerrit (default)') parser.add_option('--sort', default='number', help='Key to sort on ' '(number, project)') parser.add_option('-v', '--verbose', default=False, action='store_true', help='Be more verbose in output') opts, args = parser.parse_args(argv) if not args: parser.error('missing action') # pylint: disable=W0603 global COLOR COLOR = terminal.Color(enabled=opts.color) # TODO: This sucks. We assume that all actions which take an argument are # a CL #. Or at least, there's no other reason for it to start with a *. # We do this to automatically select internal vs external gerrit as this # convention is encoded in much of our system. However, the rest of this # script doesn't expect (or want) the leading *. if len(args) > 1: if args[1][0] == '*': if opts.internal is None: opts.internal = True args[1] = args[1][1:] opts.gerrit = gerrit.GetGerritHelper( constants.INTERNAL_REMOTE if opts.internal else constants.EXTERNAL_REMOTE, print_cmd=opts.debug) # Now look up the requested user action and run it. cmd = args[0].lower() args = args[1:] functor = globals().get(act_pfx + cmd.capitalize()) if functor: argspec = inspect.getargspec(functor) if argspec.varargs: if len(args) < len(argspec.args): parser.error('incorrect number of args: %s expects at least %s' % (cmd, len(argspec.args))) elif len(argspec.args) - 1 != len(args): parser.error('incorrect number of args: %s expects %s' % (cmd, len(argspec.args) - 1)) try: functor(opts, *args) except (cros_build_lib.RunCommandError, gerrit.GerritException) as e: cros_build_lib.Die(e.message) else: parser.error('unknown action: %s' % (cmd,))
def main(argv): parser = optparse.OptionParser( usage="usage: prog [--internal|--external] [--query|--age] " "[email_addresses|usernames]") parser.add_option("-i", "--internal", default=False, action="store_true", help="Query gerrit-int.") parser.add_option("-e", "--external", dest="internal", action="store_false", help="Query gerrit. Note this is the default.") parser.add_option( "--age", default="6mon", help="Limit results to within this gerrit age query. See " "http://goo.gl/AdJy3 for the age form; to see the last 3 " "months, it would be '3mon'. Defaults to the last 6 " "months.") parser.add_option( "--query", default=None, help="Freeform gerrit query. If specified, it overrides " "the normal querying behaviour. Use only if you know " "what you're doing.") opts, args = parser.parse_args(argv) if not args and not opts.query: parser.error("no querying parameters given.") logging.getLogger().setLevel(logging.WARNING) query = [] helper = gerrit.GetGerritHelper(constants.INTERNAL_REMOTE if opts. internal else constants.EXTERNAL_REMOTE) recomposed_args = [] for arg in args: if "@" not in arg: recomposed_args.extend(find_user(helper, arg)) else: recomposed_args.append(arg) args = set(recomposed_args) addresses = [x.replace(":", "\:") for x in args] owners = ["owner:%s" % x for x in addresses] reviewers = ["reviewer:%s" % x for x in addresses] if opts.query: query.append(opts.query) elif args: query.append("( %s )" % " OR ".join(owners)) query.append("-age:%s" % opts.age) query = " AND ".join(query) results = helper.Query(query, current_patch=False, sort="lastUpdated") targets = frozenset(args) if not args: print "query resulted in %i owned CLs." % len(results) print "no email addresses given so unable to do scoring stats" return 0 print "Owner stats: %i CLs." % len(results) stats = {} for x in results: status = x["status"].lower() stats[status] = stats.get(status, 0) + 1 if "new" in stats: stats["open"] = stats.pop("new") for status, value in sorted(stats.iteritems(), key=lambda x: x[0]): print " %s: %i" % (status, value) # Get approval stats. requested = helper.Query( "( %s AND NOT ( %s ) ) AND -age:%s" % (" OR ".join(reviewers), " OR ".join(owners), opts.age), current_patch=False, options=("--all-approvals", )) total_scored_patchsets = scoring = 0 lgtms = 0 for cl in requested: touched_it = False lgtmed = False for patchset in cl.get("patchSets", ()): for approval in patchset.get("approvals", ()): if targets.intersection([approval["by"].get("email")]): touched_it = True if (("CRVW", 2) == (approval.get("type"), int(approval.get("value", 0)))): lgtmed = True total_scored_patchsets += 1 if touched_it: scoring += 1 if lgtmed: lgtms += 1 # Find comments; gerrit doesn't give that information in --all-approvals # unfortunately. comments = helper.Query( "( %s AND NOT ( %s ) ) AND -age:%s" % (" OR ".join(reviewers), " OR ".join(owners), opts.age), options=("--comments", "--patch-sets"), raw=True, current_patch=False) requested_review = len(comments) commented_changes = total_comments = 0 for change in comments: touched = False for comment in change.get("comments", ()): if comment["reviewer"].get("email", None) in targets: total_comments += 1 touched = True if touched: commented_changes += 1 print( "Requested as a reviewer for %i changes, commented on %i, " "%i comments total; lgtm'd %i changes" % (requested_review, commented_changes, total_comments, lgtms)) ratio = float( total_scored_patchsets) / scoring if total_scored_patchsets else 0 print( "Review stats: %i Changes scored; %i individual patchsets scored " "(for a %2.2fx scoring/change rate)" % (scoring, total_scored_patchsets, ratio))