예제 #1
0
class TestReviewBotComment(OBSLocal.TestCase):
    def setUp(self):
        super(TestReviewBotComment, self).setUp()
        self.api = CommentAPI(self.apiurl)
        self.wf = OBSLocal.FactoryWorkflow()
        self.wf.create_user('factory-auto')
        self.project = self.wf.create_project(PROJECT)

        # Ensure different test runs operate in unique namespace.
        self.bot = '::'.join([type(self).__name__, str(random.getrandbits(8))])
        self.review_bot = ReviewBot(self.apiurl,
                                    logger=logging.getLogger(self.bot))
        self.review_bot.bot_name = self.bot

        self.osc_user('factory-auto')

    def tearDown(self):
        self.api.delete_from(project_name=PROJECT)
        self.assertFalse(len(self.api.get_comments(project_name=PROJECT)))
        self.osc_user('Admin')
        del self.wf

    def test_basic_logger(self):
        self.assertFalse(self.comments_filtered(self.bot)[0])

        # Initial comment.
        self.review_bot.comment_handler_add()
        self.review_bot.logger.info('something interesting')
        self.review_bot.comment_write(project=PROJECT)
        comment, _ = self.comments_filtered(self.bot)
        self.assertTrue(comment['comment'].endswith('something interesting'))

        # Second comment with extra line.
        self.review_bot.comment_handler_add()
        self.review_bot.logger.info('something interesting')
        self.review_bot.logger.info('something extra')
        self.review_bot.comment_write(project=PROJECT)
        comment, _ = self.comments_filtered(self.bot)
        self.assertTrue(comment['comment'].endswith('something extra'))

    def test_workflow(self):
        comment_count = len(self.api.get_comments(project_name=PROJECT))
        self.assertFalse(self.comments_filtered(self.bot)[0])

        # Initial comment.
        info = {'state': 'seen', 'result': 'failed'}
        info_extra = {'build': '1'}
        info_merged = info.copy()
        info_merged.update(info_extra)
        self.review_bot.comment_write(state='seen',
                                      result='failed',
                                      identical=True,
                                      info_extra=info_extra,
                                      info_extra_identical=False,
                                      project=PROJECT,
                                      message=COMMENT)
        comment, info_parsed = self.comments_filtered(self.bot)
        self.assertTrue(comment['comment'].endswith(COMMENT))
        self.assertEqual(info_parsed, info_merged)

        # Only build change (expect no change).
        info_extra = {'build': '2'}
        self.review_bot.comment_write(state='seen',
                                      result='failed',
                                      identical=True,
                                      info_extra=info_extra,
                                      info_extra_identical=False,
                                      project=PROJECT,
                                      message=COMMENT)
        comment, info_parsed = self.comments_filtered(self.bot)
        self.assertTrue(comment['comment'].endswith(COMMENT))
        self.assertEqual(info_parsed, info_merged)

        # Build and comment (except comment replacement).
        info_extra = {'build': '3'}
        info_merged.update(info_extra)
        self.review_bot.comment_write(state='seen',
                                      result='failed',
                                      identical=True,
                                      info_extra=info_extra,
                                      info_extra_identical=False,
                                      project=PROJECT,
                                      message=COMMENT + '3')
        comment, info_parsed = self.comments_filtered(self.bot)
        self.assertTrue(comment['comment'].endswith(COMMENT + '3'))
        self.assertEqual(info_parsed, info_merged)

        # Final build (except comment replacement).
        info_extra = {'build': '4'}
        info_merged.update(info_extra)
        self.review_bot.comment_write(state='seen',
                                      result='failed',
                                      identical=True,
                                      info_extra=info_extra,
                                      info_extra_identical=True,
                                      project=PROJECT,
                                      message=COMMENT + '4')
        comment, info_parsed = self.comments_filtered(self.bot)
        self.assertTrue(comment['comment'].endswith(COMMENT + '4'))
        self.assertEqual(info_parsed, info_merged)

        # Final build (except comment replacement).
        info = {'state': 'done', 'result': 'passed'}
        info_extra = {'build': '5'}
        info_merged = info.copy()
        info_merged.update(info_extra)
        self.review_bot.comment_write(state='done',
                                      result='passed',
                                      identical=True,
                                      info_extra=info_extra,
                                      info_extra_identical=True,
                                      only_replace=True,
                                      project=PROJECT,
                                      message=COMMENT + '5')
        comment, info_parsed = self.comments_filtered(self.bot)
        self.assertTrue(comment['comment'].endswith(COMMENT + '5'))
        self.assertEqual(info_parsed, info_merged)

        # Should never be more than one new comment.
        self.assertEqual(len(self.api.get_comments(project_name=PROJECT)),
                         comment_count + 1)

    def test_only_replace_none(self):
        self.review_bot.comment_write(only_replace=True,
                                      project=PROJECT,
                                      message=COMMENT)
        self.assertFalse(self.comments_filtered(self.bot)[0])

    def test_dryrun(self):
        # dryrun = True, no comment.
        self.review_bot.dryrun = True
        self.review_bot.comment_write(project=PROJECT, message=COMMENT)
        self.assertFalse(self.comments_filtered(self.bot)[0])

        # dryrun = False, a comment.
        self.review_bot.dryrun = False
        self.review_bot.comment_write(project=PROJECT, message=COMMENT)
        self.assertTrue(self.comments_filtered(self.bot)[0])

        # dryrun = True, no replacement.
        self.review_bot.dryrun = True
        self.review_bot.comment_write(state='changed',
                                      project=PROJECT,
                                      message=COMMENT)
        _, info = self.comments_filtered(self.bot)
        self.assertEqual(info['state'], 'done')

        # dryrun = False, replacement.
        self.review_bot.dryrun = False
        self.review_bot.comment_write(state='changed',
                                      project=PROJECT,
                                      message=COMMENT)
        _, info = self.comments_filtered(self.bot)
        self.assertEqual(info['state'], 'changed')

    def test_bot_name_suffix(self):
        suffix1 = 'suffix1'
        bot_suffixed1 = '::'.join([self.bot, suffix1])

        suffix2 = 'suffix2'
        bot_suffixed2 = '::'.join([self.bot, suffix2])

        self.review_bot.comment_write(bot_name_suffix=suffix1,
                                      project=PROJECT,
                                      message=COMMENT)
        self.assertFalse(self.comments_filtered(self.bot)[0])
        self.assertTrue(self.comments_filtered(bot_suffixed1)[0])
        self.assertFalse(self.comments_filtered(bot_suffixed2)[0])

        self.review_bot.comment_write(bot_name_suffix=suffix2,
                                      project=PROJECT,
                                      message=COMMENT)
        self.assertFalse(self.comments_filtered(self.bot)[0])
        self.assertTrue(self.comments_filtered(bot_suffixed1)[0])
        self.assertTrue(self.comments_filtered(bot_suffixed2)[0])

        self.review_bot.comment_write(bot_name_suffix=suffix1,
                                      project=PROJECT,
                                      message=COMMENT + '\nnew')
        comment, _ = self.comments_filtered(bot_suffixed1)
        self.assertTrue(comment['comment'].endswith(COMMENT + '\nnew'))

        comment, _ = self.comments_filtered(bot_suffixed2)
        self.assertTrue(comment['comment'].endswith(COMMENT))

    def comments_filtered(self, bot):
        comments = self.api.get_comments(project_name=PROJECT)
        return self.api.comment_find(comments, bot)
예제 #2
0
class TestCommentOBS(OBSLocal.TestCase):
    def setUp(self):
        super(TestCommentOBS, self).setUp()
        self.wf = OBSLocal.FactoryWorkflow()
        self.wf.create_user('factory-auto')
        self.wf.create_user('repo-checker')
        self.wf.create_user('staging-bot')
        self.wf.create_group('factory-staging', ['staging-bot'])
        self.wf.create_project(PROJECT,
                               maintainer={'groups': ['factory-staging']})
        self.api = CommentAPI(self.apiurl)
        # Ensure different test runs operate in unique namespace.
        self.bot = '::'.join([type(self).__name__, str(random.getrandbits(8))])

    def tearDown(self):
        self.osc_user('Admin')
        del self.wf

    def test_basic(self):
        self.osc_user('staging-bot')

        self.assertFalse(self.comments_filtered(self.bot)[0])

        self.assertTrue(
            self.api.add_comment(project_name=PROJECT,
                                 comment=self.api.add_marker(
                                     COMMENT, self.bot)))
        comment, _ = self.comments_filtered(self.bot)
        self.assertTrue(comment)

        self.assertTrue(self.api.delete(comment['id']))
        self.assertFalse(self.comments_filtered(self.bot)[0])

    def test_delete_nested(self):
        self.osc_user('staging-bot')
        comment_marked = self.api.add_marker(COMMENT, self.bot)

        # Allow for existing comments by basing assertion on delta from initial count.
        comment_count = len(self.api.get_comments(project_name=PROJECT))
        self.assertFalse(self.comments_filtered(self.bot)[0])

        self.assertTrue(
            self.api.add_comment(project_name=PROJECT, comment=comment_marked))
        comment, _ = self.comments_filtered(self.bot)
        self.assertTrue(comment)

        for i in range(0, 3):
            self.assertTrue(
                self.api.add_comment(project_name=PROJECT,
                                     comment=comment_marked,
                                     parent_id=comment['id']))

        comments = self.api.get_comments(project_name=PROJECT)
        parented_count = 0
        for comment in comments.values():
            if comment['parent']:
                parented_count += 1

        self.assertEqual(parented_count, 3)
        self.assertTrue(len(comments) == comment_count + 4)

        self.api.delete_from(project_name=PROJECT)
        self.assertFalse(len(self.api.get_comments(project_name=PROJECT)))

    def test_delete_batch(self):
        users = ['factory-auto', 'repo-checker', 'staging-bot']
        for user in users:
            self.osc_user(user)
            print('logged in as ', user)
            bot = '::'.join([self.bot, user])
            comment = self.api.add_marker(COMMENT, bot)

            self.assertFalse(self.comments_filtered(bot)[0])
            self.assertTrue(
                self.api.add_comment(project_name=PROJECT, comment=comment))
            self.assertTrue(self.comments_filtered(bot)[0])

        # Allow for existing comments by basing assertion on delta from initial count.
        comment_count = len(self.api.get_comments(project_name=PROJECT))
        self.assertTrue(comment_count >= len(users))

        self.api.delete_from_where_user(users[0], project_name=PROJECT)
        self.assertTrue(
            len(self.api.get_comments(project_name=PROJECT)) == comment_count -
            1)

        self.api.delete_from(project_name=PROJECT)
        self.assertFalse(len(self.api.get_comments(project_name=PROJECT)))

    def comments_filtered(self, bot):
        comments = self.api.get_comments(project_name=PROJECT)
        return self.api.comment_find(comments, bot)
예제 #3
0
class AcceptCommand(object):
    def __init__(self, api):
        self.api = api
        self.comment = CommentAPI(self.api.apiurl)

    def find_new_requests(self, project):
        query = "match=state/@name='new'+and+(action/target/@project='{}'+and+action/@type='submit')".format(project)
        url = self.api.makeurl(['search', 'request'], query)

        f = http_GET(url)
        root = ET.parse(f).getroot()

        rqs = []
        for rq in root.findall('request'):
            pkgs = []
            actions = rq.findall('action')
            for action in actions:
                targets = action.findall('target')
                for t in targets:
                    pkgs.append(str(t.get('package')))

            rqs.append({'id': int(rq.get('id')), 'packages': pkgs})
        return rqs

    def perform(self, project, force=False):
        """Accept the staging project for review and submit to Factory /
        openSUSE 13.2 ...

        Then disable the build to disabled
        :param project: staging project we are working with

        """

        status = self.api.check_project_status(project)

        if not status:
            print('The project "{}" is not yet acceptable.'.format(project))
            if not force:
                return False

        meta = self.api.get_prj_pseudometa(project)
        requests = []
        packages = []
        for req in meta['requests']:
            self.api.rm_from_prj(project, request_id=req['id'], msg='ready to accept')
            requests.append(req['id'])
            packages.append(req['package'])
            msg = 'Accepting staging review for {}'.format(req['package'])
            print(msg)

            oldspecs = self.api.get_filelist_for_package(pkgname=req['package'],
                                                         project=self.api.project,
                                                         extension='spec')
            change_request_state(self.api.apiurl,
                                 str(req['id']),
                                 'accepted',
                                 message='Accept to %s' % self.api.project)
            self.create_new_links(self.api.project, req['package'], oldspecs)

        # A single comment should be enough to notify everybody, since
        # they are already mentioned in the comments created by
        # select/unselect
        pkg_list = ", ".join(packages)
        cmmt = 'Project "{}" accepted.' \
               ' The following packages have been submitted to {}: {}.'.format(project,
                                                                               self.api.project,
                                                                               pkg_list)
        self.comment.add_comment(project_name=project, comment=cmmt)

        # XXX CAUTION - AFAIK the 'accept' command is expected to clean the messages here.
        self.comment.delete_from(project_name=project)

        self.api.build_switch_prj(project, 'disable')
        if self.api.item_exists(project + ':DVD'):
            self.api.build_switch_prj(project + ':DVD', 'disable')

        return True

    def cleanup(self, project):
        if not self.api.item_exists(project):
            return False

        pkglist = self.api.list_packages(project)
        clean_list = set(pkglist) - set(self.api.cstaging_nocleanup)

        for package in clean_list:
            print "[cleanup] deleted %s/%s" % (project, package)
            delete_package(self.api.apiurl, project, package, force=True, msg="autocleanup")
        return True

    def accept_other_new(self):
        changed = False
        rqlist = self.find_new_requests(self.api.project)
        if self.api.cnonfree:
            rqlist += self.find_new_requests(self.api.cnonfree)

        for req in rqlist:
            oldspecs = self.api.get_filelist_for_package(pkgname=req['packages'][0], project=self.api.project, extension='spec')
            print 'Accepting request %d: %s' % (req['id'], ','.join(req['packages']))
            change_request_state(self.api.apiurl, str(req['id']), 'accepted', message='Accept to %s' % self.api.project)
            # Check if all .spec files of the package we just accepted has a package container to build
            self.create_new_links(self.api.project, req['packages'][0], oldspecs)
            changed = True

        return changed

    def create_new_links(self, project, pkgname, oldspeclist):
        filelist = self.api.get_filelist_for_package(pkgname=pkgname, project=project, extension='spec')
        removedspecs = set(oldspeclist) - set(filelist)
        for spec in removedspecs:
            # Deleting all the packages that no longer have a .spec file
            url = self.api.makeurl(['source', project, spec[:-5]])
            print "Deleting package %s from project %s" % (spec[:-5], project)
            try:
                http_DELETE(url)
            except urllib2.HTTPError, err:
                if err.code == 404:
                    # the package link was not yet created, which was likely a mistake from earlier
                    pass
                else:
                    # If the package was there bug could not be delete, raise the error
                    raise
        if len(filelist) > 1:
            # There is more than one .spec file in the package; link package containers as needed
            origmeta = self.api.load_file_content(project, pkgname, '_meta')
            for specfile in filelist:
                package = specfile[:-5]  # stripping .spec off the filename gives the packagename
                if package == pkgname:
                    # This is the original package and does not need to be linked to itself
                    continue
                # Check if the target package already exists, if it does not, we get a HTTP error 404 to catch
                if not self.api.item_exists(project, package):
                    print "Creating new package %s linked to %s" % (package, pkgname)
                    # new package does not exist. Let's link it with new metadata
                    newmeta = re.sub(r'(<package.*name=.){}'.format(pkgname),
                                     r'\1{}'.format(package),
                                     origmeta)
                    newmeta = re.sub(r'<devel.*>',
                                     r'<devel package="{}"/>'.format(pkgname),
                                     newmeta)
                    newmeta = re.sub(r'<bcntsynctag>.*</bcntsynctag>',
                                     r'',
                                     newmeta)
                    newmeta = re.sub(r'</package>',
                                     r'<bcntsynctag>{}</bcntsynctag></package>'.format(pkgname),
                                     newmeta)
                    self.api.save_file_content(project, package, '_meta', newmeta)
                    link = "<link package=\"{}\" cicount=\"copy\" />".format(pkgname)
                    self.api.save_file_content(project, package, '_link', link)
        return True
예제 #4
0
class AcceptCommand(object):
    def __init__(self, api):
        self.api = api
        self.comment = CommentAPI(self.api.apiurl)

    def find_new_requests(self, project):
        query = "match=state/@name='new'+and+(action/target/@project='{}'+and+action/@type='submit')".format(project)
        url = self.api.makeurl(['search', 'request'], query)

        f = http_GET(url)
        root = ET.parse(f).getroot()

        rqs = []
        for rq in root.findall('request'):
            pkgs = []
            actions = rq.findall('action')
            for action in actions:
                targets = action.findall('target')
                for t in targets:
                    pkgs.append(str(t.get('package')))

            rqs.append({'id': int(rq.get('id')), 'packages': pkgs})
        return rqs

    def perform(self, project):
        """
        Accept the staging LETTER for review and submit to factory
        Then disable the build to disabled
        :param project: staging project we are working with
        """

        status = self.api.check_project_status(project)

        if not status:
            print('The project "{}" is not yet acceptable.'.format(project))
            return False

        meta = self.api.get_prj_pseudometa(project)
        requests = []
        packages = []
        for req in meta['requests']:
            self.api.rm_from_prj(project, request_id=req['id'], msg='ready to accept')
            requests.append(req['id'])
            packages.append(req['package'])
            msg = 'Accepting staging review for {}'.format(req['package'])
            print(msg)

        for req in requests:
            change_request_state(self.api.apiurl, str(req), 'accepted', message='Accept to factory')

        # A single comment should be enough to notify everybody, since they are
        # already mentioned in the comments created by select/unselect
        pkg_list = ", ".join(packages)
        cmmt = 'Project "{}" accepted. The following packages have been submitted to factory: {}.'.format(project, pkg_list)
        self.comment.add_comment(project_name=project, comment=cmmt)

        # XXX CAUTION - AFAIK the 'accept' command is expected to clean the messages here.
        self.comment.delete_from(project_name=project)

        self.api.build_switch_prj(project, 'disable')
        if self.api.project_exists(project + ':DVD'):
            self.api.build_switch_prj(project + ':DVD', 'disable')

        return True

    def accept_other_new(self):
        changed = False
        for req in self.find_new_requests('openSUSE:{}'.format(self.api.opensuse)):
            print 'Accepting request %d: %s' % (req['id'], ','.join(req['packages']))
            change_request_state(self.api.apiurl, str(req['id']), 'accepted', message='Accept to factory')
            changed = True

        return changed

    def update_factory_version(self):
        """Update openSUSE (Factory, 13.2, ...)  version if is necessary."""
        project = 'openSUSE:{}'.format(self.api.opensuse)
        url = self.api.makeurl(['source', project, '_product', 'openSUSE.product'])

        product = http_GET(url).read()
        curr_version = date.today().strftime('%Y%m%d')
        new_product = re.sub(r'<version>\d{8}</version>', '<version>%s</version>' % curr_version, product)

        if product != new_product:
            http_PUT(url + '?comment=Update+version', data=new_product)
class TestCommentOBS(OBSLocalTestCase):
    def setUp(self):
        super(TestCommentOBS, self).setUp()
        self.api = CommentAPI(self.apiurl)
        # Ensure different test runs operate in unique namespace.
        self.bot = '::'.join([type(self).__name__, str(random.getrandbits(8))])

    def test_basic(self):
        self.osc_user('staging-bot')

        self.assertFalse(self.comments_filtered(self.bot)[0])

        self.assertTrue(self.api.add_comment(
            project_name=PROJECT, comment=self.api.add_marker(COMMENT, self.bot)))
        comment, _ = self.comments_filtered(self.bot)
        self.assertTrue(comment)

        self.assertTrue(self.api.delete(comment['id']))
        self.assertFalse(self.comments_filtered(self.bot)[0])

    def test_delete_nested(self):
        self.osc_user('staging-bot')
        comment_marked = self.api.add_marker(COMMENT, self.bot)

        # Allow for existing comments by basing assertion on delta from initial count.
        comment_count = len(self.api.get_comments(project_name=PROJECT))
        self.assertFalse(self.comments_filtered(self.bot)[0])

        self.assertTrue(self.api.add_comment(project_name=PROJECT, comment=comment_marked))
        comment, _ = self.comments_filtered(self.bot)
        self.assertTrue(comment)

        for i in range(0, 3):
            self.assertTrue(self.api.add_comment(
                project_name=PROJECT, comment=comment_marked, parent_id=comment['id']))

        comments = self.api.get_comments(project_name=PROJECT)
        parented_count = 0
        for comment in comments.values():
            if comment['parent']:
                parented_count += 1

        self.assertEqual(parented_count, 3)
        self.assertTrue(len(comments) == comment_count + 4)

        self.api.delete_from(project_name=PROJECT)
        self.assertFalse(len(self.api.get_comments(project_name=PROJECT)))

    def test_delete_batch(self):
        users = ['factory-auto', 'repo-checker', 'staging-bot']
        for user in users:
            self.osc_user(user)
            from osc import conf
            bot = '::'.join([self.bot, user])
            comment = self.api.add_marker(COMMENT, bot)

            self.assertFalse(self.comments_filtered(bot)[0])
            self.assertTrue(self.api.add_comment(project_name=PROJECT, comment=comment))
            self.assertTrue(self.comments_filtered(bot)[0])

        # Allow for existing comments by basing assertion on delta from initial count.
        comment_count = len(self.api.get_comments(project_name=PROJECT))
        self.assertTrue(comment_count >= len(users))

        self.api.delete_from_where_user(users[0], project_name=PROJECT)
        self.assertTrue(len(self.api.get_comments(project_name=PROJECT)) == comment_count - 1)

        self.api.delete_from(project_name=PROJECT)
        self.assertFalse(len(self.api.get_comments(project_name=PROJECT)))

    def comments_filtered(self, bot):
        comments = self.api.get_comments(project_name=PROJECT)
        return self.api.comment_find(comments, bot)
예제 #6
0
class AcceptCommand(object):
    def __init__(self, api):
        self.api = api
        self.comment = CommentAPI(self.api.apiurl)

    def find_new_requests(self, project):
        query = "match=state/@name='new'+and+(action/target/@project='{}'+and+action/@type='submit')".format(project)
        url = self.api.makeurl(['search', 'request'], query)

        f = http_GET(url)
        root = ET.parse(f).getroot()

        rqs = []
        for rq in root.findall('request'):
            pkgs = []
            actions = rq.findall('action')
            for action in actions:
                targets = action.findall('target')
                for t in targets:
                    pkgs.append(str(t.get('package')))

            rqs.append({'id': int(rq.get('id')), 'packages': pkgs})
        return rqs

    def perform(self, project):
        """Accept the staging project for review and submit to Factory /
        openSUSE 13.2 ...

        Then disable the build to disabled
        :param project: staging project we are working with

        """

        status = self.api.check_project_status(project)

        if not status:
            print('The project "{}" is not yet acceptable.'.format(project))
            return False

        meta = self.api.get_prj_pseudometa(project)
        requests = []
        packages = []
        for req in meta['requests']:
            self.api.rm_from_prj(project, request_id=req['id'], msg='ready to accept')
            requests.append(req['id'])
            packages.append(req['package'])
            msg = 'Accepting staging review for {}'.format(req['package'])
            print(msg)

            oldspecs = self.api.get_filelist_for_package(pkgname=req['package'],
                                                         project=self.api.project,
                                                         extension='spec')
            change_request_state(self.api.apiurl,
                                 str(req['id']),
                                 'accepted',
                                 message='Accept to %s' % self.api.project)
            self.create_new_links(self.api.project, req['package'], oldspecs)

        # A single comment should be enough to notify everybody, since
        # they are already mentioned in the comments created by
        # select/unselect
        pkg_list = ", ".join(packages)
        cmmt = 'Project "{}" accepted.' \
               ' The following packages have been submitted to {}: {}.'.format(project,
                                                                               self.api.project,
                                                                               pkg_list)
        self.comment.add_comment(project_name=project, comment=cmmt)

        # XXX CAUTION - AFAIK the 'accept' command is expected to clean the messages here.
        self.comment.delete_from(project_name=project)

        self.api.build_switch_prj(project, 'disable')
        if self.api.item_exists(project + ':DVD'):
            self.api.build_switch_prj(project + ':DVD', 'disable')

        return True

    def accept_other_new(self):
        changed = False
        rqlist = self.find_new_requests(self.api.project)
        if self.api.cnonfree:
            rqlist += self.find_new_requests(self.api.cnonfree)

        for req in rqlist:
            oldspecs = self.api.get_filelist_for_package(pkgname=req['packages'][0], project=self.api.project, extension='spec')
            print 'Accepting request %d: %s' % (req['id'], ','.join(req['packages']))
            change_request_state(self.api.apiurl, str(req['id']), 'accepted', message='Accept to %s' % self.api.project)
            # Check if all .spec files of the package we just accepted has a package container to build
            self.create_new_links(self.api.project, req['packages'][0], oldspecs)
            changed = True

        return changed

    def create_new_links(self, project, pkgname, oldspeclist):
        filelist = self.api.get_filelist_for_package(pkgname=pkgname, project=project, extension='spec')
        removedspecs = set(oldspeclist) - set(filelist)
        for spec in removedspecs:
            # Deleting all the packages that no longer have a .spec file
            url = self.api.makeurl(['source', project, spec[:-5]])
            print "Deleting package %s from project %s" % (spec[:-5], project)
            try:
                http_DELETE(url)
            except urllib2.HTTPError, err:
                if err.code == 404:
                    # the package link was not yet created, which was likely a mistake from earlier
                    pass
                else:
                    # If the package was there bug could not be delete, raise the error
                    raise
        if len(filelist) > 1:
            # There is more than one .spec file in the package; link package containers as needed
            origmeta = self.api.load_file_content(project, pkgname, '_meta')
            for specfile in filelist:
                package = specfile[:-5]  # stripping .spec off the filename gives the packagename
                if package == pkgname:
                    # This is the original package and does not need to be linked to itself
                    continue
                # Check if the target package already exists, if it does not, we get a HTTP error 404 to catch
                if not self.api.item_exists(project, package):
                    print "Creating new package %s linked to %s" % (package, pkgname)
                    # new package does not exist. Let's link it with new metadata
                    newmeta = re.sub(r'(<package.*name=.){}'.format(pkgname),
                                     r'\1{}'.format(package),
                                     origmeta)
                    newmeta = re.sub(r'<devel.*>\$',
                                     r'<devel package=\'{}\'/>'.format(pkgname),
                                     newmeta)
                    newmeta = re.sub(r'<bcntsynctag>.*</bcntsynctag>',
                                     r'',
                                     newmeta)
                    newmeta = re.sub(r'</package>',
                                     r'<bcntsynctag>{}</bcntsynctag></package>'.format(pkgname),
                                     newmeta)
                    self.api.save_file_content(project, package, '_meta', newmeta)
                    link = "<link package=\"{}\" cicount=\"copy\" />".format(pkgname)
                    self.api.save_file_content(project, package, '_link', link)
        return True
예제 #7
0
class TestReviewBotComment(OBSLocal.OBSLocalTestCase):
    def setUp(self):
        super(TestReviewBotComment, self).setUp()
        self.api = CommentAPI(self.apiurl)

        # Ensure different test runs operate in unique namespace.
        self.bot = '::'.join([type(self).__name__, str(random.getrandbits(8))])
        self.review_bot = ReviewBot(self.apiurl, logger=logging.getLogger(self.bot))
        self.review_bot.bot_name = self.bot

        self.osc_user('factory-auto')

    def tearDown(self):
        self.api.delete_from(project_name=PROJECT)
        self.assertFalse(len(self.api.get_comments(project_name=PROJECT)))

    def test_basic_logger(self):
        comment_count = len(self.api.get_comments(project_name=PROJECT))
        self.assertFalse(self.comments_filtered(self.bot)[0])

        # Initial comment.
        self.review_bot.comment_handler_add()
        self.review_bot.logger.info('something interesting')
        self.review_bot.comment_write(project=PROJECT)
        comment, _ = self.comments_filtered(self.bot)
        self.assertTrue(comment['comment'].endswith('something interesting'))

        # Second comment with extra line.
        self.review_bot.comment_handler_add()
        self.review_bot.logger.info('something interesting')
        self.review_bot.logger.info('something extra')
        self.review_bot.comment_write(project=PROJECT)
        comment, _ = self.comments_filtered(self.bot)
        self.assertTrue(comment['comment'].endswith('something extra'))

    def test_workflow(self):
        comment_count = len(self.api.get_comments(project_name=PROJECT))
        self.assertFalse(self.comments_filtered(self.bot)[0])

        # Initial comment.
        info = {'state': 'seen', 'result': 'failed'}
        info_extra = {'build': '1'}
        info_merged = info.copy()
        info_merged.update(info_extra)
        self.review_bot.comment_write(state='seen', result='failed', identical=True,
                                      info_extra=info_extra, info_extra_identical=False,
                                      project=PROJECT, message=COMMENT)
        comment, info_parsed = self.comments_filtered(self.bot)
        self.assertTrue(comment['comment'].endswith(COMMENT))
        self.assertEqual(info_parsed, info_merged)

        # Only build change (expect no change).
        info_extra = {'build': '2'}
        self.review_bot.comment_write(state='seen', result='failed', identical=True,
                                      info_extra=info_extra, info_extra_identical=False,
                                      project=PROJECT, message=COMMENT)
        comment, info_parsed = self.comments_filtered(self.bot)
        self.assertTrue(comment['comment'].endswith(COMMENT))
        self.assertEqual(info_parsed, info_merged)

        # Build and comment (except comment replacement).
        info_extra = {'build': '3'}
        info_merged.update(info_extra)
        self.review_bot.comment_write(state='seen', result='failed', identical=True,
                                      info_extra=info_extra, info_extra_identical=False,
                                      project=PROJECT, message=COMMENT + '3')
        comment, info_parsed = self.comments_filtered(self.bot)
        self.assertTrue(comment['comment'].endswith(COMMENT + '3'))
        self.assertEqual(info_parsed, info_merged)

        # Final build (except comment replacement).
        info_extra = {'build': '4'}
        info_merged.update(info_extra)
        self.review_bot.comment_write(state='seen', result='failed', identical=True,
                                      info_extra=info_extra, info_extra_identical=True,
                                      project=PROJECT, message=COMMENT + '4')
        comment, info_parsed = self.comments_filtered(self.bot)
        self.assertTrue(comment['comment'].endswith(COMMENT + '4'))
        self.assertEqual(info_parsed, info_merged)

        # Final build (except comment replacement).
        info = {'state': 'done', 'result': 'passed'}
        info_extra = {'build': '5'}
        info_merged = info.copy()
        info_merged.update(info_extra)
        self.review_bot.comment_write(state='done', result='passed', identical=True,
                                      info_extra=info_extra, info_extra_identical=True,
                                      only_replace=True,
                                      project=PROJECT, message=COMMENT + '5')
        comment, info_parsed = self.comments_filtered(self.bot)
        self.assertTrue(comment['comment'].endswith(COMMENT + '5'))
        self.assertEqual(info_parsed, info_merged)

        # Should never be more than one new comment.
        self.assertEqual(len(self.api.get_comments(project_name=PROJECT)), comment_count + 1)

    def test_only_replace_none(self):
        self.review_bot.comment_write(only_replace=True,
                                      project=PROJECT, message=COMMENT)
        self.assertFalse(self.comments_filtered(self.bot)[0])

    def test_dryrun(self):
        # dryrun = True, no comment.
        self.review_bot.dryrun = True
        self.review_bot.comment_write(project=PROJECT, message=COMMENT)
        self.assertFalse(self.comments_filtered(self.bot)[0])

        # dryrun = False, a comment.
        self.review_bot.dryrun = False
        self.review_bot.comment_write(project=PROJECT, message=COMMENT)
        self.assertTrue(self.comments_filtered(self.bot)[0])

        # dryrun = True, no replacement.
        self.review_bot.dryrun = True
        self.review_bot.comment_write(state='changed', project=PROJECT, message=COMMENT)
        _, info = self.comments_filtered(self.bot)
        self.assertEqual(info['state'], 'done')

        # dryrun = False, replacement.
        self.review_bot.dryrun = False
        self.review_bot.comment_write(state='changed', project=PROJECT, message=COMMENT)
        _, info = self.comments_filtered(self.bot)
        self.assertEqual(info['state'], 'changed')

    def test_bot_name_suffix(self):
        suffix1 = 'suffix1'
        bot_suffixed1 = '::'.join([self.bot, suffix1])

        suffix2 = 'suffix2'
        bot_suffixed2 = '::'.join([self.bot, suffix2])

        self.review_bot.comment_write(bot_name_suffix=suffix1, project=PROJECT, message=COMMENT)
        self.assertFalse(self.comments_filtered(self.bot)[0])
        self.assertTrue(self.comments_filtered(bot_suffixed1)[0])
        self.assertFalse(self.comments_filtered(bot_suffixed2)[0])

        self.review_bot.comment_write(bot_name_suffix=suffix2, project=PROJECT, message=COMMENT)
        self.assertFalse(self.comments_filtered(self.bot)[0])
        self.assertTrue(self.comments_filtered(bot_suffixed1)[0])
        self.assertTrue(self.comments_filtered(bot_suffixed2)[0])

        self.review_bot.comment_write(bot_name_suffix=suffix1, project=PROJECT, message=COMMENT + '\nnew')
        comment, _ = self.comments_filtered(bot_suffixed1)
        self.assertTrue(comment['comment'].endswith(COMMENT + '\nnew'))

        comment, _ = self.comments_filtered(bot_suffixed2)
        self.assertTrue(comment['comment'].endswith(COMMENT))

    def comments_filtered(self, bot):
        comments = self.api.get_comments(project_name=PROJECT)
        return self.api.comment_find(comments, bot)
예제 #8
0
class AcceptCommand(object):
    def __init__(self, api):
        self.api = api
        self.comment = CommentAPI(self.api.apiurl)

    def find_new_requests(self, project):
        query = "match=state/@name='new'+and+(action/target/@project='{}'+and+action/@type='submit')".format(
            project)
        url = self.api.makeurl(['search', 'request'], query)

        f = http_GET(url)
        root = ET.parse(f).getroot()

        rqs = []
        for rq in root.findall('request'):
            pkgs = []
            actions = rq.findall('action')
            for action in actions:
                targets = action.findall('target')
                for t in targets:
                    pkgs.append(str(t.get('package')))

            rqs.append({'id': int(rq.get('id')), 'packages': pkgs})
        return rqs

    def perform(self, project, force=False):
        """Accept the staging project for review and submit to Factory /
        openSUSE 13.2 ...

        Then disable the build to disabled
        :param project: staging project we are working with

        """

        status = self.api.check_project_status(project)

        if not status:
            print('The project "{}" is not yet acceptable.'.format(project))
            if not force:
                return False

        meta = self.api.get_prj_pseudometa(project)
        requests = []
        packages = []
        for req in meta['requests']:
            self.api.rm_from_prj(project,
                                 request_id=req['id'],
                                 msg='ready to accept')
            requests.append(req['id'])
            packages.append(req['package'])
            msg = 'Accepting staging review for {}'.format(req['package'])
            print(msg)

            oldspecs = self.api.get_filelist_for_package(
                pkgname=req['package'],
                project=self.api.project,
                extension='spec')
            change_request_state(self.api.apiurl,
                                 str(req['id']),
                                 'accepted',
                                 message='Accept to %s' % self.api.project)
            self.create_new_links(self.api.project, req['package'], oldspecs)

        # A single comment should be enough to notify everybody, since
        # they are already mentioned in the comments created by
        # select/unselect
        pkg_list = ", ".join(packages)
        cmmt = 'Project "{}" accepted.' \
               ' The following packages have been submitted to {}: {}.'.format(project,
                                                                               self.api.project,
                                                                               pkg_list)
        self.comment.add_comment(project_name=project, comment=cmmt)

        # XXX CAUTION - AFAIK the 'accept' command is expected to clean the messages here.
        self.comment.delete_from(project_name=project)

        self.api.build_switch_prj(project, 'disable')
        if self.api.item_exists(project + ':DVD'):
            self.api.build_switch_prj(project + ':DVD', 'disable')

        return True

    def cleanup(self, project):
        if not self.api.item_exists(project):
            return False

        pkglist = self.api.list_packages(project)
        clean_list = set(pkglist) - set(self.api.cstaging_nocleanup)

        for package in clean_list:
            print "[cleanup] deleted %s/%s" % (project, package)
            delete_package(self.api.apiurl,
                           project,
                           package,
                           force=True,
                           msg="autocleanup")

        # wipe Test-DVD binaries and breaks kiwi build
        if project.startswith('openSUSE:'):
            for package in pkglist:
                if package.startswith('Test-DVD-'):
                    # intend to break the kiwi file
                    arch = package.split('-')[-1]
                    fakepkgname = 'I-am-breaks-kiwi-build'
                    oldkiwifile = self.api.load_file_content(
                        project, package, 'PRODUCT-' + arch + '.kiwi')
                    if oldkiwifile is not None:
                        newkiwifile = re.sub(
                            r'<repopackage name="openSUSE-release"/>',
                            '<repopackage name="%s"/>' % fakepkgname,
                            oldkiwifile)
                        self.api.save_file_content(project, package,
                                                   'PRODUCT-' + arch + '.kiwi',
                                                   newkiwifile)

                    # do wipe binary now
                    query = {'cmd': 'wipe'}
                    query['package'] = package
                    query['repository'] = 'images'

                    url = self.api.makeurl(['build', project], query)
                    try:
                        http_POST(url)
                    except urllib2.HTTPError, err:
                        # failed to wipe isos but we can just continue
                        pass

        return True
예제 #9
0
class AcceptCommand(object):
    def __init__(self, api):
        self.api = api
        self.comment = CommentAPI(self.api.apiurl)

    def find_new_requests(self, project):
        query = "match=state/@name='new'+and+(action/target/@project='{}'+and+action/@type='submit')".format(project)
        url = self.api.makeurl(['search', 'request'], query)

        f = http_GET(url)
        root = ET.parse(f).getroot()

        rqs = []
        for rq in root.findall('request'):
            pkgs = []
            actions = rq.findall('action')
            for action in actions:
                targets = action.findall('target')
                for t in targets:
                    pkgs.append(str(t.get('package')))

            rqs.append({'id': int(rq.get('id')), 'packages': pkgs})
        return rqs

    def perform(self, project, force=False):
        """Accept the staging project for review and submit to Factory /
        openSUSE 13.2 ...

        Then disable the build to disabled
        :param project: staging project we are working with

        """

        status = self.api.check_project_status(project)

        if not status:
            print('The project "{}" is not yet acceptable.'.format(project))
            if not force:
                return False

        meta = self.api.get_prj_pseudometa(project)
        requests = []
        packages = []
        for req in meta['requests']:
            self.api.rm_from_prj(project, request_id=req['id'], msg='ready to accept')
            requests.append(req['id'])
            packages.append(req['package'])
            msg = 'Accepting staging review for {}'.format(req['package'])
            print(msg)

            oldspecs = self.api.get_filelist_for_package(pkgname=req['package'],
                                                         project=self.api.project,
                                                         extension='spec')
            change_request_state(self.api.apiurl,
                                 str(req['id']),
                                 'accepted',
                                 message='Accept to %s' % self.api.project)
            self.create_new_links(self.api.project, req['package'], oldspecs)

        # A single comment should be enough to notify everybody, since
        # they are already mentioned in the comments created by
        # select/unselect
        pkg_list = ", ".join(packages)
        cmmt = 'Project "{}" accepted.' \
               ' The following packages have been submitted to {}: {}.'.format(project,
                                                                               self.api.project,
                                                                               pkg_list)
        self.comment.add_comment(project_name=project, comment=cmmt)

        # XXX CAUTION - AFAIK the 'accept' command is expected to clean the messages here.
        self.comment.delete_from(project_name=project)

        self.api.build_switch_prj(project, 'disable')
        if self.api.item_exists(project + ':DVD'):
            self.api.build_switch_prj(project + ':DVD', 'disable')

        return True

    def cleanup(self, project):
        if not self.api.item_exists(project):
            return False

        pkglist = self.api.list_packages(project)
        clean_list = set(pkglist) - set(self.api.cstaging_nocleanup)

        for package in clean_list:
            print "[cleanup] deleted %s/%s" % (project, package)
            delete_package(self.api.apiurl, project, package, force=True, msg="autocleanup")

        # wipe Test-DVD binaries and breaks kiwi build
        if project.startswith('openSUSE:'):
            for package in pkglist:
                if package.startswith('Test-DVD-'):
                    # intend to break the kiwi file
                    arch = package.split('-')[-1]
                    fakepkgname = 'I-am-breaks-kiwi-build'
                    oldkiwifile = self.api.load_file_content(project, package, 'PRODUCT-'+arch+'.kiwi')
                    if oldkiwifile is not None:
                        newkiwifile = re.sub(r'<repopackage name="openSUSE-release"/>', '<repopackage name="%s"/>' % fakepkgname, oldkiwifile)
                        self.api.save_file_content(project, package, 'PRODUCT-' + arch + '.kiwi', newkiwifile)

                    # do wipe binary now
                    query = { 'cmd': 'wipe' }
                    query['package'] = package
                    query['repository'] = 'images'

                    url = self.api.makeurl(['build', project], query)
                    try:
                        http_POST(url)
                    except urllib2.HTTPError, err:
                        # failed to wipe isos but we can just continue
                        pass

        return True