Skip to content

Commit

Permalink
Merge pull request #752 from /issues/751/1
Browse files Browse the repository at this point in the history
Fixes #751. Import issue labeler into webcompat app.
  • Loading branch information
Mike Taylor committed Oct 8, 2015
2 parents 9e0bb13 + ea47668 commit 9f11bff
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 2 deletions.
7 changes: 7 additions & 0 deletions tests/test_urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,13 @@ def test_issues_new(self):
result = '[{"labels": [{"name": "bug"}, {"name": "help wanted"}], "id": 0, "title": "fake bug 0"}, {"labels": [], "id": 1, "title": "fake bug 1"}]'
self.assertEqual(filter_new(issues), result)

def test_labeler_webhook(self):
'''Test that the labeler webhook can respond to ping event.'''
headers = {'X-GitHub-Event': 'ping'}
rv = self.app.get('/webhooks/labeler', headers=headers)
self.assertEqual(rv.status_code, 403)
rv = self.app.post('/webhooks/labeler', headers=headers)
self.assertEqual(rv.data, 'pong')

if __name__ == '__main__':
unittest.main()
6 changes: 4 additions & 2 deletions webcompat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@
# register blueprints
from api.endpoints import api
from api.uploads import uploads
from webhooks import webhooks

for blueprint in [api, uploads, webhooks]:
app.register_blueprint(blueprint)

app.register_blueprint(api)
app.register_blueprint(uploads)

# Start Logging Handlers
# See config.py for parameters
Expand Down
43 changes: 43 additions & 0 deletions webcompat/webhooks/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

'''Flask Blueprint for our "webhooks" module, which we use to do cool things
with GitHub events and actions.
See https://developer.github.com/webhooks/ for what is possible.'''

import json

from flask import Blueprint
from flask import abort
from flask import request

from helpers import parse_and_set_label

webhooks = Blueprint('webhooks', __name__, url_prefix='/webhooks')


@webhooks.route('/labeler', methods=['GET', 'POST'])
def hooklistener():
'''Listen for the "issues" webhook event, parse the body,
post back labels. But only do that for the 'opened' action.'''
if request.method == 'GET':
abort(403)
elif (request.method == 'POST' and
request.headers.get('X-GitHub-Event') == 'issues'):
payload = json.loads(request.data)
if payload.get('action') == 'opened':

This comment has been minimized.

Copy link
@karlcow

karlcow Oct 12, 2015

Member

hmmm no review ;)
This can be improved. I'm afraid it's too weak with regards to spam.

For example we could listen to the IP address

if not any(request.remote_addr.startswith(IP) for IP in ('192.30.252', '192.30.253', '192.30.254', '192.30.255')):
    abort(403)

But it seems now that GitHub is recommending against it and prefers we check the authentification. Reasonable.

We highly recommend that you don't white list IPs for Service Hooks. Instead, setup HTTPS and basic authentication to verify incoming requests.

This comment has been minimized.

Copy link
@miketaylr

miketaylr Oct 12, 2015

Member

Hallvord reviewed the code, we dicussed on IRC.

#752 (comment)

This comment has been minimized.

Copy link
@miketaylr

miketaylr Oct 12, 2015

Member

Ah, there is a difference between webhooks coming from GitHub and servicehooks (which come from non-GitHub services). There doesn't seem to be any way to set up Basic Auth for webhooks. Instead, they recommend either whitelisting IPs (via their API) or validating secret tokens: https://developer.github.com/webhooks/securing/

This comment has been minimized.

Copy link
@miketaylr

miketaylr Oct 12, 2015

Member

Issue #761 to track that.

This comment has been minimized.

Copy link
@karlcow

karlcow Oct 12, 2015

Member

Hallvord reviewed the code, we dicussed on IRC.

Yup That was my point. Not documented in a way which is shared/accessible by the rest of the community

This comment has been minimized.

Copy link
@miketaylr

miketaylr Oct 13, 2015

Member

OK, note to @hallvors: leave a comment in the PR. :)

issue_body = payload.get('issue')['body']
issue_number = payload.get('issue')['number']
parse_and_set_label(issue_body, issue_number)
return ('gracias, amigo.', 200)
else:
return ('cool story, bro.', 200)
elif (request.method == 'POST' and
request.headers.get('X-GitHub-Event') == 'ping'):
return ('pong', 200)
else:
abort(403)
47 changes: 47 additions & 0 deletions webcompat/webhooks/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import json
import os
import re
import requests

from webcompat import app


def api_post(endpoint, payload, issue):
'''Helper method to post junk to GitHub.'''
headers = {
'Authorization': 'token {0}'.format(app.config['BOT_OAUTH_TOKEN'])
}
uri = 'https://api.github.com/repos/{0}/{1}/{2}'.format(
app.config['ISSUES_REPO_URI'], issue, endpoint)
requests.post(uri, data=json.dumps(payload), headers=headers)


def parse_and_set_label(body, issue_number):
'''Parse the labels from the body in comments like so:
<!-- @browser: value -->. Currently this only handles a single label,
because that's all that we set in webcompat.com.
'''
match_list = re.search(r'<!--\s@(\w+):\s([^\d]+?)\s[\d\.]+\s-->', body)
if match_list:
# perhaps we do something more interesting depending on
# what groups(n)[0] is in the future.
# right now, match_list.groups(0) should look like:
# ('browser', 'firefox')
browser = match_list.groups(0)[1].lower()
dash_browser = '-'.join(browser.split())
set_label('browser-' + dash_browser, issue_number)


def set_label(label, issue_number):
'''Do a GitHub POST request using one of our bots, which has push access
and set a label for the issue.'''
# POST /repos/:owner/:repo/issues/:number/labels
# ['Label1', 'Label2']
payload = [label]
api_post('labels', payload, issue_number)

0 comments on commit 9f11bff

Please sign in to comment.