2015-05-14 02:24:51 +03:00
|
|
|
# fbconduit.py
|
|
|
|
#
|
|
|
|
# An extension to query remote servers for extra information via conduit RPC
|
|
|
|
#
|
|
|
|
# Copyright 2015 Facebook, Inc.
|
|
|
|
|
2016-01-08 05:30:24 +03:00
|
|
|
from mercurial import error, templater, extensions, revset, templatekw, node
|
2015-05-15 23:36:16 +03:00
|
|
|
from mercurial.i18n import _
|
2015-07-15 23:40:08 +03:00
|
|
|
|
2015-05-15 23:36:16 +03:00
|
|
|
import re
|
2015-05-14 02:24:51 +03:00
|
|
|
import json
|
|
|
|
from urllib import urlencode
|
2016-07-04 13:00:37 +03:00
|
|
|
from mercurial.util import httplib
|
2015-05-14 02:24:51 +03:00
|
|
|
|
|
|
|
conduit_host = None
|
|
|
|
conduit_path = None
|
|
|
|
connection = None
|
|
|
|
|
|
|
|
MAX_CONNECT_RETRIES = 3
|
|
|
|
|
|
|
|
class ConduitError(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class HttpError(Exception):
|
|
|
|
pass
|
|
|
|
|
fbconduit: de-phabricator-ize hg and git hashes
Summary:
Remove the need to ommit the rREPO prefix when copy pasting from phabricator and
looking up a commit.
Test Plan:
Ran the following in phabricator enabled HG repos
11/13 12:15 cdelahousse@dev4253 ~/fbsource/fbcode
$ hg log -r rFBS27aa00fb74d9a3b82756dad6ff26fe253f1e9a70 --config extensions.fbconduit=~/local/fb-hgext/fbconduit.py
changeset: 992495:27aa00fb74d9
user: Peng Li <pengli@fb.com>
date: Tue Nov 03 10:29:01 2015 -0800
summary: Add a simple root dir arc library
11/13 12:21 cdelahousse@dev4253 ~/fbjava
$ hg log -r rFBA8f1335e6d588 --config extensions.fbconduit=~/local/fb-hgext/fbconduit.py
changeset: 18654:8f1335e6d588
user: Ryan Menezes <ryandm@fb.com>
date: Thu Oct 22 09:59:27 2015 -0700
summary: move jenkins hook into fbjava/arcanist (fbjava changes)
Tried the same on a repo with a git mirror that exists in phabricator:
11/16 11:40 cdelahousse@dev4253 ~/fbsource/fbcode
$ hg log -r rFBCODE8272d25d65869ce059024ff38c7051388ad7b802 --config extensions.fbconduit=~/local/fb-hgext/fbconduit.py
changeset: 1027287:f26a9c32ae08
user: Oleksandr Kuvshynov <oleksandr@fb.com>
date: Sun Nov 15 19:16:39 2015 -0800
summary: [mf] simple cleanup of feed story view
If a git hash is too small, abort:
$ hg log -r rFBCODE8272d25d65869ce059024f --config extensions.fbconduit=~/local/fb-hgext/fbconduit.py
abort: git hash must be 40 characters
The previous commands depends on a list of repos set in my hgrc. See (D2660070)
[fbconduit]
gitcallsigns=LK, CFPUMA, CFSV, CFGK, CFMF, CFGMON, CF, RSIGMA, FA,·
WAWEBCLIENT, SKY, FBCODE, FBOBJC, WAWP, SV, OVRMOBILEMAIN, WAANDROID, IGSRV,
CPE, MSS, ANDROIDSDK, BUCK, IT-CHEF, WABB, ITINFRA, SIGMA, IOSSDK
Reviewers: #sourcecontrol, durham, ericsumner
Reviewed By: durham, ericsumner
Subscribers: rmcelroy, ericsumner
Differential Revision: https://phabricator.fb.com/D2653497
Tasks: 8361368
Signature: t1:2653497:1447480768:295079a7793e182ddea3aeece9cfaead1bfc1a57
2015-11-18 02:22:08 +03:00
|
|
|
githashre = re.compile('g([0-9a-fA-F]{12,40})')
|
|
|
|
phabhashre = re.compile('^r([A-Z]+)([0-9a-f]{12,40})$')
|
2015-11-13 22:31:46 +03:00
|
|
|
fbsvnhash = re.compile('^r[A-Z]+(\d+)$')
|
2015-05-15 23:36:16 +03:00
|
|
|
|
2015-05-14 02:24:51 +03:00
|
|
|
def extsetup(ui):
|
2015-07-15 09:04:03 +03:00
|
|
|
global conduit_host, conduit_path, conduit_protocol
|
2015-05-14 02:24:51 +03:00
|
|
|
conduit_host = ui.config('fbconduit', 'host')
|
|
|
|
conduit_path = ui.config('fbconduit', 'path')
|
2015-07-15 09:04:03 +03:00
|
|
|
conduit_protocol = ui.config('fbconduit', 'protocol')
|
|
|
|
|
2015-05-14 02:24:51 +03:00
|
|
|
if not conduit_host:
|
2016-01-11 21:19:22 +03:00
|
|
|
ui.warn(('No conduit host specified in config; disabling fbconduit\n'))
|
2015-05-14 02:24:51 +03:00
|
|
|
return
|
2015-07-15 09:04:03 +03:00
|
|
|
if not conduit_protocol:
|
|
|
|
conduit_protocol = 'https'
|
2015-05-14 02:24:51 +03:00
|
|
|
templater.funcs['mirrornode'] = mirrornode
|
2015-05-15 23:36:16 +03:00
|
|
|
templatekw.keywords['gitnode'] = showgitnode
|
|
|
|
|
|
|
|
revset.symbols['gitnode'] = gitnode
|
|
|
|
extensions.wrapfunction(revset, 'stringset', overridestringset)
|
|
|
|
revset.symbols['stringset'] = revset.stringset
|
|
|
|
revset.methods['string'] = revset.stringset
|
|
|
|
revset.methods['symbol'] = revset.stringset
|
2015-05-14 02:24:51 +03:00
|
|
|
|
|
|
|
def _call_conduit(method, **kwargs):
|
2015-07-15 09:04:03 +03:00
|
|
|
global connection, conduit_host, conduit_path, conduit_protocol
|
2015-05-14 02:24:51 +03:00
|
|
|
|
|
|
|
# start connection
|
|
|
|
if connection is None:
|
2015-07-15 09:04:03 +03:00
|
|
|
if conduit_protocol == 'https':
|
|
|
|
connection = httplib.HTTPSConnection(conduit_host)
|
|
|
|
elif conduit_protocol == 'http':
|
|
|
|
connection = httplib.HTTPConnection(conduit_host)
|
2015-05-14 02:24:51 +03:00
|
|
|
|
|
|
|
# send request
|
|
|
|
path = conduit_path + method
|
|
|
|
args = urlencode({'params': json.dumps(kwargs)})
|
|
|
|
for attempt in xrange(MAX_CONNECT_RETRIES):
|
|
|
|
try:
|
|
|
|
connection.request('POST', path, args, {'Connection': 'Keep-Alive'})
|
2016-01-08 05:30:24 +03:00
|
|
|
break
|
2015-05-14 02:24:51 +03:00
|
|
|
except httplib.HTTPException as e:
|
|
|
|
connection.connect()
|
|
|
|
else:
|
|
|
|
raise e
|
|
|
|
|
|
|
|
# read http response
|
|
|
|
response = connection.getresponse()
|
|
|
|
if response.status != 200:
|
|
|
|
raise HttpError(response.reason)
|
|
|
|
result = response.read()
|
|
|
|
|
|
|
|
# strip jsonp header and parse
|
|
|
|
assert result.startswith('for(;;);')
|
|
|
|
result = json.loads(result[8:])
|
|
|
|
|
|
|
|
# check for conduit errors
|
|
|
|
if result['error_code']:
|
|
|
|
raise ConduitError(result['error_info'])
|
|
|
|
|
|
|
|
# return RPC result
|
|
|
|
return result['result']
|
|
|
|
|
|
|
|
# don't close the connection b/c we want to avoid the connection overhead
|
|
|
|
|
|
|
|
def mirrornode(ctx, mapping, args):
|
|
|
|
'''template: find this commit in other repositories'''
|
|
|
|
|
|
|
|
reponame = mapping['repo'].ui.config('fbconduit', 'reponame')
|
|
|
|
if not reponame:
|
|
|
|
# We don't know who we are, so we can't ask for a translation
|
|
|
|
return ''
|
|
|
|
|
|
|
|
if mapping['ctx'].mutable():
|
|
|
|
# Local commits don't have translations
|
|
|
|
return ''
|
|
|
|
|
|
|
|
node = mapping['ctx'].hex()
|
|
|
|
args = [f(ctx, mapping, a) for f, a in args]
|
|
|
|
if len(args) == 1:
|
|
|
|
torepo, totype = reponame, args[0]
|
|
|
|
else:
|
|
|
|
torepo, totype = args
|
|
|
|
|
|
|
|
try:
|
|
|
|
result = _call_conduit('scmquery.get.mirrored.revs',
|
|
|
|
from_repo=reponame,
|
|
|
|
from_scm='hg',
|
|
|
|
to_repo=torepo,
|
|
|
|
to_scm=totype,
|
|
|
|
revs=[node]
|
|
|
|
)
|
|
|
|
except ConduitError as e:
|
|
|
|
if 'unknown revision' not in str(e.args):
|
2016-01-11 21:19:22 +03:00
|
|
|
mapping['repo'].ui.warn((str(e.args) + '\n'))
|
2015-05-14 02:24:51 +03:00
|
|
|
return ''
|
|
|
|
return result.get(node, '')
|
2015-05-15 23:36:16 +03:00
|
|
|
|
|
|
|
def showgitnode(repo, ctx, templ, **args):
|
|
|
|
"""Return the git revision corresponding to a given hg rev"""
|
|
|
|
reponame = repo.ui.config('fbconduit', 'reponame')
|
|
|
|
if not reponame:
|
|
|
|
# We don't know who we are, so we can't ask for a translation
|
|
|
|
return ''
|
2016-01-08 05:30:24 +03:00
|
|
|
backingrepos = repo.ui.configlist('fbconduit', 'backingrepos',
|
|
|
|
default=[reponame])
|
2015-05-15 23:36:16 +03:00
|
|
|
|
|
|
|
if ctx.mutable():
|
|
|
|
# Local commits don't have translations
|
|
|
|
return ''
|
2015-07-15 09:04:03 +03:00
|
|
|
|
2015-07-16 00:48:57 +03:00
|
|
|
matches = []
|
|
|
|
for backingrepo in backingrepos:
|
|
|
|
try:
|
|
|
|
result = _call_conduit('scmquery.get.mirrored.revs',
|
|
|
|
from_repo=reponame,
|
|
|
|
from_scm='hg',
|
|
|
|
to_repo=backingrepo,
|
|
|
|
to_scm='git',
|
|
|
|
revs=[ctx.hex()]
|
|
|
|
)
|
2015-08-11 07:22:21 +03:00
|
|
|
githash = result[ctx.hex()]
|
|
|
|
if githash != "":
|
|
|
|
matches.append((backingrepo, githash))
|
2015-07-16 00:48:57 +03:00
|
|
|
except ConduitError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
if len(matches) == 0:
|
2015-05-15 23:36:16 +03:00
|
|
|
return ''
|
2015-07-16 00:48:57 +03:00
|
|
|
elif len(backingrepos) == 1:
|
|
|
|
return matches[0][1]
|
|
|
|
else:
|
|
|
|
# in case it's not clear, the sort() is to ensure the output is in a
|
|
|
|
# deterministic order.
|
|
|
|
matches.sort()
|
|
|
|
return "; ".join(["{0}: {1}".format(*match)
|
|
|
|
for match in matches])
|
2015-05-15 23:36:16 +03:00
|
|
|
|
|
|
|
def gitnode(repo, subset, x):
|
|
|
|
"""``gitnode(id)``
|
|
|
|
Return the hg revision corresponding to a given git rev."""
|
|
|
|
l = revset.getargs(x, 1, 1, _("id requires one argument"))
|
|
|
|
n = revset.getstring(l[0], _("id requires a string"))
|
|
|
|
|
|
|
|
reponame = repo.ui.config('fbconduit', 'reponame')
|
|
|
|
if not reponame:
|
|
|
|
# We don't know who we are, so we can't ask for a translation
|
|
|
|
return subset.filter(lambda r: false)
|
2016-01-08 05:30:24 +03:00
|
|
|
backingrepos = repo.ui.configlist('fbconduit', 'backingrepos',
|
|
|
|
default=[reponame])
|
2015-05-15 23:36:16 +03:00
|
|
|
|
|
|
|
peerpath = repo.ui.expandpath('default')
|
2015-08-11 07:22:21 +03:00
|
|
|
translationerror = False
|
2015-07-16 00:48:57 +03:00
|
|
|
for backingrepo in backingrepos:
|
|
|
|
try:
|
|
|
|
result = _call_conduit('scmquery.get.mirrored.revs',
|
|
|
|
from_repo=backingrepo,
|
|
|
|
from_scm='git',
|
|
|
|
to_repo=reponame,
|
|
|
|
to_scm='hg',
|
|
|
|
revs=[n]
|
|
|
|
)
|
2015-08-11 07:22:21 +03:00
|
|
|
hghash = result[n]
|
|
|
|
if hghash != '':
|
|
|
|
break
|
2015-07-16 00:48:57 +03:00
|
|
|
except ConduitError as e:
|
|
|
|
pass
|
|
|
|
else:
|
2015-08-11 07:22:21 +03:00
|
|
|
translationerror = True
|
|
|
|
|
|
|
|
if translationerror or result[n] == "":
|
2016-01-11 21:19:22 +03:00
|
|
|
repo.ui.warn(("Could not translate revision {0}.\n".format(n)))
|
2015-07-15 23:40:08 +03:00
|
|
|
return subset.filter(lambda r: False)
|
2015-08-11 07:22:21 +03:00
|
|
|
|
2015-07-15 23:40:08 +03:00
|
|
|
rn = repo[node.bin(result[n])].rev()
|
2015-05-15 23:36:16 +03:00
|
|
|
return subset.filter(lambda r: r == rn)
|
|
|
|
|
|
|
|
def overridestringset(orig, repo, subset, x):
|
fbconduit: de-phabricator-ize hg and git hashes
Summary:
Remove the need to ommit the rREPO prefix when copy pasting from phabricator and
looking up a commit.
Test Plan:
Ran the following in phabricator enabled HG repos
11/13 12:15 cdelahousse@dev4253 ~/fbsource/fbcode
$ hg log -r rFBS27aa00fb74d9a3b82756dad6ff26fe253f1e9a70 --config extensions.fbconduit=~/local/fb-hgext/fbconduit.py
changeset: 992495:27aa00fb74d9
user: Peng Li <pengli@fb.com>
date: Tue Nov 03 10:29:01 2015 -0800
summary: Add a simple root dir arc library
11/13 12:21 cdelahousse@dev4253 ~/fbjava
$ hg log -r rFBA8f1335e6d588 --config extensions.fbconduit=~/local/fb-hgext/fbconduit.py
changeset: 18654:8f1335e6d588
user: Ryan Menezes <ryandm@fb.com>
date: Thu Oct 22 09:59:27 2015 -0700
summary: move jenkins hook into fbjava/arcanist (fbjava changes)
Tried the same on a repo with a git mirror that exists in phabricator:
11/16 11:40 cdelahousse@dev4253 ~/fbsource/fbcode
$ hg log -r rFBCODE8272d25d65869ce059024ff38c7051388ad7b802 --config extensions.fbconduit=~/local/fb-hgext/fbconduit.py
changeset: 1027287:f26a9c32ae08
user: Oleksandr Kuvshynov <oleksandr@fb.com>
date: Sun Nov 15 19:16:39 2015 -0800
summary: [mf] simple cleanup of feed story view
If a git hash is too small, abort:
$ hg log -r rFBCODE8272d25d65869ce059024f --config extensions.fbconduit=~/local/fb-hgext/fbconduit.py
abort: git hash must be 40 characters
The previous commands depends on a list of repos set in my hgrc. See (D2660070)
[fbconduit]
gitcallsigns=LK, CFPUMA, CFSV, CFGK, CFMF, CFGMON, CF, RSIGMA, FA,·
WAWEBCLIENT, SKY, FBCODE, FBOBJC, WAWP, SV, OVRMOBILEMAIN, WAANDROID, IGSRV,
CPE, MSS, ANDROIDSDK, BUCK, IT-CHEF, WABB, ITINFRA, SIGMA, IOSSDK
Reviewers: #sourcecontrol, durham, ericsumner
Reviewed By: durham, ericsumner
Subscribers: rmcelroy, ericsumner
Differential Revision: https://phabricator.fb.com/D2653497
Tasks: 8361368
Signature: t1:2653497:1447480768:295079a7793e182ddea3aeece9cfaead1bfc1a57
2015-11-18 02:22:08 +03:00
|
|
|
# Is the given revset a phabricator hg hash (ie: rHGEXTaaacb34aacb34aa)
|
|
|
|
phabmatch = phabhashre.match(x)
|
|
|
|
if phabmatch:
|
|
|
|
phabrepo = phabmatch.group(1)
|
|
|
|
phabhash = phabmatch.group(2)
|
|
|
|
# The hash may be a git hash
|
|
|
|
if phabrepo in repo.ui.configlist('fbconduit', 'gitcallsigns', []):
|
|
|
|
return overridestringset(orig, repo, subset, 'g%s' % phabhash)
|
|
|
|
|
|
|
|
if phabhash in repo:
|
|
|
|
return orig(repo, subset, phabhash)
|
|
|
|
|
|
|
|
# Is the given revset a phabricator svn revision (rO11223232323232)?
|
2015-11-10 02:54:07 +03:00
|
|
|
svnrev = fbsvnhash.match(x)
|
|
|
|
if svnrev and not x in repo:
|
|
|
|
try:
|
|
|
|
extensions.find('hgsubversion')
|
|
|
|
meta = repo.svnmeta()
|
|
|
|
|
|
|
|
desiredrevision = int(svnrev.group(1))
|
|
|
|
# For some odd reason, the key is a tuple instead of a revision num
|
|
|
|
# The second member always seems to be None
|
|
|
|
revmapkey = (desiredrevision, None)
|
|
|
|
hghash = meta.revmap.get(revmapkey)
|
|
|
|
if hghash:
|
|
|
|
return orig(repo, subset, hghash)
|
|
|
|
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
|
2015-05-15 23:36:16 +03:00
|
|
|
m = githashre.match(x)
|
|
|
|
if m is not None:
|
fbconduit: de-phabricator-ize hg and git hashes
Summary:
Remove the need to ommit the rREPO prefix when copy pasting from phabricator and
looking up a commit.
Test Plan:
Ran the following in phabricator enabled HG repos
11/13 12:15 cdelahousse@dev4253 ~/fbsource/fbcode
$ hg log -r rFBS27aa00fb74d9a3b82756dad6ff26fe253f1e9a70 --config extensions.fbconduit=~/local/fb-hgext/fbconduit.py
changeset: 992495:27aa00fb74d9
user: Peng Li <pengli@fb.com>
date: Tue Nov 03 10:29:01 2015 -0800
summary: Add a simple root dir arc library
11/13 12:21 cdelahousse@dev4253 ~/fbjava
$ hg log -r rFBA8f1335e6d588 --config extensions.fbconduit=~/local/fb-hgext/fbconduit.py
changeset: 18654:8f1335e6d588
user: Ryan Menezes <ryandm@fb.com>
date: Thu Oct 22 09:59:27 2015 -0700
summary: move jenkins hook into fbjava/arcanist (fbjava changes)
Tried the same on a repo with a git mirror that exists in phabricator:
11/16 11:40 cdelahousse@dev4253 ~/fbsource/fbcode
$ hg log -r rFBCODE8272d25d65869ce059024ff38c7051388ad7b802 --config extensions.fbconduit=~/local/fb-hgext/fbconduit.py
changeset: 1027287:f26a9c32ae08
user: Oleksandr Kuvshynov <oleksandr@fb.com>
date: Sun Nov 15 19:16:39 2015 -0800
summary: [mf] simple cleanup of feed story view
If a git hash is too small, abort:
$ hg log -r rFBCODE8272d25d65869ce059024f --config extensions.fbconduit=~/local/fb-hgext/fbconduit.py
abort: git hash must be 40 characters
The previous commands depends on a list of repos set in my hgrc. See (D2660070)
[fbconduit]
gitcallsigns=LK, CFPUMA, CFSV, CFGK, CFMF, CFGMON, CF, RSIGMA, FA,·
WAWEBCLIENT, SKY, FBCODE, FBOBJC, WAWP, SV, OVRMOBILEMAIN, WAANDROID, IGSRV,
CPE, MSS, ANDROIDSDK, BUCK, IT-CHEF, WABB, ITINFRA, SIGMA, IOSSDK
Reviewers: #sourcecontrol, durham, ericsumner
Reviewed By: durham, ericsumner
Subscribers: rmcelroy, ericsumner
Differential Revision: https://phabricator.fb.com/D2653497
Tasks: 8361368
Signature: t1:2653497:1447480768:295079a7793e182ddea3aeece9cfaead1bfc1a57
2015-11-18 02:22:08 +03:00
|
|
|
githash = m.group(1)
|
|
|
|
if len(githash) == 40:
|
|
|
|
return gitnode(repo, subset, ('string', githash))
|
|
|
|
else:
|
2016-01-08 05:30:24 +03:00
|
|
|
raise error.Abort('git hash must be 40 characters')
|
2015-05-15 23:36:16 +03:00
|
|
|
return orig(repo, subset, x)
|