diff --git a/README.md b/README.md index 98b129def..b3b572084 100644 --- a/README.md +++ b/README.md @@ -556,13 +556,14 @@ e.g., `codeclimate analyze -e duplication` ## Output formats -Reek supports 5 output formats: +Reek supports 6 output formats: * plain text (default) -* HTML (`--format html`) -* YAML (`--format yaml`, see also [YAML Reports](docs/YAML-Reports.md)) -* JSON (`--format json`) -* XML (`--format xml`) +* HTML (`--format html`) +* YAML (`--format yaml`, see also [YAML Reports](docs/YAML-Reports.md)) +* JSON (`--format json`) +* XML (`--format xml`) +* GitHub (`--format github`) ## Working with Rails diff --git a/features/command_line_interface/options.feature b/features/command_line_interface/options.feature index 7aa2f02c3..cc8382438 100644 --- a/features/command_line_interface/options.feature +++ b/features/command_line_interface/options.feature @@ -56,6 +56,7 @@ Feature: Reek can be controlled using command-line options yaml json xml + github Text format options: --[no-]color Use colors for the output (default: true) diff --git a/lib/reek/cli/options.rb b/lib/reek/cli/options.rb index f5143c2cb..87211af82 100644 --- a/lib/reek/cli/options.rb +++ b/lib/reek/cli/options.rb @@ -131,9 +131,9 @@ def set_generate_todo_list_options def set_alternative_formatter_options parser.separator "\nReport format:" parser.on( - '-f', '--format FORMAT', [:html, :text, :yaml, :json, :xml], + '-f', '--format FORMAT', [:html, :text, :yaml, :json, :xml, :github], 'Report smells in the given format:', - ' html', ' text (default)', ' yaml', ' json', ' xml') do |opt| + ' html', ' text (default)', ' yaml', ' json', ' xml', ' github') do |opt| self.report_format = opt end end diff --git a/lib/reek/report.rb b/lib/reek/report.rb index da0e7244e..2bf8b0967 100644 --- a/lib/reek/report.rb +++ b/lib/reek/report.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require_relative 'report/github_report' require_relative 'report/html_report' require_relative 'report/json_report' require_relative 'report/text_report' @@ -16,11 +17,12 @@ module Reek # Reek reporting functionality. module Report REPORT_CLASSES = { - yaml: YAMLReport, - json: JSONReport, - html: HTMLReport, - xml: XMLReport, - text: TextReport + yaml: YAMLReport, + json: JSONReport, + html: HTMLReport, + xml: XMLReport, + text: TextReport, + github: GithubReport }.freeze LOCATION_FORMATTERS = { diff --git a/lib/reek/report/github_report.rb b/lib/reek/report/github_report.rb new file mode 100644 index 000000000..389fe16dd --- /dev/null +++ b/lib/reek/report/github_report.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require_relative 'base_report' + +module Reek + module Report + # + # Displays smells as GitHub Workflow commands. + # + # @public + # + class GithubReport < BaseReport + def show(out = $stdout) + out.print(workflow_commands.join) + end + + private + + def workflow_commands + smells.map do |smell| + WorkflowCommand.new(smell) + end + end + + # Represents a smell as a GitHub Workflow command. + class WorkflowCommand + def initialize(smell) + @smell = smell + end + + def to_s + format( + "::warning file=%s,line=%d::%s\n", + file: file, + line: line, + message: message) + end + + private + + def file + @smell.source + end + + def line + @smell.lines.first + end + + def message + @smell.base_message.gsub('%', '%25').gsub("\r", '%0D').gsub("\n", '%0A') + end + end + end + end +end diff --git a/spec/reek/report/github_report_spec.rb b/spec/reek/report/github_report_spec.rb new file mode 100644 index 000000000..074a56aca --- /dev/null +++ b/spec/reek/report/github_report_spec.rb @@ -0,0 +1,44 @@ +require_relative '../../spec_helper' +require_lib 'reek/examiner' +require_lib 'reek/report/json_report' + +RSpec.describe Reek::Report::GithubReport do + let(:instance) do + described_class.new + end + + let(:examiner) do + Reek::Examiner.new(source) + end + + context 'with empty source' do + let(:source) do + '' + end + + it 'prints empty string' do + instance.add_examiner(examiner) + expect { instance.show }.not_to output.to_stdout + end + end + + context 'with smelly source' do + let(:source) do + <<~RUBY + def simple(a) + a[3] + end + RUBY + end + + it 'prints smells as GitHub Workflow commands' do + instance.add_examiner(examiner) + expect { instance.show }.to output( + <<~TEXT + ::warning file=string,line=1::UncommunicativeParameterName: simple has the parameter name 'a' + ::warning file=string,line=1::UtilityFunction: simple doesn't depend on instance state (maybe move it to another class?) + TEXT + ).to_stdout + end + end +end