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()
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)
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()
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)
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'])
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')
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
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#'
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 __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 = {}
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
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.
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)
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)
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)
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'))
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()
def __init__(self, *args, **kwargs): ReviewBot.ReviewBot.__init__(self, *args, **kwargs) self.do_comments = True self.commentapi = CommentAPI(self.apiurl) self.maintbot = MaintenanceChecker(*args, **kwargs) # for FactorySourceChecker self.factory = FactorySourceChecker(*args, **kwargs) self.needs_reviewteam = False self.pending_factory_submission = False self.source_in_factory = None self.needs_release_manager = False self.release_manager_group = 'leap-reviewers' self.must_approve_version_updates = False self.must_approve_maintenance_updates = False self.comment_marker_re = re.compile(r'<!-- leaper state=(?P<state>done|seen)(?: result=(?P<result>accepted|declined))? -->') self.comment_log = None self.commentlogger = LogToString(self, 'comment_log') self.logger.addFilter(self.commentlogger)
def __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()
def __init__(self, api): self.api = api self.comment = CommentAPI(api.apiurl)
def __init__(self, apiurl): self.apiurl = apiurl self.comment = CommentAPI(apiurl)
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))
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))])