rubocop-packs 0.0.39 → 0.0.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d88f485f918ad13a51e6b07c7d2777b3d6ea1f841bd8c5d00a745b0ef1021e44
4
- data.tar.gz: 003f98900ab977a97c55860ca4c8dfc024818a249afdfc43c76aecf17383e639
3
+ metadata.gz: cbc3a1521c8e2f461543c85b73db1ff0dbe3e70b20a2c4903a224b2f4f8daa7e
4
+ data.tar.gz: bb5f0503a0fe53fdb20eaaeeb64092916a19338e0d4755ebb2bafe1ef997fe41
5
5
  SHA512:
6
- metadata.gz: 4df9a6bd0d19b1824cf6927e1fe1076b36042bde212a9859dde4912e2af5e49af53bb941e6cc190eec31483962dae009d956d9050b09f5132ee1cf809de5d5f5
7
- data.tar.gz: db92154be004acb720da8ffdba1624dbbfac68a170fc7cbe214a3c5294e7c945ab9ea6308198bbf02ffd1189e823c948632ca086bd3c431b0b6875fb23576c81
6
+ metadata.gz: 0f53c5cd2a4b42c23c3974de5cbfec3b3b13a60f9fb04f98b6417a05a5da25957d40df9084882e0d68b0f02814139f01f00fe4f9800a7b358d01efd83ed16afb
7
+ data.tar.gz: b3c17fd5db8be8175aeaeed10e82c844ed5029a1ba04aca29149a44dd017d91dd7472f224a9335d4e8075674a74c7965b8f308f1e00e8d522c936a9b8d1d9ef4
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).
data/config/default.yml CHANGED
@@ -6,19 +6,15 @@ Packs/ClassMethodsAsPublicApis:
6
6
  - Struct
7
7
  - OpenStruct
8
8
  AcceptableMixins: []
9
- FailureMode: default
10
9
 
11
10
  Packs/RootNamespaceIsPackName:
12
11
  Enabled: false
13
- FailureMode: default
14
12
 
15
13
  Packs/TypedPublicApis:
16
14
  Enabled: false
17
- FailureMode: default
18
15
 
19
16
  Packs/DocumentedPublicApis:
20
17
  Enabled: false
21
- FailureMode: default
22
18
 
23
19
  PackwerkLite/Privacy:
24
20
  # It is recommended to use packwerk
@@ -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.41
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-26 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