use_packwerk 0.55.0 → 0.57.0

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.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/bin/packs +5 -0
  3. data/lib/use_packwerk/cli.rb +10 -10
  4. data/lib/use_packwerk/code_ownership_post_processor.rb +3 -3
  5. data/lib/use_packwerk/configuration.rb +18 -14
  6. data/lib/use_packwerk/default_user_event_logger.rb +7 -0
  7. data/lib/use_packwerk/logging.rb +1 -2
  8. data/lib/use_packwerk/per_file_processor_interface.rb +1 -2
  9. data/lib/use_packwerk/private/file_move_operation.rb +9 -11
  10. data/lib/use_packwerk/private/interactive_cli/pack_selector.rb +34 -0
  11. data/lib/use_packwerk/private/interactive_cli/team_selector.rb +35 -0
  12. data/lib/use_packwerk/private/interactive_cli/use_cases/add_dependency.rb +30 -0
  13. data/lib/use_packwerk/private/interactive_cli/use_cases/create.rb +27 -0
  14. data/lib/use_packwerk/private/interactive_cli/use_cases/get_info.rb +74 -0
  15. data/lib/use_packwerk/private/interactive_cli/use_cases/interface.rb +34 -0
  16. data/lib/use_packwerk/private/interactive_cli/use_cases/make_public.rb +34 -0
  17. data/lib/use_packwerk/private/interactive_cli/use_cases/move.rb +36 -0
  18. data/lib/use_packwerk/private/interactive_cli/use_cases/nest.rb +31 -0
  19. data/lib/use_packwerk/private/interactive_cli/use_cases/query.rb +51 -0
  20. data/lib/use_packwerk/private/interactive_cli/use_cases/rename.rb +34 -0
  21. data/lib/use_packwerk/private/interactive_cli/use_cases/update_deprecations.rb +25 -0
  22. data/lib/use_packwerk/private/interactive_cli/use_cases/validate.rb +25 -0
  23. data/lib/use_packwerk/private/interactive_cli.rb +48 -0
  24. data/lib/use_packwerk/private/pack_relationship_analyzer.rb +24 -108
  25. data/lib/use_packwerk/private.rb +126 -161
  26. data/lib/use_packwerk/rubocop_post_processor.rb +17 -17
  27. data/lib/use_packwerk/user_event_logger.rb +265 -0
  28. data/lib/use_packwerk.rb +38 -76
  29. metadata +74 -14
@@ -0,0 +1,265 @@
1
+ # typed: strict
2
+
3
+ module UsePackwerk
4
+ module UserEventLogger
5
+ extend T::Sig
6
+ extend T::Helpers
7
+
8
+ abstract!
9
+
10
+ sig { params(pack_name: String).returns(String) }
11
+ def before_create_pack(pack_name)
12
+ <<~MSG
13
+ You are creating a pack, which is great. Check out #{documentation_link} for more info!
14
+ MSG
15
+ end
16
+
17
+ sig { params(pack_name: String).returns(String) }
18
+ def after_create_pack(pack_name)
19
+ <<~MSG
20
+ Your next steps might be:
21
+
22
+ 1) Move files into your pack with `bin/use_packwerk move #{pack_name} path/to/file.rb`
23
+
24
+ 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.
25
+
26
+ 3) Update TODO lists for rubocop implemented protections. See #{documentation_link} for more info
27
+
28
+ 4) Expose public API in #{pack_name}/app/public. Try `bin/use_packwerk make_public #{pack_name}/path/to/file.rb`
29
+
30
+ 5) Update your readme at #{pack_name}/README.md
31
+ MSG
32
+ end
33
+
34
+ sig { params(pack_name: String).returns(String) }
35
+ def before_move_to_pack(pack_name)
36
+ <<~MSG
37
+ You are moving a file to a pack, which is great. Check out #{documentation_link} for more info!
38
+ MSG
39
+ end
40
+
41
+ sig { params(pack_name: String).returns(String) }
42
+ def after_move_to_pack(pack_name)
43
+ <<~MSG
44
+ Your next steps might be:
45
+
46
+ 1) 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.
47
+
48
+ 2) Update TODO lists for rubocop implemented protections. See #{documentation_link} for more info
49
+
50
+ 3) Touch base with each team who owns files involved in this move
51
+
52
+ 4) Expose public API in #{pack_name}/app/public. Try `bin/use_packwerk make_public #{pack_name}/path/to/file.rb`
53
+
54
+ 5) Update your readme at #{pack_name}/README.md
55
+ MSG
56
+ end
57
+
58
+ sig { returns(String) }
59
+ def before_make_public
60
+ <<~MSG
61
+ You are moving some files into public API. See #{documentation_link} for other utilities!
62
+ MSG
63
+ end
64
+
65
+ sig { returns(String) }
66
+ def after_make_public
67
+ <<~MSG
68
+ Your next steps might be:
69
+
70
+ 1) 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.
71
+
72
+ 2) Update TODO lists for rubocop implemented protections. See #{documentation_link} for more info
73
+
74
+ 3) Work to migrate clients of private API to your new public API
75
+
76
+ 4) Update your README at packs/your_package_name/README.md
77
+ MSG
78
+ end
79
+
80
+ sig { params(pack_name: String).returns(String) }
81
+ def before_add_dependency(pack_name)
82
+ <<~MSG
83
+ You are adding a dependency. See #{documentation_link} for other utilities!
84
+ MSG
85
+ end
86
+
87
+ sig { params(pack_name: String).returns(String) }
88
+ def after_add_dependency(pack_name)
89
+ <<~MSG
90
+ Your next steps might be:
91
+
92
+ 1) Run `bin/packwerk validate` to ensure you haven't introduced a cyclic dependency
93
+
94
+ 2) Run `bin/packwerk update-deprecations` to update the violations.
95
+ MSG
96
+ end
97
+
98
+ sig { params(pack_name: String).returns(String) }
99
+ def before_move_to_parent(pack_name)
100
+ <<~MSG
101
+ You are moving one pack to be a child of a different pack. Check out #{documentation_link} for more info!
102
+ MSG
103
+ end
104
+
105
+ sig { params(pack_name: String).returns(String) }
106
+ def after_move_to_parent(pack_name)
107
+ <<~MSG
108
+ Your next steps might be:
109
+
110
+ 1) Delete the old pack when things look good: `rm -rf #{pack_name}`
111
+
112
+ 2) Run `bin/packwerk update-deprecations` to update the violations. Make sure to run `spring stop` first.
113
+ MSG
114
+ end
115
+
116
+ sig { params(pack_name: String).returns(String) }
117
+ def on_create_public_directory_todo(pack_name)
118
+ <<~MSG
119
+ This directory holds your public API!
120
+
121
+ Any classes, constants, or modules that you want other packs to use and you intend to support should go in here.
122
+ Anything that is considered private should go in other folders.
123
+
124
+ If another pack uses classes, constants, or modules that are not in your public folder, it will be considered a "privacy violation" by packwerk.
125
+ You can prevent other packs from using private API by using package_protections.
126
+
127
+ Want to find how your private API is being used today?
128
+ Try running: `bin/use_packwerk list_top_privacy_violations #{pack_name}`
129
+
130
+ Want to move something into this folder?
131
+ Try running: `bin/use_packwerk make_public #{pack_name}/path/to/file.rb`
132
+
133
+ One more thing -- feel free to delete this file and replace it with a README.md describing your package in the main package directory.
134
+
135
+ See #{documentation_link} for more info!
136
+ MSG
137
+ end
138
+
139
+ sig { params(pack_name: String).returns(String) }
140
+ def on_create_readme_todo(pack_name)
141
+ <<~MSG
142
+ Welcome to `#{pack_name}`!
143
+
144
+ If you're the author, please consider replacing this file with a README.md, which may contain:
145
+ - What your pack is and does
146
+ - How you expect people to use your pack
147
+ - Example usage of your pack's public API (which lives in `#{pack_name}/app/public`)
148
+ - Limitations, risks, and important considerations of usage
149
+ - 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 `#{pack_name}/package.yml` under the `owner` metadata key)
150
+ - What SLAs/SLOs (service level agreements/objectives), if any, your package provides
151
+ - When in doubt, keep it simple
152
+ - Anything else you may want to include!
153
+
154
+ README.md files are under version control and should change as your public API changes.#{' '}
155
+
156
+ See #{documentation_link} for more info!
157
+ MSG
158
+ end
159
+
160
+ sig { params(pack_name: T.nilable(String), limit: Integer).returns(String) }
161
+ def before_list_top_dependency_violations(pack_name, limit)
162
+ if pack_name.nil?
163
+ pack_specific_content = <<~PACK_CONTENT
164
+ You are listing top #{limit} dependency violations for all packs. See #{documentation_link} for other utilities!
165
+ Pass in a limit to display more or less, e.g. `use_packwerk list_top_dependency_violations #{pack_name} -l 1000`
166
+
167
+ This script is intended to help you find which of YOUR pack's private classes, constants, or modules other packs are using the most.
168
+ Anything not in pack_name/app/public is considered private API.
169
+ PACK_CONTENT
170
+ else
171
+ pack_specific_content = <<~PACK_CONTENT
172
+ You are listing top #{limit} dependency violations for #{pack_name}. See #{documentation_link} for other utilities!
173
+ Pass in a limit to display more or less, e.g. `bin/use_packwerk list_top_dependency_violations #{pack_name} -l 1000`
174
+
175
+ This script is intended to help you find which of YOUR pack's private classes, constants, or modules other packs are using the most.
176
+ Anything not in #{pack_name}/app/public is considered private API.
177
+ PACK_CONTENT
178
+ end
179
+
180
+ <<~MSG
181
+ #{pack_specific_content}
182
+
183
+ When using this script, ask yourself some questions like:
184
+ - What do I want to support?
185
+ - What do I *not* want to support?
186
+ - Which direction should a dependency go?
187
+ - What packs should depend on you, and what packs should not depend on you?
188
+ - Would it be simpler if other packs only depended on interfaces to your pack rather than implementation?
189
+
190
+ Looking at dependency violations can help guide the development of your public API, but it is just the beginning!
191
+
192
+ The script will output in the following format:
193
+
194
+ SomeConstant # This is the name of a class, constant, or module defined in your pack, outside of app/public
195
+ - Total Count: 5 # This is the total number of unstated uses of this outside your pack
196
+ - By package: # This is a breakdown of the use of this constant by other packages
197
+ # This is the number of files in this pack that this constant is used.
198
+ # Check `packs/other_pack_a/deprecated_references.yml` under the '#{pack_name}'.'SomeConstant' key to see where this constant is used
199
+ - packs/other_pack_a: 3
200
+ - packs/other_pack_b: 2
201
+ SomeClass # This is the second most violated class, constant, or module defined in your pack
202
+ - Total Count: 2
203
+ - By package:
204
+ - packs/other_pack_a: 1
205
+ - packs/other_pack_b: 1
206
+ MSG
207
+ end
208
+
209
+ sig { params(pack_name: T.nilable(String), limit: Integer).returns(String) }
210
+ def before_list_top_privacy_violations(pack_name, limit)
211
+ if pack_name.nil?
212
+ pack_specific_content = <<~PACK_CONTENT
213
+ You are listing top #{limit} privacy violations for all packs. See #{documentation_link} for other utilities!
214
+ Pass in a limit to display more or less, e.g. `bin/use_packwerk list_top_privacy_violations #{pack_name} -l 1000`
215
+
216
+ This script is intended to help you find which of YOUR pack's private classes, constants, or modules other packs are using the most.
217
+ Anything not in pack_name/app/public is considered private API.
218
+ PACK_CONTENT
219
+ else
220
+ pack_specific_content = <<~PACK_CONTENT
221
+ You are listing top #{limit} privacy violations for #{pack_name}. See #{documentation_link} for other utilities!
222
+ Pass in a limit to display more or less, e.g. `bin/use_packwerk list_top_privacy_violations #{pack_name} -l 1000`
223
+
224
+ This script is intended to help you find which of YOUR pack's private classes, constants, or modules other packs are using the most.
225
+ Anything not in #{pack_name}/app/public is considered private API.
226
+ PACK_CONTENT
227
+ end
228
+
229
+ <<~MSG
230
+ #{pack_specific_content}
231
+
232
+ When using this script, ask yourself some questions like:
233
+ - What do I want to support?
234
+ - What do I *not* want to support?
235
+ - What is considered simply an implementation detail, and what is essential to the behavior of my pack?
236
+ - What is a simple, minimialistic API for clients to engage with the behavior of your pack?
237
+ - How do I ensure my public API is not coupled to specific client's use cases?
238
+
239
+ Looking at privacy violations can help guide the development of your public API, but it is just the beginning!
240
+
241
+ The script will output in the following format:
242
+
243
+ SomeConstant # This is the name of a class, constant, or module defined in your pack, outside of app/public
244
+ - Total Count: 5 # This is the total number of uses of this outside your pack
245
+ - By package: # This is a breakdown of the use of this constant by other packages
246
+ # This is the number of files in this pack that this constant is used.
247
+ # Check `packs/other_pack_a/deprecated_references.yml` under the '#{pack_name}'.'SomeConstant' key to see where this constant is used
248
+ - packs/other_pack_a: 3
249
+ - packs/other_pack_b: 2
250
+ SomeClass # This is the second most violated class, constant, or module defined in your pack
251
+ - Total Count: 2
252
+ - By package:
253
+ - packs/other_pack_a: 1
254
+ - packs/other_pack_b: 1
255
+
256
+ Lastly, remember you can use `bin/use_packwerk make_public #{pack_name}/path/to/file.rb` to make your class, constant, or module public API.
257
+ MSG
258
+ end
259
+
260
+ sig { returns(String) }
261
+ def documentation_link
262
+ 'https://github.com/rubyatscale/use_packwerk#readme'
263
+ end
264
+ end
265
+ end
data/lib/use_packwerk.rb CHANGED
@@ -24,28 +24,36 @@ require 'use_packwerk/cli'
24
24
  module UsePackwerk
25
25
  extend T::Sig
26
26
 
27
- PERMITTED_PACK_LOCATIONS = T.let([
28
- 'gems',
29
- 'components',
30
- 'packs',
31
- ], T::Array[String])
27
+ PERMITTED_PACK_LOCATIONS = T.let(%w[
28
+ gems
29
+ components
30
+ packs
31
+ ], T::Array[String])
32
+
33
+ sig { void }
34
+ def self.start_interactive_mode!
35
+ Private::InteractiveCli.start!
36
+ end
32
37
 
33
38
  sig do
34
39
  params(
35
40
  pack_name: String,
36
41
  enforce_privacy: T::Boolean,
37
- enforce_dependencies: T.nilable(T::Boolean)
42
+ enforce_dependencies: T.nilable(T::Boolean),
43
+ team: T.nilable(CodeTeams::Team)
38
44
  ).void
39
45
  end
40
46
  def self.create_pack!(
41
47
  pack_name:,
42
48
  enforce_privacy: true,
43
- enforce_dependencies: nil
49
+ enforce_dependencies: nil,
50
+ team: nil
44
51
  )
45
52
  Private.create_pack!(
46
53
  pack_name: pack_name,
47
54
  enforce_privacy: enforce_privacy,
48
- enforce_dependencies: enforce_dependencies,
55
+ enforce_dependencies: enforce_dependencies,
56
+ team: team
49
57
  )
50
58
  end
51
59
 
@@ -53,7 +61,7 @@ module UsePackwerk
53
61
  params(
54
62
  pack_name: String,
55
63
  paths_relative_to_root: T::Array[String],
56
- per_file_processors: T::Array[PerFileProcessorInterface],
64
+ per_file_processors: T::Array[PerFileProcessorInterface]
57
65
  ).void
58
66
  end
59
67
  def self.move_to_pack!(
@@ -62,36 +70,18 @@ module UsePackwerk
62
70
  per_file_processors: []
63
71
  )
64
72
  Logging.section('👋 Hi!') do
65
- intro = <<~INTRO
66
- You are moving a file to a pack, which is great. Check out #{UsePackwerk.config.documentation_link} for more info!
67
-
68
- Please bring any questions or issues you have in your development process to #ruby-modularity or #product-infrastructure.
69
- We'd be happy to try to help through pairing, accepting feedback, changing our process, changing our tools, and more.
70
- INTRO
73
+ intro = UsePackwerk.config.user_event_logger.before_move_to_pack(pack_name)
71
74
  Logging.print_bold_green(intro)
72
75
  end
73
76
 
74
77
  Private.move_to_pack!(
75
78
  pack_name: pack_name,
76
79
  paths_relative_to_root: paths_relative_to_root,
77
- per_file_processors: per_file_processors,
80
+ per_file_processors: per_file_processors
78
81
  )
79
82
 
80
83
  Logging.section('Next steps') do
81
- next_steps = <<~NEXT_STEPS
82
- Your next steps might be:
83
-
84
- 1) 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.
85
-
86
- 2) Update TODO lists for rubocop implemented protections. See #{UsePackwerk.config.documentation_link} for more info
87
-
88
- 3) Touch base with each team who owns files involved in this move
89
-
90
- 4) Expose public API in #{pack_name}/app/public. Try `bin/use_packwerk make_public #{pack_name}/path/to/file.rb`
91
-
92
- 5) Update your readme at #{pack_name}/README.md
93
- NEXT_STEPS
94
-
84
+ next_steps = UsePackwerk.config.user_event_logger.after_move_to_pack(pack_name)
95
85
  Logging.print_bold_green(next_steps)
96
86
  end
97
87
  end
@@ -99,7 +89,7 @@ module UsePackwerk
99
89
  sig do
100
90
  params(
101
91
  paths_relative_to_root: T::Array[String],
102
- per_file_processors: T::Array[PerFileProcessorInterface],
92
+ per_file_processors: T::Array[PerFileProcessorInterface]
103
93
  ).void
104
94
  end
105
95
  def self.make_public!(
@@ -107,9 +97,7 @@ module UsePackwerk
107
97
  per_file_processors: []
108
98
  )
109
99
  Logging.section('Making files public') do
110
- intro = <<~INTRO
111
- You are moving some files into public API. See #{UsePackwerk.config.documentation_link} for other utilities!
112
- INTRO
100
+ intro = UsePackwerk.config.user_event_logger.before_make_public
113
101
  Logging.print_bold_green(intro)
114
102
  end
115
103
 
@@ -117,20 +105,9 @@ module UsePackwerk
117
105
  paths_relative_to_root: paths_relative_to_root,
118
106
  per_file_processors: per_file_processors
119
107
  )
120
-
121
- Logging.section('Next steps') do
122
- next_steps = <<~NEXT_STEPS
123
- Your next steps might be:
124
-
125
- 1) 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.
126
-
127
- 2) Update TODO lists for rubocop implemented protections. See #{UsePackwerk.config.documentation_link} for more info
128
-
129
- 3) Work to migrate clients of private API to your new public API
130
-
131
- 4) Update your README at packs/your_package_name/README.md
132
- NEXT_STEPS
133
108
 
109
+ Logging.section('Next steps') do
110
+ next_steps = UsePackwerk.config.user_event_logger.after_make_public
134
111
  Logging.print_bold_green(next_steps)
135
112
  end
136
113
  end
@@ -146,9 +123,7 @@ module UsePackwerk
146
123
  dependency_name:
147
124
  )
148
125
  Logging.section('Adding a dependency') do
149
- intro = <<~INTRO
150
- You are adding a dependency. See #{UsePackwerk.config.documentation_link} for other utilities!
151
- INTRO
126
+ intro = UsePackwerk.config.user_event_logger.before_add_dependency(pack_name)
152
127
  Logging.print_bold_green(intro)
153
128
  end
154
129
 
@@ -158,14 +133,7 @@ module UsePackwerk
158
133
  )
159
134
 
160
135
  Logging.section('Next steps') do
161
- next_steps = <<~NEXT_STEPS
162
- Your next steps might be:
163
-
164
- 1) Run `bin/packwerk validate` to ensure you haven't introduced a cyclic dependency
165
-
166
- 2) Run `bin/packwerk update-deprecations` to update the violations.
167
- NEXT_STEPS
168
-
136
+ next_steps = UsePackwerk.config.user_event_logger.after_add_dependency(pack_name)
169
137
  Logging.print_bold_green(next_steps)
170
138
  end
171
139
  end
@@ -174,7 +142,7 @@ module UsePackwerk
174
142
  params(
175
143
  pack_name: String,
176
144
  parent_name: String,
177
- per_file_processors: T::Array[PerFileProcessorInterface],
145
+ per_file_processors: T::Array[PerFileProcessorInterface]
178
146
  ).void
179
147
  end
180
148
  def self.move_to_parent!(
@@ -183,29 +151,18 @@ module UsePackwerk
183
151
  per_file_processors: []
184
152
  )
185
153
  Logging.section('👋 Hi!') do
186
- intro = <<~INTRO
187
- You are moving one pack to be a child of a different pack. Check out #{UsePackwerk.config.documentation_link} for more info!
188
-
189
- Please bring any questions or issues you have in your development process to #ruby-modularity or #product-infrastructure.
190
- We'd be happy to try to help through pairing, accepting feedback, changing our process, changing our tools, and more.
191
- INTRO
154
+ intro = UsePackwerk.config.user_event_logger.before_move_to_parent(pack_name)
192
155
  Logging.print_bold_green(intro)
193
156
  end
194
157
 
195
158
  Private.move_to_parent!(
196
159
  pack_name: pack_name,
197
160
  parent_name: parent_name,
198
- per_file_processors: per_file_processors,
161
+ per_file_processors: per_file_processors
199
162
  )
200
163
 
201
164
  Logging.section('Next steps') do
202
- next_steps = <<~NEXT_STEPS
203
- Your next steps might be:
204
-
205
- 1) Delete the old pack when things look good: `rm -rf #{pack_name}`
206
-
207
- 2) Run `bin/packwerk update-deprecations` to update the violations. Make sure to run `spring stop` first.
208
- NEXT_STEPS
165
+ next_steps = UsePackwerk.config.user_event_logger.after_move_to_parent(pack_name)
209
166
 
210
167
  Logging.print_bold_green(next_steps)
211
168
  end
@@ -214,7 +171,7 @@ module UsePackwerk
214
171
  sig do
215
172
  params(
216
173
  pack_name: T.nilable(String),
217
- limit: Integer,
174
+ limit: Integer
218
175
  ).void
219
176
  end
220
177
  def self.list_top_privacy_violations(
@@ -247,14 +204,19 @@ module UsePackwerk
247
204
  params(
248
205
  file: String,
249
206
  find: Pathname,
250
- replace_with: Pathname,
207
+ replace_with: Pathname
251
208
  ).void
252
209
  end
253
210
  def self.replace_in_file(file:, find:, replace_with:)
254
211
  Private.replace_in_file(
255
212
  file: file,
256
213
  find: find,
257
- replace_with: replace_with,
214
+ replace_with: replace_with
258
215
  )
259
216
  end
217
+
218
+ sig { void }
219
+ def self.bust_cache!
220
+ Private.bust_cache!
221
+ end
260
222
  end