def validate_product_approval(self): """ notify in channel if not product approved (applicable only to files/dir which require product approval) :return: relevant response dict """ if self.pr.is_merged: if self.pr.opened_by in self.pr.config.superMembers: LOG.debug('pr_opened_by is super user of {repo} so NO alert, super_members={super_members}' .format(repo=self.pr.repo, super_members=self.pr.config.superMembers)) return {"msg": "Skipped product review because the PR=%s is by a Super User" % self.pr.link_pretty} if self.change_requires_product_plus1: comments = self.github.get_comments() is_product_plus1 = self.is_plus_1_in_comments(comments, self.pr.config.productTeamGithub) if not is_product_plus1: bad_name_str = MSG_BAD_START + "@" + self.created_by msg = MSG_NO_PRODUCT_REVIEW.format(name=bad_name_str, pr=self.pr.link_pretty, title=self.pr.title, branch=self.pr.base_branch, team="".join(self.pr.config.productTeamToBeNotified)) LOG.debug(msg) self.slack.postToSlack(self.pr.config.alertChannelName, msg) LOG.info("Bad PR={msg} repo:{repo}".format(repo=self.pr.repo, msg=self.is_bad_pr)) return {"msg": "Bad PR={msg} repo:{repo}".format(repo=self.pr.repo, msg=self.is_bad_pr)} return {"msg":"Product approved so no alert, pr=%s" %self.pr.link_pretty} return {"msg":"Skipped product review because no changes found which requires product +1 as well, PR=%s" %self.pr.link_pretty} return {"msg": "Skipped product review because the PR=%s is not merged" %self.pr.link_pretty}
def run_checks(self, request, data): ci = CheckImpl(PushPayloadParser(request, payload=data)) response = {} checks = ci.pr.config.checks if ci.pr.is_sensitive_branch: if len(checks) == 0: import inspect method_names = [ attr for attr in dir(ci) if inspect.ismethod(getattr(ci, attr)) ] for check in method_names: if check != "__init__": self.execute_check(ci, check) else: try: for check in checks: try: self.execute_check(ci, check) except AttributeError: LOG.debug("Exception in Run Checks", exc_info=traceback) raise CheckNotFoundException(check) except Exception, e: LOG.debug("Exception in Run Checks", exc_info=traceback) if 'invalid_auth' not in e: raise Exception( str(e) + ISSUE_FOUND.format(issue_link=ISSUE_LINK)) return response
def notify_channel_on_merge(self): """ pass entry in fixed channel for all code merge details (merged to any of sensitive branch) :return: relevant response dict """ if self.pr.is_merged: LOG.debug( "**** Repo=%s, new merge came to=%s, setting trace to=%s channel" % (self.pr.repo, self.pr.base_branch, self.pr.config.codeChannelName)) msg = MSG_CODE_CHANNEL.format(title=self.pr.title, desc=self.pr.description, pr=self.pr.link, head_branch=self.pr.head_branch, base_branch=self.pr.base_branch, pr_by=self.created_by, merge_by=self.merged_by) self.slack.postToSlack(self.pr.config.codeChannelName, msg) LOG.info( "informed %s because pr=%s is merged into sensitive branch=%s" % (self.pr.config.codeChannelName, self.pr.link_pretty, self.pr.base_branch)) return { "msg": "informed %s because pr=%s is merged into sensitive branch=%s" % (self.pr.config.codeChannelName, self.pr.link_pretty, self.pr.base_branch) } return { "msg", "Skipped posting to code channel because '%s' is not merge event" % self.pr.action }
def modify_pr(self, msg, state): data = { "title": msg, "state": state } resp = requests.post(self.pr_api_link, json.dumps(data), headers=self.headers) LOG.debug(resp.content)
def validate_tech_approval(self): """ notify in channel if not tech approved :return: relevant response dict """ if self.pr.is_merged: # and self.head_branch in PushPayload.PROTECTED_BRANCH_LIST: TO ENABLE is_bad_pr = self.is_bad_pr() bad_name_str = MSG_BAD_START + "@" + self.created_by if is_bad_pr: msg = MSG_NO_TECH_REVIEW.format( name=bad_name_str, pr=self.pr.link_pretty, title=self.pr.title, branch=self.pr.base_branch, team=self.pr.config.cc_tech_team) LOG.debug(msg) self.slack.postToSlack(self.pr.config.alertChannelName, msg) LOG.info("Bad PR={msg} repo:{repo}".format(repo=self.pr.repo, msg=is_bad_pr)) return { "msg": "Bad PR={msg} repo:{repo}".format(repo=self.pr.repo, msg=is_bad_pr) } return {"msg": "PR is approved so No Alerts"} return {"msg": "Skipped review because its not PR merge event"}
def is_bad_pr(self): """ parse approval's content to identify if PR is actually approved or just random approval click :return: bad_pr (boolean) """ reviews = self.github.get_reviews() if 200 != reviews.status_code: raise Exception(reviews.content) bad_pr = True LOG.info("***** Reading Reviews *****") for item in json.loads(reviews.content): if "APPROVED" == item["state"]: review_comment = item["body"] LOG.debug("review body= %s" + review_comment) thumbsUpIcon = THUMBS_UP_ICON in json.dumps(review_comment) LOG.debug("unicode thumbsUp icon present=%s" % (thumbsUpIcon)) if self.pr.opened_by in self.pr.config.superMembers: # FEW FOLKS TO ALLOW TO HAVE SUPER POWER LOG.debug("PR is opened by %s who is the super user of repo %s, so NO alert'" % (self.pr.opened_by_slack, self.pr.repo)) bad_pr = False break print "***** review_comment", review_comment created_by = self.pr.config.getSlackName(self.pr.opened_by) if item["user"]["login"] != created_by and (review_comment.find("+1") != -1 or thumbsUpIcon): LOG.debug("+1 is found from reviewer=%s marking No Alert " % item["user"]["login"]) bad_pr = False break return bad_pr
def notify_code_freeze(self): """ gathers accumulated data after last qa_signOff and send an attachment into channel announcing details of code freeze :return: relevant response dict """ if self.pr.is_merged and (self.pr.base_branch == self.pr.config.testBranch \ and self.pr.head_branch == self.pr.config.devBranch): LOG.debug( "*** PR merged from {dev_branch} to {qa_branch}, posting release items to slack" .format(dev_branch=self.pr.config.devBranch, qa_branch=self.pr.config.testBranch)) write_to_file_from_top( self.pr.config.codeFreezeDetailsPath, ":clubs:" + str( datetime.now(pytz.timezone(self.pr.config.timezone)). strftime('%B %d at %I.%M %p')) + " with <" + self.pr.link_pretty + "|PR>") try: msg = read_from_file(self.pr.config.releaseItemsFilePath) LOG.debug("final msg =" + msg) except Exception, e: return { "msg": "Skipped posting code-freeze because no details found in file %s" % self.pr.config.releaseItemsFilePath } CODE_FREEZE_TEXT[0]["pretext"] = CODE_FREEZE_TEXT[0][ "pretext"].format(dev_branch=self.pr.config.devBranch, test_branch=self.pr.config.testBranch) CODE_FREEZE_TEXT[0]["fields"][0]["title"] = CODE_FREEZE_TEXT[0]["fields"][0].get("title")\ .format(test_branch=self.pr.config.testBranch) CODE_FREEZE_TEXT[0]["fields"][1]["value"] = CODE_FREEZE_TEXT[0][ "fields"][1]["value"].format(pr=self.pr.link_pretty) CODE_FREEZE_TEXT[0]["text"] = CODE_FREEZE_TEXT[0]["text"].format( msg=msg) CODE_FREEZE_TEXT[0]["title_link"] = CODE_FREEZE_TEXT[0]["title_link"]\ .format(release_notes_link=self.pr.config.release_notes_link) self.slack.postToSlack(channel=self.pr.config.alertChannelName, attachments=CODE_FREEZE_TEXT) self.remind_all_finally_to_update_release_notes() return { "msg": "informed code-freeze on %s for pr=%s" % (self.pr.config.alertChannelName, self.pr.link_pretty) }
def remind_all_finally_to_update_release_notes(self): """ Final reminder to the folks who had merged code till the time code freeze was taken """ names = read_from_file(self.pr.config.releaseItemsFileMergedBy) LOG.debug("final names list =" + names) if names: time.sleep(10) msg = RELEASE_NOTES_REMINDER.format( msg=names, release_notes_link=self.pr.config.release_notes_link, qa_team=self.pr.config.qaTeamMembers) self.slack.postToSlack(channel=self.pr.config.alertChannelName, msg=msg) self.clean_up_for_next_cycle()
def save_data_for_later(self): """ saves merge event data on file to use for code freeze notification later on :return: """ if self.pr.is_merged and self.pr.base_branch == self.pr.config.devBranch: msg = DATA_SAVE_MERGED.format(title=self.pr.title, desc=self.pr.description, pr=self.pr.link_pretty, by=self.pr.opened_by_slack) write_to_file_from_top(self.pr.config.releaseItemsFilePath, msg) with open(self.pr.config.releaseItemsFileMergedBy, "a+") as f: name = "<@{0}>".format(self.pr.opened_by_slack) existing_names = f.read() if name not in existing_names: f.write(name + ", ") LOG.debug(msg + ' added unique names to file ' + self.pr.config.releaseItemsFileMergedBy) f.close()
def setup_logging(): if not app.debug: LOG.debug('************ log from setup_config *********')
def execute_check(self, ci, check): LOG.debug("************* Starting check=%s *****************" % check) response = getattr(ci, check)() LOG.debug("for check= %s, response= %s" % (check, response))
def comment_pr(self, comment_section, comment): resp = requests.post(comment_section, headers=self.headers, data=json.dumps(comment)) LOG.debug(resp.content)
def clear_file(_file): LOG.debug("************** CLEARNING FILE=" + _file) open(_file, 'w').close()
def __init__(self, request, payload): self.request = request self.payload = payload self.pr = payload["pull_request"] LOG.debug("Repo=" + self.repo) self.config = ConfigProvider(self.repo)