From de998790a97d3b41f0ca8ef98cae3a279b1931a2 Mon Sep 17 00:00:00 2001 From: "Nicholas O. Wilburn" Date: Thu, 25 Feb 2016 16:33:51 -0700 Subject: [PATCH 1/5] Adding support for multiple channels --- mattermost_gitlab/server.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/mattermost_gitlab/server.py b/mattermost_gitlab/server.py index e3d9870..8ea7372 100644 --- a/mattermost_gitlab/server.py +++ b/mattermost_gitlab/server.py @@ -37,13 +37,17 @@ def new_event(): if request.json is None: print('Invalid Content-Type') return 'Content-Type must be application/json and the request body must contain valid JSON', 400 + elif 'webhook_url' not in request.args: + print('Webhook URL not set') + return 'Webhook URL must be set' try: event = event_formatter.as_event(request.json) + webhook_url = request.args['webhook_url'] if event.should_report_event(app.config['REPORT_EVENTS']): text = event.format() - post_text(text) + post_text(text, webhook_url) except Exception: import traceback traceback.print_exc() @@ -60,13 +64,17 @@ def new_ci_event(): if request.json is None: print('Invalid Content-Type') return 'Content-Type must be application/json and the request body must contain valid JSON', 400 + elif 'webhook_url' not in request.args: + print('Webhok URL not set') + return 'Webhook URL must be set' try: event = event_formatter.CIEvent(request.json) + webhook_url = request.args['webhook_url'] if event.should_report_event(app.config['REPORT_EVENTS']): text = event.format() - post_text(text) + post_text(text, webhook_url) except Exception: import traceback traceback.print_exc() @@ -74,7 +82,7 @@ def new_ci_event(): return 'OK' -def post_text(text): +def post_text(text, webhook_url): """ Mattermost POST method, posts text to the Mattermost incoming webhook URL """ @@ -88,8 +96,9 @@ def post_text(text): if app.config['CHANNEL']: data['channel'] = app.config['CHANNEL'] + mattermost_webhook_url = app.config['MATTERMOST_URL'] + '/hooks/' + webhook_url headers = {'Content-Type': 'application/json'} - resp = requests.post(app.config['MATTERMOST_WEBHOOK_URL'], headers=headers, data=json.dumps(data)) + resp = requests.post(mattermost_webhook_url, headers=headers, data=json.dumps(data)) if resp.status_code is not requests.codes.ok: print('Encountered error posting to Mattermost URL %s, status=%d, response_body=%s' % (app.config['MATTERMOST_WEBHOOK_URL'], resp.status_code, resp.json())) @@ -97,7 +106,7 @@ def post_text(text): def parse_args(args=None): parser = argparse.ArgumentParser() - parser.add_argument('MATTERMOST_WEBHOOK_URL', help='The Mattermost webhook URL you created') + parser.add_argument('MATTERMOST_URL', help='The Mattermost URL') server_options = parser.add_argument_group("Server") server_options.add_argument('-p', '--port', type=int, default=5000) From 28609d6a6f5d19f2dad92381ed34aafa2f2d6ac1 Mon Sep 17 00:00:00 2001 From: "Nicholas O. Wilburn" Date: Thu, 25 Feb 2016 16:44:45 -0700 Subject: [PATCH 2/5] Fixing slashes in mattermost_webhook_url --- mattermost_gitlab/server.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mattermost_gitlab/server.py b/mattermost_gitlab/server.py index 8ea7372..7dbab3e 100644 --- a/mattermost_gitlab/server.py +++ b/mattermost_gitlab/server.py @@ -96,7 +96,11 @@ def post_text(text, webhook_url): if app.config['CHANNEL']: data['channel'] = app.config['CHANNEL'] - mattermost_webhook_url = app.config['MATTERMOST_URL'] + '/hooks/' + webhook_url + if (app.config['MATTERMOST_URL']).endswith('/'): + mattermost_webhook_url = app.config['MATTERMOST_URL'] + 'hooks/' + webhook_url + else: + mattermost_webhook_url = app.config['MATTERMOST_URL'] + '/hooks/' + webhook_url + headers = {'Content-Type': 'application/json'} resp = requests.post(mattermost_webhook_url, headers=headers, data=json.dumps(data)) From e5c568d169827f55c1f795b7955aa0c09eb61b16 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 26 Feb 2016 15:35:11 -0700 Subject: [PATCH 3/5] Modifying --- mattermost_gitlab/server.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/mattermost_gitlab/server.py b/mattermost_gitlab/server.py index 7dbab3e..eb5a11c 100644 --- a/mattermost_gitlab/server.py +++ b/mattermost_gitlab/server.py @@ -43,7 +43,7 @@ def new_event(): try: event = event_formatter.as_event(request.json) - webhook_url = request.args['webhook_url'] + webhook_url = request.args['webhook_url'].split('/')[:1] if event.should_report_event(app.config['REPORT_EVENTS']): text = event.format() @@ -82,7 +82,7 @@ def new_ci_event(): return 'OK' -def post_text(text, webhook_url): +def post_text(text, mattermost_webhook_url): """ Mattermost POST method, posts text to the Mattermost incoming webhook URL """ @@ -96,10 +96,6 @@ def post_text(text, webhook_url): if app.config['CHANNEL']: data['channel'] = app.config['CHANNEL'] - if (app.config['MATTERMOST_URL']).endswith('/'): - mattermost_webhook_url = app.config['MATTERMOST_URL'] + 'hooks/' + webhook_url - else: - mattermost_webhook_url = app.config['MATTERMOST_URL'] + '/hooks/' + webhook_url headers = {'Content-Type': 'application/json'} resp = requests.post(mattermost_webhook_url, headers=headers, data=json.dumps(data)) @@ -110,8 +106,6 @@ def post_text(text, webhook_url): def parse_args(args=None): parser = argparse.ArgumentParser() - parser.add_argument('MATTERMOST_URL', help='The Mattermost URL') - server_options = parser.add_argument_group("Server") server_options.add_argument('-p', '--port', type=int, default=5000) server_options.add_argument('--host', default='0.0.0.0') From f8f6893b608028634593bb3cd3a5fd24d1765e62 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 26 Feb 2016 15:45:25 -0700 Subject: [PATCH 4/5] modifying webhook_url --- build/lib/mattermost_gitlab/__init__.py | 0 build/lib/mattermost_gitlab/constants.py | 14 + .../lib/mattermost_gitlab/event_formatter.py | 262 ++++++++++++++++++ build/lib/mattermost_gitlab/mock_http.py | 181 ++++++++++++ build/lib/mattermost_gitlab/server.py | 181 ++++++++++++ ...termost_integration_gitlab-0.1.0-py2.7.egg | Bin 0 -> 20013 bytes mattermost_gitlab/server.py | 2 +- 7 files changed, 639 insertions(+), 1 deletion(-) create mode 100644 build/lib/mattermost_gitlab/__init__.py create mode 100644 build/lib/mattermost_gitlab/constants.py create mode 100644 build/lib/mattermost_gitlab/event_formatter.py create mode 100644 build/lib/mattermost_gitlab/mock_http.py create mode 100644 build/lib/mattermost_gitlab/server.py create mode 100644 dist/mattermost_integration_gitlab-0.1.0-py2.7.egg diff --git a/build/lib/mattermost_gitlab/__init__.py b/build/lib/mattermost_gitlab/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/mattermost_gitlab/constants.py b/build/lib/mattermost_gitlab/constants.py new file mode 100644 index 0000000..10fca16 --- /dev/null +++ b/build/lib/mattermost_gitlab/constants.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Python Future imports +from __future__ import unicode_literals, absolute_import, print_function + +# Python System imports + +PUSH_EVENT = 'push' +ISSUE_EVENT = 'issue' +TAG_EVENT = 'tag_push' +COMMENT_EVENT = 'note' +MERGE_EVENT = 'merge_request' +CI_EVENT = 'ci' diff --git a/build/lib/mattermost_gitlab/event_formatter.py b/build/lib/mattermost_gitlab/event_formatter.py new file mode 100644 index 0000000..a8a1079 --- /dev/null +++ b/build/lib/mattermost_gitlab/event_formatter.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Python Future imports +from __future__ import unicode_literals, absolute_import, print_function + +# Python System imports +import re + +from . import constants + + +def fix_gitlab_links(base_url, text): + """ + Fixes gitlab upload links that are relative and makes them absolute + """ + + matches = re.findall(r'(\[[^]]*\]\s*\((/[^)]+)\))', text) + + for (replace_string, link) in matches: + new_string = replace_string.replace(link, base_url + link) + text = text.replace(replace_string, new_string) + + return text + + +def add_markdown_quotes(text): + """ + Add Markdown quotes around a piece of text + """ + + if not text: + return '' + + split_desc = text.split('\n') + + for index, line in enumerate(split_desc): + split_desc[index] = '> ' + line + + return '\n'.join(split_desc) + + +class BaseEvent(object): + + def __init__(self, data): + self.data = data + self.object_kind = data['object_kind'] + + @property + def push_event(self): + raise NotImplementedError + + def should_report_event(self, report_events): + return report_events[self.object_kind] + + def format(self): + raise NotImplementedError + + def gitlab_user_url(self, username): + base_url = '/'.join(self.data['repository']['homepage'].split('/')[:-2]) + return '{}/u/{}'.format(base_url, username) + + +class PushEvent(BaseEvent): + + def format(self): + + if self.data['before'] == '0' * 40: + description = 'the first commit' + else: + description = '{} commit'.format(self.data['total_commits_count']) + if self.data['total_commits_count'] > 1: + description += "s" + + return '%s pushed %s into the `%s` branch for project [%s](%s).' % ( + self.data['user_name'], + description, + self.data['ref'], + self.data['repository']['name'], + self.data['repository']['homepage'] + ) + + +class IssueEvent(BaseEvent): + + @property + def action(self): + return self.data['object_attributes']['action'] + + def should_report_event(self, report_events): + return super(IssueEvent, self).should_report_event(report_events) and self.action != "update" + + def format(self): + description = add_markdown_quotes(self.data['object_attributes']['description']) + + if self.action == 'open': + verbose_action = 'created' + elif self.action == 'reopen': + verbose_action = 'reopened' + elif self.action == 'update': + verbose_action = 'updated' + elif self.action == 'close': + verbose_action = 'closed' + else: + raise NotImplementedError("Unsupported action %s for issue event" % self.action) + + text = '#### [%s](%s)\n*[Issue #%s](%s) %s by %s in [%s](%s) on [%s](%s)*\n %s' % ( + self.data['object_attributes']['title'], + self.data['object_attributes']['url'], + self.data['object_attributes']['iid'], + self.data['object_attributes']['url'], + verbose_action, + self.data['user']['username'], + self.data['repository']['name'], + self.data['repository']['homepage'], + self.data['object_attributes']['created_at'], + self.data['object_attributes']['url'], + description + ) + + base_url = self.data['repository']['homepage'] + + return fix_gitlab_links(base_url, text) + + +class TagEvent(BaseEvent): + def format(self): + return '%s pushed tag `%s` to the project [%s](%s).' % ( + self.data['user_name'], + self.data['ref'], + self.data['repository']['name'], + self.data['repository']['homepage'] + ) + + +class NoteEvent(BaseEvent): + def format(self): + symbol = '' + type_grammar = 'a' + note_type = self.data['object_attributes']['noteable_type'].lower() + note_id = '' + parent_title = '' + + if note_type == 'mergerequest': + symbol = '!' + note_id = self.data['merge_request']['iid'] + parent_title = self.data['merge_request']['title'] + note_type = 'merge request' + elif note_type == 'snippet': + symbol = '$' + note_id = self.data['snippet']['iid'] + parent_title = self.data['snippet']['title'] + elif note_type == 'issue': + symbol = '#' + note_id = self.data['issue']['iid'] + parent_title = self.data['issue']['title'] + type_grammar = 'an' + + subtitle = '' + if note_type == 'commit': + subtitle = '%s' % self.data['commit']['id'] + else: + subtitle = '%s%s - %s' % (symbol, note_id, parent_title) + + description = add_markdown_quotes(self.data['object_attributes']['note']) + + text = '#### **New Comment** on [%s](%s)\n*[%s](%s) commented on %s %s in [%s](%s) on [%s](%s)*\n %s' % ( + subtitle, + self.data['object_attributes']['url'], + self.data['user']['username'], + self.gitlab_user_url(self.data['user']['username']), + type_grammar, + note_type, + self.data['repository']['name'], + self.data['repository']['homepage'], + self.data['object_attributes']['created_at'], + self.data['object_attributes']['url'], + description + ) + + base_url = self.data['repository']['homepage'] + + return fix_gitlab_links(base_url, text) + + +class MergeEvent(BaseEvent): + + @property + def action(self): + return self.data['object_attributes']['action'] + + def format(self): + + if self.action == 'open': + text_action = 'created a' + elif self.action == 'reopen': + text_action = 'reopened a' + elif self.action == 'update': + text_action = 'updated a' + elif self.action == 'merge': + text_action = 'accepted a' + elif self.action == 'close': + text_action = 'closed a' + else: + raise NotImplementedError('Unsupported action %s for merge event' % self.action) + + text = '#### [!%s - %s](%s)\n*[%s](%s) %s merge request in [%s](%s) on [%s](%s)*' % ( + self.data['object_attributes']['iid'], + self.data['object_attributes']['title'], + self.data['object_attributes']['url'], + self.data['user']['username'], + self.gitlab_user_url(self.data['user']['username']), + text_action, + self.data['object_attributes']['target']['name'], + self.data['object_attributes']['target']['web_url'], + self.data['object_attributes']['created_at'], + self.data['object_attributes']['url'] + ) + + if self.action == 'open': + description = add_markdown_quotes(self.data['object_attributes']['description']) + text = '%s\n %s' % ( + text, + description + ) + + base_url = self.data['object_attributes']['target']['web_url'] + + return fix_gitlab_links(base_url, text) + + +class CIEvent(BaseEvent): + + def __init__(self, data): + self.data = data + self.object_kind = "ci" + + def format(self): + icon = ':white_check_mark:' if self.data['build_status'] == "success" else ':x:' + return '%s %s build for the project [%s](%s) on commit %s.' % ( + icon, + self.data['build_status'].title(), + self.data['project_name'], + self.data['gitlab_url'], + self.data['sha'], + ) + + +EVENT_CLASS_MAP = { + constants.PUSH_EVENT: PushEvent, + constants.ISSUE_EVENT: IssueEvent, + constants.TAG_EVENT: TagEvent, + constants.COMMENT_EVENT: NoteEvent, + constants.MERGE_EVENT: MergeEvent, +} + + +def as_event(data): + if data['object_kind'] in EVENT_CLASS_MAP: + return EVENT_CLASS_MAP[data['object_kind']](data) + else: + raise NotImplementedError('Unsupported event of type %s' % data['object_kind']) diff --git a/build/lib/mattermost_gitlab/mock_http.py b/build/lib/mattermost_gitlab/mock_http.py new file mode 100644 index 0000000..a158085 --- /dev/null +++ b/build/lib/mattermost_gitlab/mock_http.py @@ -0,0 +1,181 @@ +# -*- coding: utf-8 -*- + +# Python Future imports +from __future__ import unicode_literals, absolute_import, print_function + +# Python System imports +import threading +from six.moves.SimpleHTTPServer import SimpleHTTPRequestHandler +from six.moves.BaseHTTPServer import HTTPServer +from six.moves.http_client import HTTPConnection +import socket + +# Django imports +# from django import ... + +# Third-party imports + +# Smart impulse common modules +# from smartimpulse import ... + +# Relative imports + +""" +code from http://www.ianlewis.org/en/testing-using-mocked-server + +import threading +import mock +import gc + +setup: + def setUp(self): + self.cond = threading.Condition() + self.server = mock.http.TestServer(port=9854, self.cond) + self.cond.acquire() + self.server.start() + + # Wait until the server is ready + while not self.server.ready: + # Collect left over servers so they release their + # sockets + import gc + gc.collect() + self.cond.wait() + + self.cond.release() + +tearDown: + def tearDown(self): + self.server.stop_server() + self.server = None +""" + + +def get_available_port(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind(('localhost', 0)) + __, port = sock.getsockname() + sock.close() + return port + + +class StoppableHttpServer(HTTPServer): + """http server that reacts to self.stop flag""" + + received_requests = [] + allow_reuse_address = True + + def serve_forever(self, poll_interval=0.5): + """Handle one request at a time until stopped.""" + self.stop = False + while not self.stop: + self.handle_request() + + +class TestRequestHandler(SimpleHTTPRequestHandler): + + def log_request(self, *args, **kwargs): + pass + + def do_POST(self): + """send 200 OK response, with 'OK' as content""" + # extract any POSTDATA + self.data = "" + if "Content-Length" in self.headers: + self.data = self.rfile.read(int(self.headers["Content-Length"])) + + self.server.received_requests.append({ + 'post': self.data, + }) + + self.send_response(200) + self.end_headers() + self.wfile.write('OK\n'.encode()) + + def do_QUIT(self): + """send 200 OK response, and set server.stop to True""" + self.send_response(200) + self.end_headers() + self.server.stop = True + + +class TestServer(threading.Thread): + """HTTP Server that runs in a thread and handles a predetermined number of requests""" + TIMEOUT = 10 + + def __init__(self, port, cond=None): + threading.Thread.__init__(self) + self.port = port + self.ready = False + self.cond = cond + + def run(self): + self.cond.acquire() + timeout = 0 + self.httpd = None + while self.httpd is None: + try: + self.httpd = StoppableHttpServer(('', self.port), TestRequestHandler) + except Exception as exc: + import socket + import errno + import time + if isinstance(exc, socket.error) and errno.errorcode[exc.args[0]] == 'EADDRINUSE' and timeout < self.TIMEOUT: + timeout += 1 + time.sleep(1) + else: + print(exc) + self.cond.notifyAll() + self.cond.release() + self.ready = True + raise exc + + self.ready = True + if self.cond: + self.cond.notifyAll() + self.cond.release() + self.httpd.serve_forever() + + def stop_server(self): + """send QUIT request to http server running on localhost:""" + conn = HTTPConnection("127.0.0.1:{}".format(self.port)) + conn.request("QUIT", "/") + conn.getresponse() + + +class MockHttpServerMixin(object): + + port = 9854 + + def setUp(self): + super(MockHttpServerMixin, self).setUp() + + try: + self.server.httpd.received_requests = [] + except AssertionError: + pass + + @classmethod + def setUpClass(cls): + super(MockHttpServerMixin, cls).setUpClass() + cls.cond = threading.Condition() + cls.server = TestServer(port=cls.port, cond=cls.cond) + cls.cond.acquire() + cls.server.start() + + # Wait until the server is ready + while not cls.server.ready: + # Collect left over servers so they release their + # sockets + import gc + gc.collect() + cls.cond.wait() + + cls.cond.release() + + @classmethod + def tearDownClass(cls): + super(MockHttpServerMixin, cls).tearDownClass() + cls.server.stop_server() + cls.server.httpd.server_close() + cls.server = None diff --git a/build/lib/mattermost_gitlab/server.py b/build/lib/mattermost_gitlab/server.py new file mode 100644 index 0000000..b0f9e8b --- /dev/null +++ b/build/lib/mattermost_gitlab/server.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Python Future imports +from __future__ import unicode_literals, absolute_import, print_function + +# Python System imports +import requests +import json +import argparse + + +# Third-party imports +from flask import Flask, request + +from . import event_formatter, constants + + +app = Flask(__name__) + + +@app.route('/') +def root(): + """ + Home handler + """ + + return "OK" + + +@app.route('/new_event', methods=['POST']) +def new_event(): + """ + GitLab event handler, handles POST events from a GitLab project + """ + + if request.json is None: + print('Invalid Content-Type') + return 'Content-Type must be application/json and the request body must contain valid JSON', 400 + elif 'webhook_url' not in request.args: + print('Webhook URL not set') + return 'Webhook URL must be set' + + try: + event = event_formatter.as_event(request.json) + webhook_url = request.args['webhook_url'] + + if event.should_report_event(app.config['REPORT_EVENTS']): + text = event.format() + post_text(text, webhook_url) + except Exception: + import traceback + traceback.print_exc() + + return 'OK' + + +@app.route('/new_ci_event', methods=['POST']) +def new_ci_event(): + """ + GitLab event handler, handles POST events from a GitLab CI project + """ + + if request.json is None: + print('Invalid Content-Type') + return 'Content-Type must be application/json and the request body must contain valid JSON', 400 + elif 'webhook_url' not in request.args: + print('Webhok URL not set') + return 'Webhook URL must be set' + + try: + event = event_formatter.CIEvent(request.json) + webhook_url = request.args['webhook_url'] + + if event.should_report_event(app.config['REPORT_EVENTS']): + text = event.format() + post_text(text, webhook_url) + except Exception: + import traceback + traceback.print_exc() + + return 'OK' + + +def post_text(text, mattermost_webhook_url): + """ + Mattermost POST method, posts text to the Mattermost incoming webhook URL + """ + + data = {} + data['text'] = text.strip() + if app.config['USERNAME']: + data['username'] = app.config['USERNAME'] + if app.config['ICON_URL']: + data['icon_url'] = app.config['ICON_URL'] + if app.config['CHANNEL']: + data['channel'] = app.config['CHANNEL'] + + + headers = {'Content-Type': 'application/json'} + resp = requests.post(mattermost_webhook_url, headers=headers, data=json.dumps(data)) + + if resp.status_code is not requests.codes.ok: + print('Encountered error posting to Mattermost URL %s, status=%d, response_body=%s' % (app.config['MATTERMOST_WEBHOOK_URL'], resp.status_code, resp.json())) + + +def parse_args(args=None): + parser = argparse.ArgumentParser() + server_options = parser.add_argument_group("Server") + server_options.add_argument('-p', '--port', type=int, default=5000) + server_options.add_argument('--host', default='0.0.0.0') + + parser.add_argument('-u', '--username', dest='USERNAME', default='gitlab') + parser.add_argument('--channel', dest='CHANNEL', default='') # Leave this blank to post to the default channel of your webhook + parser.add_argument('--icon', dest='ICON_URL', default='https://gitlab.com/uploads/project/avatar/13083/gitlab-logo-square.png') + + event_options = parser.add_argument_group("Events") + + event_options.add_argument( + '--push', + action='store_true', + dest=constants.PUSH_EVENT, + help='On pushes to the repository excluding tags' + ) + event_options.add_argument( + '--tag', + action='store_true', + dest=constants.TAG_EVENT, + help='On creation of tags' + ) + event_options.add_argument( + '--no-issue', + action='store_false', + dest=constants.ISSUE_EVENT, + help='On creation of a new issue' + ) + event_options.add_argument( + '--no-comment', + action='store_false', + dest=constants.COMMENT_EVENT, + help='When a new comment is made on commits, merge requests, issues, and code snippets' + ) + event_options.add_argument( + '--no-merge-request', + action='store_false', + dest=constants.MERGE_EVENT, + help='When a merge request is created' + ) + event_options.add_argument( + '--no-ci', + action='store_false', + dest=constants.CI_EVENT, + help='On Continuous Integration events' + ) + + options = vars(parser.parse_args(args=args)) + + host, port = options.pop("host"), options.pop("port") + + options["REPORT_EVENTS"] = { + constants.PUSH_EVENT: options.pop(constants.PUSH_EVENT), + constants.TAG_EVENT: options.pop(constants.TAG_EVENT), + constants.ISSUE_EVENT: options.pop(constants.ISSUE_EVENT), + constants.COMMENT_EVENT: options.pop(constants.COMMENT_EVENT), + constants.MERGE_EVENT: options.pop(constants.MERGE_EVENT), + constants.CI_EVENT: options.pop(constants.CI_EVENT), + } + + return host, port, options + + +def main(): + host, port, options = parse_args() + app.config.update(options) + + app.run(host=host, port=port) + + +if __name__ == "__main__": + + main() diff --git a/dist/mattermost_integration_gitlab-0.1.0-py2.7.egg b/dist/mattermost_integration_gitlab-0.1.0-py2.7.egg new file mode 100644 index 0000000000000000000000000000000000000000..9e515b11b350e8c8c89edb87389e8d154502ee28 GIT binary patch literal 20013 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 literal 0 HcmV?d00001 diff --git a/mattermost_gitlab/server.py b/mattermost_gitlab/server.py index eb5a11c..b0f9e8b 100644 --- a/mattermost_gitlab/server.py +++ b/mattermost_gitlab/server.py @@ -43,7 +43,7 @@ def new_event(): try: event = event_formatter.as_event(request.json) - webhook_url = request.args['webhook_url'].split('/')[:1] + webhook_url = request.args['webhook_url'] if event.should_report_event(app.config['REPORT_EVENTS']): text = event.format() From 0faf19a0470e335d8cac354b37df6a4c3c56149d Mon Sep 17 00:00:00 2001 From: root Date: Fri, 26 Feb 2016 16:10:02 -0700 Subject: [PATCH 5/5] Adding a verify ssl flag --- build/lib/mattermost_gitlab/server.py | 7 +++++-- ...termost_integration_gitlab-0.1.0-py2.7.egg | Bin 20013 -> 20172 bytes mattermost_gitlab/server.py | 7 +++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/build/lib/mattermost_gitlab/server.py b/build/lib/mattermost_gitlab/server.py index b0f9e8b..64eb162 100644 --- a/build/lib/mattermost_gitlab/server.py +++ b/build/lib/mattermost_gitlab/server.py @@ -96,9 +96,11 @@ def post_text(text, mattermost_webhook_url): if app.config['CHANNEL']: data['channel'] = app.config['CHANNEL'] - headers = {'Content-Type': 'application/json'} - resp = requests.post(mattermost_webhook_url, headers=headers, data=json.dumps(data)) + if app.config['VERIFY_SSL'].lower() == 'false': + resp = requests.post(mattermost_webhook_url, headers=headers, data=json.dumps(data), verify=False) + else: + resp = requests.post(mattermost_webhook_url, headers=headers, data=json.dumps(data), verify=True) if resp.status_code is not requests.codes.ok: print('Encountered error posting to Mattermost URL %s, status=%d, response_body=%s' % (app.config['MATTERMOST_WEBHOOK_URL'], resp.status_code, resp.json())) @@ -109,6 +111,7 @@ def parse_args(args=None): server_options = parser.add_argument_group("Server") server_options.add_argument('-p', '--port', type=int, default=5000) server_options.add_argument('--host', default='0.0.0.0') + server_options.add_argument('--verify-ssl', dest='VERIFY_SSL', default=True) parser.add_argument('-u', '--username', dest='USERNAME', default='gitlab') parser.add_argument('--channel', dest='CHANNEL', default='') # Leave this blank to post to the default channel of your webhook diff --git a/dist/mattermost_integration_gitlab-0.1.0-py2.7.egg b/dist/mattermost_integration_gitlab-0.1.0-py2.7.egg index 9e515b11b350e8c8c89edb87389e8d154502ee28..ce0fa4f00a5adc8104c35ef262f71e72290c5ced 100644 GIT binary patch delta 4613 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 delta 4421 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 YUo~G{0v`E)K_F<1Hrq{Uwd>-407ZcdlmGw# diff --git a/mattermost_gitlab/server.py b/mattermost_gitlab/server.py index b0f9e8b..64eb162 100644 --- a/mattermost_gitlab/server.py +++ b/mattermost_gitlab/server.py @@ -96,9 +96,11 @@ def post_text(text, mattermost_webhook_url): if app.config['CHANNEL']: data['channel'] = app.config['CHANNEL'] - headers = {'Content-Type': 'application/json'} - resp = requests.post(mattermost_webhook_url, headers=headers, data=json.dumps(data)) + if app.config['VERIFY_SSL'].lower() == 'false': + resp = requests.post(mattermost_webhook_url, headers=headers, data=json.dumps(data), verify=False) + else: + resp = requests.post(mattermost_webhook_url, headers=headers, data=json.dumps(data), verify=True) if resp.status_code is not requests.codes.ok: print('Encountered error posting to Mattermost URL %s, status=%d, response_body=%s' % (app.config['MATTERMOST_WEBHOOK_URL'], resp.status_code, resp.json())) @@ -109,6 +111,7 @@ def parse_args(args=None): server_options = parser.add_argument_group("Server") server_options.add_argument('-p', '--port', type=int, default=5000) server_options.add_argument('--host', default='0.0.0.0') + server_options.add_argument('--verify-ssl', dest='VERIFY_SSL', default=True) parser.add_argument('-u', '--username', dest='USERNAME', default='gitlab') parser.add_argument('--channel', dest='CHANNEL', default='') # Leave this blank to post to the default channel of your webhook