diff --git a/check_maintenance_incidents.py b/check_maintenance_incidents.py index febde034..9984e938 100755 --- a/check_maintenance_incidents.py +++ b/check_maintenance_incidents.py @@ -94,7 +94,37 @@ class MaintenanceChecker(ReviewBot.ReviewBot): # check if pkgname was submitted by the correct maintainer. If not, set # self.needs_maintainer_review - def _check_maintainer_review_needed(self, req, pkgname, author): + def _check_maintainer_review_needed(self, req, a): + author = req.get_creator() + if a.type == 'maintenance_incident': + # check if there is a link and use that or the real package + # name as src_packge may end with something like + # .openSUSE_XX.Y_Update + pkgname = a.src_package + (linkprj, linkpkg) = self._get_linktarget(a.src_project, pkgname) + if linkpkg is not None: + pkgname = linkpkg + if pkgname == 'patchinfo': + return None + + project = a.tgt_releaseproject + else: + pkgname = a.tgt_package + project = a.tgt_project + + if project.startswith('openSUSE:Leap:'): + mapping = MaintenanceChecker._get_lookup_yml(self.apiurl, project) + if mapping is None: + self.logger.error("error loading mapping for {}".format(project)) + elif not pkgname in mapping: + self.logger.debug("{} not tracked".format(pkgname)) + else: + origin = mapping[pkgname] + self.logger.debug("{} comes from {}, submitted from {}".format(pkgname, origin, a.src_project)) + if origin.startswith('SUSE:SLE-12') and a.src_project.startswith('SUSE:SLE-12'): + self.logger.info("{} submitted from {}, no maintainer review needed".format(pkgname, a.src_project)) + return + maintainers = set(self._maintainers(pkgname)) if maintainers: known_maintainer = False @@ -115,33 +145,8 @@ class MaintenanceChecker(ReviewBot.ReviewBot): self.needs_maintainer_review.add(pkgname) def check_action_maintenance_incident(self, req, a): - author = req.get_creator() - # check if there is a link and use that or the real package - # name as src_packge may end with something like - # .openSUSE_XX.Y_Update - pkgname = a.src_package - (linkprj, linkpkg) = self._get_linktarget(a.src_project, pkgname) - if linkpkg is not None: - pkgname = linkpkg - if pkgname == 'patchinfo': - return None - skip_maintainer_review = False - if a.tgt_releaseproject.startswith('openSUSE:Leap:'): - mapping = MaintenanceChecker._get_lookup_yml(self.apiurl, a.tgt_releaseproject) - if mapping is None: - self.logger.error("error loading mapping for {}".format(a.tgt_releaseproject)) - elif not pkgname in mapping: - self.logger.error("{} not tracked".format(pkgname)) - else: - origin = mapping[pkgname] - self.logger.debug("{} comes from {}, submitted from {}".format(pkgname, origin, a.src_project)) - if origin.startswith('SUSE:SLE-12') and origin == a.src_project: - skip_maintainer_review = True - self.logger.info("{} submitted from {}, no maintainer review needed".format(pkgname, a.src_project)) - - if not skip_maintainer_review: - self._check_maintainer_review_needed(req, pkgname, author) + self._check_maintainer_review_needed(req, a) if a.tgt_releaseproject.startswith("openSUSE:Backports:"): self.add_factory_source = True @@ -149,10 +154,8 @@ class MaintenanceChecker(ReviewBot.ReviewBot): return True def check_action_submit(self, req, a): - author = req.get_creator() - pkgname = a.tgt_package - self._check_maintainer_review_needed(req, pkgname, author) + self._check_maintainer_review_needed(req, a) return True diff --git a/check_source_in_factory.py b/check_source_in_factory.py index c40eddd7..a02ebb85 100755 --- a/check_source_in_factory.py +++ b/check_source_in_factory.py @@ -33,8 +33,10 @@ except ImportError: import osc.conf import osc.core import urllib2 +import yaml import ReviewBot + class FactorySourceChecker(ReviewBot.ReviewBot): """ this review bot checks if the sources of a submission are either in Factory or a request for Factory with the same sources @@ -50,6 +52,16 @@ class FactorySourceChecker(ReviewBot.ReviewBot): self.factory = "openSUSE:Factory" ReviewBot.ReviewBot.__init__(self, *args, **kwargs) self.review_messages = { 'accepted' : 'ok', 'declined': 'the package needs to be accepted in Factory first' } + self.lookup = None + + def parse_lookup(self, project): + self.lookup = yaml.safe_load(self._load_lookup_file(project)) + + def _load_lookup_file(self, prj): + if prj.endswith(':NonFree'): + prj = prj[:-len(':NonFree')] + return osc.core.http_GET(osc.core.makeurl(self.apiurl, + ['source', prj, '00Meta', 'lookup.yml'])) def check_source_submission(self, src_project, src_package, src_rev, target_project, target_package): self.logger.info("%s/%s@%s -> %s/%s"%(src_project, src_package, src_rev, target_project, target_package)) @@ -71,24 +83,35 @@ class FactorySourceChecker(ReviewBot.ReviewBot): return good + def _package_get_upstream_project(self, package): + """ return project where the specified pacakge is supposed to come + from. Either by lookup table or self.factory """ + if self.lookup and package in self.lookup: + return self.lookup[package] + + return self.factory + def _check_factory(self, rev, package): """check if factory sources contain the package and revision. check head and history""" - self.logger.debug("checking %s in %s"%(package, self.factory)) + project = self._package_get_upstream_project(package) + if project is None: + return False + self.logger.debug("checking %s in %s"%(package, project)) try: - si = osc.core.show_package_meta(self.apiurl, self.factory, package) + si = osc.core.show_package_meta(self.apiurl, project, package) except (urllib2.HTTPError, urllib2.URLError): si = None if si is None: self.logger.debug("new package") return None else: - si = self.get_sourceinfo(self.factory, package) + si = self.get_sourceinfo(project, package) if rev == si.verifymd5: self.logger.debug("srcmd5 matches") return True self.logger.debug("%s not the latest version, checking history", rev) - u = osc.core.makeurl(self.apiurl, [ 'source', self.factory, package, '_history' ], { 'limit': '5' }) + u = osc.core.makeurl(self.apiurl, [ 'source', project, package, '_history' ], { 'limit': '5' }) try: r = osc.core.http_GET(u) except urllib2.HTTPError, e: @@ -111,7 +134,11 @@ class FactorySourceChecker(ReviewBot.ReviewBot): def _check_requests(self, rev, package): self.logger.debug("checking requests") try: - requests = osc.core.get_request_list(self.apiurl, self.factory, package, None, ['new', 'review'], 'submit') + project = self._package_get_upstream_project(package) + if project is None: + self.logger.error("no upstream project found for {}, can't check requests".format(package)) + return None + requests = osc.core.get_request_list(self.apiurl, project, package, None, ['new', 'review'], 'submit') except (urllib2.HTTPError, urllib2.URLError): self.logger.debug("none request") return None @@ -125,7 +152,7 @@ class FactorySourceChecker(ReviewBot.ReviewBot): self.logger.debug("request ok") return True elif req.state.name == 'review': - self.logger.debug("request still in review") + self.logger.info("request still in review") return None else: self.logger.error("request in state %s not expected"%req.state.name) @@ -140,6 +167,7 @@ class CommandLineInterface(ReviewBot.CommandLineInterface): def get_optparser(self): parser = ReviewBot.CommandLineInterface.get_optparser(self) parser.add_option("--factory", metavar="project", help="the openSUSE Factory project") + parser.add_option("--lookup", metavar="project", help="use lookup file") return parser @@ -152,12 +180,17 @@ class CommandLineInterface(ReviewBot.CommandLineInterface): if user is None: user = osc.conf.get_apiurl_usr(apiurl) - return FactorySourceChecker(apiurl = apiurl, \ + bot = FactorySourceChecker(apiurl = apiurl, \ factory = self.options.factory, \ dryrun = self.options.dry, \ user = user, \ logger = self.logger) + if self.options.lookup: + bot.parse_lookup(self.options.lookup) + + return bot + if __name__ == "__main__": app = CommandLineInterface() sys.exit( app.main() ) diff --git a/leaper.py b/leaper.py new file mode 100755 index 00000000..4b92a17e --- /dev/null +++ b/leaper.py @@ -0,0 +1,109 @@ +#!/usr/bin/python +# Copyright (c) 2014 SUSE Linux Products GmbH +# Copyright (c) 2016 SUSE LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from pprint import pprint +import os, sys, re +import logging +from optparse import OptionParser +import cmdln + +try: + from xml.etree import cElementTree as ET +except ImportError: + import cElementTree as ET + +import osc.conf +import osc.core +import urllib2 +import yaml +import ReviewBot +from check_maintenance_incidents import MaintenanceChecker +from check_source_in_factory import FactorySourceChecker + +class Leaper(ReviewBot.ReviewBot): + + def __init__(self, *args, **kwargs): + ReviewBot.ReviewBot.__init__(self, *args, **kwargs) + self.maintbot = MaintenanceChecker(*args, **kwargs) + # for FactorySourceChecker + self.factory = FactorySourceChecker(*args, **kwargs) + self.factory.parse_lookup('openSUSE:Leap:42.2') + + def check_source_submission(self, src_project, src_package, src_rev, target_project, target_package): + return self.factory.check_source_submission(src_project, src_package, src_rev, target_project, target_package) + + def check_one_request(self, req): + self.review_messages = self.DEFAULT_REVIEW_MESSAGES.copy() + + has_upstream_sources = ReviewBot.ReviewBot.check_one_request(self, req) + has_correct_maintainer = self.maintbot.check_one_request(req) + + # not reviewed yet? + if has_upstream_sources is None: + return None + + self.logger.debug("upstream sources: {}, maintainer ok: {}".format(has_upstream_sources, has_correct_maintainer)) + + if has_upstream_sources != True or has_correct_maintainer != True: + if has_upstream_sources != True: + self.review_messages['accepted'] += '\nOrigin project changed' + # shouldn't happen actually + if has_correct_maintainer != True: + self.review_messages['accepted'] += '\nMaintainer check failed' + return False + + return True + +class CommandLineInterface(ReviewBot.CommandLineInterface): + + def __init__(self, *args, **kwargs): + ReviewBot.CommandLineInterface.__init__(self, args, kwargs) + + def get_optparser(self): + parser = ReviewBot.CommandLineInterface.get_optparser(self) + + return parser + + def setup_checker(self): + + apiurl = osc.conf.config['apiurl'] + if apiurl is None: + raise osc.oscerr.ConfigError("missing apiurl") + user = self.options.user + group = self.options.group + # if no args are given, use the current oscrc "owner" + if user is None and group is None: + user = osc.conf.get_apiurl_usr(apiurl) + + bot = Leaper(apiurl = apiurl, \ + dryrun = self.options.dry, \ + user = user, \ + group = group, \ + logger = self.logger) + + return bot + +if __name__ == "__main__": + app = CommandLineInterface() + sys.exit( app.main() ) + +# vim: sw=4 et