From 8db3461a00784e999cb3218c70d16a10b2e70f38 Mon Sep 17 00:00:00 2001 From: Jimmy Berry Date: Thu, 29 Dec 2016 00:34:56 -0600 Subject: [PATCH] Port osc-check_source.py to ReviewBot as check_source.py. In the process of porting many improvements and bug fixes were added. Usage follows the standard ReviewBot format and allows for new execution styles, but the following examples replicate previous usage. # Review all requests assigned to factory-auto. ./check_source.py --group factory-auto review # Review request 13370. ./check_source.py --group factory-auto id 13370 # --project translates to project command. # No longer automatically includes $project:NonFree. ./check_source.py --group factory-auto project openSUSE:Factory New options available are: --ignore-devel ignore devel projects for target package --devel-whitelist=FILE file containing whitelisted projects (one per line) --review-team=GROUP review team group added to requests with > 8 diff --repo-checker=USER repo checker user added after accepted review Note that --ignore-devel used to be provided as env[IGNORE_DEVEL_PROJECTS]. Some highlights about what was cleaned up: - cryptic variable names replaced - replaced custom osc queries with osc.core calls where applicable - removed the need to load package information for all packages within target project which cuts runtime in half for single review - removed extraneous code that performed extra steps for no reason os.path.dirname(os.path.realpath(__file__.replace('.pyc', '.py'))) os.path.dirname(os.path.realpath(__file__)) and _checker_prepare_dir() which needlessly chdir() twice. - one logic branch failed to cleanup checkout directory - new flags provide additional flexibility --- ReviewBot.py | 11 + check_source.py | 246 ++++++++++++++++++++++ check_source.whitelist | 13 ++ leaper.py | 19 ++ osc-check_source.py | 458 ----------------------------------------- 5 files changed, 289 insertions(+), 458 deletions(-) create mode 100755 check_source.py create mode 100644 check_source.whitelist delete mode 100644 osc-check_source.py diff --git a/ReviewBot.py b/ReviewBot.py index 2adf9de1..9a7a29fe 100644 --- a/ReviewBot.py +++ b/ReviewBot.py @@ -91,6 +91,7 @@ class ReviewBot(object): self.prepare_review() for req in self.requests: self.logger.info("checking %s"%req.reqid) + self.request = req good = self.check_one_request(req) if self.review_mode == 'no': @@ -286,6 +287,16 @@ class ReviewBot(object): return (None, None) + def get_devel_project(self, project, package): + try: + m = osc.core.show_package_meta(self.apiurl, project, package) + node = ET.fromstring(''.join(m)).find('devel') + if node is not None: + return node.get('project'), node.get('package', None) + except urllib2.HTTPError: + pass + return None, None + def can_accept_review(self, request_id): """return True if there is a new review for the specified reviewer""" states = set() diff --git a/check_source.py b/check_source.py new file mode 100755 index 00000000..9ae69fb7 --- /dev/null +++ b/check_source.py @@ -0,0 +1,246 @@ +#!/usr/bin/python + +import os +import shutil +import subprocess +import sys + +try: + from xml.etree import cElementTree as ET +except ImportError: + import cElementTree as ET + +import osc.core +import urllib2 +import ReviewBot + +class CheckSource(ReviewBot.ReviewBot): + + SCRIPT_PATH = os.path.dirname(os.path.realpath(__file__)) + + def __init__(self, *args, **kwargs): + ReviewBot.ReviewBot.__init__(self, *args, **kwargs) + + self.ignore_devel = False + self.devel_whitelist_file = os.path.join(CheckSource.SCRIPT_PATH, 'check_source.whitelist') + self.devel_whitelist = None + self.review_team = 'opensuse-review-team' + self.repo_checker = 'factory-repo-checker' + self.skip_add_reviews = False + + def check_one_request(self, request): + # Copy original values to revert changes made to them. + self.review_messages = self.DEFAULT_REVIEW_MESSAGES.copy() + + if len(request.actions) != 1: + self.review_messages['declined'] = 'Only one action per request' + return False + + return super(CheckSource, self).check_one_request(request) + + def check_source_submission(self, source_project, source_package, source_revision, target_project, target_package): + super(CheckSource, self).check_source_submission(source_project, source_package, source_revision, target_project, target_package) + + if not self.ignore_devel: + # Check if target package exists and has devel project. + devel_project, devel_package = self.get_devel_project(target_project, target_package) + if devel_project: + if source_project != devel_project or source_package != devel_package: + self.review_messages['declined'] = 'Expected submission from devel package %s/%s' % (devel_project, devel_package) + return False + else: + # Check to see if other packages exist with the same source project + # which indicates that the project has already been used as devel. + if not self.is_devel_project(source_project, target_project): + self.review_messages['declined'] = '%s is not a devel project of %s, submit the package to a devel project first' % (source_project, target_project) + return False + + # Checkout and see if renaming package screws up version parsing. + dir = os.path.expanduser('~/co/%s' % self.request.reqid) + if os.path.exists(dir): + self.logger.warn('directory %s already exists' % dir) + shutil.rmtree(dir) + os.makedirs(dir) + os.chdir(dir) + + old_info = {'version': None} + try: + CheckSource.checkout_package(self.apiurl, target_project, target_package, pathname=dir, + server_service_files=True, expand_link=True) + shutil.rmtree(os.path.join(target_package, '.osc')) + os.rename(target_package, '_old') + old_info = self.package_source_parse(self.apiurl, target_project, target_package) + except urllib2.HTTPError: + self.logger.error('failed to checkout %s/%s' % (target_project, target_package)) + + CheckSource.checkout_package(self.apiurl, source_project, source_package, revision=source_revision, + pathname=dir, server_service_files=True, expand_link=True) + os.rename(source_package, target_package) + shutil.rmtree(os.path.join(target_package, '.osc')) + + new_info = self.package_source_parse(self.apiurl, source_project, source_package, source_revision) + if new_info['name'] != target_package: + shutil.rmtree(dir) + self.review_messages['declined'] = "A package submitted as %s has to build as 'Name: %s' - found Name '%s'" % (target_package, target_package, new_info['name']) + return False + + # Run source-checker.pl script and interpret output. + source_checker = os.path.join(CheckSource.SCRIPT_PATH, 'source-checker.pl') + civs = '' + new_version = None + if old_info['version'] and old_info['version'] != new_info['version']: + new_version = new_info['version'] + civs += "NEW_VERSION='{}' ".format(new_version) + civs += 'LC_ALL=C perl %s _old %s 2>&1' % (source_checker, target_package) + p = subprocess.Popen(civs, shell=True, stdout=subprocess.PIPE, close_fds=True) + ret = os.waitpid(p.pid, 0)[1] + checked = p.stdout.readlines() + + output = ' '.join(checked).translate(None, '\033') + os.chdir('/tmp') + + if ret != 0: + shutil.rmtree(dir) + self.review_messages['declined'] = "Output of check script:\n" + output + return False + + shutil.rmtree(dir) + self.review_messages['accepted'] = 'Check script succeeded' + + # Look for DIFFCOUNT in output. + if len(checked) and checked[-1].startswith('DIFFCOUNT'): + # This is a major break through in perl<->python communication! + diff = int(checked.pop().split(' ')[1]) + output = ' '.join(checked).translate(None, '\033') + if not new_version: + diff = 12345 + else: # e.g. new package + diff = 13579 + + if len(checked): + self.review_messages['accepted'] += "\n\nOutput of check script (non-fatal):\n" + output + + if not self.skip_add_reviews: + if diff > 8: + if self.review_team is not None: + self.add_review(self.request, by_group=self.review_team, msg='Please review sources') + + if self.repo_checker is not None: + self.add_review(self.request, by_user=self.repo_checker, msg='Please review build success') + + return True + + def is_devel_project(self, source_project, target_project): + # Load devel whitelist file if provided and check against before query. + if self.devel_whitelist_file and self.devel_whitelist is None: + self.devel_whitelist = open(self.devel_whitelist_file).read().splitlines() + if self.devel_whitelist is not None and source_project in self.devel_whitelist: + return True + + search = { + 'package': "@project='%s' and devel/@project='%s'" % (target_project, source_project), + } + result = osc.core.search(self.apiurl, **search) + return result['package'].attrib['matches'] != '0' + + @staticmethod + def checkout_package(*args, **kwargs): + _stdout = sys.stdout + sys.stdout = open(os.devnull, 'wb') + try: + result = osc.core.checkout_package(*args, **kwargs) + finally: + sys.stdout = _stdout + return result + + def package_source_parse(self, apiurl, project, package, revision=None): + query = {'view': 'info', 'parse': 1} + if revision: + query['rev'] = revision + url = osc.core.makeurl(self.apiurl, ['source', project, package], query) + + ret = {'name': None, 'version': None} + + try: + xml = ET.parse(osc.core.http_GET(url)).getroot() + except urllib2.HTTPError, e: + self.logger.error('ERROR in URL %s [%s]' % (url, e)) + return ret + + # ET boolean check fails. + if xml.find('name') is not None: + ret['name'] = xml.find('name').text + + if xml.find('version') is not None: + ret['version'] = xml.find('version').text + + return ret + + def check_action_add_role(self, request, action): + # Decline add_role request (assumed the bot acting on requests to Factory or similar). + message = 'Roles to packages are granted in the devel project, not in %s.' % action.tgt_project + + if action.tgt_package is not None: + message += ' Please send this request to %s/%s.' % self.get_devel_project(action.tgt_project, action.tgt_package) + + self.review_messages['declined'] = message + return False + + def check_action_delete(self, request, action): + try: + result = osc.core.show_project_sourceinfo(self.apiurl, action.tgt_project, True, (action.tgt_package)) + root = ET.fromstring(result) + except urllib2.HTTPError: + return None + + # Decline the delete request against linked package. + links = root.findall('linked') + if links is None or len(links) == 0: + self.review_messages['accepted'] = 'Unhandled request type %s' % action.type + return True + else: + linked = links[0] + linked_project = linked.get('project') + linked_package = linked.get('package') + self.review_messages['declined'] = "This is an incorrect request, it's a linked package to %s/%s" % (linked_project, linked_package) + return False + + def check_action__default(self, request, action): + self.review_messages['accepted'] = 'Unhandled request type %s.' % (action.type) + return True + +class CommandLineInterface(ReviewBot.CommandLineInterface): + + def __init__(self, *args, **kwargs): + ReviewBot.CommandLineInterface.__init__(self, args, kwargs) + self.clazz = CheckSource + + def get_optparser(self): + parser = ReviewBot.CommandLineInterface.get_optparser(self) + + parser.add_option('--ignore-devel', dest='ignore_devel', action='store_true', default=False, help='ignore devel projects for target package') + parser.add_option('--devel-whitelist', dest='devel_whitelist_file', metavar='FILE', help='file containing whitelisted projects (one per line)') + parser.add_option('--review-team', dest='review_team', metavar='GROUP', help='review team group added to requests with > 8 diff') + parser.add_option('--repo-checker', dest='repo_checker', metavar='USER', help='repo checker user added after accepted review') + parser.add_option('--skip-add-reviews', dest='skip_add_reviews', action='store_true', default=False, help='skip adding review after completing checks') + + return parser + + def setup_checker(self): + bot = ReviewBot.CommandLineInterface.setup_checker(self) + + if self.options.ignore_devel: + bot.ignore_devel = self.options.ignore_devel + if self.options.devel_whitelist_file: + bot.devel_whitelist_file = self.options.devel_whitelist_file + if self.options.review_team: + bot.review_team = self.options.review_team + if self.options.repo_checker: + bot.repo_checker = self.options.repo_checker + bot.skip_add_reviews = self.options.skip_add_reviews + + return bot + +if __name__ == "__main__": + app = CommandLineInterface() + sys.exit(app.main()) diff --git a/check_source.whitelist b/check_source.whitelist new file mode 100644 index 00000000..20ab9068 --- /dev/null +++ b/check_source.whitelist @@ -0,0 +1,13 @@ +Application:Dochazka +Application:ERP:Tryton:Factory +Novell:NTS +Virtualization:Appliances:Builder +devel:languages:D +devel:languages:pascal +isv:ownCloud:desktop +network:cluster +network:mail:zarafa +science:HPC +security:Stunnel +server:messaging +system:snappy diff --git a/leaper.py b/leaper.py index 773f3f82..f81e0a8d 100755 --- a/leaper.py +++ b/leaper.py @@ -76,6 +76,8 @@ class Leaper(ReviewBot.ReviewBot): 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.comment_marker_re = re.compile(r'') @@ -156,6 +158,7 @@ class Leaper(ReviewBot.ReviewBot): 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: @@ -366,6 +369,19 @@ class Leaper(ReviewBot.ReviewBot): 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): @@ -424,6 +440,7 @@ class CommandLineInterface(ReviewBot.CommandLineInterface): parser.add_option("--no-comment", dest='comment', action="store_false", default=True, help="don't actually post comments to obs") parser.add_option("--manual-version-updates", action="store_true", help="release manager must approve version updates") parser.add_option("--manual-maintenance-updates", action="store_true", help="release manager must approve maintenance updates") + parser.add_option("--check-source-group", dest="check_source_group", metavar="GROUP", help="group used by check_source.py bot which will be added as a reviewer should leaper checks pass") return parser @@ -434,6 +451,8 @@ class CommandLineInterface(ReviewBot.CommandLineInterface): bot.must_approve_version_updates = True if self.options.manual_maintenance_updates: bot.must_approve_maintenance_updates = True + if self.options.check_source_group: + bot.check_source_group = self.options.check_source_group bot.do_comments = self.options.comment return bot diff --git a/osc-check_source.py b/osc-check_source.py deleted file mode 100644 index 04d51433..00000000 --- a/osc-check_source.py +++ /dev/null @@ -1,458 +0,0 @@ -# -# (C) 2011 coolo@suse.de, Novell Inc, openSUSE.org -# Distribute under GPLv2 or GPLv3 -# -# Copy this script to ~/.osc-plugins/ or /var/lib/osc-plugins . -# Then try to run 'osc checker --help' to see the usage. - - -import os -import re -import shutil -import subprocess -import sys -import urllib2 -from xml.etree import cElementTree as ET - -from osc.core import checkout_package -from osc.core import http_GET -from osc.core import http_POST -# from osc.core import link_pac -from osc.core import makeurl -# from osc.core import show_upstream_rev_vrev -from osc import cmdln - -# Expand sys.path to search modules inside the pluging directory -PLUGINDIR = os.path.dirname(os.path.realpath(__file__.replace('.pyc', '.py'))) -sys.path.append(PLUGINDIR) -from osclib.conf import Config -from osclib.stagingapi import StagingAPI - - -# For a description of this decorator, visit -# http://www.imdb.com/title/tt0067756/ -def _silent_running(fn): - def _fn(*args, **kwargs): - _stdout = sys.stdout - sys.stdout = open(os.devnull, 'wb') - try: - result = fn(*args, **kwargs) - finally: - sys.stdout = _stdout - return result - return _fn - -checkout_pkg = _silent_running(checkout_package) - - -def _checker_parse(self, apiurl, project, package, revision=None, - brief=False, verbose=False): - query = {'view': 'info', 'parse': 1} - if revision: - query['rev'] = revision - url = makeurl(apiurl, ['source', project, package], query) - - ret = {'name': None, 'version': None} - - try: - xml = ET.parse(http_GET(url)).getroot() - except urllib2.HTTPError, e: - print('ERROR in URL %s [%s]' % (url, e)) - return ret - - # ET's boolean check is screwed - if xml.find('name') is not None: - ret['name'] = xml.find('name').text - - if xml.find('version') is not None: - ret['version'] = xml.find('version').text - - return ret - - -def _checker_change_review_state(self, opts, id_, newstate, - by_group='', by_user='', message='', - supersed=None): - """ taken from osc/osc/core.py, improved: - - verbose option added, - - empty by_user=& removed. - - numeric id_ can be int(). - """ - query = {'cmd': 'changereviewstate', 'newstate': newstate} - if by_group: - query['by_group'] = by_group - if by_user: - query['by_user'] = by_user - if supersed: - query['superseded_by'] = supersed - url = makeurl(opts.apiurl, ['request', str(id_)], query=query) - if opts.dry_run: - print 'dry-run: change review state: %s' % url - return - root = ET.parse(http_POST(url, data=message)).getroot() - return root.attrib['code'] - - -def _checker_prepare_dir(self, dir): - olddir = os.getcwd() - os.chdir(dir) - shutil.rmtree(".osc") - os.chdir(olddir) - - -def _checker_add_review(self, opts, id_, by_group=None, by_user=None, - msg=None): - query = {'cmd': 'addreview'} - if by_group: - query['by_group'] = by_group - elif by_user: - query['by_user'] = by_user - else: - raise Exception('we need either by_group or by_user') - - url = makeurl(opts.apiurl, ['request', str(id_)], query) - if opts.dry_run: - print 'dry-run: adding review %s' % url - return 0 - - try: - r = http_POST(url, data=msg) - except urllib2.HTTPError: - return 1 - - code = ET.parse(r).getroot().attrib['code'] - if code != 'ok': - raise Exception(r) - return 0 - - -def _checker_add_review_team(self, opts, id_): - return self._checker_add_review(opts, id_, by_group='opensuse-review-team', msg="Please review sources") - - -def _checker_get_verifymd5(self, opts, src_project, src_package, rev=None): - query = { - 'view': 'info', - - } - if rev: - query['rev'] = rev - url = makeurl(opts.apiurl, ('source', src_project, src_package), query=query) - try: - root = ET.parse(http_GET(url)).getroot() - except urllib2.HTTPError: - return None - - if root is not None: - srcmd5 = root.get('verifymd5') - return srcmd5 - - -def _checker_is_srcmd5_in_factory(self, opts, src_package, srcmd5): - if not srcmd5: - return False - - factory_srcmd5 = self._checker_get_verifymd5(opts, 'openSUSE:Factory', - src_package) - return srcmd5 == factory_srcmd5 - - -def _checker_accept_request(self, opts, id_, msg, diff=10000): - if diff > 8: - self._checker_add_review_team(opts, id_) - # else: - # self._checker_add_review(opts, id_, by_user='coolo', msg='Does it look harmless?') - - self._checker_add_review(opts, id_, by_user='factory-repo-checker', msg='Please review build success') - - self._checker_change_review_state(opts, id_, 'accepted', by_group='factory-auto', message=msg) - print("accepted " + msg) - - -class FakeAction(object): - def __init__(self, src_project, src_package, tgt_project, tgt_package, src_rev): - self.src_project = src_project - self.src_package = src_package - self.tgt_project = tgt_project - self.tgt_package = tgt_package - self.src_rev = src_rev - - -def _checker_one_request(self, rq, opts): - if (opts.verbose): - ET.dump(rq) - print(opts) - id_ = int(rq.get('id')) - act_id = 0 - actions = rq.findall('action') - if len(actions) > 1: - msg = '2 actions in one SR is not supported - https://github.com/openSUSE/osc-plugin-factory/fork_select' - self._checker_change_review_state(opts, id_, 'declined', by_group='factory-auto', message=msg) - print("declined " + msg) - return - - for act in actions: - act_id += 1 - _type = act.get('type') - if _type == "submit": - pkg = act.find('source').get('package') - prj = act.find('source').get('project') - rev = act.find('source').get('rev') - tprj = act.find('target').get('project') - tpkg = act.find('target').get('package') - - # Check in the srcmd5 is actually in Factory - srcmd5 = self._checker_get_verifymd5(opts, prj, pkg, rev=rev) - if self._checker_is_srcmd5_in_factory(opts, pkg, srcmd5): - msg = 'Found Factory sources' - self._checker_change_review_state(opts, id_, 'accepted', by_group='factory-auto', message=msg) - continue - - src = {'package': pkg, 'project': prj, 'rev': rev, 'error': None} - e = [] - if not pkg: - e.append('no source/package in request %d, action %d' % (id_, act_id)) - if not prj: - e.append('no source/project in request %d, action %d' % (id_, act_id)) - if e: - src.error = '; '.join(e) - - e = [] - if not tpkg: - e.append('no target/package in request %d, action %d; ' % (id_, act_id)) - if not prj: - e.append('no target/project in request %d, action %d; ' % (id_, act_id)) - # it is no error, if the target package dies not exist - - subm_id = "SUBMIT(%d):" % id_ - print ("\n%s %s/%s -> %s/%s" % (subm_id, - prj, pkg, - tprj, tpkg)) - - # Check if the package comes from openSUSE:Factory and - # have a release target (like openSUSE:13.2) - if prj == 'openSUSE:Factory' and tprj == '%s' % self.api.project: - fake_devel_prj = '%s:Devel' % self.api.project - - # _rev, _vrev = show_upstream_rev_vrev(opts.apiurl, prj, pkg, revision=rev, expand=True) - # assert _rev == rev, 'Revision is not the same' - # link_pac(src_project=prj, src_package=pkg, - # dst_project=fake_devel_prj, dst_package=tpkg, - # force=True, - # rev=_rev, vrev=_vrev) - - act = FakeAction(prj, pkg, tprj, tpkg, rev) - self.api.submit_to_prj(act, fake_devel_prj, force_enable_build=True) - self._checker_accept_request(opts, id_, 'The request is linked to %s' % fake_devel_prj, diff=0) - continue - - dpkg = self._checker_check_devel_package(opts, tprj, tpkg) - # white list - self._devel_projects['network:mail:zarafa/'] = 'x2go' - self._devel_projects['devel:languages:D/'] = 'd' - self._devel_projects['Novell:NTS'] = 'support' - self._devel_projects['Virtualization:Appliances:Builder/'] = 'python3-kiwi' - self._devel_projects['isv:ownCloud:desktop/'] = 'owncloud-client' - self._devel_projects['system:snappy/'] = 'snappy' - self._devel_projects['Application:ERP:Tryton:Factory/'] = 'tryton' - self._devel_projects['devel:languages:pascal/'] = 'fpc' - self._devel_projects['server:messaging/'] = 'libgloox' - self._devel_projects['Application:Dochazka/'] = 'perl-App-Dochazka-Common' - self._devel_projects['security:Stunnel/'] = 'stunnel' - self._devel_projects['network:cluster/' ] = 'cluster' - self._devel_projects['science:HPC/' ] = 'hpc' - if dpkg: - [dprj, dpkg] = dpkg.split('/') - else: - dprj = None - if dprj and (dprj != prj or dpkg != pkg) and ('IGNORE_DEVEL_PROJECTS' not in os.environ): - msg = "'%s/%s' is the devel package, submission is from '%s'" % (dprj, dpkg, prj) - print("declined " + msg) - self._checker_change_review_state(opts, id_, 'declined', by_group='factory-auto', message=msg) - continue - if not dprj and (prj + "/") not in self._devel_projects: - msg = "'%s' is not a valid devel project of %s - please pick one of the existent" % (prj, tprj) - print("declined " + msg) - self._checker_change_review_state(opts, id_, 'declined', by_group='factory-auto', message=msg) - continue - - dir = os.path.expanduser("~/co/%s" % str(id_)) - if os.path.exists(dir): - print("%s already exists" % dir) - continue - os.mkdir(dir) - os.chdir(dir) - - old_infos = {'version': None} - try: - checkout_pkg(opts.apiurl, tprj, tpkg, pathname=dir, - server_service_files=True, expand_link=True) - self._checker_prepare_dir(tpkg) - os.rename(tpkg, "_old") - old_infos = self._checker_parse(opts.apiurl, tprj, tpkg) - except urllib2.HTTPError: - print("failed to checkout %s/%s" % (tprj, tpkg)) - - checkout_pkg(opts.apiurl, prj, pkg, revision=rev, - pathname=dir, server_service_files=True, expand_link=True) - os.rename(pkg, tpkg) - self._checker_prepare_dir(tpkg) - - new_infos = self._checker_parse(opts.apiurl, prj, pkg, revision=rev) - if new_infos['name'] != tpkg: - msg = "A pkg submitted as %s has to build as 'Name: %s' - found Name '%s'" % (tpkg, tpkg, new_infos['name']) - self._checker_change_review_state(opts, id_, 'declined', by_group='factory-auto', message=msg) - continue - - sourcechecker = os.path.join(PLUGINDIR, 'source-checker.pl') - civs = "" - new_version = None - if old_infos['version'] and old_infos['version'] != new_infos['version']: - new_version = new_infos['version'] - civs += "NEW_VERSION='{}' ".format(new_version) - civs += "LC_ALL=C perl %s _old %s 2>&1" % (sourcechecker, tpkg) - p = subprocess.Popen(civs, shell=True, stdout=subprocess.PIPE, close_fds=True) - ret = os.waitpid(p.pid, 0)[1] - checked = p.stdout.readlines() - - output = ' '.join(checked).translate(None, '\033') - os.chdir("/tmp") - - if ret != 0: - msg = "Output of check script:\n" + output - self._checker_change_review_state(opts, id_, 'declined', by_group='factory-auto', message=msg) - print("declined " + msg) - shutil.rmtree(dir) - continue - - shutil.rmtree(dir) - msg = 'Check script succeeded' - if len(checked) and checked[-1].startswith('DIFFCOUNT'): - # this is a major break through in perl<->python communication! - diff = int(checked.pop().split(' ')[1]) - output = ' '.join(checked).translate(None, '\033') - if not new_version: - diff = 12345 - else: # e.g. new package - diff = 13579 - - if len(checked): - msg = msg + "\n\nOutput of check script (non-fatal):\n" + output - - if self._checker_accept_request(opts, id_, msg, diff=diff): - continue - - elif _type == "add_role": - tprj = act.find('target').get('project') - tpkg = act.find('target').get('package') - # decline add_role request to Factory - if tpkg is None: - msg = "Roles to packages are granted in the devel project, not in openSUSE:Factory." - else: - dpkg = self._checker_check_devel_package(opts, tprj, tpkg) - [dprj, dpkg] = dpkg.split('/') - msg = "Roles to packages are granted in the devel project, not in openSUSE:Factory. Please send this request to %s/%s" % (dprj, dpkg) - - self._checker_change_review_state(opts, id_, 'declined', - by_group='factory-auto', - message=msg) - elif _type == "delete": - tprj = act.find('target').get('project') - tpkg = act.find('target').get('package') - query = { - 'view': 'info', - 'nofilename': '1', - } - url = makeurl(opts.apiurl, ('source', tprj, tpkg), query=query) - try: - root = ET.parse(http_GET(url)).getroot() - except urllib2.HTTPError: - return - - # decline the delete request against linked package - links = root.findall('linked') - if links is None or len(links) == 0: - self._checker_change_review_state(opts, id_, 'accepted', - by_group='factory-auto', - message="Unchecked request type %s" % _type) - else: - linked = links[0] - lprj = linked.get('project') - lpkg = linked.get('package') - msg = "This is an incorrect request, it's a linked package to %s/%s" % (lprj, lpkg) - self._checker_change_review_state(opts, id_, 'declined', - by_group='factory-auto', - message=msg) - else: - self._checker_change_review_state(opts, id_, 'accepted', - by_group='factory-auto', - message="Unchecked request type %s" % _type) - - -def _checker_check_devel_package(self, opts, project, package): - if project not in self._devel_projects: - url = makeurl(opts.apiurl, ['search', 'package'], "match=[@project='%s']" % project) - root = ET.parse(http_GET(url)).getroot() - for p in root.findall('package'): - name = p.attrib['name'] - d = p.find('devel') - if d is not None: - dprj = d.attrib['project'] - self._devel_projects["%s/%s" % (project, name)] = "%s/%s" % (dprj, d.attrib['package']) - # for new packages to check - self._devel_projects[dprj + "/"] = 1 - elif not name.startswith("_product") and not name.startswith('preinstallimage') and not name == 'Test-DVD-x86_64': - print("NO DEVEL IN", name) - # mark we tried - self._devel_projects[project] = 1 - key = "%s/%s" % (project, package) - if key in self._devel_projects: - return self._devel_projects[key] - - -@cmdln.option('-v', '--verbose', action='store_true', help="verbose output") -@cmdln.option('-p', '--project', dest='project', metavar='PROJECT', default='Factory', - help='select a different project instead of openSUSE:Factory') -@cmdln.option('-n', '--dry-run', action='store_true', help="dry run") -def do_check_source(self, subcmd, opts, *args): - """${cmd_name}: checker review of submit requests. - - Usage: - osc check_source [OPT] [list] [FILTER|PACKAGE_SRC] - Shows pending review requests and their current state. - - ${cmd_option_list} - """ - - self._devel_projects = {} - opts.apiurl = self.get_api_url() - - Config('openSUSE:%s' % opts.project) - self.api = StagingAPI(opts.apiurl, 'openSUSE:%s' % opts.project) - - if len(args) and args[0] == 'skip': - for id_ in args[1:]: - self._checker_accept_request(opts, id_, 'skip review') - return - ids = {} - for a in args: - if re.match('\d+', a): - ids[a] = 1 - - if not ids: - review = "@by_group='factory-auto'+and+@state='new'" - target = "@project='{}'".format(self.api.project) - target_nf = "@project='{}:NonFree'".format(self.api.project) - url = makeurl(opts.apiurl, ('search', 'request'), - "match=state/@name='review'+and+review[%s]+and+(target[%s]+or+target[%s])" % (review, target, target_nf)) - root = ET.parse(http_GET(url)).getroot() - for rq in root.findall('request'): - self._checker_one_request(rq, opts) - else: - # we have a list, use them. - for id_ in ids: - url = makeurl(opts.apiurl, ('request', id_)) - f = http_GET(url) - xml = ET.parse(f) - root = xml.getroot() - self._checker_one_request(root, opts)