def setUp(self):
     self.api = CommentAPI('bogus')
     self.bot = type(self).__name__
     self.comments = {
         1: {'comment': '<!-- {} -->\n\nshort comment'.format(self.bot)},
         2: {'comment': '<!-- {} foo=bar distro=openSUSE -->\n\nshort comment'.format(self.bot)}
     }
    def __init__(self, *args, **kwargs):
        ReviewBot.ReviewBot.__init__(self, *args, **kwargs)

        self.force = False
        self.openqa = None
        self.commentapi = CommentAPI(self.apiurl)
        self.update_test_builds = dict()
예제 #3
0
def remind_comment(apiurl, repeat_age, request_id, project, package=None):
    comment_api = CommentAPI(apiurl)
    comments = comment_api.get_comments(request_id=request_id)
    comment, _ = comment_api.comment_find(comments, BOT_NAME)

    if comment:
        delta = datetime.utcnow() - comment['when']
        if delta.days < repeat_age:
            print(
                '  skipping due to previous reminder from {} days ago'.format(
                    delta.days))
            return

        # Repeat notification so remove old comment.
        try:
            comment_api.delete(comment['id'])
        except HTTPError as e:
            if e.code == 403:
                # Gracefully skip when previous reminder was by another user.
                print('  unable to remove previous reminder')
                return
            raise e

    userids = sorted(maintainers_get(apiurl, project, package))
    if len(userids):
        users = ['@' + userid for userid in userids]
        message = '{}: {}'.format(', '.join(users), REMINDER)
    else:
        message = REMINDER
    print('  ' + message)
    message = comment_api.add_marker(message, BOT_NAME)
    comment_api.add_comment(request_id=request_id, comment=message)
예제 #4
0
    def __init__(self,
                 apiurl=None,
                 dryrun=False,
                 logger=None,
                 user=None,
                 group=None):
        self.apiurl = apiurl
        self.ibs = apiurl.startswith('https://api.suse.de')
        self.dryrun = dryrun
        self.logger = logger
        self.review_user = user
        self.review_group = group
        self.requests = []
        self.review_messages = ReviewBot.DEFAULT_REVIEW_MESSAGES
        self._review_mode = 'normal'
        self.fallback_user = None
        self.fallback_group = None
        self.comment_api = CommentAPI(self.apiurl)
        self.bot_name = self.__class__.__name__
        self.only_one_action = False
        self.request_default_return = None
        self.comment_handler = False
        self.override_allow = True
        self.override_group_key = '{}-override-group'.format(
            self.bot_name.lower())
        self.request_age_min_default = 0
        self.request_age_min_key = '{}-request-age-min'.format(
            self.bot_name.lower())
        self.lookup = PackageLookup(self.apiurl)

        self.load_config()
예제 #5
0
    def __init__(self, *args, **kwargs):
        self.no_review = False
        self.force = False
        if 'no_review' in kwargs:
            if kwargs['no_review'] == True:
                self.no_review = True
            del kwargs['no_review']
        if 'force' in kwargs:
            if kwargs['force'] == True:
                self.force = True
            del kwargs['force']

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

        self.ts = rpm.TransactionSet()
        self.ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)

        self.pkgcache = PkgCache(BINCACHE)

        # reports of source submission
        self.reports = []
        # textual report summary for use in accept/decline message
        # or comments
        self.text_summary = ''

        self.session = DB.db_session()

        self.dblogger = LogToDB(self.session)

        self.logger.addFilter(self.dblogger)

        self.commentapi = CommentAPI(self.apiurl)
예제 #6
0
    def test_select_comments(self):
        c_api = CommentAPI(self.api.apiurl)
        staging_b = 'openSUSE:Factory:Staging:B'
        comments = c_api.get_comments(project_name=staging_b)

        # First select
        self.assertEqual(
            True,
            SelectCommand(self.api, staging_b).perform(['gcc', 'wine']))
        first_select_comments = c_api.get_comments(project_name=staging_b)
        last_id = sorted(first_select_comments.keys())[-1]
        first_select_comment = first_select_comments[last_id]
        # Only one comment is added
        self.assertEqual(len(first_select_comments), len(comments) + 1)
        # With the right content
        self.assertTrue('Request#123 for package gcc submitted by @Admin' in
                        first_select_comment['comment'])

        # Second select
        self.assertEqual(
            True,
            SelectCommand(self.api, staging_b).perform(['puppet']))
        second_select_comments = c_api.get_comments(project_name=staging_b)
        last_id = sorted(second_select_comments.keys())[-1]
        second_select_comment = second_select_comments[last_id]
        # The number of comments remains, but they are different
        self.assertEqual(len(second_select_comments),
                         len(first_select_comments))
        self.assertNotEqual(second_select_comment['comment'],
                            first_select_comment['comment'])
        # The new comments contents both old and new information
        self.assertTrue('Request#123 for package gcc submitted by @Admin' in
                        second_select_comment['comment'])
        self.assertTrue('Request#321 for package puppet submitted by @Admin' in
                        second_select_comment['comment'])
예제 #7
0
 def __init__(self, *args, **kwargs):
     super(OpenQABot, self).__init__(*args, **kwargs)
     self.tgt_repo = {}
     self.project_settings = {}
     self.api_map = {}
     self.bot_name = 'openqa'
     self.force = False
     self.openqa = None
     self.commentapi = CommentAPI(self.apiurl)
    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')
예제 #9
0
def check_comment(apiurl, bot, **kwargs):
    if not len(kwargs):
        return False

    api = CommentAPI(apiurl)
    comments = api.get_comments(**kwargs)
    comment = api.comment_find(comments, bot)[0]
    if comment:
        return (datetime.utcnow() - comment['when']).total_seconds()

    return False
예제 #10
0
    def __init__(self, *args, **kwargs):
        ReviewBot.ReviewBot.__init__(self, *args, **kwargs)

        self.do_comments = True
        self.legaldb = None
        self.commentapi = CommentAPI(self.apiurl)
        self.apinick = None
        self.message = None
        if self.ibs:
            self.apinick = 'ibs#'
        else:
            self.apinick = 'obs#'
예제 #11
0
 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))])
예제 #12
0
    def __init__(self, *args, **kwargs):
        super(OpenQABot, self).__init__(*args, **kwargs)
        self.tgt_repo = {}
        self.project_settings = {}
        self.api_map = {}

        self.force = False
        self.openqa = None
        self.commentapi = CommentAPI(self.apiurl)
        self.update_test_builds = {}
        self.pending_target_repos = set()
        self.openqa_jobs = {}
예제 #13
0
    def setup_wf(self):
        wf = OBSLocal.StagingWorkflow()
        wf.setup_rings()

        self.c_api = CommentAPI(wf.api.apiurl)

        staging_b = wf.create_staging('B', freeze=True)
        self.prj = staging_b.name

        self.winerq = wf.create_submit_request('devel:wine', 'wine', text='Hallo World')
        self.assertEqual(True, SelectCommand(wf.api, self.prj).perform(['wine']))
        self.comments = self.c_api.get_comments(project_name=self.prj)
        return wf
예제 #14
0
    def __init__(self, *args, **kwargs):
        ReviewBot.ReviewBot.__init__(self, *args, **kwargs)

        self.do_comments = True
        self.legaldb = None
        self.legaldb_headers = {}
        self.commentapi = CommentAPI(self.apiurl)
        self.apinick = None
        self.message = None
        if self.ibs:
            self.apinick = 'ibs#'
        else:
            self.apinick = 'obs#'
        self.override_allow = False  # Handled via external tool.
예제 #15
0
    def setup_wf(self, description=''):
        wf = OBSLocal.FactoryWorkflow()
        wf.setup_rings()

        self.c_api = CommentAPI(wf.api.apiurl)

        staging_b = wf.create_staging('B', freeze=True)
        self.prj = staging_b.name

        self.winerq = wf.create_submit_request('devel:wine', 'wine', text='Hallo World', description=description)
        self.assertEqual(True, SelectCommand(wf.api, self.prj).perform(['wine']))
        self.comments = self.c_api.get_comments(project_name=self.prj)
        wf.create_attribute_type('OSRT', 'ProductVersion', 1)
        return wf
    def setUp(self):
        super(TestReviewBotComment, self).setUp()
        self.api = CommentAPI(self.apiurl)
        self.wf = OBSLocal.StagingWorkflow()
        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 create_comments(self, state):
        comments = dict()
        for source, details in state['check'].items():
            rebuild = dateutil.parser.parse(details["rebuild"])
            if datetime.now() - rebuild < timedelta(days=2):
                self.logger.debug(f"Ignore {source} - problem too recent")
                continue
            _, _, arch, rpm = source.split('/')
            rpm = rpm.split(':')[0]
            comments.setdefault(rpm, {})
            comments[rpm][arch] = details['problem']

        url = makeurl(self.apiurl, ['comments', 'user'])
        root = ET.parse(http_GET(url)).getroot()
        for comment in root.findall('.//comment'):
            if comment.get('project') != self.project:
                continue
            if comment.get('package') in comments:
                continue
            self.logger.info("Removing comment for package {}".format(
                comment.get('package')))
            url = makeurl(self.apiurl, ['comment', comment.get('id')])
            http_DELETE(url)

        commentapi = CommentAPI(self.apiurl)
        MARKER = 'Installcheck'

        for package in comments:
            newcomment = ''
            for arch in sorted(comments[package]):
                newcomment += f"\n\n**Installcheck problems for {arch}**\n\n"
                for problem in sorted(comments[package][arch]):
                    newcomment += "+ " + problem + "\n"

            newcomment = commentapi.add_marker(newcomment.strip(), MARKER)
            oldcomments = commentapi.get_comments(project_name=self.project,
                                                  package_name=package)
            oldcomment, _ = commentapi.comment_find(oldcomments, MARKER)
            if oldcomment and oldcomment['comment'] == newcomment:
                continue

            if oldcomment:
                commentapi.delete(oldcomment['id'])
            self.logger.debug("Adding comment to {}/{}".format(
                self.project, package))
            commentapi.add_comment(project_name=self.project,
                                   package_name=package,
                                   comment=newcomment)
    def __init__(self, api, config):
        self.api = api
        self.config = conf.config[api.project]
        self.logger = logging.getLogger('InstallChecker')
        self.commentapi = CommentAPI(api.apiurl)

        self.arch_whitelist = self.config.get('repo_checker-arch-whitelist')
        if self.arch_whitelist:
            self.arch_whitelist = set(self.arch_whitelist.split(' '))

        self.ring_whitelist = set(self.config.get('repo_checker-binary-whitelist-ring', '').split(' '))

        self.cycle_packages = self.config.get('repo_checker-allowed-in-cycles')
        self.calculate_allowed_cycles()

        self.existing_problems = self.binary_list_existing_problem(api.project, api.cmain_repo)
예제 #19
0
    def update_status_comments(self, project, command):
        """
        Refresh the status comments, used for notification purposes, based on
        the current list of requests. To ensure that all involved users
        (and nobody else) get notified, old status comments are deleted and
        a new one is created.
        :param project: project name
        :param command: name of the command to include in the message
        """

        # TODO: we need to discuss the best way to keep track of status
        # comments. Right now they are marked with an initial markdown
        # comment. Maybe a cleaner approach would be to store something
        # like 'last_status_comment_id' in the pseudometa. But the current
        # OBS API for adding comments doesn't return the id of the created
        # comment.

        comment_api = CommentAPI(self.apiurl)

        comments = comment_api.get_comments(project_name=project)
        for comment in comments.values():
            # TODO: update the comment removing the user mentions instead of
            # deleting the whole comment. But there is currently not call in
            # OBS API to update a comment
            if comment['comment'].startswith('<!--- osc staging'):
                comment_api.delete(comment['id'])
                break  # There can be only one! (if we keep deleting them)

        meta = self.get_prj_pseudometa(project)
        lines = ['<!--- osc staging %s --->' % command]
        lines.append('The list of requests tracked in %s has changed:\n' %
                     project)
        for req in meta['requests']:
            author = req.get('autor', None)
            if not author:
                # Old style metadata
                author = get_request(self.apiurl, str(req['id'])).get_creator()
            lines.append('  * Request#%s for package %s submitted by @%s' %
                         (req['id'], req['package'], author))
        msg = '\n'.join(lines)
        comment_api.add_comment(project_name=project, comment=msg)
예제 #20
0
    def test_accept_comments(self):
        c_api = CommentAPI(self.api.apiurl)
        staging_c = 'openSUSE:Factory:Staging:C'
        comments = c_api.get_comments(project_name=staging_c)

        # Accept staging C (containing apparmor and mariadb)
        self.assertEqual(True, AcceptCommand(self.api).perform(staging_c))

        # Comments are cleared up
        accepted_comments = c_api.get_comments(project_name=staging_c)
        self.assertNotEqual(len(comments), 0)
        self.assertEqual(len(accepted_comments), 0)

        # But the comment was written at some point
        self.assertEqual(len(self.obs.comment_bodies), 1)
        comment = self.obs.comment_bodies[0]
        self.assertTrue(
            'The following packages have been submitted to openSUSE:Factory' in
            comment)
        self.assertTrue('apparmor' in comment)
        self.assertTrue('mariadb' in comment)
예제 #21
0
    def __init__(self, *args, **kwargs):
        ReviewBot.ReviewBot.__init__(self, *args, **kwargs)

        self.no_review = False
        self.force = False

        self.ts = rpm.TransactionSet()
        self.ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)

        # reports of source submission
        self.reports = []
        # textual report summary for use in accept/decline message
        # or comments
        self.text_summary = ''

        self.session = DB.db_session()

        self.dblogger = LogToDB(self.session)

        self.logger.addFilter(self.dblogger)

        self.commentapi = CommentAPI(self.apiurl)
    def __init__(self, api, config):
        self.api = api
        self.logger = logging.getLogger('InstallChecker')
        self.commentapi = CommentAPI(api.apiurl)

        self.arch_whitelist = config.get('repo_checker-arch-whitelist')
        if self.arch_whitelist:
            self.arch_whitelist = set(self.arch_whitelist.split(' '))

        self.ring_whitelist = set(
            config.get('repo_checker-binary-whitelist-ring', '').split(' '))

        self.cycle_packages = config.get('repo_checker-allowed-in-cycles')
        self.calculate_allowed_cycles()

        self.ignore_duplicated = set(
            config.get('installcheck-ignore-duplicated-binaries',
                       '').split(' '))
        self.ignore_conflicts = set(
            config.get('installcheck-ignore-conflicts', '').split(' '))
        self.ignore_deletes = str2bool(
            config.get('installcheck-ignore-deletes', 'False'))
예제 #23
0
    def __init__(self, *args, **kwargs):
        self.force = False
        self.openqa = None
        self.do_comments = True
        if 'force' in kwargs:
            if kwargs['force'] is True:
                self.force = True
            del kwargs['force']
        if 'openqa' in kwargs:
            self.openqa = OpenQA_Client(server=kwargs['openqa'])
            del kwargs['openqa']
        if 'do_comments' in kwargs:
            if kwargs['do_comments'] is not None:
                self.do_comments = kwargs['do_comments']
            del kwargs['do_comments']

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

        self.logger.debug(self.do_comments)

        self.commentapi = CommentAPI(self.apiurl)
        self.update_test_builds = dict()
예제 #24
0
    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)
예제 #25
0
    def __init__(self,
                 apiurl=None,
                 dryrun=False,
                 logger=None,
                 user=None,
                 group=None):
        self.apiurl = apiurl
        self.ibs = apiurl.startswith('https://api.suse.de')
        self.dryrun = dryrun
        self.logger = logger
        self.review_user = user
        self.review_group = group
        self.requests = []
        self.review_messages = ReviewBot.DEFAULT_REVIEW_MESSAGES
        self._review_mode = 'normal'
        self.fallback_user = None
        self.fallback_group = None
        self.comment_api = CommentAPI(self.apiurl)
        self.bot_name = self.__class__.__name__
        self.only_one_action = False
        self.request_default_return = None
        self.comment_handler = False

        self.load_config()
예제 #26
0
 def __init__(self, api):
     self.api = api
     self.comment = CommentAPI(api.apiurl)
 def __init__(self, apiurl):
     self.apiurl = apiurl
     self.comment = CommentAPI(apiurl)
예제 #28
0
 def assertComment(self, request_id, comment):
     comments_actual = CommentAPI(self.wf.api.apiurl).get_comments(request_id=request_id)
     comment_actual = next(iter(comments_actual.values()))
     self.assertEqual(comment_actual['who'], self.bot_user)
     self.assertEqual(comment_actual['comment'], '\n\n'.join(comment))
예제 #29
0
    def devel_workflow(self, only_devel):
        self.remote_config_set_age_minimum()

        devel_project = self.randomString('devel')
        package = self.randomString('package')
        request = self.wf.create_submit_request(devel_project, package)
        attribute_value_save(self.wf.apiurl, devel_project, 'ApprovedRequestSource', '', 'OBS')

        if not only_devel:
            self.assertReviewBot(request.reqid, self.bot_user, 'new', 'new')

            comment = [
                '<!-- OriginManager state=seen result=None -->',
                'Source not found in allowed origins:',
                f'- {self.product_project}',
                f'Decision may be overridden via `@{self.bot_user} override`.',
            ]
            self.assertComment(request.reqid, comment)

            CommentAPI(self.wf.api.apiurl).add_comment(
                request_id=request.reqid, comment=f'@{self.bot_user} change_devel')

            comment = 'change_devel command by {}'.format('Admin')
        else:
            comment = 'only devel origin allowed'

        self.assertReviewBot(request.reqid, self.bot_user, 'new', 'accepted')
        self.assertAnnotation(request.reqid, {
            'comment': comment,
            'origin': devel_project,
        })

        request.change_state('accepted')

        memoize_session_reset()
        self.osc_user(self.bot_user)
        request_future = origin_update(self.wf.apiurl, self.wf.project, package)
        self.assertNotEqual(request_future, False)
        if request_future:
            request_id_change_devel = request_future.print_and_create()

        # Ensure a second request is not triggered.
        request_future = origin_update(self.wf.apiurl, self.wf.project, package)
        self.assertEqual(request_future, False)
        self.osc_user_pop()

        memoize_session_reset()
        origin_info = origin_find(self.wf.apiurl, self.wf.project, package)
        self.assertEqual(origin_info, None)

        self.assertReviewBot(request_id_change_devel, self.bot_user, 'new', 'accepted')
        self.assertAnnotation(request_id_change_devel, {
            'origin': devel_project,
        })

        # Origin should change before request is accepted since it is properly
        # annotated and without fallback review.
        memoize_session_reset()
        origin_info = origin_find(self.wf.apiurl, self.wf.project, package)
        self.assertEqual(str(origin_info), devel_project)

        self.wf.projects[devel_project].packages[0].create_commit()

        self.osc_user(self.bot_user)
        request_future = origin_update(self.wf.apiurl, self.wf.project, package)
        self.assertNotEqual(request_future, False)
        if request_future:
            request_id_update = request_future.print_and_create()

        request_future = origin_update(self.wf.apiurl, self.wf.project, package)
        self.assertEqual(request_future, False)
        self.osc_user_pop()

        self.assertReviewBot(request_id_update, self.bot_user, 'new', 'accepted')
        self.assertAnnotation(request_id_update, {
            'origin': devel_project,
        })

        memoize_session_reset()
        devel_project_actual, _ = devel_project_get(self.wf.apiurl, self.wf.project, package)
        self.assertEqual(devel_project_actual, None)

        request = get_request(self.wf.apiurl, request_id_change_devel)
        request_state_change(self.wf.apiurl, request_id_change_devel, 'accepted')

        memoize_session_reset()
        devel_project_actual, devel_package_actual = devel_project_get(
            self.wf.apiurl, self.wf.project, package)
        self.assertEqual(devel_project_actual, devel_project)
        self.assertEqual(devel_package_actual, package)

        request = get_request(self.wf.apiurl, request_id_update)
        request_state_change(self.wf.apiurl, request_id_update, 'accepted')

        devel_project_new = self.randomString('develnew')
        self.wf.create_package(devel_project_new, package)
        attribute_value_save(self.wf.apiurl, devel_project_new, 'ApprovedRequestSource', '', 'OBS')

        copy_package(self.wf.apiurl, devel_project, package,
                     self.wf.apiurl, devel_project_new, package)

        request_future = request_create_change_devel(
            self.wf.apiurl, devel_project_new, package, self.wf.project)
        self.assertNotEqual(request_future, False)
        if request_future:
            request_id_change_devel_new = request_future.print_and_create()

        self.assertReviewBot(request_id_change_devel_new, self.bot_user, 'new', 'accepted')
        self.assertAnnotation(request_id_change_devel_new, {
            'origin': devel_project_new,
            'origin_old': devel_project,
        })

        self.accept_fallback_review(request_id_change_devel_new)
        request_state_change(self.wf.apiurl, request_id_change_devel_new, 'accepted')

        memoize_session_reset()
        origin_info = origin_find(self.wf.apiurl, self.wf.project, package)
        self.assertEqual(str(origin_info), devel_project_new)
 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))])