31:c3c5a64068a5 draft
Anton Shestakov <av6@dwimlabs.net>, Thu, 22 Nov 2018 15:30:13 +0800
plugin: snarf differential revisions and URLs

next change 32:6f02219bf75b
previous change 30:8db5c52046c7

plugin.py

Permissions: -rw-r--r--

Other formats: Feeds:
###
# Copyright (c) 2007, 2009 Brendan Cully
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
from supybot import utils, plugins, ircutils, callbacks
from supybot.commands import *
import os
import re
import subprocess
def revparse(irc, msg, args, state):
if ':' in args[0]:
state.errorInvalid('revision', args[0], 'Contains ":".')
state.args.append(args.pop(0))
addConverter('revision', revparse)
def phabrevparse(irc, msg, args, state):
if args[0].startswith('D'):
args[0] = args[0][1:]
callConverter('positiveInt', irc, msg, args, state)
addConverter('phabrev', phabrevparse)
class Hg(object):
def __init__(self, path):
self.path = path
def run(self, args):
with open(os.devnull) as null:
p = subprocess.Popen([self.path] + args, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stdin=null,
env={'HGPLAIN': '1', 'HGENCODING': 'UTF-8'})
out, err = p.communicate()
return (out.decode('utf-8'), err.decode('utf-8'))
class Mercurial(callbacks.PluginRegexp):
"""Goodies for #mercurial."""
threaded = True
callBefore = ['URL', 'Web']
regexps = ['snarfDifferential', 'snarfDifferentialURL']
def __init__(self, irc):
super(Mercurial, self).__init__(irc)
self.path = self.registryValue('path')
self.hg = Hg(self.path)
def hghelp(self, irc, msg, args, cmd):
"""<topic>
Runs hg help with the given args."""
def fmt(text):
lines = text.splitlines()
out = []
for line in lines[:3]:
line = line.strip()
if not line or line.startswith('aliases:'):
continue
if line.startswith(('options', 'enabled extensions:')):
break
out.append(line)
if out and ' extension - ' in out[0]:
out[0] = ircutils.bold(out[0])
elif len(out) > 1:
out[1] = ircutils.bold(out[1])
out = out[:10]
return out
out, err = self.hg.run(['help', cmd])
if err:
irc.reply(err.splitlines()[0])
else:
irc.replies(fmt(out))
hghelp = wrap(hghelp, ['text'])
def _changelog(self, irc, repo, rev):
tmpl = '{rev}:{node|short}\n{date|age}\n{author|person}\n{desc}'
out, err = self.hg.run([
'-R', repo, 'log', '-r', rev, '-l', '1', '--template', tmpl, '-v'
])
if err:
return irc.reply(err.splitlines()[0])
if not out:
return irc.reply('no changesets found')
lines = [line.strip() for line in out.splitlines()]
node, date, user = lines[:3]
lines = lines[3:]
node = ircutils.bold(node)
user = ircutils.mircColor(user, fg='green')
lines = ['%s %s %s' % (node, user, date)] + lines
baseurl, err = self.hg.run(['-R', repo, 'paths', 'default'])
url = "%s/rev/%s" % (baseurl.strip("\n/"), rev)
irc.replies(lines[:2] + [url])
def _changelogcmd(key):
def cmd(self, irc, msg, args, rev):
"""<revision>
Returns the changelog message and the URL for the given revision."""
self._changelog(irc, self.registryValue(key), rev)
return wrap(cmd, ['revision'])
main = _changelogcmd('repos.hg')
hgcommitted = _changelogcmd('repos.hg-committed')
hgall = _changelogcmd('repos.hg-all')
def glossary(self, irc, msg, args, term):
"""<term>
Returns glossary definition (from hg help glossary) for the term."""
raw, err = self.hg.run(['help', 'glossary'])
found = False
done = False
answer = []
for line in raw.splitlines():
if not found and line.lower().startswith(' ' + term.lower()):
found = True
elif found and not (line.startswith(' ') or not line):
done = True
line = line.strip()
if found and not done and line:
answer.append(line)
if answer:
irc.replies(answer[1:], joiner=' ')
else:
irc.reply('no match found')
glossary = wrap(glossary, ['text'])
def differential(self, irc, msg, args, phabrev, gotURL=False):
"""<revision id>
Returns URL, description and status of revisions on differential."""
baseurl = self.registryValue('phaburl')
url = '{}/D{}'.format(baseurl.rstrip('/'), phabrev)
page = utils.web.getUrl(url).decode('utf-8')
status = 'Status N/A'
desc = 'Description N/A'
match = re.search(r'<div class="phui-header-subheader">.*?</span>(?P<status>[^<]+?)</span>', page)
if match:
status = utils.web.htmlToText(match.group('status'))
match = re.search(r'<span class="phui-header-header">.*?</span>(?P<desc>[^<]+?)</span>', page)
if match:
desc = utils.web.htmlToText(match.group('desc'))
if gotURL:
url = 'D{}'.format(phabrev)
item = 'Revision {} {}, {}'.format(url, status, desc)
irc.reply(item)
differential = wrap(differential, ['phabrev'])
def snarfDifferential(self, irc, msg, match):
r'\bD(?P<phabrev>\d+)\b'
channel = msg.args[0]
if not irc.isChannel(channel):
return
phabrev = match.group('phabrev')
self.differential(self, irc, msg, msg.args, phabrev)
@urlSnarfer
def snarfDifferentialURL(self, irc, msg, match):
r'\b(?P<url>https?://\S+/)D(?P<phabrev>\d+)\b'
channel = msg.args[0]
if not irc.isChannel(channel):
return
url = match.group('url')
if url.rstrip('/') != self.registryValue('phaburl').rstrip('/'):
return
phabrev = match.group('phabrev')
self.differential(self, irc, msg, msg.args, phabrev, gotURL=True)
Class = Mercurial