From 8d900be5af63ddf93b19adcd4e960167cc6d50ff Mon Sep 17 00:00:00 2001 From: Jude N Date: Sun, 14 Apr 2019 09:09:28 -0400 Subject: [PATCH] Tests are passing again --- .pylintrc | 2 +- plugins/marge.py | 149 +++++++++++++++++++++++++++++++++++++++----- tests/test_marge.py | 18 +++--- 3 files changed, 144 insertions(+), 25 deletions(-) diff --git a/.pylintrc b/.pylintrc index 20e66a0..694a07c 100644 --- a/.pylintrc +++ b/.pylintrc @@ -297,7 +297,7 @@ callbacks=cb_,_cb [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. -notes=FIXME,XXX,TODO +notes=FIXME,XXX [DESIGN] diff --git a/plugins/marge.py b/plugins/marge.py index 2764209..e81e00a 100755 --- a/plugins/marge.py +++ b/plugins/marge.py @@ -3,13 +3,55 @@ Margebot: A Errbot Plugin for Gitlab MR reminders """ import re from datetime import datetime, timezone +from time import sleep from dateutil import parser from dateutil.tz import tzutc from dateutil.relativedelta import relativedelta -from errbot import BotPlugin, arg_botcmd, botcmd, re_botcmd, webhook +from errbot import BotPlugin, botcmd, arg_botcmd, re_botcmd, webhook from errbot.templating import tenv from errcron.bot import CrontabMixin import gitlab +import requests + + +def addprojecthook_extra(self, project_id, url, push=False, issues=False, merge_requests=False, tag_push=False, extra_data=None): + """ + A copy parent addprojecthook with an extra_data field + """ + data = {"id": project_id, "url": url} + if extra_data: + for ed_key, ed_value in extra_data.items(): + data[ed_key] = ed_value + data['push_events'] = int(bool(push)) + data['issues_events'] = int(bool(issues)) + data['merge_requests_events'] = int(bool(merge_requests)) + data['tag_push_events'] = int(bool(tag_push)) + request = requests.post("{0}/{1}/hooks".format(self.projects_url, project_id), + headers=self.headers, data=data, verify=self.verify_ssl) + if request.status_code == 201: + return request.json() + return False + +gitlab.Gitlab.addprojecthook_extra = addprojecthook_extra + + +def editprojecthook_extra(self, project_id, hook_id, url, push=False, issues=False, merge_requests=False, tag_push=False, extra_data=None): + """ + A copy of the parent editprojecthook with an extra_data field + """ + data = {"id": project_id, "hook_id": hook_id, "url": url} + if extra_data: + for ed_key, ed_value in extra_data.items(): + data[ed_key] = ed_value + data['push_events'] = int(bool(push)) + data['issues_events'] = int(bool(issues)) + data['merge_requests_events'] = int(bool(merge_requests)) + data['tag_push_events'] = int(bool(tag_push)) + request = requests.put("{0}/{1}/hooks/{2}".format(self.projects_url, project_id, hook_id), + headers=self.headers, data=data, verify=self.verify_ssl) + return request.status_code == 200 + +gitlab.Gitlab.editprojecthook_extra = editprojecthook_extra def deltastr(any_delta): @@ -144,7 +186,13 @@ class Marge(BotPlugin, CrontabMixin): target_project_id = request['object_attributes']['target_project_id'] iid = request['object_attributes']['iid'] - mr_id = request['object_attributes']['id'] + + # If the MR is tagged 'never-close' ignore it + if 'labels' in request: + for a_label in request['labels']: + if a_label['title'] == 'never-close': + self.log.info("Skipping never-close notice for {} MR".format(url)) + return "OK" msg_template = "Hi there ! {} has opened a new {}MR: \"{}\"\n{}/merge_requests/{}" msg = msg_template.format(author_name, wip, title, url, iid) @@ -155,14 +203,21 @@ class Marge(BotPlugin, CrontabMixin): open_mrs = self['OPEN_MRS'] - if (target_project_id, mr_id, rooms) not in open_mrs: + if (target_project_id, iid, rooms) not in open_mrs: for a_room in rooms.split(','): if self.config: self.send(self.build_identifier(a_room + '@' + self.chatroom_host), msg) - self.log.info("webhook: Saving ({}, {}, {})".format(target_project_id, mr_id, rooms)) - open_mrs[(target_project_id, mr_id, rooms)] = True + self.log.info("webhook: Saving ({}, {}, {})".format(target_project_id, iid, rooms)) + open_mrs[(target_project_id, iid, rooms)] = True self['OPEN_MRS'] = open_mrs + + # TODO: Add check if an MR has toggled the WIP indicator + # (trigger on updates (what's that look like in request['object_attributes']['state']) + # Then check in request['changes']['title']['previous'] starts with 'WIP:' + # but not request['changes']['title']['current'], and vice versa + # See https://gitlab.com/gitlab-org/gitlab-ce/issues/53529 + return "OK" def mr_status_msg(self, a_mr, author=None): @@ -201,9 +256,15 @@ class Marge(BotPlugin, CrontabMixin): # 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 = "" + # approved = "" # for approved in approvals['approved_by']: - # also_approved += "," + approved['user']['name'] + # approved += "," + approvals['user']['name'] + + # See https://gitlab.com/gitlab-org/gitlab-ce/issues/35498 + # awards = GET /projects/:id/merge_requests/:merge_request_iid/award_emoji + # for an award in awards: + # if name==??? and award_type==???: + # approved += "," + award["user"]["username"] upvotes = a_mr['upvotes'] msg = "{} (opened {})".format(a_mr['web_url'], str_open_since) @@ -249,6 +310,7 @@ class Marge(BotPlugin, CrontabMixin): # initialize the reminders rooms = self.rooms() for a_room in rooms: + self.log.info("poller: a_room.node: {}".format(a_room.node)) reminder_msg[a_room.node] = [] still_open_mrs = {} @@ -282,7 +344,12 @@ class Marge(BotPlugin, CrontabMixin): continue for a_room in notify_rooms.split(','): - reminder_msg[a_room].append(msg_dict) + if a_room in reminder_msg: + reminder_msg[a_room].append(msg_dict) + else: + self.log.error("{} not in reminder_msg (project_id={}, mr_id={})".format(a_room, project_id, mr_id)) + + self['OPEN_MRS'] = open_mrs # Remind each of the rooms about open MRs for a_room, room_msg_list in reminder_msg.items(): @@ -333,6 +400,7 @@ class Marge(BotPlugin, CrontabMixin): msg = "" still_open_mrs = {} open_mrs = self['OPEN_MRS'] + self.log.info('open_mrs: {}'.format(open_mrs)) for (project, mr_id, notify_rooms) in open_mrs: # Lookup the MR from the project/id @@ -350,6 +418,7 @@ class Marge(BotPlugin, CrontabMixin): # If the MR is no longer open, skip to the next MR, # and don't include this MR in the next check if 'opened' not in a_mr['state']: + self.log.info('state not opened: {}'.format(a_mr['state'])) continue else: still_open_mrs[(project, mr_id, notify_rooms)] = True @@ -396,7 +465,7 @@ class Marge(BotPlugin, CrontabMixin): return msg else: for a_hook in hooks: - self.log.info('a_hook: {} {}'.format(a_hook, self.webhook_url)) + self.log.info('a_hook: {}'.format(a_hook)) if a_hook['merge_requests_events'] and a_hook['url'].startswith(self.webhook_url): marge_hook = a_hook break @@ -411,11 +480,11 @@ class Marge(BotPlugin, CrontabMixin): self.log.info('watchrepo: {}'.format(msg)) return msg else: - hook_updated = self.gitlab.editprojecthook(target_project_id, marge_hook['id'], url, merge_requests=True) + hook_updated = self.gitlab.editprojecthook_extra(target_project_id, marge_hook['id'], url, merge_requests=True, extra_data={'enable_ssl_verification': True}) s_watch_msg = "Updating room list for {} MRs from {} to {}".format(repo, old_rooms, rooms) s_action = "update" else: - hook_updated = self.gitlab.addprojecthook(target_project_id, url, merge_requests=True) + hook_updated = self.gitlab.addprojecthook_extra(target_project_id, url, merge_requests=True, extra_data={'enable_ssl_verification': True}) s_watch_msg = "Now watching for new MRs in the {} repo to the {} room(s)".format(repo, rooms) s_action = "add" @@ -442,14 +511,17 @@ class Marge(BotPlugin, CrontabMixin): # If adding a new repo, check for existing opened/reopened MRs in the repo. else: + # For debugging the 'watchrepo didn't find my MR' issue. + # mr_list = self.gitlab.getmergerequests(target_project_id, page=1, per_page=100) + # self.log.error('juden: mr_list state: {}'.format(mr_list[0]['state'])) for state in ['opened', 'reopened']: page = 1 mr_list = self.gitlab.getmergerequests(target_project_id, page=page, per_page=100, state=state) while (mr_list is not False) and (mr_list != []): for an_mr in mr_list: mr_count += 1 - self.log.info('watchrepo: an_mr WATS THE ID\n{}'.format(an_mr)) - mr_id = an_mr['id'] + self.log.info('watchrepo: an_mr WATS THE IID\n{}'.format(an_mr)) + mr_id = an_mr['iid'] open_mrs[(target_project_id, mr_id, rooms)] = True # Get the next page of MRs page += 1 @@ -463,11 +535,19 @@ class Marge(BotPlugin, CrontabMixin): mr_msg = "1 open MR was found in the repo. Run !reviews to see the updated MR list." else: mr_msg = "{} open MRs were found in the repo. Run !reviews to see the updated MR list." - return "{}\n{}".format(s_watch_msg, mr_msg) # pragma pylint: disable=unused-argument + @botcmd() + def xyzzy(self, msg, args): + """ + Don't call this command... + """ + yield "/me whispers \"All open MRs have been merged into master.\"" + sleep(5) + yield "(just kidding)" + @re_botcmd(pattern=r"I blame marge(bot)?", prefixed=False, flags=re.IGNORECASE) def dont_blame_margebot(self, msg, match): """ @@ -475,6 +555,16 @@ class Marge(BotPlugin, CrontabMixin): """ yield u"(\u300D\uFF9F\uFF9B\uFF9F)\uFF63NOOOooooo say it ain't so." + @re_botcmd(pattern=r"\u0028\u256F\u00B0\u25A1\u00B0\uFF09\u256F\uFE35\u0020\u253B(\u2501+)\u253B", prefixed=False) + def deflipped(self, msg, match): + """ + Unflip a properly sized table + """ + table_len = len(match.group(1)) + deflip_table = u"\u252c" + (u"\u2500" * table_len) + u"\u252c \u30ce( \u309c-\u309c\u30ce)" + # yield u"\u252c\u2500\u2500\u252c \u30ce( \u309c-\u309c\u30ce)" + yield deflip_table + @re_botcmd(pattern=r"good bot", prefixed=False, flags=re.IGNORECASE) def best_bot(self, msg, match): """ @@ -489,13 +579,42 @@ class Marge(BotPlugin, CrontabMixin): """ return "More like MargeFest, amirite ?" +# ha the dev-infra room sez koji sooooooooooooooooooooooooooooooooooooooooooo much +# @re_botcmd(pattern=r"k+o+j+i+", prefixed=False, flags=re.IGNORECASE) +# def koji(self, msg, args): +# """ +# More like daikaiju, amirite ? +# """ +# return "More like kaiju, amirite ?" + + @re_botcmd(pattern=r"peruvian chicken", prefixed=False, flags=re.IGNORECASE) + def booruvian(self, msg, args): + """ + They put hard boiled eggs in their tamales too. + """ + return "More like Booruvian chicken, amirite ?" + + @re_botcmd(pattern=r"booruvian chicken", prefixed=False, flags=re.IGNORECASE) + def booihoohooruvian(self, msg, args): + """ + That chicken I do not like. + """ + return "More like Boohoohooruvian chicken, amirite ?" + @re_botcmd(pattern=r"margebot sucks", prefixed=False, flags=re.IGNORECASE) - def margebot_sucks(self, msg, args): + def new_agenda_item(self, msg, args): """ Bring it up with the committee """ return "Bring it up with the Margebot steering committee." + @re_botcmd(pattern=r"jackie", prefixed=False, flags=re.IGNORECASE) + def jackie(self, msg, args): + """ + Who dat ? + """ + return "I don't know any Jackies. I'm calling security." + @re_botcmd(pattern=r".*", prefixed=True) def catchall(self, msg, args): """ diff --git a/tests/test_marge.py b/tests/test_marge.py index 2fc7429..d8b85b3 100644 --- a/tests/test_marge.py +++ b/tests/test_marge.py @@ -166,13 +166,14 @@ class TestMarge(object): return [] monkeypatch.setattr(gitlab.Gitlab, 'getmergerequests', mock_getmergerequests) - def mock_addprojecthook(self, project, url, **kwargs): + def mock_addprojecthook_extra(self, project, url, push=False, issues=False, merge_requests=False, tag_push=False, extra_data=None): return True - monkeypatch.setattr(gitlab.Gitlab, 'addprojecthook', mock_addprojecthook) + monkeypatch.setattr(gitlab.Gitlab, 'addprojecthook_extra', mock_addprojecthook_extra, raising=False) - def mock_editprojecthook(self, project, hook_id, url, **kwargs): + def mock_editprojecthook_extra(self, project, hook_id, url, push=False, issues=False, merge_requests=False, tag_push=False, extra_data=None): return True - monkeypatch.setattr(gitlab.Gitlab, 'editprojecthook', mock_editprojecthook) + monkeypatch.setattr(gitlab.Gitlab, 'editprojecthook_extra', mock_editprojecthook_extra, raising=False) + return gitlab.Gitlab @@ -180,12 +181,12 @@ class TestMarge(object): # def MargeGitlab(self, monkeypatch): # def mock_addprojecthook_extra(self, project, url, push=False, issues=False, merge_requests=False, tag_push=False, extra_data=None): # return True -# monkeypatch.setattr('plugins.marge.MargeGitlab.addprojecthook_extra', mock_addprojecthook_extra) +# monkeypatch.setattr(plugins.marge.MargeGitlab, 'addprojecthook_extra') # # def mock_editprojecthook_extra(self, project, hook_id, url, push=False, issues=False, merge_requests=False, tag_push=False, extra_data=None): # return True -# monkeypatch.setattr('plugins.marge.MargeGitlab.editprojecthook_extra', mock_editprojecthook_extra) -# return marge.MargeGitlab +# monkeypatch.setattr(plugins.marge.MargeGitlab, 'editprojecthook_extra') +# return plugins.marge.MargeGitlab @pytest.fixture def gitlab_no_reviews(self, gitlab, monkeypatch): @@ -196,7 +197,7 @@ class TestMarge(object): @pytest.fixture def gitlab_one_review(self, gitlab, monkeypatch): - ret_mr = {'id': 'mr_id', + ret_mr = {'iid': 'mr_id', 'author': {'id': 2001}, 'created_at': 'Oct 29, 2017 2:37am', 'merge_status': 'can_be_merged', @@ -278,7 +279,6 @@ class TestMarge(object): pm = margebot.pop_message() assert 'Updating room list for group/new_repo MRs from room1,room2,room3 to room4,room5,room6' in pm -# @pytest.mark.skip(reason='until I figure out how to mock MargeGitlab') def test_watchrepo_existing_mr(self, margebot, gitlab_one_review): margebot.push_message('!watchrepo sample/mr room1,room2,room3') pm = margebot.pop_message() -- 2.39.2