2019-05-28 18:06:10 +02:00
|
|
|
#!/usr/bin/python3
|
2016-08-12 14:00:16 +02:00
|
|
|
|
|
|
|
import json
|
|
|
|
import time
|
|
|
|
import osc
|
|
|
|
import osc.core
|
|
|
|
import osc.conf
|
|
|
|
import xml.etree.ElementTree as ET
|
2019-04-10 14:03:27 +02:00
|
|
|
import cgi
|
2016-08-12 14:00:16 +02:00
|
|
|
import logging
|
|
|
|
import argparse
|
|
|
|
import sys
|
|
|
|
from collections import namedtuple
|
2019-08-29 16:06:55 +02:00
|
|
|
from osclib.util import mail_send_with_details
|
|
|
|
import email.utils
|
2016-08-12 14:00:16 +02:00
|
|
|
|
|
|
|
# for maintainer search
|
2019-11-27 11:08:06 +01:00
|
|
|
FACTORY = 'openSUSE:Factory'
|
2016-08-12 14:00:16 +02:00
|
|
|
|
|
|
|
class RemindedPackage(object):
|
2017-10-20 08:54:37 +02:00
|
|
|
def __init__(self, firstfail, reminded, remindCount, bug):
|
2019-11-27 11:08:06 +01:00
|
|
|
self.firstfail = firstfail
|
|
|
|
self.reminded = reminded
|
|
|
|
self.bug = bug
|
|
|
|
self.remindCount = remindCount
|
2017-10-17 09:10:17 +02:00
|
|
|
|
2016-08-12 14:00:16 +02:00
|
|
|
def __str__(self):
|
|
|
|
return '{} {} {} {}'.format(self.firstfail, self.reminded, self.bug, self.remindCount)
|
|
|
|
|
|
|
|
def jdefault(o):
|
2019-01-30 16:08:52 +01:00
|
|
|
return o.__dict__
|
2016-08-12 14:00:16 +02:00
|
|
|
|
2019-10-17 09:30:25 +02:00
|
|
|
MAIL_TEMPLATES = ( u"""Dear %(recipient)s,
|
2016-08-12 14:00:16 +02:00
|
|
|
|
|
|
|
Please be informed that '%(package)s' in %(project)s has
|
|
|
|
not had a successful build since %(date)s. See
|
|
|
|
https://build.opensuse.org/package/show/%(project)s/%(package)s
|
|
|
|
|
|
|
|
This can be due to an error in your package directly or could be
|
|
|
|
caused by a package you depend on to build. In any case, please do
|
|
|
|
your utmost to get the status back to building.
|
|
|
|
|
|
|
|
You will get another reminder in a week if the package still fails
|
|
|
|
by then.
|
|
|
|
|
|
|
|
*** NOTE:
|
|
|
|
This is an attempt to raise awareness of the maintainers about
|
|
|
|
broken builds in %(project)s. You receive this mail because you are
|
|
|
|
marked as maintainer for the above mentioned package (or project
|
|
|
|
maintainer if the package has no explicit maintainer assigned)
|
|
|
|
|
|
|
|
Kind regards,
|
|
|
|
%(sender)s
|
|
|
|
""",
|
2019-10-17 09:30:25 +02:00
|
|
|
u"""Dear %(recipient)s,
|
2016-08-12 14:00:16 +02:00
|
|
|
|
2018-01-31 11:26:34 +01:00
|
|
|
Following-up the reminder of one week ago, we have to inform you that
|
2016-08-12 14:00:16 +02:00
|
|
|
'%(package)s' is still failing in %(project)s. See
|
|
|
|
https://build.opensuse.org/package/show/%(project)s/%(package)s
|
|
|
|
|
|
|
|
It has been failing to build since %(date)s.
|
|
|
|
|
|
|
|
Please find the time to fix the build of this package. If needed,
|
|
|
|
also reach out to the broader community, trying to find somebody to
|
|
|
|
help you fix this package.
|
|
|
|
|
|
|
|
*** NOTE:
|
|
|
|
This is an attempt to raise awareness of the maintainers about
|
|
|
|
broken builds in Tumbleweed. You receive this mail because you are
|
|
|
|
marked as maintainer for the above mentioned package (or project
|
|
|
|
maintainer if the package has no explicit maintainer assigned)
|
|
|
|
|
2019-04-10 14:03:27 +02:00
|
|
|
Kind regards,
|
|
|
|
%(sender)s
|
2019-08-30 11:50:17 +02:00
|
|
|
""")
|
2019-04-10 14:03:27 +02:00
|
|
|
|
|
|
|
def SendMail(logger, project, sender, to, fullname, subject, text):
|
2019-08-29 16:06:55 +02:00
|
|
|
try:
|
|
|
|
xmailer = '{} - Failure Notification'.format(project)
|
2019-10-04 09:58:15 +02:00
|
|
|
to = email.utils.formataddr((fullname, to))
|
2019-08-29 16:06:55 +02:00
|
|
|
mail_send_with_details(sender=sender, to=to,
|
|
|
|
subject=subject, text=text, xmailer=xmailer,
|
|
|
|
relay=args.relay, dry=args.dry)
|
|
|
|
except Exception as e:
|
|
|
|
print(e)
|
|
|
|
logger.error("Failed to send an email to %s (%s)" % (fullname, to))
|
2019-04-10 14:03:27 +02:00
|
|
|
|
2016-08-12 14:00:16 +02:00
|
|
|
def main(args):
|
|
|
|
|
|
|
|
# do some work here
|
|
|
|
logger = logging.getLogger("build-fail-reminder")
|
|
|
|
logger.info("start")
|
|
|
|
|
|
|
|
osc.conf.get_config(override_apiurl=args.apiurl)
|
|
|
|
osc.conf.config['debug'] = args.osc_debug
|
|
|
|
apiurl = osc.conf.config['apiurl']
|
|
|
|
|
|
|
|
sender = args.sender
|
|
|
|
project = args.project
|
|
|
|
|
2019-11-27 11:08:06 +01:00
|
|
|
logger.debug('loading build fails for %s' % project)
|
2019-10-16 22:50:23 +08:00
|
|
|
url = osc.core.makeurl(apiurl, ['projects', project, 'status'],
|
2019-09-03 17:07:33 +02:00
|
|
|
{ 'ignore_pending': True,
|
|
|
|
'limit_to_fails': True,
|
|
|
|
'include_versions': False,
|
|
|
|
'format': 'json'
|
|
|
|
})
|
|
|
|
json_data = osc.core.http_GET(url)
|
2016-08-12 14:00:16 +02:00
|
|
|
data = json.load(json_data)
|
|
|
|
json_data.close()
|
|
|
|
|
2019-05-28 18:06:10 +02:00
|
|
|
reminded_json = args.json
|
|
|
|
if not reminded_json:
|
|
|
|
reminded_json = '{}.reminded.json'.format(project)
|
|
|
|
|
2016-08-12 14:00:16 +02:00
|
|
|
try:
|
2019-05-28 18:06:10 +02:00
|
|
|
with open(reminded_json) as json_data:
|
2016-08-12 14:00:16 +02:00
|
|
|
RemindedLoaded = json.load(json_data)
|
|
|
|
json_data.close()
|
|
|
|
except:
|
|
|
|
RemindedLoaded = {}
|
|
|
|
pass
|
|
|
|
|
|
|
|
seconds_to_remember = 7 * 86400
|
|
|
|
now = int(time.time())
|
|
|
|
|
|
|
|
Reminded = {}
|
|
|
|
Person = {}
|
2019-04-10 14:03:27 +02:00
|
|
|
ProjectComplainList = []
|
2016-08-12 14:00:16 +02:00
|
|
|
|
|
|
|
# Go through all the failed packages and update the reminder
|
|
|
|
for package in data:
|
|
|
|
# Only consider packages that failed for > seconds_to_remember days (7 days)
|
|
|
|
if package["firstfail"] < now - seconds_to_remember:
|
2017-10-17 09:10:17 +02:00
|
|
|
if not package["name"] in RemindedLoaded.keys():
|
2016-08-12 14:00:16 +02:00
|
|
|
# This is the first time we see this package failing for > 7 days
|
|
|
|
reminded = now
|
2019-11-27 11:08:06 +01:00
|
|
|
bug = ""
|
2016-08-12 14:00:16 +02:00
|
|
|
remindCount = 1
|
|
|
|
else:
|
|
|
|
if RemindedLoaded[package["name"]]["reminded"] < now - seconds_to_remember:
|
|
|
|
# We had seen this package in the last run - special treatment
|
|
|
|
reminded = now
|
2019-11-27 11:08:06 +01:00
|
|
|
bug = "boo#123"
|
2016-08-12 14:00:16 +02:00
|
|
|
remindCount = RemindedLoaded[package["name"]]["remindCount"] + 1
|
|
|
|
else:
|
|
|
|
reminded = RemindedLoaded[package["name"]]["reminded"]
|
|
|
|
remindCount = RemindedLoaded[package["name"]]["remindCount"]
|
|
|
|
bug = RemindedLoaded[package["name"]]["bug"]
|
|
|
|
Reminded[package["name"]] = RemindedPackage(package["firstfail"], reminded, remindCount, bug)
|
|
|
|
|
2016-08-12 15:47:15 +02:00
|
|
|
if not args.dry:
|
2019-05-28 18:06:10 +02:00
|
|
|
with open(reminded_json, 'w') as json_result:
|
2016-08-12 15:47:15 +02:00
|
|
|
json.dump(Reminded, json_result, default=jdefault)
|
2016-08-12 14:00:16 +02:00
|
|
|
|
|
|
|
for package in Reminded:
|
|
|
|
# Now we check on all the packages if we have to perform any reminder actions...
|
|
|
|
if Reminded[package].reminded == now:
|
|
|
|
# find the maintainers, try to not hammer the server too much
|
|
|
|
query = {
|
|
|
|
'binary': package,
|
|
|
|
'project': FACTORY,
|
|
|
|
}
|
|
|
|
url = osc.core.makeurl(apiurl, ('search', 'owner'), query=query)
|
|
|
|
root = ET.parse(osc.core.http_GET(url)).getroot()
|
|
|
|
maintainers = set([p.get('name') for p in root.findall('.//person') if p.get('role') in ('maintainer', 'bugowner')])
|
|
|
|
# TODO: expand groups if no persons found
|
|
|
|
for userid in maintainers:
|
|
|
|
if not userid in Person:
|
|
|
|
Person[userid] = osc.core.get_user_data(apiurl, userid, 'login', 'realname', 'email')
|
|
|
|
if Reminded[package].remindCount in (1, 2):
|
|
|
|
for userid in maintainers:
|
2019-10-04 09:58:15 +02:00
|
|
|
to = Person[userid][2]
|
|
|
|
fullname = Person[userid][1]
|
2019-04-10 14:03:27 +02:00
|
|
|
subject = '%s - %s - Build fail notification' % (project, package)
|
2019-11-27 11:08:06 +01:00
|
|
|
text = MAIL_TEMPLATES[Reminded[package].remindCount - 1] % {
|
2019-10-17 09:30:25 +02:00
|
|
|
'recipient': fullname,
|
2016-08-12 14:00:16 +02:00
|
|
|
'sender': sender,
|
2019-12-10 08:54:32 +01:00
|
|
|
'project': project,
|
|
|
|
'package': package,
|
2016-08-12 14:00:16 +02:00
|
|
|
'date': time.ctime(Reminded[package].firstfail),
|
|
|
|
}
|
2019-04-10 14:03:27 +02:00
|
|
|
SendMail(logger, project, sender, to, fullname, subject, text)
|
|
|
|
elif Reminded[package].remindCount == 4:
|
|
|
|
# Package has failed for 4 weeks - Collect packages to send a mail to openSUSE-factory@ (one mail per day max)
|
|
|
|
ProjectComplainList.append(package)
|
|
|
|
elif Reminded[package].remindCount == 6:
|
|
|
|
# Package failed to build for 6 weeks - file a delete request
|
|
|
|
r = osc.core.Request()
|
|
|
|
r.add_action('delete', tgt_project=project, tgt_package=package)
|
|
|
|
r.description = cgi.escape("[botdel] Package has failed to build for >= 6 weeks")
|
|
|
|
r.create(apiurl)
|
|
|
|
|
2019-08-30 11:50:17 +02:00
|
|
|
if len(ProjectComplainList):
|
2019-04-10 14:03:27 +02:00
|
|
|
# At least to report to the project for not building - send a mail to openSUSE-Factory
|
|
|
|
ProjectComplainList.sort()
|
|
|
|
to = 'openSUSE-Factory@opensuse.org'
|
|
|
|
fullname = "openSUSE Factory - Mailing List"
|
|
|
|
subject = "%(project)s - Build fail notification" % {'project': project}
|
2019-08-30 11:50:17 +02:00
|
|
|
|
|
|
|
text = u"""Dear Package maintainers and hackers.
|
|
|
|
|
2019-10-30 14:08:45 +01:00
|
|
|
Below package(s) in %(project)s have been failing to build for at
|
|
|
|
least 4 weeks. We tried to send out notifications to the
|
|
|
|
configured bugowner/maintainers of the package(s), but so far no
|
|
|
|
fix has been submitted. This probably means that the
|
|
|
|
maintainer/bugowner did not yet find the time to look into the
|
|
|
|
matter and he/she would certainly appreciate help to get this
|
|
|
|
sorted.
|
|
|
|
|
|
|
|
""" % { 'project': project }
|
2019-04-10 14:03:27 +02:00
|
|
|
for pkg in ProjectComplainList:
|
|
|
|
text += "- %s\n" % pkg
|
2019-08-30 11:50:17 +02:00
|
|
|
text += u"""
|
2019-10-30 14:08:45 +01:00
|
|
|
Unless somebody is stepping up and submitting fixes, the listed
|
|
|
|
package(s) are going to be removed from %(project)s.
|
2019-08-30 11:50:17 +02:00
|
|
|
|
2019-10-30 14:08:45 +01:00
|
|
|
Kind regards,
|
|
|
|
%(sender)s
|
|
|
|
""" % { 'project': project, 'sender': sender }
|
2019-04-10 14:03:27 +02:00
|
|
|
SendMail(logger, project, sender, to, fullname, subject, text)
|
2016-08-12 14:00:16 +02:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
parser = argparse.ArgumentParser(description='boilerplate python commmand line program')
|
|
|
|
parser.add_argument('-A', '--apiurl', metavar='URL', help='API URL')
|
|
|
|
parser.add_argument("--dry", action="store_true", help="dry run")
|
|
|
|
parser.add_argument("--debug", action="store_true", help="debug output")
|
|
|
|
parser.add_argument("--verbose", action="store_true", help="verbose")
|
|
|
|
parser.add_argument("--sender", metavar="SENDER", help="who the mail comes from", required=True)
|
|
|
|
parser.add_argument("--project", metavar="PROJECT", help="which project to check", default="openSUSE:Factory")
|
|
|
|
parser.add_argument("--relay", metavar="RELAY", help="relay server", required=True)
|
|
|
|
parser.add_argument("--osc-debug", action="store_true", help="osc debug output")
|
2019-05-28 18:06:10 +02:00
|
|
|
parser.add_argument("--json", metavar="JSON", help="filename to store reminds")
|
2016-08-12 14:00:16 +02:00
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
if args.debug:
|
2019-11-27 11:08:06 +01:00
|
|
|
level = logging.DEBUG
|
2016-08-12 14:00:16 +02:00
|
|
|
elif args.verbose:
|
|
|
|
level = logging.INFO
|
|
|
|
else:
|
|
|
|
level = None
|
|
|
|
|
|
|
|
logging.basicConfig(level = level)
|
|
|
|
|
|
|
|
sys.exit(main(args))
|