hookworm-handlers 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +3 -0
  5. data/.simplecov +6 -0
  6. data/.travis.yml +17 -0
  7. data/.yardopts +1 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE.md +23 -0
  10. data/README.md +29 -0
  11. data/Rakefile +20 -0
  12. data/bin/hookworm-annotator +6 -0
  13. data/bin/hookworm-build-index-handler +6 -0
  14. data/bin/hookworm-logging-handler +6 -0
  15. data/bin/hookworm-rogue-commit-handler +6 -0
  16. data/hookworm-handlers.gemspec +32 -0
  17. data/lib/hookworm/annotator.rb +49 -0
  18. data/lib/hookworm/build_index_handler.rb +132 -0
  19. data/lib/hookworm/github_payload_annotator.rb +110 -0
  20. data/lib/hookworm/handlers/version.rb +5 -0
  21. data/lib/hookworm/handlers.rb +5 -0
  22. data/lib/hookworm/logging_handler.rb +55 -0
  23. data/lib/hookworm/rogue-commit-email-tmpl.erb +153 -0
  24. data/lib/hookworm/rogue_commit_handler.rb +240 -0
  25. data/lib/hookworm/travis_payload_annotator.rb +23 -0
  26. data/lib/hookworm-handlers.rb +7 -0
  27. data/sampledata/github-payloads/bogus.json +4 -0
  28. data/sampledata/github-payloads/branch_delete.json +41 -0
  29. data/sampledata/github-payloads/pull_request.json +153 -0
  30. data/sampledata/github-payloads/rogue.json +152 -0
  31. data/sampledata/github-payloads/rogue_unwatched_branch.json +152 -0
  32. data/sampledata/github-payloads/rogue_unwatched_path.json +91 -0
  33. data/sampledata/github-payloads/valid.json +66 -0
  34. data/sampledata/github-payloads/with_nulls.json +67 -0
  35. data/sampledata/travis-payloads/annotated_success.json +76 -0
  36. data/sampledata/travis-payloads/success.json +74 -0
  37. data/sampledata/travis-payloads/valid.json +56 -0
  38. data/spec/lib/hookworm/annotator_spec.rb +103 -0
  39. data/spec/lib/hookworm/build_index_handler_spec.rb +110 -0
  40. data/spec/lib/hookworm/logging_handler_spec.rb +134 -0
  41. data/spec/lib/hookworm/rogue_commit_handler_spec.rb +65 -0
  42. data/spec/spec_helper.rb +3 -0
  43. data/spec/support.rb +117 -0
  44. metadata +222 -0
@@ -0,0 +1,153 @@
1
+ From: <%= @from %>
2
+ To: <%= @recipients %>
3
+ Subject: [hookworm] Rogue commit by <%= @head_commit_author %> to <%= @repo %> <%= @ref %> (<%= @head_commit_id %>)
4
+ Date: <%= @date %>
5
+ Message-ID: <<%= @message_id %>@<%= @hostname %>>
6
+ List-ID: <%= @repo %> <hookworm.github.com>
7
+ Content-Type: multipart/alternative;
8
+ boundary="--==ZOMGBOUNDARAAAYYYYY";
9
+ charset=UTF-8
10
+ Content-Transfer-Encoding: 7bit
11
+
12
+ ----==ZOMGBOUNDARAAAYYYYY
13
+ Date: <%= @date %>
14
+ Mime-Version: 1.0
15
+ Content-Type: text/plain; charset=utf8
16
+ Content-Transfer-Encoding: 7bit
17
+
18
+ Rogue commit detected!
19
+
20
+ Repo <%= @repo %>
21
+ Ref <%= @ref %>
22
+ ID <%= @head_commit_id %>
23
+ Author <%= @head_commit_author %>
24
+ Committer <%= @head_commit_committer %>
25
+ Timestamp <%= @head_commit_timestamp %>
26
+ Message <%= @head_commit_message_text %>
27
+
28
+ --
29
+ This email was sent by hookworm: https://github.com/modcloth-labs/hookworm
30
+
31
+ A rogue commit is a commit made directly to a branch that is being watched such
32
+ that only pull requests should be merged into them.
33
+
34
+ The configured watched branches are:
35
+
36
+ <% (@watched_branches || []).each do |branch| %> - <%= branch %>
37
+ <% end %>
38
+
39
+ The configured watched paths are:
40
+
41
+ <% (@watched_paths || []).each do |path| %> - <%= path %>
42
+ <% end %>
43
+
44
+
45
+ If you believe this rogue commit email is an error, you should hunt down the
46
+ party responsible for the hookworm instance registered as a WebHook URL in this
47
+ repo's service hook settings (<%= @repo_url %>/settings/hooks).
48
+
49
+ Pretty please submit issues specific to hookworm functionality on github:
50
+ https://github.com/modcloth-labs/hookworm/issues/new
51
+
52
+ ----==ZOMGBOUNDARAAAYYYYY
53
+ Date: <%= @date %>
54
+ Mime-Version: 1.0
55
+ Content-Type: text/html; charset=utf8
56
+ Content-Transfer-Encoding: 7bit
57
+
58
+ <div>
59
+ <h1><a href="<%= @head_commit_url %>">Rogue commit detected!</a></h1>
60
+
61
+ <table>
62
+ <thead><th></th><th></th></thead>
63
+ <tbody>
64
+ <tr>
65
+ <td style="text-align:right;vertical-align:top;">
66
+ <strong>Repo</strong>:
67
+ </td>
68
+ <td><%= @repo %></td>
69
+ </tr>
70
+ <tr>
71
+ <td style="text-align:right;vertical-align:top;">
72
+ <strong>Ref</strong>:
73
+ </td>
74
+ <td><%= @ref %></td>
75
+ </tr>
76
+ <tr>
77
+ <td style="text-align:right;vertical-align:top;">
78
+ <strong>ID</strong>:
79
+ </td>
80
+ <td><a href="<%= @head_commit_url %>"><%= @head_commit_id %></a></td>
81
+ </tr>
82
+ <tr>
83
+ <td style="text-align:right;vertical-align:top;">
84
+ <strong>Author</strong>:
85
+ </td>
86
+ <td><%= @head_commit_author %></td>
87
+ </tr>
88
+ <tr>
89
+ <td style="text-align:right;vertical-align:top;">
90
+ <strong>Committer</strong>:
91
+ </td>
92
+ <td><%= @head_commit_committer %></td>
93
+ </tr>
94
+ <tr>
95
+ <td style="text-align:right;vertical-align:top;">
96
+ <strong>Timestamp</strong>:
97
+ </td>
98
+ <td><%= @head_commit_timestamp %></td>
99
+ </tr>
100
+ <tr>
101
+ <td style="text-align:right;vertical-align:top;">
102
+ <strong>Message</strong>:
103
+ </td>
104
+ <td><%= @head_commit_message_html %></td>
105
+ </tr>
106
+ </tbody>
107
+ </table>
108
+ </div>
109
+
110
+ <hr/>
111
+ <div style="font-size:.8em">
112
+ <p>
113
+ This email was sent by
114
+ <a href="https://github.com/modcloth-labs/hookworm">hookworm</a>.
115
+ </p>
116
+
117
+ <p>
118
+ A rogue commit is a commit made directly to a branch that is being watched such
119
+ that only pull requests should be merged into them.
120
+ </p>
121
+
122
+ <p>
123
+ The configured watched branches are:
124
+ </p>
125
+
126
+ <ul>
127
+ <% (@watched_branches || []).each do |branch| %><li><strong><%= branch %></strong></li>
128
+ <% end %>
129
+ </ul>
130
+
131
+ <p>
132
+ The configured watched paths are:
133
+ </p>
134
+
135
+ <ul>
136
+ <% (@watched_paths || []).each do |path| %><li><strong><%= path %></strong></li>
137
+ <% end %>
138
+ </ul>
139
+
140
+ <p>
141
+ If you believe this rogue commit email is an error, you should hunt down the
142
+ party responsible for the hookworm instance registered as a WebHook URL in
143
+ this repo's <a href="<%= @repo_url %>/settings/hooks">service hook settings</a>.
144
+ </p>
145
+
146
+ <p>
147
+ Pretty please submit issues specific to hookworm functionality
148
+ <a href="https://github.com/modcloth-labs/hookworm/issues/new">on github</a>
149
+ </p>
150
+
151
+ </div>
152
+
153
+ ----==ZOMGBOUNDARAAAYYYYY--
@@ -0,0 +1,240 @@
1
+ # vim:fileencoding=utf-8
2
+
3
+ require 'erb'
4
+ require 'json'
5
+ require 'net/smtp'
6
+ require 'time'
7
+ require 'uri'
8
+ require 'hookworm-base'
9
+ require 'hookworm/emailer'
10
+
11
+ module Hookworm
12
+ # # Hookworm Rogue Commit Handler
13
+ #
14
+ # The rogue commit handler is specific to GitHub payloads. It will inspect a
15
+ # payload in the context of the given `watched_branches` and `watched_paths`
16
+ # and send a "rogue commit email" to the email recipients given in
17
+ # `email_recipients` to provide visibility roughly equivalent to those
18
+ # commits that result from pull request merges.
19
+ #
20
+ # Because the rogue commit handler is affected by so many arguments, here
21
+ # they are again with more details about their associated behavior:
22
+ #
23
+ # ## `watched_branches`
24
+ # The `watched_branches` argument should be a comma-delimited list of regular
25
+ # expressions, e.g.: `watched_branches='^master$,^release_[0-9]'`. If a
26
+ # commit payload is received that was not the result of a pull request merge
27
+ # and the Hookworm Annotator handler has determined that the branch name
28
+ # matches any of the entries in `watched_branches`, then a rogue commit email
29
+ # will be sent.
30
+ #
31
+ # ## `watched_paths`
32
+ # The `watched_paths` argument should be a comma-delimited list of regular
33
+ # expressions, e.g.: `watched_paths='.*\.(go|rb|py)$,bin/.*'`. If a commit
34
+ # payload is received that was not the result of a pull request merge and the
35
+ # Hookworm Annotator handler has determined that one of the commits in the
36
+ # payload contains a path matching any of the entries in `watched_paths`,
37
+ # then a rogue commit email will be sent.
38
+ #
39
+ # ## `email_from_addr`
40
+ # The `email_from_addr` is the email address used as the `From` header and
41
+ # SMTP MAIL address when sending rogue commit emails, e.g.:
42
+ # `email_from_addr='hookworm-noreply@company.example.com'`.
43
+ #
44
+ # ## `email_recipients`
45
+ # The `email_recipients` argument should be a comma-delimited list of email
46
+ # addresses (without display name) used in the `To` header and SMTP RCPT
47
+ # addresses when sending rogue commit emails, e.g.:
48
+ # `email_recipients='devs@example.com,proj+hookworm@partner.example.net'`
49
+ #
50
+ # ## `email_uri`
51
+ # The `email_uri` argument should be a well-formed URI containing the SMTP
52
+ # hostname and port and potentially the username and password used for plain
53
+ # SMTP auth, e.g.:
54
+ # `email_uri='smtp://hookworm:secret@mailhost.example.com:1587'`
55
+ class RogueCommitHandler
56
+ include Hookworm::Base
57
+
58
+ private
59
+
60
+ def handle_github
61
+ payload = JSON.parse(input_stream.read, symbolize_names: true)
62
+ begin
63
+ Judger.new(cfg).judge_payload(payload)
64
+ rescue StandardError
65
+ return 1
66
+ ensure
67
+ output_stream.puts JSON.pretty_generate(payload)
68
+ end
69
+ 0
70
+ end
71
+
72
+ def handle_travis
73
+ 78
74
+ end
75
+ end
76
+
77
+ class Judger
78
+ attr_reader :cfg
79
+
80
+ def initialize(cfg)
81
+ @cfg = cfg
82
+ end
83
+
84
+ def judge_payload(payload)
85
+ return unless watched_branch?(payload)
86
+ hcid = payload[:head_commit][:id]
87
+ return if pr_merge?(payload, hcid)
88
+ return unless watched_path?(payload, hcid)
89
+ safe_send_rogue_commit_email!(payload)
90
+ end
91
+
92
+ def new_judge_payload(payload)
93
+ return unless watched_branch?(payload)
94
+ hcid = payload[:head_commit][:id]
95
+ return if pr_merge?(payload, hcid)
96
+ return unless watched_path?(payload, hcid)
97
+ safe_send_rogue_commit_email!(payload)
98
+ end
99
+
100
+ def watched_branch?(payload)
101
+ unless payload[:is_watched_branch]
102
+ log.debug { "#{payload[:ref]} is not a watched branch, yay!" }
103
+ return false
104
+ end
105
+
106
+ log.debug { "#{payload[:ref]} is a watched branch!" }
107
+ true
108
+ end
109
+
110
+ def pr_merge?(payload, hcid)
111
+ if payload[:is_pr_merge]
112
+ log.info { "#{hcid} is a pull request merge, yay!" }
113
+ return true
114
+ end
115
+
116
+ log.debug { "#{hcid} is not a pull request merge!" }
117
+ false
118
+ end
119
+
120
+ def watched_path?(payload, hcid)
121
+ unless payload[:has_watched_path]
122
+ log.debug { "#{hcid} does not contain watched paths, yay!" }
123
+ return false
124
+ end
125
+
126
+ log.debug { "#{hcid} contains watched paths!" }
127
+ true
128
+ end
129
+
130
+ def safe_send_rogue_commit_email!(payload)
131
+ send_rogue_commit_email!(payload)
132
+ log.debug { "Sent rogue commit email to #{recipients}" }
133
+ rescue => e
134
+ log.error { "#{e.class.name} #{e.message}" }
135
+ log.debug { e.backtrace.join("\n") }
136
+ raise e
137
+ end
138
+
139
+ def send_rogue_commit_email!(payload)
140
+ log_rogue_commit(payload)
141
+
142
+ if recipients.empty?
143
+ log.warn { 'No email recipients specified, so no emailing!' }
144
+ return
145
+ end
146
+
147
+ email_body = render_email(payload)
148
+ log.debug { "Email message:\n#{email_body}" }
149
+
150
+ emailer.send(fromaddr, recipients, email_body)
151
+ end
152
+
153
+ def render_email(payload)
154
+ EmailRenderContext.new(self, payload, rogue_commit_email_tmpl).render
155
+ end
156
+
157
+ def rogue_commit_email_tmpl
158
+ ERB.new(rogue_commit_email_tmpl_string)
159
+ end
160
+
161
+ def rogue_commit_email_tmpl_string
162
+ File.read(File.expand_path('../rogue-commit-email-tmpl.erb', __FILE__))
163
+ end
164
+
165
+ def recipients
166
+ @recipients ||= (cfg[:worm_flags][:email_recipients] || '').commasplit
167
+ end
168
+
169
+ def fromaddr
170
+ @fromaddr ||= cfg[:worm_flags][:email_from_addr]
171
+ end
172
+
173
+ def watched_branches
174
+ @watched_branches ||= (
175
+ cfg[:worm_flags][:watched_branches] || ''
176
+ ).commasplit
177
+ end
178
+
179
+ def watched_paths
180
+ @watched_paths ||= (cfg[:worm_flags][:watched_paths] || '').commasplit
181
+ end
182
+
183
+ def emailer
184
+ @emailer ||= Hookworm::Emailer.new(cfg[:worm_flags][:email_uri])
185
+ end
186
+
187
+ def log_rogue_commit(payload)
188
+ log.warn do
189
+ "WARNING rogue commit! #{payload}, " <<
190
+ "head commit: #{payload[:head_commit]}"
191
+ end
192
+ end
193
+
194
+ def log
195
+ @log ||= Logger.new($stderr)
196
+ end
197
+
198
+ class EmailRenderContext
199
+ def initialize(judger, payload, tmpl)
200
+ assign_judger_vars(judger)
201
+ assign_payload_vars(payload)
202
+ @tmpl = tmpl
203
+ @date = Time.now.utc.rfc2822
204
+ @message_id = Time.now.strftime('%s%9N')
205
+ @hostname = Socket.gethostname
206
+ assign_head_commit_vars(payload[:head_commit])
207
+ end
208
+
209
+ def assign_judger_vars(judger)
210
+ @cfg = judger.cfg
211
+ @from = judger.fromaddr
212
+ @recipients = judger.recipients.join(', ')
213
+ @watched_branches = judger.watched_branches
214
+ @watched_paths = judger.watched_paths
215
+ end
216
+
217
+ def assign_payload_vars(payload)
218
+ @payload = payload
219
+ @repo = "#{payload[:repository][:owner][:name]}/" <<
220
+ "#{payload[:repository][:name]}"
221
+ @ref = payload[:ref]
222
+ @repo_url = payload[:repository][:url]
223
+ end
224
+
225
+ def assign_head_commit_vars(hc)
226
+ @head_commit_id = hc[:id]
227
+ @head_commit_url = hc[:url]
228
+ @head_commit_author = hc[:author][:name]
229
+ @head_commit_committer = hc[:committer][:name]
230
+ @head_commit_message_text = hc[:message].to_plaintext
231
+ @head_commit_message_html = hc[:message].to_html
232
+ @head_commit_timestamp = hc[:timestamp]
233
+ end
234
+
235
+ def render
236
+ @tmpl.result(binding)
237
+ end
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,23 @@
1
+ module Hookworm
2
+ class TravisPayloadAnnotator
3
+ def annotate(payload)
4
+ annotated = payload.clone
5
+ add_repo_slug!(annotated)
6
+ annotated[:success] = success?(payload)
7
+ annotated
8
+ end
9
+
10
+ private
11
+
12
+ def add_repo_slug!(payload)
13
+ repo = (payload[:repository] || {})
14
+ if repo[:owner_name] && repo[:name]
15
+ repo[:slug] = "#{repo[:owner_name]}/#{repo[:name]}"
16
+ end
17
+ end
18
+
19
+ def success?(payload)
20
+ payload[:result] == 0 && payload[:result_message].downcase == 'passed'
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,7 @@
1
+ module Hookworm
2
+ autoload :Annotator, 'hookworm/annotator'
3
+ autoload :BuildIndexHandler, 'hookworm/build_index_handler'
4
+ autoload :Handlers, 'hookworm/handlers'
5
+ autoload :LoggingHandler, 'hookworm/logging_handler'
6
+ autoload :RogueCommitHandler, 'hookworm/rogue_commit_handler'
7
+ end
@@ -0,0 +1,4 @@
1
+ {
2
+ "fizz": "buzz",
3
+ "bonk": 9000
4
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "ref": "refs/heads/ensuring-watched-branches-and-paths-are-quote-free",
3
+ "after": "0000000000000000000000000000000000000000",
4
+ "before": "a1fcfc8fe4017bccb4f156ac5a786c08a6968517",
5
+ "created": false,
6
+ "deleted": true,
7
+ "forced": true,
8
+ "compare": "https://github.com/modcloth-labs/hookworm/compare/a1fcfc8fe401...000000000000",
9
+ "commits": [],
10
+ "head_commit": null,
11
+ "repository": {
12
+ "id": 10417548,
13
+ "name": "hookworm",
14
+ "url": "https://github.com/modcloth-labs/hookworm",
15
+ "description": "GitHub & Travis hook receiving thingydoo",
16
+ "homepage": "",
17
+ "watchers": 2,
18
+ "stargazers": 2,
19
+ "forks": 2,
20
+ "fork": false,
21
+ "size": 612,
22
+ "owner": {
23
+ "name": "modcloth-labs",
24
+ "email": "labs@modcloth.com"
25
+ },
26
+ "private": false,
27
+ "open_issues": 0,
28
+ "has_issues": true,
29
+ "has_downloads": true,
30
+ "has_wiki": true,
31
+ "language": "Ruby",
32
+ "created_at": 1370060266,
33
+ "pushed_at": 1378653604,
34
+ "master_branch": "master",
35
+ "organization": "modcloth-labs"
36
+ },
37
+ "pusher": {
38
+ "name": "meatballhat",
39
+ "email": "dan@meatballhat.com"
40
+ }
41
+ }
@@ -0,0 +1,153 @@
1
+ {
2
+ "ref": "refs/heads/master",
3
+ "after": "d0afb08e931361f179a7e0fd29daec7ca1663233",
4
+ "before": "926ed297f149354424c1968924bf944c5b69a7ec",
5
+ "created": false,
6
+ "deleted": false,
7
+ "forced": false,
8
+ "compare": "https://github.com/modcloth-labs/hookworm/compare/926ed297f149...d0afb08e9313",
9
+ "commits": [
10
+ {
11
+ "id": "7afdcae33a1903e52d0290e738b868d944ce3f60",
12
+ "distinct": true,
13
+ "message": "Merge pull request #1 from modcloth-labs/payload-parsing-and-emailing\n\nPayload parsing and emaœling",
14
+ "timestamp": "2013-06-01T19:28:39-07:00",
15
+ "url": "https://github.com/modcloth-labs/hookworm/commit/7afdcae33a1903e52d0290e738b868d944ce3f60",
16
+ "author": {
17
+ "name": "Dan Buch",
18
+ "email": "daniel.buch@gmail.com",
19
+ "username": "meatballhat"
20
+ },
21
+ "committer": {
22
+ "name": "Dan Buch",
23
+ "email": "daniel.buch@gmail.com",
24
+ "username": "meatballhat"
25
+ },
26
+ "added": [
27
+ "emailer.go",
28
+ "example-payload.json",
29
+ "payload.go"
30
+ ],
31
+ "removed": [
32
+
33
+ ],
34
+ "modified": [
35
+ "Makefile",
36
+ "server.go"
37
+ ]
38
+ },
39
+ {
40
+ "id": "323fa5bebc49cbeac869a242682a56c83d4566cc",
41
+ "distinct": true,
42
+ "message": "Handling JSON nulls all over the place\n\nwhich I may end up reverting in favor of the {}interface + type\nassertion approach.",
43
+ "timestamp": "2013-06-02T09:42:59-07:00",
44
+ "url": "https://github.com/modcloth-labs/hookworm/commit/323fa5bebc49cbeac869a242682a56c83d4566cc",
45
+ "author": {
46
+ "name": "Dan Buch",
47
+ "email": "d.buch@modcloth.com",
48
+ "username": "meatballhat"
49
+ },
50
+ "committer": {
51
+ "name": "Dan Buch",
52
+ "email": "d.buch@modcloth.com",
53
+ "username": "meatballhat"
54
+ },
55
+ "added": [
56
+ "sampledata/payload-with-nulls.json",
57
+ "sampledata/payload.json"
58
+ ],
59
+ "removed": [
60
+ "example-payload.json"
61
+ ],
62
+ "modified": [
63
+ "payload.go",
64
+ "server.go"
65
+ ]
66
+ },
67
+ {
68
+ "id": "d0afb08e931361f179a7e0fd29daec7ca1663233",
69
+ "distinct": true,
70
+ "message": "Merge pull request #2 from modcloth-labs/payload-parsing-and-emailing\n\nHandling JSON nulls all over the place",
71
+ "timestamp": "2013-06-02T09:47:08-07:00",
72
+ "url": "https://github.com/modcloth-labs/hookworm/commit/d0afb08e931361f179a7e0fd29daec7ca1663233",
73
+ "author": {
74
+ "name": "Dan Buch",
75
+ "email": "daniel.buch@gmail.com",
76
+ "username": "meatballhat"
77
+ },
78
+ "committer": {
79
+ "name": "Dan Buch",
80
+ "email": "daniel.buch@gmail.com",
81
+ "username": "meatballhat"
82
+ },
83
+ "added": [
84
+ "sampledata/payload-with-nulls.json",
85
+ "sampledata/payload.json"
86
+ ],
87
+ "removed": [
88
+ "example-payload.json"
89
+ ],
90
+ "modified": [
91
+ "payload.go",
92
+ "server.go"
93
+ ]
94
+ }
95
+ ],
96
+ "head_commit": {
97
+ "id": "d0afb08e931361f179a7e0fd29daec7ca1663233",
98
+ "distinct": true,
99
+ "message": "Merge pull request #2 from modcloth-labs/payload-parsing-and-emailing\n\nHandling JSON nulls all over the place",
100
+ "timestamp": "2013-06-02T09:47:08-07:00",
101
+ "url": "https://github.com/modcloth-labs/hookworm/commit/d0afb08e931361f179a7e0fd29daec7ca1663233",
102
+ "author": {
103
+ "name": "Dan Buch",
104
+ "email": "daniel.buch@gmail.com",
105
+ "username": "meatballhat"
106
+ },
107
+ "committer": {
108
+ "name": "Dan Buch",
109
+ "email": "daniel.buch@gmail.com",
110
+ "username": "meatballhat"
111
+ },
112
+ "added": [
113
+ "sampledata/payload-with-nulls.json",
114
+ "sampledata/payload.json"
115
+ ],
116
+ "removed": [
117
+ "example-payload.json"
118
+ ],
119
+ "modified": [
120
+ "payload.go",
121
+ "server.go"
122
+ ]
123
+ },
124
+ "repository": {
125
+ "id": 10417548,
126
+ "name": "hookworm",
127
+ "url": "https://github.com/modcloth-labs/hookworm",
128
+ "description": "github hook receiving thingydoo",
129
+ "watchers": 0,
130
+ "stargazers": 0,
131
+ "forks": 0,
132
+ "fork": false,
133
+ "size": 208,
134
+ "owner": {
135
+ "name": "modcloth-labs",
136
+ "email": null
137
+ },
138
+ "private": false,
139
+ "open_issues": 0,
140
+ "has_issues": true,
141
+ "has_downloads": true,
142
+ "has_wiki": true,
143
+ "language": "Go",
144
+ "created_at": 1370060266,
145
+ "pushed_at": 1370191630,
146
+ "master_branch": "master",
147
+ "organization": "modcloth-labs"
148
+ },
149
+ "pusher": {
150
+ "name": "none"
151
+ },
152
+ "is_pr_merge": true
153
+ }