2 from errbot
import BotPlugin
, botcmd
, webhook
3 from errbot
.backends
import xmpp
4 from errcron
.bot
import CrontabMixin
7 from Secret
import admin_token
10 class Marge(BotPlugin
, CrontabMixin
):
12 I remind you about merge requests
16 Add a merge request webook of the form 'https://webookserver/margeboot/<rooms>' to the projects you want tracked.
17 <rooms> should be a comma-separated list of rooms you want notified.
19 Add <roomname>@domain to the CHATROOM_PRESENCE list in config.py for rooms margebot should join
23 '0 11,17 * * * .crontab_hook' # 7:00AM and 1:00PM EST warnings
26 def __init__(self
, *args
, **kwargs
):
28 self
.chatroom_host
= None
29 super().__init
__(*args
, **kwargs
)
31 def get_configuration_template(self
):
32 return {'GIT_HOST': 'gitlab.example.com',
33 'CHATROOM_HOST': 'conference.jabber.example.com'}
35 def check_configuration(self
, configuration
):
36 super().check_configuration(configuration
)
40 self
.log
.info('Margebot is not configured. Forbid activation')
42 self
.git_host
= self
.config
['GIT_HOST']
43 self
.chatroom_host
= self
.config
['CHATROOM_HOST']
44 self
.gitlab
= gitlab
.Gitlab(self
.git_host
,admin_token
,verify_ssl
=False)
49 # TODO: Anything special for closing gitlab ?
52 # def configure(self, configuration):
53 # ## TODO: set up a connection to the gitlab API
55 # self.gitlab = gitlab.Gitlab(self.git_host,admin_token,verify_ssl=False)
57 @webhook('/margebot/<rooms>/')
58 def gitlab_hook(self
, request
, rooms
):
60 Webhook that listens on http://<server>:<port>/gitlab
63 # TODO: Will errbot return a json struct or not ?
65 # verify it's a merge request
66 if request
['object_kind'] != 'merge_request':
67 self
.log
.error('expecting object_kind of merge_request but got {}'.format(request
['object_kind']))
68 self
.log
.error('request: {}'.format(request
))
69 elif request
['object_attributes']['state'] == 'opened':
70 if request
['object_attributes']['work_in_progress']:
74 url
= request
['object_attributes']['url']
75 state
= request
['object_attributes']['state']
76 title
= request
['object_attributes']['title']
78 author_id
= request
['object_attributes']['author_id'] # map this to user name ...
79 author
= self
.gitlab
.getuser(author_id
)
80 author_name
= author
['username']
82 target_project_id
= request
['object_attributes']['target_project_id']
83 iid
= request
['object_attributes']['iid']
85 user_name
= request
['user']['username'] # will this always be Administrator ?
87 message
= "Reviews: {} has opened a new {} MR: {}\n{}".format(author_name
, wip
, title
, url
)
89 # TODO: Maybe also check the notify_<room> labels assigned to the MR as well ?
90 #mr_rooms = self.get_mr_rooms(target_project_id)
91 for a_room
in rooms
.split(','):
93 self
.send( self
.build_identifier(a_room
+ '@' + self
.chatroom_host
), message
)
95 with self
.mutable('OPEN_MRS') as open_mrs
:
96 open_mrs
[(target_project_id
,iid
,rooms
)] = True
100 def crontab_hook(self
):
102 Send a scheduled message to the rooms margebot is watching about open MRs the room cares about.
105 reminder_msg
= {} # Map of reminder_msg['roomname@domain'] = msg
107 # initialize the reminders
110 reminder_msg
[a_room
.node()] = ''
114 # Let's walk through the MRs we've seen already:
115 for (project
,iid
,notify_rooms
) in self
['OPEM_MRS']:
117 # Lookup the MR from the project/iid
118 a_mr
= self
.gitlab
.getmergerequest(project
, iid
)
120 # If the MR is no longer open, skip to the next MR,
121 # and don't include this MR in the next check
122 if a_mr
['state'] != 'open':
125 still_open_mrs
[(project
, iid
, notify_rooms
)] = True
127 # TODO: Warn if an open MR has has conflicts (merge_status == ??)
128 # TODO: Include the count of opened MR notes (does the API show resolved state ??)
130 approvals
= self
.gitlab
.getapprovals(a_mr
['id'])
132 for approved
in approvals
['approved_by']:
133 also_approved
+= "," + approved
['user']['name']
135 upvotes
= a_mr
['upvotes']
137 msg
= "\n{}: Has 2+ upvotes and could be merged in now.".format(a_mr
['web_url'])
139 msg
= "\n{}: {} already approved and is waiting for another upvote.".format(a_mr
['web_url'], also_approved
[1:])
141 msg
= '\n{}: Has no upvotes.'.format(a_mr
['web_url'])
143 for a_room
in notify_rooms
.split(','):
144 reminder_msg
[a_room
] += msg
146 # Remind each of the rooms about open MRs
147 for a_room
, room_msg
in reminder_msg
.iteritems():
150 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
))
152 self
['OPEN_MRS'] = still_open_mrs
155 def reviews(self
, msg
, args
): # a command callable with !mrs
157 Returns a list of MRs that are waiting for some luv.
158 Also returns a list of MRs that have had enough luv but aren't merged in yet.
162 send_gitlab_id
= None
163 for user
in self
.gitlab
.getusers():
164 if user
['username'] == sender
.node
:
165 sender_gitlab_id
= user
['id']
168 if not send_gitlab_id
:
169 self
.log
.error('problem mapping {} to a gitlab user'.format(sender
.node
))
170 self
.send(self
.build_identifier(msg
.frm
), "Sorry I couldn't find your gitlab ID")
173 # TODO: how to get the room the message was sent from ? I'm assuming this will either be in msg.frm or msg.to
174 # TODO: weed out MRs the sender opened or otherwise indicate they've opened or have already +1'd
176 roomname
= msg
.to
.domain
#???
178 # Let's walk through the MRs we've seen already:
181 for (project
,iid
,notify_rooms
) in self
['OPEM_MRS']:
183 # Lookup the MR from the project/iid
184 a_mr
= self
.gitlab
.getmergerequest(project
, iid
)
186 # If the MR is no longer open, skip to the next MR,
187 # and don't include this MR in the next check
188 if a_mr
['state'] != 'open':
191 still_open_mrs
[(project
, iid
, notify_rooms
)] = True
193 authored
= (a_mr
['author_id'] == sender_gitlab_id
)
194 already_approved
= False
196 approvals
= self
.gitlab
.getapprovals(a_mr
['id'])
198 for approved
in approvals
['approved_by']:
199 if approved
['user']['id'] == sender_gitlab_id
:
200 already_approved
= True
202 also_approved
+= "," + approved
['user']['name']
204 upvotes
= a_mr
['upvotes']
206 msg
= "\n{}: has 2+ upvotes from {} and could be merged in now.".format(a_mr
['web_url'], also_approved
[1:])
210 msg
= "\n{}: has already been approved by you and is waiting for another upvote.".format(a_mr
['web_url'])
212 msg
= "\n{}: has been approved by {} and is waiting for your upvote.".format(a_mr
['web_url'], also_approved
[1:])
214 msg
= "\n{}: Your MR has approved by {} and is waiting for another upvote.".format(a_mr
['web_url'], also_approved
[1:])
218 msg
= '\n{}: Has no upvotes and needs your attention.'.format(a_mr
['web_url'])
220 msg
= '\n{}: Your MR has no upvotes.'.format(a_mr
['web_url'])
224 response
= 'I found no open merge requests for you.'
228 self
.send(self
.build_identifier(msg
.frm
), response
)
230 self
['OPEN_MRS'] = still_open_mrs
235 def hello(self
,msg
, args
):