コード例 #1
0
def get_auth(ui, bugzilla, profile):
    auth = getbugzillaauth(ui, require=True, profile=profile)
    if auth.apikey:
        return bzAuth(bugzilla, apikey=auth.apikey, username=auth.username)
    if auth.userid:
        return bzAuth(bugzilla, userid=auth.userid, cookie=auth.cookie)
    return bzAuth(bugzilla, username=auth.username, password=auth.password)
コード例 #2
0
def get_auth(ui, bugzilla, profile):
    auth = getbugzillaauth(ui, require=True, profile=profile)
    if auth.apikey:
        return bzAuth(bugzilla, apikey=auth.apikey, username=auth.username)
    if auth.userid:
        return bzAuth(bugzilla, userid=auth.userid, cookie=auth.cookie)
    return bzAuth(bugzilla, username=auth.username, password=auth.password)
コード例 #3
0
def bzauth(ui, require=False, fakegetpass=None):
    if fakegetpass:
        def newgetpass(arg):
            return fakegetpass
        getpass.getpass = newgetpass

    a = getbugzillaauth(ui, require=require)
    if a:
        ui.write('userid: %s\n' % a.userid)
        ui.write('cookie: %s\n' % a.cookie)
        ui.write('username: %s\n' % a.username)
        ui.write('password: %s\n' % a.password)
    else:
        ui.write('no auth\n')
コード例 #4
0
def bzauth(ui, require=False, fakegetpass=None, ffprofile=None):
    if fakegetpass:
        def newgetpass(arg):
            return fakegetpass
        getpass.getpass = newgetpass

    a = getbugzillaauth(ui, require=require, profile=ffprofile)
    if a:
        ui.write('userid: %s\n' % a.userid)
        ui.write('cookie: %s\n' % a.cookie)
        ui.write('username: %s\n' % a.username)
        ui.write('password: %s\n' % a.password)
        ui.write('apikey: %s\n' % a.apikey)
    else:
        ui.write('no auth\n')
コード例 #5
0
ファイル: auth.py プロジェクト: Nephyrin/bzexport
def bzauth(ui, require=False, fakegetpass=None, ffprofile=None):
    if fakegetpass:

        def newgetpass(arg):
            return fakegetpass

        getpass.getpass = newgetpass

    a = getbugzillaauth(ui, require=require, profile=ffprofile)
    if a:
        ui.write("userid: %s\n" % a.userid)
        ui.write("cookie: %s\n" % a.cookie)
        ui.write("username: %s\n" % a.username)
        ui.write("password: %s\n" % a.password)
        ui.write("apikey: %s\n" % a.apikey)
    else:
        ui.write("no auth\n")
コード例 #6
0
def wrappedpushbookmark(orig, pushop):
    result = orig(pushop)

    # pushop.ret was renamed to pushop.cgresult in Mercurial 3.2. We can drop
    # this branch once we drop <3.2 support.
    if hasattr(pushop, 'cgresult'):
        origresult = pushop.cgresult
    else:
        origresult = pushop.ret

    # Don't do anything if error from push.
    if not origresult:
        return result

    remoteurl = pushop.remote.url()
    tree = repository.resolve_uri_to_tree(remoteurl)
    # We don't support release trees (yet) because they have special flags
    # that need to get updated.
    if tree and tree in repository.RELEASE_TREES:
        return result

    ui = pushop.ui
    if tree and tree in ui.configlist('bzpost', 'excludetrees', default=[]):
        return result

    if tree:
        baseuri = repository.resolve_trees_to_uris([tree])[0][1].encode('utf-8')
        assert baseuri
    else:
        # This isn't a known Firefox tree. Fall back to resolving URLs by
        # hostname.

        # Only attend Mozilla's server.
        if not updateunknown(remoteurl, repository.BASE_WRITE_URI, ui):
            return result

        baseuri = remoteurl.replace(repository.BASE_WRITE_URI, repository.BASE_READ_URI).rstrip('/')

    bugsmap = {}
    lastbug = None
    lastnode = None

    for node in pushop.outgoing.missing:
        ctx = pushop.repo[node]

        # Don't do merge commits.
        if len(ctx.parents()) > 1:
            continue

        # Our bug parser is buggy for Gaia bump commit messages.
        if '<*****@*****.**>' in ctx.user():
            continue

        # Pushing to Try (and possibly other repos) could push unrelated
        # changesets that have been pushed to an official tree but aren't yet
        # on this specific remote. We use the phase information as a proxy
        # for "already pushed" and prune public changesets from consideration.
        if tree == 'try' and ctx.phase() == phases.public:
            continue

        bugs = parse_bugs(ctx.description())

        if not bugs:
            continue

        bugsmap.setdefault(bugs[0], []).append(ctx.hex()[0:12])
        lastbug = bugs[0]
        lastnode = ctx.hex()[0:12]

    if not bugsmap:
        return result

    bzauth = getbugzillaauth(ui)
    if not bzauth:
        return result

    bzurl = ui.config('bugzilla', 'url', 'https://bugzilla.mozilla.org/rest')

    bugsy = Bugsy(username=bzauth.username, password=bzauth.password,
                  userid=bzauth.userid, cookie=bzauth.cookie,
                  api_key=bzauth.apikey, bugzilla_url=bzurl)

    def public_url_for_bug(bug):
        '''Turn 123 into "https://bugzilla.mozilla.org/show_bug.cgi?id=123".'''
        public_baseurl = bzurl.replace('rest', '').rstrip('/')
        return '%s/show_bug.cgi?id=%s' % (public_baseurl, bug)

    # If this is a try push, we paste the Treeherder link for the tip commit, because
    # the per-commit URLs don't have much value.
    # TODO roll this into normal pushing so we get a Treeherder link in bugs as well.
    if tree == 'try' and lastbug:
        treeherderurl = repository.treeherder_url(tree, lastnode)

        bug = bugsy.get(lastbug)
        comments = bug.get_comments()
        for comment in comments:
            if treeherderurl in comment.text:
                return result

        ui.write(_('recording Treeherder push at %s\n') % public_url_for_bug(lastbug))
        bug.add_comment(treeherderurl)
        return result

    for bugnumber, nodes in bugsmap.items():
        bug = bugsy.get(bugnumber)

        comments = bug.get_comments()
        missing_nodes = []

        # When testing whether this changeset URL is referenced in a
        # comment, we only need to test for the node fragment. The
        # important side-effect is that each unique node for a changeset
        # is recorded in the bug.
        for node in nodes:
            if not any(node in comment.text for comment in comments):
                missing_nodes.append(node)

        if not missing_nodes:
            ui.write(_('bug %s already knows about pushed changesets\n') %
                     bugnumber)
            continue

        lines = []

        for node in missing_nodes:
            ctx = pushop.repo[node]
            lines.append('%s/rev/%s' % (baseuri, ctx.hex()))
            # description is using local encodings. Depending on the
            # configured encoding, replacement characters could be involved. We
            # use encoding.fromlocal() to get the raw bytes, which should be
            # valid UTF-8.
            lines.append(encoding.fromlocal(ctx.description()).splitlines()[0])
            lines.append('')

        comment = '\n'.join(lines)

        ui.write(_('recording push at %s\n') % public_url_for_bug(bugnumber))
        bug.add_comment(comment)

    return result
コード例 #7
0
def doreview(repo, ui, remote, nodes):
    """Do the work of submitting a review to a remote repo.

    :remote is a peerrepository.
    :nodes is a list of nodes to review.
    """
    assert nodes
    assert "pushreview" in getreviewcaps(remote)

    bzauth = getbugzillaauth(ui)
    if not bzauth:
        ui.warn(_("Bugzilla credentials not available. Not submitting review.\n"))
        return

    identifier = None

    # The review identifier can come from a number of places. In order of
    # priority:
    # 1. --reviewid argument passed to push command
    # 2. The active bookmark
    # 3. The active branch (if it isn't default)
    # 4. A bug number extracted from commit messages

    if repo.reviewid:
        identifier = repo.reviewid

    # TODO The server currently requires a bug number for the identifier.
    # Pull bookmark and branch names in once allowed.
    # elif repo._bookmarkcurrent:
    #    identifier = repo._bookmarkcurrent
    # elif repo.dirstate.branch() != 'default':
    #    identifier = repo.dirstate.branch()

    if not identifier:
        for node in nodes:
            ctx = repo[node]
            bugs = parse_bugs(ctx.description())
            if bugs:
                identifier = "bz://%s" % bugs[0]
                break

    identifier = ReviewID(identifier)

    if not identifier:
        ui.write(
            _(
                "Unable to determine review identifier. Review "
                "identifiers are extracted from commit messages automatically. "
                'Try to begin one of your commit messages with "Bug XXXXXX -"\n'
            )
        )
        return

    # Append irc nick to review identifier.
    # This is an ugly workaround to a limitation in ReviewBoard. RB doesn't
    # really support changing the owner of a review. It is doable, but no
    # history is stored and this leads to faulty attribution. More details
    # in bug 1034188.
    if not identifier.user:
        ircnick = ui.config("mozilla", "ircnick", None)
        identifier.user = ircnick

    if hasattr(repo, "mq"):
        for patch in repo.mq.applied:
            if patch.node in nodes:
                ui.warn(
                    _(
                        "(You are using mq to develop patches. For the best "
                        "code review experience, use bookmark-based development "
                        "with changeset evolution. Read more at "
                        "http://mozilla-version-control-tools.readthedocs.org/en/latest/mozreview-user.html)\n"
                    )
                )
                break

    lines = commonrequestlines(ui, bzauth)
    lines.append("reviewidentifier %s" % urllib.quote(identifier.full))

    reviews = repo.reviews
    oldparentid = reviews.findparentreview(identifier=identifier.full)

    # Include obsolescence data so server can make intelligent decisions.
    obsstore = repo.obsstore
    for node in nodes:
        lines.append("csetreview %s" % hex(node))
        precursors = [hex(n) for n in obsolete.allprecursors(obsstore, [node])]
        lines.append("precursors %s %s" % (hex(node), " ".join(precursors)))

    ui.write(_("submitting %d changesets for review\n") % len(nodes))

    res = remote._call("pushreview", data="\n".join(lines))
    lines = getpayload(res)

    newparentid = None
    nodereviews = {}
    reviewdata = {}

    for line in lines:
        t, d = line.split(" ", 1)

        if t == "display":
            ui.write("%s\n" % d)
        elif t == "error":
            raise util.Abort(d)
        elif t == "parentreview":
            newparentid = d
            reviews.addparentreview(identifier.full, newparentid)
            reviewdata[newparentid] = {}
        elif t == "csetreview":
            node, rid = d.split(" ", 1)
            node = bin(node)
            reviewdata[rid] = {}
            nodereviews[node] = rid
        elif t == "reviewdata":
            rid, field, value = d.split(" ", 2)
            reviewdata[rid][field] = decodepossiblelistvalue(value)
        elif t == "rburl":
            reviews.baseurl = d

    reviews.remoteurl = remote.url()

    for node, rid in nodereviews.items():
        reviews.addnodereview(node, rid, newparentid)

    reviews.write()
    for rid, data in reviewdata.iteritems():
        reviews.savereviewrequest(rid, data)

    havedraft = False

    ui.write("\n")
    for node in nodes:
        rid = nodereviews[node]
        ctx = repo[node]
        # Bug 1065024 use cmdutil.show_changeset() here.
        ui.write("changeset:  %s:%s\n" % (ctx.rev(), ctx.hex()[0:12]))
        ui.write("summary:    %s\n" % ctx.description().splitlines()[0])
        ui.write("review:     %s" % reviews.reviewurl(rid))
        if reviewdata[rid].get("public") == "False":
            havedraft = True
            ui.write(" (draft)")
        ui.write("\n\n")

    ui.write(_("review id:  %s\n") % identifier.full)
    ui.write(_("review url: %s") % reviews.parentreviewurl(identifier.full))
    if reviewdata[newparentid].get("public", None) == "False":
        havedraft = True
        ui.write(" (draft)")
    ui.write("\n")

    # Warn people that they have not assigned reviewers for at least some
    # of their commits.
    for node in nodes:
        rd = reviewdata[nodereviews[node]]
        if not rd.get("reviewers", None):
            ui.status(_("(review requests lack reviewers; visit review url " "to assign reviewers)\n"))
            break

    # Make it clear to the user that they need to take action in order for
    # others to see this review series.
    if havedraft:
        # At some point we may want an yes/no/prompt option for autopublish
        # but for safety reasons we only allow no/prompt for now.
        if ui.configbool("reviewboard", "autopublish", True):
            ui.write("\n")
            publish = ui.promptchoice(_("publish these review requests now (Yn)? $$ &Yes $$ &No"))
            if publish == 0:
                publishreviewrequests(ui, remote, bzauth, [newparentid])
            else:
                ui.status(_("(visit review url to publish these review " "requests so others can see them)\n"))
        else:
            ui.status(_("(visit review url to publish these review requests " "so others can see them)\n"))
コード例 #8
0
def doreview(repo, ui, remote, nodes):
    """Do the work of submitting a review to a remote repo.

    :remote is a peerrepository.
    :nodes is a list of nodes to review.
    """
    assert nodes
    assert 'pushreview' in getreviewcaps(remote)

    bzauth = getbugzillaauth(ui)
    if not bzauth:
        ui.warn(_('Bugzilla credentials not available. Not submitting review.\n'))
        return

    identifier = None

    # The review identifier can come from a number of places. In order of
    # priority:
    # 1. --reviewid argument passed to push command
    # 2. The active bookmark
    # 3. The active branch (if it isn't default)
    # 4. A bug number extracted from commit messages

    if repo.reviewid:
        identifier = repo.reviewid

    # TODO The server currently requires a bug number for the identifier.
    # Pull bookmark and branch names in once allowed.
    #elif repo._bookmarkcurrent:
    #    identifier = repo._bookmarkcurrent
    #elif repo.dirstate.branch() != 'default':
    #    identifier = repo.dirstate.branch()

    if not identifier:
        identifiers = set()
        for node in nodes:
            ctx = repo[node]
            bugs = parse_bugs(ctx.description().split('\n')[0])
            if bugs:
                identifier = 'bz://%s' % bugs[0]
                identifiers.add(identifier)

        if len(identifiers) > 1:
            raise util.Abort('cannot submit reviews referencing multiple '
                             'bugs', hint='limit reviewed changesets '
                             'with "-c" or "-r" arguments')

    identifier = ReviewID(identifier)

    if not identifier:
        ui.write(_('Unable to determine review identifier. Review '
            'identifiers are extracted from commit messages automatically. '
            'Try to begin one of your commit messages with "Bug XXXXXX -"\n'))
        return

    # Append irc nick to review identifier.
    # This is an ugly workaround to a limitation in ReviewBoard. RB doesn't
    # really support changing the owner of a review. It is doable, but no
    # history is stored and this leads to faulty attribution. More details
    # in bug 1034188.
    if not identifier.user:
        ircnick = ui.config('mozilla', 'ircnick', None)
        identifier.user = ircnick

    if hasattr(repo, 'mq'):
        for patch in repo.mq.applied:
            if patch.node in nodes:
                ui.warn(_('(You are using mq to develop patches. For the best '
                    'code review experience, use bookmark-based development '
                    'with changeset evolution. Read more at '
                    'http://mozilla-version-control-tools.readthedocs.org/en/latest/mozreview-user.html)\n'))
                break

    req = commonrequestdict(ui, bzauth)
    req['identifier'] = identifier.full
    req['changesets'] = []
    req['obsolescence'] = obsolete.isenabled(repo, obsolete.createmarkersopt)

    reviews = repo.reviews
    oldparentid = reviews.findparentreview(identifier=identifier.full)

    # Include obsolescence data so server can make intelligent decisions.
    obsstore = repo.obsstore
    for node in nodes:
        precursors = [hex(n) for n in obsolete.allprecursors(obsstore, [node])]
        req['changesets'].append({
            'node': hex(node),
            'precursors': precursors,
        })

    ui.write(_('submitting %d changesets for review\n') % len(nodes))

    res = calljsoncommand(ui, remote, 'pushreview', data=req, httpcap='submithttp',
                          httpcommand='mozreviewsubmitseries')
    if 'error' in res:
        raise error.Abort(res['error'])

    for w in res['display']:
        ui.write('%s\n' % w)

    reviews.baseurl = res['rburl']
    newparentid = res['parentrrid']
    reviews.addparentreview(identifier.full, newparentid)

    nodereviews = {}
    reviewdata = {}

    for rid, info in sorted(res['reviewrequests'].iteritems()):
        if 'node' in info:
            node = bin(info['node'])
            nodereviews[node] = rid

        reviewdata[rid] = {
            'status': info['status'],
            'public': info['public'],
        }

        if 'reviewers' in info:
            reviewdata[rid]['reviewers'] = info['reviewers']

    reviews.remoteurl = remote.url()

    for node, rid in nodereviews.items():
        reviews.addnodereview(node, rid, newparentid)

    reviews.write()
    for rid, data in reviewdata.iteritems():
        reviews.savereviewrequest(rid, data)

    havedraft = False

    ui.write('\n')
    for node in nodes:
        rid = nodereviews[node]
        ctx = repo[node]
        # Bug 1065024 use cmdutil.show_changeset() here.
        ui.write('changeset:  %s:%s\n' % (ctx.rev(), ctx.hex()[0:12]))
        ui.write('summary:    %s\n' % ctx.description().splitlines()[0])
        ui.write('review:     %s' % reviews.reviewurl(rid))
        if not reviewdata[rid].get('public'):
            havedraft = True
            ui.write(' (draft)')
        ui.write('\n\n')

    ui.write(_('review id:  %s\n') % identifier.full)
    ui.write(_('review url: %s') % reviews.parentreviewurl(identifier.full))
    if not reviewdata[newparentid].get('public'):
        havedraft = True
        ui.write(' (draft)')
    ui.write('\n')

    # Warn people that they have not assigned reviewers for at least some
    # of their commits.
    for node in nodes:
        rd = reviewdata[nodereviews[node]]
        if not rd.get('reviewers', None):
            ui.status(_('(review requests lack reviewers; visit review url '
                        'to assign reviewers)\n'))
            break

    # Make it clear to the user that they need to take action in order for
    # others to see this review series.
    if havedraft:
        # At some point we may want an yes/no/prompt option for autopublish
        # but for safety reasons we only allow no/prompt for now.
        if ui.configbool('reviewboard', 'autopublish', True):
            ui.write('\n')
            publish = ui.promptchoice(
                _('publish these review requests now (Yn)? $$ &Yes $$ &No'))
            if publish == 0:
                publishreviewrequests(ui, remote, bzauth, [newparentid])
            else:
                ui.status(_('(visit review url to publish these review '
                            'requests so others can see them)\n'))
        else:
            ui.status(_('(visit review url to publish these review requests '
                        'so others can see them)\n'))
コード例 #9
0
def wrappedpushbookmark(orig, pushop):
    result = orig(pushop)

    # pushop.ret was renamed to pushop.cgresult in Mercurial 3.2. We can drop
    # this branch once we drop <3.2 support.
    if hasattr(pushop, 'cgresult'):
        origresult = pushop.cgresult
    else:
        origresult = pushop.ret

    # Don't do anything if error from push.
    if not origresult:
        return result

    remoteurl = pushop.remote.url()
    tree = repository.resolve_uri_to_tree(remoteurl)
    # We don't support release trees (yet) because they have special flags
    # that need to get updated.
    if tree and tree in repository.RELEASE_TREES:
        return result

    ui = pushop.ui
    if tree and tree in ui.configlist('bzpost', 'excludetrees', default=[]):
        return result

    if tree:
        baseuri = repository.resolve_trees_to_uris([tree
                                                    ])[0][1].encode('utf-8')
        assert baseuri
    else:
        # This isn't a known Firefox tree. Fall back to resolving URLs by
        # hostname.

        # Only attend Mozilla's server.
        if not updateunknown(remoteurl, repository.BASE_WRITE_URI, ui):
            return result

        baseuri = remoteurl.replace(repository.BASE_WRITE_URI,
                                    repository.BASE_READ_URI).rstrip('/')

    bugsmap = {}
    lastbug = None
    lastnode = None

    for node in pushop.outgoing.missing:
        ctx = pushop.repo[node]

        # Don't do merge commits.
        if len(ctx.parents()) > 1:
            continue

        # Our bug parser is buggy for Gaia bump commit messages.
        if '<*****@*****.**>' in ctx.user():
            continue

        # Pushing to Try (and possibly other repos) could push unrelated
        # changesets that have been pushed to an official tree but aren't yet
        # on this specific remote. We use the phase information as a proxy
        # for "already pushed" and prune public changesets from consideration.
        if tree == 'try' and ctx.phase() == phases.public:
            continue

        bugs = parse_bugs(ctx.description())

        if not bugs:
            continue

        bugsmap.setdefault(bugs[0], []).append(ctx.hex())
        lastbug = bugs[0]
        lastnode = ctx.hex()

    if not bugsmap:
        return result

    bzauth = getbugzillaauth(ui)
    if not bzauth:
        return result

    bzurl = ui.config('bugzilla', 'url', 'https://bugzilla.mozilla.org/rest')

    bugsy = Bugsy(username=bzauth.username,
                  password=bzauth.password,
                  userid=bzauth.userid,
                  cookie=bzauth.cookie,
                  api_key=bzauth.apikey,
                  bugzilla_url=bzurl)

    def public_url_for_bug(bug):
        '''Turn 123 into "https://bugzilla.mozilla.org/show_bug.cgi?id=123".'''
        public_baseurl = bzurl.replace('rest', '').rstrip('/')
        return '%s/show_bug.cgi?id=%s' % (public_baseurl, bug)

    # If this is a try push, we paste the Treeherder link for the tip commit, because
    # the per-commit URLs don't have much value.
    # TODO roll this into normal pushing so we get a Treeherder link in bugs as well.
    if tree == 'try' and lastbug:
        treeherderurl = repository.treeherder_url(tree, lastnode)

        bug = bugsy.get(lastbug)
        comments = bug.get_comments()
        for comment in comments:
            if treeherderurl in comment.text:
                return result

        ui.write(
            _('recording Treeherder push at %s\n') %
            public_url_for_bug(lastbug))
        bug.add_comment(treeherderurl)
        return result

    for bugnumber, nodes in bugsmap.items():
        bug = bugsy.get(bugnumber)

        comments = bug.get_comments()
        missing_nodes = []

        # When testing whether this changeset URL is referenced in a
        # comment, we only need to test for the node fragment. The
        # important side-effect is that each unique node for a changeset
        # is recorded in the bug.
        for node in nodes:
            if not any(node in comment.text for comment in comments):
                missing_nodes.append(node)

        if not missing_nodes:
            ui.write(
                _('bug %s already knows about pushed changesets\n') %
                bugnumber)
            continue

        lines = []

        for node in missing_nodes:
            ctx = pushop.repo[node]
            lines.append('%s/rev/%s' % (baseuri, ctx.hex()))
            # description is using local encodings. Depending on the
            # configured encoding, replacement characters could be involved. We
            # use encoding.fromlocal() to get the raw bytes, which should be
            # valid UTF-8.
            lines.append(encoding.fromlocal(ctx.description()).splitlines()[0])
            lines.append('')

        comment = '\n'.join(lines)

        ui.write(_('recording push at %s\n') % public_url_for_bug(bugnumber))
        bug.add_comment(comment)

    return result
コード例 #10
0
def doreview(repo, ui, remote, nodes):
    """Do the work of submitting a review to a remote repo.

    :remote is a peerrepository.
    :nodes is a list of nodes to review.
    """
    assert nodes
    assert 'pushreview' in getreviewcaps(remote)

    # Ensure a color for ui.warning is defined.
    try:
        color = extensions.find('color')
        if 'ui.warning' not in color._styles:
            color._styles['ui.warning'] = 'red'
    except Exception:
        pass

    bzauth = getbugzillaauth(ui)
    if not bzauth:
        ui.warn(
            _('Bugzilla credentials not available. Not submitting review.\n'))
        return

    identifier = None

    # The review identifier can come from a number of places. In order of
    # priority:
    # 1. --reviewid argument passed to push command
    # 2. The active bookmark
    # 3. The active branch (if it isn't default)
    # 4. A bug number extracted from commit messages

    if repo.reviewid:
        identifier = repo.reviewid

    # TODO The server currently requires a bug number for the identifier.
    # Pull bookmark and branch names in once allowed.
    #elif repo._bookmarkcurrent:
    #    identifier = repo._bookmarkcurrent
    #elif repo.dirstate.branch() != 'default':
    #    identifier = repo.dirstate.branch()

    if not identifier:
        identifiers = set()
        for node in nodes:
            ctx = repo[node]
            bugs = parse_bugs(ctx.description().split('\n')[0])
            if bugs:
                identifier = 'bz://%s' % bugs[0]
                identifiers.add(identifier)

        if len(identifiers) > 1:
            raise util.Abort(
                'cannot submit reviews referencing multiple '
                'bugs',
                hint='limit reviewed changesets '
                'with "-c" or "-r" arguments')

    identifier = ReviewID(identifier)

    if not identifier:
        ui.write(
            _('Unable to determine review identifier. Review '
              'identifiers are extracted from commit messages automatically. '
              'Try to begin one of your commit messages with "Bug XXXXXX -"\n')
        )
        return

    # Append irc nick to review identifier.
    # This is an ugly workaround to a limitation in ReviewBoard. RB doesn't
    # really support changing the owner of a review. It is doable, but no
    # history is stored and this leads to faulty attribution. More details
    # in bug 1034188.
    if not identifier.user:
        ircnick = ui.config('mozilla', 'ircnick', None)
        identifier.user = ircnick

    if hasattr(repo, 'mq'):
        for patch in repo.mq.applied:
            if patch.node in nodes:
                ui.warn(
                    _('(You are using mq to develop patches. For the best '
                      'code review experience, use bookmark-based development '
                      'with changeset evolution. Read more at '
                      'https://mozilla-version-control-tools.readthedocs.io/en/latest/mozreview-user.html)\n'
                      ))
                break

    req = commonrequestdict(ui, bzauth)
    req['identifier'] = identifier.full
    req['changesets'] = []
    req['obsolescence'] = obsolete.isenabled(repo, obsolete.createmarkersopt)
    req['deduce-reviewers'] = ui.configbool('reviewboard', 'deduce-reviewers',
                                            True)

    reviews = repo.reviews
    oldparentid = reviews.findparentreview(identifier=identifier.full)

    # Include obsolescence data so server can make intelligent decisions.
    obsstore = repo.obsstore
    for node in nodes:
        precursors = [hex(n) for n in obsolete.allprecursors(obsstore, [node])]
        req['changesets'].append({
            'node': hex(node),
            'precursors': precursors,
        })

    ui.write(_('submitting %d changesets for review\n') % len(nodes))

    res = calljsoncommand(ui,
                          remote,
                          'pushreview',
                          data=req,
                          httpcap='submithttp',
                          httpcommand='mozreviewsubmitseries')

    # Re-encode all items in res from u'' to utf-8 byte str to avoid
    # exceptions during str operations.
    reencoderesponseinplace(res)

    if 'error' in res:
        raise error.Abort(res['error'])

    for w in res['display']:
        ui.write('%s\n' % w)

    reviews.baseurl = res['rburl']
    newparentid = res['parentrrid']
    reviews.addparentreview(identifier.full, newparentid)

    nodereviews = {}
    reviewdata = {}

    for rid, info in sorted(res['reviewrequests'].iteritems()):
        if 'node' in info:
            node = bin(info['node'])
            nodereviews[node] = rid

        reviewdata[rid] = {
            'status': info['status'],
            'public': info['public'],
        }

        if 'reviewers' in info:
            reviewdata[rid]['reviewers'] = info['reviewers']

    reviews.remoteurl = remote.url()

    for node, rid in nodereviews.items():
        reviews.addnodereview(node, rid, newparentid)

    reviews.write()
    for rid, data in reviewdata.iteritems():
        reviews.savereviewrequest(rid, data)

    havedraft = False

    ui.write('\n')
    for node in nodes:
        rid = nodereviews[node]
        ctx = repo[node]
        # Bug 1065024 use cmdutil.show_changeset() here.
        ui.write('changeset:  %s:%s\n' % (ctx.rev(), ctx.hex()[0:12]))
        ui.write('summary:    %s\n' % ctx.description().splitlines()[0])
        ui.write('review:     %s' % reviews.reviewurl(rid))
        if not reviewdata[rid].get('public'):
            havedraft = True
            ui.write(' (draft)')
        ui.write('\n\n')

    ui.write(_('review id:  %s\n') % identifier.full)
    ui.write(_('review url: %s') % reviews.parentreviewurl(identifier.full))
    if not reviewdata[newparentid].get('public'):
        havedraft = True
        ui.write(' (draft)')
    ui.write('\n')

    # Warn people that they have not assigned reviewers for at least some
    # of their commits.
    for node in nodes:
        rd = reviewdata[nodereviews[node]]
        if not rd.get('reviewers', None):
            ui.write('\n')
            ui.warn(
                _('(review requests lack reviewers; visit review url '
                  'to assign reviewers)\n'))
            break

    # Make it clear to the user that they need to take action in order for
    # others to see this review series.
    if havedraft:
        # If there is no configuration value specified for
        # reviewboard.autopublish, prompt the user. Otherwise, publish
        # automatically or not based on this value.
        if ui.config('reviewboard', 'autopublish', None) is None:
            ui.write('\n')
            publish = ui.promptchoice(
                _('publish these review '
                  'requests now (Yn)? '
                  '$$ &Yes $$ &No')) == 0
        else:
            publish = ui.configbool('reviewboard', 'autopublish')

        if publish:
            publishreviewrequests(ui, remote, bzauth, [newparentid])
        else:
            ui.status(
                _('(visit review url to publish these review '
                  'requests so others can see them)\n'))
コード例 #11
0
def doreview(repo, ui, remote, reviewnode, basenode=None):
    """Do the work of submitting a review to a remote repo.

    :remote is a peerrepository.
    :reviewnode is the node of the tip to review.
    :basenode is the bottom node to review. If not specified, we will review
    all non-public ancestors of :reviewnode.
    """
    assert remote.capable('reviewboard')

    bzauth = getbugzillaauth(ui)
    if not bzauth:
        ui.warn(_('Bugzilla credentials not available. Not submitting review.\n'))
        return

    # Given a tip node, we need to find all changesets to review.
    #
    # A solution that works most of the time is to find all non-public
    # ancestors of that node. This is our default.
    #
    # If basenode is specified, we stop the traversal when we encounter it.
    #
    # Note that we will still refuse to review a public changeset even with
    # basenode. This decision is somewhat arbitrary and can be revisited later
    # if there is an actual need to review public changesets.
    nodes = [reviewnode]
    for node in repo[reviewnode].ancestors():
        ctx = repo[node]

        if ctx.phase() == phases.public:
            break
        if basenode and ctx.node() == basenode:
            nodes.insert(0, ctx.node())
            break

        nodes.insert(0, ctx.node())

    identifier = None

    # The review identifier can come from a number of places. In order of
    # priority:
    # 1. --reviewid argument passed to push command
    # 2. The active bookmark
    # 3. The active branch (if it isn't default)
    # 4. A bug number extracted from commit messages

    if repo.reviewid:
        identifier = repo.reviewid

    # TODO The server currently requires a bug number for the identifier.
    # Pull bookmark and branch names in once allowed.
    #elif repo._bookmarkcurrent:
    #    identifier = repo._bookmarkcurrent
    #elif repo.dirstate.branch() != 'default':
    #    identifier = repo.dirstate.branch()

    if not identifier:
        for node in nodes:
            ctx = repo[node]
            bugs = parse_bugs(ctx.description())
            if bugs:
                identifier = 'bz://%s' % bugs[0]
                break

    identifier = ReviewID(identifier)

    if not identifier:
        ui.write(_('Unable to determine review identifier. Review '
            'identifiers are extracted from commit messages automatically. '
            'Try to begin one of your commit messages with "Bug XXXXXX -"\n'))
        return

    # Append irc nick to review identifier.
    # This is an ugly workaround to a limitation in ReviewBoard. RB doesn't
    # really support changing the owner of a review. It is doable, but no
    # history is stored and this leads to faulty attribution. More details
    # in bug 1034188.
    if not identifier.user:
        ircnick = ui.config('mozilla', 'ircnick', None)
        identifier.user = ircnick

    if hasattr(repo, 'mq'):
        for patch in repo.mq.applied:
            if patch.node in nodes:
                ui.warn(_('(You are using mq to develop patches. For the best '
                    'code review experience, use bookmark-based development '
                    'with changeset evolution. Read more at '
                    'http://mozilla-version-control-tools.readthedocs.org/en/latest/mozreview-user.html)\n'))
                break

    lines = [
        '1',
        'reviewidentifier %s' % urllib.quote(identifier.full),
    ]

    for p in ('username', 'password', 'userid', 'cookie'):
        if getattr(bzauth, p, None):
            lines.append('bz%s %s' % (p, urllib.quote(getattr(bzauth, p))))

    reviews = repo.reviews
    oldparentid = reviews.findparentreview(identifier=identifier.full)

    # Include obsolescence data so server can make intelligent decisions.
    obsstore = repo.obsstore
    for node in nodes:
        lines.append('csetreview %s' % hex(node))
        precursors = [hex(n) for n in obsolete.allprecursors(obsstore, [node])]
        lines.append('precursors %s %s' % (hex(node), ' '.join(precursors)))

    ui.write(_('submitting %d changesets for review\n') % len(nodes))

    res = remote._call('pushreview', data='\n'.join(lines))

    # All protocol versions begin with: <version>\n
    try:
        off = res.index('\n')
        version = int(res[0:off])

        if version != 1:
            raise util.Abort(_('do not know how to handle response from server.'))
    except ValueError:
        raise util.Abort(_('invalid response from server.'))

    assert version == 1
    lines = res.split('\n')[1:]

    newparentid = None
    nodereviews = {}
    reviewdata = {}

    for line in lines:
        t, d = line.split(' ', 1)

        if t == 'display':
            ui.write('%s\n' % d)
        elif t == 'error':
            raise util.Abort(d)
        elif t == 'parentreview':
            newparentid = d
            reviews.addparentreview(identifier.full, newparentid)
            reviewdata[newparentid] = {}
        elif t == 'csetreview':
            node, rid = d.split(' ', 1)
            node = bin(node)
            reviews.addnodereview(node, rid, newparentid)
            reviewdata[rid] = {}
            nodereviews[node] = rid
        elif t == 'reviewdata':
            rid, field, value = d.split(' ', 2)
            value = urllib.unquote(value)
            reviewdata[rid][field] = value
        elif t == 'rburl':
            reviews.baseurl = d

    reviews.remoteurl = remote.url()

    reviews.write()
    for rid, data in reviewdata.iteritems():
        reviews.savereviewrequest(rid, data)

    ui.write('\n')
    for node in nodes:
        rid = nodereviews[node]
        ctx = repo[node]
        # Bug 1065024 use cmdutil.show_changeset() here.
        ui.write('changeset:  %s:%s\n' % (ctx.rev(), ctx.hex()[0:12]))
        ui.write('summary:    %s\n' % ctx.description().splitlines()[0])
        ui.write('review:     %s' % reviews.reviewurl(rid))
        if reviewdata[rid].get('status') == 'pending':
            ui.write(' (pending)')
        ui.write('\n\n')

    ispending = reviewdata[newparentid].get('status', None) == 'pending'
    ui.write(_('review id:  %s\n') % identifier.full)
    ui.write(_('review url: %s') % reviews.parentreviewurl(identifier.full))
    if ispending:
        ui.write(' (pending)')
    ui.write('\n')

    # Make it clear to the user that they need to take action in order for
    # others to see this review series.
    if ispending:
        ui.status(_('(visit review url to publish this review request so others can see it)\n'))
コード例 #12
0
ファイル: client.py プロジェクト: Nephyrin/bzexport
def doreview(repo, ui, remote, nodes):
    """Do the work of submitting a review to a remote repo.

    :remote is a peerrepository.
    :nodes is a list of nodes to review.
    """
    assert nodes
    assert 'pushreview' in getreviewcaps(remote)

    bzauth = getbugzillaauth(ui)
    if not bzauth:
        ui.warn(_('Bugzilla credentials not available. Not submitting review.\n'))
        return

    identifier = None

    # The review identifier can come from a number of places. In order of
    # priority:
    # 1. --reviewid argument passed to push command
    # 2. The active bookmark
    # 3. The active branch (if it isn't default)
    # 4. A bug number extracted from commit messages

    if repo.reviewid:
        identifier = repo.reviewid

    # TODO The server currently requires a bug number for the identifier.
    # Pull bookmark and branch names in once allowed.
    #elif repo._bookmarkcurrent:
    #    identifier = repo._bookmarkcurrent
    #elif repo.dirstate.branch() != 'default':
    #    identifier = repo.dirstate.branch()

    if not identifier:
        for node in nodes:
            ctx = repo[node]
            bugs = parse_bugs(ctx.description())
            if bugs:
                identifier = 'bz://%s' % bugs[0]
                break

    identifier = ReviewID(identifier)

    if not identifier:
        ui.write(_('Unable to determine review identifier. Review '
            'identifiers are extracted from commit messages automatically. '
            'Try to begin one of your commit messages with "Bug XXXXXX -"\n'))
        return

    # Append irc nick to review identifier.
    # This is an ugly workaround to a limitation in ReviewBoard. RB doesn't
    # really support changing the owner of a review. It is doable, but no
    # history is stored and this leads to faulty attribution. More details
    # in bug 1034188.
    if not identifier.user:
        ircnick = ui.config('mozilla', 'ircnick', None)
        identifier.user = ircnick

    if hasattr(repo, 'mq'):
        for patch in repo.mq.applied:
            if patch.node in nodes:
                ui.warn(_('(You are using mq to develop patches. For the best '
                    'code review experience, use bookmark-based development '
                    'with changeset evolution. Read more at '
                    'http://mozilla-version-control-tools.readthedocs.org/en/latest/mozreview-user.html)\n'))
                break

    lines = commonrequestlines(ui, bzauth)
    lines.append('reviewidentifier %s' % urllib.quote(identifier.full))

    reviews = repo.reviews
    oldparentid = reviews.findparentreview(identifier=identifier.full)

    # Include obsolescence data so server can make intelligent decisions.
    obsstore = repo.obsstore
    for node in nodes:
        lines.append('csetreview %s' % hex(node))
        precursors = [hex(n) for n in obsolete.allprecursors(obsstore, [node])]
        lines.append('precursors %s %s' % (hex(node), ' '.join(precursors)))

    ui.write(_('submitting %d changesets for review\n') % len(nodes))

    res = remote._call('pushreview', data='\n'.join(lines))
    lines = getpayload(res)

    newparentid = None
    nodereviews = {}
    reviewdata = {}

    for line in lines:
        t, d = line.split(' ', 1)

        if t == 'display':
            ui.write('%s\n' % d)
        elif t == 'error':
            raise util.Abort(d)
        elif t == 'parentreview':
            newparentid = d
            reviews.addparentreview(identifier.full, newparentid)
            reviewdata[newparentid] = {}
        elif t == 'csetreview':
            node, rid = d.split(' ', 1)
            node = bin(node)
            reviewdata[rid] = {}
            nodereviews[node] = rid
        elif t == 'reviewdata':
            rid, field, value = d.split(' ', 2)
            reviewdata[rid][field] = decodepossiblelistvalue(value)
        elif t == 'rburl':
            reviews.baseurl = d

    reviews.remoteurl = remote.url()

    for node, rid in nodereviews.items():
        reviews.addnodereview(node, rid, newparentid)

    reviews.write()
    for rid, data in reviewdata.iteritems():
        reviews.savereviewrequest(rid, data)

    havedraft = False

    ui.write('\n')
    for node in nodes:
        rid = nodereviews[node]
        ctx = repo[node]
        # Bug 1065024 use cmdutil.show_changeset() here.
        ui.write('changeset:  %s:%s\n' % (ctx.rev(), ctx.hex()[0:12]))
        ui.write('summary:    %s\n' % ctx.description().splitlines()[0])
        # We want to encourage people to use r? when asking for a review rather
        # than r=.
        if list(parse_requal_reviewers(ctx.description())):
            ui.warn(_('(It appears you are using r= to specify reviewers for a'
                ' patch under review. Please use r? to avoid ambiguity as to'
                ' whether or not review has been granted.)\n'))
        ui.write('review:     %s' % reviews.reviewurl(rid))
        if reviewdata[rid].get('public') == 'False':
            havedraft = True
            ui.write(' (draft)')
        ui.write('\n\n')

    ui.write(_('review id:  %s\n') % identifier.full)
    ui.write(_('review url: %s') % reviews.parentreviewurl(identifier.full))
    if reviewdata[newparentid].get('public', None) == 'False':
        havedraft = True
        ui.write(' (draft)')
    ui.write('\n')

    havereviewers = bool(nodes)
    for node in nodes:
        rd = reviewdata[nodereviews[node]]
        if not rd.get('reviewers', None):
            havereviewers = False
            break

    # Make it clear to the user that they need to take action in order for
    # others to see this review series.
    if havedraft:
        # If the series is ready for publishing, prompt the user to perform the
        # publishing.
        if havereviewers:
            caps = getreviewcaps(remote)
            if 'publish' in caps:
                ui.write('\n')
                publish = ui.promptchoice(
                    _('publish these review requests now (Yn)? $$ &Yes $$ &No'))
                if publish == 0:
                    publishreviewrequests(ui, remote, bzauth, [newparentid])
                else:
                    ui.status(_('(visit review url to publish these review '
                                'requests so others can see them)\n'))
            else:
                ui.status(_('(visit review url to publish these review requests'
                            'so others can see them)\n'))
        else:
            ui.status(_('(review requests lack reviewers; visit review url '
                        'to assign reviewers and publish these review '
                        'requests)\n'))