##// END OF EJS Templates
env-variables: make it safer if there's a syntax problem inside .ini file....
env-variables: make it safer if there's a syntax problem inside .ini file. It's better to not crash, since it means server wont start. Let users fix problems instead of breaking the startup because of that.

File last commit:

r3218:5fbf95d0 default
r3237:5cf82ecc default
Show More
hipchat.py
253 lines | 10.7 KiB | text/x-python | PythonLexer
dan
integrations: add hipchat integration
r550 # -*- coding: utf-8 -*-
release: update copyright year to 2018
r2487 # Copyright (C) 2012-2018 RhodeCode GmbH
dan
integrations: add hipchat integration
r550 #
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
from __future__ import unicode_literals
import deform
import logging
import requests
import colander
import textwrap
from mako.template import Template
from rhodecode import events
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 from rhodecode.translation import _
dan
integrations: add hipchat integration
r550 from rhodecode.lib import helpers as h
celery: celery 4.X support. Fixes #4169...
r2359 from rhodecode.lib.celerylib import run_task, async_task, RequestContextTask
dan
integrations: add hipchat integration
r550 from rhodecode.lib.colander_utils import strip_whitespace
dan
integrations: use a common logic for parsing the commits branches inside the integrations that require it....
r2644 from rhodecode.integrations.types.base import (
integrations: implement retry to HTTP[S] calls for integrations.
r3110 IntegrationTypeBase, CommitParsingDataHandler, render_with_traceback,
requests_retry_call)
dan
integrations: add hipchat integration
r550
integrations: fix logger name
r647 log = logging.getLogger(__name__)
dan
integrations: add hipchat integration
r550
integrations: organized templates a bit more.
r3218 REPO_PUSH_TEMPLATE = Template('''
<b>${data['actor']['username']}</b> pushed to repo <a href="${data['repo']['url']}">${data['repo']['repo_name']}</a>:
<br>
<ul>
%for branch, branch_commits in branches_commits.items():
<li>
% if branch:
<a href="${branch_commits['branch']['url']}">branch: ${branch_commits['branch']['name']}</a>
% else:
to trunk
% endif
<ul>
% for commit in branch_commits['commits']:
<li><a href="${commit['url']}">${commit['short_id']}</a> - ${commit['message_html']}</li>
% endfor
</ul>
</li>
%endfor
''')
dan
integrations: add hipchat integration
r550
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 class HipchatSettingsSchema(colander.Schema):
dan
integrations: add hipchat integration
r550 color_choices = [
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 ('yellow', _('Yellow')),
('red', _('Red')),
('green', _('Green')),
('purple', _('Purple')),
('gray', _('Gray')),
dan
integrations: add hipchat integration
r550 ]
server_url = colander.SchemaNode(
colander.String(),
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 title=_('Hipchat server URL'),
description=_('Hipchat integration url.'),
dan
integrations: add hipchat integration
r550 default='',
preparer=strip_whitespace,
validator=colander.url,
widget=deform.widget.TextInputWidget(
placeholder='https://?.hipchat.com/v2/room/?/notification?auth_token=?',
),
)
notify = colander.SchemaNode(
colander.Bool(),
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 title=_('Notify'),
description=_('Make a notification to the users in room.'),
dan
integrations: add hipchat integration
r550 missing=False,
default=False,
)
color = colander.SchemaNode(
colander.String(),
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 title=_('Color'),
description=_('Background color of message.'),
dan
integrations: add hipchat integration
r550 missing='',
validator=colander.OneOf([x[0] for x in color_choices]),
widget=deform.widget.Select2Widget(
values=color_choices,
),
)
dan
integrations: use a common logic for parsing the commits branches inside the integrations that require it....
r2644 class HipchatIntegrationType(IntegrationTypeBase, CommitParsingDataHandler):
dan
integrations: add hipchat integration
r550 key = 'hipchat'
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 display_name = _('Hipchat')
description = _('Send events such as repo pushes and pull requests to '
'your hipchat channel.')
integrations: use classmethod for icon extraction for easier code readability.
r2576
@classmethod
def icon(cls):
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>'''
dan
integrations: add hipchat integration
r550 valid_events = [
events.PullRequestCloseEvent,
events.PullRequestMergeEvent,
events.PullRequestUpdateEvent,
events.PullRequestCommentEvent,
events.PullRequestReviewEvent,
events.PullRequestCreateEvent,
events.RepoPushEvent,
events.RepoCreateEvent,
]
def send_event(self, event):
if event.__class__ not in self.valid_events:
logging: use lazy parameter evaluation in log calls.
r3061 log.debug('event not valid: %r', event)
dan
integrations: add hipchat integration
r550 return
if event.name not in self.settings['events']:
logging: use lazy parameter evaluation in log calls.
r3061 log.debug('event ignored: %r', event)
dan
integrations: add hipchat integration
r550 return
data = event.as_dict()
text = '<b>%s<b> caused a <b>%s</b> event' % (
data['actor']['username'], event.name)
logging: use lazy parameter evaluation in log calls.
r3061 log.debug('handling hipchat event for %s', event.name)
dan
integrations: add hipchat integration
r550
if isinstance(event, events.PullRequestCommentEvent):
text = self.format_pull_request_comment_event(event, data)
elif isinstance(event, events.PullRequestReviewEvent):
text = self.format_pull_request_review_event(event, data)
elif isinstance(event, events.PullRequestEvent):
text = self.format_pull_request_event(event, data)
elif isinstance(event, events.RepoPushEvent):
text = self.format_repo_push_event(data)
elif isinstance(event, events.RepoCreateEvent):
text = self.format_repo_create_event(data)
else:
logging: use lazy parameter evaluation in log calls.
r3061 log.error('unhandled event type: %r', event)
dan
integrations: add hipchat integration
r550
run_task(post_text_to_hipchat, self.settings, text)
def settings_schema(self):
schema = HipchatSettingsSchema()
schema.add(colander.SchemaNode(
colander.Set(),
widget=deform.widget.CheckboxChoiceWidget(
values=sorted(
[(e.name, e.display_name) for e in self.valid_events]
)
),
description="Events activated for this integration",
name='events'
))
return schema
def format_pull_request_comment_event(self, event, data):
comment_text = data['comment']['text']
if len(comment_text) > 200:
comment_text = '{comment_text}<a href="{comment_url}">...<a/>'.format(
dan
hipchat: escape user entered data to avoid xss/formatting problems
r934 comment_text=h.html_escape(comment_text[:200]),
dan
integrations: add hipchat integration
r550 comment_url=data['comment']['url'],
)
comment_status = ''
if data['comment']['status']:
comment_status = '[{}]: '.format(data['comment']['status'])
return (textwrap.dedent(
'''
{user} commented on pull request <a href="{pr_url}">{number}</a> - {pr_title}:
>>> {comment_status}{comment_text}
''').format(
comment_status=comment_status,
user=data['actor']['username'],
number=data['pullrequest']['pull_request_id'],
pr_url=data['pullrequest']['url'],
pr_status=data['pullrequest']['status'],
dan
hipchat: escape user entered data to avoid xss/formatting problems
r934 pr_title=h.html_escape(data['pullrequest']['title']),
comment_text=h.html_escape(comment_text)
dan
integrations: add hipchat integration
r550 )
)
def format_pull_request_review_event(self, event, data):
return (textwrap.dedent(
'''
Status changed to {pr_status} for pull request <a href="{pr_url}">#{number}</a> - {pr_title}
''').format(
user=data['actor']['username'],
number=data['pullrequest']['pull_request_id'],
pr_url=data['pullrequest']['url'],
pr_status=data['pullrequest']['status'],
dan
hipchat: escape user entered data to avoid xss/formatting problems
r934 pr_title=h.html_escape(data['pullrequest']['title']),
dan
integrations: add hipchat integration
r550 )
)
def format_pull_request_event(self, event, data):
action = {
events.PullRequestCloseEvent: 'closed',
events.PullRequestMergeEvent: 'merged',
events.PullRequestUpdateEvent: 'updated',
events.PullRequestCreateEvent: 'created',
}.get(event.__class__, str(event.__class__))
return ('Pull request <a href="{url}">#{number}</a> - {title} '
integrations: sync slack/hipchat formatting
r937 '{action} by <b>{user}</b>').format(
dan
integrations: add hipchat integration
r550 user=data['actor']['username'],
number=data['pullrequest']['pull_request_id'],
url=data['pullrequest']['url'],
dan
hipchat: escape user entered data to avoid xss/formatting problems
r934 title=h.html_escape(data['pullrequest']['title']),
dan
integrations: add hipchat integration
r550 action=action
)
def format_repo_push_event(self, data):
dan
integrations: use a common logic for parsing the commits branches inside the integrations that require it....
r2644 branches_commits = self.aggregate_branch_data(
data['push']['branches'], data['push']['commits'])
dan
integrations: show branches/commits separately when posting push...
r776
dan
integrations: added safe renderers with detailed traceback information.
r2646 result = render_with_traceback(
integrations: organized templates a bit more.
r3218 REPO_PUSH_TEMPLATE,
dan
integrations: add hipchat integration
r550 data=data,
dan
integrations: show branches/commits separately when posting push...
r776 branches_commits=branches_commits,
dan
integrations: add hipchat integration
r550 )
return result
def format_repo_create_event(self, data):
return '<a href="{}">{}</a> ({}) repository created by <b>{}</b>'.format(
data['repo']['url'],
dan
hipchat: escape user entered data to avoid xss/formatting problems
r934 h.html_escape(data['repo']['repo_name']),
dan
integrations: add hipchat integration
r550 data['repo']['repo_type'],
data['actor']['username'],
)
celery: celery 4.X support. Fixes #4169...
r2359 @async_task(ignore_result=True, base=RequestContextTask)
dan
integrations: add hipchat integration
r550 def post_text_to_hipchat(settings, text):
logging: use lazy parameter evaluation in log calls.
r3061 log.debug('sending %s to hipchat %s', text, settings['server_url'])
requests: added a default timeout for operation calling the endpoint url....
r2950 json_message = {
dan
integrations: add hipchat integration
r550 "message": text,
"color": settings.get('color', 'yellow'),
"notify": settings.get('notify', False),
requests: added a default timeout for operation calling the endpoint url....
r2950 }
integrations: implement retry to HTTP[S] calls for integrations.
r3110 req_session = requests_retry_call()
resp = req_session.post(settings['server_url'], json=json_message, timeout=60)
dan
integrations: add hipchat integration
r550 resp.raise_for_status() # raise exception on a failed request