Skip to content
This repository has been archived by the owner on Jan 29, 2022. It is now read-only.

Commit

Permalink
Merge pull request #45 from williamberman/master
Browse files Browse the repository at this point in the history
Allow github to send form-urlencoded payloads and bug fix in webrick_opts
  • Loading branch information
dhollinger authored Feb 12, 2018
2 parents 178cf03 + 7c1f21d commit 7741957
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 109 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ logs/*.log
.idea/
coverage
Gemfile.lock
.byebug_history
1 change: 0 additions & 1 deletion bin/puppet_webhook
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ webrick_opts = {
Port: options[:port],
DocumentRoot: approot,
ServerType: options[:server_type],
ServerSoftware: PuppetWebhook,
StartCallBack: proc { File.open(options[:pidfile], 'w') { |f| f.write Process.pid } }
}

Expand Down
2 changes: 1 addition & 1 deletion lib/helpers/deployments.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def deploy(branch, deleted)
generate_types(branch) if types?
end
notify_slack(status_message) if slack?
status_message.to_json
[status_message[:status_code], status_message.to_json]
rescue StandardError => e
status_message = { status: :fail, message: e.message, trace: e.backtrace, branch: branch, status_code: 500 }
LOGGER.error("message: #{e.message} trace: #{e.backtrace}")
Expand Down
110 changes: 5 additions & 105 deletions lib/parsers/webhook_json_parser.rb
Original file line number Diff line number Diff line change
@@ -1,115 +1,15 @@
require 'rack/bodyparser'
require 'json'

require_relative 'webhook_parser'

module Sinatra
module Parsers
class WebhookJsonParser # rubocop:disable Style/Documentation
attr_accessor :env, :data

def call(body)
@data = JSON.parse(body, quirks_mode: true)
@vcs = detect_vcs
{
branch: branch,
deleted: deleted?,
module_name: repo_name.sub(%r{^.*-}, ''),
repo_name: repo_name,
repo_user: repo_user
}.delete_if { |_k, v| v.nil? }
end

def detect_vcs
return 'github' if github_webhook?
return 'gitlab' if gitlab_webhook?
return 'stash' if stash_webhook?
return 'bitbucket' if bitbucket_webhook?
return 'tfs' if tfs_webhook?
raise StandardError, 'payload not recognised'
end

def github_webhook?
# https://developer.github.com/v3/activity/events/types/#pushevent
env.key?('HTTP_X_GITHUB_EVENT')
end

def gitlab_webhook?
# https://docs.gitlab.com/ce/user/project/integrations/webhooks.html
env.key?('HTTP_X_GITLAB_EVENT')
end

# stash/bitbucket server
def stash_webhook?
# https://confluence.atlassian.com/bitbucketserver/post-service-webhook-for-bitbucket-server-776640367.html
env.key?('HTTP_X_ATLASSIAN_TOKEN')
end

def bitbucket_webhook?
# https://confluence.atlassian.com/bitbucket/event-payloads-740262817.html
env.key?('HTTP_X_EVENT_KEY')
end

def tfs_webhook?
# https://docs.microsoft.com/en-us/vsts/service-hooks/services/webhooks
return false unless @data.key? 'resource'
return false unless @data.key? 'eventType'
true
end

def branch
case @vcs
when 'github'
if @data.key? 'ref'
@data['ref'].sub('refs/heads/', '')
else
@data['repository']['default_branch']
end
when 'gitlab'
@data['ref'].sub('refs/heads/', '')
when 'stash'
@data['refChanges'][0]['refId'].sub('refs/heads/', '')
when 'bitbucket'
return @data['push']['changes'][0]['new']['name'] unless deleted?
@data['push']['changes'][0]['old']['name']
when 'tfs'
@data['resource']['refUpdates'][0]['name'].sub('refs/heads/', '')
end
end

def deleted?
case @vcs
when 'github'
@data['deleted']
when 'gitlab'
@data['after'] == '0000000000000000000000000000000000000000'
when 'stash'
@data['refChanges'][0]['type'] == 'DELETE'
when 'bitbucket'
@data['push']['changes'][0]['closed']
when 'tfs'
@data['resource']['refUpdates'][0]['newObjectId'] == '0000000000000000000000000000000000000000'
end
end

def repo_name
if @vcs == 'gitlab'
@data['project']['name']
elsif @vcs == 'tfs'
@data['resource']['repository']['name']
else
@data['repository']['name']
end
end
include Sinatra::Parsers::WebhookParser

def repo_user
# TODO: Clarify what repo_user actually is.
# github is currently using the repo's 'owner', gitlab is using the user who pushed.
case @vcs
when 'github'
@data['repository']['owner']['login']
when 'gitlab'
@data['user_username']
end
# TODO: Bitbucket, Stash/Bitbucket Server, TFS
def parse_data(body)
JSON.parse(body, quirks_mode: true)
end
end
end
Expand Down
116 changes: 116 additions & 0 deletions lib/parsers/webhook_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
require 'rack/bodyparser'
require 'json'

module Sinatra
module Parsers
module WebhookParser # rubocop:disable Style/Documentation
attr_accessor :env, :data

def call(body)
@data = parse_data(body)
@vcs = detect_vcs
{
branch: branch,
deleted: deleted?,
module_name: repo_name.sub(%r{^.*-}, ''),
repo_name: repo_name,
repo_user: repo_user
}.delete_if { |_k, v| v.nil? }
end

def detect_vcs
return 'github' if github_webhook?
return 'gitlab' if gitlab_webhook?
return 'stash' if stash_webhook?
return 'bitbucket' if bitbucket_webhook?
return 'tfs' if tfs_webhook?
raise StandardError, 'payload not recognised'
end

def github_webhook?
# https://developer.github.com/v3/activity/events/types/#pushevent
env.key?('HTTP_X_GITHUB_EVENT')
end

def gitlab_webhook?
# https://docs.gitlab.com/ce/user/project/integrations/webhooks.html
env.key?('HTTP_X_GITLAB_EVENT')
end

# stash/bitbucket server
def stash_webhook?
# https://confluence.atlassian.com/bitbucketserver/post-service-webhook-for-bitbucket-server-776640367.html
env.key?('HTTP_X_ATLASSIAN_TOKEN')
end

def bitbucket_webhook?
# https://confluence.atlassian.com/bitbucket/event-payloads-740262817.html
env.key?('HTTP_X_EVENT_KEY')
end

def tfs_webhook?
# https://docs.microsoft.com/en-us/vsts/service-hooks/services/webhooks
return false unless @data.key? 'resource'
return false unless @data.key? 'eventType'
true
end

def branch
case @vcs
when 'github'
if @data.key? 'ref'
@data['ref'].sub('refs/heads/', '')
else
@data['repository']['default_branch']
end
when 'gitlab'
@data['ref'].sub('refs/heads/', '')
when 'stash'
@data['refChanges'][0]['refId'].sub('refs/heads/', '')
when 'bitbucket'
return @data['push']['changes'][0]['new']['name'] unless deleted?
@data['push']['changes'][0]['old']['name']
when 'tfs'
@data['resource']['refUpdates'][0]['name'].sub('refs/heads/', '')
end
end

def deleted?
case @vcs
when 'github'
@data['deleted']
when 'gitlab'
@data['after'] == '0000000000000000000000000000000000000000'
when 'stash'
@data['refChanges'][0]['type'] == 'DELETE'
when 'bitbucket'
@data['push']['changes'][0]['closed']
when 'tfs'
@data['resource']['refUpdates'][0]['newObjectId'] == '0000000000000000000000000000000000000000'
end
end

def repo_name
if @vcs == 'gitlab'
@data['project']['name']
elsif @vcs == 'tfs'
@data['resource']['repository']['name']
else
@data['repository']['name']
end
end

def repo_user
# TODO: Clarify what repo_user actually is.
# github is currently using the repo's 'owner', gitlab is using the user who pushed.
case @vcs
when 'github'
@data['repository']['owner']['login']
when 'gitlab'
@data['user_username']
end
# TODO: Bitbucket, Stash/Bitbucket Server, TFS
end
end
end
end
16 changes: 16 additions & 0 deletions lib/parsers/webhook_www_form_url_encoded_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
require 'rack/bodyparser'
require 'json'

require_relative 'webhook_parser'

module Sinatra
module Parsers
class WebhookWWWFormURLEncodedParser # rubocop:disable Style/Documentation
include Sinatra::Parsers::WebhookParser

def parse_data(body)
JSON.parse(CGI.unescape(body).gsub(%r{^payload\=}, ''))
end
end
end
end
12 changes: 10 additions & 2 deletions lib/puppet_webhook.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'json'
require 'cgi'
require 'parsers/webhook_json_parser'
require 'parsers/webhook_www_form_url_encoded_parser'

# Routes
require_relative 'routes/default'
Expand All @@ -12,8 +13,15 @@
class PuppetWebhook < Sinatra::Base # rubocop:disable Style/Documentation
set :root, File.dirname(__FILE__)
use Rack::BodyParser,
parsers: { 'application/json' => Sinatra::Parsers::WebhookJsonParser.new },
handlers: { 'application/json' => proc { |e, type| [400, { 'Content-Type' => type }, [{ error: e.to_s }.to_json]] } }
parsers: {
'application/json' => Sinatra::Parsers::WebhookJsonParser.new,
'application/x-www-form-urlencoded' => Sinatra::Parsers::WebhookWWWFormURLEncodedParser.new
},
handlers: {
'application/json' => proc { |e, type|
[400, { 'Content-Type' => type }, [{ error: e.to_s }.to_json]]
}
}
register Sinatra::ConfigFile

config_file(File.join(__dir__, '..', 'config', 'app.yml'), '/etc/puppet_webhook/app.yml')
Expand Down

0 comments on commit 7741957

Please sign in to comment.