Lcov report (#2)
* execute node bin * calculate percentage for lcov file * install node in docker image * use relative path to bin folder * update readme
This commit is contained in:
parent
6dc7a86bd5
commit
d734a59c05
@ -1,5 +1,8 @@
|
|||||||
FROM ruby:2.6.5-alpine
|
FROM ruby:2.6.5-alpine
|
||||||
|
|
||||||
|
RUN apk add --update nodejs
|
||||||
|
|
||||||
COPY lib /action/lib
|
COPY lib /action/lib
|
||||||
|
COPY bin /action/bin
|
||||||
|
|
||||||
CMD ["ruby", "/action/lib/index.rb"]
|
CMD ["ruby", "/action/lib/index.rb"]
|
||||||
|
|||||||
17
README.md
17
README.md
@ -4,15 +4,26 @@ A GitHub Action that check minimum coverage percentage!
|
|||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
|
#### Lcov
|
||||||
|
|
||||||
|
```yml
|
||||||
|
- uses: devmasx/coverage-check-action@v1.1.0
|
||||||
|
with:
|
||||||
|
type: lcov
|
||||||
|
result_path: coverage/example.lcov
|
||||||
|
min_coverage: 90
|
||||||
|
token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
```
|
||||||
|
|
||||||
#### [Simplecov](https://github.com/colszowka/simplecov)
|
#### [Simplecov](https://github.com/colszowka/simplecov)
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
- uses: devmasx/coverage-check-action@v1.0.1
|
- uses: devmasx/coverage-check-action@v1.1.0
|
||||||
with:
|
with:
|
||||||
result_path: coverage/.last_run.json
|
|
||||||
token: ${{secrets.GITHUB_TOKEN}}
|
|
||||||
type: simplecov
|
type: simplecov
|
||||||
|
result_path: coverage/.last_run.json
|
||||||
min_coverage: 90
|
min_coverage: 90
|
||||||
|
token: ${{secrets.GITHUB_TOKEN}}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|||||||
@ -6,9 +6,9 @@ branding:
|
|||||||
color: "green"
|
color: "green"
|
||||||
inputs:
|
inputs:
|
||||||
type:
|
type:
|
||||||
description: "simplecov | jest"
|
description: "lcov | simplecov"
|
||||||
required: true
|
required: true
|
||||||
default: "simplecov"
|
default: "lcov"
|
||||||
token:
|
token:
|
||||||
description: "Github token for create checks"
|
description: "Github token for create checks"
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
129
bin/lcov-parse.js
Normal file
129
bin/lcov-parse.js
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2012, Yahoo! Inc. All rights reserved.
|
||||||
|
Code licensed under the BSD License:
|
||||||
|
http://yuilibrary.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
var fs = require('fs'),
|
||||||
|
path = require('path');
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
var exists = fs.exists || path.exists;
|
||||||
|
|
||||||
|
var walkFile = function (str, cb) {
|
||||||
|
var data = [], item;
|
||||||
|
|
||||||
|
['end_of_record'].concat(str.split('\n')).forEach(function (line) {
|
||||||
|
line = line.trim();
|
||||||
|
var allparts = line.split(':'),
|
||||||
|
parts = [allparts.shift(), allparts.join(':')],
|
||||||
|
lines, fn;
|
||||||
|
|
||||||
|
switch (parts[0].toUpperCase()) {
|
||||||
|
case 'TN':
|
||||||
|
item.title = parts[1].trim();
|
||||||
|
break;
|
||||||
|
case 'SF':
|
||||||
|
item.file = parts.slice(1).join(':').trim();
|
||||||
|
break;
|
||||||
|
case 'FNF':
|
||||||
|
item.functions.found = Number(parts[1].trim());
|
||||||
|
break;
|
||||||
|
case 'FNH':
|
||||||
|
item.functions.hit = Number(parts[1].trim());
|
||||||
|
break;
|
||||||
|
case 'LF':
|
||||||
|
item.lines.found = Number(parts[1].trim());
|
||||||
|
break;
|
||||||
|
case 'LH':
|
||||||
|
item.lines.hit = Number(parts[1].trim());
|
||||||
|
break;
|
||||||
|
case 'DA':
|
||||||
|
lines = parts[1].split(',');
|
||||||
|
item.lines.details.push({
|
||||||
|
line: Number(lines[0]),
|
||||||
|
hit: Number(lines[1])
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'FN':
|
||||||
|
fn = parts[1].split(',');
|
||||||
|
item.functions.details.push({
|
||||||
|
name: fn[1],
|
||||||
|
line: Number(fn[0])
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'FNDA':
|
||||||
|
fn = parts[1].split(',');
|
||||||
|
item.functions.details.some(function (i, k) {
|
||||||
|
if (i.name === fn[1] && i.hit === undefined) {
|
||||||
|
item.functions.details[k].hit = Number(fn[0]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'BRDA':
|
||||||
|
fn = parts[1].split(',');
|
||||||
|
item.branches.details.push({
|
||||||
|
line: Number(fn[0]),
|
||||||
|
block: Number(fn[1]),
|
||||||
|
branch: Number(fn[2]),
|
||||||
|
taken: ((fn[3] === '-') ? 0 : Number(fn[3]))
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'BRF':
|
||||||
|
item.branches.found = Number(parts[1]);
|
||||||
|
break;
|
||||||
|
case 'BRH':
|
||||||
|
item.branches.hit = Number(parts[1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.indexOf('end_of_record') > -1) {
|
||||||
|
data.push(item);
|
||||||
|
item = {
|
||||||
|
lines: {
|
||||||
|
found: 0,
|
||||||
|
hit: 0,
|
||||||
|
details: []
|
||||||
|
},
|
||||||
|
functions: {
|
||||||
|
hit: 0,
|
||||||
|
found: 0,
|
||||||
|
details: []
|
||||||
|
},
|
||||||
|
branches: {
|
||||||
|
hit: 0,
|
||||||
|
found: 0,
|
||||||
|
details: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
data.shift();
|
||||||
|
|
||||||
|
if (data.length) {
|
||||||
|
cb(null, data);
|
||||||
|
} else {
|
||||||
|
cb('Failed to parse string');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var parse = function (file, cb) {
|
||||||
|
exists(file, function (x) {
|
||||||
|
if (!x) {
|
||||||
|
return walkFile(file, cb);
|
||||||
|
}
|
||||||
|
fs.readFile(file, 'utf8', function (err, str) {
|
||||||
|
walkFile(str, cb);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
var path = process.argv[2]
|
||||||
|
parse(path, function (err, data) {
|
||||||
|
console.log(JSON.stringify(data, null, 2))
|
||||||
|
});
|
||||||
|
|
||||||
|
// console.log(process.argv[2])
|
||||||
@ -1,33 +1,46 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CoverageReport
|
class CoverageReport
|
||||||
def self.generate(type, report_path, data)
|
class << self
|
||||||
if type == 'simplecov'
|
def generate(type, report_path, data)
|
||||||
simplecov(report_path, data)
|
if type == 'simplecov'
|
||||||
elsif type == 'jest'
|
simplecov(report_path, data)
|
||||||
jest(report_path, data)
|
elsif type == 'lcov'
|
||||||
else
|
lcov(report_path, data)
|
||||||
raise 'InvalidCoverageReportType'
|
else
|
||||||
|
raise 'InvalidCoverageReportType'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def simplecov(report_path, data)
|
||||||
|
report = read_json(report_path)
|
||||||
|
minumum_percent = data[:min]
|
||||||
|
covered_percent = report.dig('result', 'covered_percent')
|
||||||
|
{ 'lines' => { 'covered_percent' => covered_percent, 'minumum_percent' => minumum_percent } }
|
||||||
|
end
|
||||||
|
|
||||||
|
def lcov(report_path, data)
|
||||||
|
lcov_result = execute_lcov_parse(report_path)
|
||||||
|
minumum_percent = data[:min]
|
||||||
|
{ 'lines' => { 'covered_percent' => lcov_covered_percent(lcov_result), 'minumum_percent' => minumum_percent } }
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def lcov_covered_percent(lcov_result)
|
||||||
|
lines = lcov_result.map { |r| r['lines']['details'] }.flatten
|
||||||
|
total_lines = lines.count.to_f.round(2)
|
||||||
|
covered_lines = lines.select { |r| r['hit'] >= 1 }.count.to_f
|
||||||
|
((covered_lines / total_lines) * 100).round(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute_lcov_parse(report_path)
|
||||||
|
bin_path = "#{File.dirname(__FILE__)}/../bin"
|
||||||
|
JSON.parse(`node #{bin_path}/lcov-parse.js #{report_path}`)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_json(path)
|
||||||
|
JSON.parse(File.read(path))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.simplecov(report_path, data)
|
|
||||||
report = read_json(report_path)
|
|
||||||
minumum_percent = data[:min]
|
|
||||||
covered_percent = report.dig('result', 'covered_percent')
|
|
||||||
{ 'lines' => { 'covered_percent' => covered_percent, 'minumum_percent' => minumum_percent } }
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.jest(report_path, data)
|
|
||||||
report = read_json(report_path)
|
|
||||||
minumum_percent = data[:min]
|
|
||||||
covered_percent = report.dig('result', 'covered_percent')
|
|
||||||
{ 'lines' => { 'covered_percent' => covered_percent, 'minumum_percent' => minumum_percent } }
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def self.read_json(path)
|
|
||||||
JSON.parse(File.read(path))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -7,4 +7,9 @@ describe CoverageReport do
|
|||||||
result = CoverageReport.simplecov('./spec/fixtures/simplecov.json', min: 80)
|
result = CoverageReport.simplecov('./spec/fixtures/simplecov.json', min: 80)
|
||||||
expect(result['lines']['covered_percent']).to eq(80.5)
|
expect(result['lines']['covered_percent']).to eq(80.5)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it '.lcov' do
|
||||||
|
result = CoverageReport.lcov('./spec/fixtures/example.lcov', min: 80)
|
||||||
|
expect(result['lines']['covered_percent']).to eq(85.61)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
193
spec/fixtures/example.lcov
vendored
Normal file
193
spec/fixtures/example.lcov
vendored
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
SF:./app/controllers/application_controller.rb
|
||||||
|
DA:1,1
|
||||||
|
DA:2,1
|
||||||
|
end_of_record
|
||||||
|
SF:./app/controllers/posts_controller.rb
|
||||||
|
DA:1,1
|
||||||
|
DA:2,1
|
||||||
|
DA:6,1
|
||||||
|
DA:7,1
|
||||||
|
DA:12,1
|
||||||
|
DA:16,1
|
||||||
|
DA:17,1
|
||||||
|
DA:21,1
|
||||||
|
DA:26,1
|
||||||
|
DA:27,0
|
||||||
|
DA:29,0
|
||||||
|
DA:30,0
|
||||||
|
DA:31,0
|
||||||
|
DA:32,0
|
||||||
|
DA:34,0
|
||||||
|
DA:35,0
|
||||||
|
DA:42,1
|
||||||
|
DA:43,0
|
||||||
|
DA:44,0
|
||||||
|
DA:45,0
|
||||||
|
DA:46,0
|
||||||
|
DA:48,0
|
||||||
|
DA:49,0
|
||||||
|
DA:56,1
|
||||||
|
DA:57,0
|
||||||
|
DA:58,0
|
||||||
|
DA:59,0
|
||||||
|
DA:60,0
|
||||||
|
DA:64,1
|
||||||
|
DA:66,1
|
||||||
|
DA:67,0
|
||||||
|
DA:71,1
|
||||||
|
DA:72,0
|
||||||
|
end_of_record
|
||||||
|
SF:./app/helpers/application_helper.rb
|
||||||
|
DA:1,1
|
||||||
|
end_of_record
|
||||||
|
SF:./app/helpers/posts_helper.rb
|
||||||
|
DA:1,1
|
||||||
|
end_of_record
|
||||||
|
SF:./app/models/application_record.rb
|
||||||
|
DA:1,1
|
||||||
|
DA:2,1
|
||||||
|
end_of_record
|
||||||
|
SF:./app/models/post.rb
|
||||||
|
DA:1,1
|
||||||
|
end_of_record
|
||||||
|
SF:./config/application.rb
|
||||||
|
DA:1,1
|
||||||
|
DA:3,1
|
||||||
|
DA:7,1
|
||||||
|
DA:9,1
|
||||||
|
DA:10,1
|
||||||
|
DA:12,1
|
||||||
|
end_of_record
|
||||||
|
SF:./config/boot.rb
|
||||||
|
DA:1,1
|
||||||
|
DA:3,1
|
||||||
|
DA:4,1
|
||||||
|
end_of_record
|
||||||
|
SF:./config/environment.rb
|
||||||
|
DA:2,1
|
||||||
|
DA:5,1
|
||||||
|
end_of_record
|
||||||
|
SF:./config/environments/test.rb
|
||||||
|
DA:1,1
|
||||||
|
DA:8,1
|
||||||
|
DA:13,1
|
||||||
|
DA:16,1
|
||||||
|
DA:17,1
|
||||||
|
DA:22,1
|
||||||
|
DA:23,1
|
||||||
|
DA:26,1
|
||||||
|
DA:29,1
|
||||||
|
DA:32,1
|
||||||
|
DA:34,1
|
||||||
|
DA:39,1
|
||||||
|
DA:42,1
|
||||||
|
end_of_record
|
||||||
|
SF:./config/initializers/application_controller_renderer.rb
|
||||||
|
|
||||||
|
end_of_record
|
||||||
|
SF:./config/initializers/assets.rb
|
||||||
|
DA:4,1
|
||||||
|
DA:9,1
|
||||||
|
end_of_record
|
||||||
|
SF:./config/initializers/backtrace_silencers.rb
|
||||||
|
|
||||||
|
end_of_record
|
||||||
|
SF:./config/initializers/content_security_policy.rb
|
||||||
|
|
||||||
|
end_of_record
|
||||||
|
SF:./config/initializers/cookies_serializer.rb
|
||||||
|
DA:5,1
|
||||||
|
end_of_record
|
||||||
|
SF:./config/initializers/filter_parameter_logging.rb
|
||||||
|
DA:4,1
|
||||||
|
end_of_record
|
||||||
|
SF:./config/initializers/inflections.rb
|
||||||
|
|
||||||
|
end_of_record
|
||||||
|
SF:./config/initializers/mime_types.rb
|
||||||
|
|
||||||
|
end_of_record
|
||||||
|
SF:./config/initializers/wrap_parameters.rb
|
||||||
|
DA:7,1
|
||||||
|
DA:8,2
|
||||||
|
end_of_record
|
||||||
|
SF:./config/routes.rb
|
||||||
|
DA:1,1
|
||||||
|
DA:3,1
|
||||||
|
DA:4,1
|
||||||
|
DA:5,1
|
||||||
|
DA:6,1
|
||||||
|
end_of_record
|
||||||
|
SF:./spec/helpers/posts_helper_spec.rb
|
||||||
|
DA:1,1
|
||||||
|
DA:13,1
|
||||||
|
DA:14,1
|
||||||
|
end_of_record
|
||||||
|
SF:./spec/models/post_spec.rb
|
||||||
|
DA:1,1
|
||||||
|
DA:3,1
|
||||||
|
DA:4,1
|
||||||
|
end_of_record
|
||||||
|
SF:./spec/requests/posts_spec.rb
|
||||||
|
DA:1,1
|
||||||
|
DA:3,1
|
||||||
|
DA:4,1
|
||||||
|
DA:5,1
|
||||||
|
DA:6,1
|
||||||
|
DA:7,1
|
||||||
|
end_of_record
|
||||||
|
SF:./spec/routing/posts_routing_spec.rb
|
||||||
|
DA:1,1
|
||||||
|
DA:3,1
|
||||||
|
DA:4,1
|
||||||
|
DA:5,1
|
||||||
|
DA:6,1
|
||||||
|
DA:9,1
|
||||||
|
DA:10,1
|
||||||
|
DA:13,1
|
||||||
|
DA:14,1
|
||||||
|
DA:17,1
|
||||||
|
DA:18,1
|
||||||
|
DA:22,1
|
||||||
|
DA:23,1
|
||||||
|
DA:26,1
|
||||||
|
DA:27,1
|
||||||
|
DA:30,1
|
||||||
|
DA:31,1
|
||||||
|
DA:34,1
|
||||||
|
DA:35,1
|
||||||
|
end_of_record
|
||||||
|
SF:./spec/views/posts/edit.html.erb_spec.rb
|
||||||
|
DA:1,1
|
||||||
|
DA:3,1
|
||||||
|
DA:4,1
|
||||||
|
DA:5,1
|
||||||
|
DA:8,1
|
||||||
|
DA:9,1
|
||||||
|
DA:11,1
|
||||||
|
end_of_record
|
||||||
|
SF:./spec/views/posts/index.html.erb_spec.rb
|
||||||
|
DA:1,1
|
||||||
|
DA:3,1
|
||||||
|
DA:4,1
|
||||||
|
DA:5,1
|
||||||
|
DA:11,1
|
||||||
|
DA:12,1
|
||||||
|
end_of_record
|
||||||
|
SF:./spec/views/posts/new.html.erb_spec.rb
|
||||||
|
DA:1,1
|
||||||
|
DA:3,1
|
||||||
|
DA:4,1
|
||||||
|
DA:5,1
|
||||||
|
DA:8,1
|
||||||
|
DA:9,1
|
||||||
|
DA:11,1
|
||||||
|
end_of_record
|
||||||
|
SF:./spec/views/posts/show.html.erb_spec.rb
|
||||||
|
DA:1,1
|
||||||
|
DA:3,1
|
||||||
|
DA:4,1
|
||||||
|
DA:5,1
|
||||||
|
DA:8,1
|
||||||
|
DA:9,1
|
||||||
|
end_of_record
|
||||||
Reference in New Issue
Block a user