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.
- checksums.yaml +4 -4
- data/bin/packs +5 -0
- data/lib/use_packwerk/cli.rb +10 -10
- data/lib/use_packwerk/code_ownership_post_processor.rb +3 -3
- data/lib/use_packwerk/configuration.rb +18 -14
- data/lib/use_packwerk/default_user_event_logger.rb +7 -0
- data/lib/use_packwerk/logging.rb +1 -2
- data/lib/use_packwerk/per_file_processor_interface.rb +1 -2
- data/lib/use_packwerk/private/file_move_operation.rb +9 -11
- data/lib/use_packwerk/private/interactive_cli/pack_selector.rb +34 -0
- data/lib/use_packwerk/private/interactive_cli/team_selector.rb +35 -0
- data/lib/use_packwerk/private/interactive_cli/use_cases/add_dependency.rb +30 -0
- data/lib/use_packwerk/private/interactive_cli/use_cases/create.rb +27 -0
- data/lib/use_packwerk/private/interactive_cli/use_cases/get_info.rb +74 -0
- data/lib/use_packwerk/private/interactive_cli/use_cases/interface.rb +34 -0
- data/lib/use_packwerk/private/interactive_cli/use_cases/make_public.rb +34 -0
- data/lib/use_packwerk/private/interactive_cli/use_cases/move.rb +36 -0
- data/lib/use_packwerk/private/interactive_cli/use_cases/nest.rb +31 -0
- data/lib/use_packwerk/private/interactive_cli/use_cases/query.rb +51 -0
- data/lib/use_packwerk/private/interactive_cli/use_cases/rename.rb +34 -0
- data/lib/use_packwerk/private/interactive_cli/use_cases/update_deprecations.rb +25 -0
- data/lib/use_packwerk/private/interactive_cli/use_cases/validate.rb +25 -0
- data/lib/use_packwerk/private/interactive_cli.rb +48 -0
- data/lib/use_packwerk/private/pack_relationship_analyzer.rb +24 -108
- data/lib/use_packwerk/private.rb +126 -161
- data/lib/use_packwerk/rubocop_post_processor.rb +17 -17
- data/lib/use_packwerk/user_event_logger.rb +265 -0
- data/lib/use_packwerk.rb +38 -76
- metadata +74 -14
@@ -0,0 +1,51 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module UsePackwerk
|
4
|
+
module Private
|
5
|
+
module InteractiveCli
|
6
|
+
module UseCases
|
7
|
+
#
|
8
|
+
# We have not yet pulled QueryPackwerk into open source, so we cannot include it in this CLI yet
|
9
|
+
#
|
10
|
+
class Query
|
11
|
+
extend T::Sig
|
12
|
+
extend T::Helpers
|
13
|
+
include Interface
|
14
|
+
|
15
|
+
sig { override.returns(String) }
|
16
|
+
def user_facing_name
|
17
|
+
'Query violations about a pack'
|
18
|
+
end
|
19
|
+
|
20
|
+
sig { override.params(prompt: TTY::Prompt).void }
|
21
|
+
def perform!(prompt)
|
22
|
+
selection = prompt.select('For one pack or all packs?', ['One pack', 'All packs'])
|
23
|
+
if selection == 'All packs'
|
24
|
+
# Probably should just make `list_top_dependency_violations` take in an array of things
|
25
|
+
# Better yet we might just want to replace these functions with `QueryPackwerk`
|
26
|
+
selected_pack = nil
|
27
|
+
else
|
28
|
+
selected_pack = PackSelector.single_pack_select(prompt).name
|
29
|
+
end
|
30
|
+
|
31
|
+
limit = prompt.ask('Specify the limit of constants to analyze', default: 10, convert: :integer)
|
32
|
+
|
33
|
+
selection = prompt.select('Are you interested in dependency or privacy violations?', %w[Dependency Privacy], default: 'Privacy')
|
34
|
+
|
35
|
+
if selection == 'Dependency'
|
36
|
+
UsePackwerk.list_top_dependency_violations(
|
37
|
+
pack_name: selected_pack,
|
38
|
+
limit: limit
|
39
|
+
)
|
40
|
+
else
|
41
|
+
UsePackwerk.list_top_privacy_violations(
|
42
|
+
pack_name: selected_pack,
|
43
|
+
limit: limit
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module UsePackwerk
|
4
|
+
module Private
|
5
|
+
module InteractiveCli
|
6
|
+
module UseCases
|
7
|
+
class Rename
|
8
|
+
extend T::Sig
|
9
|
+
extend T::Helpers
|
10
|
+
include Interface
|
11
|
+
|
12
|
+
sig { override.returns(String) }
|
13
|
+
def user_facing_name
|
14
|
+
'Rename a pack'
|
15
|
+
end
|
16
|
+
|
17
|
+
sig { override.params(prompt: TTY::Prompt).void }
|
18
|
+
def perform!(prompt)
|
19
|
+
prompt.warn(<<~WARNING)
|
20
|
+
We do not yet have an automated API for this.
|
21
|
+
|
22
|
+
Follow these steps:
|
23
|
+
1. Rename the `packs/your_pack` directory to the name of the new pack, `packs/new_pack_name
|
24
|
+
2. Replace references to `- packs/your_pack` in `package.yml` files with `- packs/new_pack_name`
|
25
|
+
3. Rerun `bin/packwerk update-deprecations` to update violations
|
26
|
+
4. Run `bin/codeownership validate` to update ownership information
|
27
|
+
5. Please let us know if anything is missing.
|
28
|
+
WARNING
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module UsePackwerk
|
4
|
+
module Private
|
5
|
+
module InteractiveCli
|
6
|
+
module UseCases
|
7
|
+
class UpdateDeprecations
|
8
|
+
extend T::Sig
|
9
|
+
extend T::Helpers
|
10
|
+
include Interface
|
11
|
+
|
12
|
+
sig { override.returns(String) }
|
13
|
+
def user_facing_name
|
14
|
+
'Run bin/packwerk update-deprecations'
|
15
|
+
end
|
16
|
+
|
17
|
+
sig { override.params(prompt: TTY::Prompt).void }
|
18
|
+
def perform!(prompt)
|
19
|
+
`bin/packwerk update-deprecations`
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module UsePackwerk
|
4
|
+
module Private
|
5
|
+
module InteractiveCli
|
6
|
+
module UseCases
|
7
|
+
class Validate
|
8
|
+
extend T::Sig
|
9
|
+
extend T::Helpers
|
10
|
+
include Interface
|
11
|
+
|
12
|
+
sig { override.returns(String) }
|
13
|
+
def user_facing_name
|
14
|
+
'Run bin/packwerk validate (detects cycles)'
|
15
|
+
end
|
16
|
+
|
17
|
+
sig { override.params(prompt: TTY::Prompt).void }
|
18
|
+
def perform!(prompt)
|
19
|
+
`bin/packwerk validate`
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
# https://github.com/piotrmurach/tty-prompt
|
4
|
+
require 'tty-prompt'
|
5
|
+
|
6
|
+
require 'use_packwerk/private/interactive_cli/team_selector'
|
7
|
+
require 'use_packwerk/private/interactive_cli/pack_selector'
|
8
|
+
require 'use_packwerk/private/interactive_cli/use_cases/interface'
|
9
|
+
require 'use_packwerk/private/interactive_cli/use_cases/create'
|
10
|
+
require 'use_packwerk/private/interactive_cli/use_cases/move'
|
11
|
+
require 'use_packwerk/private/interactive_cli/use_cases/add_dependency'
|
12
|
+
require 'use_packwerk/private/interactive_cli/use_cases/get_info'
|
13
|
+
require 'use_packwerk/private/interactive_cli/use_cases/query'
|
14
|
+
require 'use_packwerk/private/interactive_cli/use_cases/make_public'
|
15
|
+
require 'use_packwerk/private/interactive_cli/use_cases/nest'
|
16
|
+
require 'use_packwerk/private/interactive_cli/use_cases/rename'
|
17
|
+
require 'use_packwerk/private/interactive_cli/use_cases/update_deprecations'
|
18
|
+
require 'use_packwerk/private/interactive_cli/use_cases/validate'
|
19
|
+
|
20
|
+
module UsePackwerk
|
21
|
+
module Private
|
22
|
+
module InteractiveCli
|
23
|
+
extend T::Sig
|
24
|
+
|
25
|
+
sig { void }
|
26
|
+
def self.start!
|
27
|
+
prompt = TTY::Prompt.new(interrupt: lambda {
|
28
|
+
puts "\n\nGoodbye! I hope you have a good day."
|
29
|
+
exit 1 })
|
30
|
+
help_text = '(Press ↑/↓ arrow to move, Enter to select and letters to filter)'
|
31
|
+
choice = prompt.select('Hello! What would you like to do?',
|
32
|
+
cycle: true,
|
33
|
+
filter: true,
|
34
|
+
help: help_text,
|
35
|
+
show_help: :always,
|
36
|
+
per_page: 15) do |menu|
|
37
|
+
menu.enum '.'
|
38
|
+
|
39
|
+
UseCases::Interface.all.each do |use_case|
|
40
|
+
menu.choice use_case.user_facing_name, use_case
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
choice.perform!(prompt)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -8,72 +8,28 @@ module UsePackwerk
|
|
8
8
|
sig do
|
9
9
|
params(
|
10
10
|
pack_name: T.nilable(String),
|
11
|
-
limit: Integer
|
11
|
+
limit: Integer
|
12
12
|
).void
|
13
13
|
end
|
14
14
|
def self.list_top_privacy_violations(pack_name, limit)
|
15
15
|
all_packages = ParsePackwerk.all
|
16
|
-
if
|
16
|
+
if pack_name.nil?
|
17
|
+
to_package_names = all_packages.map(&:name)
|
18
|
+
else
|
17
19
|
pack_name = Private.clean_pack_name(pack_name)
|
18
|
-
package = all_packages.find { |
|
20
|
+
package = all_packages.find { |p| p.name == pack_name }
|
19
21
|
if package.nil?
|
20
|
-
raise StandardError
|
22
|
+
raise StandardError, "Can not find package with name #{pack_name}. Make sure the argument is of the form `packs/my_pack/`"
|
21
23
|
end
|
22
24
|
|
23
|
-
pack_specific_content = <<~PACK_CONTENT
|
24
|
-
You are listing top #{limit} privacy violations for #{pack_name}. See #{UsePackwerk.config.documentation_link} for other utilities!
|
25
|
-
Pass in a limit to display more or less, e.g. `bin/use_packwerk list_top_privacy_violations #{pack_name} -l 1000`
|
26
|
-
|
27
|
-
This script is intended to help you find which of YOUR pack's private classes, constants, or modules other packs are using the most.
|
28
|
-
Anything not in #{pack_name}/app/public is considered private API.
|
29
|
-
PACK_CONTENT
|
30
|
-
|
31
25
|
to_package_names = [pack_name]
|
32
|
-
else
|
33
|
-
pack_specific_content = <<~PACK_CONTENT
|
34
|
-
You are listing top #{limit} privacy violations for all packs. See #{UsePackwerk.config.documentation_link} for other utilities!
|
35
|
-
Pass in a limit to display more or less, e.g. `bin/use_packwerk list_top_privacy_violations #{pack_name} -l 1000`
|
36
|
-
|
37
|
-
This script is intended to help you find which of YOUR pack's private classes, constants, or modules other packs are using the most.
|
38
|
-
Anything not in pack_name/app/public is considered private API.
|
39
|
-
PACK_CONTENT
|
40
|
-
|
41
|
-
to_package_names = all_packages.map(&:name)
|
42
26
|
end
|
43
27
|
|
44
28
|
violations_by_count = {}
|
45
29
|
total_pack_violation_count = 0
|
46
30
|
|
47
31
|
Logging.section('👋 Hi there') do
|
48
|
-
intro =
|
49
|
-
#{pack_specific_content}
|
50
|
-
|
51
|
-
When using this script, ask yourself some questions like:
|
52
|
-
- What do I want to support?
|
53
|
-
- What do I *not* want to support?
|
54
|
-
- What is considered simply an implementation detail, and what is essential to the behavior of my pack?
|
55
|
-
- What is a simple, minimialistic API for clients to engage with the behavior of your pack?
|
56
|
-
- How do I ensure my public API is not coupled to specific client's use cases?
|
57
|
-
|
58
|
-
Looking at privacy violations can help guide the development of your public API, but it is just the beginning!
|
59
|
-
|
60
|
-
The script will output in the following format:
|
61
|
-
|
62
|
-
SomeConstant # This is the name of a class, constant, or module defined in your pack, outside of app/public
|
63
|
-
- Total Count: 5 # This is the total number of uses of this outside your pack
|
64
|
-
- By package: # This is a breakdown of the use of this constant by other packages
|
65
|
-
# This is the number of files in this pack that this constant is used.
|
66
|
-
# Check `packs/other_pack_a/deprecated_references.yml` under the '#{pack_name}'.'SomeConstant' key to see where this constant is used
|
67
|
-
- packs/other_pack_a: 3
|
68
|
-
- packs/other_pack_b: 2
|
69
|
-
SomeClass # This is the second most violated class, constant, or module defined in your pack
|
70
|
-
- Total Count: 2
|
71
|
-
- By package:
|
72
|
-
- packs/other_pack_a: 1
|
73
|
-
- packs/other_pack_b: 1
|
74
|
-
|
75
|
-
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.
|
76
|
-
INTRO
|
32
|
+
intro = UsePackwerk.config.user_event_logger.before_list_top_privacy_violations(pack_name, limit)
|
77
33
|
Logging.print_bold_green(intro)
|
78
34
|
end
|
79
35
|
|
@@ -81,10 +37,11 @@ module UsePackwerk
|
|
81
37
|
all_packages.each do |client_package|
|
82
38
|
PackageProtections::ProtectedPackage.from(client_package).violations.select(&:privacy?).each do |violation|
|
83
39
|
next unless to_package_names.include?(violation.to_package_name)
|
40
|
+
|
84
41
|
if pack_name.nil?
|
85
42
|
violated_symbol = "#{violation.class_name} (#{violation.to_package_name})"
|
86
43
|
else
|
87
|
-
violated_symbol =
|
44
|
+
violated_symbol = violation.class_name
|
88
45
|
end
|
89
46
|
violations_by_count[violated_symbol] ||= {}
|
90
47
|
violations_by_count[violated_symbol][:total_count] ||= 0
|
@@ -104,8 +61,8 @@ module UsePackwerk
|
|
104
61
|
Logging.print(violated_symbol)
|
105
62
|
Logging.print(" - Total Count: #{count_info[:total_count]} (#{percentage_of_total}% of total)")
|
106
63
|
|
107
|
-
Logging.print(
|
108
|
-
count_info[:by_package].sort_by{ |client_package_name, count| [-count, client_package_name] }.each do |client_package_name, count|
|
64
|
+
Logging.print(' - By package:')
|
65
|
+
count_info[:by_package].sort_by { |client_package_name, count| [-count, client_package_name] }.each do |client_package_name, count|
|
109
66
|
Logging.print(" - #{client_package_name}: #{count}")
|
110
67
|
end
|
111
68
|
end
|
@@ -120,76 +77,35 @@ module UsePackwerk
|
|
120
77
|
def self.list_top_dependency_violations(pack_name, limit)
|
121
78
|
all_packages = ParsePackwerk.all
|
122
79
|
|
123
|
-
if
|
80
|
+
if pack_name.nil?
|
81
|
+
to_package_names = all_packages.map(&:name)
|
82
|
+
else
|
124
83
|
pack_name = Private.clean_pack_name(pack_name)
|
125
|
-
package = all_packages.find { |
|
84
|
+
package = all_packages.find { |p| p.name == pack_name }
|
126
85
|
if package.nil?
|
127
|
-
raise StandardError
|
86
|
+
raise StandardError, "Can not find package with name #{pack_name}. Make sure the argument is of the form `packs/my_pack/`"
|
128
87
|
end
|
129
88
|
|
130
|
-
pack_specific_content = <<~PACK_CONTENT
|
131
|
-
You are listing top #{limit} dependency violations for #{pack_name}. See #{UsePackwerk.config.documentation_link} for other utilities!
|
132
|
-
Pass in a limit to display more or less, e.g. `bin/use_packwerk list_top_dependency_violations #{pack_name} -l 1000`
|
133
|
-
|
134
|
-
This script is intended to help you find which of YOUR pack's private classes, constants, or modules other packs are using the most.
|
135
|
-
Anything not in #{pack_name}/app/public is considered private API.
|
136
|
-
PACK_CONTENT
|
137
|
-
|
138
89
|
to_package_names = [pack_name]
|
139
|
-
else
|
140
|
-
pack_specific_content = <<~PACK_CONTENT
|
141
|
-
You are listing top #{limit} dependency violations for all packs. See #{UsePackwerk.config.documentation_link} for other utilities!
|
142
|
-
Pass in a limit to display more or less, e.g. `use_packwerk list_top_dependency_violations #{pack_name} -l 1000`
|
143
|
-
|
144
|
-
This script is intended to help you find which of YOUR pack's private classes, constants, or modules other packs are using the most.
|
145
|
-
Anything not in pack_name/app/public is considered private API.
|
146
|
-
PACK_CONTENT
|
147
|
-
|
148
|
-
to_package_names = all_packages.map(&:name)
|
149
90
|
end
|
150
91
|
|
151
|
-
violations_by_count = {}
|
152
|
-
total_pack_violation_count = 0
|
153
|
-
|
154
92
|
Logging.section('👋 Hi there') do
|
155
|
-
intro =
|
156
|
-
#{pack_specific_content}
|
157
|
-
|
158
|
-
When using this script, ask yourself some questions like:
|
159
|
-
- What do I want to support?
|
160
|
-
- What do I *not* want to support?
|
161
|
-
- Which direction should a dependency go?
|
162
|
-
- What packs should depend on you, and what packs should not depend on you?
|
163
|
-
- Would it be simpler if other packs only depended on interfaces to your pack rather than implementation?
|
164
|
-
|
165
|
-
Looking at dependency violations can help guide the development of your public API, but it is just the beginning!
|
166
|
-
|
167
|
-
The script will output in the following format:
|
168
|
-
|
169
|
-
SomeConstant # This is the name of a class, constant, or module defined in your pack, outside of app/public
|
170
|
-
- Total Count: 5 # This is the total number of unstated uses of this outside your pack
|
171
|
-
- By package: # This is a breakdown of the use of this constant by other packages
|
172
|
-
# This is the number of files in this pack that this constant is used.
|
173
|
-
# Check `packs/other_pack_a/deprecated_references.yml` under the '#{pack_name}'.'SomeConstant' key to see where this constant is used
|
174
|
-
- packs/other_pack_a: 3
|
175
|
-
- packs/other_pack_b: 2
|
176
|
-
SomeClass # This is the second most violated class, constant, or module defined in your pack
|
177
|
-
- Total Count: 2
|
178
|
-
- By package:
|
179
|
-
- packs/other_pack_a: 1
|
180
|
-
- packs/other_pack_b: 1
|
181
|
-
INTRO
|
93
|
+
intro = UsePackwerk.config.user_event_logger.before_list_top_dependency_violations(pack_name, limit)
|
182
94
|
Logging.print_bold_green(intro)
|
183
95
|
end
|
184
96
|
|
97
|
+
violations_by_count = {}
|
98
|
+
total_pack_violation_count = 0
|
99
|
+
|
185
100
|
# TODO: This is a copy of the implementation above. We may want to refactor out this implementation detail before making changes that apply to both.
|
186
101
|
all_packages.each do |client_package|
|
187
102
|
PackageProtections::ProtectedPackage.from(client_package).violations.select(&:dependency?).each do |violation|
|
188
103
|
next unless to_package_names.include?(violation.to_package_name)
|
104
|
+
|
189
105
|
if pack_name.nil?
|
190
106
|
violated_symbol = "#{violation.class_name} (#{violation.to_package_name})"
|
191
107
|
else
|
192
|
-
violated_symbol =
|
108
|
+
violated_symbol = violation.class_name
|
193
109
|
end
|
194
110
|
violations_by_count[violated_symbol] ||= {}
|
195
111
|
violations_by_count[violated_symbol][:total_count] ||= 0
|
@@ -208,8 +124,8 @@ module UsePackwerk
|
|
208
124
|
percentage_of_total = (count_info[:total_count] * 100.0 / total_pack_violation_count).round(2)
|
209
125
|
Logging.print(violated_symbol)
|
210
126
|
Logging.print(" - Total Count: #{count_info[:total_count]} (#{percentage_of_total}% of total)")
|
211
|
-
Logging.print(
|
212
|
-
count_info[:by_package].sort_by{ |client_package_name, count| [-count, client_package_name] }.each do |client_package_name, count|
|
127
|
+
Logging.print(' - By package:')
|
128
|
+
count_info[:by_package].sort_by { |client_package_name, count| [-count, client_package_name] }.each do |client_package_name, count|
|
213
129
|
Logging.print(" - #{client_package_name}: #{count}")
|
214
130
|
end
|
215
131
|
end
|