##// END OF EJS Templates
hipchat: added a case for SVN commits to trunk
marcink -
r2648:79c76515 default
parent child Browse files
Show More
@@ -1,247 +1,251 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 from __future__ import unicode_literals
22 22 import deform
23 23 import logging
24 24 import requests
25 25 import colander
26 26 import textwrap
27 27 from mako.template import Template
28 28 from rhodecode import events
29 29 from rhodecode.translation import _
30 30 from rhodecode.lib import helpers as h
31 31 from rhodecode.lib.celerylib import run_task, async_task, RequestContextTask
32 32 from rhodecode.lib.colander_utils import strip_whitespace
33 33 from rhodecode.integrations.types.base import (
34 34 IntegrationTypeBase, CommitParsingDataHandler, render_with_traceback)
35 35
36 36 log = logging.getLogger(__name__)
37 37
38 38
39 39 class HipchatSettingsSchema(colander.Schema):
40 40 color_choices = [
41 41 ('yellow', _('Yellow')),
42 42 ('red', _('Red')),
43 43 ('green', _('Green')),
44 44 ('purple', _('Purple')),
45 45 ('gray', _('Gray')),
46 46 ]
47 47
48 48 server_url = colander.SchemaNode(
49 49 colander.String(),
50 50 title=_('Hipchat server URL'),
51 51 description=_('Hipchat integration url.'),
52 52 default='',
53 53 preparer=strip_whitespace,
54 54 validator=colander.url,
55 55 widget=deform.widget.TextInputWidget(
56 56 placeholder='https://?.hipchat.com/v2/room/?/notification?auth_token=?',
57 57 ),
58 58 )
59 59 notify = colander.SchemaNode(
60 60 colander.Bool(),
61 61 title=_('Notify'),
62 62 description=_('Make a notification to the users in room.'),
63 63 missing=False,
64 64 default=False,
65 65 )
66 66 color = colander.SchemaNode(
67 67 colander.String(),
68 68 title=_('Color'),
69 69 description=_('Background color of message.'),
70 70 missing='',
71 71 validator=colander.OneOf([x[0] for x in color_choices]),
72 72 widget=deform.widget.Select2Widget(
73 73 values=color_choices,
74 74 ),
75 75 )
76 76
77 77
78 78 repo_push_template = Template('''
79 79 <b>${data['actor']['username']}</b> pushed to repo <a href="${data['repo']['url']}">${data['repo']['repo_name']}</a>:
80 80 <br>
81 81 <ul>
82 82 %for branch, branch_commits in branches_commits.items():
83 83 <li>
84 % if branch:
84 85 <a href="${branch_commits['branch']['url']}">branch: ${branch_commits['branch']['name']}</a>
86 % else:
87 to trunk
88 % endif
85 89 <ul>
86 %for commit in branch_commits['commits']:
90 % for commit in branch_commits['commits']:
87 91 <li><a href="${commit['url']}">${commit['short_id']}</a> - ${commit['message_html']}</li>
88 %endfor
92 % endfor
89 93 </ul>
90 94 </li>
91 95 %endfor
92 96 ''')
93 97
94 98
95 99 class HipchatIntegrationType(IntegrationTypeBase, CommitParsingDataHandler):
96 100 key = 'hipchat'
97 101 display_name = _('Hipchat')
98 102 description = _('Send events such as repo pushes and pull requests to '
99 103 'your hipchat channel.')
100 104
101 105 @classmethod
102 106 def icon(cls):
103 107 return '''<?xml version="1.0" encoding="utf-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve"><g><g transform="translate(0.000000,511.000000) scale(0.100000,-0.100000)"><path fill="#205281" d="M4197.1,4662.4c-1661.5-260.4-3018-1171.6-3682.6-2473.3C219.9,1613.6,100,1120.3,100,462.6c0-1014,376.8-1918.4,1127-2699.4C2326.7-3377.6,3878.5-3898.3,5701-3730.5l486.5,44.5l208.9-123.3c637.2-373.4,1551.8-640.6,2240.4-650.9c304.9-6.9,335.7,0,417.9,75.4c185,174.7,147.3,411.1-89.1,548.1c-315.2,181.6-620,544.7-733.1,870.1l-51.4,157.6l472.7,472.7c349.4,349.4,520.7,551.5,657.7,774.2c784.5,1281.2,784.5,2788.5,0,4052.6c-236.4,376.8-794.8,966-1178.4,1236.7c-572.1,407.7-1264.1,709.1-1993.7,870.1c-267.2,58.2-479.6,75.4-1038,82.2C4714.4,4686.4,4310.2,4679.6,4197.1,4662.4z M5947.6,3740.9c1856.7-380.3,3127.6-1709.4,3127.6-3275c0-1000.3-534.4-1949.2-1466.2-2600.1c-188.4-133.6-287.8-226.1-301.5-284.4c-41.1-157.6,263.8-938.6,397.4-1020.8c20.5-10.3,34.3-44.5,34.3-75.4c0-167.8-811.9,195.3-1363.4,609.8l-181.6,137l-332.3-58.2c-445.3-78.8-1281.2-78.8-1702.6,0C2796-2569.2,1734.1-1832.6,1220.2-801.5C983.8-318.5,905,51.5,929,613.3c27.4,640.6,243.2,1192.1,685.1,1740.3c620,770.8,1661.5,1305.2,2822.8,1452.5C4806.9,3854,5553.7,3819.7,5947.6,3740.9z"/><path fill="#205281" d="M2381.5-345.9c-75.4-106.2-68.5-167.8,34.3-322c332.3-500.2,1010.6-928.4,1760.8-1120.2c417.9-106.2,1226.4-106.2,1644.3,0c712.5,181.6,1270.9,517.3,1685.4,1014C7681-561.7,7715.3-424.7,7616-325.4c-89.1,89.1-167.9,65.1-431.7-133.6c-835.8-630.3-2028-856.4-3086.5-585.8C3683.3-938.6,3142-685,2830.3-448.7C2576.8-253.4,2463.7-229.4,2381.5-345.9z"/></g></g><!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon --></svg>'''
104 108
105 109 valid_events = [
106 110 events.PullRequestCloseEvent,
107 111 events.PullRequestMergeEvent,
108 112 events.PullRequestUpdateEvent,
109 113 events.PullRequestCommentEvent,
110 114 events.PullRequestReviewEvent,
111 115 events.PullRequestCreateEvent,
112 116 events.RepoPushEvent,
113 117 events.RepoCreateEvent,
114 118 ]
115 119
116 120 def send_event(self, event):
117 121 if event.__class__ not in self.valid_events:
118 122 log.debug('event not valid: %r' % event)
119 123 return
120 124
121 125 if event.name not in self.settings['events']:
122 126 log.debug('event ignored: %r' % event)
123 127 return
124 128
125 129 data = event.as_dict()
126 130
127 131 text = '<b>%s<b> caused a <b>%s</b> event' % (
128 132 data['actor']['username'], event.name)
129 133
130 134 log.debug('handling hipchat event for %s' % event.name)
131 135
132 136 if isinstance(event, events.PullRequestCommentEvent):
133 137 text = self.format_pull_request_comment_event(event, data)
134 138 elif isinstance(event, events.PullRequestReviewEvent):
135 139 text = self.format_pull_request_review_event(event, data)
136 140 elif isinstance(event, events.PullRequestEvent):
137 141 text = self.format_pull_request_event(event, data)
138 142 elif isinstance(event, events.RepoPushEvent):
139 143 text = self.format_repo_push_event(data)
140 144 elif isinstance(event, events.RepoCreateEvent):
141 145 text = self.format_repo_create_event(data)
142 146 else:
143 147 log.error('unhandled event type: %r' % event)
144 148
145 149 run_task(post_text_to_hipchat, self.settings, text)
146 150
147 151 def settings_schema(self):
148 152 schema = HipchatSettingsSchema()
149 153 schema.add(colander.SchemaNode(
150 154 colander.Set(),
151 155 widget=deform.widget.CheckboxChoiceWidget(
152 156 values=sorted(
153 157 [(e.name, e.display_name) for e in self.valid_events]
154 158 )
155 159 ),
156 160 description="Events activated for this integration",
157 161 name='events'
158 162 ))
159 163
160 164 return schema
161 165
162 166 def format_pull_request_comment_event(self, event, data):
163 167 comment_text = data['comment']['text']
164 168 if len(comment_text) > 200:
165 169 comment_text = '{comment_text}<a href="{comment_url}">...<a/>'.format(
166 170 comment_text=h.html_escape(comment_text[:200]),
167 171 comment_url=data['comment']['url'],
168 172 )
169 173
170 174 comment_status = ''
171 175 if data['comment']['status']:
172 176 comment_status = '[{}]: '.format(data['comment']['status'])
173 177
174 178 return (textwrap.dedent(
175 179 '''
176 180 {user} commented on pull request <a href="{pr_url}">{number}</a> - {pr_title}:
177 181 >>> {comment_status}{comment_text}
178 182 ''').format(
179 183 comment_status=comment_status,
180 184 user=data['actor']['username'],
181 185 number=data['pullrequest']['pull_request_id'],
182 186 pr_url=data['pullrequest']['url'],
183 187 pr_status=data['pullrequest']['status'],
184 188 pr_title=h.html_escape(data['pullrequest']['title']),
185 189 comment_text=h.html_escape(comment_text)
186 190 )
187 191 )
188 192
189 193 def format_pull_request_review_event(self, event, data):
190 194 return (textwrap.dedent(
191 195 '''
192 196 Status changed to {pr_status} for pull request <a href="{pr_url}">#{number}</a> - {pr_title}
193 197 ''').format(
194 198 user=data['actor']['username'],
195 199 number=data['pullrequest']['pull_request_id'],
196 200 pr_url=data['pullrequest']['url'],
197 201 pr_status=data['pullrequest']['status'],
198 202 pr_title=h.html_escape(data['pullrequest']['title']),
199 203 )
200 204 )
201 205
202 206 def format_pull_request_event(self, event, data):
203 207 action = {
204 208 events.PullRequestCloseEvent: 'closed',
205 209 events.PullRequestMergeEvent: 'merged',
206 210 events.PullRequestUpdateEvent: 'updated',
207 211 events.PullRequestCreateEvent: 'created',
208 212 }.get(event.__class__, str(event.__class__))
209 213
210 214 return ('Pull request <a href="{url}">#{number}</a> - {title} '
211 215 '{action} by <b>{user}</b>').format(
212 216 user=data['actor']['username'],
213 217 number=data['pullrequest']['pull_request_id'],
214 218 url=data['pullrequest']['url'],
215 219 title=h.html_escape(data['pullrequest']['title']),
216 220 action=action
217 221 )
218 222
219 223 def format_repo_push_event(self, data):
220 224 branches_commits = self.aggregate_branch_data(
221 225 data['push']['branches'], data['push']['commits'])
222 226
223 227 result = render_with_traceback(
224 228 repo_push_template,
225 229 data=data,
226 230 branches_commits=branches_commits,
227 231 )
228 232 return result
229 233
230 234 def format_repo_create_event(self, data):
231 235 return '<a href="{}">{}</a> ({}) repository created by <b>{}</b>'.format(
232 236 data['repo']['url'],
233 237 h.html_escape(data['repo']['repo_name']),
234 238 data['repo']['repo_type'],
235 239 data['actor']['username'],
236 240 )
237 241
238 242
239 243 @async_task(ignore_result=True, base=RequestContextTask)
240 244 def post_text_to_hipchat(settings, text):
241 245 log.debug('sending %s to hipchat %s' % (text, settings['server_url']))
242 246 resp = requests.post(settings['server_url'], json={
243 247 "message": text,
244 248 "color": settings.get('color', 'yellow'),
245 249 "notify": settings.get('notify', False),
246 250 })
247 251 resp.raise_for_status() # raise exception on a failed request
General Comments 0
You need to be logged in to leave comments. Login now