Skip to content

Add Github Enterprise support #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
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
32 changes: 32 additions & 0 deletions lib/cc/services/github_enterprise_pull_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
require_relative 'github_pull_requests'

class CC::Service::GithubEnterprisePullRequest < CC::Service::GitHubPullRequests
class Config < CC::Service::Config
attribute :oauth_token, String,
label: "OAuth Token",
description: "A personal OAuth token with permissions for the repo. The owner of the token will be the author of the pull request update."
attribute :base_url, String,
label: "Base API URL",
description: "The Base URL to your Github Enterprise instance."
attribute :ssl_verification, Boolean,
default: true,
label: "SSL Verification",
description: "Turn this off at your own risk. (Useful for self signed certificates)"

validates :oauth_token, presence: true
validates :base_url, presence: true
end

def setup_http
http.ssl[:verify] = config.ssl_verification
super
end

def base_status_url(commit_sha)
"#{config.base_url}/repos/#{github_slug}/statuses/#{commit_sha}"
end

def user_url
"#{config.base_url}/user"
end
end
82 changes: 12 additions & 70 deletions lib/cc/services/github_pull_requests.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,19 @@ class CC::Service::GitHubPullRequests < CC::Service
class Config < CC::Service::Config
attribute :oauth_token, String,
label: "OAuth Token",
description: "A personal OAuth token with permissions for the repo. The owner of the token will be the author of the pull request comment."
attribute :update_status, Boolean,
label: "Update status?",
description: "Update the pull request status after analyzing?"
attribute :add_comment, Boolean,
label: "Add a comment?",
description: "Comment on the pull request after analyzing?"
description: "A personal OAuth token with permissions for the repo. The owner of the token will be the author of the pull request status."

validates :oauth_token, presence: true
end

class ResponseAggregator
def initialize(status_response, comment_response)
def initialize(status_response)
@status_response = status_response
@comment_response = comment_response
end

def response
return @status_response if @status_response[:ok] && @comment_response[:ok]
message = if !@status_response[:ok] && !@comment_response[:ok]
"Unable to post comment or update status"
elsif !@status_response[:ok]
"Unable to update status: #{@status_response[:message]}"
elsif !@comment_response[:ok]
"Unable to post comment: #{@comment_response[:message]}"
end
{ ok: false, message: message }
error_message ||= "Unable to update status: #{@status_response[:message]}"
@status_response[:ok] ? @status_response : { ok: false, message: error_message}
end
end

Expand All @@ -37,21 +23,13 @@ def response

BASE_URL = "https://api.github.com"
BODY_REGEX = %r{<b>Code Climate</b> has <a href=".*">analyzed this pull request</a>}
COMMENT_BODY = '<img src="https://codeclimate.com/favicon.png" width="20" height="20" />&nbsp;<b>Code Climate</b> has <a href="%s">analyzed this pull request</a>.'

# Just make sure we can access GH using the configured token. Without
# additional information (github-slug, PR number, etc) we can't test much
# else.
def receive_test
setup_http

if config.update_status && config.add_comment
ResponseAggregator.new(receive_test_status, receive_test_comment).response
elsif config.update_status
receive_test_status
elsif config.add_comment
receive_test_comment
end
ResponseAggregator.new(receive_test_status).response
end

def receive_pull_request
Expand All @@ -61,34 +39,21 @@ def receive_pull_request
when "pending"
update_status("pending", "Code Climate is analyzing this code.")
when "success"
add_comment
update_status("success", "Code Climate has analyzed this pull request.")
end
end

private

def update_status(state, description)
if config.update_status
body = {
state: state,
description: description,
target_url: @payload["details_url"],
context: "codeclimate"
}.to_json

http_post(status_url, body)
end
end

def add_comment
if config.add_comment && !comment_present?
body = {
body: COMMENT_BODY % @payload["compare_url"]
}.to_json
body = {
state: state,
description: description,
target_url: @payload["details_url"],
context: "codeclimate"
}.to_json

http_post(comments_url, body)
end
http_post(status_url, body)
end

def receive_test_status
Expand All @@ -104,25 +69,6 @@ def receive_test_status
{ ok: false, message: ex.message }
end

def receive_test_comment
response = http_get(user_url)
if response_includes_repo_scope?(response)
{ ok: true, message: "OAuth token is valid" }
else
{ ok: false, message: "OAuth token requires 'repo' scope to post comments." }
end

rescue => ex
{ ok: false, message: ex.message }
end

def comment_present?
response = http_get(comments_url)
comments = JSON.parse(response.body)

comments.any? { |comment| comment["body"] =~ BODY_REGEX }
end

def setup_http
http.headers["Content-Type"] = "application/json"
http.headers["Authorization"] = "token #{config.oauth_token}"
Expand All @@ -137,10 +83,6 @@ def base_status_url(commit_sha)
"#{BASE_URL}/repos/#{github_slug}/statuses/#{commit_sha}"
end

def comments_url
"#{BASE_URL}/repos/#{github_slug}/issues/#{number}/comments"
end

def user_url
"#{BASE_URL}/user"
end
Expand Down
18 changes: 16 additions & 2 deletions pull_request_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ def call

service = CC::Service::GitHubPullRequests.new({
oauth_token: ENV.fetch("OAUTH_TOKEN"),
update_status: true,
add_comment: true,
}, {
name: "pull_request",
# https://github.com/codeclimate/nillson/pull/33
Expand All @@ -36,3 +34,19 @@ def call
CC::Service::Invocation.new(service) do |i|
i.wrap(WithResponseLogging)
end

ghe_service = CC::Service::GithubEnterprisePullRequest.new({
oauth_token: ENV.fetch("OAUTH_TOKEN"),
base_url: ENV.fetch("GITHUB_BASE_URL"),
ssl_verification: false
}, {
name: "pull_request",
state: "success",
github_slug: ENV.fetch("GITHUB_SLUG"),
number: ENV.fetch("GITHUB_PR_NUMBER"),
commit_sha: ENV.fetch("GITHUB_COMMIT_SHA")
})

CC::Service::Invocation.new(ghe_service) do |i|
i.wrap(WithResponseLogging)
end
88 changes: 88 additions & 0 deletions test/github_enterprise_pull_request_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
require File.expand_path('../helper', __FILE__)

class TestGithubEnterprisePullRequests < CC::Service::TestCase
def test_pull_request_status_pending
expect_status_update("pbrisbin/foo", "abc123", {
"state" => "pending",
"description" => /is analyzing/,
})

receive_pull_request({ update_status: true }, {
github_slug: "pbrisbin/foo",
commit_sha: "abc123",
state: "pending",
})
end

def test_pull_request_status_success
expect_status_update("pbrisbin/foo", "abc123", {
"state" => "success",
"description" => /has analyzed/,
})

receive_pull_request({ update_status: true }, {
github_slug: "pbrisbin/foo",
commit_sha: "abc123",
state: "success",
})
end

def test_pull_request_status_test_success
@stubs.post("/repos/pbrisbin/foo/statuses/#{"0" * 40}") { |env| [422, {}, ""] }

assert receive_test({ update_status: true }, { github_slug: "pbrisbin/foo" })[:ok], "Expected test of pull request to be true"
end

def test_pull_request_status_test_failure
@stubs.post("/repos/pbrisbin/foo/statuses/#{"0" * 40}") { |env| [401, {}, ""] }

assert !receive_test({ update_status: true }, { github_slug: "pbrisbin/foo" })[:ok], "Expected failed test of pull request"
end

def test_response_aggregator_success
response = aggregrate_response({ok: true, message: "OK"},)
assert_equal response, { ok: true, message: "OK" }
end

def test_response_aggregator_failure_status
response = aggregrate_response({ok: false, message: "Bad Token"})
assert !response[:ok], "Expected invalid response because status response is invalid"
assert_match /Bad Token/, response[:message]
end

private

def expect_status_update(repo, commit_sha, params)
@stubs.post "repos/#{repo}/statuses/#{commit_sha}" do |env|
assert_equal "token 123", env[:request_headers]["Authorization"]

body = JSON.parse(env[:body])

params.each do |k, v|
assert v === body[k],
"Unexpected value for #{k}. #{v.inspect} !== #{body[k].inspect}"
end
end
end

def receive_pull_request(config, event_data)
receive(
CC::Service::GithubEnterprisePullRequest,
{ oauth_token: "123", base_url: "http://github.test.com" }.merge(config),
{ name: "pull_request" }.merge(event_data)
)
end

def receive_test(config, event_data = {})
receive(
CC::Service::GithubEnterprisePullRequest,
{ oauth_token: "123", base_url: "http://github.test.com" }.merge(config),
{ name: "test" }.merge(event_data)
)
end

def aggregrate_response(status_response)
CC::Service::GithubEnterprisePullRequest::ResponseAggregator.new(status_response).response
end

end
Loading