comparison hgext/phabricator.py @ 42434:f163e2b2594c

phabricator: pass ui instead of repo to callconduit This will help us make `hg debugcallconduit` work outside a hg repo as next patch will mark that command as no repo. Differential Revision: https://phab.mercurial-scm.org/D6498
author Pulkit Goyal <pulkit@yandex-team.ru>
date Sat, 08 Jun 2019 18:41:15 +0300
parents 500b64c5d991
children 16312ea45a8b
comparison
equal deleted inserted replaced
42433:500b64c5d991 42434:f163e2b2594c
208 raise error.Abort(_(b'Can\'t find conduit token associated to %s') 208 raise error.Abort(_(b'Can\'t find conduit token associated to %s')
209 % (url,)) 209 % (url,))
210 210
211 return url, token 211 return url, token
212 212
213 def callconduit(repo, name, params): 213 def callconduit(ui, name, params):
214 """call Conduit API, params is a dict. return json.loads result, or None""" 214 """call Conduit API, params is a dict. return json.loads result, or None"""
215 host, token = readurltoken(repo.ui) 215 host, token = readurltoken(ui)
216 url, authinfo = util.url(b'/'.join([host, b'api', name])).authinfo() 216 url, authinfo = util.url(b'/'.join([host, b'api', name])).authinfo()
217 repo.ui.debug(b'Conduit Call: %s %s\n' % (url, pycompat.byterepr(params))) 217 ui.debug(b'Conduit Call: %s %s\n' % (url, pycompat.byterepr(params)))
218 params = params.copy() 218 params = params.copy()
219 params[b'api.token'] = token 219 params[b'api.token'] = token
220 data = urlencodenested(params) 220 data = urlencodenested(params)
221 curlcmd = repo.ui.config(b'phabricator', b'curlcmd') 221 curlcmd = ui.config(b'phabricator', b'curlcmd')
222 if curlcmd: 222 if curlcmd:
223 sin, sout = procutil.popen2(b'%s -d @- %s' 223 sin, sout = procutil.popen2(b'%s -d @- %s'
224 % (curlcmd, procutil.shellquote(url))) 224 % (curlcmd, procutil.shellquote(url)))
225 sin.write(data) 225 sin.write(data)
226 sin.close() 226 sin.close()
227 body = sout.read() 227 body = sout.read()
228 else: 228 else:
229 urlopener = urlmod.opener(repo.ui, authinfo) 229 urlopener = urlmod.opener(ui, authinfo)
230 request = util.urlreq.request(pycompat.strurl(url), data=data) 230 request = util.urlreq.request(pycompat.strurl(url), data=data)
231 with contextlib.closing(urlopener.open(request)) as rsp: 231 with contextlib.closing(urlopener.open(request)) as rsp:
232 body = rsp.read() 232 body = rsp.read()
233 repo.ui.debug(b'Conduit Response: %s\n' % body) 233 ui.debug(b'Conduit Response: %s\n' % body)
234 parsed = pycompat.rapply( 234 parsed = pycompat.rapply(
235 lambda x: encoding.unitolocal(x) if isinstance(x, pycompat.unicode) 235 lambda x: encoding.unitolocal(x) if isinstance(x, pycompat.unicode)
236 else x, 236 else x,
237 json.loads(body) 237 json.loads(body)
238 ) 238 )
257 json.loads(rawparams) 257 json.loads(rawparams)
258 ) 258 )
259 # json.dumps only accepts unicode strings 259 # json.dumps only accepts unicode strings
260 result = pycompat.rapply(lambda x: 260 result = pycompat.rapply(lambda x:
261 encoding.unifromlocal(x) if isinstance(x, bytes) else x, 261 encoding.unifromlocal(x) if isinstance(x, bytes) else x,
262 callconduit(repo, name, params) 262 callconduit(ui, name, params)
263 ) 263 )
264 s = json.dumps(result, sort_keys=True, indent=2, separators=(u',', u': ')) 264 s = json.dumps(result, sort_keys=True, indent=2, separators=(u',', u': '))
265 ui.write(b'%s\n' % encoding.unitolocal(s)) 265 ui.write(b'%s\n' % encoding.unitolocal(s))
266 266
267 def getrepophid(repo): 267 def getrepophid(repo):
271 if repophid: 271 if repophid:
272 return repophid 272 return repophid
273 callsign = repo.ui.config(b'phabricator', b'callsign') 273 callsign = repo.ui.config(b'phabricator', b'callsign')
274 if not callsign: 274 if not callsign:
275 return None 275 return None
276 query = callconduit(repo, b'diffusion.repository.search', 276 query = callconduit(repo.ui, b'diffusion.repository.search',
277 {b'constraints': {b'callsigns': [callsign]}}) 277 {b'constraints': {b'callsigns': [callsign]}})
278 if len(query[b'data']) == 0: 278 if len(query[b'data']) == 0:
279 return None 279 return None
280 repophid = query[b'data'][0][b'phid'] 280 repophid = query[b'data'][0][b'phid']
281 repo.ui.setconfig(b'phabricator', b'repophid', repophid) 281 repo.ui.setconfig(b'phabricator', b'repophid', repophid)
327 327
328 # Double check if tags are genuine by collecting all old nodes from 328 # Double check if tags are genuine by collecting all old nodes from
329 # Phabricator, and expect precursors overlap with it. 329 # Phabricator, and expect precursors overlap with it.
330 if toconfirm: 330 if toconfirm:
331 drevs = [drev for force, precs, drev in toconfirm.values()] 331 drevs = [drev for force, precs, drev in toconfirm.values()]
332 alldiffs = callconduit(unfi, b'differential.querydiffs', 332 alldiffs = callconduit(unfi.ui, b'differential.querydiffs',
333 {b'revisionIDs': drevs}) 333 {b'revisionIDs': drevs})
334 getnode = lambda d: bin( 334 getnode = lambda d: bin(
335 getdiffmeta(d).get(b'node', b'')) or None 335 getdiffmeta(d).get(b'node', b'')) or None
336 for newnode, (force, precset, drev) in toconfirm.items(): 336 for newnode, (force, precset, drev) in toconfirm.items():
337 diffs = [d for d in alldiffs.values() 337 diffs = [d for d in alldiffs.values()
377 repophid = getrepophid(repo) 377 repophid = getrepophid(repo)
378 # Create a "Differential Diff" via "differential.createrawdiff" API 378 # Create a "Differential Diff" via "differential.createrawdiff" API
379 params = {b'diff': getdiff(ctx, mdiff.diffopts(git=True, context=32767))} 379 params = {b'diff': getdiff(ctx, mdiff.diffopts(git=True, context=32767))}
380 if repophid: 380 if repophid:
381 params[b'repositoryPHID'] = repophid 381 params[b'repositoryPHID'] = repophid
382 diff = callconduit(repo, b'differential.createrawdiff', params) 382 diff = callconduit(repo.ui, b'differential.createrawdiff', params)
383 if not diff: 383 if not diff:
384 raise error.Abort(_(b'cannot create diff for %s') % ctx) 384 raise error.Abort(_(b'cannot create diff for %s') % ctx)
385 return diff 385 return diff
386 386
387 def writediffproperties(ctx, diff): 387 def writediffproperties(ctx, diff):
395 b'branch': ctx.branch(), 395 b'branch': ctx.branch(),
396 b'node': ctx.hex(), 396 b'node': ctx.hex(),
397 b'parent': ctx.p1().hex(), 397 b'parent': ctx.p1().hex(),
398 }), 398 }),
399 } 399 }
400 callconduit(ctx.repo(), b'differential.setdiffproperty', params) 400 callconduit(ctx.repo().ui, b'differential.setdiffproperty', params)
401 401
402 params = { 402 params = {
403 b'diff_id': diff[b'id'], 403 b'diff_id': diff[b'id'],
404 b'name': b'local:commits', 404 b'name': b'local:commits',
405 b'data': templatefilters.json({ 405 b'data': templatefilters.json({
411 b'parents': [ctx.p1().hex()], 411 b'parents': [ctx.p1().hex()],
412 b'branch': ctx.branch(), 412 b'branch': ctx.branch(),
413 }, 413 },
414 }), 414 }),
415 } 415 }
416 callconduit(ctx.repo(), b'differential.setdiffproperty', params) 416 callconduit(ctx.repo().ui, b'differential.setdiffproperty', params)
417 417
418 def createdifferentialrevision(ctx, revid=None, parentrevid=None, oldnode=None, 418 def createdifferentialrevision(ctx, revid=None, parentrevid=None, oldnode=None,
419 olddiff=None, actions=None, comment=None): 419 olddiff=None, actions=None, comment=None):
420 """create or update a Differential Revision 420 """create or update a Differential Revision
421 421
461 if actions: 461 if actions:
462 transactions += actions 462 transactions += actions
463 463
464 # Parse commit message and update related fields. 464 # Parse commit message and update related fields.
465 desc = ctx.description() 465 desc = ctx.description()
466 info = callconduit(repo, b'differential.parsecommitmessage', 466 info = callconduit(repo.ui, b'differential.parsecommitmessage',
467 {b'corpus': desc}) 467 {b'corpus': desc})
468 for k, v in info[b'fields'].items(): 468 for k, v in info[b'fields'].items():
469 if k in [b'title', b'summary', b'testPlan']: 469 if k in [b'title', b'summary', b'testPlan']:
470 transactions.append({b'type': k, b'value': v}) 470 transactions.append({b'type': k, b'value': v})
471 471
472 params = {b'transactions': transactions} 472 params = {b'transactions': transactions}
473 if revid is not None: 473 if revid is not None:
474 # Update an existing Differential Revision 474 # Update an existing Differential Revision
475 params[b'objectIdentifier'] = revid 475 params[b'objectIdentifier'] = revid
476 476
477 revision = callconduit(repo, b'differential.revision.edit', params) 477 revision = callconduit(repo.ui, b'differential.revision.edit', params)
478 if not revision: 478 if not revision:
479 raise error.Abort(_(b'cannot create revision for %s') % ctx) 479 raise error.Abort(_(b'cannot create revision for %s') % ctx)
480 480
481 return revision, diff 481 return revision, diff
482 482
483 def userphids(repo, names): 483 def userphids(repo, names):
484 """convert user names to PHIDs""" 484 """convert user names to PHIDs"""
485 names = [name.lower() for name in names] 485 names = [name.lower() for name in names]
486 query = {b'constraints': {b'usernames': names}} 486 query = {b'constraints': {b'usernames': names}}
487 result = callconduit(repo, b'user.search', query) 487 result = callconduit(repo.ui, b'user.search', query)
488 # username not found is not an error of the API. So check if we have missed 488 # username not found is not an error of the API. So check if we have missed
489 # some names here. 489 # some names here.
490 data = result[b'data'] 490 data = result[b'data']
491 resolved = set(entry[b'fields'][b'username'].lower() for entry in data) 491 resolved = set(entry[b'fields'][b'username'].lower() for entry in data)
492 unresolved = set(names) - resolved 492 unresolved = set(names) - resolved
607 lastrevid = newrevid 607 lastrevid = newrevid
608 608
609 # Update commit messages and remove tags 609 # Update commit messages and remove tags
610 if opts.get(b'amend'): 610 if opts.get(b'amend'):
611 unfi = repo.unfiltered() 611 unfi = repo.unfiltered()
612 drevs = callconduit(repo, b'differential.query', {b'ids': drevids}) 612 drevs = callconduit(ui, b'differential.query', {b'ids': drevids})
613 with repo.wlock(), repo.lock(), repo.transaction(b'phabsend'): 613 with repo.wlock(), repo.lock(), repo.transaction(b'phabsend'):
614 wnode = unfi[b'.'].node() 614 wnode = unfi[b'.'].node()
615 mapping = {} # {oldnode: [newnode]} 615 mapping = {} # {oldnode: [newnode]}
616 for i, rev in enumerate(revs): 616 for i, rev in enumerate(revs):
617 old = unfi[rev] 617 old = unfi[rev]
793 def fetch(params): 793 def fetch(params):
794 """params -> single drev or None""" 794 """params -> single drev or None"""
795 key = (params.get(b'ids') or params.get(b'phids') or [None])[0] 795 key = (params.get(b'ids') or params.get(b'phids') or [None])[0]
796 if key in prefetched: 796 if key in prefetched:
797 return prefetched[key] 797 return prefetched[key]
798 drevs = callconduit(repo, b'differential.query', params) 798 drevs = callconduit(repo.ui, b'differential.query', params)
799 # Fill prefetched with the result 799 # Fill prefetched with the result
800 for drev in drevs: 800 for drev in drevs:
801 prefetched[drev[b'phid']] = drev 801 prefetched[drev[b'phid']] = drev
802 prefetched[int(drev[b'id'])] = drev 802 prefetched[int(drev[b'id'])] = drev
803 if key not in prefetched: 803 if key not in prefetched:
951 write is usually ui.write. drevs is what "querydrev" returns, results of 951 write is usually ui.write. drevs is what "querydrev" returns, results of
952 "differential.query". 952 "differential.query".
953 """ 953 """
954 # Prefetch hg:meta property for all diffs 954 # Prefetch hg:meta property for all diffs
955 diffids = sorted(set(max(int(v) for v in drev[b'diffs']) for drev in drevs)) 955 diffids = sorted(set(max(int(v) for v in drev[b'diffs']) for drev in drevs))
956 diffs = callconduit(repo, b'differential.querydiffs', {b'ids': diffids}) 956 diffs = callconduit(repo.ui, b'differential.querydiffs', {b'ids': diffids})
957 957
958 # Generate patch for each drev 958 # Generate patch for each drev
959 for drev in drevs: 959 for drev in drevs:
960 repo.ui.note(_(b'reading D%s\n') % drev[b'id']) 960 repo.ui.note(_(b'reading D%s\n') % drev[b'id'])
961 961
962 diffid = max(int(v) for v in drev[b'diffs']) 962 diffid = max(int(v) for v in drev[b'diffs'])
963 body = callconduit(repo, b'differential.getrawdiff', 963 body = callconduit(repo.ui, b'differential.getrawdiff',
964 {b'diffID': diffid}) 964 {b'diffID': diffid})
965 desc = getdescfromdrev(drev) 965 desc = getdescfromdrev(drev)
966 header = b'# HG changeset patch\n' 966 header = b'# HG changeset patch\n'
967 967
968 # Try to preserve metadata from hg:meta property. Write hg patch 968 # Try to preserve metadata from hg:meta property. Write hg patch
1032 if i + 1 == len(drevs) and opts.get(b'comment'): 1032 if i + 1 == len(drevs) and opts.get(b'comment'):
1033 actions.append({b'type': b'comment', b'value': opts[b'comment']}) 1033 actions.append({b'type': b'comment', b'value': opts[b'comment']})
1034 if actions: 1034 if actions:
1035 params = {b'objectIdentifier': drev[b'phid'], 1035 params = {b'objectIdentifier': drev[b'phid'],
1036 b'transactions': actions} 1036 b'transactions': actions}
1037 callconduit(repo, b'differential.revision.edit', params) 1037 callconduit(ui, b'differential.revision.edit', params)
1038 1038
1039 templatekeyword = registrar.templatekeyword() 1039 templatekeyword = registrar.templatekeyword()
1040 1040
1041 @templatekeyword(b'phabreview', requires={b'ctx'}) 1041 @templatekeyword(b'phabreview', requires={b'ctx'})
1042 def template_review(context, mapping): 1042 def template_review(context, mapping):