diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/README.md b/README.md
index 7968217..fcfe4d3 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,46 @@
Highfive
========
-GitHub hooks to provide an encouraging atmosphere for new contributors
+GitHub hooks to provide an encouraging atmosphere for new contributors.
-Docs for this highfive live [on the Servo
-wiki](https://github.com/servo/servo/wiki/Highfive)
+Docs for the highfive instance for servo/servo repository live [on the Servo
+wiki](https://github.com/servo/servo/wiki/Highfive).
+
+## Design
+
+Highfive is built as a modular, loosely-coupled set of handlers for Github
+API events. Each time an API event is processed, each handler is given the
+opportunity to respond to it, either by making direct API calls (such as
+manipulating PR labels) or using cross-handler features such as logging a
+warning (which are aggregated at the end and posted as a single comment).
+
+## Testing
+
+Per-handler tests can be run using `python test.py`. These consist of
+a set of JSON documents collected from the `tests/` subdirectory of
+each handler, using the following format:
+```json
+{
+ "initial": {
+ // Initial state of the PR before any handlers process the payload.
+ // Defaults:
+ "labels": [],
+ "diff": "",
+ "new_contributor": false,
+ "assignee": null,
+ },
+ "expected": {
+ // Expected state of the PR after all the handlers process
+ // the following payload.
+ // Only fields present in this object will be checked.
+ // comments: 5,
+ // labels: ["S-awaiting-review"],
+ // assignee: "jdm"
+ },
+ "payload": {
+ // Github API event payload in JSON format
+ }
+}
+```
+Each test runs with a mock Github API provider, so no account information
+or network connection is required to run the test suite.
diff --git a/eventhandler.py b/eventhandler.py
new file mode 100644
index 0000000..3e28e0d
--- /dev/null
+++ b/eventhandler.py
@@ -0,0 +1,64 @@
+import imp
+import json
+import os
+
+_warnings = []
+
+class EventHandler:
+ def on_pr_opened(self, api, payload):
+ pass
+
+ def on_pr_updated(self, api, payload):
+ pass
+
+ def on_new_comment(self, api, payload):
+ pass
+
+ def warn(self, msg):
+ global _warnings
+ _warnings += [msg]
+
+ def is_open_pr(self, payload):
+ return payload['issue']['state'] == 'open' and 'pull_request' in payload['issue']
+
+ def register_tests(self, path):
+ from test import create_test
+ tests_location = os.path.join(path, 'tests')
+ if not os.path.isdir(tests_location):
+ return
+ tests = [os.path.join(tests_location, f) for f in os.listdir(tests_location) if f.endswith('.json')]
+ for testfile in tests:
+ with open(testfile) as f:
+ contents = json.load(f)
+ if not isinstance(contents['initial'], list):
+ assert not isinstance(contents['expected'], list)
+ contents['initial'] = [contents['initial']]
+ contents['expected'] = [contents['expected']]
+ for initial, expected in zip(contents['initial'], contents['expected']):
+ yield create_test(testfile, initial, expected)
+
+def reset_test_state():
+ global _warnings
+ _warnings = []
+
+def get_warnings():
+ global _warnings
+ return _warnings
+
+def get_handlers():
+ modules = []
+ handlers = []
+ possiblehandlers = os.listdir('handlers')
+ for i in possiblehandlers:
+ location = os.path.join('handlers', i)
+ module = '__init__'
+ if not os.path.isdir(location) or not module + ".py" in os.listdir(location):
+ continue
+ try:
+ (file, pathname, description) = imp.find_module(module, [location])
+ module = imp.load_module(module, file, pathname, description)
+ handlers.append(module.handler_interface())
+ modules.append((module, location))
+ finally:
+ file.close()
+ return (modules, handlers)
diff --git a/handlers/assign_reviewer/__init__.py b/handlers/assign_reviewer/__init__.py
new file mode 100644
index 0000000..fd55624
--- /dev/null
+++ b/handlers/assign_reviewer/__init__.py
@@ -0,0 +1,22 @@
+from eventhandler import EventHandler
+import re
+
+# If the user specified a reviewer, return the username, otherwise returns None.
+def find_reviewer(commit_msg):
+ reviewer_re = re.compile("\\b[rR]\?[:\- ]*@([a-zA-Z0-9\-]+)")
+ match = reviewer_re.search(commit_msg)
+ if not match:
+ return None
+ return match.group(1)
+
+class AssignReviewerHandler(EventHandler):
+ def on_new_comment(self, api, payload):
+ if not self.is_open_pr(payload):
+ return
+
+ reviewer = find_reviewer(payload["comment"]["body"])
+ if reviewer:
+ api.set_assignee(reviewer)
+
+
+handler_interface = AssignReviewerHandler
diff --git a/test_comment.json b/handlers/assign_reviewer/tests/comment.json
similarity index 99%
rename from test_comment.json
rename to handlers/assign_reviewer/tests/comment.json
index 9459c33..2192279 100644
--- a/test_comment.json
+++ b/handlers/assign_reviewer/tests/comment.json
@@ -1,3 +1,10 @@
+{
+ "initial": {
+ },
+ "expected": {
+ "assignee": "jdm"
+ },
+ "payload":
{
"action": "created",
"issue": {
@@ -197,3 +204,4 @@
"site_admin": false
}
}
+}
diff --git a/handlers/homu_status/__init__.py b/handlers/homu_status/__init__.py
new file mode 100644
index 0000000..5878f07
--- /dev/null
+++ b/handlers/homu_status/__init__.py
@@ -0,0 +1,34 @@
+from eventhandler import EventHandler
+
+class HomuStatusHandler(EventHandler):
+ def on_new_comment(self, api, payload):
+ if not self.is_open_pr(payload):
+ return
+
+ if payload['comment']['user']['login'] != 'bors-servo':
+ return
+
+ labels = api.get_labels();
+ msg = payload["comment"]["body"]
+
+ def remove_if_exists(label):
+ if label in labels:
+ api.remove_label(label)
+
+ if 'has been approved by' in msg or 'Testing commit' in msg:
+ for label in ["S-awaiting-review", "S-needs-rebase", "S-tests-failed",
+ "S-needs-code-changes", "S-needs-squash", "S-awaiting-answer"]:
+ remove_if_exists(label)
+ if not "S-awaiting-merge" in labels:
+ api.add_label("S-awaiting-merge")
+
+ elif 'Test failed' in msg:
+ remove_if_exists("S-awaiting-merge")
+ api.add_label("S-tests-failed")
+
+ elif 'Please resolve the merge conflicts' in msg:
+ remove_if_exists("S-awaiting-merge")
+ api.add_label("S-needs-rebase")
+
+
+handler_interface = HomuStatusHandler
diff --git a/test_merge_approved.json b/handlers/homu_status/tests/merge_approved.json
similarity index 97%
rename from test_merge_approved.json
rename to handlers/homu_status/tests/merge_approved.json
index 8864e7d..e29469d 100644
--- a/test_merge_approved.json
+++ b/handlers/homu_status/tests/merge_approved.json
@@ -1,3 +1,12 @@
+{
+ "initial": {
+ "labels": ["S-needs-code-changes", "S-needs-rebase", "S-tests-failed",
+ "S-needs-squash", "S-awaiting-review"]
+ },
+ "expected": {
+ "labels": ["S-awaiting-merge"]
+ },
+ "payload":
{
"action": "created",
"issue": {
@@ -197,3 +206,4 @@
"site_admin": false
}
}
+}
diff --git a/test_merge_conflict.json b/handlers/homu_status/tests/merge_conflict.json
similarity index 98%
rename from test_merge_conflict.json
rename to handlers/homu_status/tests/merge_conflict.json
index aacf788..1211e18 100644
--- a/test_merge_conflict.json
+++ b/handlers/homu_status/tests/merge_conflict.json
@@ -1,3 +1,11 @@
+{
+ "initial": {
+ "labels": ["S-awaiting-merge"]
+ },
+ "expected": {
+ "labels": ["S-needs-rebase"]
+ },
+ "payload":
{
"action": "created",
"issue": {
@@ -197,3 +205,4 @@
"site_admin": false
}
}
+}
diff --git a/test_post_retry.json b/handlers/homu_status/tests/post_retry.json
similarity index 97%
rename from test_post_retry.json
rename to handlers/homu_status/tests/post_retry.json
index 9b8c521..4556ac8 100644
--- a/test_post_retry.json
+++ b/handlers/homu_status/tests/post_retry.json
@@ -1,3 +1,17 @@
+{
+ "initial": [{
+ "labels": ["S-tests-failed"]
+ },
+ {
+ "labels": ["S-awaiting-merge"]
+ }],
+ "expected": [{
+ "labels": ["S-awaiting-merge"]
+ },
+ {
+ "labels": ["S-awaiting-merge"]
+ }],
+ "payload":
{
"action": "created",
"issue": {
@@ -197,3 +211,4 @@
"site_admin": false
}
}
+}
diff --git a/test_tests_failed.json b/handlers/homu_status/tests/tests_failed.json
similarity index 98%
rename from test_tests_failed.json
rename to handlers/homu_status/tests/tests_failed.json
index f98940d..1c37ee7 100644
--- a/test_tests_failed.json
+++ b/handlers/homu_status/tests/tests_failed.json
@@ -1,3 +1,11 @@
+{
+ "initial": {
+ "labels": ["S-awaiting-merge"]
+ },
+ "expected": {
+ "labels": ["S-tests-failed"]
+ },
+ "payload":
{
"action": "created",
"issue": {
@@ -197,3 +205,4 @@
"site_admin": false
}
}
+}
diff --git a/handlers/missing_test/__init__.py b/handlers/missing_test/__init__.py
new file mode 100644
index 0000000..4815a5a
--- /dev/null
+++ b/handlers/missing_test/__init__.py
@@ -0,0 +1,20 @@
+from eventhandler import EventHandler
+
+reftest_required_msg = 'These commits modify layout code, but no reftests are modified. Please consider adding a reftest!'
+
+class MissingTestHandler(EventHandler):
+ def on_pr_opened(self, api, payload):
+ diff = api.get_diff()
+ layout_changed = False
+ for line in diff.split('\n'):
+ if line.startswith('diff --git') and line.find('components/layout/') > -1:
+ layout_changed = True
+ if line.startswith('diff --git') and \
+ (line.find('tests/ref') > -1 or line.find('tests/wpt') > -1):
+ return
+
+ if layout_changed:
+ self.warn(reftest_required_msg)
+
+
+handler_interface = MissingTestHandler
diff --git a/test_ignored_action.json b/handlers/missing_test/tests/new_pr.json
similarity index 58%
rename from test_ignored_action.json
rename to handlers/missing_test/tests/new_pr.json
index 2850291..89bbc74 100644
--- a/test_ignored_action.json
+++ b/handlers/missing_test/tests/new_pr.json
@@ -1,169 +1,165 @@
{
- "action": "labeled",
- "number": 7066,
+ "initial": [{
+ "diff": "diff --git components/layout/"
+ },
+ {
+ "diff": "diff --git components/layout/\ndiff --git tests/wpt"
+ }],
+ "expected": [{
+ "comments": 1
+ },
+ {
+ "comments": 0
+ }],
+ "payload":
+{
+ "action": "opened",
+ "number": 7076,
"pull_request": {
- "url": "https://api.github.com/repos/servo/servo/pulls/7066",
- "id": 41874546,
- "html_url": "https://github.com/servo/servo/pull/7066",
- "diff_url": "https://github.com/servo/servo/pull/7066.diff",
- "patch_url": "https://github.com/servo/servo/pull/7066.patch",
- "issue_url": "https://api.github.com/repos/servo/servo/issues/7066",
- "number": 7066,
+ "url": "https://api.github.com/repos/servo/servo/pulls/7076",
+ "id": 41896942,
+ "html_url": "https://github.com/servo/servo/pull/7076",
+ "diff_url": "https://github.com/servo/servo/pull/7076.diff",
+ "patch_url": "https://github.com/servo/servo/pull/7076.patch",
+ "issue_url": "https://api.github.com/repos/servo/servo/issues/7076",
+ "number": 7076,
"state": "open",
"locked": false,
- "title": "Dispatch message events for WebSocket.",
+ "title": "Remove invalid file path in ignored_files for tidying",
"user": {
- "login": "Ms2ger",
- "id": 111161,
- "avatar_url": "https://avatars.githubusercontent.com/u/111161?v=3",
+ "login": "frewsxcv",
+ "id": 416575,
+ "avatar_url": "https://avatars.githubusercontent.com/u/416575?v=3",
"gravatar_id": "",
- "url": "https://api.github.com/users/Ms2ger",
- "html_url": "https://github.com/Ms2ger",
- "followers_url": "https://api.github.com/users/Ms2ger/followers",
- "following_url": "https://api.github.com/users/Ms2ger/following{/other_user}",
- "gists_url": "https://api.github.com/users/Ms2ger/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/Ms2ger/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/Ms2ger/subscriptions",
- "organizations_url": "https://api.github.com/users/Ms2ger/orgs",
- "repos_url": "https://api.github.com/users/Ms2ger/repos",
- "events_url": "https://api.github.com/users/Ms2ger/events{/privacy}",
- "received_events_url": "https://api.github.com/users/Ms2ger/received_events",
+ "url": "https://api.github.com/users/frewsxcv",
+ "html_url": "https://github.com/frewsxcv",
+ "followers_url": "https://api.github.com/users/frewsxcv/followers",
+ "following_url": "https://api.github.com/users/frewsxcv/following{/other_user}",
+ "gists_url": "https://api.github.com/users/frewsxcv/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/frewsxcv/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/frewsxcv/subscriptions",
+ "organizations_url": "https://api.github.com/users/frewsxcv/orgs",
+ "repos_url": "https://api.github.com/users/frewsxcv/repos",
+ "events_url": "https://api.github.com/users/frewsxcv/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/frewsxcv/received_events",
"type": "User",
"site_admin": false
},
- "body": "\n\n\n[
](https://reviewable.io/reviews/servo/servo/7066)\n\n",
- "created_at": "2015-08-07T09:32:45Z",
- "updated_at": "2015-08-07T16:13:17Z",
+ "body": null,
+ "created_at": "2015-08-07T14:24:50Z",
+ "updated_at": "2015-08-07T14:24:50Z",
"closed_at": null,
"merged_at": null,
- "merge_commit_sha": "155eb5fd6e65dbcc70613c2f614f6eb0fc791baa",
- "assignee": {
- "login": "metajack",
- "id": 28357,
- "avatar_url": "https://avatars.githubusercontent.com/u/28357?v=3",
- "gravatar_id": "",
- "url": "https://api.github.com/users/metajack",
- "html_url": "https://github.com/metajack",
- "followers_url": "https://api.github.com/users/metajack/followers",
- "following_url": "https://api.github.com/users/metajack/following{/other_user}",
- "gists_url": "https://api.github.com/users/metajack/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/metajack/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/metajack/subscriptions",
- "organizations_url": "https://api.github.com/users/metajack/orgs",
- "repos_url": "https://api.github.com/users/metajack/repos",
- "events_url": "https://api.github.com/users/metajack/events{/privacy}",
- "received_events_url": "https://api.github.com/users/metajack/received_events",
- "type": "User",
- "site_admin": false
- },
+ "merge_commit_sha": null,
+ "assignee": null,
"milestone": null,
- "commits_url": "https://api.github.com/repos/servo/servo/pulls/7066/commits",
- "review_comments_url": "https://api.github.com/repos/servo/servo/pulls/7066/comments",
+ "commits_url": "https://api.github.com/repos/servo/servo/pulls/7076/commits",
+ "review_comments_url": "https://api.github.com/repos/servo/servo/pulls/7076/comments",
"review_comment_url": "https://api.github.com/repos/servo/servo/pulls/comments{/number}",
- "comments_url": "https://api.github.com/repos/servo/servo/issues/7066/comments",
- "statuses_url": "https://api.github.com/repos/servo/servo/statuses/749743f424cddd137c5d78d0bf6b49016a640c29",
+ "comments_url": "https://api.github.com/repos/servo/servo/issues/7076/comments",
+ "statuses_url": "https://api.github.com/repos/servo/servo/statuses/da44f31cb1debe2c8161ee280121d79d3dd5ec18",
"head": {
- "label": "Ms2ger:ws-event",
- "ref": "ws-event",
- "sha": "749743f424cddd137c5d78d0bf6b49016a640c29",
+ "label": "frewsxcv:tidy-rm-invalid-file",
+ "ref": "tidy-rm-invalid-file",
+ "sha": "da44f31cb1debe2c8161ee280121d79d3dd5ec18",
"user": {
- "login": "Ms2ger",
- "id": 111161,
- "avatar_url": "https://avatars.githubusercontent.com/u/111161?v=3",
+ "login": "frewsxcv",
+ "id": 416575,
+ "avatar_url": "https://avatars.githubusercontent.com/u/416575?v=3",
"gravatar_id": "",
- "url": "https://api.github.com/users/Ms2ger",
- "html_url": "https://github.com/Ms2ger",
- "followers_url": "https://api.github.com/users/Ms2ger/followers",
- "following_url": "https://api.github.com/users/Ms2ger/following{/other_user}",
- "gists_url": "https://api.github.com/users/Ms2ger/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/Ms2ger/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/Ms2ger/subscriptions",
- "organizations_url": "https://api.github.com/users/Ms2ger/orgs",
- "repos_url": "https://api.github.com/users/Ms2ger/repos",
- "events_url": "https://api.github.com/users/Ms2ger/events{/privacy}",
- "received_events_url": "https://api.github.com/users/Ms2ger/received_events",
+ "url": "https://api.github.com/users/frewsxcv",
+ "html_url": "https://github.com/frewsxcv",
+ "followers_url": "https://api.github.com/users/frewsxcv/followers",
+ "following_url": "https://api.github.com/users/frewsxcv/following{/other_user}",
+ "gists_url": "https://api.github.com/users/frewsxcv/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/frewsxcv/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/frewsxcv/subscriptions",
+ "organizations_url": "https://api.github.com/users/frewsxcv/orgs",
+ "repos_url": "https://api.github.com/users/frewsxcv/repos",
+ "events_url": "https://api.github.com/users/frewsxcv/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/frewsxcv/received_events",
"type": "User",
"site_admin": false
},
"repo": {
- "id": 12599239,
+ "id": 27014207,
"name": "servo",
- "full_name": "Ms2ger/servo",
+ "full_name": "frewsxcv/servo",
"owner": {
- "login": "Ms2ger",
- "id": 111161,
- "avatar_url": "https://avatars.githubusercontent.com/u/111161?v=3",
+ "login": "frewsxcv",
+ "id": 416575,
+ "avatar_url": "https://avatars.githubusercontent.com/u/416575?v=3",
"gravatar_id": "",
- "url": "https://api.github.com/users/Ms2ger",
- "html_url": "https://github.com/Ms2ger",
- "followers_url": "https://api.github.com/users/Ms2ger/followers",
- "following_url": "https://api.github.com/users/Ms2ger/following{/other_user}",
- "gists_url": "https://api.github.com/users/Ms2ger/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/Ms2ger/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/Ms2ger/subscriptions",
- "organizations_url": "https://api.github.com/users/Ms2ger/orgs",
- "repos_url": "https://api.github.com/users/Ms2ger/repos",
- "events_url": "https://api.github.com/users/Ms2ger/events{/privacy}",
- "received_events_url": "https://api.github.com/users/Ms2ger/received_events",
+ "url": "https://api.github.com/users/frewsxcv",
+ "html_url": "https://github.com/frewsxcv",
+ "followers_url": "https://api.github.com/users/frewsxcv/followers",
+ "following_url": "https://api.github.com/users/frewsxcv/following{/other_user}",
+ "gists_url": "https://api.github.com/users/frewsxcv/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/frewsxcv/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/frewsxcv/subscriptions",
+ "organizations_url": "https://api.github.com/users/frewsxcv/orgs",
+ "repos_url": "https://api.github.com/users/frewsxcv/repos",
+ "events_url": "https://api.github.com/users/frewsxcv/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/frewsxcv/received_events",
"type": "User",
"site_admin": false
},
"private": false,
- "html_url": "https://github.com/Ms2ger/servo",
+ "html_url": "https://github.com/frewsxcv/servo",
"description": "The Servo Browser Engine",
"fork": true,
- "url": "https://api.github.com/repos/Ms2ger/servo",
- "forks_url": "https://api.github.com/repos/Ms2ger/servo/forks",
- "keys_url": "https://api.github.com/repos/Ms2ger/servo/keys{/key_id}",
- "collaborators_url": "https://api.github.com/repos/Ms2ger/servo/collaborators{/collaborator}",
- "teams_url": "https://api.github.com/repos/Ms2ger/servo/teams",
- "hooks_url": "https://api.github.com/repos/Ms2ger/servo/hooks",
- "issue_events_url": "https://api.github.com/repos/Ms2ger/servo/issues/events{/number}",
- "events_url": "https://api.github.com/repos/Ms2ger/servo/events",
- "assignees_url": "https://api.github.com/repos/Ms2ger/servo/assignees{/user}",
- "branches_url": "https://api.github.com/repos/Ms2ger/servo/branches{/branch}",
- "tags_url": "https://api.github.com/repos/Ms2ger/servo/tags",
- "blobs_url": "https://api.github.com/repos/Ms2ger/servo/git/blobs{/sha}",
- "git_tags_url": "https://api.github.com/repos/Ms2ger/servo/git/tags{/sha}",
- "git_refs_url": "https://api.github.com/repos/Ms2ger/servo/git/refs{/sha}",
- "trees_url": "https://api.github.com/repos/Ms2ger/servo/git/trees{/sha}",
- "statuses_url": "https://api.github.com/repos/Ms2ger/servo/statuses/{sha}",
- "languages_url": "https://api.github.com/repos/Ms2ger/servo/languages",
- "stargazers_url": "https://api.github.com/repos/Ms2ger/servo/stargazers",
- "contributors_url": "https://api.github.com/repos/Ms2ger/servo/contributors",
- "subscribers_url": "https://api.github.com/repos/Ms2ger/servo/subscribers",
- "subscription_url": "https://api.github.com/repos/Ms2ger/servo/subscription",
- "commits_url": "https://api.github.com/repos/Ms2ger/servo/commits{/sha}",
- "git_commits_url": "https://api.github.com/repos/Ms2ger/servo/git/commits{/sha}",
- "comments_url": "https://api.github.com/repos/Ms2ger/servo/comments{/number}",
- "issue_comment_url": "https://api.github.com/repos/Ms2ger/servo/issues/comments{/number}",
- "contents_url": "https://api.github.com/repos/Ms2ger/servo/contents/{+path}",
- "compare_url": "https://api.github.com/repos/Ms2ger/servo/compare/{base}...{head}",
- "merges_url": "https://api.github.com/repos/Ms2ger/servo/merges",
- "archive_url": "https://api.github.com/repos/Ms2ger/servo/{archive_format}{/ref}",
- "downloads_url": "https://api.github.com/repos/Ms2ger/servo/downloads",
- "issues_url": "https://api.github.com/repos/Ms2ger/servo/issues{/number}",
- "pulls_url": "https://api.github.com/repos/Ms2ger/servo/pulls{/number}",
- "milestones_url": "https://api.github.com/repos/Ms2ger/servo/milestones{/number}",
- "notifications_url": "https://api.github.com/repos/Ms2ger/servo/notifications{?since,all,participating}",
- "labels_url": "https://api.github.com/repos/Ms2ger/servo/labels{/name}",
- "releases_url": "https://api.github.com/repos/Ms2ger/servo/releases{/id}",
- "created_at": "2013-09-04T18:49:05Z",
- "updated_at": "2015-06-21T07:02:55Z",
- "pushed_at": "2015-08-07T14:34:22Z",
- "git_url": "git://github.com/Ms2ger/servo.git",
- "ssh_url": "git@github.com:Ms2ger/servo.git",
- "clone_url": "https://github.com/Ms2ger/servo.git",
- "svn_url": "https://github.com/Ms2ger/servo",
+ "url": "https://api.github.com/repos/frewsxcv/servo",
+ "forks_url": "https://api.github.com/repos/frewsxcv/servo/forks",
+ "keys_url": "https://api.github.com/repos/frewsxcv/servo/keys{/key_id}",
+ "collaborators_url": "https://api.github.com/repos/frewsxcv/servo/collaborators{/collaborator}",
+ "teams_url": "https://api.github.com/repos/frewsxcv/servo/teams",
+ "hooks_url": "https://api.github.com/repos/frewsxcv/servo/hooks",
+ "issue_events_url": "https://api.github.com/repos/frewsxcv/servo/issues/events{/number}",
+ "events_url": "https://api.github.com/repos/frewsxcv/servo/events",
+ "assignees_url": "https://api.github.com/repos/frewsxcv/servo/assignees{/user}",
+ "branches_url": "https://api.github.com/repos/frewsxcv/servo/branches{/branch}",
+ "tags_url": "https://api.github.com/repos/frewsxcv/servo/tags",
+ "blobs_url": "https://api.github.com/repos/frewsxcv/servo/git/blobs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/frewsxcv/servo/git/tags{/sha}",
+ "git_refs_url": "https://api.github.com/repos/frewsxcv/servo/git/refs{/sha}",
+ "trees_url": "https://api.github.com/repos/frewsxcv/servo/git/trees{/sha}",
+ "statuses_url": "https://api.github.com/repos/frewsxcv/servo/statuses/{sha}",
+ "languages_url": "https://api.github.com/repos/frewsxcv/servo/languages",
+ "stargazers_url": "https://api.github.com/repos/frewsxcv/servo/stargazers",
+ "contributors_url": "https://api.github.com/repos/frewsxcv/servo/contributors",
+ "subscribers_url": "https://api.github.com/repos/frewsxcv/servo/subscribers",
+ "subscription_url": "https://api.github.com/repos/frewsxcv/servo/subscription",
+ "commits_url": "https://api.github.com/repos/frewsxcv/servo/commits{/sha}",
+ "git_commits_url": "https://api.github.com/repos/frewsxcv/servo/git/commits{/sha}",
+ "comments_url": "https://api.github.com/repos/frewsxcv/servo/comments{/number}",
+ "issue_comment_url": "https://api.github.com/repos/frewsxcv/servo/issues/comments{/number}",
+ "contents_url": "https://api.github.com/repos/frewsxcv/servo/contents/{+path}",
+ "compare_url": "https://api.github.com/repos/frewsxcv/servo/compare/{base}...{head}",
+ "merges_url": "https://api.github.com/repos/frewsxcv/servo/merges",
+ "archive_url": "https://api.github.com/repos/frewsxcv/servo/{archive_format}{/ref}",
+ "downloads_url": "https://api.github.com/repos/frewsxcv/servo/downloads",
+ "issues_url": "https://api.github.com/repos/frewsxcv/servo/issues{/number}",
+ "pulls_url": "https://api.github.com/repos/frewsxcv/servo/pulls{/number}",
+ "milestones_url": "https://api.github.com/repos/frewsxcv/servo/milestones{/number}",
+ "notifications_url": "https://api.github.com/repos/frewsxcv/servo/notifications{?since,all,participating}",
+ "labels_url": "https://api.github.com/repos/frewsxcv/servo/labels{/name}",
+ "releases_url": "https://api.github.com/repos/frewsxcv/servo/releases{/id}",
+ "created_at": "2014-11-22T22:10:35Z",
+ "updated_at": "2014-11-22T22:10:38Z",
+ "pushed_at": "2015-08-07T14:24:45Z",
+ "git_url": "git://github.com/frewsxcv/servo.git",
+ "ssh_url": "git@github.com:frewsxcv/servo.git",
+ "clone_url": "https://github.com/frewsxcv/servo.git",
+ "svn_url": "https://github.com/frewsxcv/servo",
"homepage": "",
- "size": 124200,
+ "size": 119557,
"stargazers_count": 0,
"watchers_count": 0,
"language": "Rust",
"has_issues": false,
"has_downloads": true,
"has_wiki": true,
- "has_pages": false,
+ "has_pages": true,
"forks_count": 0,
"mirror_url": null,
"open_issues_count": 0,
@@ -176,7 +172,7 @@
"base": {
"label": "servo:master",
"ref": "master",
- "sha": "44c4bb00c1cb8645ee2fc303848a5136108e594f",
+ "sha": "b4e30da3dbf58c16703864f4bec4b0b0132084fa",
"user": {
"login": "servo",
"id": 2566135,
@@ -261,13 +257,13 @@
"releases_url": "https://api.github.com/repos/servo/servo/releases{/id}",
"created_at": "2012-02-08T19:07:25Z",
"updated_at": "2015-08-07T09:37:49Z",
- "pushed_at": "2015-08-07T16:09:30Z",
+ "pushed_at": "2015-08-07T14:10:08Z",
"git_url": "git://github.com/servo/servo.git",
"ssh_url": "git@github.com:servo/servo.git",
"clone_url": "https://github.com/servo/servo.git",
"svn_url": "https://github.com/servo/servo",
"homepage": "",
- "size": 1903023,
+ "size": 1904176,
"stargazers_count": 4571,
"watchers_count": 4571,
"language": "Rust",
@@ -277,54 +273,49 @@
"has_pages": false,
"forks_count": 717,
"mirror_url": null,
- "open_issues_count": 1057,
+ "open_issues_count": 1060,
"forks": 717,
- "open_issues": 1057,
+ "open_issues": 1060,
"watchers": 4571,
"default_branch": "master"
}
},
"_links": {
"self": {
- "href": "https://api.github.com/repos/servo/servo/pulls/7066"
+ "href": "https://api.github.com/repos/servo/servo/pulls/7076"
},
"html": {
- "href": "https://github.com/servo/servo/pull/7066"
+ "href": "https://github.com/servo/servo/pull/7076"
},
"issue": {
- "href": "https://api.github.com/repos/servo/servo/issues/7066"
+ "href": "https://api.github.com/repos/servo/servo/issues/7076"
},
"comments": {
- "href": "https://api.github.com/repos/servo/servo/issues/7066/comments"
+ "href": "https://api.github.com/repos/servo/servo/issues/7076/comments"
},
"review_comments": {
- "href": "https://api.github.com/repos/servo/servo/pulls/7066/comments"
+ "href": "https://api.github.com/repos/servo/servo/pulls/7076/comments"
},
"review_comment": {
"href": "https://api.github.com/repos/servo/servo/pulls/comments{/number}"
},
"commits": {
- "href": "https://api.github.com/repos/servo/servo/pulls/7066/commits"
+ "href": "https://api.github.com/repos/servo/servo/pulls/7076/commits"
},
"statuses": {
- "href": "https://api.github.com/repos/servo/servo/statuses/749743f424cddd137c5d78d0bf6b49016a640c29"
+ "href": "https://api.github.com/repos/servo/servo/statuses/da44f31cb1debe2c8161ee280121d79d3dd5ec18"
}
},
"merged": false,
- "mergeable": true,
- "mergeable_state": "clean",
+ "mergeable": null,
+ "mergeable_state": "unknown",
"merged_by": null,
"comments": 0,
"review_comments": 0,
- "commits": 2,
- "additions": 127,
- "deletions": 421,
- "changed_files": 86
- },
- "label": {
- "url": "https://api.github.com/repos/servo/servo/labels/S-awaiting-merge",
- "name": "S-awaiting-merge",
- "color": "d4c5f9"
+ "commits": 1,
+ "additions": 0,
+ "deletions": 1,
+ "changed_files": 1
},
"repository": {
"id": 3390243,
@@ -391,13 +382,13 @@
"releases_url": "https://api.github.com/repos/servo/servo/releases{/id}",
"created_at": "2012-02-08T19:07:25Z",
"updated_at": "2015-08-07T09:37:49Z",
- "pushed_at": "2015-08-07T16:09:30Z",
+ "pushed_at": "2015-08-07T14:10:08Z",
"git_url": "git://github.com/servo/servo.git",
"ssh_url": "git@github.com:servo/servo.git",
"clone_url": "https://github.com/servo/servo.git",
"svn_url": "https://github.com/servo/servo",
"homepage": "",
- "size": 1903023,
+ "size": 1904176,
"stargazers_count": 4571,
"watchers_count": 4571,
"language": "Rust",
@@ -407,9 +398,9 @@
"has_pages": false,
"forks_count": 717,
"mirror_url": null,
- "open_issues_count": 1057,
+ "open_issues_count": 1060,
"forks": 717,
- "open_issues": 1057,
+ "open_issues": 1060,
"watchers": 4571,
"default_branch": "master"
},
@@ -425,22 +416,23 @@
"description": null
},
"sender": {
- "login": "metajack",
- "id": 28357,
- "avatar_url": "https://avatars.githubusercontent.com/u/28357?v=3",
+ "login": "frewsxcv",
+ "id": 416575,
+ "avatar_url": "https://avatars.githubusercontent.com/u/416575?v=3",
"gravatar_id": "",
- "url": "https://api.github.com/users/metajack",
- "html_url": "https://github.com/metajack",
- "followers_url": "https://api.github.com/users/metajack/followers",
- "following_url": "https://api.github.com/users/metajack/following{/other_user}",
- "gists_url": "https://api.github.com/users/metajack/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/metajack/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/metajack/subscriptions",
- "organizations_url": "https://api.github.com/users/metajack/orgs",
- "repos_url": "https://api.github.com/users/metajack/repos",
- "events_url": "https://api.github.com/users/metajack/events{/privacy}",
- "received_events_url": "https://api.github.com/users/metajack/received_events",
+ "url": "https://api.github.com/users/frewsxcv",
+ "html_url": "https://github.com/frewsxcv",
+ "followers_url": "https://api.github.com/users/frewsxcv/followers",
+ "following_url": "https://api.github.com/users/frewsxcv/following{/other_user}",
+ "gists_url": "https://api.github.com/users/frewsxcv/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/frewsxcv/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/frewsxcv/subscriptions",
+ "organizations_url": "https://api.github.com/users/frewsxcv/orgs",
+ "repos_url": "https://api.github.com/users/frewsxcv/repos",
+ "events_url": "https://api.github.com/users/frewsxcv/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/frewsxcv/received_events",
"type": "User",
"site_admin": false
}
}
+}
diff --git a/handlers/status_update/__init__.py b/handlers/status_update/__init__.py
new file mode 100644
index 0000000..749f06d
--- /dev/null
+++ b/handlers/status_update/__init__.py
@@ -0,0 +1,26 @@
+from eventhandler import EventHandler
+
+def manage_pr_state(api, payload):
+ labels = api.get_labels();
+
+ for label in ["S-awaiting-merge", "S-tests-failed", "S-needs-code-changes"]:
+ if label in labels:
+ api.remove_label(label)
+ if not "S-awaiting-review" in labels:
+ api.add_label("S-awaiting-review")
+
+ # If mergeable is null, the data wasn't available yet. It would be nice to try to fetch that
+ # information again.
+ if payload["action"] == "synchronize" and payload['pull_request']['mergeable']:
+ if "S-needs-rebase" in labels:
+ api.remove_label("S-needs-rebase")
+
+class StatusUpdateHandler(EventHandler):
+ def on_pr_opened(self, api, payload):
+ manage_pr_state(api, payload)
+
+ def on_pr_updated(self, api, payload):
+ manage_pr_state(api, payload)
+
+
+handler_interface = StatusUpdateHandler
diff --git a/test_new_pr.json b/handlers/status_update/tests/new_pr.json
similarity index 99%
rename from test_new_pr.json
rename to handlers/status_update/tests/new_pr.json
index 91bd48d..ab1cf33 100644
--- a/test_new_pr.json
+++ b/handlers/status_update/tests/new_pr.json
@@ -1,3 +1,10 @@
+{
+ "initial": {
+ },
+ "expected": {
+ "labels": ["S-awaiting-review"]
+ },
+ "payload":
{
"action": "opened",
"number": 7076,
@@ -421,3 +428,4 @@
"site_admin": false
}
}
+}
diff --git a/test_synchronize.json b/handlers/status_update/tests/synchronize.json
similarity index 99%
rename from test_synchronize.json
rename to handlers/status_update/tests/synchronize.json
index 1b26d31..3b724f5 100644
--- a/test_synchronize.json
+++ b/handlers/status_update/tests/synchronize.json
@@ -1,3 +1,11 @@
+{
+ "initial": {
+ "labels": ["S-needs-code-changes", "S-tests-failed", "S-awaiting-merge"]
+ },
+ "expected": {
+ "labels": ["S-awaiting-review"]
+ },
+ "payload":
{
"action": "synchronize",
"number": 7062,
@@ -421,3 +429,4 @@
"site_admin": false
}
}
+}
diff --git a/handlers/unsafe/__init__.py b/handlers/unsafe/__init__.py
new file mode 100644
index 0000000..a64656c
--- /dev/null
+++ b/handlers/unsafe/__init__.py
@@ -0,0 +1,14 @@
+from eventhandler import EventHandler
+
+unsafe_warning_msg = 'These commits modify **unsafe code**. Please review it carefully!'
+
+class UnsafeHandler(EventHandler):
+ def on_pr_opened(self, api, payload):
+ diff = api.get_diff()
+ for line in diff.split('\n'):
+ if line.startswith('+') and not line.startswith('+++') and line.find('unsafe ') > -1:
+ self.warn(unsafe_warning_msg)
+ return
+
+
+handler_interface = UnsafeHandler
diff --git a/handlers/unsafe/tests/new_pr.json b/handlers/unsafe/tests/new_pr.json
new file mode 100644
index 0000000..d9b98d6
--- /dev/null
+++ b/handlers/unsafe/tests/new_pr.json
@@ -0,0 +1,432 @@
+{
+ "initial": {
+ "diff": "+ unsafe fn foo()"
+ },
+ "expected": {
+ "comments": 1
+ },
+ "payload":
+{
+ "action": "opened",
+ "number": 7076,
+ "pull_request": {
+ "url": "https://api.github.com/repos/servo/servo/pulls/7076",
+ "id": 41896942,
+ "html_url": "https://github.com/servo/servo/pull/7076",
+ "diff_url": "https://github.com/servo/servo/pull/7076.diff",
+ "patch_url": "https://github.com/servo/servo/pull/7076.patch",
+ "issue_url": "https://api.github.com/repos/servo/servo/issues/7076",
+ "number": 7076,
+ "state": "open",
+ "locked": false,
+ "title": "Remove invalid file path in ignored_files for tidying",
+ "user": {
+ "login": "frewsxcv",
+ "id": 416575,
+ "avatar_url": "https://avatars.githubusercontent.com/u/416575?v=3",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/frewsxcv",
+ "html_url": "https://github.com/frewsxcv",
+ "followers_url": "https://api.github.com/users/frewsxcv/followers",
+ "following_url": "https://api.github.com/users/frewsxcv/following{/other_user}",
+ "gists_url": "https://api.github.com/users/frewsxcv/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/frewsxcv/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/frewsxcv/subscriptions",
+ "organizations_url": "https://api.github.com/users/frewsxcv/orgs",
+ "repos_url": "https://api.github.com/users/frewsxcv/repos",
+ "events_url": "https://api.github.com/users/frewsxcv/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/frewsxcv/received_events",
+ "type": "User",
+ "site_admin": false
+ },
+ "body": null,
+ "created_at": "2015-08-07T14:24:50Z",
+ "updated_at": "2015-08-07T14:24:50Z",
+ "closed_at": null,
+ "merged_at": null,
+ "merge_commit_sha": null,
+ "assignee": null,
+ "milestone": null,
+ "commits_url": "https://api.github.com/repos/servo/servo/pulls/7076/commits",
+ "review_comments_url": "https://api.github.com/repos/servo/servo/pulls/7076/comments",
+ "review_comment_url": "https://api.github.com/repos/servo/servo/pulls/comments{/number}",
+ "comments_url": "https://api.github.com/repos/servo/servo/issues/7076/comments",
+ "statuses_url": "https://api.github.com/repos/servo/servo/statuses/da44f31cb1debe2c8161ee280121d79d3dd5ec18",
+ "head": {
+ "label": "frewsxcv:tidy-rm-invalid-file",
+ "ref": "tidy-rm-invalid-file",
+ "sha": "da44f31cb1debe2c8161ee280121d79d3dd5ec18",
+ "user": {
+ "login": "frewsxcv",
+ "id": 416575,
+ "avatar_url": "https://avatars.githubusercontent.com/u/416575?v=3",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/frewsxcv",
+ "html_url": "https://github.com/frewsxcv",
+ "followers_url": "https://api.github.com/users/frewsxcv/followers",
+ "following_url": "https://api.github.com/users/frewsxcv/following{/other_user}",
+ "gists_url": "https://api.github.com/users/frewsxcv/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/frewsxcv/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/frewsxcv/subscriptions",
+ "organizations_url": "https://api.github.com/users/frewsxcv/orgs",
+ "repos_url": "https://api.github.com/users/frewsxcv/repos",
+ "events_url": "https://api.github.com/users/frewsxcv/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/frewsxcv/received_events",
+ "type": "User",
+ "site_admin": false
+ },
+ "repo": {
+ "id": 27014207,
+ "name": "servo",
+ "full_name": "frewsxcv/servo",
+ "owner": {
+ "login": "frewsxcv",
+ "id": 416575,
+ "avatar_url": "https://avatars.githubusercontent.com/u/416575?v=3",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/frewsxcv",
+ "html_url": "https://github.com/frewsxcv",
+ "followers_url": "https://api.github.com/users/frewsxcv/followers",
+ "following_url": "https://api.github.com/users/frewsxcv/following{/other_user}",
+ "gists_url": "https://api.github.com/users/frewsxcv/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/frewsxcv/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/frewsxcv/subscriptions",
+ "organizations_url": "https://api.github.com/users/frewsxcv/orgs",
+ "repos_url": "https://api.github.com/users/frewsxcv/repos",
+ "events_url": "https://api.github.com/users/frewsxcv/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/frewsxcv/received_events",
+ "type": "User",
+ "site_admin": false
+ },
+ "private": false,
+ "html_url": "https://github.com/frewsxcv/servo",
+ "description": "The Servo Browser Engine",
+ "fork": true,
+ "url": "https://api.github.com/repos/frewsxcv/servo",
+ "forks_url": "https://api.github.com/repos/frewsxcv/servo/forks",
+ "keys_url": "https://api.github.com/repos/frewsxcv/servo/keys{/key_id}",
+ "collaborators_url": "https://api.github.com/repos/frewsxcv/servo/collaborators{/collaborator}",
+ "teams_url": "https://api.github.com/repos/frewsxcv/servo/teams",
+ "hooks_url": "https://api.github.com/repos/frewsxcv/servo/hooks",
+ "issue_events_url": "https://api.github.com/repos/frewsxcv/servo/issues/events{/number}",
+ "events_url": "https://api.github.com/repos/frewsxcv/servo/events",
+ "assignees_url": "https://api.github.com/repos/frewsxcv/servo/assignees{/user}",
+ "branches_url": "https://api.github.com/repos/frewsxcv/servo/branches{/branch}",
+ "tags_url": "https://api.github.com/repos/frewsxcv/servo/tags",
+ "blobs_url": "https://api.github.com/repos/frewsxcv/servo/git/blobs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/frewsxcv/servo/git/tags{/sha}",
+ "git_refs_url": "https://api.github.com/repos/frewsxcv/servo/git/refs{/sha}",
+ "trees_url": "https://api.github.com/repos/frewsxcv/servo/git/trees{/sha}",
+ "statuses_url": "https://api.github.com/repos/frewsxcv/servo/statuses/{sha}",
+ "languages_url": "https://api.github.com/repos/frewsxcv/servo/languages",
+ "stargazers_url": "https://api.github.com/repos/frewsxcv/servo/stargazers",
+ "contributors_url": "https://api.github.com/repos/frewsxcv/servo/contributors",
+ "subscribers_url": "https://api.github.com/repos/frewsxcv/servo/subscribers",
+ "subscription_url": "https://api.github.com/repos/frewsxcv/servo/subscription",
+ "commits_url": "https://api.github.com/repos/frewsxcv/servo/commits{/sha}",
+ "git_commits_url": "https://api.github.com/repos/frewsxcv/servo/git/commits{/sha}",
+ "comments_url": "https://api.github.com/repos/frewsxcv/servo/comments{/number}",
+ "issue_comment_url": "https://api.github.com/repos/frewsxcv/servo/issues/comments{/number}",
+ "contents_url": "https://api.github.com/repos/frewsxcv/servo/contents/{+path}",
+ "compare_url": "https://api.github.com/repos/frewsxcv/servo/compare/{base}...{head}",
+ "merges_url": "https://api.github.com/repos/frewsxcv/servo/merges",
+ "archive_url": "https://api.github.com/repos/frewsxcv/servo/{archive_format}{/ref}",
+ "downloads_url": "https://api.github.com/repos/frewsxcv/servo/downloads",
+ "issues_url": "https://api.github.com/repos/frewsxcv/servo/issues{/number}",
+ "pulls_url": "https://api.github.com/repos/frewsxcv/servo/pulls{/number}",
+ "milestones_url": "https://api.github.com/repos/frewsxcv/servo/milestones{/number}",
+ "notifications_url": "https://api.github.com/repos/frewsxcv/servo/notifications{?since,all,participating}",
+ "labels_url": "https://api.github.com/repos/frewsxcv/servo/labels{/name}",
+ "releases_url": "https://api.github.com/repos/frewsxcv/servo/releases{/id}",
+ "created_at": "2014-11-22T22:10:35Z",
+ "updated_at": "2014-11-22T22:10:38Z",
+ "pushed_at": "2015-08-07T14:24:45Z",
+ "git_url": "git://github.com/frewsxcv/servo.git",
+ "ssh_url": "git@github.com:frewsxcv/servo.git",
+ "clone_url": "https://github.com/frewsxcv/servo.git",
+ "svn_url": "https://github.com/frewsxcv/servo",
+ "homepage": "",
+ "size": 119557,
+ "stargazers_count": 0,
+ "watchers_count": 0,
+ "language": "Rust",
+ "has_issues": false,
+ "has_downloads": true,
+ "has_wiki": true,
+ "has_pages": true,
+ "forks_count": 0,
+ "mirror_url": null,
+ "open_issues_count": 0,
+ "forks": 0,
+ "open_issues": 0,
+ "watchers": 0,
+ "default_branch": "master"
+ }
+ },
+ "base": {
+ "label": "servo:master",
+ "ref": "master",
+ "sha": "b4e30da3dbf58c16703864f4bec4b0b0132084fa",
+ "user": {
+ "login": "servo",
+ "id": 2566135,
+ "avatar_url": "https://avatars.githubusercontent.com/u/2566135?v=3",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/servo",
+ "html_url": "https://github.com/servo",
+ "followers_url": "https://api.github.com/users/servo/followers",
+ "following_url": "https://api.github.com/users/servo/following{/other_user}",
+ "gists_url": "https://api.github.com/users/servo/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/servo/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/servo/subscriptions",
+ "organizations_url": "https://api.github.com/users/servo/orgs",
+ "repos_url": "https://api.github.com/users/servo/repos",
+ "events_url": "https://api.github.com/users/servo/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/servo/received_events",
+ "type": "Organization",
+ "site_admin": false
+ },
+ "repo": {
+ "id": 3390243,
+ "name": "servo",
+ "full_name": "servo/servo",
+ "owner": {
+ "login": "servo",
+ "id": 2566135,
+ "avatar_url": "https://avatars.githubusercontent.com/u/2566135?v=3",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/servo",
+ "html_url": "https://github.com/servo",
+ "followers_url": "https://api.github.com/users/servo/followers",
+ "following_url": "https://api.github.com/users/servo/following{/other_user}",
+ "gists_url": "https://api.github.com/users/servo/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/servo/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/servo/subscriptions",
+ "organizations_url": "https://api.github.com/users/servo/orgs",
+ "repos_url": "https://api.github.com/users/servo/repos",
+ "events_url": "https://api.github.com/users/servo/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/servo/received_events",
+ "type": "Organization",
+ "site_admin": false
+ },
+ "private": false,
+ "html_url": "https://github.com/servo/servo",
+ "description": "The Servo Browser Engine",
+ "fork": false,
+ "url": "https://api.github.com/repos/servo/servo",
+ "forks_url": "https://api.github.com/repos/servo/servo/forks",
+ "keys_url": "https://api.github.com/repos/servo/servo/keys{/key_id}",
+ "collaborators_url": "https://api.github.com/repos/servo/servo/collaborators{/collaborator}",
+ "teams_url": "https://api.github.com/repos/servo/servo/teams",
+ "hooks_url": "https://api.github.com/repos/servo/servo/hooks",
+ "issue_events_url": "https://api.github.com/repos/servo/servo/issues/events{/number}",
+ "events_url": "https://api.github.com/repos/servo/servo/events",
+ "assignees_url": "https://api.github.com/repos/servo/servo/assignees{/user}",
+ "branches_url": "https://api.github.com/repos/servo/servo/branches{/branch}",
+ "tags_url": "https://api.github.com/repos/servo/servo/tags",
+ "blobs_url": "https://api.github.com/repos/servo/servo/git/blobs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/servo/servo/git/tags{/sha}",
+ "git_refs_url": "https://api.github.com/repos/servo/servo/git/refs{/sha}",
+ "trees_url": "https://api.github.com/repos/servo/servo/git/trees{/sha}",
+ "statuses_url": "https://api.github.com/repos/servo/servo/statuses/{sha}",
+ "languages_url": "https://api.github.com/repos/servo/servo/languages",
+ "stargazers_url": "https://api.github.com/repos/servo/servo/stargazers",
+ "contributors_url": "https://api.github.com/repos/servo/servo/contributors",
+ "subscribers_url": "https://api.github.com/repos/servo/servo/subscribers",
+ "subscription_url": "https://api.github.com/repos/servo/servo/subscription",
+ "commits_url": "https://api.github.com/repos/servo/servo/commits{/sha}",
+ "git_commits_url": "https://api.github.com/repos/servo/servo/git/commits{/sha}",
+ "comments_url": "https://api.github.com/repos/servo/servo/comments{/number}",
+ "issue_comment_url": "https://api.github.com/repos/servo/servo/issues/comments{/number}",
+ "contents_url": "https://api.github.com/repos/servo/servo/contents/{+path}",
+ "compare_url": "https://api.github.com/repos/servo/servo/compare/{base}...{head}",
+ "merges_url": "https://api.github.com/repos/servo/servo/merges",
+ "archive_url": "https://api.github.com/repos/servo/servo/{archive_format}{/ref}",
+ "downloads_url": "https://api.github.com/repos/servo/servo/downloads",
+ "issues_url": "https://api.github.com/repos/servo/servo/issues{/number}",
+ "pulls_url": "https://api.github.com/repos/servo/servo/pulls{/number}",
+ "milestones_url": "https://api.github.com/repos/servo/servo/milestones{/number}",
+ "notifications_url": "https://api.github.com/repos/servo/servo/notifications{?since,all,participating}",
+ "labels_url": "https://api.github.com/repos/servo/servo/labels{/name}",
+ "releases_url": "https://api.github.com/repos/servo/servo/releases{/id}",
+ "created_at": "2012-02-08T19:07:25Z",
+ "updated_at": "2015-08-07T09:37:49Z",
+ "pushed_at": "2015-08-07T14:10:08Z",
+ "git_url": "git://github.com/servo/servo.git",
+ "ssh_url": "git@github.com:servo/servo.git",
+ "clone_url": "https://github.com/servo/servo.git",
+ "svn_url": "https://github.com/servo/servo",
+ "homepage": "",
+ "size": 1904176,
+ "stargazers_count": 4571,
+ "watchers_count": 4571,
+ "language": "Rust",
+ "has_issues": true,
+ "has_downloads": true,
+ "has_wiki": true,
+ "has_pages": false,
+ "forks_count": 717,
+ "mirror_url": null,
+ "open_issues_count": 1060,
+ "forks": 717,
+ "open_issues": 1060,
+ "watchers": 4571,
+ "default_branch": "master"
+ }
+ },
+ "_links": {
+ "self": {
+ "href": "https://api.github.com/repos/servo/servo/pulls/7076"
+ },
+ "html": {
+ "href": "https://github.com/servo/servo/pull/7076"
+ },
+ "issue": {
+ "href": "https://api.github.com/repos/servo/servo/issues/7076"
+ },
+ "comments": {
+ "href": "https://api.github.com/repos/servo/servo/issues/7076/comments"
+ },
+ "review_comments": {
+ "href": "https://api.github.com/repos/servo/servo/pulls/7076/comments"
+ },
+ "review_comment": {
+ "href": "https://api.github.com/repos/servo/servo/pulls/comments{/number}"
+ },
+ "commits": {
+ "href": "https://api.github.com/repos/servo/servo/pulls/7076/commits"
+ },
+ "statuses": {
+ "href": "https://api.github.com/repos/servo/servo/statuses/da44f31cb1debe2c8161ee280121d79d3dd5ec18"
+ }
+ },
+ "merged": false,
+ "mergeable": null,
+ "mergeable_state": "unknown",
+ "merged_by": null,
+ "comments": 0,
+ "review_comments": 0,
+ "commits": 1,
+ "additions": 0,
+ "deletions": 1,
+ "changed_files": 1
+ },
+ "repository": {
+ "id": 3390243,
+ "name": "servo",
+ "full_name": "servo/servo",
+ "owner": {
+ "login": "servo",
+ "id": 2566135,
+ "avatar_url": "https://avatars.githubusercontent.com/u/2566135?v=3",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/servo",
+ "html_url": "https://github.com/servo",
+ "followers_url": "https://api.github.com/users/servo/followers",
+ "following_url": "https://api.github.com/users/servo/following{/other_user}",
+ "gists_url": "https://api.github.com/users/servo/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/servo/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/servo/subscriptions",
+ "organizations_url": "https://api.github.com/users/servo/orgs",
+ "repos_url": "https://api.github.com/users/servo/repos",
+ "events_url": "https://api.github.com/users/servo/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/servo/received_events",
+ "type": "Organization",
+ "site_admin": false
+ },
+ "private": false,
+ "html_url": "https://github.com/servo/servo",
+ "description": "The Servo Browser Engine",
+ "fork": false,
+ "url": "https://api.github.com/repos/servo/servo",
+ "forks_url": "https://api.github.com/repos/servo/servo/forks",
+ "keys_url": "https://api.github.com/repos/servo/servo/keys{/key_id}",
+ "collaborators_url": "https://api.github.com/repos/servo/servo/collaborators{/collaborator}",
+ "teams_url": "https://api.github.com/repos/servo/servo/teams",
+ "hooks_url": "https://api.github.com/repos/servo/servo/hooks",
+ "issue_events_url": "https://api.github.com/repos/servo/servo/issues/events{/number}",
+ "events_url": "https://api.github.com/repos/servo/servo/events",
+ "assignees_url": "https://api.github.com/repos/servo/servo/assignees{/user}",
+ "branches_url": "https://api.github.com/repos/servo/servo/branches{/branch}",
+ "tags_url": "https://api.github.com/repos/servo/servo/tags",
+ "blobs_url": "https://api.github.com/repos/servo/servo/git/blobs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/servo/servo/git/tags{/sha}",
+ "git_refs_url": "https://api.github.com/repos/servo/servo/git/refs{/sha}",
+ "trees_url": "https://api.github.com/repos/servo/servo/git/trees{/sha}",
+ "statuses_url": "https://api.github.com/repos/servo/servo/statuses/{sha}",
+ "languages_url": "https://api.github.com/repos/servo/servo/languages",
+ "stargazers_url": "https://api.github.com/repos/servo/servo/stargazers",
+ "contributors_url": "https://api.github.com/repos/servo/servo/contributors",
+ "subscribers_url": "https://api.github.com/repos/servo/servo/subscribers",
+ "subscription_url": "https://api.github.com/repos/servo/servo/subscription",
+ "commits_url": "https://api.github.com/repos/servo/servo/commits{/sha}",
+ "git_commits_url": "https://api.github.com/repos/servo/servo/git/commits{/sha}",
+ "comments_url": "https://api.github.com/repos/servo/servo/comments{/number}",
+ "issue_comment_url": "https://api.github.com/repos/servo/servo/issues/comments{/number}",
+ "contents_url": "https://api.github.com/repos/servo/servo/contents/{+path}",
+ "compare_url": "https://api.github.com/repos/servo/servo/compare/{base}...{head}",
+ "merges_url": "https://api.github.com/repos/servo/servo/merges",
+ "archive_url": "https://api.github.com/repos/servo/servo/{archive_format}{/ref}",
+ "downloads_url": "https://api.github.com/repos/servo/servo/downloads",
+ "issues_url": "https://api.github.com/repos/servo/servo/issues{/number}",
+ "pulls_url": "https://api.github.com/repos/servo/servo/pulls{/number}",
+ "milestones_url": "https://api.github.com/repos/servo/servo/milestones{/number}",
+ "notifications_url": "https://api.github.com/repos/servo/servo/notifications{?since,all,participating}",
+ "labels_url": "https://api.github.com/repos/servo/servo/labels{/name}",
+ "releases_url": "https://api.github.com/repos/servo/servo/releases{/id}",
+ "created_at": "2012-02-08T19:07:25Z",
+ "updated_at": "2015-08-07T09:37:49Z",
+ "pushed_at": "2015-08-07T14:10:08Z",
+ "git_url": "git://github.com/servo/servo.git",
+ "ssh_url": "git@github.com:servo/servo.git",
+ "clone_url": "https://github.com/servo/servo.git",
+ "svn_url": "https://github.com/servo/servo",
+ "homepage": "",
+ "size": 1904176,
+ "stargazers_count": 4571,
+ "watchers_count": 4571,
+ "language": "Rust",
+ "has_issues": true,
+ "has_downloads": true,
+ "has_wiki": true,
+ "has_pages": false,
+ "forks_count": 717,
+ "mirror_url": null,
+ "open_issues_count": 1060,
+ "forks": 717,
+ "open_issues": 1060,
+ "watchers": 4571,
+ "default_branch": "master"
+ },
+ "organization": {
+ "login": "servo",
+ "id": 2566135,
+ "url": "https://api.github.com/orgs/servo",
+ "repos_url": "https://api.github.com/orgs/servo/repos",
+ "events_url": "https://api.github.com/orgs/servo/events",
+ "members_url": "https://api.github.com/orgs/servo/members{/member}",
+ "public_members_url": "https://api.github.com/orgs/servo/public_members{/member}",
+ "avatar_url": "https://avatars.githubusercontent.com/u/2566135?v=3",
+ "description": null
+ },
+ "sender": {
+ "login": "frewsxcv",
+ "id": 416575,
+ "avatar_url": "https://avatars.githubusercontent.com/u/416575?v=3",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/frewsxcv",
+ "html_url": "https://github.com/frewsxcv",
+ "followers_url": "https://api.github.com/users/frewsxcv/followers",
+ "following_url": "https://api.github.com/users/frewsxcv/following{/other_user}",
+ "gists_url": "https://api.github.com/users/frewsxcv/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/frewsxcv/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/frewsxcv/subscriptions",
+ "organizations_url": "https://api.github.com/users/frewsxcv/orgs",
+ "repos_url": "https://api.github.com/users/frewsxcv/repos",
+ "events_url": "https://api.github.com/users/frewsxcv/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/frewsxcv/received_events",
+ "type": "User",
+ "site_admin": false
+ }
+}
+}
diff --git a/handlers/welcome/__init__.py b/handlers/welcome/__init__.py
new file mode 100644
index 0000000..0bf8f3e
--- /dev/null
+++ b/handlers/welcome/__init__.py
@@ -0,0 +1,21 @@
+from eventhandler import EventHandler
+import random
+
+welcome_msg = "Thanks for the pull request, and welcome! The Servo team is excited to review your changes, and you should hear from @%s (or someone else) soon."
+
+class WelcomeHandler(EventHandler):
+ def on_pr_opened(self, api, payload):
+ author = payload["pull_request"]['user']['login']
+ if api.is_new_contributor(author):
+ collaborators = ['jdm', 'larsbergstrom', 'metajack', 'mbrubeck',
+ 'Ms2ger', 'Manishearth', 'glennw', 'pcwalton',
+ 'SimonSapin'] \
+ if api.repo == 'servo' and api.owner == 'servo' \
+ else ['test_user_selection_ignore_this']
+ random.seed()
+ to_notify = random.choice(collaborators)
+ api.post_comment(welcome_msg % to_notify)
+
+
+handler_interface = WelcomeHandler
+
diff --git a/handlers/welcome/tests/new_pr.json b/handlers/welcome/tests/new_pr.json
new file mode 100644
index 0000000..cfcc1cb
--- /dev/null
+++ b/handlers/welcome/tests/new_pr.json
@@ -0,0 +1,432 @@
+{
+ "initial": {
+ "new_contributor": true
+ },
+ "expected": {
+ "comments": 1
+ },
+ "payload":
+{
+ "action": "opened",
+ "number": 7076,
+ "pull_request": {
+ "url": "https://api.github.com/repos/servo/servo/pulls/7076",
+ "id": 41896942,
+ "html_url": "https://github.com/servo/servo/pull/7076",
+ "diff_url": "https://github.com/servo/servo/pull/7076.diff",
+ "patch_url": "https://github.com/servo/servo/pull/7076.patch",
+ "issue_url": "https://api.github.com/repos/servo/servo/issues/7076",
+ "number": 7076,
+ "state": "open",
+ "locked": false,
+ "title": "Remove invalid file path in ignored_files for tidying",
+ "user": {
+ "login": "frewsxcv",
+ "id": 416575,
+ "avatar_url": "https://avatars.githubusercontent.com/u/416575?v=3",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/frewsxcv",
+ "html_url": "https://github.com/frewsxcv",
+ "followers_url": "https://api.github.com/users/frewsxcv/followers",
+ "following_url": "https://api.github.com/users/frewsxcv/following{/other_user}",
+ "gists_url": "https://api.github.com/users/frewsxcv/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/frewsxcv/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/frewsxcv/subscriptions",
+ "organizations_url": "https://api.github.com/users/frewsxcv/orgs",
+ "repos_url": "https://api.github.com/users/frewsxcv/repos",
+ "events_url": "https://api.github.com/users/frewsxcv/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/frewsxcv/received_events",
+ "type": "User",
+ "site_admin": false
+ },
+ "body": null,
+ "created_at": "2015-08-07T14:24:50Z",
+ "updated_at": "2015-08-07T14:24:50Z",
+ "closed_at": null,
+ "merged_at": null,
+ "merge_commit_sha": null,
+ "assignee": null,
+ "milestone": null,
+ "commits_url": "https://api.github.com/repos/servo/servo/pulls/7076/commits",
+ "review_comments_url": "https://api.github.com/repos/servo/servo/pulls/7076/comments",
+ "review_comment_url": "https://api.github.com/repos/servo/servo/pulls/comments{/number}",
+ "comments_url": "https://api.github.com/repos/servo/servo/issues/7076/comments",
+ "statuses_url": "https://api.github.com/repos/servo/servo/statuses/da44f31cb1debe2c8161ee280121d79d3dd5ec18",
+ "head": {
+ "label": "frewsxcv:tidy-rm-invalid-file",
+ "ref": "tidy-rm-invalid-file",
+ "sha": "da44f31cb1debe2c8161ee280121d79d3dd5ec18",
+ "user": {
+ "login": "frewsxcv",
+ "id": 416575,
+ "avatar_url": "https://avatars.githubusercontent.com/u/416575?v=3",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/frewsxcv",
+ "html_url": "https://github.com/frewsxcv",
+ "followers_url": "https://api.github.com/users/frewsxcv/followers",
+ "following_url": "https://api.github.com/users/frewsxcv/following{/other_user}",
+ "gists_url": "https://api.github.com/users/frewsxcv/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/frewsxcv/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/frewsxcv/subscriptions",
+ "organizations_url": "https://api.github.com/users/frewsxcv/orgs",
+ "repos_url": "https://api.github.com/users/frewsxcv/repos",
+ "events_url": "https://api.github.com/users/frewsxcv/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/frewsxcv/received_events",
+ "type": "User",
+ "site_admin": false
+ },
+ "repo": {
+ "id": 27014207,
+ "name": "servo",
+ "full_name": "frewsxcv/servo",
+ "owner": {
+ "login": "frewsxcv",
+ "id": 416575,
+ "avatar_url": "https://avatars.githubusercontent.com/u/416575?v=3",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/frewsxcv",
+ "html_url": "https://github.com/frewsxcv",
+ "followers_url": "https://api.github.com/users/frewsxcv/followers",
+ "following_url": "https://api.github.com/users/frewsxcv/following{/other_user}",
+ "gists_url": "https://api.github.com/users/frewsxcv/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/frewsxcv/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/frewsxcv/subscriptions",
+ "organizations_url": "https://api.github.com/users/frewsxcv/orgs",
+ "repos_url": "https://api.github.com/users/frewsxcv/repos",
+ "events_url": "https://api.github.com/users/frewsxcv/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/frewsxcv/received_events",
+ "type": "User",
+ "site_admin": false
+ },
+ "private": false,
+ "html_url": "https://github.com/frewsxcv/servo",
+ "description": "The Servo Browser Engine",
+ "fork": true,
+ "url": "https://api.github.com/repos/frewsxcv/servo",
+ "forks_url": "https://api.github.com/repos/frewsxcv/servo/forks",
+ "keys_url": "https://api.github.com/repos/frewsxcv/servo/keys{/key_id}",
+ "collaborators_url": "https://api.github.com/repos/frewsxcv/servo/collaborators{/collaborator}",
+ "teams_url": "https://api.github.com/repos/frewsxcv/servo/teams",
+ "hooks_url": "https://api.github.com/repos/frewsxcv/servo/hooks",
+ "issue_events_url": "https://api.github.com/repos/frewsxcv/servo/issues/events{/number}",
+ "events_url": "https://api.github.com/repos/frewsxcv/servo/events",
+ "assignees_url": "https://api.github.com/repos/frewsxcv/servo/assignees{/user}",
+ "branches_url": "https://api.github.com/repos/frewsxcv/servo/branches{/branch}",
+ "tags_url": "https://api.github.com/repos/frewsxcv/servo/tags",
+ "blobs_url": "https://api.github.com/repos/frewsxcv/servo/git/blobs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/frewsxcv/servo/git/tags{/sha}",
+ "git_refs_url": "https://api.github.com/repos/frewsxcv/servo/git/refs{/sha}",
+ "trees_url": "https://api.github.com/repos/frewsxcv/servo/git/trees{/sha}",
+ "statuses_url": "https://api.github.com/repos/frewsxcv/servo/statuses/{sha}",
+ "languages_url": "https://api.github.com/repos/frewsxcv/servo/languages",
+ "stargazers_url": "https://api.github.com/repos/frewsxcv/servo/stargazers",
+ "contributors_url": "https://api.github.com/repos/frewsxcv/servo/contributors",
+ "subscribers_url": "https://api.github.com/repos/frewsxcv/servo/subscribers",
+ "subscription_url": "https://api.github.com/repos/frewsxcv/servo/subscription",
+ "commits_url": "https://api.github.com/repos/frewsxcv/servo/commits{/sha}",
+ "git_commits_url": "https://api.github.com/repos/frewsxcv/servo/git/commits{/sha}",
+ "comments_url": "https://api.github.com/repos/frewsxcv/servo/comments{/number}",
+ "issue_comment_url": "https://api.github.com/repos/frewsxcv/servo/issues/comments{/number}",
+ "contents_url": "https://api.github.com/repos/frewsxcv/servo/contents/{+path}",
+ "compare_url": "https://api.github.com/repos/frewsxcv/servo/compare/{base}...{head}",
+ "merges_url": "https://api.github.com/repos/frewsxcv/servo/merges",
+ "archive_url": "https://api.github.com/repos/frewsxcv/servo/{archive_format}{/ref}",
+ "downloads_url": "https://api.github.com/repos/frewsxcv/servo/downloads",
+ "issues_url": "https://api.github.com/repos/frewsxcv/servo/issues{/number}",
+ "pulls_url": "https://api.github.com/repos/frewsxcv/servo/pulls{/number}",
+ "milestones_url": "https://api.github.com/repos/frewsxcv/servo/milestones{/number}",
+ "notifications_url": "https://api.github.com/repos/frewsxcv/servo/notifications{?since,all,participating}",
+ "labels_url": "https://api.github.com/repos/frewsxcv/servo/labels{/name}",
+ "releases_url": "https://api.github.com/repos/frewsxcv/servo/releases{/id}",
+ "created_at": "2014-11-22T22:10:35Z",
+ "updated_at": "2014-11-22T22:10:38Z",
+ "pushed_at": "2015-08-07T14:24:45Z",
+ "git_url": "git://github.com/frewsxcv/servo.git",
+ "ssh_url": "git@github.com:frewsxcv/servo.git",
+ "clone_url": "https://github.com/frewsxcv/servo.git",
+ "svn_url": "https://github.com/frewsxcv/servo",
+ "homepage": "",
+ "size": 119557,
+ "stargazers_count": 0,
+ "watchers_count": 0,
+ "language": "Rust",
+ "has_issues": false,
+ "has_downloads": true,
+ "has_wiki": true,
+ "has_pages": true,
+ "forks_count": 0,
+ "mirror_url": null,
+ "open_issues_count": 0,
+ "forks": 0,
+ "open_issues": 0,
+ "watchers": 0,
+ "default_branch": "master"
+ }
+ },
+ "base": {
+ "label": "servo:master",
+ "ref": "master",
+ "sha": "b4e30da3dbf58c16703864f4bec4b0b0132084fa",
+ "user": {
+ "login": "servo",
+ "id": 2566135,
+ "avatar_url": "https://avatars.githubusercontent.com/u/2566135?v=3",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/servo",
+ "html_url": "https://github.com/servo",
+ "followers_url": "https://api.github.com/users/servo/followers",
+ "following_url": "https://api.github.com/users/servo/following{/other_user}",
+ "gists_url": "https://api.github.com/users/servo/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/servo/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/servo/subscriptions",
+ "organizations_url": "https://api.github.com/users/servo/orgs",
+ "repos_url": "https://api.github.com/users/servo/repos",
+ "events_url": "https://api.github.com/users/servo/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/servo/received_events",
+ "type": "Organization",
+ "site_admin": false
+ },
+ "repo": {
+ "id": 3390243,
+ "name": "servo",
+ "full_name": "servo/servo",
+ "owner": {
+ "login": "servo",
+ "id": 2566135,
+ "avatar_url": "https://avatars.githubusercontent.com/u/2566135?v=3",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/servo",
+ "html_url": "https://github.com/servo",
+ "followers_url": "https://api.github.com/users/servo/followers",
+ "following_url": "https://api.github.com/users/servo/following{/other_user}",
+ "gists_url": "https://api.github.com/users/servo/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/servo/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/servo/subscriptions",
+ "organizations_url": "https://api.github.com/users/servo/orgs",
+ "repos_url": "https://api.github.com/users/servo/repos",
+ "events_url": "https://api.github.com/users/servo/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/servo/received_events",
+ "type": "Organization",
+ "site_admin": false
+ },
+ "private": false,
+ "html_url": "https://github.com/servo/servo",
+ "description": "The Servo Browser Engine",
+ "fork": false,
+ "url": "https://api.github.com/repos/servo/servo",
+ "forks_url": "https://api.github.com/repos/servo/servo/forks",
+ "keys_url": "https://api.github.com/repos/servo/servo/keys{/key_id}",
+ "collaborators_url": "https://api.github.com/repos/servo/servo/collaborators{/collaborator}",
+ "teams_url": "https://api.github.com/repos/servo/servo/teams",
+ "hooks_url": "https://api.github.com/repos/servo/servo/hooks",
+ "issue_events_url": "https://api.github.com/repos/servo/servo/issues/events{/number}",
+ "events_url": "https://api.github.com/repos/servo/servo/events",
+ "assignees_url": "https://api.github.com/repos/servo/servo/assignees{/user}",
+ "branches_url": "https://api.github.com/repos/servo/servo/branches{/branch}",
+ "tags_url": "https://api.github.com/repos/servo/servo/tags",
+ "blobs_url": "https://api.github.com/repos/servo/servo/git/blobs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/servo/servo/git/tags{/sha}",
+ "git_refs_url": "https://api.github.com/repos/servo/servo/git/refs{/sha}",
+ "trees_url": "https://api.github.com/repos/servo/servo/git/trees{/sha}",
+ "statuses_url": "https://api.github.com/repos/servo/servo/statuses/{sha}",
+ "languages_url": "https://api.github.com/repos/servo/servo/languages",
+ "stargazers_url": "https://api.github.com/repos/servo/servo/stargazers",
+ "contributors_url": "https://api.github.com/repos/servo/servo/contributors",
+ "subscribers_url": "https://api.github.com/repos/servo/servo/subscribers",
+ "subscription_url": "https://api.github.com/repos/servo/servo/subscription",
+ "commits_url": "https://api.github.com/repos/servo/servo/commits{/sha}",
+ "git_commits_url": "https://api.github.com/repos/servo/servo/git/commits{/sha}",
+ "comments_url": "https://api.github.com/repos/servo/servo/comments{/number}",
+ "issue_comment_url": "https://api.github.com/repos/servo/servo/issues/comments{/number}",
+ "contents_url": "https://api.github.com/repos/servo/servo/contents/{+path}",
+ "compare_url": "https://api.github.com/repos/servo/servo/compare/{base}...{head}",
+ "merges_url": "https://api.github.com/repos/servo/servo/merges",
+ "archive_url": "https://api.github.com/repos/servo/servo/{archive_format}{/ref}",
+ "downloads_url": "https://api.github.com/repos/servo/servo/downloads",
+ "issues_url": "https://api.github.com/repos/servo/servo/issues{/number}",
+ "pulls_url": "https://api.github.com/repos/servo/servo/pulls{/number}",
+ "milestones_url": "https://api.github.com/repos/servo/servo/milestones{/number}",
+ "notifications_url": "https://api.github.com/repos/servo/servo/notifications{?since,all,participating}",
+ "labels_url": "https://api.github.com/repos/servo/servo/labels{/name}",
+ "releases_url": "https://api.github.com/repos/servo/servo/releases{/id}",
+ "created_at": "2012-02-08T19:07:25Z",
+ "updated_at": "2015-08-07T09:37:49Z",
+ "pushed_at": "2015-08-07T14:10:08Z",
+ "git_url": "git://github.com/servo/servo.git",
+ "ssh_url": "git@github.com:servo/servo.git",
+ "clone_url": "https://github.com/servo/servo.git",
+ "svn_url": "https://github.com/servo/servo",
+ "homepage": "",
+ "size": 1904176,
+ "stargazers_count": 4571,
+ "watchers_count": 4571,
+ "language": "Rust",
+ "has_issues": true,
+ "has_downloads": true,
+ "has_wiki": true,
+ "has_pages": false,
+ "forks_count": 717,
+ "mirror_url": null,
+ "open_issues_count": 1060,
+ "forks": 717,
+ "open_issues": 1060,
+ "watchers": 4571,
+ "default_branch": "master"
+ }
+ },
+ "_links": {
+ "self": {
+ "href": "https://api.github.com/repos/servo/servo/pulls/7076"
+ },
+ "html": {
+ "href": "https://github.com/servo/servo/pull/7076"
+ },
+ "issue": {
+ "href": "https://api.github.com/repos/servo/servo/issues/7076"
+ },
+ "comments": {
+ "href": "https://api.github.com/repos/servo/servo/issues/7076/comments"
+ },
+ "review_comments": {
+ "href": "https://api.github.com/repos/servo/servo/pulls/7076/comments"
+ },
+ "review_comment": {
+ "href": "https://api.github.com/repos/servo/servo/pulls/comments{/number}"
+ },
+ "commits": {
+ "href": "https://api.github.com/repos/servo/servo/pulls/7076/commits"
+ },
+ "statuses": {
+ "href": "https://api.github.com/repos/servo/servo/statuses/da44f31cb1debe2c8161ee280121d79d3dd5ec18"
+ }
+ },
+ "merged": false,
+ "mergeable": null,
+ "mergeable_state": "unknown",
+ "merged_by": null,
+ "comments": 0,
+ "review_comments": 0,
+ "commits": 1,
+ "additions": 0,
+ "deletions": 1,
+ "changed_files": 1
+ },
+ "repository": {
+ "id": 3390243,
+ "name": "servo",
+ "full_name": "servo/servo",
+ "owner": {
+ "login": "servo",
+ "id": 2566135,
+ "avatar_url": "https://avatars.githubusercontent.com/u/2566135?v=3",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/servo",
+ "html_url": "https://github.com/servo",
+ "followers_url": "https://api.github.com/users/servo/followers",
+ "following_url": "https://api.github.com/users/servo/following{/other_user}",
+ "gists_url": "https://api.github.com/users/servo/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/servo/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/servo/subscriptions",
+ "organizations_url": "https://api.github.com/users/servo/orgs",
+ "repos_url": "https://api.github.com/users/servo/repos",
+ "events_url": "https://api.github.com/users/servo/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/servo/received_events",
+ "type": "Organization",
+ "site_admin": false
+ },
+ "private": false,
+ "html_url": "https://github.com/servo/servo",
+ "description": "The Servo Browser Engine",
+ "fork": false,
+ "url": "https://api.github.com/repos/servo/servo",
+ "forks_url": "https://api.github.com/repos/servo/servo/forks",
+ "keys_url": "https://api.github.com/repos/servo/servo/keys{/key_id}",
+ "collaborators_url": "https://api.github.com/repos/servo/servo/collaborators{/collaborator}",
+ "teams_url": "https://api.github.com/repos/servo/servo/teams",
+ "hooks_url": "https://api.github.com/repos/servo/servo/hooks",
+ "issue_events_url": "https://api.github.com/repos/servo/servo/issues/events{/number}",
+ "events_url": "https://api.github.com/repos/servo/servo/events",
+ "assignees_url": "https://api.github.com/repos/servo/servo/assignees{/user}",
+ "branches_url": "https://api.github.com/repos/servo/servo/branches{/branch}",
+ "tags_url": "https://api.github.com/repos/servo/servo/tags",
+ "blobs_url": "https://api.github.com/repos/servo/servo/git/blobs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/servo/servo/git/tags{/sha}",
+ "git_refs_url": "https://api.github.com/repos/servo/servo/git/refs{/sha}",
+ "trees_url": "https://api.github.com/repos/servo/servo/git/trees{/sha}",
+ "statuses_url": "https://api.github.com/repos/servo/servo/statuses/{sha}",
+ "languages_url": "https://api.github.com/repos/servo/servo/languages",
+ "stargazers_url": "https://api.github.com/repos/servo/servo/stargazers",
+ "contributors_url": "https://api.github.com/repos/servo/servo/contributors",
+ "subscribers_url": "https://api.github.com/repos/servo/servo/subscribers",
+ "subscription_url": "https://api.github.com/repos/servo/servo/subscription",
+ "commits_url": "https://api.github.com/repos/servo/servo/commits{/sha}",
+ "git_commits_url": "https://api.github.com/repos/servo/servo/git/commits{/sha}",
+ "comments_url": "https://api.github.com/repos/servo/servo/comments{/number}",
+ "issue_comment_url": "https://api.github.com/repos/servo/servo/issues/comments{/number}",
+ "contents_url": "https://api.github.com/repos/servo/servo/contents/{+path}",
+ "compare_url": "https://api.github.com/repos/servo/servo/compare/{base}...{head}",
+ "merges_url": "https://api.github.com/repos/servo/servo/merges",
+ "archive_url": "https://api.github.com/repos/servo/servo/{archive_format}{/ref}",
+ "downloads_url": "https://api.github.com/repos/servo/servo/downloads",
+ "issues_url": "https://api.github.com/repos/servo/servo/issues{/number}",
+ "pulls_url": "https://api.github.com/repos/servo/servo/pulls{/number}",
+ "milestones_url": "https://api.github.com/repos/servo/servo/milestones{/number}",
+ "notifications_url": "https://api.github.com/repos/servo/servo/notifications{?since,all,participating}",
+ "labels_url": "https://api.github.com/repos/servo/servo/labels{/name}",
+ "releases_url": "https://api.github.com/repos/servo/servo/releases{/id}",
+ "created_at": "2012-02-08T19:07:25Z",
+ "updated_at": "2015-08-07T09:37:49Z",
+ "pushed_at": "2015-08-07T14:10:08Z",
+ "git_url": "git://github.com/servo/servo.git",
+ "ssh_url": "git@github.com:servo/servo.git",
+ "clone_url": "https://github.com/servo/servo.git",
+ "svn_url": "https://github.com/servo/servo",
+ "homepage": "",
+ "size": 1904176,
+ "stargazers_count": 4571,
+ "watchers_count": 4571,
+ "language": "Rust",
+ "has_issues": true,
+ "has_downloads": true,
+ "has_wiki": true,
+ "has_pages": false,
+ "forks_count": 717,
+ "mirror_url": null,
+ "open_issues_count": 1060,
+ "forks": 717,
+ "open_issues": 1060,
+ "watchers": 4571,
+ "default_branch": "master"
+ },
+ "organization": {
+ "login": "servo",
+ "id": 2566135,
+ "url": "https://api.github.com/orgs/servo",
+ "repos_url": "https://api.github.com/orgs/servo/repos",
+ "events_url": "https://api.github.com/orgs/servo/events",
+ "members_url": "https://api.github.com/orgs/servo/members{/member}",
+ "public_members_url": "https://api.github.com/orgs/servo/public_members{/member}",
+ "avatar_url": "https://avatars.githubusercontent.com/u/2566135?v=3",
+ "description": null
+ },
+ "sender": {
+ "login": "frewsxcv",
+ "id": 416575,
+ "avatar_url": "https://avatars.githubusercontent.com/u/416575?v=3",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/frewsxcv",
+ "html_url": "https://github.com/frewsxcv",
+ "followers_url": "https://api.github.com/users/frewsxcv/followers",
+ "following_url": "https://api.github.com/users/frewsxcv/following{/other_user}",
+ "gists_url": "https://api.github.com/users/frewsxcv/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/frewsxcv/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/frewsxcv/subscriptions",
+ "organizations_url": "https://api.github.com/users/frewsxcv/orgs",
+ "repos_url": "https://api.github.com/users/frewsxcv/repos",
+ "events_url": "https://api.github.com/users/frewsxcv/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/frewsxcv/received_events",
+ "type": "User",
+ "site_admin": false
+ }
+}
+}
diff --git a/newpr.py b/newpr.py
index 929cb6e..4cbbd1c 100755
--- a/newpr.py
+++ b/newpr.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python
import base64
+import eventhandler
import urllib, urllib2
import cgi
import cgitb
@@ -57,6 +58,8 @@ class GithubAPIProvider(APIProvider):
def __init__(self, payload, user, token):
APIProvider.__init__(self, payload, user)
self.token = token
+ self._labels = None
+ self._diff = None
if "pull_request" in payload:
self.diff_url = payload["pull_request"]["diff_url"]
@@ -132,6 +135,8 @@ def post_comment(self, body):
raise e
def add_label(self, label):
+ if self._labels:
+ self._labels += [label]
try:
result = self.api_req("POST", self.add_label_url % (self.owner, self.repo, self.issue),
[label])
@@ -142,6 +147,8 @@ def add_label(self, label):
raise e
def remove_label(self, label):
+ if self._labels and label in self._labels:
+ self._labels.remove(label)
try:
result = self.api_req("DELETE", self.remove_label_url % (self.owner, self.repo, self.issue, label), {})
except urllib2.HTTPError, e:
@@ -152,6 +159,8 @@ def remove_label(self, label):
pass
def get_labels(self):
+ if self._labels is not None:
+ return self._labels
try:
result = self.api_req("GET", self.get_label_url % (self.owner, self.repo, self.issue))
except urllib2.HTTPError, e:
@@ -159,10 +168,14 @@ def get_labels(self):
pass
else:
raise e
- return map(lambda x: x["name"], json.loads(result['body']))
+ self._labels = map(lambda x: x["name"], json.loads(result['body']))
+ return self._labels
def get_diff(self):
- return self.api_req("GET", self.diff_url)['body']
+ if self._diff:
+ return self._diff;
+ self._diff = self.api_req("GET", self.diff_url)['body']
+ return self._diff
def set_assignee(self, assignee):
try:
@@ -175,20 +188,7 @@ def set_assignee(self, assignee):
raise e
-# If the user specified a reviewer, return the username, otherwise returns None.
-def find_reviewer(commit_msg):
- match = reviewer_re.search(commit_msg)
- if not match:
- return None
- return match.group(1)
-
-
-welcome_msg = "Thanks for the pull request, and welcome! The Servo team is excited to review your changes, and you should hear from @%s (or someone else) soon."
warning_summary = '
**Warning**
\n\n%s'
-unsafe_warning_msg = 'These commits modify **unsafe code**. Please review it carefully!'
-reftest_required_msg = 'These commits modify layout code, but no reftests are modified. Please consider adding a reftest!'
-
-reviewer_re = re.compile("\\b[rR]\?[:\- ]*@([a-zA-Z0-9\-]+)")
def extract_globals_from_payload(payload):
if payload["action"] == "created":
@@ -202,109 +202,26 @@ def extract_globals_from_payload(payload):
return (owner, repo, issue)
-def manage_pr_state(api, payload):
- labels = api.get_labels();
-
- if payload["action"] in ["synchronize", "opened"]:
- for label in ["S-awaiting-merge", "S-tests-failed", "S-needs-code-changes"]:
- if label in labels:
- api.remove_label(label)
- if not "S-awaiting-review" in labels:
- api.add_label("S-awaiting-review")
-
- # If mergeable is null, the data wasn't available yet. It would be nice to try to fetch that
- # information again.
- if payload["action"] == "synchronize" and payload['pull_request']['mergeable']:
- if "S-needs-rebase" in labels:
- api.remove_label("S-needs-rebase")
-
-
-def new_comment(api, payload):
- # We only care about comments in open PRs
- if payload['issue']['state'] != 'open' or 'pull_request' not in payload['issue']:
- return
-
- commenter = payload['comment']['user']['login']
- # Ignore our own comments.
- if commenter == api.user:
- return
-
- msg = payload["comment"]["body"]
- reviewer = find_reviewer(msg)
- if reviewer:
- api.set_assignee(reviewer)
-
- if commenter == 'bors-servo':
- labels = api.get_labels();
-
- if 'has been approved by' in msg or 'Testing commit' in msg:
- for label in ["S-awaiting-review", "S-needs-rebase", "S-tests-failed",
- "S-needs-code-changes", "S-needs-squash", "S-awaiting-answer"]:
- if label in labels:
- api.remove_label(label)
- if not "S-awaiting-merge" in labels:
- api.add_label("S-awaiting-merge")
-
- elif 'Test failed' in msg:
- api.remove_label("S-awaiting-merge")
- api.add_label("S-tests-failed")
-
- elif 'Please resolve the merge conflicts' in msg:
- api.remove_label("S-awaiting-merge")
- api.add_label("S-needs-rebase")
-
-
-def new_pr(api, payload):
- manage_pr_state(api, payload)
-
- author = payload["pull_request"]['user']['login']
- if api.is_new_contributor(author):
- #collaborators = json.load(urllib2.urlopen(collaborators_url))
- collaborators = ['jdm', 'larsbergstrom', 'metajack', 'mbrubeck', 'Ms2ger', 'Manishearth', 'glennw', 'pcwalton', 'SimonSapin'] if api.repo == 'servo' and api.owner == 'servo' else ['test_user_selection_ignore_this']
- random.seed()
- to_notify = random.choice(collaborators)
- api.post_comment(welcome_msg % to_notify)
-
- warn_unsafe = False
- layout_changed = False
- saw_reftest = False
- diff = api.get_diff()
- for line in diff.split('\n'):
- if line.startswith('+') and not line.startswith('+++') and line.find('unsafe') > -1:
- warn_unsafe = True
- if line.startswith('diff --git') and line.find('components/layout/') > -1:
- layout_changed = True
- if line.startswith('diff --git') and line.find('tests/ref') > -1:
- saw_reftest = True
- if line.startswith('diff --git') and line.find('tests/wpt') > -1:
- saw_reftest = True
-
- warnings = []
- if warn_unsafe:
- warnings += [unsafe_warning_msg]
-
- if layout_changed:
- if not saw_reftest:
- warnings += [reftest_required_msg]
-
- if warnings:
- api.post_comment(warning_summary % '\n'.join(map(lambda x: '* ' + x, warnings)))
-
-
-def update_pr(api, payload):
- manage_pr_state(api, payload)
-
-
def handle_payload(api, payload):
+ (modules, handlers) = eventhandler.get_handlers()
+
if payload["action"] == "opened":
- new_pr(api, payload)
+ for handler in handlers:
+ handler.on_pr_opened(api, payload)
elif payload["action"] == "synchronize":
- update_pr(api, payload)
+ for handler in handlers:
+ handler.on_pr_updated(api, payload)
elif payload["action"] == "created":
- new_comment(api, payload)
+ for handler in handlers:
+ handler.on_new_comment(api, payload)
else:
pass
+ warnings = eventhandler.get_warnings()
+ if warnings:
+ api.post_comment(warning_summary % '\n'.join(map(lambda x: '* ' + x, warnings)))
+
+
if __name__ == "__main__":
print "Content-Type: text/html;charset=utf-8"
print
diff --git a/test.py b/test.py
index 8917538..f050e5f 100644
--- a/test.py
+++ b/test.py
@@ -38,33 +38,36 @@ def get_payload(filename):
with open(filename) as f:
return json.load(f)
-tests = []
-def add_test(filename, initial, expected):
+def create_test(filename, initial, expected):
global tests
initial_values = {'new_contributor': initial.get('new_contributor', False),
'labels': initial.get('labels', []),
'diff': initial.get('diff', ''),
'assignee': initial.get('assignee', None)}
- expected_values = {'labels': expected.get('labels', []),
- 'assignee': expected.get('assignee', None),
- 'comments': expected.get('comments', 0)}
- tests += [{'filename': filename,
- 'initial': initial_values,
- 'expected': expected_values}]
+ return {'filename': filename,
+ 'initial': initial_values,
+ 'expected': expected}
def run_tests(tests):
+ import eventhandler
+
failed = 0
for test in tests:
+ eventhandler.reset_test_state()
+
try:
- payload = get_payload(test['filename'])
+ payload = get_payload(test['filename'])['payload']
initial = test['initial']
api = TestAPIProvider(payload, 'highfive', initial['new_contributor'], initial['labels'],
initial['assignee'], initial['diff'])
handle_payload(api, payload)
expected = test['expected']
- assert len(api.comments_posted) == expected['comments']
- assert api.labels == expected['labels']
- assert api.assignee == expected['assignee']
+ if 'comments' in expected:
+ assert len(api.comments_posted) == expected['comments']
+ if 'labels' in expected:
+ assert api.labels == expected['labels']
+ if 'assignee' in expected:
+ assert api.assignee == expected['assignee']
except AssertionError:
_, _, tb = sys.exc_info()
traceback.print_tb(tb) # Fixed format
@@ -73,52 +76,19 @@ def run_tests(tests):
print('{}: An error occurred on line {} in statement {}'.format(test['filename'], line, text))
failed += 1
- possible_tests = [f for f in os.listdir('.') if f.endswith('.json')]
- test_files = set([test['filename'] for test in tests])
- if len(possible_tests) != len(test_files):
- print 'Found unused JSON test data: %s' % ', '.join(filter(lambda x: x not in test_files, possible_tests))
- sys.exit(1)
print 'Ran %d tests, %d failed' % (len(tests), failed)
if failed:
sys.exit(1)
-add_test('test_new_pr.json', {'new_contributor': True},
- {'labels': ['S-awaiting-review'], 'comments': 1})
-
-add_test('test_new_pr.json', {'diff': "+ unsafe fn foo()"},
- {'labels': ['S-awaiting-review'], 'comments': 1})
-
-add_test('test_new_pr.json', {'diff': "diff --git components/layout/"},
- {'labels': ['S-awaiting-review'], 'comments': 1})
-
-add_test('test_new_pr.json', {'diff': "diff --git components/layout/\ndiff --git tests/wpt"},
- {'labels': ['S-awaiting-review'], 'comments': 0})
-
-add_test('test_new_pr.json', {'new_contributor': True},
- {'labels': ['S-awaiting-review'], 'comments': 1})
-
-add_test('test_ignored_action.json', {}, {})
-
-add_test('test_synchronize.json', {'labels': ['S-needs-code-changes', 'S-tests-failed', 'S-awaiting-merge']},
- {'labels': ['S-awaiting-review']})
-
-add_test('test_comment.json', {}, {'assignee': 'jdm'})
-
-add_test('test_merge_approved.json', {'labels': ['S-needs-code-changes', 'S-needs-rebase',
- 'S-tests-failed', 'S-needs-squash',
- 'S-awaiting-review']}, {'labels': ['S-awaiting-merge']})
-
-add_test('test_merge_conflict.json', {'labels': ['S-awaiting-merge']},
- {'labels': ['S-needs-rebase']})
-
-add_test('test_tests_failed.json', {'labels': ['S-awaiting-merge']},
- {'labels': ['S-tests-failed']})
-
-add_test('test_post_retry.json', {'labels': ['S-tests-failed']},
- {'labels': ['S-awaiting-merge']})
-
-add_test('test_post_retry.json', {'labels': ['S-awaiting-merge']},
- {'labels': ['S-awaiting-merge']})
-
-run_tests(tests)
+def setup_tests():
+ import eventhandler
+ (modules, handlers) = eventhandler.get_handlers()
+ tests = []
+ for module, handler in zip(modules, handlers):
+ tests.extend(handler.register_tests(module[1]))
+ return tests
+
+if __name__ == "__main__":
+ tests = setup_tests()
+ run_tests(tests)