ubuntu-bots/Bugtracker/plugin.py

626 lines
26 KiB
Python
Raw Normal View History

# -*- Encoding: utf-8 -*-
2006-06-26 17:57:20 +00:00
###
# Copyright (c) 2005-2007 Dennis Kaarsemaker
# Copyright (c) 2008-2010 Terence Simpson
# Copyright (c) 2017- Krytarik Raido
2006-06-26 17:57:20 +00:00
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
###
from supybot.commands import *
import supybot.utils as utils
2006-06-26 17:57:20 +00:00
import supybot.ircutils as ircutils
import supybot.ircdb as ircdb
2006-06-26 17:57:20 +00:00
import supybot.callbacks as callbacks
import supybot.conf as conf
import supybot.registry as registry
import supybot.log as supylog
2006-06-26 17:57:20 +00:00
import re, time
2006-06-26 17:57:20 +00:00
from .config import registerBugtracker, default_bugtrackers
from .trackers import defined_bugtrackers
from . import trackers
def defaultIgnored(hostmask):
if not conf.supybot.defaultIgnore():
return False
try:
user = ircdb.users.getUser(hostmask)
except KeyError:
return True
return False
def checkIgnored(hostmask, channel):
try:
user = ircdb.users.getUser(hostmask)
try:
if user._checkCapability('trusted'):
return False
except KeyError:
pass
if user.ignore:
return True
except KeyError:
pass
if ircdb.ignores.checkIgnored(hostmask):
return True
if channel:
c = ircdb.channels.getChannel(channel)
if c.checkIgnored(hostmask):
return True
return False
def checkAddressed(text, channel):
if channel:
if text[0] in str(conf.supybot.reply.whenAddressedBy.chars.get(channel)):
return True
elif text[0] in conf.supybot.reply.whenAddressedBy.chars():
return True
return False
2006-06-26 17:57:20 +00:00
class Bugtracker(callbacks.PluginRegexp):
"""Show a link to a bug report with a brief description"""
threaded = True
callBefore = ('URL')
2020-06-21 07:04:04 +00:00
regexps = ('bugSnarfer', 'bugUrlSnarfer', 'commitSnarfer', 'commitUrlSnarfer', 'cveSnarfer', 'oopsSnarfer')
2006-06-26 17:57:20 +00:00
def __init__(self, irc):
self.__parent = super(Bugtracker, self)
self.__parent.__init__(irc)
self.set_trackers()
self.shown = {}
def set_trackers(self):
2006-06-26 17:57:20 +00:00
self.db = ircutils.IrcDict()
self.aliases = {}
trackers = self.registryValue('bugtrackers')
if not trackers:
for (name, (url, description, trackertype, aliases)) in list(default_bugtrackers.items()):
registerBugtracker(name, url, description, trackertype, aliases)
for name in trackers:
tracker = self.registryValue('bugtrackers', value=False).get(name)
if tracker.trackertype() in defined_bugtrackers:
self.db[name] = defined_bugtrackers[tracker.trackertype()](name, tracker.url(),
tracker.description(), tracker.trackertype(), tracker.aliases())
for a in tracker.aliases():
self.aliases[a] = name
2006-06-26 17:57:20 +00:00
else:
supylog.warning("Bugtracker: Unknown trackertype '%s' (%s)" % (trackers[name].trackertype(), name))
self.shorthand = utils.abbrev(list(self.db.keys()))
def is_ok(self, channel, network, tracker, bugid):
"""Flood/repeat protection"""
now = time.time()
for k in list(self.shown.keys()):
if self.shown[k] < now - self.registryValue('repeatdelay', channel, network):
self.shown.pop(k)
if (network, channel, tracker, bugid) not in self.shown:
self.shown[(network, channel, tracker, bugid)] = now
return True
return False
2006-06-26 17:57:20 +00:00
def add(self, irc, msg, args, name, trackertype, url, description):
"""<name> <type> <url> [<description>]
Add a bugtracker to the list of defined bugtrackers. Currently supported types are
2020-06-22 05:04:04 +00:00
Launchpad, Debbugs, Bugzilla, SourceForge, GitHub, GitLab, Gitea, CGit, Mantis, and Trac.
<name> will be used to reference the bugtracker in all commands.
Unambiguous abbreviations of it will also be accepted.
<description> will be used to reference the bugtracker in the
query result. If not given, it defaults to <name>.
2006-06-26 17:57:20 +00:00
"""
name = name.lower()
if not description:
description = name
if url[-1] == '/':
url = url[:-1]
trackertype = trackertype.lower()
if trackertype in defined_bugtrackers:
self.db[name] = defined_bugtrackers[trackertype](name, url, description, trackertype)
2006-06-26 17:57:20 +00:00
else:
irc.error("Bugtrackers of type '%s' are not understood" % trackertype)
return
registerBugtracker(name, url, description, trackertype)
self.shorthand = utils.abbrev(list(self.db.keys()))
2006-06-26 17:57:20 +00:00
irc.replySuccess()
add = wrap(add, [('checkCapability', 'admin'), 'something', 'something', 'url', additional('text')])
2006-06-26 17:57:20 +00:00
def remove(self, irc, msg, args, name):
"""<abbreviation>
Remove the bugtracker associated with <abbreviation> from the list of
defined bugtrackers.
"""
try:
name = self.shorthand[name.lower()]
for a in self.db[name].aliases:
del self.aliases[a]
2006-06-26 17:57:20 +00:00
del self.db[name]
trackers = self.registryValue('bugtrackers')
if name in trackers:
trackers.remove(name)
self.registryValue('bugtrackers', value=False).unregister(name)
self.shorthand = utils.abbrev(list(self.db.keys()))
2006-06-26 17:57:20 +00:00
irc.replySuccess()
except KeyError:
s = self.registryValue('replyNoBugtracker', msg.channel, irc.network)
2006-06-26 17:57:20 +00:00
irc.error(s % name)
remove = wrap(remove, [('checkCapability', 'admin'), 'text'])
2006-06-26 17:57:20 +00:00
def rename(self, irc, msg, args, oldname, newname, newdesc):
"""<oldname> <newname> [<newdescription>]
Rename the bugtracker associated with <oldname> to <newname>,
optionally with <newdescription>.
"""
try:
oldname = self.shorthand[oldname.lower()]
newname = newname.lower()
tracker = self.db[oldname]
d = tracker.description
if newdesc:
d = newdesc
self.db[newname] = defined_bugtrackers[tracker.trackertype](newname, tracker.url, d, tracker.aliases)
registerBugtracker(newname, tracker.url, d, tracker.trackertype, tracker.aliases)
del self.db[oldname]
trackers = self.registryValue('bugtrackers')
if oldname in trackers:
trackers.remove(oldname)
self.registryValue('bugtrackers', value=False).unregister(oldname)
self.shorthand = utils.abbrev(list(self.db.keys()))
for a in tracker.aliases:
self.aliases[a] = newname
irc.replySuccess()
except KeyError:
s = self.registryValue('replyNoBugtracker', msg.channel, irc.network)
irc.error(s % oldname)
rename = wrap(rename, [('checkCapability', 'admin'), 'something', 'something', additional('text')])
def alias(self, irc, msg, args, name, alias):
"""<name> [<alias>]
Add an alias to a defined bugtracker, or list any set ones.
"""
try:
name = self.shorthand[name.lower()]
if not alias:
a = self.db[name].aliases
irc.reply('%s: %s' % (name, utils.str.commaAndify(sorted(a)) if a else 'No aliases set'))
return
alias = alias.lower()
self.db[name].aliases.add(alias)
trackers = self.registryValue('bugtrackers')
if name in trackers:
self.registryValue('bugtrackers', value=False).get(name).aliases().add(alias)
else:
tracker = self.db[name]
registerBugtracker(tracker.name, tracker.url, tracker.description, tracker.trackertype, tracker.aliases)
self.aliases[alias] = name
irc.replySuccess()
except KeyError:
s = self.registryValue('replyNoBugtracker', msg.channel, irc.network)
irc.error(s % name)
alias = wrap(alias, [('checkCapability', 'admin'), 'something', additional('text')])
def unalias(self, irc, msg, args, name, alias):
"""<name> <alias>
Remove an alias from a defined bugtracker.
"""
try:
name = self.shorthand[name.lower()]
alias = alias.lower()
try:
self.db[name].aliases.remove(alias)
trackers = self.registryValue('bugtrackers')
if name in trackers:
self.registryValue('bugtrackers', value=False).get(name).aliases().remove(alias)
del self.aliases[alias]
irc.replySuccess()
except ValueError:
irc.error("Bugtracker '%s' has no alias '%s' set" % (name, alias))
return
except KeyError:
s = self.registryValue('replyNoBugtracker', msg.channel, irc.network)
irc.error(s % name)
unalias = wrap(unalias, [('checkCapability', 'admin'), 'something', 'something'])
2006-06-26 17:57:20 +00:00
def list(self, irc, msg, args, name):
"""[<abbreviation>]
2006-06-26 17:57:20 +00:00
List defined bugtrackers. If <abbreviation> is specified, list the
2006-06-26 17:57:20 +00:00
information for that bugtracker.
"""
if name:
try:
name = self.shorthand[name.lower()]
tracker = self.db[name]
irc.reply('%s%s: %s, %s [%s]' % (name, ' (%s)' % ', '.join(sorted(tracker.aliases)) if tracker.aliases else '',
tracker.description, tracker.url, tracker.__class__.__name__))
2006-06-26 17:57:20 +00:00
except KeyError:
s = self.registryValue('replyNoBugtracker', msg.channel, irc.network)
2006-06-26 17:57:20 +00:00
irc.error(s % name)
else:
if self.db:
irc.reply(utils.str.commaAndify(sorted(list(self.db.keys()))))
2006-06-26 17:57:20 +00:00
else:
irc.reply('I have no defined bugtrackers.')
list = wrap(list, [additional('text')])
def reset(self, irc, msg, args, name):
"""[<abbreviation>]
Reset defined bugtrackers to defaults. If <abbreviation> is specified,
reset only that bugtracker.
"""
if name:
try:
name = self.shorthand[name.lower()]
for a in self.db[name].aliases:
del self.aliases[a]
del self.db[name]
trackers = self.registryValue('bugtrackers')
if name in trackers:
trackers.remove(name)
self.registryValue('bugtrackers', value=False).unregister(name)
if name in default_bugtrackers:
(url, description, trackertype, aliases) = default_bugtrackers[name]
if trackertype in defined_bugtrackers:
self.db[name] = defined_bugtrackers[trackertype](name, url, description, trackertype, aliases)
for a in aliases:
self.aliases[a] = name
registerBugtracker(name, url, description, trackertype, aliases)
else:
supylog.warning("Bugtracker: Unknown trackertype '%s' (%s)" % (trackers[name].trackertype(), name))
self.shorthand = utils.abbrev(list(self.db.keys()))
except KeyError:
s = self.registryValue('replyNoBugtracker', msg.channel, irc.network)
irc.error(s % name)
return
else:
trackers = self.registryValue('bugtrackers')
for name in list(trackers)[:]:
trackers.remove(name)
self.registryValue('bugtrackers', value=False).unregister(name)
self.set_trackers()
irc.replySuccess()
reset = wrap(reset, [('checkCapability', 'admin'), additional('text')])
def inFilter(self, irc, msg):
if not (msg.prefix and msg.args):
return msg
if not ircutils.isUserHostmask(msg.prefix):
return msg
if not defaultIgnored(msg.prefix):
return msg
if checkIgnored(msg.prefix, msg.channel):
return msg
if msg.command == 'PRIVMSG':
self.doPrivmsg(irc, msg)
return msg
2006-06-26 17:57:20 +00:00
def bugSnarfer(self, irc, msg, match):
r"(?P<bt>[a-z][^\s:]*(\s+(bug|ticket|issue|pull|pr|merge|mr)('?s)?)?)(?P<prefix>:+\s*[#!]?|\s+[#!]?|\s*[#!])(?P<bug>\d+(?!\d*[-.]\d+)(\s*([,\s]+|[,\s]*(and|und|en|et|ir|[&+]+))\s*#?\d+(?!\d*[-.]\d+))*)"
2020-06-21 07:04:04 +00:00
self.termSnarfer(irc, msg, match, 'bug')
def commitSnarfer(self, irc, msg, match):
r"(?P<bt>[a-z][^\s:]*(\s+(commit)('?s)?)?)(?P<prefix>:+\s*#?|\s+#?|\s*#)(?P<bug>[a-f0-9]{7,}(?![a-f0-9]*[-.][a-f0-9]{7,})(\s*([,\s]+|[,\s]*(and|und|en|et|ir|[&+]+))\s*#?[a-f0-9]{7,}(?![a-f0-9]*[-.][a-f0-9]{7,}))*)"
2020-06-21 07:04:04 +00:00
self.termSnarfer(irc, msg, match, 'commit')
def termSnarfer(self, irc, msg, match, termtype):
channel = msg.channel
if checkAddressed(msg.args[1].strip(), channel):
return
if not self.registryValue('{}Snarfer'.format(termtype), channel, irc.network):
2006-06-26 17:57:20 +00:00
return
nbugs = msg.tagged('nbugs') or 0
2007-02-04 22:10:10 +00:00
if nbugs >= 5:
return
2020-06-21 07:04:04 +00:00
if termtype != 'commit':
bugids = re.findall(r'[0-9]+', match.group('bug'))[:5-nbugs]
else:
bugids = re.findall(r'[a-f0-9]{7,}', match.group('bug'))[:5-nbugs]
# Begin HACK
# Strings like "Ubuntu 1004" and "Ubuntu 1610" are false triggers for us
2020-06-21 07:04:04 +00:00
if match.group('bt').lower() == 'ubuntu' and termtype == 'bug':
bugids = [x for x in bugids if not re.match(r'^([4-9]|[12][0-9])(04|10)$', x)]
# End HACK
# Get tracker name
bt = [x.lower() for x in match.group('bt').split()]
2020-06-21 07:04:04 +00:00
if termtype != 'commit':
sure_bug = re.match(r"^(?P<type>bug|ticket|issue|pull|pr|merge|mr)('?s)?$", bt[-1])
else:
sure_bug = re.match(r"^(?P<type>commit)('?s)?$", bt[-1])
# Get bug type
if sure_bug:
bugtype = sure_bug.group('type')
elif '#' in match.group('prefix'):
bugtype = 'bug'
elif '!' in match.group('prefix'):
bugtype = 'merge'
else:
bugtype = ''
bugids = list(set(bugids)) # remove dupes
if termtype == 'bug' and not bugtype:
bugids = [x for x in bugids if int(x) >= 100]
2007-02-04 22:10:10 +00:00
msg.tag('nbugs', nbugs + len(bugids))
2006-06-26 17:57:20 +00:00
name = ''
showTracker = True
if len(bt) == 1 and not sure_bug:
2006-06-26 17:57:20 +00:00
try:
name = bt[0]
if name in list(self.aliases.keys()):
name = self.aliases[name]
2006-06-26 17:57:20 +00:00
tracker = self.db[name]
except:
return
elif len(bt) == 2:
try:
name = bt[0]
if name in list(self.aliases.keys()):
name = self.aliases[name]
2006-06-26 17:57:20 +00:00
tracker = self.db[name]
except:
name = ''
2006-06-26 17:57:20 +00:00
if not name:
showTracker = False
snarfTarget = self.registryValue('snarfTarget', channel, irc.network)
2006-06-26 17:57:20 +00:00
if not snarfTarget:
supylog.warning("Bugtracker: No snarfTarget set")
2006-06-26 17:57:20 +00:00
return
try:
name = self.shorthand[snarfTarget.lower()]
tracker = self.db[name]
2006-06-26 17:57:20 +00:00
except:
s = self.registryValue('replyNoBugtracker', channel, irc.network)
2019-07-11 22:56:04 +00:00
irc.error(s % (name or snarfTarget))
return
if bugtype in ('issue', 'pull', 'pr', 'merge', 'mr') and \
tracker.trackertype not in ('github', 'gitlab', 'gitea', 'sourceforge'):
return
for bugid in bugids:
try:
report = self.get_bug(channel or msg.nick, irc.network, tracker, bugtype, bugid,
self.registryValue('showassignee', channel, irc.network),
self.registryValue('extended', channel, irc.network),
do_tracker=showTracker)
except trackers.BugNotFoundError:
if self.registryValue('replyWhenNotFound'):
2020-06-21 07:04:04 +00:00
irc.error("Could not find %s %s %s" % (tracker.description, termtype, bugid))
except trackers.BugtrackerError as e:
if self.registryValue('replyWhenError') and sure_bug:
irc.error(str(e))
else:
if report:
if not self.registryValue('useNotices', channel, irc.network):
irc.reply(report)
else:
irc.reply(report, notice=True)
2006-06-26 17:57:20 +00:00
2020-06-21 07:04:04 +00:00
def bugUrlSnarfer(self, irc, msg, match):
r"(https?://)?((bugs\.debian\.org|pad\.lv)/|\S+/(show_bug\.cgi\?id=|bugreport\.cgi\?bug=|view\.php\?id=|bug=|bugs/|\+bug/|tickets?/|feature-requests/|patches/|todo/|issues/|pulls?/|merge_requests/))(?P<bug>\d+)"
2020-06-21 07:04:04 +00:00
self.urlSnarfer(irc, msg, match, 'bug')
def commitUrlSnarfer(self, irc, msg, match):
2020-06-22 05:04:04 +00:00
r"(https?://)?\S+/commits?/([^\s?]*\?([^\s?&]+&)?id=)?(?P<bug>[a-f0-9]{7,})"
2020-06-21 07:04:04 +00:00
self.urlSnarfer(irc, msg, match, 'commit')
def urlSnarfer(self, irc, msg, match, urltype):
channel = msg.channel
if checkAddressed(msg.args[1].strip(), channel):
return
if not self.registryValue('{}Snarfer'.format(urltype), channel, irc.network):
2006-06-26 17:57:20 +00:00
return
nbugs = msg.tagged('nbugs') or 0
2007-02-04 22:10:10 +00:00
if nbugs >= 5:
return
msg.tag('nbugs', nbugs+1)
url = match.group(0)
2020-06-21 07:04:04 +00:00
bugid = match.group('bug')
if '://' in url:
url = url[url.rfind('://')+3:]
2006-06-26 17:57:20 +00:00
try:
tracker = self.get_tracker(url, bugid)
2006-06-26 17:57:20 +00:00
if not tracker:
return
report = self.get_bug(channel or msg.nick, irc.network, tracker, 'url', bugid,
self.registryValue('showassignee', channel, irc.network),
self.registryValue('extended', channel, irc.network),
do_url=False)
except trackers.BugNotFoundError:
if self.registryValue('replyWhenNotFound'):
2020-06-21 07:04:04 +00:00
irc.error("Could not find %s %s %s" % (tracker.description, urltype, match.group('bug')))
except trackers.BugtrackerError as e:
if self.registryValue('replyWhenError'):
irc.error(str(e))
2006-06-26 17:57:20 +00:00
else:
if report:
if not self.registryValue('useNotices', channel, irc.network):
irc.reply(report)
else:
irc.reply(report, notice=True)
2006-06-26 17:57:20 +00:00
# Only useful to Launchpad developers
2006-06-26 17:57:20 +00:00
def oopsSnarfer(self, irc, msg, match):
2020-06-21 07:04:04 +00:00
r"(https?://\S+[=/])?OOPS-(?P<oopsid>[a-f0-9]{6,})"
channel = msg.channel
if checkAddressed(msg.args[1].strip(), channel):
return
if not (self.registryValue('bugSnarfer', channel, irc.network) \
and self.registryValue('oopsSnarfer', channel, irc.network)):
return
oopsid = match.group('oopsid')
if not self.is_ok(channel or msg.nick, irc.network, 'lpoops', oopsid):
return
if not match.group(1):
report = 'https://oops.canonical.com/?oopsid=OOPS-%s' % oopsid
if not self.registryValue('useNotices', channel, irc.network):
irc.reply(report)
else:
irc.reply(report, notice=True)
2006-06-26 17:57:20 +00:00
def cveSnarfer(self, irc, msg, match):
r"(https?://\S+=)?CVE[- ](?P<cveid>\d{4}[- ]\d{4,})"
channel = msg.channel
if checkAddressed(msg.args[1].strip(), channel):
return
if not (self.registryValue('bugSnarfer', channel, irc.network) \
and self.registryValue('cveSnarfer', channel, irc.network)):
return
cveid = match.group('cveid').replace(' ','-')
if not self.is_ok(channel or msg.nick, irc.network, 'cve', cveid):
return
if not match.group(1):
do_url = True
else:
do_url = False
try:
report = trackers.CVE().get_bug(channel or msg.nick, cveid, do_url)
except trackers.BugNotFoundError:
if self.registryValue('replyWhenNotFound'):
irc.error("Could not find CVE %s" % cveid)
except trackers.BugtrackerError as e:
if self.registryValue('replyWhenError'):
irc.error(str(e))
else:
if report:
if not self.registryValue('useNotices', channel, irc.network):
irc.reply(report)
else:
irc.reply(report, notice=True)
#TODO: As we will depend on launchpadlib, we should consider using lazr.uri.URI to do URL parsing
def get_tracker(self, snarfurl, bugid):
# SourceForge short domain
snarfurl = snarfurl.replace('sf.net', 'sourceforge.net', 1)
# Launchpad URL shortening
snarfurl = re.sub(r'pad\.lv/(bug=)?(?P<bug>[0-9]+)', r'launchpad.net/bugs/\g<bug>', snarfurl)
for t in list(self.db.keys()):
tracker = self.db[t]
url = tracker.url[tracker.url.rfind('://')+3:]
2020-06-22 05:04:04 +00:00
if url in snarfurl and not (url == 'launchpad.net' \
and snarfurl.startswith('git.launchpad.net')):
2006-06-26 17:57:20 +00:00
return tracker
# No tracker found, bummer. Let's try and get one
2006-06-26 17:57:20 +00:00
if 'show_bug.cgi' in snarfurl:
tracker = trackers.Bugzilla().get_tracker(snarfurl)
elif 'sourceforge.net' in snarfurl:
tracker = trackers.SourceForge().get_tracker(snarfurl)
elif 'github.com' in snarfurl:
tracker = trackers.GitHub().get_tracker(snarfurl)
2020-06-22 05:04:04 +00:00
elif re.match(r'\S+/commit/[^\s?]*\?([^\s?&]+&)?id=', snarfurl):
tracker = trackers.CGit().get_tracker(snarfurl, bugid)
2020-06-21 07:04:04 +00:00
elif re.match(r'[^\s/]+/[^\s/]+(/[^\s/]+)+/-/(issues|merge_requests|commits?)', snarfurl) \
or re.match(r'[^\s/]+/[^\s/]+(/[^\s/]+)+/merge_requests', snarfurl) \
2020-06-21 07:04:04 +00:00
or re.match(r'[^\s/]+/[^\s/]+(/[^\s/]+){2,}/(issues|commits?)', snarfurl):
tracker = trackers.GitLab().get_tracker(snarfurl, bugid)
2020-06-21 07:04:04 +00:00
elif re.match(r'[^\s/]+/[^\s/]+/[^\s/]+/(issues|commits?)', snarfurl):
tracker = trackers.GitLab().get_tracker(snarfurl, bugid)
if not tracker:
tracker = trackers.Gitea().get_tracker(snarfurl, bugid)
2020-03-30 03:17:04 +00:00
elif re.match(r'[^\s/]+/[^\s/]+/[^\s/]+/pulls', snarfurl):
tracker = trackers.Gitea().get_tracker(snarfurl, bugid)
elif 'view.php' in snarfurl:
tracker = trackers.Mantis().get_tracker(snarfurl)
elif '/ticket/' in snarfurl:
tracker = trackers.Trac().get_tracker(snarfurl)
else:
return
if tracker:
self.db[tracker.name] = tracker
if self.registryValue('saveDiscoveredTrackers'):
registerBugtracker(tracker.name, tracker.url, tracker.description, tracker.trackertype, tracker.aliases)
self.shorthand = utils.abbrev(list(self.db.keys()))
return tracker
2006-06-26 17:57:20 +00:00
def get_bug(self, channel, network, tracker, bugtype, bugid, do_assignee, do_extinfo, do_url=True, do_tracker=True):
if not self.is_ok(channel, network, tracker, bugid):
return
bugdata = tracker.get_bug(bugtype, bugid)
if not bugdata:
return
(bugid, product, title, severity, status, assignee, url, extinfo, duplicate) = bugdata
if duplicate and not self.is_ok(channel, network, tracker, bugid):
return
bugtype = re.match(r'\S+/(feature-)?(?P<type>request|patch|todo|issue|pull|merge|ticket|commit)(_requests)?(e?s)?/(\?id=)?[a-f0-9]+/?$', url)
2020-06-22 05:04:04 +00:00
if do_tracker and tracker.trackertype not in ('github', 'gitlab', 'gitea', 'cgit'):
if re.match(r'\S+/(bugs|feature-requests|patches|todo|issues|pulls?|merge_requests|tickets?|commits?)$', tracker.description):
2020-06-21 07:04:04 +00:00
report = '%s %s' % (tracker.description, bugid)
else:
if bugtype:
2020-06-21 07:04:04 +00:00
report = '%s %s %s' % (tracker.description, bugtype.group('type'), bugid)
else:
2020-06-21 07:04:04 +00:00
report = '%s bug %s' % (tracker.description, bugid)
else:
if bugtype:
2020-06-21 07:04:04 +00:00
report = '%s %s' % (bugtype.group('type').title(), bugid)
else:
2020-06-21 07:04:04 +00:00
report = 'Bug %s' % bugid
if product:
report += ' in %s' % product
report += ' "%s"' % title.replace('"', "'").strip()
if do_extinfo and extinfo:
report += ' (%s)' % ', '.join(extinfo)
if do_assignee and assignee:
report += ' (assigned: %s)' % assignee
severity_status = []
if severity:
severity_status.append(' '.join(word[0].upper() + word[1:].lower() for word in severity.split()))
2020-06-21 07:04:04 +00:00
if status:
severity_status.append(' '.join(word[0].upper() + word[1:].lower() for word in status.split()))
if severity_status:
report += ' [%s]' % ', '.join(severity_status)
if duplicate:
report += ' [duplicate: %s]' % duplicate[0]
if do_url:
report += ' %s' % url
message_max = 450 - len(channel)
if len(report) > message_max:
report_parts = report.split('"')
report_start = report_parts[0]
report_end = report_parts[-1]
report_title = '"'.join(report_parts[1:-1])
title_max = message_max - len(report_start) - len(report_end) - 5
report_title_cut = report_title[:title_max].rsplit(None, 1)[0] + '...'
report = '%s"%s"%s' % (report_start, report_title_cut, report_end)
return report
2006-06-26 17:57:20 +00:00
Class = Bugtracker