parent
97fef59c82
commit
b796f9a6dd
58
lib/github_check_run_service.rb
Normal file
58
lib/github_check_run_service.rb
Normal file
@ -0,0 +1,58 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class GithubCheckRunService
|
||||
CHECK_NAME = 'Rubocop'
|
||||
|
||||
def initialize(report, github_data, report_adapter)
|
||||
@report = report
|
||||
@github_data = github_data
|
||||
@report_adapter = report_adapter
|
||||
@client = GithubClient.new(@github_data[:token], user_agent: 'rubocop-action')
|
||||
end
|
||||
|
||||
def run
|
||||
id = @client.post(
|
||||
endpoint_url,
|
||||
create_check_payload
|
||||
)['id']
|
||||
@summary = @report_adapter.summary(@report)
|
||||
@annotations = @report_adapter.annotations(@report)
|
||||
@conclusion = @report_adapter.conslusion(@report)
|
||||
|
||||
@client.patch(
|
||||
"#{endpoint_url}/#{id}",
|
||||
update_check_payload
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def endpoint_url
|
||||
"/repos/#{@github_data[:owner]}/#{@github_data[:repo]}/check-runs"
|
||||
end
|
||||
|
||||
def create_check_payload
|
||||
{
|
||||
name: CHECK_NAME,
|
||||
head_sha: @github_data[:sha],
|
||||
status: 'in_progress',
|
||||
started_at: Time.now.iso8601
|
||||
}
|
||||
end
|
||||
|
||||
def update_check_payload
|
||||
{
|
||||
name: CHECK_NAME,
|
||||
head_sha: @github_data[:sha],
|
||||
status: 'completed',
|
||||
completed_at: Time.now.iso8601,
|
||||
conclusion: @conclusion,
|
||||
output: {
|
||||
title: CHECK_NAME,
|
||||
summary: @summary,
|
||||
annotations: @annotations
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
40
lib/github_client.rb
Normal file
40
lib/github_client.rb
Normal file
@ -0,0 +1,40 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class GithubClient
|
||||
def initialize(github_token, user_agent: 'ruby')
|
||||
@github_token = github_token
|
||||
@user_agent = user_agent
|
||||
end
|
||||
|
||||
def patch(url, body = {})
|
||||
request_http do |http|
|
||||
http.patch(url, body.to_json, headers)
|
||||
end
|
||||
end
|
||||
|
||||
def post(url, body = {})
|
||||
request_http do |http|
|
||||
http.post(url, body.to_json, headers)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def headers
|
||||
@headers ||= {
|
||||
"Content-Type": 'application/json',
|
||||
"Accept": 'application/vnd.github.antiope-preview+json',
|
||||
"Authorization": "Bearer #{@github_token}",
|
||||
"User-Agent": @user_agent
|
||||
}
|
||||
end
|
||||
|
||||
def request_http
|
||||
http = Net::HTTP.new('api.github.com', 443)
|
||||
http.use_ssl = true
|
||||
response = yield(http)
|
||||
raise "#{response.message}: #{response.body}" if response.code.to_i >= 300
|
||||
|
||||
JSON.parse(response.body)
|
||||
end
|
||||
end
|
||||
145
lib/index.rb
145
lib/index.rb
@ -1,134 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'net/http'
|
||||
require 'json'
|
||||
require 'time'
|
||||
require_relative './report_adapter'
|
||||
require_relative './github_check_run_service'
|
||||
require_relative './github_client'
|
||||
|
||||
@GITHUB_SHA = ENV["GITHUB_SHA"]
|
||||
@GITHUB_EVENT_PATH = ENV["GITHUB_EVENT_PATH"]
|
||||
@GITHUB_TOKEN = ENV["GITHUB_TOKEN"]
|
||||
@GITHUB_WORKSPACE = ENV["GITHUB_WORKSPACE"]
|
||||
def read_json(path)
|
||||
JSON.parse(File.read(path))
|
||||
end
|
||||
|
||||
@event = JSON.parse(File.read(ENV["GITHUB_EVENT_PATH"]))
|
||||
@repository = @event["repository"]
|
||||
@owner = @repository["owner"]["login"]
|
||||
@repo = @repository["name"]
|
||||
@conclusion = "success"
|
||||
@check_name = "Rubocop"
|
||||
@headers = {
|
||||
"Content-Type": 'application/json',
|
||||
"Accept": 'application/vnd.github.antiope-preview+json',
|
||||
"Authorization": "Bearer #{@GITHUB_TOKEN}",
|
||||
"User-Agent": 'rubocop-action'
|
||||
@event_json = read_json(ENV['GITHUB_EVENT_PATH']) if ENV['GITHUB_EVENT_PATH']
|
||||
@github_data = {
|
||||
sha: ENV['GITHUB_SHA'],
|
||||
token: ENV['GITHUB_TOKEN'],
|
||||
owner: ENV['GITHUB_REPOSITORY_OWNER'] || @event_json.dig('repository', 'owner', 'login'),
|
||||
repo: ENV['GITHUB_REPOSITORY_NAME'] || @event_json.dig('repository', 'name')
|
||||
}
|
||||
|
||||
def create_check
|
||||
body = {
|
||||
"name" => @check_name,
|
||||
"head_sha" => @GITHUB_SHA,
|
||||
"status" => "in_progress",
|
||||
"started_at" => Time.now.iso8601
|
||||
}
|
||||
|
||||
http = Net::HTTP.new('api.github.com', 443)
|
||||
http.use_ssl = true
|
||||
path = "/repos/#{@owner}/#{@repo}/check-runs"
|
||||
|
||||
resp = http.post(path, body.to_json, @headers)
|
||||
|
||||
if resp.code.to_i >= 300
|
||||
raise resp.message
|
||||
@report =
|
||||
if ENV['REPORT_PATH']
|
||||
read_json(ENV['REPORT_PATH'])
|
||||
else
|
||||
Dir.chdir(ENV['GITHUB_WORKSPACE']) { JSON.parse(`rubocop -f json`) }
|
||||
end
|
||||
|
||||
data = JSON.parse(resp.body)
|
||||
return data["id"]
|
||||
end
|
||||
|
||||
def update_check(id, conclusion, output)
|
||||
body = {
|
||||
"name" => @check_name,
|
||||
"completed_at" => Time.now.iso8601,
|
||||
"conclusion" => conclusion,
|
||||
"output" => output
|
||||
}
|
||||
|
||||
http = Net::HTTP.new('api.github.com', 443)
|
||||
http.use_ssl = true
|
||||
path = "/repos/#{@owner}/#{@repo}/check-runs/#{id}"
|
||||
resp = http.patch(path, body.to_json, @headers)
|
||||
|
||||
if resp.code.to_i >= 300
|
||||
raise resp.message
|
||||
end
|
||||
end
|
||||
|
||||
@annotation_levels = {
|
||||
"refactor" => 'failure',
|
||||
"convention" => 'failure',
|
||||
"warning" => 'warning',
|
||||
"error" => 'failure',
|
||||
"fatal" => 'failure'
|
||||
}
|
||||
|
||||
def run_rubocop
|
||||
annotations = annotation_messages
|
||||
|
||||
output = {
|
||||
"title": @check_name,
|
||||
"summary": "#{annotations.size} offense(s) found",
|
||||
"annotations" => annotations
|
||||
}
|
||||
|
||||
return { "output" => output, "conclusion" => @conclusion }
|
||||
end
|
||||
|
||||
def annotation_messages
|
||||
errors = nil
|
||||
count = 0
|
||||
annotation_list = []
|
||||
|
||||
Dir.chdir(@GITHUB_WORKSPACE) {
|
||||
errors = JSON.parse(`rubocop --format json`)
|
||||
}
|
||||
|
||||
errors["files"].each do |file|
|
||||
path = file["path"]
|
||||
offenses = file["offenses"]
|
||||
|
||||
offenses.each do |offense|
|
||||
severity = offense["severity"]
|
||||
message = offense["message"]
|
||||
location = offense["location"]
|
||||
annotation_level = @annotation_levels[severity]
|
||||
count = count + 1
|
||||
|
||||
return annotation_list if count == 48
|
||||
|
||||
if annotation_level == "failure"
|
||||
@conclusion = "failure"
|
||||
end
|
||||
|
||||
annotation_list.push({
|
||||
"path" => path,
|
||||
"start_line" => location["start_line"],
|
||||
"end_line" => location["start_line"],
|
||||
"annotation_level": annotation_level,
|
||||
"message" => message
|
||||
})
|
||||
end
|
||||
end
|
||||
return annotation_list
|
||||
end
|
||||
|
||||
def run
|
||||
id = create_check()
|
||||
begin
|
||||
results = run_rubocop()
|
||||
conclusion = results["conclusion"]
|
||||
output = results["output"]
|
||||
update_check(id, conclusion, output)
|
||||
rescue
|
||||
update_check(id, "failure", nil)
|
||||
fail
|
||||
end
|
||||
end
|
||||
|
||||
run()
|
||||
GithubCheckRunService.new(@report, @github_data, ReportAdapter).run
|
||||
|
||||
54
lib/report_adapter.rb
Normal file
54
lib/report_adapter.rb
Normal file
@ -0,0 +1,54 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ReportAdapter
|
||||
class << self
|
||||
CONCLUSION_TYPES = { failure: 'failure', success: 'success' }.freeze
|
||||
ANNOTATION_LEVELS = {
|
||||
'refactor' => 'failure',
|
||||
'convention' => 'failure',
|
||||
'warning' => 'warning',
|
||||
'error' => 'failure',
|
||||
'fatal' => 'failure'
|
||||
}.freeze
|
||||
|
||||
def conslusion(report)
|
||||
return CONCLUSION_TYPES[:failure] if total_offenses(report).positive?
|
||||
|
||||
CONCLUSION_TYPES[:success]
|
||||
end
|
||||
|
||||
def summary(report)
|
||||
"#{total_offenses(report)} offense(s) found"
|
||||
end
|
||||
|
||||
def annotations(report)
|
||||
annotation_list = []
|
||||
count = 0
|
||||
report['files'].each do |file|
|
||||
file['offenses'].each do |offense|
|
||||
count += 1
|
||||
return annotation_list if count == 48
|
||||
|
||||
location = offense['location']
|
||||
annotation_list.push({
|
||||
'path' => file['path'],
|
||||
'start_line' => location['start_line'],
|
||||
'end_line' => location['last_line'],
|
||||
'annotation_level' => annotation_level(offense['severity']),
|
||||
'message' => offense['message']
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def annotation_level(severity)
|
||||
ANNOTATION_LEVELS[severity]
|
||||
end
|
||||
|
||||
def total_offenses(report)
|
||||
report.dig('summary', 'offense_count')
|
||||
end
|
||||
end
|
||||
end
|
||||
3301
spec/fixtures/report.json
vendored
Normal file
3301
spec/fixtures/report.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
18
spec/github_check_run_service_spec.rb
Normal file
18
spec/github_check_run_service_spec.rb
Normal file
@ -0,0 +1,18 @@
|
||||
require './spec/spec_helper'
|
||||
|
||||
describe GithubCheckRunService do
|
||||
let(:rubocop_report) { JSON(File.read('./spec/fixtures/report.json')) }
|
||||
let(:github_data) { { sha: 'sha', token: 'token', owner: 'owner', repo: 'repository_name' } }
|
||||
let(:service) { GithubCheckRunService.new(rubocop_report, github_data, ReportAdapter) }
|
||||
|
||||
it '#run' do
|
||||
stub_request(:any, 'https://api.github.com/repos/owner/repository_name/check-runs/id')
|
||||
.to_return(status: 200, body: '{}')
|
||||
|
||||
stub_request(:any, 'https://api.github.com/repos/owner/repository_name/check-runs')
|
||||
.to_return(status: 200, body: '{"id": "id"}')
|
||||
|
||||
output = service.run
|
||||
expect(output).to be_a(Hash)
|
||||
end
|
||||
end
|
||||
30
spec/report_adapter_spec.rb
Normal file
30
spec/report_adapter_spec.rb
Normal file
@ -0,0 +1,30 @@
|
||||
require './spec/spec_helper'
|
||||
|
||||
describe ReportAdapter do
|
||||
let(:rubocop_report) {
|
||||
JSON(File.read('./spec/fixtures/report.json'))
|
||||
}
|
||||
|
||||
let(:adapter) { ReportAdapter }
|
||||
|
||||
it '.conslusion' do
|
||||
result = adapter.conslusion(rubocop_report)
|
||||
expect(result).to eq('failure')
|
||||
end
|
||||
|
||||
it '.summary' do
|
||||
result = adapter.summary(rubocop_report)
|
||||
expect(result).to eq('201 offense(s) found')
|
||||
end
|
||||
|
||||
it '.annotations' do
|
||||
result = adapter.annotations(rubocop_report)
|
||||
expect(result.first).to eq({
|
||||
'path' => 'Gemfile',
|
||||
'start_line' => 1,
|
||||
'end_line' => 1,
|
||||
'annotation_level' => 'failure',
|
||||
'message' => 'Missing magic comment `# frozen_string_literal: true`.'
|
||||
})
|
||||
end
|
||||
end
|
||||
6
spec/spec_helper.rb
Normal file
6
spec/spec_helper.rb
Normal file
@ -0,0 +1,6 @@
|
||||
require 'webmock/rspec'
|
||||
require 'json'
|
||||
require 'pry'
|
||||
require './lib/report_adapter'
|
||||
require './lib/github_check_run_service'
|
||||
require './lib/github_client'
|
||||
Reference in New Issue
Block a user