示例#1
0
class Leaper(ReviewBot.ReviewBot):

    def __init__(self, *args, **kwargs):
        ReviewBot.ReviewBot.__init__(self, *args, **kwargs)

        self.do_comments = True

        self.maintbot = MaintenanceChecker(*args, **kwargs)
        # for FactorySourceChecker
        self.factory = FactorySourceChecker(*args, **kwargs)

        self.needs_reviewteam = False
        self.pending_factory_submission = False
        self.source_in_factory = None
        self.needs_release_manager = False
        self.release_manager_group = 'leap-reviewers'
        self.must_approve_version_updates = False
        self.must_approve_maintenance_updates = False
        self.needs_check_source = False
        self.check_source_group = None
        self.automatic_submission = False

        # project => package list
        self.packages = {}

    def prepare_review(self):
        # update lookup information on every run

        if self.ibs:
            self.factory.parse_lookup('SUSE:SLE-12-SP3:GA')
            self.lookup_sp3 = self.factory.lookup.copy()
            return

        self.factory.parse_lookup('openSUSE:Leap:42.3')
        self.factory.parse_lookup('openSUSE:Leap:42.3:NonFree')
        self.lookup_423 = self.factory.lookup.copy()
        self.factory.reset_lookup()
        self.factory.parse_lookup('openSUSE:Leap:42.2:Update')
        self.factory.parse_lookup('openSUSE:Leap:42.2:NonFree:Update')
        self.lookup_422 = self.factory.lookup.copy()
        self.factory.reset_lookup()
        self.factory.parse_lookup('openSUSE:Leap:42.1:Update')
        self.lookup_421 = self.factory.lookup.copy()
        self.factory.reset_lookup()

    def get_source_packages(self, project, expand=False):
        """Return the list of packages in a project."""
        query = {'expand': 1} if expand else {}
        root = ET.parse(osc.core.http_GET(osc.core.makeurl(self.apiurl,['source', project],
                                 query=query))).getroot()
        packages = [i.get('name') for i in root.findall('entry')]

        return packages

    def is_package_in_project(self, project, package):
        if not project in self.packages:
            self.packages[project] = self.get_source_packages(project)
        return True if package in self.packages[project] else False

    def rdiff_link(self, src_project, src_package, src_rev, target_project, target_package = None):
        if target_package is None:
            target_package = src_package

        return '[%(target_project)s/%(target_package)s](/package/rdiff/%(src_project)s/%(src_package)s?opackage=%(target_package)s&oproject=%(target_project)s&rev=%(src_rev)s)'%{
                'src_project': src_project,
                'src_package': src_package,
                'src_rev': src_rev,
                'target_project': target_project,
                'target_package': target_package,
                }

    def check_source_submission(self, src_project, src_package, src_rev, target_project, target_package):
        super(Leaper, self).check_source_submission(src_project, src_package, src_rev, target_project, target_package)
        src_srcinfo = self.get_sourceinfo(src_project, src_package, src_rev)
        package = target_package

        origin = None

        if src_srcinfo is None:
            # source package does not exist?
            # handle here to avoid crashing on the next line
            self.logger.warn("Could not get source info for %s/%s@%s" % (src_project, src_package, src_rev))
            return False

        if self.ibs and target_project.startswith('SUSE:SLE'):
            if package in self.lookup_sp3:
                origin = self.lookup_sp3[package]

            origin_same = True
            if origin:
                origin_same = True if origin == 'FORK' else src_project.startswith(origin)
                self.logger.info("expected origin is '%s' (%s)", origin,
                                 "unchanged" if origin_same else "changed")

            prj = 'openSUSE.org:openSUSE:Factory'
            # True or None (open request) are acceptable for SLE.
            self.source_in_factory = self._check_factory(package, src_srcinfo, prj)
            if self.source_in_factory is None:
                self.pending_factory_submission = True
            if self.source_in_factory is not False:
                return self.source_in_factory

            # got false. could mean package doesn't exist or no match
            if self.is_package_in_project(prj, package):
                self.logger.info('different sources in {}'.format(self.rdiff_link(src_project, src_package, src_rev, prj, package)))

            prj = 'openSUSE.org:openSUSE:Leap:42.2'
            if self.is_package_in_project(prj, package):
                if self._check_factory(package, src_srcinfo, prj) is True:
                    self.logger.info('found source match in {}'.format(prj))
                else:
                    self.logger.info('different sources in {}'.format(self.rdiff_link(src_project, src_package, src_rev, prj, package)))

            devel_project, devel_package = self.get_devel_project('openSUSE.org:openSUSE:Factory', package)
            if devel_project is not None:
                # specifying devel package is optional
                if devel_package is None:
                    devel_package = package
                if self.is_package_in_project(devel_project, devel_package):
                    if self.factory._check_project(devel_project, devel_package, src_srcinfo.verifymd5) == True:
                        self.logger.info('matching sources in {}/{}'.format(devel_project, devel_package))
                        return True
                    else:
                        self.logger.info('different sources in {}'.format(self.rdiff_link(src_project, src_package, src_rev, devel_project, devel_package)))
            else:
                self.logger.info('no devel project found for {}/{}'.format('openSUSE.org:openSUSE:Factory', package))

            self.logger.info('no matching sources in Factory, Leap:42.2, nor devel project')

            return origin_same

        if package in self.lookup_423:
            origin = self.lookup_423[package]

        is_fine_if_factory = False
        not_in_factory_okish = False
        if origin:
            origin_same = src_project.startswith(origin)
            self.logger.info("expected origin is '%s' (%s)", origin,
                             "unchanged" if origin_same else "changed")
            if origin.startswith('Devel;'):
                (dummy, origin, dummy) = origin.split(';')
                if origin != src_project:
                    self.logger.debug("not submitted from devel project")
                    return False
                is_fine_if_factory = True
                not_in_factory_okish = True
                if self.must_approve_version_updates:
                    self.needs_release_manager = True
                # fall through to check history and requests
            elif origin.startswith('openSUSE:Factory'):
                # A large number of requests are created by hand that leaper
                # would have created via update_crawler.py. This applies to
                # other origins, but primary looking to let Factory submitters
                # know that there is no need to make manual submissions to both.
                # Since it has a lookup entry it is not a new package.
                self.automatic_submission = True
                if self.must_approve_version_updates:
                    self.needs_release_manager = True
                if origin == src_project:
                    self.source_in_factory = True
                    return True
                is_fine_if_factory = True
                # fall through to check history and requests
            elif origin == 'FORK':
                is_fine_if_factory = True
                not_in_factory_okish = True
                self.needs_release_manager = True
                self.needs_check_source = True
                # fall through to check history and requests
            elif origin.startswith('openSUSE:Leap:42.2'):
                if self.must_approve_maintenance_updates:
                    self.needs_release_manager = True
                # submitted from :Update
                if origin_same:
                    self.logger.debug("submission from 42.2 ok")
                    return True
                # switching to sle package might make sense
                if src_project.startswith('SUSE:SLE-12'):
                    self.needs_release_manager = True
                    return True
                # submitted from elsewhere but is in :Update
                else:
                    good = self.factory._check_project('openSUSE:Leap:42.2:Update', target_package, src_srcinfo.verifymd5)
                    if good:
                        self.logger.info("submission found in 42.2")
                        return good
                    # check release requests too
                    good = self.factory._check_requests('openSUSE:Leap:42.2:Update', target_package, src_srcinfo.verifymd5)
                    if good or good == None:
                        self.logger.debug("found request")
                        return good
                # let's see where it came from before
                if package in self.lookup_422:
                    oldorigin = self.lookup_422[package]
                    self.logger.debug("oldorigin {}".format(oldorigin))
                    # Factory. So it's ok to keep upgrading it to Factory
                    # TODO: whitelist packages where this is ok and block others?
                    if oldorigin.startswith('openSUSE:Factory'):
                        self.logger.info("Package was from Factory in 42.2")
                        # check if an attempt to switch to SLE package is made
                        for sp in ('SP2:GA', 'SP2:Update', 'SP3:GA'):
                            good = self.factory._check_project('SUSE:SLE-12-{}'.format(sp), target_package, src_srcinfo.verifymd5)
                            if good:
                                self.logger.info("request sources come from SLE")
                                self.needs_release_manager = True
                                return good
                # the release manager needs to review attempts to upgrade to Factory
                is_fine_if_factory = True
                self.needs_release_manager = True

            elif origin.startswith('SUSE:SLE-12'):
                if self.must_approve_maintenance_updates:
                    self.needs_release_manager = True
                for v in ('42.3', '42.2'):
                    prj = 'openSUSE:Leap:{}:SLE-workarounds'.format(v)
                    if self.is_package_in_project( prj, target_package):
                        self.logger.info("found package in %s", prj)
                        if not self.factory._check_project(prj,
                                target_package,
                                src_srcinfo.verifymd5):
                            self.logger.info("sources in %s are NOT identical", prj)

                        self.needs_release_manager = True
                # submitted from :Update
                if origin == src_project:
                    self.logger.debug("submission origin ok")
                    return True
                elif origin.endswith(':GA') \
                    and src_project == origin[:-2]+'Update':
                    self.logger.debug("sle update submission")
                    return True

                # check  if submitted from higher SP
                priolist = ['SUSE:SLE-12:', 'SUSE:SLE-12-SP1:', 'SUSE:SLE-12-SP2:', 'SUSE:SLE-12-SP3:']
                for i in range(len(priolist)-1):
                    if origin.startswith(priolist[i]):
                        for prj in priolist[i+1:]:
                            if src_project.startswith(prj):
                                self.logger.info("submission from higher service pack %s:* ok", prj)
                                return True

                self.needs_release_manager = True
                # the release manager needs to review attempts to upgrade to Factory
                is_fine_if_factory = True
            else:
                self.logger.error("unhandled origin %s", origin)
                return False
        else: # no origin
            # submission from SLE is ok
            if src_project.startswith('SUSE:SLE-12'):
                return True

            is_fine_if_factory = True
            self.needs_release_manager = True

        # we came here because none of the above checks find it good, so
        # let's see if the package is in Factory at least
        is_in_factory = self._check_factory(target_package, src_srcinfo)
        if is_in_factory:
            self.source_in_factory = True
            self.needs_reviewteam = False
        elif is_in_factory is None:
            self.pending_factory_submission = True
            self.needs_reviewteam = False
        else:
            if src_project.startswith('SUSE:SLE-12') \
                or src_project.startswith('openSUSE:Leap:42.'):
                self.needs_reviewteam = False
            else:
                self.needs_reviewteam = True
            self.source_in_factory = False

        if is_fine_if_factory:
            if self.source_in_factory:
                return True
            elif self.pending_factory_submission:
                return None
            elif not_in_factory_okish:
                self.needs_reviewteam = True
                return True

        return False

    def _check_factory(self, target_package, src_srcinfo, target_project='openSUSE:Factory'):
            good = self.factory._check_project(target_project, target_package, src_srcinfo.verifymd5)
            if good:
                return good
            good = self.factory._check_requests(target_project, target_package, src_srcinfo.verifymd5)
            if good or good == None:
                self.logger.debug("found request to Factory")
                return good
            target_project_nonfree = '{}:NonFree'.format(target_project)
            good = self.factory._check_project(target_project_nonfree, target_package, src_srcinfo.verifymd5)
            if good:
                return good
            good = self.factory._check_requests(target_project_nonfree, target_package, src_srcinfo.verifymd5)
            if good or good == None:
                self.logger.debug('found request to {}'.format(target_project_nonfree))
                return good
            return False

    def _check_project_and_request(self, project, target_package, src_srcinfo):
        good = self.factory._check_project(project, target_package, src_srcinfo.verifymd5)
        if good:
            return good
        good = self.factory._check_requests(project, target_package, src_srcinfo.verifymd5)
        if good or good == None:
            return good
        return False

    def check_one_request(self, req):
        self.review_messages = self.DEFAULT_REVIEW_MESSAGES.copy()
        self.needs_reviewteam = False
        self.needs_release_manager = False
        self.pending_factory_submission = False
        self.source_in_factory = None
        self.comment_handler_add()
        self.packages = {}

        if len(req.actions) != 1:
            msg = "only one action per request please"
            self.review_messages['declined'] = msg
            return False

        request_ok = ReviewBot.ReviewBot.check_one_request(self, req)
        if not self.ibs:
            has_correct_maintainer = self.maintbot.check_one_request(req)
            self.logger.debug("has_correct_maintainer: %s", has_correct_maintainer)

        self.logger.debug("review result: %s", request_ok)
        if self.pending_factory_submission:
            self.logger.info("submission is waiting for a Factory request to complete")
            creator = req.get_creator()
            bot_name = self.bot_name.lower()
            if self.automatic_submission and creator != bot_name:
                self.logger.info('@{}: this request would have been automatically created by {} after the Factory submission was accepted in order to eleviate the need to manually create requests for packages sourced from Factory'.format(creator, bot_name))
        elif self.source_in_factory:
            self.logger.info("the submitted sources are in or accepted for Factory")
        elif self.source_in_factory == False:
            self.logger.info("the submitted sources are NOT in Factory")

        if request_ok == False:
            self.logger.info("NOTE: if you think the automated review was wrong here, please talk to the release team before reopening the request")
        elif self.needs_release_manager:
            self.logger.info("request needs review by release management")

        if self.do_comments:
            result = None
            if request_ok is None:
                state = 'seen'
            elif request_ok:
                state = 'done'
                result = 'accepted'
            else:
                state = 'done'
                result = 'declined'
            # Since leaper calls other bots (like maintbot) comments may
            # sometimes contain identical lines (like for unhandled requests).
            self.comment_handler_lines_deduplicate()
            self.comment_write(state, result)

        if self.needs_release_manager:
            add_review = True
            for r in req.reviews:
                if r.by_group == self.release_manager_group and (r.state == 'new' or r.state == 'accepted'):
                    add_review = False
                    self.logger.debug("%s already is a reviewer", self.release_manager_group)
                    break
            if add_review:
                if self.add_review(req, by_group = self.release_manager_group) != True:
                    self.review_messages['declined'] += '\nadding %s failed' % self.release_manager_group
                    return False

        if self.needs_reviewteam:
            add_review = True
            self.logger.info("%s needs review by opensuse-review-team"%req.reqid)
            for r in req.reviews:
                if r.by_group == 'opensuse-review-team':
                    add_review = False
                    self.logger.debug("opensuse-review-team already is a reviewer")
                    break
            if add_review:
                if self.add_review(req, by_group = "opensuse-review-team") != True:
                    self.review_messages['declined'] += '\nadding opensuse-review-team failed'
                    return False

        if self.needs_check_source and self.check_source_group is not None:
            add_review = True
            self.logger.info("%s needs review by %s" % (req.reqid, self.check_source_group))
            for r in req.reviews:
                if r.by_group == self.check_source_group:
                    add_review = False
                    self.logger.debug("%s already is a reviewer", self.check_source_group)
                    break
            if add_review:
                if self.add_review(req, by_group = self.check_source_group) != True:
                    self.review_messages['declined'] += '\nadding %s failed' % self.check_source_group
                    return False

        return request_ok

    def check_action__default(self, req, a):
        super(Leaper, self).check_action__default(req, a)
        self.needs_release_manager = True
        return True
class Leaper(ReviewBot.ReviewBot):
    def __init__(self, *args, **kwargs):
        ReviewBot.ReviewBot.__init__(self, *args, **kwargs)

        # ReviewBot options.
        self.only_one_action = True
        self.request_default_return = True
        self.comment_handler = True

        self.do_comments = True

        self.maintbot = MaintenanceChecker(*args, **kwargs)
        # for FactorySourceChecker
        self.factory = FactorySourceChecker(*args, **kwargs)

        self.needs_legal_review = False
        self.needs_reviewteam = False
        self.pending_factory_submission = False
        self.source_in_factory = None
        self.needs_release_manager = False
        self.release_manager_group = None
        self.review_team_group = None
        self.legal_review_group = None
        self.must_approve_version_updates = False
        self.must_approve_maintenance_updates = False
        self.needs_check_source = False
        self.check_source_group = None
        self.automatic_submission = False

        # project => package list
        self.packages = {}

    def prepare_review(self):
        # update lookup information on every run

        if self.ibs:
            self.factory.parse_lookup('SUSE:SLE-15:GA')
            self.lookup_sle15 = self.factory.lookup.copy()
            return

        self.factory.parse_lookup('openSUSE:Leap:15.0')
        self.factory.parse_lookup('openSUSE:Leap:15.0:NonFree')
        self.lookup_150 = self.factory.lookup.copy()

    def get_source_packages(self, project, expand=False):
        """Return the list of packages in a project."""
        query = {'expand': 1} if expand else {}
        try:
            root = ET.parse(
                osc.core.http_GET(
                    osc.core.makeurl(self.apiurl, ['source', project],
                                     query=query))).getroot()
            packages = [i.get('name') for i in root.findall('entry')]
        except urllib2.HTTPError as e:
            # in case the project doesn't exist yet (like sle update)
            if e.code != 404:
                raise e
            packages = []

        return packages

    def is_package_in_project(self, project, package):
        if not project in self.packages:
            self.packages[project] = self.get_source_packages(project)
        return True if package in self.packages[project] else False

    def rdiff_link(self,
                   src_project,
                   src_package,
                   src_rev,
                   target_project,
                   target_package=None):
        if target_package is None:
            target_package = src_package

        return '[%(target_project)s/%(target_package)s](/package/rdiff/%(src_project)s/%(src_package)s?opackage=%(target_package)s&oproject=%(target_project)s&rev=%(src_rev)s)' % {
            'src_project': src_project,
            'src_package': src_package,
            'src_rev': src_rev,
            'target_project': target_project,
            'target_package': target_package,
        }

    def _check_same_origin(self, origin, project):

        if origin == 'FORK':
            return True

        if origin.startswith('Devel;'):
            (dummy, origin, dummy) = origin.split(';')

        return project.startswith(origin)

    def check_source_submission(self, src_project, src_package, src_rev,
                                target_project, target_package):
        super(Leaper,
              self).check_source_submission(src_project, src_package, src_rev,
                                            target_project, target_package)
        self.automatic_submission = False

        if src_project == target_project and src_package == target_package:
            self.logger.info('self submission detected')
            self.needs_release_manager = True
            return True

        src_srcinfo = self.get_sourceinfo(src_project, src_package, src_rev)
        package = target_package

        origin = None

        if src_srcinfo is None:
            # source package does not exist?
            # handle here to avoid crashing on the next line
            self.logger.warn("Could not get source info for %s/%s@%s" %
                             (src_project, src_package, src_rev))
            return False

        if self.ibs and target_project.startswith('SUSE:SLE'):

            if package in self.lookup_sle15:
                origin = self.lookup_sle15[package]

            origin_same = True
            if origin:
                origin_same = self._check_same_origin(origin, src_project)
                self.logger.info("expected origin is '%s' (%s)", origin,
                                 "unchanged" if origin_same else "changed")

            prj = 'openSUSE.org:openSUSE:Factory'
            # True or None (open request) are acceptable for SLE.
            self.source_in_factory = self._check_factory(
                package, src_srcinfo, prj)
            if self.source_in_factory is None:
                self.pending_factory_submission = True
            if self.source_in_factory is not False:
                return self.source_in_factory

            # got false. could mean package doesn't exist or no match
            if self.is_package_in_project(prj, package):
                self.logger.info('different sources in {}'.format(
                    self.rdiff_link(src_project, src_package, src_rev, prj,
                                    package)))

            prj = 'openSUSE.org:openSUSE:Leap:15.0'
            # TODO Ugly save for SLE-15-SP1.
            if False and self.is_package_in_project(prj, package):
                if self._check_factory(package, src_srcinfo, prj) is True:
                    self.logger.info('found source match in {}'.format(prj))
                else:
                    self.logger.info('different sources in {}'.format(
                        self.rdiff_link(src_project, src_package, src_rev, prj,
                                        package)))

            devel_project, devel_package = devel_project_get(
                self.apiurl, 'openSUSE.org:openSUSE:Factory', package)
            if devel_project is not None:
                # specifying devel package is optional
                if devel_package is None:
                    devel_package = package
                if self.is_package_in_project(devel_project, devel_package):
                    if self.factory._check_project(
                            devel_project, devel_package,
                            src_srcinfo.verifymd5) == True:
                        self.logger.info('matching sources in {}/{}'.format(
                            devel_project, devel_package))
                        return True
                    else:
                        self.logger.info('different sources in {}'.format(
                            self.rdiff_link(src_project, src_package, src_rev,
                                            devel_project, devel_package)))
            else:
                self.logger.info('no devel project found for {}/{}'.format(
                    'openSUSE.org:openSUSE:Factory', package))

            #self.logger.info('no matching sources in Factory, Leap:15.0, nor devel project')
            self.logger.info(
                'no matching sources in Factory, nor devel project')

            if origin_same is False:
                # Rather than decline, leave review open in-case of change and
                # ask release manager for input via override comment.
                self.logger.info(
                    'Comment `(at){} override accept` to force accept.'.format(
                        self.review_user))
                self.needs_release_manager = True
                return None

            return origin_same

        if package in self.lookup_150:
            origin = self.lookup_150[package]

        # obviously
        if src_project in ('openSUSE:Factory', 'openSUSE:Factory:NonFree'):
            self.source_in_factory = True

        is_fine_if_factory = False
        not_in_factory_okish = False
        if origin:
            origin_same = self._check_same_origin(origin, src_project)
            self.logger.info("expected origin is '%s' (%s)", origin,
                             "unchanged" if origin_same else "changed")
            if origin.startswith('Devel;'):
                if origin_same == False:
                    self.logger.debug("not submitted from devel project")
                    return False
                is_fine_if_factory = True
                not_in_factory_okish = True
                if self.must_approve_version_updates:
                    self.needs_release_manager = True
                # fall through to check history and requests
            elif origin.startswith('openSUSE:Factory'):
                # A large number of requests are created by hand that leaper
                # would have created via update_crawler.py. This applies to
                # other origins, but primary looking to let Factory submitters
                # know that there is no need to make manual submissions to both.
                # Since it has a lookup entry it is not a new package.
                self.automatic_submission = False
                if self.must_approve_version_updates:
                    self.needs_release_manager = True
                if origin == src_project:
                    self.source_in_factory = True
                    # no need to approve submissions from Factory if
                    # the lookup file points to Factory. Just causes
                    # spam for many maintainers #1393
                    self.do_check_maintainer_review = False
                is_fine_if_factory = True
                # fall through to check history and requests
            elif origin == 'FORK':
                is_fine_if_factory = True
                if not src_project.startswith('SUSE:SLE-'):
                    not_in_factory_okish = True
                    self.needs_check_source = True
                self.needs_release_manager = True
                # fall through to check history and requests
            # TODO Ugly save for 15.1 (n-1).
            elif False and origin.startswith('openSUSE:Leap:15.0'):
                if self.must_approve_maintenance_updates:
                    self.needs_release_manager = True
                # submitted from :Update
                if origin_same:
                    self.logger.debug("submission from 15.0 ok")
                    return True
                # switching to sle package might make sense
                if src_project.startswith('SUSE:SLE-15'):
                    self.needs_release_manager = True
                    return True
                # submitted from elsewhere but is in :Update
                else:
                    good = self.factory._check_project(
                        'openSUSE:Leap:15.0:Update', target_package,
                        src_srcinfo.verifymd5)
                    if good:
                        self.logger.info("submission found in 15.0")
                        return good
                    # check release requests too
                    good = self.factory._check_requests(
                        'openSUSE:Leap:15.0:Update', target_package,
                        src_srcinfo.verifymd5)
                    if good or good == None:
                        self.logger.debug("found request")
                        return good
                # let's see where it came from before
                if package in self.lookup_150:
                    oldorigin = self.lookup_150[package]
                    self.logger.debug("oldorigin {}".format(oldorigin))
                    # Factory. So it's ok to keep upgrading it to Factory
                    # TODO: whitelist packages where this is ok and block others?
                    self.logger.info("Package was from %s in 15.0", oldorigin)
                    if oldorigin.startswith('openSUSE:Factory'):
                        # check if an attempt to switch to SLE package is made
                        for sp in ('SP1:GA', 'SP1:Update'):
                            good = self.factory._check_project(
                                'SUSE:SLE-15-{}'.format(sp), target_package,
                                src_srcinfo.verifymd5)
                            if good:
                                self.logger.info(
                                    "request sources come from SLE")
                                self.needs_release_manager = True
                                return good
                    # TODO Ugly save for 15.2 (n-2).
                    elif False and oldorigin.startswith('openSUSE:Leap:15.0'):
                        o = self.lookup_150[package]
                        self.logger.info("Package was from %s in 15.0", o)
                # the release manager needs to review attempts to upgrade to Factory
                is_fine_if_factory = True
                self.needs_release_manager = True

            elif origin.startswith('SUSE:SLE-15'):
                if self.must_approve_maintenance_updates:
                    self.needs_release_manager = True
                for v in ('15.0', ):
                    prj = 'openSUSE:Leap:{}:SLE-workarounds'.format(v)
                    if self.is_package_in_project(prj, target_package):
                        self.logger.info("found package in %s", prj)
                        if not self.factory._check_project(
                                prj, target_package, src_srcinfo.verifymd5):
                            self.logger.info(
                                "sources in %s are [NOT identical](%s)", prj,
                                self.rdiff_link(src_project, src_package,
                                                src_rev, prj, package))

                        self.needs_release_manager = True
                # submitted from :Update
                if origin == src_project:
                    self.logger.debug("submission origin ok")
                    return True
                elif origin.endswith(':GA') \
                    and src_project == origin[:-2]+'Update':
                    self.logger.debug("sle update submission")
                    return True

                # check  if submitted from higher SP
                priolist = [
                    'SUSE:SLE-15:', 'SUSE:SLE-15-SP1:', 'SUSE:SLE-15-SP2:',
                    'SUSE:SLE-15-SP3:'
                ]
                for i in range(len(priolist) - 1):
                    if origin.startswith(priolist[i]):
                        for prj in priolist[i + 1:]:
                            if src_project.startswith(prj):
                                self.logger.info(
                                    "submission from higher service pack %s:* ok",
                                    prj)
                                return True

                in_sle_origin = self._check_factory(target_package,
                                                    src_srcinfo, origin)
                if in_sle_origin:
                    self.logger.info(
                        'parallel submission, also in {}'.format(origin))
                    return True

                self.needs_release_manager = True
                # the release manager needs to review attempts to upgrade to Factory
                is_fine_if_factory = True
            else:
                self.logger.error("unhandled origin %s", origin)
                return False
        else:  # no origin
            # submission from SLE is ok
            if src_project.startswith('SUSE:SLE-15'):
                self.do_check_maintainer_review = False
                return True

            # new package submitted from Factory. Check if it was in
            # 42.3 before and skip maintainer review if so.
            subprj = src_project[len('openSUSE:Factory'):]
            if self.source_in_factory and target_project.startswith('openSUSE:Leap:15.0') \
                and self.is_package_in_project('openSUSE:Leap:42.3'+subprj, package):
                self.logger.info('package was in 42.3')
                self.do_check_maintainer_review = False
                return True

            is_fine_if_factory = True
            self.needs_release_manager = True

        if origin is None or not origin.startswith('SUSE:SLE-'):
            for p in (':Update', ':GA'):
                prj = 'SUSE:SLE-15' + p
                if self.is_package_in_project(prj, package):
                    self.logger.info('Package is in {}'.format(
                        self.rdiff_link(src_project, src_package, src_rev, prj,
                                        package)))
                    break

        is_in_factory = self.source_in_factory

        # we came here because none of the above checks find it good, so
        # let's see if the package is in Factory at least
        if is_in_factory is None:
            is_in_factory = self._check_factory(package, src_srcinfo)
        if is_in_factory:
            self.source_in_factory = True
            self.needs_reviewteam = False
            self.needs_legal_review = False
        elif is_in_factory is None:
            self.pending_factory_submission = True
            self.needs_reviewteam = False
            self.needs_legal_review = False
        else:
            if src_project.startswith('SUSE:SLE-15') \
                or src_project.startswith('openSUSE:Leap:15.'):
                self.needs_reviewteam = False
                self.needs_legal_review = False
            else:
                self.needs_reviewteam = True
                self.needs_legal_review = True
            self.source_in_factory = False

        if is_fine_if_factory:
            if self.source_in_factory:
                return True
            elif self.pending_factory_submission:
                return None
            elif not_in_factory_okish:
                self.needs_reviewteam = True
                self.needs_legal_review = True
                return True

        if self.override_allow:
            # Rather than decline, leave review open and ask release
            # manager for input via override comment.
            self.logger.info(
                'Comment `(at){} override accept` to force accept.'.format(
                    self.review_user))
            self.needs_release_manager = True
            return None

        return False

    def _check_factory(self,
                       target_package,
                       src_srcinfo,
                       target_project='openSUSE:Factory'):
        for subprj in ('', ':NonFree', ':Live'):
            prj = ''.join((target_project, subprj))
            good = self.factory._check_project(prj, target_package,
                                               src_srcinfo.verifymd5)
            if good:
                return good
            good = self.factory._check_requests(prj, target_package,
                                                src_srcinfo.verifymd5)
            if good or good == None:
                self.logger.debug("found request to %s", prj)
                return good

        return False

    def _check_project_and_request(self, project, target_package, src_srcinfo):
        good = self.factory._check_project(project, target_package,
                                           src_srcinfo.verifymd5)
        if good:
            return good
        good = self.factory._check_requests(project, target_package,
                                            src_srcinfo.verifymd5)
        if good or good == None:
            return good
        return False

    def check_one_request(self, req):
        api = self.staging_api(req.actions[0].tgt_project)
        config = self.staging_config[api.project]
        self.needs_legal_review = False
        self.needs_reviewteam = False
        self.needs_release_manager = False
        self.pending_factory_submission = False
        self.source_in_factory = None
        self.do_check_maintainer_review = not self.ibs
        self.packages = {}

        request_ok = ReviewBot.ReviewBot.check_one_request(self, req)
        if self.do_check_maintainer_review:
            has_correct_maintainer = self.maintbot.check_one_request(req)
            self.logger.debug("has_correct_maintainer: %s",
                              has_correct_maintainer)

        self.logger.debug("review result: %s", request_ok)
        if self.pending_factory_submission:
            self.logger.info(
                "submission is waiting for a Factory request to complete")
            creator = req.get_creator()
            bot_name = self.bot_name.lower()
            if self.automatic_submission and creator != bot_name:
                self.logger.info(
                    '@{}: this request would have been automatically created by {} after the Factory submission was accepted in order to eleviate the need to manually create requests for packages sourced from Factory'
                    .format(creator, bot_name))
        elif self.source_in_factory:
            self.logger.info(
                "the submitted sources are in or accepted for Factory")
        elif self.source_in_factory == False:
            self.logger.info("the submitted sources are NOT in Factory")

        if request_ok == False:
            self.logger.info(
                "NOTE: if you think the automated review was wrong here, please talk to the release team before reopening the request"
            )

        if self.do_comments:
            result = None
            if request_ok is None:
                state = 'seen'
            elif request_ok:
                state = 'done'
                result = 'accepted'
            else:
                state = 'done'
                result = 'declined'
            # Since leaper calls other bots (like maintbot) comments may
            # sometimes contain identical lines (like for unhandled requests).
            self.comment_handler_lines_deduplicate()
            self.comment_write(state, result)

        add_review_groups = []
        if self.needs_release_manager:
            add_review_groups.append(self.release_manager_group
                                     or config.get(self.override_group_key))
        if self.needs_reviewteam:
            add_review_groups.append(self.review_team_group
                                     or config.get('review-team'))
        if self.needs_legal_review:
            add_review_groups.append(self.legal_review_group
                                     or config.get('legal-review-group'))
        if self.needs_check_source and self.check_source_group is not None:
            add_review_groups.append(self.check_source_group)

        for group in add_review_groups:
            if group is None:
                continue
            self.logger.info(
                "{0} needs review by [{1}](/group/show/{1})".format(
                    req.reqid, group))
            self.add_review(req, by_group=group)

        return request_ok

    def check_action__default(self, req, a):
        self.needs_release_manager = True
        if self.ibs:
            self.do_check_maintainer_review = False
        return super(Leaper, self).check_action__default(req, a)
示例#3
0
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.needs_reviewteam = False
        self.pending_factory_submission = False
        self.source_in_factory = False

    def prepare_review(self):

        # update lookup information on every run
        self.factory.parse_lookup('openSUSE:Leap:42.2')
        self.factory.parse_lookup('openSUSE:Leap:42.2:NonFree')
        self.lookup_422 = self.factory.lookup.copy()
        self.factory.lookup = {}
        self.factory.parse_lookup('openSUSE:Leap:42.1:Update')
        self.lookup_421 = self.factory.lookup.copy()
        self.factory.lookup = {}

    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))
        src_srcinfo = self.get_sourceinfo(src_project, src_package, src_rev)
        package = target_package

        if src_srcinfo is None:
            # source package does not exist?
            # handle here to avoid crashing on the next line
            self.logger.warn("Could not get source info for %s/%s@%s" %
                             (src_project, src_package, src_rev))
            return False

        origin = None
        if package in self.lookup_422:
            origin = self.lookup_422[package]

        if origin:
            self.logger.debug("origin {}".format(origin))
            if origin.startswith('Devel;'):
                self.needs_reviewteam = True
                (dummy, origin, dummy) = origin.split(';')
            if origin == src_project:
                self.logger.debug("exact match")
                return True
            elif origin.startswith('openSUSE:Factory'):
                return self._check_factory(target_package, src_srcinfo)
            elif origin.startswith('openSUSE:Leap:42.1'):
                # submitted from :Update
                if src_project.startswith(origin):
                    self.logger.debug("match 42.1")
                    return True
                # submitted from elsewhere but is in :Update
                else:
                    good = self.factory._check_project(
                        'openSUSE:Leap:42.1:Update', target_package,
                        src_srcinfo.verifymd5)
                    if good:
                        self.logger.info("submission found in 42.1")
                        return good
                    # check release requests too
                    good = self.factory._check_requests(
                        'openSUSE:Leap:42.1:Update', target_package,
                        src_srcinfo.verifymd5)
                    if good or good == None:
                        self.logger.debug("found request")
                        return good
                # let's see where it came from before
                if package in self.lookup_421:
                    oldorigin = self.lookup_421[package]
                    self.logger.debug("oldorigin {}".format(oldorigin))
                    # Factory. So it's ok to keep upgrading it to Factory
                    # TODO: whitelist packages where this is ok and block others?
                    if oldorigin.startswith('openSUSE:Factory'):
                        if src_project == oldorigin:
                            self.logger.debug(
                                "Upgrade to Factory again. Submitted from Factory"
                            )
                            return True
                        good = self._check_factory(target_package, src_srcinfo)
                        if good or good == None:
                            self.logger.debug(
                                "Upgrade to Factory again. It's in Factory")
                            return good
                        # or maybe in SP2?
                        good = self.factory._check_project(
                            'SUSE:SLE-12-SP2:GA', target_package,
                            src_srcinfo.verifymd5)
                        if good:
                            self.logger.debug("hope it's ok to change to SP2")
                            return good
                # else other project or FORK, fall through

            elif origin.startswith('SUSE:SLE-12'):
                # submitted from :Update
                if src_project.startswith(origin):
                    self.logger.debug("match sle")
                    return True
                # submitted from higher SP
                if origin.startswith('SUSE:SLE-12'):
                    if src_project.startswith('SUSE:SLE-12-SP1') \
                        or src_project.startswith('SUSE:SLE-12-SP2'):
                        self.logger.debug("higher service pack ok")
                        return True
            # else other project or FORK, fall through

            # we came here because none of the above checks find it good, so
            # let's see if the package is in Factory at least
            is_in_factory = self._check_factory(target_package, src_srcinfo)
            if is_in_factory:
                self.source_in_factory = True
            elif is_in_factory is None:
                self.pending_factory_submission = True
            else:
                if not src_project.startswith('SUSE:SLE-12'):
                    self.needs_reviewteam = True

        else:  # no origin
            # SLE and Factory are ok
            if src_project.startswith('SUSE:SLE-12') \
                or src_project.startswith('openSUSE:Factory'):
                return True
            # submitted from elsewhere, check it's in Factory
            good = self._check_factory(target_package, src_srcinfo)
            if good:
                self.source_in_factory = True
                return True
            elif good == None:
                self.pending_factory_submission = True
                return good
            # or maybe in SP2?
            good = self.factory._check_project('SUSE:SLE-12-SP2:GA',
                                               target_package,
                                               src_srcinfo.verifymd5)
            if good:
                return good

        return False

    def _check_factory(self, target_package, src_srcinfo):
        good = self.factory._check_project('openSUSE:Factory', target_package,
                                           src_srcinfo.verifymd5)
        if good:
            return good
        good = self.factory._check_requests('openSUSE:Factory', target_package,
                                            src_srcinfo.verifymd5)
        if good or good == None:
            self.logger.debug("found request to Factory")
            return good
        good = self.factory._check_project('openSUSE:Factory:NonFree',
                                           target_package,
                                           src_srcinfo.verifymd5)
        if good:
            return good
        good = self.factory._check_requests('openSUSE:Factory:NonFree',
                                            target_package,
                                            src_srcinfo.verifymd5)
        if good or good == None:
            self.logger.debug("found request to Factory:NonFree")
            return good
        return False

    def check_one_request(self, req):
        self.review_messages = self.DEFAULT_REVIEW_MESSAGES.copy()
        self.needs_reviewteam = False
        self.pending_factory_submission = False
        self.source_in_factory = False

        if len(req.actions) != 1:
            msg = "only one action per request please"
            self.review_messages['declined'] = msg
            return False

        # if the fallback reviewer created the request she probably
        # knows what she does :-)
        if self.fallback_user and req.get_creator() == self.fallback_user:
            self.logger.debug("skip fallback review")
            return True

        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 self.needs_reviewteam:
            add_review = True
            self.logger.debug("%s needs review by opensuse-review-team" %
                              req.reqid)
            for r in req.reviews:
                if r.by_group == 'opensuse-review-team':
                    add_review = False
                    self.logger.debug(
                        "opensuse-review-team already is a reviewer")
                    break
            if add_review:
                if self.add_review(req,
                                   by_group="opensuse-review-team") != True:
                    self.review_messages[
                        'declined'] += '\nadding opensuse-review-team failed'
                    return False

        if has_upstream_sources != True or has_correct_maintainer != True:
            if has_upstream_sources != True:
                self.review_messages['declined'] += '\nOrigin project changed'
                pkg = req.actions[0].tgt_package
                if pkg in self.lookup_422:
                    self.review_messages['declined'] += '(was {})'.format(
                        self.lookup_422[pkg])
                if self.source_in_factory:
                    self.review_messages[
                        'declined'] += '\nsource is in Factory'
                if self.pending_factory_submission:
                    self.review_messages[
                        'declined'] += '\na submission to Factory is pending'
                    self.logger.debug(
                        "origin changed but waiting for Factory submission to complete"
                    )
                    # FXIME: we should add the human reviewer here
                    # and leave a comment
                    return None
            # shouldn't happen actually
            if has_correct_maintainer != True:
                self.review_messages['declined'] += '\nMaintainer check failed'
            return False

        return True

    def check_action__default(self, req, a):
        # decline all other requests for fallback reviewer
        self.logger.debug("auto decline request type %s" % a.type)
        return False
示例#4
0
class Leaper(ReviewBot.ReviewBot):

    def __init__(self, *args, **kwargs):
        ReviewBot.ReviewBot.__init__(self, *args, **kwargs)
        self.maintbot = MaintenanceChecker(*args, **kwargs)
        # for FactorySourceChecker
        self.lookup_checker = FactorySourceChecker(*args, **kwargs)
        self.lookup_checker.parse_lookup('openSUSE:Leap:42.2')
        self.lookup_checker.parse_lookup('openSUSE:Leap:42.2:NonFree')
        self.factory = FactorySourceChecker(*args, **kwargs)
        # XXX: FactorySourceChecker should be able to handle that itself
        self.factory_nonfree = FactorySourceChecker(*args, **kwargs)
        self.factory_nonfree.factory = 'openSUSE:Factory:NonFree'

    def check_source_submission(self, src_project, src_package, src_rev, target_project, target_package):
        return self.lookup_checker.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()

        if len(req.actions) != 1:
            msg = "only one action per request please"
            self.review_messages['declined'] = msg
            return False

        # if the fallback reviewer created the request she probably
        # knows what she does :-)
        if self.fallback_user and req.get_creator() == self.fallback_user:
            self.logger.debug("skip fallback review")
            return True

        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['declined'] += '\nOrigin project changed'
                pkg = req.actions[0].tgt_package
                prj = self.lookup_checker._package_get_upstream_project(pkg)
                if prj:
                    self.review_messages['declined'] += '(was {})'.format(prj)
                r = self.factory.check_one_request(req)
                if r == True:
                    self.review_messages['declined'] += '\nsource is in Factory though'
                elif r == None:
                    self.logger.info("waiting for review")
                    return None
                else:
                    r = self.factory_nonfree.check_one_request(req)
                    if r == True:
                        self.review_messages['declined'] += '\nsource is in Factory:NonFree though'
                    elif r == None:
                        self.logger.info("waiting for review")
                        return None
            # shouldn't happen actually
            if has_correct_maintainer != True:
                self.review_messages['declined'] += '\nMaintainer check failed'
            return False

        return True
示例#5
0
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.needs_reviewteam = False
        self.pending_factory_submission = False
        self.source_in_factory = False

    def prepare_review(self):

        # update lookup information on every run
        self.factory.parse_lookup('openSUSE:Leap:42.2')
        self.factory.parse_lookup('openSUSE:Leap:42.2:NonFree')
        self.lookup_422 = self.factory.lookup.copy()
        self.factory.lookup = {}
        self.factory.parse_lookup('openSUSE:Leap:42.1:Update')
        self.lookup_421 = self.factory.lookup.copy()
        self.factory.lookup = {}

    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))
        src_srcinfo = self.get_sourceinfo(src_project, src_package, src_rev)
        package = target_package

        if src_srcinfo is None:
            # source package does not exist?
            # handle here to avoid crashing on the next line
            self.logger.warn("Could not get source info for %s/%s@%s" % (src_project, src_package, src_rev))
            return False

        origin = None
        if package in self.lookup_422:
            origin = self.lookup_422[package]

        if origin:
            self.logger.debug("origin {}".format(origin))
            if origin.startswith('Devel;'):
                self.needs_reviewteam = True
                (dummy, origin, dummy) = origin.split(';')
            if origin == src_project:
                self.logger.debug("exact match")
                return True
            elif origin.startswith('openSUSE:Factory'):
                return self._check_factory(target_package, src_srcinfo)
            elif origin.startswith('openSUSE:Leap:42.1'):
                # submitted from :Update
                if src_project.startswith(origin):
                    self.logger.debug("match 42.1")
                    return True
                # submitted from elsewhere but is in :Update
                else:
                    good = self.factory._check_project('openSUSE:Leap:42.1:Update', target_package, src_srcinfo.verifymd5)
                    if good:
                        self.logger.info("submission found in 42.1")
                        return good
                    # check release requests too
                    good = self.factory._check_requests('openSUSE:Leap:42.1:Update', target_package, src_srcinfo.verifymd5)
                    if good or good == None:
                        self.logger.debug("found request")
                        return good
                # let's see where it came from before
                if package in self.lookup_421:
                    oldorigin = self.lookup_421[package]
                    self.logger.debug("oldorigin {}".format(oldorigin))
                    # Factory. So it's ok to keep upgrading it to Factory
                    # TODO: whitelist packages where this is ok and block others?
                    if oldorigin.startswith('openSUSE:Factory'):
                        if src_project == oldorigin:
                            self.logger.debug("Upgrade to Factory again. Submitted from Factory")
                            return True
                        good = self._check_factory(target_package, src_srcinfo)
                        if good or good == None:
                            self.logger.debug("Upgrade to Factory again. It's in Factory")
                            return good
                        # or maybe in SP2?
                        good = self.factory._check_project('SUSE:SLE-12-SP2:GA', target_package, src_srcinfo.verifymd5)
                        if good:
                            self.logger.debug("hope it's ok to change to SP2")
                            return good
                # else other project or FORK, fall through

            elif origin.startswith('SUSE:SLE-12'):
                # submitted from :Update
                if src_project.startswith(origin):
                    self.logger.debug("match sle")
                    return True
                # submitted from higher SP
                if origin.startswith('SUSE:SLE-12'):
                    if src_project.startswith('SUSE:SLE-12-SP1') \
                        or src_project.startswith('SUSE:SLE-12-SP2'):
                            self.logger.debug("higher service pack ok")
                            return True
            # else other project or FORK, fall through

            # we came here because none of the above checks find it good, so
            # let's see if the package is in Factory at least
            is_in_factory = self._check_factory(target_package, src_srcinfo)
            if is_in_factory:
                self.source_in_factory = True
            elif is_in_factory is None:
                self.pending_factory_submission = True
            else:
                if not src_project.startswith('SUSE:SLE-12'):
                    self.needs_reviewteam = True

        else: # no origin
            # SLE and Factory are ok
            if src_project.startswith('SUSE:SLE-12') \
                or src_project.startswith('openSUSE:Factory'):
                return True
            # submitted from elsewhere, check it's in Factory
            good = self._check_factory(target_package, src_srcinfo)
            if good:
                self.source_in_factory = True
                return True
            elif good == None:
                self.pending_factory_submission = True
                return good
            # or maybe in SP2?
            good = self.factory._check_project('SUSE:SLE-12-SP2:GA', target_package, src_srcinfo.verifymd5)
            if good:
                return good

        return False

    def _check_factory(self, target_package, src_srcinfo):
            good = self.factory._check_project('openSUSE:Factory', target_package, src_srcinfo.verifymd5)
            if good:
                return good
            good = self.factory._check_requests('openSUSE:Factory', target_package, src_srcinfo.verifymd5)
            if good or good == None:
                self.logger.debug("found request to Factory")
                return good
            good = self.factory._check_project('openSUSE:Factory:NonFree', target_package, src_srcinfo.verifymd5)
            if good:
                return good
            good = self.factory._check_requests('openSUSE:Factory:NonFree', target_package, src_srcinfo.verifymd5)
            if good or good == None:
                self.logger.debug("found request to Factory:NonFree")
                return good
            return False

    def check_one_request(self, req):
        self.review_messages = self.DEFAULT_REVIEW_MESSAGES.copy()
        self.needs_reviewteam = False
        self.pending_factory_submission = False
        self.source_in_factory = False

        if len(req.actions) != 1:
            msg = "only one action per request please"
            self.review_messages['declined'] = msg
            return False

        # if the fallback reviewer created the request she probably
        # knows what she does :-)
        if self.fallback_user and req.get_creator() == self.fallback_user:
            self.logger.debug("skip fallback review")
            return True

        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 self.needs_reviewteam:
            add_review = True
            self.logger.debug("%s needs review by opensuse-review-team"%req.reqid)
            for r in req.reviews:
                if r.by_group == 'opensuse-review-team':
                    add_review = False
                    self.logger.debug("opensuse-review-team already is a reviewer")
                    break
            if add_review:
                if self.add_review(req, by_group = "opensuse-review-team") != True:
                    self.review_messages['declined'] += '\nadding opensuse-review-team failed'
                    return False

        if has_upstream_sources != True or has_correct_maintainer != True:
            if has_upstream_sources != True:
                self.review_messages['declined'] += '\nOrigin project changed'
                pkg = req.actions[0].tgt_package
                if pkg in self.lookup_422:
                    self.review_messages['declined'] += '(was {})'.format(self.lookup_422[pkg])
                if self.source_in_factory:
                    self.review_messages['declined'] += '\nsource is in Factory'
                if self.pending_factory_submission:
                    self.review_messages['declined'] += '\na submission to Factory is pending'
                    self.logger.debug("origin changed but waiting for Factory submission to complete")
                    # FXIME: we should add the human reviewer here
                    # and leave a comment
                    return None
            # shouldn't happen actually
            if has_correct_maintainer != True:
                self.review_messages['declined'] += '\nMaintainer check failed'
            return False

        return True

    def check_action__default(self, req, a):
        # decline all other requests for fallback reviewer
        self.logger.debug("auto decline request type %s"%a.type)
        return False
示例#6
0
class Leaper(ReviewBot.ReviewBot):

    def __init__(self, *args, **kwargs):
        ReviewBot.ReviewBot.__init__(self, *args, **kwargs)

        self.do_comments = True
        self.commentapi = CommentAPI(self.apiurl)

        self.maintbot = MaintenanceChecker(*args, **kwargs)
        # for FactorySourceChecker
        self.factory = FactorySourceChecker(*args, **kwargs)

        self.needs_reviewteam = False
        self.pending_factory_submission = False
        self.source_in_factory = None
        self.needs_release_manager = False
        self.release_manager_group = 'leap-reviewers'
        self.must_approve_version_updates = False
        self.must_approve_maintenance_updates = False

        self.comment_marker_re = re.compile(r'<!-- leaper state=(?P<state>done|seen)(?: result=(?P<result>accepted|declined))? -->')

        self.comment_log = None
        self.commentlogger = LogToString(self, 'comment_log')
        self.logger.addFilter(self.commentlogger)

    def prepare_review(self):

        # update lookup information on every run
        self.factory.parse_lookup('openSUSE:Leap:42.2')
        self.factory.parse_lookup('openSUSE:Leap:42.2:NonFree')
        self.lookup_422 = self.factory.lookup.copy()
        self.factory.lookup = {}
        self.factory.parse_lookup('openSUSE:Leap:42.1:Update')
        self.lookup_421 = self.factory.lookup.copy()
        self.factory.lookup = {}

    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))
        src_srcinfo = self.get_sourceinfo(src_project, src_package, src_rev)
        package = target_package

        if src_srcinfo is None:
            # source package does not exist?
            # handle here to avoid crashing on the next line
            self.logger.warn("Could not get source info for %s/%s@%s" % (src_project, src_package, src_rev))
            return False

        origin = None
        if package in self.lookup_422:
            origin = self.lookup_422[package]

        is_fine_if_factory = False
        not_in_factory_okish = False
        if origin:
            self.logger.info("expected origin is '%s'", origin)
            if origin.startswith('Devel;'):
                (dummy, origin, dummy) = origin.split(';')
                if origin != src_project:
                    self.logger.debug("not submitted from devel project")
                    return False
                is_fine_if_factory = True
                not_in_factory_okish = True
                if self.must_approve_version_updates:
                    self.needs_release_manager = True
                # fall through to check history and requests
            elif origin.startswith('openSUSE:Factory'):
                if self.must_approve_version_updates:
                    self.needs_release_manager = True
                if origin == src_project:
                    self.source_in_factory = True
                    return True
                is_fine_if_factory = True
                # fall through to check history and requests
            elif origin == 'FORK':
                is_fine_if_factory = True
                not_in_factory_okish = True
                self.needs_release_manager = True
                # fall through to check history and requests
            elif origin.startswith('openSUSE:Leap:42.1'):
                if self.must_approve_maintenance_updates:
                    self.needs_release_manager = True
                # submitted from :Update
                if src_project.startswith(origin):
                    self.logger.debug("submission from 42.1 ok")
                    return True
                # submitted from elsewhere but is in :Update
                else:
                    good = self.factory._check_project('openSUSE:Leap:42.1:Update', target_package, src_srcinfo.verifymd5)
                    if good:
                        self.logger.info("submission found in 42.1")
                        return good
                    # check release requests too
                    good = self.factory._check_requests('openSUSE:Leap:42.1:Update', target_package, src_srcinfo.verifymd5)
                    if good or good == None:
                        self.logger.debug("found request")
                        return good
                # let's see where it came from before
                if package in self.lookup_421:
                    oldorigin = self.lookup_421[package]
                    self.logger.debug("oldorigin {}".format(oldorigin))
                    # Factory. So it's ok to keep upgrading it to Factory
                    # TODO: whitelist packages where this is ok and block others?
                    if oldorigin.startswith('openSUSE:Factory'):
                        self.logger.info("Package was from Factory in 42.1")
                        # check if an attempt to switch to SLE package is made
                        good = self.factory._check_project('SUSE:SLE-12-SP2:GA', target_package, src_srcinfo.verifymd5)
                        if good:
                            self.logger.info("request sources come from SLE")
                            self.needs_release_manager = True
                            return good
                # the release manager needs to review attempts to upgrade to Factory
                is_fine_if_factory = True
                self.needs_release_manager = True

            elif origin.startswith('SUSE:SLE-12'):
                if self.must_approve_maintenance_updates:
                    self.needs_release_manager = True
                # submitted from :Update
                if origin == src_project:
                    self.logger.debug("submission origin ok")
                    return True
                elif origin.endswith(':GA') \
                    and src_project == origin[:-2]+'Update':
                    self.logger.debug("sle update submission")
                    return True
                # submitted from higher SP
                if origin.startswith('SUSE:SLE-12:'):
                    if src_project.startswith('SUSE:SLE-12-SP1:') \
                        or src_project.startswith('SUSE:SLE-12-SP2:'):
                            self.logger.info("submission from service pack ok")
                            return True
                elif origin.startswith('SUSE:SLE-12-SP1:'):
                    if src_project.startswith('SUSE:SLE-12-SP2:'):
                        self.logger.info("submission from service pack ok")
                        return True

                self.needs_release_manager = True
                good = self._check_project_and_request('openSUSE:Leap:42.2:SLE-workarounds', target_package, src_srcinfo)
                if good or good == None:
                    self.logger.info("found sources in SLE-workarounds")
                    return good
                # the release manager needs to review attempts to upgrade to Factory
                is_fine_if_factory = True
            else:
                self.logger.error("unhandled origin %s", origin)
                return False
        else: # no origin
            # submission from SLE is ok
            if src_project.startswith('SUSE:SLE-12'):
                return True

            is_fine_if_factory = True
            self.needs_release_manager = True

        # we came here because none of the above checks find it good, so
        # let's see if the package is in Factory at least
        is_in_factory = self._check_factory(target_package, src_srcinfo)
        if is_in_factory:
            self.source_in_factory = True
            self.needs_reviewteam = False
        elif is_in_factory is None:
            self.pending_factory_submission = True
            self.needs_reviewteam = False
        else:
            if src_project.startswith('SUSE:SLE-12') \
                or src_project.startswith('openSUSE:Leap:42.'):
                self.needs_reviewteam = False
            else:
                self.needs_reviewteam = True
            self.source_in_factory = False

        if is_fine_if_factory:
            if self.source_in_factory:
                return True
            elif self.pending_factory_submission:
                return None
            elif not_in_factory_okish:
                self.needs_reviewteam = True
                return True

        return False

    def _check_factory(self, target_package, src_srcinfo):
            good = self.factory._check_project('openSUSE:Factory', target_package, src_srcinfo.verifymd5)
            if good:
                return good
            good = self.factory._check_requests('openSUSE:Factory', target_package, src_srcinfo.verifymd5)
            if good or good == None:
                self.logger.debug("found request to Factory")
                return good
            good = self.factory._check_project('openSUSE:Factory:NonFree', target_package, src_srcinfo.verifymd5)
            if good:
                return good
            good = self.factory._check_requests('openSUSE:Factory:NonFree', target_package, src_srcinfo.verifymd5)
            if good or good == None:
                self.logger.debug("found request to Factory:NonFree")
                return good
            return False

    def _check_project_and_request(self, project, target_package, src_srcinfo):
        good = self.factory._check_project(project, target_package, src_srcinfo.verifymd5)
        if good:
            return good
        good = self.factory._check_requests(project, target_package, src_srcinfo.verifymd5)
        if good or good == None:
            return good
        return False

    def check_one_request(self, req):
        self.review_messages = self.DEFAULT_REVIEW_MESSAGES.copy()
        self.needs_reviewteam = False
        self.needs_release_manager = False
        self.pending_factory_submission = False
        self.source_in_factory = None
        self.comment_log = []

        if len(req.actions) != 1:
            msg = "only one action per request please"
            self.review_messages['declined'] = msg
            return False

        request_ok = ReviewBot.ReviewBot.check_one_request(self, req)
        has_correct_maintainer = self.maintbot.check_one_request(req)

        self.logger.debug("review result: %s", request_ok)
        self.logger.debug("has_correct_maintainer: %s", has_correct_maintainer)
        if self.pending_factory_submission:
            self.logger.info("submission is waiting for a Factory request to complete")
        elif self.source_in_factory:
            self.logger.info("the submitted sources are in or accepted for Factory")
        elif self.source_in_factory == False:
            self.logger.info("the submitted sources are NOT in Factory")

        if request_ok == False:
            self.logger.info("NOTE: if you think the automated review was wrong here, please talk to the release team before reopening the request")
        elif self.needs_release_manager:
            self.logger.info("request needs review by release management")

        if self.comment_log:
            result = None
            if request_ok is None:
                state = 'seen'
            elif request_ok:
                state = 'done'
                result = 'accepted'
            else:
                state = 'done'
                result = 'declined'
            self.add_comment(req, '\n\n'.join(self.comment_log), state)
        self.comment_log = None

        if self.needs_release_manager:
            add_review = True
            for r in req.reviews:
                if r.by_group == self.release_manager_group and (r.state == 'new' or r.state == 'accepted'):
                    add_review = False
                    self.logger.debug("%s already is a reviewer", self.release_manager_group)
                    break
            if add_review:
                if self.add_review(req, by_group = self.release_manager_group) != True:
                    self.review_messages['declined'] += '\nadding %s failed' % self.release_manager_group
                    return False

        if self.needs_reviewteam:
            add_review = True
            self.logger.info("%s needs review by opensuse-review-team"%req.reqid)
            for r in req.reviews:
                if r.by_group == 'opensuse-review-team':
                    add_review = False
                    self.logger.debug("opensuse-review-team already is a reviewer")
                    break
            if add_review:
                if self.add_review(req, by_group = "opensuse-review-team") != True:
                    self.review_messages['declined'] += '\nadding opensuse-review-team failed'
                    return False

        return request_ok

    def check_action__default(self, req, a):
        # decline all other requests for fallback reviewer
        self.logger.debug("auto decline request type %s"%a.type)
        return False

    # TODO: make generic, move to Reviewbot. Used by multiple bots
    def add_comment(self, req, msg, state, result=None):
        if not self.do_comments:
            return

        comment = "<!-- leaper state=%s%s -->\n" % (state, ' result=%s' % result if result else '')
        comment += "\n" + msg

        (comment_id, comment_state, comment_result, comment_text) = self.find_obs_request_comment(req, state)

        if comment_id is not None and state == comment_state:
            # count number of lines as aproximation to avoid spamming requests
            # for slight wording changes in the code
            if len(comment_text.split('\n')) == len(comment.split('\n')):
                self.logger.debug("not worth the update, previous comment %s is state %s", comment_id, comment_state)
                return

        self.logger.debug("adding comment to %s, state %s result %s", req.reqid, state, result)
        self.logger.debug("message: %s", msg)
        if not self.dryrun:
            if comment_id is not None:
                self.commentapi.delete(comment_id)
            self.commentapi.add_comment(request_id=req.reqid, comment=str(comment))

    def find_obs_request_comment(self, req, state=None):
        """Return previous comments (should be one)."""
        if self.do_comments:
            comments = self.commentapi.get_comments(request_id=req.reqid)
            for c in comments.values():
                m = self.comment_marker_re.match(c['comment'])
                if m and (state is None or state == m.group('state')):
                    return c['id'], m.group('state'), m.group('result'), c['comment']
        return None, None, None, None

    def check_action__default(self, req, a):
        self.logger.info("unhandled request type %s"%a.type)
        self.needs_release_manager = True
        return True