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

Allow github to send form-urlencoded payloads and bug fix in webrick_opts #45

Merged
merged 3 commits into from
Feb 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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