From 564dfdfbbb40604a20be799e0cb91ffeed7ee7ed Mon Sep 17 00:00:00 2001 From: JudeN Date: Tue, 31 Oct 2017 00:31:43 -0400 Subject: [PATCH] Checkpoint commit / pep8 / fixes from cololoco testing --- marge.py | 292 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 164 insertions(+), 128 deletions(-) diff --git a/marge.py b/marge.py index bf47aee..87743ef 100755 --- a/marge.py +++ b/marge.py @@ -2,31 +2,39 @@ import re from errbot import BotPlugin, botcmd, webhook from errbot.backends import xmpp from errcron.bot import CrontabMixin +from time import sleep import gitlab from Secret import admin_token +# TODO: Add certificate verification to the gitlab API calls. + class Marge(BotPlugin, CrontabMixin): """ I remind you about merge requests - Use: - In gitlab: - Add a merge request webook of the form 'https://webookserver/margeboot/' to the projects you want tracked. - should be a comma-separated list of rooms you want notified. + Use: + In gitlab: + Add a merge request webook of the form + 'https://webookserver/margeboot/' + to the projects you want tracked. should be a + comma-separated list of short room names (anything before the '@') + that you want notified. In errbot: - Add @domain to the CHATROOM_PRESENCE list in config.py for rooms margebot should join + Add @domain to the CHATROOM_PRESENCE list in config.py for + rooms margebot should join """ - + + # TODO: The rontab schedule should be part of the configuration CRONTAB = [ - '0 11,17 * * * .crontab_hook' # 7:00AM and 1:00PM EST warnings + '0 11,17 * * * .crontab_hook' # 7:00AM and 1:00PM EST warnings ] def __init__(self, *args, **kwargs): - self.git_host = None - self.chatroom_host = None - super().__init__(*args, **kwargs) + self.git_host = None + self.chatroom_host = None + super().__init__(*args, **kwargs) def get_configuration_template(self): return {'GIT_HOST': 'gitlab.example.com', @@ -41,7 +49,7 @@ class Marge(BotPlugin, CrontabMixin): return self.git_host = self.config['GIT_HOST'] self.chatroom_host = self.config['CHATROOM_HOST'] - self.gitlab = gitlab.Gitlab(self.git_host,admin_token,verify_ssl=False) + self.gitlab = gitlab.Gitlab(self.git_host, admin_token, verify_ssl=False) super().activate() @@ -49,105 +57,120 @@ class Marge(BotPlugin, CrontabMixin): # TODO: Anything special for closing gitlab ? super().deactivate() -# def configure(self, configuration): -# ## TODO: set up a connection to the gitlab API -# if self.config: -# self.gitlab = gitlab.Gitlab(self.git_host,admin_token,verify_ssl=False) - @webhook('/margebot//') def gitlab_hook(self, request, rooms): """ Webhook that listens on http://:/gitlab """ + self.log.info('margebot webhook request: {}'.format(request)) + self.log.info('margebot webhook rooms {}'.format(rooms)) + # TODO: Will errbot return a json struct or not ? # verify it's a merge request if request['object_kind'] != 'merge_request': - self.log.error('expecting object_kind of merge_request but got {}'.format(request['object_kind'])) - self.log.error('request: {}'.format(request)) + self.log.error('expecting object_kind of merge_request but got {}'.format(request['object_kind'])) + self.log.error('request: {}'.format(request)) elif request['object_attributes']['state'] == 'opened': - if request['object_attributes']['work_in_progress']: - wip = "WIP" - else: - wip = "" - url = request['object_attributes']['url'] - state = request['object_attributes']['state'] - title = request['object_attributes']['title'] - - author_id = request['object_attributes']['author_id'] # map this to user name ... - author = self.gitlab.getuser(author_id) - author_name = author['username'] - - target_project_id = request['object_attributes']['target_project_id'] - iid = request['object_attributes']['iid'] - - user_name = request['user']['username'] # will this always be Administrator ? - - message = "Reviews: {} has opened a new {} MR: {}\n{}".format(author_name, wip, title, url) - - # TODO: Maybe also check the notify_ labels assigned to the MR as well ? - #mr_rooms = self.get_mr_rooms(target_project_id) - for a_room in rooms.split(','): - if self.config: - self.send( self.build_identifier(a_room + '@' + self.chatroom_host ), message) - - with self.mutable('OPEN_MRS') as open_mrs: - open_mrs[(target_project_id,iid,rooms)] = True - + + # TODO: + # - check for reopened / request['object_attributes']['action'] == 'reopn' + # (there's no 'action': 'opened' for MRs are created... + # - pop open_mrs when MRs are closed (action == close / state == closed + + if request['object_attributes']['work_in_progress']: + wip = "WIP" + else: + wip = "" + url = request['project']['homepage'] + state = request['object_attributes']['state'] + title = request['object_attributes']['title'] + + author_id = request['object_attributes']['author_id'] # map this to user name ... + author = self.gitlab.getuser(author_id) + author_name = author['username'] + + target_project_id = request['object_attributes']['target_project_id'] + iid = request['object_attributes']['iid'] + + user_name = request['user']['username'] # will this always be Administrator ? + + msg_template = "New Review: {} has opened a new {} MR: \"{}\"\n{}/merge_requests/{}" + msg = msg_template.format(author_name, wip, title, url, iid) + + for a_room in rooms.split(','): + if self.config: + self.send(self.build_identifier(a_room + '@' + self.chatroom_host), msg) + + if 'OPEN_MRS' not in self.keys(): + empty_dict = {} + self['OPEN_MRS'] = empty_dict + + with self.mutable('OPEN_MRS') as open_mrs: + open_mrs[(target_project_id, iid, rooms)] = True + return "OK" def crontab_hook(self): """ - Send a scheduled message to the rooms margebot is watching about open MRs the room cares about. + Send a scheduled message to the rooms margebot is watching + about open MRs the room cares about. """ reminder_msg = {} # Map of reminder_msg['roomname@domain'] = msg - + # initialize the reminders rooms = xmpp.rooms() for a_room in rooms: reminder_msg[a_room.node()] = '' + msg = "" still_open_mrs = {} # Let's walk through the MRs we've seen already: - for (project,iid,notify_rooms) in self['OPEM_MRS']: + with self.mutable('OPEN_MRS') as open_mrs: + for (project, iid, notify_rooms) in open_mrs: - # Lookup the MR from the project/iid - a_mr = self.gitlab.getmergerequest(project, iid) + # Lookup the MR from the project/iid + a_mr = self.gitlab.getmergerequest(project, iid) - # If the MR is no longer open, skip to the next MR, - # and don't include this MR in the next check - if a_mr['state'] != 'open': - continue - else: - still_open_mrs[(project, iid, notify_rooms)] = True - - # TODO: Warn if an open MR has has conflicts (merge_status == ??) - # TODO: Include the count of opened MR notes (does the API show resolved state ??) - - approvals = self.gitlab.getapprovals(a_mr['id']) - also_approved = "" - for approved in approvals['approved_by']: - also_approved += "," + approved['user']['name'] + # If the MR is no longer open, skip to the next MR, + # and don't include this MR in the next check + if a_mr['state'] != 'open': + continue + else: + still_open_mrs[(project, iid, notify_rooms)] = True + + # TODO: Warn if an open MR has has conflicts (merge_status == ??) + # TODO: Include the count of opened MR notes (does the API show resolved state ??) + + # getapprovals is only available in GitLab 8.9 EE or greater (not the open source CE version) + # approvals = self.gitlab.getapprovals(a_mr['id']) + # also_approved = "" + # for approved in approvals['approved_by']: + # also_approved += "," + approved['user']['name'] + + upvotes = a_mr['upvotes'] + if upvotes >= 2: + msg = "\n{}: Has 2+ upvotes / Could be merged in now.".format(a_mr['web_url']) + elif upvotes == 1: + msg_template = "\n{}: Waiting for another upvote." + msg = msg_template.format(a_mr['web_url']) + else: + msg = '\n{}: Noo upvotes / Please Review.'.format(a_mr['web_url']) - upvotes = a_mr['upvotes'] - if upvotes >= 2: - msg = "\n{}: Has 2+ upvotes and could be merged in now.".format(a_mr['web_url']) - elif upvotes == 1: - msg = "\n{}: {} already approved and is waiting for another upvote.".format(a_mr['web_url'], also_approved[1:]) - else: - msg = '\n{}: Has no upvotes.'.format(a_mr['web_url']) - - for a_room in notify_rooms.split(','): - reminder_msg[a_room] += msg + for a_room in notify_rooms.split(','): + reminder_msg[a_room] += msg # Remind each of the rooms about open MRs for a_room, room_msg in reminder_msg.iteritems(): if room_msg != "": if self.config: - self.send(self.build_identifier(a_room + '@' + self.config['CHATROOM_HOST'] ), "Heads up these MRs need some luv:{}\n You can get a list of open reviews I know about by sending me a 'Marge, reviews' command.".format(room_msg)) + msg_template = "Heads up these MRs need some attention:{}\n" + msg_template += "You can get an updated list with the '/msg MargeB !reviews' command." + msg = msg_template.format(room_msg) + self.send(self.build_identifier(a_room + '@' + self.config['CHATROOM_HOST']), msg) self['OPEN_MRS'] = still_open_mrs @@ -157,80 +180,93 @@ class Marge(BotPlugin, CrontabMixin): Returns a list of MRs that are waiting for some luv. Also returns a list of MRs that have had enough luv but aren't merged in yet. """ - sender = msg.frm + sender = msg._from._resource + self.log.info('juden: reviews: frm: {} nick: {}\n{}'.format(msg.frm, msg.nick, msg._from.__dict__)) + + if 'OPEN_MRS' not in self.keys(): + return "No MRs to review" - send_gitlab_id = None + sender_gitlab_id = None for user in self.gitlab.getusers(): - if user['username'] == sender.node: + # self.log.info('juden: users: {} {}'.format(sender, user)) + if user['username'] == sender: sender_gitlab_id = user['id'] + self.log.info('juden: sender_gitlab_id = {}'.format(sender_gitlab_id)) break - if not send_gitlab_id: - self.log.error('problem mapping {} to a gitlab user'.format(sender.node)) - self.send(self.build_identifier(msg.frm), "Sorry I couldn't find your gitlab ID") - return + if not sender_gitlab_id: + self.log.error('problem mapping {} to a gitlab user'.format(sender)) + # self.send(self.build_identifier(msg.frm), "Sorry I couldn't find your gitlab ID") + return "Sorry, I couldn't find your gitlab account." # TODO: how to get the room the message was sent from ? I'm assuming this will either be in msg.frm or msg.to # TODO: weed out MRs the sender opened or otherwise indicate they've opened or have already +1'd - roomname = msg.to.domain #??? + roomname = msg.to.domain # ??? # Let's walk through the MRs we've seen already: msg = "" still_open_mrs = {} - for (project,iid,notify_rooms) in self['OPEM_MRS']: + with self.mutable('OPEN_MRS') as open_mrs: + for (project, iid, notify_rooms) in open_mrs: - # Lookup the MR from the project/iid - a_mr = self.gitlab.getmergerequest(project, iid) + # Lookup the MR from the project/iid + a_mr = self.gitlab.getmergerequest(project, iid) + + self.log.info('juden: a_mr: {} {} {} {}'.format(project, iid, notify_rooms, a_mr)) - # If the MR is no longer open, skip to the next MR, - # and don't include this MR in the next check - if a_mr['state'] != 'open': - continue - else: - still_open_mrs[(project, iid, notify_rooms)] = True - - authored = (a_mr['author_id'] == sender_gitlab_id) - already_approved = False - - approvals = self.gitlab.getapprovals(a_mr['id']) - also_approved = "" - for approved in approvals['approved_by']: - if approved['user']['id'] == sender_gitlab_id: - already_approved = True - else: - also_approved += "," + approved['user']['name'] - - upvotes = a_mr['upvotes'] - if upvotes >= 2: - msg = "\n{}: has 2+ upvotes from {} and could be merged in now.".format(a_mr['web_url'], also_approved[1:]) - elif upvotes == 1: - if not authored: - if already_approved: - msg = "\n{}: has already been approved by you and is waiting for another upvote.".format(a_mr['web_url']) - else: - msg = "\n{}: has been approved by {} and is waiting for your upvote.".format(a_mr['web_url'], also_approved[1:]) + # If the MR is no longer open, skip to the next MR, + # and don't include this MR in the next check + if a_mr['state'] != 'opened': + continue else: - msg = "\n{}: Your MR has approved by {} and is waiting for another upvote.".format(a_mr['web_url'], also_approved[1:]) + still_open_mrs[(project, iid, notify_rooms)] = True + + authored = (a_mr['author']['id'] == sender_gitlab_id) + already_approved = False + + # getapprovals is currently only available in GitLab >= 8.9 EE (not available in the CE yet) + # approvals = self.gitlab.getapprovals(a_mr['id']) + # also_approved = "" + # for approved in approvals['approved_by']: + # if approved['user']['id'] == sender_gitlab_id: + # already_approved = True + # else: + # also_approved += "," + approved['user']['name'] + + upvotes = a_mr['upvotes'] + if upvotes >= 2: + msg += "\n{}: has 2+ upvotes and could be merged in now.".format(a_mr['web_url']) + elif upvotes == 1: + if not authored: + msg += "\n{}: is waiting for another upvote.".format(a_mr['web_url']) + else: + msg += "\n{}: Your MR is waiting for another upvote.".format(a_mr['web_url']) - else: - if not authored: - msg = '\n{}: Has no upvotes and needs your attention.'.format(a_mr['web_url']) - else: - msg = '\n{}: Your MR has no upvotes.'.format(a_mr['web_url']) - + else: + if not authored: + msg += '\n{}: Has no upvotes and needs your attention.'.format(a_mr['web_url']) + else: + msg += '\n{}: Your MR has no upvotes.'.format(a_mr['web_url']) if msg == "": - response = 'I found no open merge requests for you.' + response = 'Hi {}\n{}'.format(sender, 'I found no open MRs for you.') else: - response = msg - - self.send(self.build_identifier(msg.frm), response) + response = 'Hi {}\n{}'.format(sender,msg) - self['OPEN_MRS'] = still_open_mrs + # self.send(self.build_identifier(msg.frm), response) + + with self.mutable('OPEN_MRS') as open_mrs: + open_mrs = still_open_mrs - return + return response @botcmd() - def hello(self,msg, args): + def hello(self, msg, args): return "Hi there" + + @botcmd() + def xyzzy(self, msg, args): + yield "/me whispers \"All open MRs have ben merged into master.\"" + sleep(5) + yield "(just kidding)" -- 2.39.2