lib/rubocop/packs.rb in rubocop-packs-0.0.22 vs lib/rubocop/packs.rb in rubocop-packs-0.0.23
- old
+ new
@@ -21,50 +21,66 @@
CONFIG_DEFAULT = T.let(PROJECT_ROOT.join('config', 'default.yml').freeze, Pathname)
CONFIG = T.let(YAML.safe_load(CONFIG_DEFAULT.read).freeze, T.untyped)
private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
+ class Offense < T::Struct
+ extend T::Sig
+
+ const :cop_name, String
+ const :filepath, String
+
+ sig { returns(ParsePackwerk::Package) }
+ def pack
+ ParsePackwerk.package_from_path(filepath)
+ end
+ end
+
+ sig { params(paths: T::Array[String], cop_names: T::Array[String]).returns(T::Array[Offense]) }
+ def self.offenses_for(paths:, cop_names:)
+ path_arguments = paths.join(' ')
+ cop_arguments = cop_names.join(',')
+ # I think we can potentially use `RuboCop::CLI.new(args)` for this to avoid shelling out and starting another process that needs to reload the bundle
+ args = [path_arguments, "--only=#{cop_arguments}", '--format=json']
+ puts "Executing: bundle exec rubocop #{args.join(' ')}"
+ json = JSON.parse(Private.execute_rubocop(args))
+ offenses = T.let([], T::Array[Offense])
+ json['files'].each do |file_hash|
+ filepath = file_hash['path']
+ file_hash['offenses'].each do |offense_hash|
+ offenses << Offense.new(
+ cop_name: offense_hash['cop_name'],
+ filepath: filepath
+ )
+ end
+ end
+
+ offenses
+ end
+
#
# Ideally, this is API that is available to us via `rubocop` itself.
# That is: the ability to preserve the location of `.rubocop_todo.yml` files and associate
# exclusions with the closest ancestor `.rubocop_todo.yml`
#
sig { params(packs: T::Array[ParsePackwerk::Package]).void }
def self.auto_generate_rubocop_todo(packs:)
- pack_arguments = packs.map(&:name).join(' ')
- cop_arguments = config.permitted_pack_level_cops.join(',')
- command = "bundle exec rubocop #{pack_arguments} --only=#{cop_arguments} --format=json"
- puts "Executing: #{command}"
- json = JSON.parse(`#{command}`)
- new_rubocop_todo_exclusions = {}
- json['files'].each do |file_hash|
- filepath = file_hash['path']
- pack = ParsePackwerk.package_from_path(filepath)
- next if pack.name == ParsePackwerk::ROOT_PACKAGE_NAME
+ offenses = offenses_for(
+ paths: packs.map(&:name).reject { |name| name == ParsePackwerk::ROOT_PACKAGE_NAME },
+ cop_names: config.permitted_pack_level_cops
+ )
- file_hash['offenses'].each do |offense_hash|
- cop_name = offense_hash['cop_name']
- next unless config.permitted_pack_level_cops.include?(cop_name)
-
- new_rubocop_todo_exclusions[pack.name] ||= {}
- new_rubocop_todo_exclusions[pack.name][filepath] ||= []
- new_rubocop_todo_exclusions[pack.name][filepath] << cop_name
- end
- end
-
- new_rubocop_todo_exclusions.each do |pack_name, file_hash|
- pack = T.must(ParsePackwerk.find(pack_name))
+ offenses.group_by(&:pack).each do |pack, offenses_for_pack|
rubocop_todo_yml = pack.directory.join(PACK_LEVEL_RUBOCOP_TODO_YML)
if rubocop_todo_yml.exist?
rubocop_todo = YAML.load_file(rubocop_todo_yml)
else
rubocop_todo = {}
end
- file_hash.each do |file, failing_cops|
- failing_cops.each do |failing_cop|
- rubocop_todo[failing_cop] ||= { 'Exclude' => [] }
- rubocop_todo[failing_cop]['Exclude'] << file
- end
+
+ offenses_for_pack.each do |offense|
+ rubocop_todo[offense.cop_name] ||= { 'Exclude' => [] }
+ rubocop_todo[offense.cop_name]['Exclude'] << offense.filepath
end
next if rubocop_todo.empty?
rubocop_todo_yml.write(YAML.dump(rubocop_todo))