rubocop-packs 0.0.39 → 0.0.40

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d88f485f918ad13a51e6b07c7d2777b3d6ea1f841bd8c5d00a745b0ef1021e44
4
- data.tar.gz: 003f98900ab977a97c55860ca4c8dfc024818a249afdfc43c76aecf17383e639
3
+ metadata.gz: 17defa98a11d3caa88807cae3f453ada518673b40f5995c95080a5625fadee25
4
+ data.tar.gz: 7f1a52fe63b4991297bb5061ce6151d0aa76c6167e9f4211a4858ef782756930
5
5
  SHA512:
6
- metadata.gz: 4df9a6bd0d19b1824cf6927e1fe1076b36042bde212a9859dde4912e2af5e49af53bb941e6cc190eec31483962dae009d956d9050b09f5132ee1cf809de5d5f5
7
- data.tar.gz: db92154be004acb720da8ffdba1624dbbfac68a170fc7cbe214a3c5294e7c945ab9ea6308198bbf02ffd1189e823c948632ca086bd3c431b0b6875fb23576c81
6
+ metadata.gz: 4b618d55a754985965bf3bc0b21d6810d35f0f9adb5283504be3560096842cbecdcf5ccc78b2ab42dee90a68d8ef345cf36ce15d4a3866dbd7e42219029ea144
7
+ data.tar.gz: 17d8709a6ef3103535269977bea8ebb16bd6436d009cb12925ec3bc2d169ab9716ef0c75be8d20f94785914243b73ca15b1cacc07f57edbaccf72e3d35d11530
data/README.md CHANGED
@@ -41,9 +41,6 @@ Packs/RootNamespaceIsPackName:
41
41
  - lib/example.rb
42
42
  ```
43
43
 
44
- ## Pack-Level `package_rubocop.yml` and `package_rubocop_todo.yml` files
45
- See [ADVANCED_USAGE.md](ADVANCED_USAGE.md)
46
-
47
44
  ## Contributing
48
45
 
49
46
  Bug reports and pull requests are welcome on GitHub at https://github.com/rubyatscale/rubocop-packs. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Code Of Conduct](CODE_OF_CONDUCT.MD).
@@ -7,27 +7,17 @@ module RuboCop
7
7
  class Configuration
8
8
  extend T::Sig
9
9
 
10
- sig { returns(T::Array[String]) }
11
- attr_accessor :permitted_pack_level_cops
12
-
13
- sig { returns(T::Array[String]) }
14
- attr_accessor :required_pack_level_cops
15
-
16
10
  sig { returns(T::Array[String]) }
17
11
  attr_accessor :globally_permitted_namespaces
18
12
 
19
13
  sig { void }
20
14
  def initialize
21
- @permitted_pack_level_cops = T.let([], T::Array[String])
22
15
  @globally_permitted_namespaces = T.let([], T::Array[String])
23
- @required_pack_level_cops = T.let([], T::Array[String])
24
16
  end
25
17
 
26
18
  sig { void }
27
19
  def bust_cache!
28
- @permitted_pack_level_cops = []
29
20
  @globally_permitted_namespaces = []
30
- @required_pack_level_cops = []
31
21
  end
32
22
  end
33
23
  end
@@ -11,7 +11,6 @@ module RuboCop
11
11
 
12
12
  sig { void }
13
13
  def self.bust_cache!
14
- @rubocop_todo_ymls = nil
15
14
  @loaded_client_configuration = nil
16
15
  end
17
16
 
@@ -24,161 +23,6 @@ module RuboCop
24
23
  client_configuration = Bundler.root.join('config/rubocop_packs.rb')
25
24
  require client_configuration.to_s if client_configuration.exist?
26
25
  end
27
-
28
- sig { returns(T::Array[T::Hash[T.untyped, T.untyped]]) }
29
- def self.rubocop_todo_ymls
30
- @rubocop_todo_ymls = T.let(@rubocop_todo_ymls, T.nilable(T::Array[T::Hash[T.untyped, T.untyped]]))
31
- @rubocop_todo_ymls ||= begin
32
- todo_files = Pathname.glob("**/#{PACK_LEVEL_RUBOCOP_TODO_YML}")
33
- todo_files.map do |todo_file|
34
- YAML.load_file(todo_file)
35
- end
36
- end
37
- end
38
-
39
- sig { params(package: ::Packs::Pack).returns(T::Array[String]) }
40
- def self.validate_rubocop_todo_yml(package)
41
- errors = []
42
- rubocop_todo = package.relative_path.join(PACK_LEVEL_RUBOCOP_TODO_YML)
43
- return errors unless rubocop_todo.exist?
44
-
45
- loaded_rubocop_todo = YAML.load_file(rubocop_todo)
46
- loaded_rubocop_todo.each_key do |key|
47
- if !Packs.config.permitted_pack_level_cops.include?(key)
48
- errors << <<~ERROR_MESSAGE
49
- #{rubocop_todo} contains invalid configuration for #{key}.
50
- Please only configure the following cops on a per-pack basis: #{Packs.config.permitted_pack_level_cops.inspect}"
51
- For ignoring other cops, please instead modify the top-level #{PACK_LEVEL_RUBOCOP_TODO_YML} file.
52
- ERROR_MESSAGE
53
- elsif loaded_rubocop_todo[key].keys != ['Exclude']
54
- errors << <<~ERROR_MESSAGE
55
- #{rubocop_todo} contains invalid configuration for #{key}.
56
- Please ensure the only configuration for #{key} is `Exclude`
57
- ERROR_MESSAGE
58
- else
59
- loaded_rubocop_todo[key]['Exclude'].each do |filepath|
60
- pack = ::Packs.for_file(filepath)
61
- next unless pack && pack.name != package.name
62
-
63
- errors << <<~ERROR_MESSAGE
64
- #{rubocop_todo} contains invalid configuration for #{key}.
65
- #{filepath} does not belong to #{package.name}. Please ensure you only add exclusions
66
- for files within this pack.
67
- ERROR_MESSAGE
68
- end
69
- end
70
- end
71
-
72
- errors
73
- end
74
-
75
- sig { params(package: ::Packs::Pack).returns(T::Array[String]) }
76
- def self.validate_rubocop_yml(package)
77
- errors = []
78
- rubocop_yml = package.relative_path.join(PACK_LEVEL_RUBOCOP_YML)
79
- return errors unless rubocop_yml.exist?
80
-
81
- loaded_rubocop_yml = YAML.load_file(rubocop_yml)
82
- missing_keys = Packs.config.required_pack_level_cops - loaded_rubocop_yml.keys
83
- missing_keys.each do |key|
84
- errors << <<~ERROR_MESSAGE
85
- #{rubocop_yml} is missing configuration for #{key}.
86
- ERROR_MESSAGE
87
- end
88
-
89
- loaded_rubocop_yml.each_key do |key|
90
- if !Packs.config.permitted_pack_level_cops.include?(key)
91
- errors << <<~ERROR_MESSAGE
92
- #{rubocop_yml} contains invalid configuration for #{key}.
93
- Please only configure the following cops on a per-pack basis: #{Packs.config.permitted_pack_level_cops.inspect}"
94
- For ignoring other cops, please instead modify the top-level .rubocop.yml file.
95
- ERROR_MESSAGE
96
- elsif (loaded_rubocop_yml[key].keys - %w[Enabled FailureMode]).any?
97
- errors << <<~ERROR_MESSAGE
98
- #{rubocop_yml} contains invalid configuration for #{key}.
99
- Please ensure the only configuration for #{key} is `Enabled` and `FailureMode`
100
- ERROR_MESSAGE
101
- end
102
- end
103
-
104
- errors
105
- end
106
-
107
- sig { params(rule: String).returns(T::Set[String]) }
108
- def self.exclude_for_rule(rule)
109
- excludes = T.let(Set.new, T::Set[String])
110
-
111
- Private.rubocop_todo_ymls.each do |todo_yml|
112
- next if !todo_yml
113
-
114
- config = todo_yml[rule]
115
- next if config.nil?
116
-
117
- exclude_list = config['Exclude']
118
- next if exclude_list.nil?
119
-
120
- excludes += exclude_list
121
- end
122
-
123
- excludes
124
- end
125
-
126
- sig { params(package: ::Packs::Pack).returns(T::Array[String]) }
127
- def self.validate_failure_mode_strict(package)
128
- errors = T.let([], T::Array[String])
129
-
130
- Packs.config.permitted_pack_level_cops.each do |cop|
131
- excludes = exclude_for_rule(cop)
132
-
133
- rubocop_yml = package.relative_path.join(PACK_LEVEL_RUBOCOP_YML)
134
-
135
- next unless rubocop_yml.exist?
136
-
137
- loaded_rubocop_yml = YAML.load_file(rubocop_yml)
138
- next unless loaded_rubocop_yml[cop] && loaded_rubocop_yml[cop]['FailureMode'] == 'strict'
139
-
140
- excludes_for_package = excludes.select do |exclude|
141
- pack = ::Packs.for_file(exclude)
142
- pack && pack.name == package.name
143
- end
144
- next if excludes_for_package.empty?
145
-
146
- formatted_excludes = excludes_for_package.map { |ex| "`#{ex}`" }.join(', ')
147
- errors << "#{package.name} has set `#{cop}` to `FailureMode: strict` in `packs/some_pack/#{PACK_LEVEL_RUBOCOP_YML}`, forbidding new exceptions. Please either remove #{formatted_excludes} from the top-level and pack-specific `#{PACK_LEVEL_RUBOCOP_TODO_YML}` files or remove `FailureMode: strict`."
148
- end
149
-
150
- errors
151
- end
152
-
153
- sig { params(args: T.untyped).void }
154
- def self.execute_rubocop(args)
155
- RuboCop::CLI.new.run(args)
156
- end
157
-
158
- sig { params(paths: T::Array[String], cop_names: T::Array[String]).returns(T::Array[Offense]) }
159
- def self.offenses_for(paths:, cop_names:)
160
- cop_arguments = cop_names.join(',')
161
- # 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
162
- args = [*paths, "--only=#{cop_arguments}", '--format=json', '--out=tmp/rubocop-output']
163
- FileUtils.mkdir_p('tmp')
164
- puts "Executing: bundle exec rubocop #{args.join(' ')}"
165
- Private.execute_rubocop(args)
166
- output = Pathname.new('tmp/rubocop-output')
167
- json = JSON.parse(Pathname.new('tmp/rubocop-output').read)
168
- output.delete
169
- offenses = T.let([], T::Array[Offense])
170
- json['files'].each do |file_hash|
171
- filepath = file_hash['path']
172
- file_hash['offenses'].each do |offense_hash|
173
- offenses << Offense.new(
174
- cop_name: offense_hash['cop_name'],
175
- filepath: filepath
176
- )
177
- end
178
- end
179
-
180
- offenses
181
- end
182
26
  end
183
27
 
184
28
  private_constant :Private
data/lib/rubocop/packs.rb CHANGED
@@ -7,131 +7,11 @@ module RuboCop
7
7
  module Packs
8
8
  extend T::Sig
9
9
 
10
- # Pack-level rubocop and rubocop_todo YML files are named differently because they are not integrated
11
- # into rubocop in the standard way. For example, we could call these the standard `.rubocop.yml` and
12
- # `.rubocop_todo.yml`. However, this introduces a number of path relativity issues (https://docs.rubocop.org/rubocop/configuration.html#path-relativity)
13
- # that make this approach not possible. Therefore, for pack level rubocops, we name them in a way that mirrors packwerk `package_todo.yml` files
14
- # for consistency and to ensure that thes are not read by rubocop except via the ERB templating mechanism.
15
- PACK_LEVEL_RUBOCOP_YML = 'package_rubocop.yml'
16
- PACK_LEVEL_RUBOCOP_TODO_YML = 'package_rubocop_todo.yml'
17
-
18
10
  PROJECT_ROOT = T.let(Pathname.new(__dir__).parent.parent.expand_path.freeze, Pathname)
19
11
  CONFIG_DEFAULT = T.let(PROJECT_ROOT.join('config', 'default.yml').freeze, Pathname)
20
12
 
21
13
  private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
22
14
 
23
- #
24
- # Ideally, this is API that is available to us via `rubocop` itself.
25
- # That is: the ability to preserve the location of `.rubocop_todo.yml` files and associate
26
- # exclusions with the closest ancestor `.rubocop_todo.yml`
27
- #
28
- sig { params(packs: T::Array[::Packs::Pack], files: T::Array[String]).void }
29
- def self.regenerate_todo(packs: [], files: [])
30
- # Delete the old pack-level rubocop todo files so that we can regenerate the new one from scratch
31
- packs.each do |pack|
32
- rubocop_todo_yml = pack.relative_path.join(PACK_LEVEL_RUBOCOP_TODO_YML)
33
- rubocop_todo_yml.delete if rubocop_todo_yml.exist?
34
- end
35
-
36
- paths = packs.empty? ? files : packs.map(&:name)
37
-
38
- cop_names = config.permitted_pack_level_cops.select do |cop_name|
39
- YAML.load_file('.rubocop.yml').fetch(cop_name, {})['Enabled']
40
- end
41
-
42
- offenses = Private.offenses_for(
43
- paths: paths,
44
- cop_names: cop_names
45
- )
46
-
47
- offenses.group_by(&:pack).each do |pack, offenses_for_pack|
48
- next if pack.nil?
49
- next if !pack.relative_path.join(PACK_LEVEL_RUBOCOP_YML).exist?
50
-
51
- rubocop_todo_yml = pack.relative_path.join(PACK_LEVEL_RUBOCOP_TODO_YML)
52
- if rubocop_todo_yml.exist?
53
- rubocop_todo = YAML.load_file(rubocop_todo_yml)
54
- else
55
- rubocop_todo = {}
56
- end
57
-
58
- offenses_for_pack.group_by(&:filepath).each do |filepath, offenses_by_filepath|
59
- offenses_by_filepath.map(&:cop_name).uniq.each do |cop_name|
60
- rubocop_todo[cop_name] ||= { 'Exclude' => [] }
61
- rubocop_todo[cop_name]['Exclude'] << filepath
62
- end
63
- end
64
-
65
- next if rubocop_todo.empty?
66
-
67
- rubocop_todo_yml.write(YAML.dump(rubocop_todo))
68
- end
69
- end
70
-
71
- #
72
- # Ideally, this is API that is available to us via `rubocop` itself.
73
- # That is: the ability to preserve the location of `.rubocop_todo.yml` files and associate
74
- # exclusions with the closest ancestor `.rubocop_todo.yml`
75
- #
76
- sig { params(packs: T::Array[::Packs::Pack]).void }
77
- def self.set_default_rubocop_yml(packs:)
78
- packs.each do |pack|
79
- rubocop_yml = pack.relative_path.join(PACK_LEVEL_RUBOCOP_YML)
80
- rubocop_yml_hash = {}
81
- config.required_pack_level_cops.each do |cop|
82
- rubocop_yml_hash[cop] = { 'Enabled' => true }
83
- end
84
-
85
- formatted_yml = YAML.dump(rubocop_yml_hash).
86
- # Find lines of the form \nCopDepartment/CopName: and add a new line before it.
87
- gsub(%r{^(\w+/\w+:)}, "\n\\1").
88
- # Remove the `---` header at the top of the file
89
- gsub("---\n\n", '')
90
-
91
- rubocop_yml.write(formatted_yml)
92
- end
93
- end
94
-
95
- # It would be great if rubocop (upstream) could take in a glob for `inherit_from`, which
96
- # would allow us to delete this method and this additional complexity.
97
- sig { params(root_pathname: String).returns(String) }
98
- def self.pack_based_rubocop_config(root_pathname: Bundler.root)
99
- rubocop_config = {}
100
- # We do this because when the ERB is evaluated Dir.pwd is at the directory containing the YML.
101
- # Ideally rubocop wouldn't change the PWD before invoking this method.
102
- Dir.chdir(root_pathname) do
103
- ::Packs.all.each do |package|
104
- rubocop_todo = package.relative_path.join(PACK_LEVEL_RUBOCOP_TODO_YML)
105
- if rubocop_todo.exist?
106
- loaded_rubocop_todo = YAML.load_file(rubocop_todo)
107
- loaded_rubocop_todo.each do |cop_name, key_config|
108
- rubocop_config[cop_name] ||= {}
109
- rubocop_config[cop_name]['Exclude'] ||= []
110
- rubocop_config[cop_name]['Exclude'] += key_config['Exclude']
111
- end
112
- end
113
-
114
- pack_rubocop = package.relative_path.join(PACK_LEVEL_RUBOCOP_YML)
115
- next unless pack_rubocop.exist?
116
-
117
- loaded_pack_rubocop = YAML.load_file(pack_rubocop)
118
- loaded_pack_rubocop.each do |cop_name, key_config|
119
- rubocop_config[cop_name] ||= {}
120
-
121
- if key_config['Enabled']
122
- rubocop_config[cop_name]['Include'] ||= []
123
- rubocop_config[cop_name]['Include'] << package.relative_path.join('**/*').to_s
124
- else
125
- rubocop_config[cop_name]['Exclude'] ||= []
126
- rubocop_config[cop_name]['Exclude'] << package.relative_path.join('**/*').to_s
127
- end
128
- end
129
- end
130
- end
131
-
132
- YAML.dump(rubocop_config)
133
- end
134
-
135
15
  sig { void }
136
16
  def self.bust_cache!
137
17
  config.bust_cache!
@@ -149,28 +29,5 @@ module RuboCop
149
29
  @config = T.let(@config, T.nilable(Private::Configuration))
150
30
  @config ||= Private::Configuration.new
151
31
  end
152
-
153
- # We can remove this function once package_protections is fully deprecated
154
- sig { params(rule: String).returns(T::Set[String]) }
155
- def self.exclude_for_rule(rule)
156
- Private.exclude_for_rule(rule)
157
- end
158
-
159
- #
160
- # Note: when we add per-pack `.rubocop.yml` files, we'll want to add some validations here
161
- # to restrict what cops are permitted to be configured in those files.
162
- # We might also want further (configurable?) constraints *requiring* that the "permitted pack level cops" be specified explicitly.
163
- #
164
- sig { returns(T::Array[String]) }
165
- def self.validate
166
- errors = T.let([], T::Array[String])
167
- ::Packs.all.each do |package|
168
- errors += Private.validate_rubocop_todo_yml(package)
169
- errors += Private.validate_rubocop_yml(package)
170
- errors += Private.validate_failure_mode_strict(package)
171
- end
172
-
173
- errors
174
- end
175
32
  end
176
33
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-packs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.39
4
+ version: 0.0.40
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gusto Engineers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-09 00:00:00.000000000 Z
11
+ date: 2023-07-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -201,8 +201,6 @@ extra_rdoc_files: []
201
201
  files:
202
202
  - README.md
203
203
  - config/default.yml
204
- - config/erb_populated_data.yml
205
- - config/pack_config.yml
206
204
  - lib/rubocop-packs.rb
207
205
  - lib/rubocop/cop/packs/class_methods_as_public_apis.rb
208
206
  - lib/rubocop/cop/packs/documented_public_apis.rb
@@ -1,7 +0,0 @@
1
- # We do this inherit *after* setting the defaults so that pack-specific rubocops can override the defaults
2
- # Relevant documentation:
3
- # - Inheriting config from a gem:
4
- # - https://docs.rubocop.org/rubocop/configuration.html#inheriting-configuration-from-a-dependency-gem
5
- # - ERB in a .rubocop.yml file
6
- # - https://docs.rubocop.org/rubocop/configuration.html#pre-processing
7
- <%= RuboCop::Packs.pack_based_rubocop_config %>
@@ -1,6 +0,0 @@
1
- # Relevant documentation:
2
- # - Inheriting config from a gem:
3
- # - https://docs.rubocop.org/rubocop/configuration.html#inheriting-configuration-from-a-dependency-gem
4
- # - ERB in a .rubocop.yml file
5
- # - https://docs.rubocop.org/rubocop/configuration.html#pre-processing
6
- inherit_from: ./erb_populated_data.yml