#!/usr/bin/env ruby # frozen_string_literal: true # # Wrapper for the CodeClimate integration. require_relative '../lib/reek' require_relative '../lib/reek/cli/application' require_relative '../lib/reek/code_climate' Reek::CLI::Silencer.silently { require 'parser/current' } # Map input coming from CodeClimate to Reek. class CodeClimateToReek # Following the spec (https://github.com/codeclimate/spec/blob/master/SPEC.md) # we have to exit with a zero for both failure and success. ENGINE_CONFIGURATION = [ '--failure-exit-code', '0', '--success-exit-code', '0' ].freeze CUSTOM_CONFIG_KEY = 'config' CUSTOM_CONFIG_TARGET_RUBY_VERSION_KEY = 'target_ruby_version' attr_reader :configuration_file_path, :include_paths_key, :include_paths_default def initialize(configuration_file_path: '/config.json', include_paths_key: 'include_paths', include_paths_default: ['.']) @configuration_file_path = configuration_file_path @include_paths_key = include_paths_key @include_paths_default = include_paths_default end def cli_arguments include_paths + ENGINE_CONFIGURATION end def target_ruby_version config.dig(CUSTOM_CONFIG_KEY, CUSTOM_CONFIG_TARGET_RUBY_VERSION_KEY) end private def configuration_file_exists? Pathname.new(configuration_file_path).exist? end # The config.json file we try to read below might look like this: # { # "include_paths":[ # "lib", # "spec" # ] # } def include_paths config.fetch include_paths_key, include_paths_default end def config if configuration_file_exists? JSON.parse File.read(configuration_file_path) else {} end end end # Override for ReportCommand to force the use of CodeClimateReport. module ReportClassOverride def report_class Reek::CodeClimate::CodeClimateReport end end # Override Reek::Source::SourceCode to use a parser version specified by the user module SourceCodeOverride # override self.default_parser method def default_parser parser_class.new(Reek::AST::Builder.new).tap do |parser| diagnostics = parser.diagnostics diagnostics.all_errors_are_fatal = true diagnostics.ignore_warnings = true end end # config.json file will look like this: # { # "include_paths":[ # "lib", # "spec" # ], # "config": { # "target_ruby_version": "3.1.0" # } # } def parser_class # convert an X.Y.Z version number to an XY two digit number requested_version = CodeClimateToReek.new.target_ruby_version return Parser::CurrentRuby if requested_version.nil? version_number = Gem::Version.new(requested_version).segments[0..1].join begin Reek::CLI::Silencer.silently { require "parser/ruby#{version_number}" } Module.const_get("Parser::Ruby#{version_number}") rescue LoadError, NameError # use Parser::CurrentRuby when an invalid version number is provided Parser::CurrentRuby end end end Reek::CLI::Command::ReportCommand.prepend ReportClassOverride Reek::Source::SourceCode.singleton_class.prepend SourceCodeOverride application = Reek::CLI::Application.new(CodeClimateToReek.new.cli_arguments) exit application.execute