diff --git a/.rubocop.yml b/.rubocop.yml index 6ee570d..bd96ec2 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -8,4 +8,4 @@ Style/Documentation: Enabled: false Metrics/LineLength: - Max: 120 + Max: 150 diff --git a/README.md b/README.md index b1a3f2b..80adf39 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,21 @@ ## Brakeman github action -Brakeman is a static analysis tool which checks Ruby on Rails applications for security vulnerabilities. -[See more](https://github.com/presidentbeef/brakeman) +Check your coverage percentage. ### Usage -```yml -- name: Brakeman - uses: devmasx/brakeman-linter-action@v1.0.0 - env: - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} -``` - -### Custom report +#### [Simplecov](https://github.com/colszowka/simplecov) ```yml -- name: Install gems - run: | - gem install brakeman -v 4.5.0 -- name: brakeman report - run: | - brakeman -f json > tmp/brakeman.json || exit 0 -- name: Brakeman - uses: devmasx/brakeman-linter-action@v1.0.0 - env: - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - REPORT_PATH: tmp/brakeman.json +- uses: devmasx/coverage-check-action@coverage-check + with: + result_path: coverage/.last_run.json + token: ${{secrets.GITHUB_TOKEN}} + type: simplecov + min_coverage: 90 ``` ## Screenshots -![example GitHub Action UI](./screenshots/action.png) -![example Pull request](./screenshots/pull_request.png) +![Success](./screenshots/success.png) +![Fail](./screenshots/fail.png) diff --git a/action.yml b/action.yml index b1d1d75..eeca20f 100644 --- a/action.yml +++ b/action.yml @@ -1,9 +1,24 @@ name: "Brakeman linter" description: "A GitHub Action that lints your Ruby code with Brakeman!" author: Miguel Savignano -runs: - using: "docker" - image: "Dockerfile" branding: icon: "check-square" color: "red" +inputs: + type: + description: "simplecov | jest" + required: true + default: "simplecov" + token: + description: "Github token for create checks" + required: true + default: "World" + min_coverage: + description: "Minimum coverage" + default: "80" + result_path: + description: "Json with coverage result" + required: true +runs: + using: "docker" + image: "Dockerfile" diff --git a/lib/coverage_report.rb b/lib/coverage_report.rb new file mode 100644 index 0000000..7646611 --- /dev/null +++ b/lib/coverage_report.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +class CoverageReport + def self.generate(type, report_path, data) + if type == 'simplecov' + simplecov(report_path, data) + elsif type == 'jest' + jest(report_path, data) + else + raise 'InvalidCoverageReportType' + 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 read_json(path) + JSON.parse(File.read(path)) + end +end diff --git a/lib/github_check_run_service.rb b/lib/github_check_run_service.rb index 919e477..509e81f 100644 --- a/lib/github_check_run_service.rb +++ b/lib/github_check_run_service.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true class GithubCheckRunService - CHECK_NAME = 'Brakeman' + CHECK_NAME = 'Coverage' 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: 'brakeman-action') + @client = GithubClient.new(@github_data[:token], user_agent: 'coverage-action') end def run diff --git a/lib/index.rb b/lib/index.rb index 4638fc3..e888441 100644 --- a/lib/index.rb +++ b/lib/index.rb @@ -6,6 +6,7 @@ require 'time' require_relative './report_adapter' require_relative './github_check_run_service' require_relative './github_client' +require_relative './coverage_report' def read_json(path) JSON.parse(File.read(path)) @@ -14,16 +15,15 @@ end @event_json = read_json(ENV['GITHUB_EVENT_PATH']) if ENV['GITHUB_EVENT_PATH'] @github_data = { sha: ENV['GITHUB_SHA'], - token: ENV['GITHUB_TOKEN'], + token: ENV['INPUT_TOKEN'], owner: ENV['GITHUB_REPOSITORY_OWNER'] || @event_json.dig('repository', 'owner', 'login'), repo: ENV['GITHUB_REPOSITORY_NAME'] || @event_json.dig('repository', 'name') } -@report = - if ENV['REPORT_PATH'] - read_json(ENV['REPORT_PATH']) - else - Dir.chdir(ENV['GITHUB_WORKSPACE']) { JSON.parse(`brakeman -f json`) } - end +@coverage_type = ENV['INPUT_TYPE'] +@report_path = ENV['INPUT_RESULT_PATH'] +@data = { min: ENV['INPUT_MIN_COVERAGE'] } + +@report = CoverageReport.generate(@coverage_type, @report_path, @data) GithubCheckRunService.new(@report, @github_data, ReportAdapter).run diff --git a/lib/report_adapter.rb b/lib/report_adapter.rb index 3e57b09..ded4ac0 100644 --- a/lib/report_adapter.rb +++ b/lib/report_adapter.rb @@ -7,40 +7,29 @@ class ReportAdapter ANNOTATION_LEVEL = { notice: 'notice', warning: 'warning', failure: 'failure' }.freeze def conslusion(report) - return CONCLUSION_TYPES[:failure] if security_warnings(report).positive? - - CONCLUSION_TYPES[:success] + lines_covered_percent(report) >= lines_minimum_percent(report).to_f ? CONCLUSION_TYPES[:success] : CONCLUSION_TYPES[:failure] end def summary(report) - "**Brakeman Report**:\n#{security_warnings(report)} security warnings\n#{check_table(report)}" + "**Coverage**:\n\n#{table_head}\n| Lines | #{lines_covered_percent(report)}% | #{lines_minimum_percent(report)}% |\n" end - def annotations(report) - report['warnings'].map do |error| - { - 'path' => error['file'], - 'start_line' => error['line'], - 'end_line' => error['line'], - 'annotation_level' => ANNOTATION_LEVEL[:warning], - 'title' => "#{error['confidence']} - #{error['check_name']}", - 'message' => error['message'] - } - end + def annotations(_report) + [] end private - def check_table(report) - uniq_checks(report).reduce('') { |memo, check| memo + "- [#{check[:check_name]}](#{check[:link]})\n" } + def table_head + "| Type | covered | minimum |\n| ----- | ------- | ------- |" end - def uniq_checks(report) - report['warnings'].map { |w| { check_name: w['check_name'], link: w['link'] } }.uniq { |w| w[:check_name] } + def lines_covered_percent(report) + @lines_covered_percent ||= report.dig('lines', 'covered_percent') end - def security_warnings(report) - report['scan_info']['security_warnings'] + def lines_minimum_percent(report) + @lines_minimum_percent ||= report.dig('lines', 'minumum_percent') end end end diff --git a/screenshots/action.png b/screenshots/action.png deleted file mode 100644 index 6fc094c..0000000 Binary files a/screenshots/action.png and /dev/null differ diff --git a/screenshots/fail.png b/screenshots/fail.png new file mode 100644 index 0000000..07f4369 Binary files /dev/null and b/screenshots/fail.png differ diff --git a/screenshots/pull_request.png b/screenshots/pull_request.png deleted file mode 100644 index fbb0634..0000000 Binary files a/screenshots/pull_request.png and /dev/null differ diff --git a/screenshots/success.png b/screenshots/success.png new file mode 100644 index 0000000..94383db Binary files /dev/null and b/screenshots/success.png differ diff --git a/spec/fixtures/output/annotations.json b/spec/fixtures/output/annotations.json deleted file mode 100644 index 4d53208..0000000 --- a/spec/fixtures/output/annotations.json +++ /dev/null @@ -1,34 +0,0 @@ -[ - { - "path": "app/controllers/posts_controller.rb", - "start_line": 29, - "end_line": 29, - "annotation_level": "warning", - "title": "High - Evaluation", - "message": "User input in eval" - }, - { - "path": "app/controllers/posts_controller.rb", - "start_line": 18, - "end_line": 18, - "annotation_level": "warning", - "title": "High - MassAssignment", - "message": "Parameters should be whitelisted for mass assignment" - }, - { - "path": "app/controllers/posts_controller.rb", - "start_line": 19, - "end_line": 19, - "annotation_level": "warning", - "title": "High - MassAssignment", - "message": "Parameters should be whitelisted for mass assignment" - }, - { - "path": "app/controllers/posts_controller.rb", - "start_line": 13, - "end_line": 13, - "annotation_level": "warning", - "title": "Medium - SQL", - "message": "Possible SQL injection" - } -] diff --git a/spec/fixtures/output/summary.md b/spec/fixtures/output/summary.md index 1f35bb5..7707b12 100644 --- a/spec/fixtures/output/summary.md +++ b/spec/fixtures/output/summary.md @@ -1,5 +1,5 @@ -**Brakeman Report**: -4 security warnings -- [Evaluation](https://brakemanscanner.org/docs/warning_types/dangerous_eval/) -- [MassAssignment](https://brakemanscanner.org/docs/warning_types/mass_assignment/) -- [SQL](https://brakemanscanner.org/docs/warning_types/sql_injection/) +**Coverage**: + +| Type | covered | minimum | +| ----- | ------- | ------- | +| Lines | 80% | 80% | diff --git a/spec/github_check_run_service_spec.rb b/spec/github_check_run_service_spec.rb index fbd86b1..b76cb51 100644 --- a/spec/github_check_run_service_spec.rb +++ b/spec/github_check_run_service_spec.rb @@ -3,9 +3,11 @@ require './spec/spec_helper' describe GithubCheckRunService do - let(:brakeman_report) { JSON(File.read('./spec/fixtures/report.json')) } + let(:report) do + { 'lines' => { 'covered_percent' => 80, 'minumum_percent' => 80 } } + end let(:github_data) { { sha: 'sha', token: 'token', owner: 'owner', repo: 'repository_name' } } - let(:service) { GithubCheckRunService.new(brakeman_report, github_data, ReportAdapter) } + let(:service) { GithubCheckRunService.new(report, github_data, ReportAdapter) } it '#run' do stub_request(:any, 'https://api.github.com/repos/owner/repository_name/check-runs/id') diff --git a/spec/report_adapter_spec.rb b/spec/report_adapter_spec.rb index 132dec7..137c0e8 100644 --- a/spec/report_adapter_spec.rb +++ b/spec/report_adapter_spec.rb @@ -3,12 +3,8 @@ require './spec/spec_helper' describe ReportAdapter do - let(:brakeman_report) do - JSON(File.read('./spec/fixtures/report.json')) - end - - let(:spec_annotations) do - JSON(File.read('./spec/fixtures/output/annotations.json')) + let(:report) do + { 'lines' => { 'covered_percent' => 80, 'minumum_percent' => 80 } } end let(:spec_summary) do @@ -18,17 +14,17 @@ describe ReportAdapter do let(:adapter) { ReportAdapter } it '.conslusion' do - result = adapter.conslusion(brakeman_report) - expect(result).to eq('failure') + result = adapter.conslusion(report) + expect(result).to eq('success') end it '.summary' do - result = adapter.summary(brakeman_report) + result = adapter.summary(report) expect(result).to eq(spec_summary) end it '.annotations' do - result = adapter.annotations(brakeman_report) - expect(result).to eq(spec_annotations) + result = adapter.annotations(report) + expect(result).to eq([]) end end