def commit_message_filter(msg): # type: (bytes) -> Tuple[bytes, Dict[Text, Text]] metadata = {} m = commitparser.BUG_RE.match(msg) if m: bug_bytes, bug_number = m.groups()[:2] if msg.startswith(bug_bytes): prefix = re.compile(br"^%s[^\w\d\[\(]*" % bug_bytes) msg = prefix.sub(b"", msg) metadata[u"bugzilla-url"] = env.bz.bugzilla_url(int(bug_number)) reviewers = u", ".join( item.decode("utf8", "replace") for item in commitparser.parse_reviewers(msg)) if reviewers: metadata["gecko-reviewers"] = reviewers msg = commitparser.replace_reviewers(msg, "") msg = commitparser.strip_commit_metadata(msg) description = msg.splitlines() if description: summary = description.pop(0) summary = summary.rstrip(b"!#$%&(*+,-/:;<=>@[\\^_`{|~").rstrip() msg = summary + (b"\n" + b"\n".join(description) if description else b"") return msg, metadata
def revset_reviewer(repo, subset, x): """``reviewer(REVIEWER)`` Changesets reviewed by a specific person. """ n = revset.getstring(x, _('reviewer() requires a string argument.')) return subset.filter(lambda x: n in parse_reviewers(repo[x].description()))
def revset_me(repo, subset, x): """``me()`` Changesets that you are involved in. """ 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 = get_ircnick(repo.ui) n = encoding.lower(me) kind, pattern, matcher = revset._substringmatcher(n) revs = [] for r in subset: ctx = repo[r] if matcher(encoding.lower(ctx.user())): revs.append(r) continue if ircnick in parse_reviewers(ctx.description()): revs.append(r) continue return revs
def reviewersgen(_context): '''Generator for reviewers list''' for reviewer in commitparser.parse_reviewers(description): yield { b'name': reviewer, b'revset': b'reviewer(%s)' % reviewer, }
def revset_reviewed(repo, subset, x): """Changesets that were reviewed.""" if x: raise ParseError(_('reviewed() does not take an argument')) return subset.filter( lambda x: list(parse_reviewers(repo[x].description())))
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 revset_reviewed(repo, subset, x): """``reviewed()`` Changesets that were reviewed. """ if x: raise ParseError(_('reviewed() does not take an argument')) return [r for r in subset if list(parse_reviewers(repo[r].description()))]
def template_reviewer(repo, ctx, **args): """:reviewer: String. The first reviewer of this changeset.""" reviewers = parse_reviewers(ctx.description()) try: first_reviewer = next(reviewers) return first_reviewer except StopIteration: return None
def revset_reviewed(repo, subset, x): """``reviewed()`` Changesets that were reviewed. """ if x: raise ParseError(_('reviewed() does not take an argument')) return subset.filter(lambda x: list(parse_reviewers(repo[x].description())))
def template_reviewers(repo, ctx, **args): """:reviewers: List of strings. The reviewers associated with tis changeset.""" # TRACKING hg47 if templateutil: return templateutil.mappinggenerator(parse_reviewers, args=(ctx.description(), )) else: return parse_reviewers(ctx.description())
def commit_message_filter(msg): metadata = {} m = commitparser.BUG_RE.match(msg) if m: bug_str, bug_number = m.groups()[:2] if msg.startswith(bug_str): prefix = re.compile(r"^%s[^\w\d\[\(]*" % bug_str) msg = prefix.sub("", msg) metadata["bugzilla-url"] = env.bz.bugzilla_url(bug_number) reviewers = ", ".join(commitparser.parse_reviewers(msg)) if reviewers: metadata["gecko-reviewers"] = reviewers msg = commitparser.replace_reviewers(msg, "") msg = commitparser.strip_commit_metadata(msg) description = msg.splitlines() if description: summary = description.pop(0) summary = summary.rstrip("!#$%&(*+,-/:;<=>@[\\^_`{|~").rstrip() description = "\n".join(description) msg = summary + ("\n" + description if description else "") return msg, metadata
def test_first_reviewer_with_period_at_end_of_name(): # TODO: this is not the current behaviour, but implementing this would # yield more expected results. We should probably also account for the # case of users having a period at the end of their username. self.assertEqual(list(parse_reviewers(b"stuff;r=a.,b")), [b"a", b"b"])
def hasreviewer(r): for reviewer in commitparser.parse_reviewers(repo[r].description()): if encoding.lower(reviewer) == n: return True return False
def test_reviewers(self): # first with r? reviewer request syntax self.assertEqual(list(parse_reviewers('Bug 1 - some stuff; r?romulus')), ['romulus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r?romulus, r?remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r?romulus,r?remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r?romulus, remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r?romulus,remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; (r?romulus)')),['romulus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; (r?romulus,remus)')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; [r?romulus]')), ['romulus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; [r?remus, r?romulus]')), ['remus', 'romulus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r?romulus, a=test-only')), ['romulus', 'test-only']) # now with r= review granted syntax self.assertEqual(list(parse_reviewers('Bug 1 - some stuff; r=romulus')), ['romulus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r=romulus, r=remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r=romulus,r=remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r=romulus, remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r=romulus,remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; (r=romulus)')),['romulus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; (r=romulus,remus)')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; [r=romulus]')), ['romulus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; [r=remus, r=romulus]')), ['remus', 'romulus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r=romulus, a=test-only')), ['romulus', 'test-only']) # try some other separators than ; self.assertEqual(list(parse_reviewers('Bug 1 - some stuff r=romulus')), ['romulus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff. r=romulus, r=remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff - r=romulus,r=remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff, r=romulus, remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff.. r=romulus,remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff | (r=romulus)')),['romulus']) # make sure things work with different spacing self.assertEqual(list(parse_reviewers('Bug 1 - some stuff;r=romulus,r=remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff.r=romulus, r=remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff,r=romulus, remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers( 'Bug 1094764 - Implement AudioContext.suspend and friends. r=roc,ehsan\n' '- Relevant spec text:\n' '- http://webaudio.github.io/web-audio-api/#widl-AudioContext-suspend-Promise\n' '- http://webaudio.github.io/web-audio-api/#widl-AudioContext-resume-Promise\n')), ['roc', 'ehsan']) self.assertEqual(list(parse_reviewers( 'Bug 380783 - nsStringAPI.h: no equivalent of IsVoid (tell if ' 'string is null), patch by Mook <*****@*****.**>, ' 'r=bsmedberg/dbaron, sr=dbaron, a1.9=bz')), ['bsmedberg', 'dbaron', 'dbaron']) self.assertEqual(list(parse_reviewers( 'Bug 123 - Blah blah; r=gps DONTBUILD (NPOTB)')), ['gps']) self.assertEqual(list(parse_reviewers( 'Bug 123 - Blah blah; r=gps DONTBUILD')), ['gps']) self.assertEqual(list(parse_reviewers( 'Bug 123 - Blah blah; r=gps (DONTBUILD)')), ['gps'])
def fltr(x): ctx = repo[x] if matcher(encoding.lower(ctx.user())): return True return ircnick in parse_reviewers(ctx.description())
def template_reviewer(repo, ctx, **args): """:reviewer: String. The first reviewer of this changeset.""" reviewers = list(parse_reviewers(ctx.description())) return reviewers[0] if reviewers else None
def reviewboard(repo, proto, args=None): proto.redirect() o = parsepayload(proto, args) if isinstance(o, ServerError): return formatresponse(str(o)) bzusername = o["bzusername"] bzapikey = o["bzapikey"] identifier, nodes, precursors = parseidentifier(o) if not identifier: return ["error %s" % _("no review identifier in request")] diffopts = mdiff.diffopts(context=8, showfunc=True, git=True) commits = {"individual": [], "squashed": {}} # We do multiple passes over the changesets requested for review because # some operations could be slow or may involve queries to external # resources. We want to run the fast checks first so we don't waste # resources before finding the error. The drawback here is the client # will not see the full set of errors. We may revisit this decision # later. for node in nodes: ctx = repo[node] # Reviewing merge commits doesn't make much sense and only makes # situations more complicated. So disallow the practice. if len(ctx.parents()) > 1: msg = "cannot review merge commits (%s)" % short(ctx.node()) return formatresponse("error %s" % msg) # Invalid or confidental bugs will raise errors in the Review Board # interface later. Fail fast to minimize wasted time and resources. try: reviewid = ReviewID(identifier) except util.Abort as e: return formatresponse("error %s" % e) # We use xmlrpc here because the Bugsy REST client doesn't currently handle # errors in responses. # We don't use available Bugzilla credentials because that's the # easiest way to test for confidential bugs. If/when we support posting # reviews to confidential bugs, we'll need to change this. xmlrpc_url = repo.ui.config("bugzilla", "url").rstrip("/") + "/xmlrpc.cgi" proxy = xmlrpclib.ServerProxy(xmlrpc_url) try: proxy.Bug.get({"ids": [reviewid.bug]}) except xmlrpclib.Fault as f: if f.faultCode == 101: return formatresponse( "error bug %s does not exist; " "please change the review id (%s)" % (reviewid.bug, reviewid.full) ) elif f.faultCode == 102: return formatresponse( "error bug %s could not be accessed " "(we do not currently allow posting of reviews to " "confidential bugs)" % reviewid.bug ) return formatresponse( "error server error verifying bug %s exists; " "please retry or report a bug" % reviewid.bug ) # Find the first public node in the ancestry of this series. This is # used by MozReview to query the upstream repo for additional context. first_public_ancestor = None for node in repo[nodes[0]].ancestors(): ctx = repo[node] if ctx.phase() == phases.public: first_public_ancestor = ctx.hex() break commits["squashed"]["first_public_ancestor"] = first_public_ancestor # Note patch.diff() appears to accept anything that can be fed into # repo[]. However, it blindly does a hex() on the argument as opposed # to the changectx, so we need to pass in the binary node. base_ctx = repo[nodes[0]].p1() base_parent_node = base_ctx.node() for i, node in enumerate(nodes): ctx = repo[node] p1 = ctx.p1().node() diff = None parent_diff = None diff = "".join(patch.diff(repo, node1=p1, node2=ctx.node(), opts=diffopts)) + "\n" if i: base_commit_id = nodes[i - 1] else: base_commit_id = base_ctx.hex() summary = encoding.fromlocal(ctx.description().splitlines()[0]) commits["individual"].append( { "id": node, "precursors": precursors.get(node, []), "message": encoding.fromlocal(ctx.description()), "diff": diff, "bug": str(reviewid.bug), "base_commit_id": base_commit_id, "first_public_ancestor": first_public_ancestor, "reviewers": list(commitparser.parse_reviewers(summary)), } ) commits["squashed"]["diff"] = ( "".join(patch.diff(repo, node1=base_parent_node, node2=repo[nodes[-1]].node(), opts=diffopts)) + "\n" ) commits["squashed"]["base_commit_id"] = base_ctx.hex() rburl = repo.ui.config("reviewboard", "url", None).rstrip("/") repoid = repo.ui.configint("reviewboard", "repoid", None) privleged_rb_username = repo.ui.config("reviewboard", "username", None) privleged_rb_password = repo.ui.config("reviewboard", "password", None) # We support pushing via HTTP and SSH. REMOTE_USER will be set via HTTP. # USER via SSH. But USER is a common variable and could also sneak into # the HTTP environment. # # REMOTE_USER values come from Bugzilla. USER values come from LDAP. # There is a potential privilege escalation vulnerability if someone # obtains a Bugzilla account overlapping with a LDAP user having # special privileges. So, we explicitly don't perform an LDAP lookup # if REMOTE_USER is present because we could be crossing the user # stores. ldap_username = os.environ.get("USER") remote_user = repo.ui.environ.get("REMOTE_USER", os.environ.get("REMOTE_USER")) if ldap_username and not remote_user: associate_ldap_username( rburl, ldap_username, privleged_rb_username, privleged_rb_password, username=bzusername, apikey=bzapikey ) lines = ["rburl %s" % rburl, "reviewid %s" % identifier] try: parentrid, commitmap, reviews = post_reviews( rburl, repoid, identifier, commits, lines, username=bzusername, apikey=bzapikey ) lines.extend( [ "parentreview %s" % parentrid, "reviewdata %s status %s" % (parentrid, urllib.quote(reviews[parentrid]["status"].encode("utf-8"))), "reviewdata %s public %s" % (parentrid, reviews[parentrid]["public"]), ] ) for node, rid in commitmap.items(): rd = reviews[rid] lines.append("csetreview %s %s" % (node, rid)) lines.append("reviewdata %s status %s" % (rid, urllib.quote(rd["status"].encode("utf-8")))) lines.append("reviewdata %s public %s" % (rid, rd["public"])) if rd["reviewers"]: parts = [urllib.quote(r.encode("utf-8")) for r in rd["reviewers"]] lines.append("reviewdata %s reviewers %s" % (rid, ",".join(parts))) except AuthorizationError as e: lines.append("error %s" % str(e)) except BadRequestError as e: lines.append("error %s" % str(e)) res = formatresponse(*lines) return res
def test_reviewers(self): # first with r? reviewer request syntax self.assertEqual( list(parse_reviewers(b'Bug 1 - some stuff; r?romulus')), [b'romulus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff; r?romulus, r?remus')), [b'romulus', b'remus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff; r?romulus,r?remus')), [b'romulus', b'remus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff; r?romulus, remus')), [b'romulus', b'remus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff; r?romulus,remus')), [b'romulus', b'remus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff; (r?romulus)')), [b'romulus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff; (r?romulus,remus)')), [b'romulus', b'remus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff; [r?romulus]')), [b'romulus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff; [r?remus, r?romulus]')), [b'remus', b'romulus']) self.assertEqual( list(parse_reviewers( b'Bug 1 - More stuff; r?romulus, a=test-only')), [b'romulus', b'test-only']) self.assertEqual( list( parse_reviewers( b'Bug 1 - More stuff; r?romulus, ux-r=test-only')), [b'romulus']) # now with r= review granted syntax self.assertEqual( list(parse_reviewers(b'Bug 1 - some stuff; r=romulus')), [b'romulus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff; r=romulus, r=remus')), [b'romulus', b'remus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff; r=romulus,r=remus')), [b'romulus', b'remus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff; r=romulus, remus')), [b'romulus', b'remus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff; r=romulus,remus')), [b'romulus', b'remus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff; (r=romulus)')), [b'romulus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff; (r=romulus,remus)')), [b'romulus', b'remus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff; [r=romulus]')), [b'romulus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff; [r=remus, r=romulus]')), [b'remus', b'romulus']) self.assertEqual( list(parse_reviewers( b'Bug 1 - More stuff; r=romulus, a=test-only')), [b'romulus', b'test-only']) # try some other separators than ; self.assertEqual( list(parse_reviewers(b'Bug 1 - some stuff r=romulus')), [b'romulus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff. r=romulus, r=remus')), [b'romulus', b'remus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff - r=romulus,r=remus')), [b'romulus', b'remus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff, r=romulus, remus')), [b'romulus', b'remus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff.. r=romulus,remus')), [b'romulus', b'remus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff | (r=romulus)')), [b'romulus']) # make sure things work with different spacing self.assertEqual( list(parse_reviewers(b'Bug 1 - some stuff;r=romulus,r=remus')), [b'romulus', b'remus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff.r=romulus, r=remus')), [b'romulus', b'remus']) self.assertEqual( list(parse_reviewers(b'Bug 1 - More stuff,r=romulus, remus')), [b'romulus', b'remus']) # test that periods in names are OK self.assertEqual( list( parse_reviewers( b'Bug 1 - More stuff.r=jimmy.jones, r=bill.mcneal')), [b'jimmy.jones', b'bill.mcneal']) self.assertEqual(list(parse_reviewers(b'Bug 1 - More stuff,r=jimmy.')), [b'jimmy']) # check some funky names too self.assertEqual(list(parse_reviewers(b"stuff;r=a")), [b"a"]) self.assertEqual(list(parse_reviewers(b"stuff;r=aa")), [b"aa"]) self.assertEqual(list(parse_reviewers(b"stuff;r=.a")), [b".a"]) self.assertEqual(list(parse_reviewers(b"stuff;r=..a")), [b"..a"]) self.assertEqual(list(parse_reviewers(b"stuff;r=...a")), [b"...a"]) self.assertEqual(list(parse_reviewers(b"stuff;r=a...a")), [b"a...a"]) self.assertEqual(list(parse_reviewers(b"stuff;r=a.b")), [b"a.b"]) self.assertEqual(list(parse_reviewers(b"stuff;r=a.b.c")), [b"a.b.c"]) self.assertEqual(list(parse_reviewers(b"stuff;r=-.-.-")), [b"-.-.-"]) # NOTE: a string such as "stuff;r=a.,b" will not be parsed as expected # and will yield ["a"]. TODO: fix this in the future in the regex, or # do some post processing in `parse_reviewers` if this is needed. The # following test is testing the current behaviour only. self.assertEqual(list(parse_reviewers(b"stuff;r=a.,b")), [b"a"]) # altogether now with some spaces sprinkled here and there self.assertEqual( list( parse_reviewers( b"hi;r=a,aa,.a,..a,...a, a...a,a.b, a.b.c, -.-.-")), [ b"a", b"aa", b".a", b"..a", b"...a", b"a...a", b"a.b", b"a.b.c", b"-.-.-" ]) # bare r? self.assertEqual(list(parse_reviewers(b'Bug 123 - Blah blah; r?')), []) self.assertEqual( list( parse_reviewers( b'Bug 1313324 - Cover the screensharing UI with browser chrome test, r=' )), []) # oddball real-world examples self.assertEqual( list( parse_reviewers( b'Bug 1094764 - Implement AudioContext.suspend and friends. r=roc,ehsan\n' b'- Relevant spec text:\n' b'- http://webaudio.github.io/web-audio-api/#widl-AudioContext-suspend-Promise\n' b'- http://webaudio.github.io/web-audio-api/#widl-AudioContext-resume-Promise\n' )), [b'roc', b'ehsan']) self.assertEqual( list( parse_reviewers( b'Bug 380783 - nsStringAPI.h: no equivalent of IsVoid (tell if ' b'string is null), patch by Mook <*****@*****.**>, ' b'r=bsmedberg/dbaron, sr=dbaron, a1.9=bz')), [b'bsmedberg', b'dbaron', b'dbaron']) self.assertEqual( list( parse_reviewers( b'Bug 123 - Blah blah; r=gps DONTBUILD (NPOTB)')), [b'gps']) self.assertEqual( list(parse_reviewers(b'Bug 123 - Blah blah; r=gps DONTBUILD')), [b'gps']) self.assertEqual( list(parse_reviewers(b'Bug 123 - Blah blah; r=gps (DONTBUILD)')), [b'gps']) self.assertEqual( list( parse_reviewers( b'Bug 1181382: move declaration into namespace to resolve conflict. r=hsinyi. try: -b d -p all -u none -t none' )), [b'hsinyi']) self.assertEqual( list( parse_reviewers( b'Bug 1024110 - Change Aurora\'s default profile behavior to use channel-specific profiles. r=bsmedberg f=gavin,markh' )), [b'bsmedberg']) self.assertEqual( list( parse_reviewers( b'Bug 1199050 - Round off the corners of browser-extension-panel\'s content. ui-r=maritz, r=gijs' )), [b'maritz', b'gijs']) self.assertEqual( list( parse_reviewers( b'Bug 1197422 - Part 2: [webext] Implement the pageAction API. r=billm ui-r=bwinton' )), [b'billm', b'bwinton']) # 'ui-reviewer=' isn't supported (less than 4% of ui-review commits use # it, 'ui-r=' being the preferred syntax) self.assertEqual( list( parse_reviewers( b'Bug 1217369 - "Welcome to ..." has extra padding on Loop\'s standalone UI making it feel strange. r=mikedeboer,ui-review=sevaan' )), [b'mikedeboer']) self.assertEqual( list( parse_reviewers( b'Bug 1182996 - Fix and add missing namespace comments. rs=ehsan\n' b'run-clang-tidy.py \\\n' b'-checks=\'-*,llvm-namespace-comment\' \\\n' b'-header-filter=^/.../mozilla-central/.* \\\n' b'-fix')), [b'ehsan'])
def addmetadata(repo, ctx, d, onlycheap=False): """Add changeset metadata for hgweb templates.""" bugs = list(set(commitparser.parse_bugs(ctx.description()))) d['bugs'] = [] for bug in commitparser.parse_bugs(ctx.description()): d['bugs'].append({ 'no': str(bug), 'url': 'https://bugzilla.mozilla.org/show_bug.cgi?id=%s' % bug, }) d['reviewers'] = [] for reviewer in commitparser.parse_reviewers(ctx.description()): d['reviewers'].append({ 'name': reviewer, 'revset': 'reviewer(%s)' % reviewer, }) d['backsoutnodes'] = [] backouts = commitparser.parse_backouts(ctx.description()) if backouts: for node in backouts[0]: try: bctx = repo[node] d['backsoutnodes'].append({'node': bctx.hex()}) except error.LookupError: pass # Repositories can define which TreeHerder repository they are associated # with. treeherder = repo.ui.config('mozilla', 'treeherder_repo') if treeherder: d['treeherderrepourl'] = 'https://treeherder.mozilla.org/#/jobs?repo=%s' % treeherder if onlycheap: return # Obtain the Gecko/app version/milestone. # # We could probably only do this if the repo is a known app repo (by # looking at the initial changeset). But, path based lookup is relatively # fast, so just do it. However, we need this in the "onlycheap" # section because resolving manifests is relatively slow and resolving # several on changelist pages may add seconds to page load times. try: fctx = repo.filectx('config/milestone.txt', changeid=ctx.node()) lines = fctx.data().splitlines() lines = [l for l in lines if not l.startswith('#') and l.strip()] if lines: d['milestone'] = lines[0].strip() except error.LookupError: pass # Look for changesets that back out this one. # # We limit the distance we search for backouts because an exhaustive # search could be very intensive. e.g. you load up the root commit # on a repository with 200,000 changesets and that commit is never # backed out. This finds most backouts because backouts typically happen # shortly after a bad commit is introduced. thisshort = short(ctx.node()) count = 0 searchlimit = repo.ui.configint('hgmo', 'backoutsearchlimit', 100) for bctx in repo.set('%ld::', [ctx.rev()]): count += 1 if count >= searchlimit: break backouts = commitparser.parse_backouts(bctx.description()) if backouts and thisshort in backouts[0]: d['backedoutbynode'] = bctx.hex() break
def test_reviewers(self): # first with r? reviewer request syntax self.assertEqual(list(parse_reviewers('Bug 1 - some stuff; r?romulus')), ['romulus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r?romulus, r?remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r?romulus,r?remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r?romulus, remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r?romulus,remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; (r?romulus)')),['romulus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; (r?romulus,remus)')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; [r?romulus]')), ['romulus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; [r?remus, r?romulus]')), ['remus', 'romulus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r?romulus, a=test-only')), ['romulus', 'test-only']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r?romulus, ux-r=test-only')), ['romulus']) # now with r= review granted syntax self.assertEqual(list(parse_reviewers('Bug 1 - some stuff; r=romulus')), ['romulus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r=romulus, r=remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r=romulus,r=remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r=romulus, remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r=romulus,remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; (r=romulus)')),['romulus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; (r=romulus,remus)')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; [r=romulus]')), ['romulus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; [r=remus, r=romulus]')), ['remus', 'romulus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff; r=romulus, a=test-only')), ['romulus', 'test-only']) # try some other separators than ; self.assertEqual(list(parse_reviewers('Bug 1 - some stuff r=romulus')), ['romulus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff. r=romulus, r=remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff - r=romulus,r=remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff, r=romulus, remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff.. r=romulus,remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff | (r=romulus)')),['romulus']) # make sure things work with different spacing self.assertEqual(list(parse_reviewers('Bug 1 - some stuff;r=romulus,r=remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff.r=romulus, r=remus')), ['romulus', 'remus']) self.assertEqual(list(parse_reviewers('Bug 1 - More stuff,r=romulus, remus')), ['romulus', 'remus']) # oddball real-world examples self.assertEqual(list(parse_reviewers( 'Bug 1094764 - Implement AudioContext.suspend and friends. r=roc,ehsan\n' '- Relevant spec text:\n' '- http://webaudio.github.io/web-audio-api/#widl-AudioContext-suspend-Promise\n' '- http://webaudio.github.io/web-audio-api/#widl-AudioContext-resume-Promise\n')), ['roc', 'ehsan']) self.assertEqual(list(parse_reviewers( 'Bug 380783 - nsStringAPI.h: no equivalent of IsVoid (tell if ' 'string is null), patch by Mook <*****@*****.**>, ' 'r=bsmedberg/dbaron, sr=dbaron, a1.9=bz')), ['bsmedberg', 'dbaron', 'dbaron']) self.assertEqual(list(parse_reviewers( 'Bug 123 - Blah blah; r=gps DONTBUILD (NPOTB)')), ['gps']) self.assertEqual(list(parse_reviewers( 'Bug 123 - Blah blah; r=gps DONTBUILD')), ['gps']) self.assertEqual(list(parse_reviewers( 'Bug 123 - Blah blah; r=gps (DONTBUILD)')), ['gps']) self.assertEqual(list(parse_reviewers( 'Bug 1181382: move declaration into namespace to resolve conflict. r=hsinyi. try: -b d -p all -u none -t none')), ['hsinyi']) self.assertEqual(list(parse_reviewers( 'Bug 1024110 - Change Aurora\'s default profile behavior to use channel-specific profiles. r=bsmedberg f=gavin,markh')), ['bsmedberg']) self.assertEqual(list(parse_reviewers( 'Bug 1199050 - Round off the corners of browser-extension-panel\'s content. ui-r=maritz, r=gijs')), ['maritz', 'gijs']) self.assertEqual(list(parse_reviewers( 'Bug 1197422 - Part 2: [webext] Implement the pageAction API. r=billm ui-r=bwinton')), ['billm', 'bwinton']) # 'ui-reviewer=' isn't supported (less than 4% of ui-review commits use # it, 'ui-r=' being the preferred syntax) self.assertEqual(list(parse_reviewers( 'Bug 1217369 - "Welcome to ..." has extra padding on Loop''s standalone UI making it feel strange. r=mikedeboer,ui-review=sevaan')), ['mikedeboer']) self.assertEqual(list(parse_reviewers( 'Bug 1182996 - Fix and add missing namespace comments. rs=ehsan\n' 'run-clang-tidy.py \\\n' '-checks=\'-*,llvm-namespace-comment\' \\\n' '-header-filter=^/.../mozilla-central/.* \\\n' '-fix')), ['ehsan'])
def template_reviewers(repo, ctx, **args): """:reviewers: List of strings. The reviewers associated with tis changeset.""" return parse_reviewers(ctx.description())
def test_reviewers(self): # first with r? reviewer request syntax self.assertEqual( list(parse_reviewers('Bug 1 - some stuff; r?romulus')), ['romulus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff; r?romulus, r?remus')), ['romulus', 'remus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff; r?romulus,r?remus')), ['romulus', 'remus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff; r?romulus, remus')), ['romulus', 'remus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff; r?romulus,remus')), ['romulus', 'remus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff; (r?romulus)')), ['romulus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff; (r?romulus,remus)')), ['romulus', 'remus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff; [r?romulus]')), ['romulus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff; [r?remus, r?romulus]')), ['remus', 'romulus']) self.assertEqual( list( parse_reviewers('Bug 1 - More stuff; r?romulus, a=test-only')), ['romulus', 'test-only']) self.assertEqual( list( parse_reviewers( 'Bug 1 - More stuff; r?romulus, ux-r=test-only')), ['romulus']) # now with r= review granted syntax self.assertEqual( list(parse_reviewers('Bug 1 - some stuff; r=romulus')), ['romulus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff; r=romulus, r=remus')), ['romulus', 'remus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff; r=romulus,r=remus')), ['romulus', 'remus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff; r=romulus, remus')), ['romulus', 'remus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff; r=romulus,remus')), ['romulus', 'remus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff; (r=romulus)')), ['romulus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff; (r=romulus,remus)')), ['romulus', 'remus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff; [r=romulus]')), ['romulus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff; [r=remus, r=romulus]')), ['remus', 'romulus']) self.assertEqual( list( parse_reviewers('Bug 1 - More stuff; r=romulus, a=test-only')), ['romulus', 'test-only']) # try some other separators than ; self.assertEqual(list(parse_reviewers('Bug 1 - some stuff r=romulus')), ['romulus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff. r=romulus, r=remus')), ['romulus', 'remus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff - r=romulus,r=remus')), ['romulus', 'remus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff, r=romulus, remus')), ['romulus', 'remus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff.. r=romulus,remus')), ['romulus', 'remus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff | (r=romulus)')), ['romulus']) # make sure things work with different spacing self.assertEqual( list(parse_reviewers('Bug 1 - some stuff;r=romulus,r=remus')), ['romulus', 'remus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff.r=romulus, r=remus')), ['romulus', 'remus']) self.assertEqual( list(parse_reviewers('Bug 1 - More stuff,r=romulus, remus')), ['romulus', 'remus']) # bare r? self.assertEqual(list(parse_reviewers('Bug 123 - Blah blah; r?')), []) self.assertEqual( list( parse_reviewers( 'Bug 1313324 - Cover the screensharing UI with browser chrome test, r=' )), []) # oddball real-world examples self.assertEqual( list( parse_reviewers( 'Bug 1094764 - Implement AudioContext.suspend and friends. r=roc,ehsan\n' '- Relevant spec text:\n' '- http://webaudio.github.io/web-audio-api/#widl-AudioContext-suspend-Promise\n' '- http://webaudio.github.io/web-audio-api/#widl-AudioContext-resume-Promise\n' )), ['roc', 'ehsan']) self.assertEqual( list( parse_reviewers( 'Bug 380783 - nsStringAPI.h: no equivalent of IsVoid (tell if ' 'string is null), patch by Mook <*****@*****.**>, ' 'r=bsmedberg/dbaron, sr=dbaron, a1.9=bz')), ['bsmedberg', 'dbaron', 'dbaron']) self.assertEqual( list( parse_reviewers( 'Bug 123 - Blah blah; r=gps DONTBUILD (NPOTB)')), ['gps']) self.assertEqual( list(parse_reviewers('Bug 123 - Blah blah; r=gps DONTBUILD')), ['gps']) self.assertEqual( list(parse_reviewers('Bug 123 - Blah blah; r=gps (DONTBUILD)')), ['gps']) self.assertEqual( list( parse_reviewers( 'Bug 1181382: move declaration into namespace to resolve conflict. r=hsinyi. try: -b d -p all -u none -t none' )), ['hsinyi']) self.assertEqual( list( parse_reviewers( 'Bug 1024110 - Change Aurora\'s default profile behavior to use channel-specific profiles. r=bsmedberg f=gavin,markh' )), ['bsmedberg']) self.assertEqual( list( parse_reviewers( 'Bug 1199050 - Round off the corners of browser-extension-panel\'s content. ui-r=maritz, r=gijs' )), ['maritz', 'gijs']) self.assertEqual( list( parse_reviewers( 'Bug 1197422 - Part 2: [webext] Implement the pageAction API. r=billm ui-r=bwinton' )), ['billm', 'bwinton']) # 'ui-reviewer=' isn't supported (less than 4% of ui-review commits use # it, 'ui-r=' being the preferred syntax) self.assertEqual( list( parse_reviewers( 'Bug 1217369 - "Welcome to ..." has extra padding on Loop' 's standalone UI making it feel strange. r=mikedeboer,ui-review=sevaan' )), ['mikedeboer']) self.assertEqual( list( parse_reviewers( 'Bug 1182996 - Fix and add missing namespace comments. rs=ehsan\n' 'run-clang-tidy.py \\\n' '-checks=\'-*,llvm-namespace-comment\' \\\n' '-header-filter=^/.../mozilla-central/.* \\\n' '-fix')), ['ehsan'])
def addmetadata(repo, ctx, d, onlycheap=False): """Add changeset metadata for hgweb templates.""" description = encoding.fromlocal(ctx.description()) d['bugs'] = [] for bug in commitparser.parse_bugs(description): d['bugs'].append({ 'no': str(bug), 'url': 'https://bugzilla.mozilla.org/show_bug.cgi?id=%s' % bug, }) d['reviewers'] = [] for reviewer in commitparser.parse_reviewers(description): d['reviewers'].append({ 'name': reviewer, 'revset': 'reviewer(%s)' % reviewer, }) d['backsoutnodes'] = [] backouts = commitparser.parse_backouts(description) if backouts: for node in backouts[0]: try: bctx = repo[node] d['backsoutnodes'].append({'node': bctx.hex()}) except error.RepoLookupError: pass # Repositories can define which TreeHerder repository they are associated # with. treeherder = repo.ui.config('mozilla', 'treeherder_repo') if treeherder: d['treeherderrepourl'] = 'https://treeherder.mozilla.org/#/jobs?repo=%s' % treeherder d['treeherderrepo'] = treeherder push = repo.pushlog.pushfromchangeset(ctx) # Don't print Perfherder link on non-publishing repos (like Try) # because the previous push likely has nothing to do with this # push. if push and push.nodes and repo.ui.configbool('phases', 'publish', True): lastpushhead = repo[push.nodes[0]].hex() d['perfherderurl'] = ( 'https://treeherder.mozilla.org/perf.html#/compare?' 'originalProject=%s&' 'originalRevision=%s&' 'newProject=%s&' 'newRevision=%s') % (treeherder, push.nodes[-1], treeherder, lastpushhead) # If this changeset was converted from another one and we know which repo # it came from, add that metadata. convertrevision = ctx.extra().get('convert_revision') if convertrevision: sourcerepo = repo.ui.config('hgmo', 'convertsource') if sourcerepo: d['convertsourcepath'] = sourcerepo d['convertsourcenode'] = convertrevision if onlycheap: return # Obtain the Gecko/app version/milestone. # # We could probably only do this if the repo is a known app repo (by # looking at the initial changeset). But, path based lookup is relatively # fast, so just do it. However, we need this in the "onlycheap" # section because resolving manifests is relatively slow and resolving # several on changelist pages may add seconds to page load times. try: fctx = repo.filectx('config/milestone.txt', changeid=ctx.node()) lines = fctx.data().splitlines() lines = [l for l in lines if not l.startswith('#') and l.strip()] if lines: d['milestone'] = lines[0].strip() except error.LookupError: pass # Look for changesets that back out this one. # # We limit the distance we search for backouts because an exhaustive # search could be very intensive. e.g. you load up the root commit # on a repository with 200,000 changesets and that commit is never # backed out. This finds most backouts because backouts typically happen # shortly after a bad commit is introduced. thisshort = short(ctx.node()) count = 0 searchlimit = repo.ui.configint('hgmo', 'backoutsearchlimit', 100) for bctx in repo.set('%ld::', [ctx.rev()]): count += 1 if count >= searchlimit: break backouts = commitparser.parse_backouts( encoding.fromlocal(bctx.description())) if backouts and thisshort in backouts[0]: d['backedoutbynode'] = bctx.hex() break
def phabsend(ui, repo, *revs, **opts): """upload changesets to Phabricator If there are multiple revisions specified, they will be send as a stack with a linear dependencies relationship using the order specified by the revset. For the first time uploading changesets, local tags will be created to maintain the association. After the first time, phabsend will check obsstore and tags information so it can figure out whether to update an existing Differential Revision, or create a new one. If --amend is set, update commit messages so they have the ``Differential Revision`` URL, remove related tags. This is similar to what arcanist will do, and is more desired in author-push workflows. Otherwise, use local tags to record the ``Differential Revision`` association. The --confirm option lets you confirm changesets before sending them. You can also add following to your configuration file to make it default behaviour:: [phabsend] confirm = true phabsend will check obsstore and the above association to decide whether to update an existing Differential Revision, or create a new one. """ revs = list(revs) + opts.get(b'rev', []) revs = scmutil.revrange(repo, revs) if not revs: raise error.Abort(_(b'phabsend requires at least one changeset')) if opts.get(b'amend'): cmdutil.checkunfinished(repo) # {newnode: (oldnode, olddiff, olddrev} oldmap = getoldnodedrevmap(repo, [repo[r].node() for r in revs]) confirm = ui.configbool(b'phabsend', b'confirm') confirm |= bool(opts.get(b'confirm')) if confirm: confirmed = _confirmbeforesend(repo, revs, oldmap) if not confirmed: raise error.Abort(_(b'phabsend cancelled')) actions = [] reviewers = opts.get(b'reviewer', []) if reviewers: phids = user_group_phids(repo, reviewers) actions.append({b'type': b'reviewers.add', b'value': phids}) drevids = [] # [int] diffmap = {} # {newnode: diff} # Send patches one by one so we know their Differential Revision IDs and # can provide dependency relationship lastrevid = None for rev in revs: ui.debug(b'sending rev %d\n' % rev) ctx = repo[rev] acts = list(actions) reviewers = list(commitparser.parse_reviewers(ctx.description())) if reviewers: phids = user_group_phids(repo, reviewers) acts.append({b'type': b'reviewers.add', b'value': phids}) bugs = commitparser.parse_bugs(ctx.description()) if bugs: acts.append({b'type': b'bugzilla.bug-id', b'value': str(bugs[0]).encode('utf-8')}) # Get Differential Revision ID oldnode, olddiff, revid = oldmap.get(ctx.node(), (None, None, None)) if oldnode != ctx.node() or opts.get(b'amend'): # Create or update Differential Revision revision, diff = createdifferentialrevision( ctx, revid, lastrevid, oldnode, olddiff, acts) diffmap[ctx.node()] = diff newrevid = int(revision[r'object'][r'id']) if revid: action = b'updated' else: action = b'created' # Create a local tag to note the association, if commit message # does not have it already m = _differentialrevisiondescre.search(ctx.description()) if not m or int(m.group(b'id')) != newrevid: tagname = b'D%d' % newrevid tags.tag(repo, tagname, ctx.node(), message=None, user=None, date=None, local=True) else: # Nothing changed. But still set "newrevid" so the next revision # could depend on this one. newrevid = revid action = b'skipped' actiondesc = ui.label( {b'created': _(b'created'), b'skipped': _(b'skipped'), b'updated': _(b'updated')}[action], b'phabricator.action.%s' % action) drevdesc = ui.label(b'D%s' % newrevid, b'phabricator.drev') nodedesc = ui.label(bytes(ctx), b'phabricator.node') desc = ui.label(ctx.description().split(b'\n')[0], b'phabricator.desc') ui.write(_(b'%s - %s - %s: %s\n') % (drevdesc, actiondesc, nodedesc, desc)) drevids.append(newrevid) lastrevid = newrevid # Update commit messages and remove tags if opts.get(b'amend'): unfi = repo.unfiltered() drevs = callconduit(repo, b'differential.query', {b'ids': drevids}) with repo.wlock(), repo.lock(), repo.transaction(b'phabsend'): wnode = unfi[b'.'].node() mapping = {} # {oldnode: [newnode]} for i, rev in enumerate(revs): old = unfi[rev] drevid = drevids[i] drev = [d for d in drevs if int(d[r'id']) == drevid][0] newdesc = getdescfromdrev(drev) # Make sure commit message contain "Differential Revision" if old.description() != newdesc: parents = [ mapping.get(old.p1().node(), (old.p1(),))[0], mapping.get(old.p2().node(), (old.p2(),))[0], ] new = context.metadataonlyctx( repo, old, parents=parents, text=newdesc, user=old.user(), date=old.date(), extra=old.extra()) newnode = new.commit() mapping[old.node()] = [newnode] # Update diff property writediffproperties(unfi[newnode], diffmap[old.node()]) # Remove local tags since it's no longer necessary tagname = b'D%d' % drevid if tagname in repo.tags(): tags.tag(repo, tagname, nullid, message=None, user=None, date=None, local=True) scmutil.cleanupnodes(repo, mapping, b'phabsend') if wnode in mapping: unfi.setparents(mapping[wnode][0])
def reviewboard(repo, proto, args=None): proto.redirect() o = parsepayload(proto, args) if isinstance(o, ServerError): return formatresponse(str(o)) bzusername = o['bzusername'] bzapikey = o['bzapikey'] identifier, nodes, precursors = parseidentifier(o) if not identifier: return ['error %s' % _('no review identifier in request')] diffopts = mdiff.diffopts(context=8, showfunc=True, git=True) commits = { 'individual': [], 'squashed': {} } # We do multiple passes over the changesets requested for review because # some operations could be slow or may involve queries to external # resources. We want to run the fast checks first so we don't waste # resources before finding the error. The drawback here is the client # will not see the full set of errors. We may revisit this decision # later. for node in nodes: ctx = repo[node] # Reviewing merge commits doesn't make much sense and only makes # situations more complicated. So disallow the practice. if len(ctx.parents()) > 1: msg = 'cannot review merge commits (%s)' % short(ctx.node()) return formatresponse('error %s' % msg) # Invalid or confidental bugs will raise errors in the Review Board # interface later. Fail fast to minimize wasted time and resources. try: reviewid = ReviewID(identifier) except util.Abort as e: return formatresponse('error %s' % e) if reviewid.bug: # We use xmlrpc here because the Bugsy REST client doesn't currently handle # errors in responses. # We don't use available Bugzilla credentials because that's the # easiest way to test for confidential bugs. If/when we support posting # reviews to confidential bugs, we'll need to change this. xmlrpc_url = repo.ui.config('bugzilla', 'url').rstrip('/') + '/xmlrpc.cgi' proxy = xmlrpclib.ServerProxy(xmlrpc_url) try: proxy.Bug.get({'ids': [reviewid.bug]}) except xmlrpclib.Fault as f: if f.faultCode == 101: return formatresponse('error bug %s does not exist; ' 'please change the review id (%s)' % (reviewid.bug, reviewid.full)) elif f.faultCode == 102: return formatresponse('error bug %s could not be accessed ' '(we do not currently allow posting of reviews to ' 'confidential bugs)' % reviewid.bug) return formatresponse('error server error verifying bug %s exists; ' 'please retry or report a bug' % reviewid.bug) # Note patch.diff() appears to accept anything that can be fed into # repo[]. However, it blindly does a hex() on the argument as opposed # to the changectx, so we need to pass in the binary node. base_ctx = repo[nodes[0]].p1() base_parent_node = base_ctx.node() for i, node in enumerate(nodes): ctx = repo[node] p1 = ctx.p1().node() diff = None parent_diff = None diff = ''.join(patch.diff(repo, node1=p1, node2=ctx.node(), opts=diffopts)) + '\n' if i: base_commit_id = nodes[i-1] else: base_commit_id = base_ctx.hex() summary = ctx.description().splitlines()[0] commits['individual'].append({ 'id': node, 'precursors': precursors.get(node, []), 'message': ctx.description(), 'diff': diff, 'base_commit_id': base_commit_id, 'reviewers': list(commitparser.parse_reviewers(summary)) }) commits['squashed']['diff'] = ''.join(patch.diff(repo, node1=base_parent_node, node2=repo[nodes[-1]].node(), opts=diffopts)) + '\n' commits['squashed']['base_commit_id'] = base_ctx.hex() rburl = repo.ui.config('reviewboard', 'url', None).rstrip('/') repoid = repo.ui.configint('reviewboard', 'repoid', None) privleged_rb_username = repo.ui.config('reviewboard', 'username', None) privleged_rb_password = repo.ui.config('reviewboard', 'password', None) ldap_username = os.environ.get('USER') if ldap_username: associate_ldap_username(rburl, ldap_username, privleged_rb_username, privleged_rb_password, username=bzusername, apikey=bzapikey) lines = [ 'rburl %s' % rburl, 'reviewid %s' % identifier, ] try: parentrid, commitmap, reviews = post_reviews(rburl, repoid, identifier, commits, lines, username=bzusername, apikey=bzapikey) lines.extend([ 'parentreview %s' % parentrid, 'reviewdata %s status %s' % ( parentrid, urllib.quote(reviews[parentrid]['status'].encode('utf-8'))), 'reviewdata %s public %s' % ( parentrid, reviews[parentrid]['public']), ]) for node, rid in commitmap.items(): rd = reviews[rid] lines.append('csetreview %s %s' % (node, rid)) lines.append('reviewdata %s status %s' % (rid, urllib.quote(rd['status'].encode('utf-8')))) lines.append('reviewdata %s public %s' % (rid, rd['public'])) if rd['reviewers']: parts = [urllib.quote(r.encode('utf-8')) for r in rd['reviewers']] lines.append('reviewdata %s reviewers %s' % (rid, ','.join(parts))) except AuthorizationError as e: lines.append('error %s' % str(e)) except BadRequestError as e: lines.append('error %s' % str(e)) res = formatresponse(*lines) return res