use_packwerk 0.54.0 → 0.56.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,25 +18,26 @@ module UsePackwerk
18
18
  # This results in the pack not being found, but when we write the package YML it writes to the same place,
19
19
  # causing a behaviorally confusing diff.
20
20
  # We ignore trailing slashes as an ergonomic feature to the user.
21
- pack_name.gsub(/\/$/, '')
21
+ pack_name.gsub(%r{/$}, '')
22
22
  end
23
23
 
24
24
  sig do
25
25
  params(
26
26
  file: String,
27
27
  find: Pathname,
28
- replace_with: Pathname,
28
+ replace_with: Pathname
29
29
  ).void
30
30
  end
31
31
  def self.replace_in_file(file:, find:, replace_with:)
32
32
  file = Pathname.new(file)
33
33
  return if !file.exist?
34
+
34
35
  count = 0
35
36
  file.write(file.read.gsub(find.to_s) do
36
37
  count += 1
37
38
  replace_with.to_s
38
39
  end)
39
- Logging.print "Replaced #{count} occurrence(s) of #{find} in #{file.to_s}" if count > 0
40
+ Logging.print "Replaced #{count} occurrence(s) of #{find} in #{file}" if count > 0
40
41
  end
41
42
 
42
43
  sig do
@@ -48,12 +49,7 @@ module UsePackwerk
48
49
  end
49
50
  def self.create_pack!(pack_name:, enforce_privacy:, enforce_dependencies:)
50
51
  Logging.section('👋 Hi!') do
51
- intro = <<~INTRO
52
- You are creating a pack, which is great. Check out #{UsePackwerk.config.documentation_link} for more info!
53
-
54
- Please bring any questions or issues you have in your development process to #ruby-modularity or #product-infrastructure.
55
- We'd be happy to try to help through pairing, accepting feedback, changing our process, changing our tools, and more.
56
- INTRO
52
+ intro = UsePackwerk.config.user_event_logger.before_create_pack(pack_name)
57
53
  Logging.print_bold_green(intro)
58
54
  end
59
55
 
@@ -64,19 +60,7 @@ module UsePackwerk
64
60
  add_readme_todo(package)
65
61
 
66
62
  Logging.section('Next steps') do
67
- next_steps = <<~NEXT_STEPS
68
- Your next steps might be:
69
-
70
- 1) Move files into your pack with `bin/use_packwerk move #{pack_name} path/to/file.rb`
71
-
72
- 2) Run `bin/packwerk update-deprecations` to update the violations. Make sure to run `spring stop` if you've added new load paths (new top-level directories) in your pack.
73
-
74
- 3) Update TODO lists for rubocop implemented protections. See #{UsePackwerk.config.documentation_link} for more info
75
-
76
- 4) Expose public API in #{pack_name}/app/public. Try `bin/use_packwerk make_public #{pack_name}/path/to/file.rb`
77
-
78
- 5) Update your readme at #{pack_name}/README.md
79
- NEXT_STEPS
63
+ next_steps = UsePackwerk.config.user_event_logger.after_create_pack(pack_name)
80
64
 
81
65
  Logging.print_bold_green(next_steps)
82
66
  end
@@ -91,9 +75,9 @@ module UsePackwerk
91
75
  end
92
76
  def self.move_to_pack!(pack_name:, paths_relative_to_root:, per_file_processors: [])
93
77
  pack_name = Private.clean_pack_name(pack_name)
94
- package = ParsePackwerk.all.find { |package| package.name == pack_name }
78
+ package = ParsePackwerk.all.find { |p| p.name == pack_name }
95
79
  if package.nil?
96
- raise StandardError.new("Can not find package with name #{pack_name}. Make sure the argument is of the form `packs/my_pack/`")
80
+ raise StandardError, "Can not find package with name #{pack_name}. Make sure the argument is of the form `packs/my_pack/`"
97
81
  end
98
82
 
99
83
  add_public_directory(package)
@@ -118,9 +102,9 @@ module UsePackwerk
118
102
  # Later, if we choose to go back to moving whole directories at a time, it should be a refactor and all tests should still pass
119
103
  #
120
104
  if origin_pathname.directory?
121
- origin_pathname.glob('**/*.*').reject do |path|
122
- path.to_s.include?(ParsePackwerk::PACKAGE_YML_NAME) ||
123
- path.to_s.include?(ParsePackwerk::DEPRECATED_REFERENCES_YML_NAME)
105
+ origin_pathname.glob('**/*.*').reject do |origin_path|
106
+ origin_path.to_s.include?(ParsePackwerk::PACKAGE_YML_NAME) ||
107
+ origin_path.to_s.include?(ParsePackwerk::DEPRECATED_REFERENCES_YML_NAME)
124
108
  end
125
109
  else
126
110
  origin_pathname
@@ -130,7 +114,7 @@ module UsePackwerk
130
114
  FileMoveOperation.new(
131
115
  origin_pathname: origin_pathname,
132
116
  destination_pathname: FileMoveOperation.destination_pathname_for_package_move(origin_pathname, package_location),
133
- destination_pack: package,
117
+ destination_pack: package
134
118
  )
135
119
  end
136
120
  file_move_operations.each do |file_move_operation|
@@ -140,89 +124,87 @@ module UsePackwerk
140
124
  end
141
125
  end
142
126
 
143
- per_file_processors.each do |per_file_processor|
144
- per_file_processor.print_final_message!
145
- end
127
+ per_file_processors.each(&:print_final_message!)
146
128
  end
147
129
 
148
- sig do
149
- params(
150
- pack_name: String,
151
- parent_name: String,
152
- per_file_processors: T::Array[PerFileProcessorInterface],
153
- ).void
154
- end
155
- def self.move_to_parent!(
156
- pack_name:,
157
- parent_name:,
158
- per_file_processors: []
159
- )
160
- pack_name = Private.clean_pack_name(pack_name)
161
- package = ParsePackwerk.all.find { |package| package.name == pack_name }
162
- if package.nil?
163
- raise StandardError.new("Can not find package with name #{pack_name}. Make sure the argument is of the form `packs/my_pack/`")
164
- end
165
-
166
- parent_name = Private.clean_pack_name(parent_name)
167
- parent_package = ParsePackwerk.all.find { |package| package.name == parent_name }
168
- if parent_package.nil?
169
- parent_package = create_pack_if_not_exists!(pack_name: parent_name, enforce_privacy: true, enforce_dependencies: true)
130
+ sig do
131
+ params(
132
+ pack_name: String,
133
+ parent_name: String,
134
+ per_file_processors: T::Array[PerFileProcessorInterface]
135
+ ).void
170
136
  end
171
-
172
- # First we create a new pack that has the exact same properties of the old one!
173
- package_last_name = package.directory.basename
174
- new_package_name = parent_package.directory.join(package_last_name).to_s
175
-
176
- new_package = ParsePackwerk::Package.new(
177
- name: new_package_name,
178
- enforce_privacy: package.enforce_dependencies,
179
- enforce_dependencies: package.enforce_dependencies,
180
- dependencies: package.dependencies,
181
- metadata: package.metadata,
182
- )
183
- ParsePackwerk.write_package_yml!(new_package)
184
- ParsePackwerk.bust_cache!
185
-
186
- # Move everything from the old pack to the new one
187
- self.move_to_pack!(
188
- pack_name: new_package_name,
189
- paths_relative_to_root: [package.directory.to_s],
190
- per_file_processors: per_file_processors,
137
+ def self.move_to_parent!(
138
+ pack_name:,
139
+ parent_name:,
140
+ per_file_processors: []
191
141
  )
142
+ pack_name = Private.clean_pack_name(pack_name)
143
+ package = ParsePackwerk.all.find { |p| p.name == pack_name }
144
+ if package.nil?
145
+ raise StandardError, "Can not find package with name #{pack_name}. Make sure the argument is of the form `packs/my_pack/`"
146
+ end
192
147
 
193
- # Then delete the old package.yml and deprecated_references.yml files
194
- package.yml.delete
195
- deprecated_references_file = ParsePackwerk::DeprecatedReferences.for(package).pathname
196
- deprecated_references_file.delete if deprecated_references_file.exist?
148
+ parent_name = Private.clean_pack_name(parent_name)
149
+ parent_package = ParsePackwerk.all.find { |p| p.name == parent_name }
150
+ if parent_package.nil?
151
+ parent_package = create_pack_if_not_exists!(pack_name: parent_name, enforce_privacy: true, enforce_dependencies: true)
152
+ end
197
153
 
198
- ParsePackwerk.bust_cache!
154
+ # First we create a new pack that has the exact same properties of the old one!
155
+ package_last_name = package.directory.basename
156
+ new_package_name = parent_package.directory.join(package_last_name).to_s
199
157
 
200
- ParsePackwerk.all.each do |other_package|
201
- new_dependencies = other_package.dependencies.map{|d| d == pack_name ? new_package_name : d}
202
- if other_package.name == parent_name && !new_dependencies.include?(new_package_name)
203
- new_dependencies += [new_package_name]
204
- end
158
+ new_package = ParsePackwerk::Package.new(
159
+ name: new_package_name,
160
+ enforce_privacy: package.enforce_dependencies,
161
+ enforce_dependencies: package.enforce_dependencies,
162
+ dependencies: package.dependencies,
163
+ metadata: package.metadata
164
+ )
165
+ ParsePackwerk.write_package_yml!(new_package)
166
+ ParsePackwerk.bust_cache!
205
167
 
206
- new_other_package = ParsePackwerk::Package.new(
207
- name: other_package.name,
208
- enforce_privacy: other_package.enforce_dependencies,
209
- enforce_dependencies: other_package.enforce_dependencies,
210
- dependencies: new_dependencies.uniq.sort,
211
- metadata: other_package.metadata,
168
+ # Move everything from the old pack to the new one
169
+ move_to_pack!(
170
+ pack_name: new_package_name,
171
+ paths_relative_to_root: [package.directory.to_s],
172
+ per_file_processors: per_file_processors
212
173
  )
213
174
 
214
- ParsePackwerk.write_package_yml!(new_other_package)
215
- end
175
+ # Then delete the old package.yml and deprecated_references.yml files
176
+ package.yml.delete
177
+ deprecated_references_file = ParsePackwerk::DeprecatedReferences.for(package).pathname
178
+ deprecated_references_file.delete if deprecated_references_file.exist?
216
179
 
217
- sorbet_config = Pathname.new('sorbet/config')
218
- if sorbet_config.exist?
219
- UsePackwerk.replace_in_file(
220
- file: sorbet_config.to_s,
221
- find: package.directory.join('spec'),
222
- replace_with: new_package.directory.join('spec'),
223
- )
180
+ ParsePackwerk.bust_cache!
181
+
182
+ ParsePackwerk.all.each do |other_package|
183
+ new_dependencies = other_package.dependencies.map { |d| d == pack_name ? new_package_name : d }
184
+ if other_package.name == parent_name && !new_dependencies.include?(new_package_name)
185
+ new_dependencies += [new_package_name]
186
+ end
187
+
188
+ new_other_package = ParsePackwerk::Package.new(
189
+ name: other_package.name,
190
+ enforce_privacy: other_package.enforce_privacy,
191
+ enforce_dependencies: other_package.enforce_dependencies,
192
+ dependencies: new_dependencies.uniq.sort,
193
+ metadata: other_package.metadata
194
+ )
195
+
196
+ ParsePackwerk.write_package_yml!(new_other_package)
197
+ end
198
+
199
+ sorbet_config = Pathname.new('sorbet/config')
200
+ if sorbet_config.exist?
201
+ UsePackwerk.replace_in_file(
202
+ file: sorbet_config.to_s,
203
+ find: package.directory.join('spec'),
204
+ replace_with: new_package.directory.join('spec')
205
+ )
206
+ end
224
207
  end
225
- end
226
208
 
227
209
  sig do
228
210
  params(
@@ -242,7 +224,6 @@ module UsePackwerk
242
224
  end
243
225
  end
244
226
 
245
-
246
227
  file_move_operations = file_paths.map do |path|
247
228
  package = T.must(ParsePackwerk.package_from_path(path))
248
229
  origin_pathname = Pathname.new(path).cleanpath
@@ -250,7 +231,7 @@ module UsePackwerk
250
231
  FileMoveOperation.new(
251
232
  origin_pathname: origin_pathname,
252
233
  destination_pathname: FileMoveOperation.destination_pathname_for_new_public_api(origin_pathname),
253
- destination_pack: package,
234
+ destination_pack: package
254
235
  )
255
236
  end
256
237
 
@@ -272,15 +253,15 @@ module UsePackwerk
272
253
  all_packages = ParsePackwerk.all
273
254
 
274
255
  pack_name = Private.clean_pack_name(pack_name)
275
- package = all_packages.find { |package| package.name == pack_name }
256
+ package = all_packages.find { |p| p.name == pack_name }
276
257
  if package.nil?
277
- raise StandardError.new("Can not find package with name #{pack_name}. Make sure the argument is of the form `packs/my_pack/`")
258
+ raise StandardError, "Can not find package with name #{pack_name}. Make sure the argument is of the form `packs/my_pack/`"
278
259
  end
279
260
 
280
261
  dependency_name = Private.clean_pack_name(dependency_name)
281
- package_dependency = all_packages.find { |package| package.name == dependency_name }
262
+ package_dependency = all_packages.find { |p| p.name == dependency_name }
282
263
  if package_dependency.nil?
283
- raise StandardError.new("Can not find package with name #{dependency_name}. Make sure the argument is of the form `packs/my_pack/`")
264
+ raise StandardError, "Can not find package with name #{dependency_name}. Make sure the argument is of the form `packs/my_pack/`"
284
265
  end
285
266
 
286
267
  new_package = ParsePackwerk::Package.new(
@@ -288,7 +269,7 @@ module UsePackwerk
288
269
  dependencies: (package.dependencies + [dependency_name]).uniq.sort,
289
270
  enforce_privacy: package.enforce_privacy,
290
271
  enforce_dependencies: package.enforce_dependencies,
291
- metadata: package.metadata,
272
+ metadata: package.metadata
292
273
  )
293
274
  ParsePackwerk.write_package_yml!(new_package)
294
275
  end
@@ -322,63 +303,29 @@ module UsePackwerk
322
303
  # use git mv so that git knows that it was a move
323
304
  FileUtils.mv(origin, destination)
324
305
  elsif !origin.exist? && destination.exist?
325
- Logging.print ColorizedString.new("[SKIP] Not moving #{origin.to_s}, does not exist, (#{destination.to_s} already exists)").red
306
+ Logging.print ColorizedString.new("[SKIP] Not moving #{origin}, does not exist, (#{destination} already exists)").red
326
307
  else
327
- Logging.print ColorizedString.new("[SKIP] Not moving #{origin.to_s}, does not exist").red
308
+ Logging.print ColorizedString.new("[SKIP] Not moving #{origin}, does not exist").red
328
309
  end
329
310
  end
330
311
 
331
- sig { params(package: ParsePackwerk::Package).void }
312
+ sig { params(package: ParsePackwerk::Package).void }
332
313
  def self.add_public_directory(package)
333
314
  public_directory = package.directory.join('app/public')
334
315
 
335
316
  if public_directory.glob('**/**.rb').none?
336
317
  FileUtils.mkdir_p(public_directory)
337
- todo_md = <<~TODO
338
- This directory holds your public API!
339
-
340
- Any classes, constants, or modules that you want other packs to use and you intend to support should go in here.
341
- Anything that is considered private should go in other folders.
342
-
343
- If another pack uses classes, constants, or modules that are not in your public folder, it will be considered a "privacy violation" by packwerk.
344
- You can prevent other packs from using private API by using package_protections.
345
-
346
- Want to find how your private API is being used today?
347
- Try running: `bin/use_packwerk list_top_privacy_violations #{package.name}`
348
-
349
- Want to move something into this folder?
350
- Try running: `bin/use_packwerk make_public #{package.name}/path/to/file.rb`
351
-
352
- One more thing -- feel free to delete this file and replace it with a README.md describing your package in the main package directory.
353
-
354
- See #{UsePackwerk.config.documentation_link} for more info!
355
- TODO
318
+ todo_md = UsePackwerk.config.user_event_logger.on_create_public_directory_todo(package.name)
356
319
  public_directory.join('TODO.md').write(todo_md)
357
320
  end
358
321
  end
359
322
 
360
- sig { params(package: ParsePackwerk::Package).void }
323
+ sig { params(package: ParsePackwerk::Package).void }
361
324
  def self.add_readme_todo(package)
362
325
  pack_directory = package.directory
363
326
 
364
327
  if !pack_directory.join('README.md').exist?
365
- readme_todo_md = <<~TODO
366
- Welcome to `#{package.name}`!
367
-
368
- If you're the author, please consider replacing this file with a README.md, which may contain:
369
- - What your pack is and does
370
- - How you expect people to use your pack
371
- - Example usage of your pack's public API (which lives in `#{package.name}/app/public`)
372
- - Limitations, risks, and important considerations of usage
373
- - How to get in touch with eng and other stakeholders for questions or issues pertaining to this pack (note: it is recommended to add ownership in `#{package.name}/package.yml` under the `owner` metadata key)
374
- - What SLAs/SLOs (service level agreements/objectives), if any, your package provides
375
- - When in doubt, keep it simple
376
- - Anything else you may want to include!
377
-
378
- README.md files are under version control and should change as your public API changes.
379
-
380
- See #{UsePackwerk.config.documentation_link} for more info!
381
- TODO
328
+ readme_todo_md = UsePackwerk.config.user_event_logger.on_create_readme_todo(package.name)
382
329
  pack_directory.join('README_TODO.md').write(readme_todo_md)
383
330
  end
384
331
  end
@@ -392,13 +339,10 @@ module UsePackwerk
392
339
  end
393
340
  def self.create_pack_if_not_exists!(pack_name:, enforce_privacy:, enforce_dependencies:)
394
341
  if PERMITTED_PACK_LOCATIONS.none? { |permitted_location| pack_name.start_with?(permitted_location) }
395
- raise StandardError.new("UsePackwerk only supports packages in the the following directories: #{PERMITTED_PACK_LOCATIONS.inspect}. Please make sure to pass in the name of the pack including the full directory path, e.g. `packs/my_pack`.")
342
+ raise StandardError, "UsePackwerk only supports packages in the the following directories: #{PERMITTED_PACK_LOCATIONS.inspect}. Please make sure to pass in the name of the pack including the full directory path, e.g. `packs/my_pack`."
396
343
  end
397
344
 
398
- existing_package = ParsePackwerk.all.find { |package| package.name == pack_name }
399
-
400
- package_location = Pathname.new(pack_name)
401
-
345
+ existing_package = ParsePackwerk.all.find { |p| p.name == pack_name }
402
346
  if existing_package.nil?
403
347
  should_enforce_dependenceies = enforce_dependencies.nil? ? UsePackwerk.config.enforce_dependencies : enforce_dependencies
404
348
 
@@ -409,7 +353,7 @@ module UsePackwerk
409
353
  metadata: {
410
354
  'owner' => 'MyTeam'
411
355
  },
412
- name: pack_name,
356
+ name: pack_name
413
357
  )
414
358
 
415
359
  ParsePackwerk.write_package_yml!(package)
@@ -417,7 +361,7 @@ module UsePackwerk
417
361
  package = rewrite_package_with_original_packwerk_values(package)
418
362
 
419
363
  current_contents = package.yml.read
420
- new_contents = current_contents.gsub("MyTeam", "MyTeam # specify your team here, or delete this key if this package is not owned by one team")
364
+ new_contents = current_contents.gsub('MyTeam', 'MyTeam # specify your team here, or delete this key if this package is not owned by one team')
421
365
  package.yml.write(new_contents)
422
366
  existing_package = package
423
367
  end
@@ -428,19 +372,37 @@ module UsePackwerk
428
372
  sig { params(original_package: ParsePackwerk::Package).returns(ParsePackwerk::Package) }
429
373
  def self.rewrite_package_with_original_packwerk_values(original_package)
430
374
  ParsePackwerk.bust_cache!
431
- package_with_protection_defaults = T.must(ParsePackwerk.all.find { |package| package.name == original_package.name })
375
+ package_with_protection_defaults = T.must(ParsePackwerk.all.find { |p| p.name == original_package.name })
432
376
  # PackageProtections also sets `enforce_privacy` and `enforce_dependency` to be true, so we set these back down to their original values
433
377
  package = ParsePackwerk::Package.new(
434
378
  enforce_dependencies: original_package.enforce_dependencies,
435
379
  enforce_privacy: original_package.enforce_privacy,
436
380
  dependencies: original_package.dependencies,
437
381
  metadata: package_with_protection_defaults.metadata,
438
- name: original_package.name,
382
+ name: original_package.name
439
383
  )
440
384
 
441
385
  ParsePackwerk.write_package_yml!(package)
442
386
  package
443
387
  end
388
+
389
+ sig { void }
390
+ def self.load_client_configuration
391
+ @loaded_client_configuration ||= T.let(false, T.nilable(T::Boolean))
392
+ return if @loaded_client_configuration
393
+
394
+ @loaded_client_configuration = true
395
+ client_configuration = Pathname.pwd.join('config/use_packwerk.rb')
396
+ require client_configuration.to_s if client_configuration.exist?
397
+ end
398
+
399
+ sig { void }
400
+ def self.bust_cache!
401
+ UsePackwerk.config.bust_cache!
402
+ # This comes explicitly after `PackageProtections.config.bust_cache!` because
403
+ # otherwise `PackageProtections.config` will attempt to reload the client configuratoin.
404
+ @loaded_client_configuration = false
405
+ end
444
406
  end
445
407
 
446
408
  private_constant :Private
@@ -15,7 +15,7 @@ module UsePackwerk
15
15
  UsePackwerk.replace_in_file(
16
16
  file: rubocop_todo.to_s,
17
17
  find: relative_path_to_origin,
18
- replace_with: relative_path_to_destination,
18
+ replace_with: relative_path_to_destination
19
19
  )
20
20
  end
21
21
 
@@ -26,23 +26,23 @@ module UsePackwerk
26
26
  new_origin_rubocop_todo = loaded_origin_rubocop_todo.dup
27
27
 
28
28
  loaded_origin_rubocop_todo.each do |cop_name, cop_config|
29
- if cop_config['Exclude'].include?(relative_path_to_origin.to_s)
30
- new_origin_rubocop_todo[cop_name]['Exclude'] = cop_config['Exclude'] - [relative_path_to_origin.to_s]
31
- origin_rubocop_todo.write(YAML.dump(new_origin_rubocop_todo))
32
-
33
- destination_rubocop_todo = file_move_operation.destination_pack.directory.join('.rubocop_todo.yml')
34
- if destination_rubocop_todo.exist?
35
- new_destination_rubocop_todo = YAML.load_file(destination_rubocop_todo).dup
36
- else
37
- new_destination_rubocop_todo = {}
38
- end
39
-
40
- new_destination_rubocop_todo[cop_name] ||= { 'Exclude' => [] }
41
-
42
- new_destination_rubocop_todo[cop_name]['Exclude'] += [relative_path_to_destination.to_s]
43
- destination_rubocop_todo.write(YAML.dump(new_destination_rubocop_todo))
29
+ next unless cop_config['Exclude'].include?(relative_path_to_origin.to_s)
30
+
31
+ new_origin_rubocop_todo[cop_name]['Exclude'] = cop_config['Exclude'] - [relative_path_to_origin.to_s]
32
+ origin_rubocop_todo.write(YAML.dump(new_origin_rubocop_todo))
33
+
34
+ destination_rubocop_todo = file_move_operation.destination_pack.directory.join('.rubocop_todo.yml')
35
+ if destination_rubocop_todo.exist?
36
+ new_destination_rubocop_todo = YAML.load_file(destination_rubocop_todo).dup
37
+ else
38
+ new_destination_rubocop_todo = {}
44
39
  end
45
- end
40
+
41
+ new_destination_rubocop_todo[cop_name] ||= { 'Exclude' => [] }
42
+
43
+ new_destination_rubocop_todo[cop_name]['Exclude'] += [relative_path_to_destination.to_s]
44
+ destination_rubocop_todo.write(YAML.dump(new_destination_rubocop_todo))
45
+ end
46
46
  end
47
47
  end
48
48
  end