github_webhook 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0fbad7d92b15339ae2705eb8fa70f8473ead19de
4
+ data.tar.gz: 7651d1752b27aeecb0be2fcbbe1b40c5844ebe23
5
+ SHA512:
6
+ metadata.gz: ff8ad0cc5bca27dea1c2b8422b451d7b569dac12a78994fd0942c074d2f6107935497ea9eed0dfe0aa2552d10a0317820955d1aa73787186a11f9ac755feff6e
7
+ data.tar.gz: 3edff72e3d0f132112b3358965c1385d58aada7ce51b4352a85708c892e94197f7cca925b50cebb7cef272f6b5cbf5198a9c6cd37519083408a307ef6aaaa51a
@@ -0,0 +1,2 @@
1
+ .DS_Store
2
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in github_webhook.gemspec
4
+ gemspec
@@ -0,0 +1,40 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ github_webhook (0.1.0)
5
+ activesupport (~> 4)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activesupport (4.1.0)
11
+ i18n (~> 0.6, >= 0.6.9)
12
+ json (~> 1.7, >= 1.7.7)
13
+ minitest (~> 5.1)
14
+ thread_safe (~> 0.1)
15
+ tzinfo (~> 1.1)
16
+ diff-lcs (1.2.5)
17
+ i18n (0.6.9)
18
+ json (1.8.1)
19
+ minitest (5.3.3)
20
+ rake (10.3.1)
21
+ rspec (2.14.1)
22
+ rspec-core (~> 2.14.0)
23
+ rspec-expectations (~> 2.14.0)
24
+ rspec-mocks (~> 2.14.0)
25
+ rspec-core (2.14.8)
26
+ rspec-expectations (2.14.5)
27
+ diff-lcs (>= 1.1.3, < 2.0)
28
+ rspec-mocks (2.14.6)
29
+ thread_safe (0.3.3)
30
+ tzinfo (1.1.0)
31
+ thread_safe (~> 0.1)
32
+
33
+ PLATFORMS
34
+ ruby
35
+
36
+ DEPENDENCIES
37
+ bundler (~> 1.5)
38
+ github_webhook!
39
+ rake (~> 10.1)
40
+ rspec (~> 2.14)
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Sebastien Saunier
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,75 @@
1
+ # GithubWebhook
2
+
3
+ This gem will help you to quickly setup a route in your Rails application which listens
4
+ to a [GitHub webhook](https://developer.github.com/webhooks/)
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'github_webhook'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle install
17
+
18
+ ## Configuration
19
+
20
+ First, configure a route to receive the github webhook POST requests.
21
+
22
+ ```ruby
23
+ # config/routes.rb
24
+ resource :github_webhooks, only: :create, defaults: { formats: :json }
25
+ ```
26
+
27
+ Then create a new controller:
28
+
29
+ ```ruby
30
+ # app/controllers/github_webhooks_controller.rb
31
+ class GithubWebhooksController < ActionController::Base
32
+ include GithubWebhook::Processor
33
+ WEBHOOK_SECRET = ENV['GITHUB_WEBHOOK_SECRET']
34
+
35
+ def push(payload)
36
+ # TODO: handle push webhook
37
+ end
38
+ end
39
+ ```
40
+
41
+ Add as many instance methods as events you want to handle in
42
+ your controller. You can read the [full list of events](https://developer.github.com/v3/activity/events/types/) GitHub can notify you about.
43
+
44
+ ## Adding the Webhook to your git repository:
45
+
46
+ First, install [octokit](https://github.com/octokit/octokit.rb), then run a rails console.
47
+
48
+ ```bash
49
+ $ gem install octokit
50
+ $ rails console
51
+ ```
52
+
53
+ In the rails console, add the WebHook to GitHub:
54
+
55
+ ```ruby
56
+ require "octokit"
57
+ client = Octokit::Client.new(:login => 'ssaunier', :password => 's3cr3t!!!')
58
+
59
+ repo = "ssaunier/github_webhook"
60
+ callback_url = "yourdomain.com/github_webhooks"
61
+ webhook_secret = "a_gr34t_s3cr3t" # Must be set after that in ENV['GITHUB_WEBHOOK_SECRET']
62
+
63
+ # Create the WebHook
64
+ client.subscribe "https://github.com/#{repo}/events/push.json", callback_url, secret
65
+ ```
66
+
67
+ The secret is set at the webhook creation. Store it in an environment variable,
68
+ `GITHUB_WEBHOOK_SECRET` as per the example. It is important to have such a secret,
69
+ as it will guarantee that your process legit webhooks requests, thus only from GitHub.
70
+
71
+ You can have an overview of your webhooks at the following URL:
72
+
73
+ ```
74
+ https://github.com/:username/:repo/settings/hooks
75
+ ```
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rspec'
5
+ require 'rspec/core/rake_task'
6
+
7
+ desc "Run all RSpec test examples"
8
+ RSpec::Core::RakeTask.new do |spec|
9
+ spec.rspec_opts = ["-c", "-f progress"]
10
+ spec.pattern = 'spec/**/*_spec.rb'
11
+ end
12
+
13
+ task :default => :spec
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'github_webhook/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "github_webhook"
8
+ spec.version = GithubWebhook::VERSION
9
+ spec.authors = ["Sebastien Saunier"]
10
+ spec.email = ["seb@saunier.me"]
11
+ spec.summary = %q{Rails engine handling Github Webhook}
12
+ spec.description = spec.summary
13
+ spec.homepage = "https://github.com/ssaunier/github_webhook"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "activesupport", "~> 4"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.5"
24
+ spec.add_development_dependency "rake", "~> 10.1"
25
+ spec.add_development_dependency "rspec", "~> 2.14"
26
+ end
@@ -0,0 +1,7 @@
1
+ require 'json'
2
+ require 'openssl'
3
+ require 'active_support/concern'
4
+ require 'active_support/core_ext/hash/indifferent_access'
5
+
6
+ require 'github_webhook/version'
7
+ require 'github_webhook/processor'
@@ -0,0 +1,43 @@
1
+ module GithubWebhook::Processor
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ before_filter :authenticate_github_request!, :only => :create
6
+ end
7
+
8
+ class SignatureError < StandardError; end
9
+ class UnspecifiedWebhookSecretError < StandardError; end
10
+
11
+ def create
12
+ self.send json_body[:action], json_body
13
+ head(:ok)
14
+ end
15
+
16
+ private
17
+
18
+ HMAC_DIGEST = OpenSSL::Digest.new('sha1')
19
+
20
+ def authenticate_github_request!
21
+ raise UnspecifiedWebhookSecretError.new unless defined?(self.class::WEBHOOK_SECRET)
22
+
23
+ expected_signature = "sha1=#{OpenSSL::HMAC.hexdigest(HMAC_DIGEST, self.class::WEBHOOK_SECRET, request_body)}"
24
+ if signature_header != expected_signature
25
+ raise SignatureError.new "Actual: #{signature_header}, Expected: #{expected_signature}"
26
+ end
27
+ end
28
+
29
+ def request_body
30
+ @request_body ||= (
31
+ request.body.rewind
32
+ request.body.read
33
+ )
34
+ end
35
+
36
+ def json_body
37
+ @json_body ||= ActiveSupport::HashWithIndifferentAccess.new(JSON.load(request_body))
38
+ end
39
+
40
+ def signature_header
41
+ @signature_header ||= request.headers['X-Hub-Signature']
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ module GithubWebhook
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ module GithubWebhook
4
+ describe Processor do
5
+
6
+ class Request
7
+ attr_accessor :headers, :body
8
+
9
+ def initialize
10
+ @headers = {}
11
+ @body = StringIO.new
12
+ end
13
+ end
14
+
15
+ class ControllerWithoutSecret
16
+ ### Helpers to mock ActionController::Base behavior
17
+ attr_accessor :request, :pushed
18
+
19
+ def self.skip_before_filter(*args); end
20
+ def self.before_filter(*args); end
21
+ def head(*args); end
22
+ ###
23
+
24
+ include GithubWebhook::Processor
25
+
26
+ def push(payload)
27
+ @pushed = payload[:foo]
28
+ end
29
+ end
30
+
31
+ class Controller < ControllerWithoutSecret
32
+ WEBHOOK_SECRET = "secret"
33
+ end
34
+
35
+ let(:controller) do
36
+ controller = Controller.new
37
+ controller.request = Request.new
38
+ controller
39
+ end
40
+
41
+ let(:controller_without_secret) do
42
+ ControllerWithoutSecret.new
43
+ end
44
+
45
+ describe "#create" do
46
+ it "raises an error when secret is not defined" do
47
+ expect { controller_without_secret.send :authenticate_github_request! }.to raise_error
48
+ end
49
+
50
+ it "calls the #push method in controller" do
51
+ controller.request.body = StringIO.new({ :action => "push", :foo => "bar" }.to_json.to_s)
52
+ controller.request.headers['X-Hub-Signature'] = "sha1=2bceed8940ebc87562f68ee5028db18685ce5607"
53
+ controller.send :authenticate_github_request! # Manually as we don't have the before_filter logic in our Mock object
54
+ controller.create
55
+ controller.pushed.should eq "bar"
56
+ end
57
+
58
+ it "raises an error when signature does not match" do
59
+ controller.request.body = StringIO.new({ :action => "push", :foo => "bar" }.to_json.to_s)
60
+ controller.request.headers['X-Hub-Signature'] = "sha1=FOOBAR"
61
+ expect { controller_without_secret.send :authenticate_github_request! }.to raise_error
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,10 @@
1
+ ENV['RAILS_ENV'] ||= 'test'
2
+
3
+ require "github_webhook"
4
+
5
+ # Load support files
6
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
7
+
8
+ RSpec.configure do |config|
9
+ config.order = "random"
10
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: github_webhook
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sebastien Saunier
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.14'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.14'
69
+ description: Rails engine handling Github Webhook
70
+ email:
71
+ - seb@saunier.me
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - Gemfile
78
+ - Gemfile.lock
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - github_webhook.gemspec
83
+ - lib/github_webhook.rb
84
+ - lib/github_webhook/processor.rb
85
+ - lib/github_webhook/version.rb
86
+ - spec/github_webhook/processor_spec.rb
87
+ - spec/spec_helper.rb
88
+ homepage: https://github.com/ssaunier/github_webhook
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.2.2
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Rails engine handling Github Webhook
112
+ test_files:
113
+ - spec/github_webhook/processor_spec.rb
114
+ - spec/spec_helper.rb