packwerk 3.0.0 → 3.2.2
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/README.md +4 -2
- data/exe/packwerk +4 -1
- data/lib/packwerk/application_validator.rb +3 -0
- data/lib/packwerk/association_inspector.rb +17 -4
- data/lib/packwerk/checker.rb +16 -7
- data/lib/packwerk/cli.rb +14 -177
- data/lib/packwerk/commands/base_command.rb +69 -0
- data/lib/packwerk/commands/check_command.rb +62 -0
- data/lib/packwerk/commands/help_command.rb +33 -0
- data/lib/packwerk/commands/init_command.rb +42 -0
- data/lib/packwerk/commands/lazy_loaded_entry.rb +37 -0
- data/lib/packwerk/commands/update_todo_command.rb +60 -0
- data/lib/packwerk/commands/uses_parse_run.rb +92 -0
- data/lib/packwerk/commands/validate_command.rb +46 -0
- data/lib/packwerk/commands/version_command.rb +18 -0
- data/lib/packwerk/commands.rb +54 -0
- data/lib/packwerk/configuration.rb +8 -1
- data/lib/packwerk/const_node_inspector.rb +2 -2
- data/lib/packwerk/constant_name_inspector.rb +2 -2
- data/lib/packwerk/file_processor.rb +12 -1
- data/lib/packwerk/formatters/default_offenses_formatter.rb +3 -3
- data/lib/packwerk/formatters/progress_formatter.rb +11 -0
- data/lib/packwerk/generators/templates/package.yml +2 -2
- data/lib/packwerk/generators/templates/packwerk.yml.erb +1 -1
- data/lib/packwerk/offense_collection.rb +32 -12
- data/lib/packwerk/offenses_formatter.rb +14 -5
- data/lib/packwerk/package.rb +1 -1
- data/lib/packwerk/package_todo.rb +86 -69
- data/lib/packwerk/parse_run.rb +42 -82
- data/lib/packwerk/parsers/factory.rb +3 -3
- data/lib/packwerk/parsers/ruby.rb +9 -2
- data/lib/packwerk/reference_checking/checkers/dependency_checker.rb +3 -3
- data/lib/packwerk/reference_extractor.rb +29 -1
- data/lib/packwerk/reference_offense.rb +1 -1
- data/lib/packwerk/run_context.rb +15 -4
- data/lib/packwerk/spring_command.rb +0 -2
- data/lib/packwerk/validator.rb +19 -5
- data/lib/packwerk/version.rb +1 -1
- data/lib/packwerk.rb +5 -28
- data/sorbet/config +1 -0
- data/sorbet/rbi/gems/actionpack@7.0.3.1.rbi +3280 -3450
- data/sorbet/rbi/gems/actionview@7.0.3.1.rbi +2322 -1782
- data/sorbet/rbi/gems/activesupport@7.0.3.1.rbi +2654 -3268
- data/sorbet/rbi/gems/ast@2.4.2.rbi +535 -6
- data/sorbet/rbi/gems/better_html@2.0.1.rbi +529 -0
- data/sorbet/rbi/gems/builder@3.2.4.rbi +4 -4
- data/sorbet/rbi/gems/byebug@11.1.3.rbi +32 -4
- data/sorbet/rbi/gems/concurrent-ruby@1.1.10.rbi +1750 -1840
- data/sorbet/rbi/gems/constant_resolver@0.2.0.rbi +15 -15
- data/sorbet/rbi/gems/crass@1.0.6.rbi +489 -5
- data/sorbet/rbi/gems/erubi@1.11.0.rbi +24 -21
- data/sorbet/rbi/gems/i18n@1.12.0.rbi +395 -395
- data/sorbet/rbi/gems/json@2.6.2.rbi +70 -77
- data/sorbet/rbi/gems/language_server-protocol@3.16.0.3.rbi +1 -1
- data/sorbet/rbi/gems/loofah@2.18.0.rbi +134 -134
- data/sorbet/rbi/gems/m@1.6.0.rbi +60 -60
- data/sorbet/rbi/gems/method_source@1.1.0.rbi +303 -0
- data/sorbet/rbi/gems/minitest-focus@1.3.1.rbi +22 -28
- data/sorbet/rbi/gems/minitest@5.16.2.rbi +384 -396
- data/sorbet/rbi/gems/mocha@1.14.0.rbi +589 -589
- data/sorbet/rbi/gems/netrc@0.11.0.rbi +37 -32
- data/sorbet/rbi/gems/{nokogiri@1.13.8.rbi → nokogiri@1.15.3.rbi} +1869 -1030
- data/sorbet/rbi/gems/{parallel@1.22.1.rbi → parallel@1.24.0.rbi} +85 -82
- data/sorbet/rbi/gems/parser@3.3.1.0.rbi +7320 -0
- data/sorbet/rbi/gems/prettier_print@0.1.0.rbi +1 -1
- data/sorbet/rbi/gems/prism@0.27.0.rbi +36983 -0
- data/sorbet/rbi/gems/{racc@1.6.0.rbi → racc@1.7.1.rbi} +42 -33
- data/sorbet/rbi/gems/rack-test@2.0.2.rbi +148 -338
- data/sorbet/rbi/gems/rack@2.2.4.rbi +1079 -1130
- data/sorbet/rbi/gems/rails-dom-testing@2.0.3.rbi +354 -22
- data/sorbet/rbi/gems/rails-html-sanitizer@1.4.3.rbi +113 -259
- data/sorbet/rbi/gems/railties@7.0.3.1.rbi +642 -638
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +109 -99
- data/sorbet/rbi/gems/rake@13.0.6.rbi +714 -599
- data/sorbet/rbi/gems/{rbi@0.0.15.rbi → rbi@0.1.12.rbi} +865 -801
- data/sorbet/rbi/gems/regexp_parser@2.5.0.rbi +853 -870
- data/sorbet/rbi/gems/rexml@3.2.5.rbi +480 -477
- data/sorbet/rbi/gems/rubocop-ast@1.21.0.rbi +1621 -1622
- data/sorbet/rbi/gems/rubocop-performance@1.14.3.rbi +507 -526
- data/sorbet/rbi/gems/rubocop-shopify@2.9.0.rbi +1 -1
- data/sorbet/rbi/gems/rubocop-sorbet@0.6.11.rbi +186 -203
- data/sorbet/rbi/gems/rubocop@1.34.1.rbi +8126 -8367
- data/sorbet/rbi/gems/{ruby-lsp@0.2.1.rbi → ruby-lsp@0.2.3.rbi} +2 -2
- data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +1235 -4
- data/sorbet/rbi/gems/smart_properties@1.17.0.rbi +90 -90
- data/sorbet/rbi/gems/spoom@1.3.2.rbi +4420 -0
- data/sorbet/rbi/gems/spring@4.0.0.rbi +104 -104
- data/sorbet/rbi/gems/syntax_tree@3.3.0.rbi +1 -1
- data/sorbet/rbi/gems/{tapioca@0.9.2.rbi → tapioca@0.13.3.rbi} +1596 -1253
- data/sorbet/rbi/gems/{thor@1.2.1.rbi → thor@1.3.1.rbi} +1047 -652
- data/sorbet/rbi/gems/tzinfo@2.0.5.rbi +531 -513
- data/sorbet/rbi/gems/unicode-display_width@2.2.0.rbi +13 -13
- data/sorbet/rbi/gems/{yard-sorbet@0.6.1.rbi → yard-sorbet@0.8.1.rbi} +132 -92
- data/sorbet/rbi/gems/{yard@0.9.28.rbi → yard@0.9.36.rbi} +3158 -3067
- data/sorbet/rbi/gems/zeitwerk@2.6.4.rbi +149 -145
- metadata +36 -84
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -27
- data/.github/pull_request_template.md +0 -28
- data/.github/workflows/ci.yml +0 -65
- data/.github/workflows/cla.yml +0 -22
- data/.gitignore +0 -13
- data/.rubocop.yml +0 -75
- data/.ruby-version +0 -1
- data/CODEOWNERS +0 -1
- data/CODE_OF_CONDUCT.md +0 -76
- data/CONTRIBUTING.md +0 -17
- data/Gemfile +0 -27
- data/Gemfile.lock +0 -201
- data/RESOLVING_VIOLATIONS.md +0 -81
- data/Rakefile +0 -13
- data/TROUBLESHOOT.md +0 -45
- data/UPGRADING.md +0 -54
- data/USAGE.md +0 -367
- data/bin/console +0 -15
- data/bin/m +0 -29
- data/bin/rake +0 -29
- data/bin/rubocop +0 -29
- data/bin/setup +0 -8
- data/bin/srb +0 -29
- data/bin/tapioca +0 -29
- data/dev.yml +0 -32
- data/docs/cohesion.png +0 -0
- data/gemfiles/Gemfile-rails-6-0 +0 -22
- data/gemfiles/Gemfile-rails-6-1 +0 -22
- data/lib/packwerk/cli/result.rb +0 -11
- data/packwerk.gemspec +0 -58
- data/shipit.rubygems.yml +0 -5
- data/sorbet/rbi/gems/actioncable@7.0.3.1.rbi +0 -2754
- data/sorbet/rbi/gems/actionmailbox@7.0.3.1.rbi +0 -1496
- data/sorbet/rbi/gems/actionmailer@7.0.3.1.rbi +0 -2362
- data/sorbet/rbi/gems/actiontext@7.0.3.1.rbi +0 -1569
- data/sorbet/rbi/gems/activejob@7.0.3.1.rbi +0 -2553
- data/sorbet/rbi/gems/activemodel@7.0.3.1.rbi +0 -5999
- data/sorbet/rbi/gems/activerecord@7.0.3.1.rbi +0 -37832
- data/sorbet/rbi/gems/activestorage@7.0.3.1.rbi +0 -2321
- data/sorbet/rbi/gems/better_html@1.0.16.rbi +0 -317
- data/sorbet/rbi/gems/coderay@1.1.3.rbi +0 -8
- data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +0 -1079
- data/sorbet/rbi/gems/digest@3.1.0.rbi +0 -189
- data/sorbet/rbi/gems/globalid@1.0.0.rbi +0 -572
- data/sorbet/rbi/gems/mail@2.7.1.rbi +0 -2490
- data/sorbet/rbi/gems/marcel@1.0.2.rbi +0 -220
- data/sorbet/rbi/gems/method_source@1.0.0.rbi +0 -76
- data/sorbet/rbi/gems/mini_mime@1.1.2.rbi +0 -170
- data/sorbet/rbi/gems/net-imap@0.2.3.rbi +0 -2147
- data/sorbet/rbi/gems/net-pop@0.1.1.rbi +0 -926
- data/sorbet/rbi/gems/net-protocol@0.1.3.rbi +0 -11
- data/sorbet/rbi/gems/net-smtp@0.3.1.rbi +0 -1108
- data/sorbet/rbi/gems/nio4r@2.5.8.rbi +0 -292
- data/sorbet/rbi/gems/parser@3.1.2.1.rbi +0 -9029
- data/sorbet/rbi/gems/pry@0.14.1.rbi +0 -8
- data/sorbet/rbi/gems/rails@7.0.3.1.rbi +0 -8
- data/sorbet/rbi/gems/spoom@1.1.11.rbi +0 -2181
- data/sorbet/rbi/gems/strscan@3.0.4.rbi +0 -8
- data/sorbet/rbi/gems/timeout@0.3.0.rbi +0 -142
- data/sorbet/rbi/gems/unparser@0.6.5.rbi +0 -4529
- data/sorbet/rbi/gems/webrick@1.7.0.rbi +0 -2582
- data/sorbet/rbi/gems/websocket-driver@0.7.5.rbi +0 -993
- data/sorbet/rbi/gems/websocket-extensions@0.1.5.rbi +0 -71
|
@@ -7,16 +7,20 @@ module Packwerk
|
|
|
7
7
|
class PackageTodo
|
|
8
8
|
extend T::Sig
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
PackageName = T.type_alias { String }
|
|
11
|
+
ConstantName = T.type_alias { String }
|
|
12
|
+
FilePath = T.type_alias { String }
|
|
13
|
+
Entry = T.type_alias { T::Hash[ConstantName, T::Hash[ConstantName, T::Array[FilePath]]] }
|
|
14
|
+
Entries = T.type_alias do
|
|
15
|
+
T::Hash[PackageName, Entry]
|
|
12
16
|
end
|
|
13
17
|
|
|
14
|
-
sig { params(package: Packwerk::Package,
|
|
15
|
-
def initialize(package,
|
|
18
|
+
sig { params(package: Packwerk::Package, path: String).void }
|
|
19
|
+
def initialize(package, path)
|
|
16
20
|
@package = package
|
|
17
|
-
@
|
|
18
|
-
@new_entries = T.let({},
|
|
19
|
-
@
|
|
21
|
+
@path = path
|
|
22
|
+
@new_entries = T.let({}, Entries)
|
|
23
|
+
@old_entries = T.let(nil, T.nilable(Entries))
|
|
20
24
|
end
|
|
21
25
|
|
|
22
26
|
sig do
|
|
@@ -24,7 +28,7 @@ module Packwerk
|
|
|
24
28
|
.returns(T::Boolean)
|
|
25
29
|
end
|
|
26
30
|
def listed?(reference, violation_type:)
|
|
27
|
-
violated_constants_found =
|
|
31
|
+
violated_constants_found = old_entries.dig(reference.constant.package.name, reference.constant.name)
|
|
28
32
|
return false unless violated_constants_found
|
|
29
33
|
|
|
30
34
|
violated_constant_in_file = violated_constants_found.fetch("files", []).include?(reference.relative_path)
|
|
@@ -37,16 +41,16 @@ module Packwerk
|
|
|
37
41
|
params(reference: Packwerk::Reference, violation_type: String).returns(T::Boolean)
|
|
38
42
|
end
|
|
39
43
|
def add_entries(reference, violation_type)
|
|
40
|
-
package_violations =
|
|
44
|
+
package_violations = new_entries.fetch(reference.constant.package.name, {})
|
|
41
45
|
entries_for_constant = package_violations[reference.constant.name] ||= {}
|
|
42
46
|
|
|
43
47
|
entries_for_constant["violations"] ||= []
|
|
44
|
-
entries_for_constant
|
|
48
|
+
entries_for_constant.fetch("violations") << violation_type
|
|
45
49
|
|
|
46
50
|
entries_for_constant["files"] ||= []
|
|
47
|
-
entries_for_constant
|
|
51
|
+
entries_for_constant.fetch("files") << reference.relative_path.to_s
|
|
48
52
|
|
|
49
|
-
|
|
53
|
+
new_entries[reference.constant.package.name] = package_violations
|
|
50
54
|
listed?(reference, violation_type: violation_type)
|
|
51
55
|
end
|
|
52
56
|
|
|
@@ -54,102 +58,115 @@ module Packwerk
|
|
|
54
58
|
def stale_violations?(for_files)
|
|
55
59
|
prepare_entries_for_dump
|
|
56
60
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
entries_for_files = for_files & entries_for_constant["files"]
|
|
61
|
-
next if entries_for_files.none?
|
|
62
|
-
|
|
63
|
-
package_violations_for_files[constant_name] = {
|
|
64
|
-
"violations" => entries_for_constant["violations"],
|
|
65
|
-
"files" => entries_for_files.to_a,
|
|
66
|
-
}
|
|
67
|
-
end
|
|
61
|
+
old_entries.any? do |package, violations|
|
|
62
|
+
files = for_files + deleted_files_for(package)
|
|
63
|
+
violations_for_files = package_violations_for(violations, files: files)
|
|
68
64
|
|
|
69
65
|
# We `next false` because if we cannot find existing violations for `for_files` within
|
|
70
66
|
# the `package_todo.yml` file, then there are no violations that
|
|
71
67
|
# can be considered stale.
|
|
72
|
-
next false if
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
new_entries_violation_types = @new_entries.dig(package, constant_name, "violations")
|
|
76
|
-
# If there are no NEW entries that match the old entries `for_files`,
|
|
77
|
-
# @new_entries is from the list of violations we get when we check this file.
|
|
78
|
-
# If this list is empty, we also must have stale violations.
|
|
79
|
-
next true if new_entries_violation_types.nil?
|
|
80
|
-
|
|
81
|
-
if entries_for_constant["violations"].all? { |type| new_entries_violation_types.include?(type) }
|
|
82
|
-
stale_violations =
|
|
83
|
-
entries_for_constant["files"] - Array(@new_entries.dig(package, constant_name, "files"))
|
|
84
|
-
stale_violations.any?
|
|
85
|
-
else
|
|
86
|
-
return true
|
|
87
|
-
end
|
|
88
|
-
end
|
|
68
|
+
next false if violations_for_files.empty?
|
|
69
|
+
|
|
70
|
+
stale_violation_for_package?(package, violations: violations_for_files)
|
|
89
71
|
end
|
|
90
72
|
end
|
|
91
73
|
|
|
92
74
|
sig { void }
|
|
93
75
|
def dump
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if @new_entries.empty?
|
|
76
|
+
if new_entries.empty?
|
|
97
77
|
delete_if_exists
|
|
98
78
|
else
|
|
99
79
|
prepare_entries_for_dump
|
|
100
80
|
message = <<~MESSAGE
|
|
101
|
-
# This file contains a list of dependencies that are not part of the long term plan for
|
|
102
|
-
#
|
|
81
|
+
# This file contains a list of dependencies that are not part of the long term plan for the
|
|
82
|
+
# '#{@package.name}' package.
|
|
83
|
+
# We should generally work to reduce this list over time.
|
|
103
84
|
#
|
|
104
85
|
# You can regenerate this file using the following command:
|
|
105
86
|
#
|
|
106
|
-
# bin/packwerk update-todo
|
|
87
|
+
# bin/packwerk update-todo
|
|
107
88
|
MESSAGE
|
|
108
|
-
File.open(@
|
|
89
|
+
File.open(@path, "w") do |f|
|
|
109
90
|
f.write(message)
|
|
110
|
-
f.write(
|
|
91
|
+
f.write(new_entries.to_yaml)
|
|
111
92
|
end
|
|
112
93
|
end
|
|
113
94
|
end
|
|
114
95
|
|
|
115
96
|
sig { void }
|
|
116
97
|
def delete_if_exists
|
|
117
|
-
File.delete(@
|
|
98
|
+
File.delete(@path) if File.exist?(@path)
|
|
118
99
|
end
|
|
119
100
|
|
|
120
101
|
private
|
|
121
102
|
|
|
122
|
-
sig {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
103
|
+
sig { returns(Entries) }
|
|
104
|
+
attr_reader(:new_entries)
|
|
105
|
+
|
|
106
|
+
sig { params(package: String).returns(T::Array[String]) }
|
|
107
|
+
def deleted_files_for(package)
|
|
108
|
+
old_files = old_entries.fetch(package, {}).values.flat_map { |violation| violation.fetch("files") }
|
|
109
|
+
new_files = new_entries.fetch(package, {}).values.flat_map { |violation| violation.fetch("files") }
|
|
110
|
+
old_files - new_files
|
|
126
111
|
end
|
|
127
112
|
|
|
128
|
-
sig { returns(
|
|
113
|
+
sig { params(package: String, violations: Entry).returns(T::Boolean) }
|
|
114
|
+
def stale_violation_for_package?(package, violations:)
|
|
115
|
+
violations.any? do |constant_name, entries_for_constant|
|
|
116
|
+
new_entries_violation_types = new_entries.dig(package, constant_name, "violations")
|
|
117
|
+
# If there are no NEW entries that match the old entries `for_files`,
|
|
118
|
+
# new_entries is from the list of violations we get when we check this file.
|
|
119
|
+
# If this list is empty, we also must have stale violations.
|
|
120
|
+
next true if new_entries_violation_types.nil?
|
|
121
|
+
|
|
122
|
+
if entries_for_constant.fetch("violations").all? { |type| new_entries_violation_types.include?(type) }
|
|
123
|
+
stale_violations =
|
|
124
|
+
entries_for_constant.fetch("files") - Array(new_entries.dig(package, constant_name, "files"))
|
|
125
|
+
stale_violations.any?
|
|
126
|
+
else
|
|
127
|
+
return true
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
sig { params(package_violations: Entry, files: T::Set[String]).returns(Entry) }
|
|
133
|
+
def package_violations_for(package_violations, files:)
|
|
134
|
+
{}.tap do |package_violations_for_files|
|
|
135
|
+
package_violations_for_files = T.cast(package_violations_for_files, Entry)
|
|
136
|
+
|
|
137
|
+
package_violations.each do |constant_name, entries_for_constant|
|
|
138
|
+
entries_for_files = files & entries_for_constant.fetch("files")
|
|
139
|
+
next if entries_for_files.none?
|
|
140
|
+
|
|
141
|
+
package_violations_for_files[constant_name] = {
|
|
142
|
+
"violations" => entries_for_constant["violations"],
|
|
143
|
+
"files" => entries_for_files.to_a,
|
|
144
|
+
}
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
sig { returns(Entries) }
|
|
129
150
|
def prepare_entries_for_dump
|
|
130
|
-
|
|
151
|
+
new_entries.each do |package_name, package_violations|
|
|
131
152
|
package_violations.each do |_, entries_for_constant|
|
|
132
|
-
entries_for_constant
|
|
133
|
-
entries_for_constant
|
|
153
|
+
entries_for_constant.fetch("violations").sort!.uniq!
|
|
154
|
+
entries_for_constant.fetch("files").sort!.uniq!
|
|
134
155
|
end
|
|
135
|
-
|
|
156
|
+
new_entries[package_name] = package_violations.sort.to_h
|
|
136
157
|
end
|
|
137
158
|
|
|
138
|
-
@new_entries =
|
|
159
|
+
@new_entries = new_entries.sort.to_h
|
|
139
160
|
end
|
|
140
161
|
|
|
141
|
-
sig { returns(
|
|
142
|
-
def
|
|
143
|
-
@
|
|
144
|
-
load_yaml(@filepath)
|
|
145
|
-
else
|
|
146
|
-
{}
|
|
147
|
-
end
|
|
162
|
+
sig { returns(Entries) }
|
|
163
|
+
def old_entries
|
|
164
|
+
@old_entries ||= load_yaml_file(@path)
|
|
148
165
|
end
|
|
149
166
|
|
|
150
|
-
sig { params(
|
|
151
|
-
def
|
|
152
|
-
YAML.load_file(
|
|
167
|
+
sig { params(path: String).returns(Entries) }
|
|
168
|
+
def load_yaml_file(path)
|
|
169
|
+
File.exist?(path) && YAML.load_file(path) || {}
|
|
153
170
|
rescue Psych::Exception
|
|
154
171
|
{}
|
|
155
172
|
end
|
data/lib/packwerk/parse_run.rb
CHANGED
|
@@ -14,93 +14,62 @@ module Packwerk
|
|
|
14
14
|
sig do
|
|
15
15
|
params(
|
|
16
16
|
relative_file_set: FilesForProcessing::RelativeFileSet,
|
|
17
|
-
|
|
18
|
-
file_set_specified: T::Boolean,
|
|
19
|
-
offenses_formatter: T.nilable(OffensesFormatter),
|
|
20
|
-
progress_formatter: Formatters::ProgressFormatter,
|
|
17
|
+
parallel: T::Boolean,
|
|
21
18
|
).void
|
|
22
19
|
end
|
|
23
|
-
def initialize(
|
|
24
|
-
relative_file_set:,
|
|
25
|
-
configuration:,
|
|
26
|
-
file_set_specified: false,
|
|
27
|
-
offenses_formatter: nil,
|
|
28
|
-
progress_formatter: Formatters::ProgressFormatter.new(StringIO.new)
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
@configuration = configuration
|
|
32
|
-
@progress_formatter = progress_formatter
|
|
33
|
-
@offenses_formatter = T.let(offenses_formatter || configuration.offenses_formatter, Packwerk::OffensesFormatter)
|
|
20
|
+
def initialize(relative_file_set:, parallel:)
|
|
34
21
|
@relative_file_set = relative_file_set
|
|
35
|
-
@
|
|
22
|
+
@parallel = parallel
|
|
36
23
|
end
|
|
37
24
|
|
|
38
|
-
sig
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
run_context = RunContext.from_configuration(@configuration)
|
|
49
|
-
offense_collection = find_offenses(run_context)
|
|
50
|
-
offense_collection.persist_package_todo_files(run_context.package_set)
|
|
51
|
-
|
|
52
|
-
message = <<~EOS
|
|
53
|
-
#{@offenses_formatter.show_offenses(offense_collection.errors)}
|
|
54
|
-
✅ `package_todo.yml` has been updated.
|
|
55
|
-
EOS
|
|
56
|
-
|
|
57
|
-
Cli::Result.new(message: message, status: offense_collection.errors.empty?)
|
|
25
|
+
sig do
|
|
26
|
+
params(
|
|
27
|
+
run_context: RunContext,
|
|
28
|
+
on_interrupt: T.nilable(T.proc.void),
|
|
29
|
+
block: T.nilable(T.proc.params(
|
|
30
|
+
offenses: T::Array[Packwerk::Offense],
|
|
31
|
+
).void)
|
|
32
|
+
).returns(T::Array[Offense])
|
|
58
33
|
end
|
|
34
|
+
def find_offenses(run_context, on_interrupt: nil, &block)
|
|
35
|
+
process_file_proc = process_file_proc(run_context, &block)
|
|
59
36
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
messages = [
|
|
66
|
-
@offenses_formatter.show_offenses(offense_collection.outstanding_offenses),
|
|
67
|
-
@offenses_formatter.show_stale_violations(offense_collection, @relative_file_set),
|
|
68
|
-
@offenses_formatter.show_strict_mode_violations(offense_collection.strict_mode_violations),
|
|
69
|
-
]
|
|
70
|
-
|
|
71
|
-
result_status = offense_collection.outstanding_offenses.empty? &&
|
|
72
|
-
!offense_collection.stale_violations?(@relative_file_set) && offense_collection.strict_mode_violations.empty?
|
|
37
|
+
offenses = if @parallel
|
|
38
|
+
Parallel.flat_map(@relative_file_set, &process_file_proc)
|
|
39
|
+
else
|
|
40
|
+
serial_find_offenses(on_interrupt: on_interrupt, &process_file_proc)
|
|
41
|
+
end
|
|
73
42
|
|
|
74
|
-
|
|
43
|
+
offenses
|
|
75
44
|
end
|
|
76
45
|
|
|
77
46
|
private
|
|
78
47
|
|
|
79
|
-
sig
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
serial_find_offenses(&process_file)
|
|
95
|
-
end
|
|
48
|
+
sig do
|
|
49
|
+
params(
|
|
50
|
+
run_context: RunContext,
|
|
51
|
+
block: T.nilable(T.proc.params(offenses: T::Array[Offense]).void)
|
|
52
|
+
).returns(ProcessFileProc)
|
|
53
|
+
end
|
|
54
|
+
def process_file_proc(run_context, &block)
|
|
55
|
+
if block
|
|
56
|
+
T.let(proc do |relative_file|
|
|
57
|
+
run_context.process_file(relative_file: relative_file).tap(&block)
|
|
58
|
+
end, ProcessFileProc)
|
|
59
|
+
else
|
|
60
|
+
T.let(proc do |relative_file|
|
|
61
|
+
run_context.process_file(relative_file: relative_file)
|
|
62
|
+
end, ProcessFileProc)
|
|
96
63
|
end
|
|
97
|
-
|
|
98
|
-
all_offenses.each { |offense| offense_collection.add_offense(offense) }
|
|
99
|
-
offense_collection
|
|
100
64
|
end
|
|
101
65
|
|
|
102
|
-
sig
|
|
103
|
-
|
|
66
|
+
sig do
|
|
67
|
+
params(
|
|
68
|
+
on_interrupt: T.nilable(T.proc.void),
|
|
69
|
+
block: ProcessFileProc
|
|
70
|
+
).returns(T::Array[Offense])
|
|
71
|
+
end
|
|
72
|
+
def serial_find_offenses(on_interrupt: nil, &block)
|
|
104
73
|
all_offenses = T.let([], T::Array[Offense])
|
|
105
74
|
begin
|
|
106
75
|
@relative_file_set.each do |relative_file|
|
|
@@ -108,20 +77,11 @@ module Packwerk
|
|
|
108
77
|
all_offenses.concat(offenses)
|
|
109
78
|
end
|
|
110
79
|
rescue Interrupt
|
|
111
|
-
|
|
80
|
+
on_interrupt&.call
|
|
112
81
|
all_offenses
|
|
113
82
|
end
|
|
114
83
|
all_offenses
|
|
115
84
|
end
|
|
116
|
-
|
|
117
|
-
sig { params(failed: T::Boolean).void }
|
|
118
|
-
def update_progress(failed: false)
|
|
119
|
-
if failed
|
|
120
|
-
@progress_formatter.mark_as_failed
|
|
121
|
-
else
|
|
122
|
-
@progress_formatter.mark_as_inspected
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
85
|
end
|
|
126
86
|
|
|
127
87
|
private_constant :ParseRun
|
|
@@ -24,7 +24,7 @@ module Packwerk
|
|
|
24
24
|
def initialize
|
|
25
25
|
@ruby_parser = T.let(nil, T.nilable(ParserInterface))
|
|
26
26
|
@erb_parser = T.let(nil, T.nilable(ParserInterface))
|
|
27
|
-
@erb_parser_class = T.let(nil, T.nilable(Class))
|
|
27
|
+
@erb_parser_class = T.let(nil, T.nilable(T::Class[T.anything]))
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
sig { params(path: String).returns(T.nilable(ParserInterface)) }
|
|
@@ -37,12 +37,12 @@ module Packwerk
|
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
sig { returns(Class) }
|
|
40
|
+
sig { returns(T::Class[T.anything]) }
|
|
41
41
|
def erb_parser_class
|
|
42
42
|
@erb_parser_class ||= Erb
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
sig { params(klass: T.nilable(Class)).void }
|
|
45
|
+
sig { params(klass: T.nilable(T::Class[T.anything])).void }
|
|
46
46
|
def erb_parser_class=(klass)
|
|
47
47
|
@erb_parser_class = klass
|
|
48
48
|
@erb_parser = nil
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "parser"
|
|
5
|
-
require "
|
|
5
|
+
require "prism"
|
|
6
6
|
|
|
7
7
|
module Packwerk
|
|
8
8
|
module Parsers
|
|
@@ -11,7 +11,7 @@ module Packwerk
|
|
|
11
11
|
|
|
12
12
|
include ParserInterface
|
|
13
13
|
|
|
14
|
-
class RaiseExceptionsParser < Parser
|
|
14
|
+
class RaiseExceptionsParser < Prism::Translation::Parser
|
|
15
15
|
extend T::Sig
|
|
16
16
|
|
|
17
17
|
sig { params(builder: T.untyped).void }
|
|
@@ -19,6 +19,13 @@ module Packwerk
|
|
|
19
19
|
super(builder)
|
|
20
20
|
super.diagnostics.all_errors_are_fatal = true
|
|
21
21
|
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
sig { params(error: Prism::ParseError).returns(T::Boolean) }
|
|
26
|
+
def valid_error?(error)
|
|
27
|
+
error.type != :invalid_yield
|
|
28
|
+
end
|
|
22
29
|
end
|
|
23
30
|
|
|
24
31
|
class TolerateInvalidUtf8Builder < Parser::Builders::Default
|
|
@@ -47,9 +47,9 @@ module Packwerk
|
|
|
47
47
|
EOS
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
sig { override.params(
|
|
51
|
-
def strict_mode_violation?(
|
|
52
|
-
referencing_package =
|
|
50
|
+
sig { override.params(offense: ReferenceOffense).returns(T::Boolean) }
|
|
51
|
+
def strict_mode_violation?(offense)
|
|
52
|
+
referencing_package = offense.reference.package
|
|
53
53
|
referencing_package.config["enforce_dependencies"] == "strict"
|
|
54
54
|
end
|
|
55
55
|
|
|
@@ -80,7 +80,12 @@ module Packwerk
|
|
|
80
80
|
constant_name = T.let(nil, T.nilable(String))
|
|
81
81
|
|
|
82
82
|
@constant_name_inspectors.each do |inspector|
|
|
83
|
-
constant_name =
|
|
83
|
+
constant_name = inspect_node(
|
|
84
|
+
inspector,
|
|
85
|
+
node: node,
|
|
86
|
+
ancestors: ancestors,
|
|
87
|
+
relative_file: relative_file
|
|
88
|
+
)
|
|
84
89
|
|
|
85
90
|
break if constant_name
|
|
86
91
|
end
|
|
@@ -97,6 +102,29 @@ module Packwerk
|
|
|
97
102
|
|
|
98
103
|
private
|
|
99
104
|
|
|
105
|
+
sig do
|
|
106
|
+
params(
|
|
107
|
+
inspector: ConstantNameInspector,
|
|
108
|
+
node: Parser::AST::Node,
|
|
109
|
+
ancestors: T::Array[Parser::AST::Node],
|
|
110
|
+
relative_file: String
|
|
111
|
+
).returns(T.nilable(String))
|
|
112
|
+
end
|
|
113
|
+
def inspect_node(inspector, node:, ancestors:, relative_file:)
|
|
114
|
+
inspector.constant_name_from_node(node, ancestors: ancestors, relative_file: relative_file)
|
|
115
|
+
rescue ArgumentError => error
|
|
116
|
+
if error.message == "unknown keyword: :relative_file"
|
|
117
|
+
T.unsafe(inspector).constant_name_from_node(node, ancestors: ancestors).tap do
|
|
118
|
+
warn(<<~MSG.squish)
|
|
119
|
+
#{T.cast(inspector, Object).class}#reference_from_node without a relative_file: keyword
|
|
120
|
+
argument is deprecated and will be required in Packwerk 3.1.1.
|
|
121
|
+
MSG
|
|
122
|
+
end
|
|
123
|
+
else
|
|
124
|
+
raise
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
100
128
|
sig do
|
|
101
129
|
params(
|
|
102
130
|
constant_name: String,
|
|
@@ -23,7 +23,7 @@ module Packwerk
|
|
|
23
23
|
.void
|
|
24
24
|
end
|
|
25
25
|
def initialize(reference:, violation_type:, message:, location: nil)
|
|
26
|
-
super(file: reference.relative_path, message: message, location: location)
|
|
26
|
+
super(file: T.must(reference.relative_path), message: message, location: location)
|
|
27
27
|
@reference = reference
|
|
28
28
|
@violation_type = violation_type
|
|
29
29
|
end
|
data/lib/packwerk/run_context.rb
CHANGED
|
@@ -15,14 +15,13 @@ module Packwerk
|
|
|
15
15
|
params(configuration: Configuration).returns(RunContext)
|
|
16
16
|
end
|
|
17
17
|
def from_configuration(configuration)
|
|
18
|
-
inflector = ActiveSupport::Inflector
|
|
19
|
-
|
|
20
18
|
new(
|
|
21
19
|
root_path: configuration.root_path,
|
|
22
20
|
load_paths: configuration.load_paths,
|
|
23
21
|
package_paths: configuration.package_paths,
|
|
24
|
-
inflector:
|
|
22
|
+
inflector: ActiveSupport::Inflector,
|
|
25
23
|
custom_associations: configuration.custom_associations,
|
|
24
|
+
associations_exclude: configuration.associations_exclude,
|
|
26
25
|
cache_enabled: configuration.cache_enabled?,
|
|
27
26
|
cache_directory: configuration.cache_directory,
|
|
28
27
|
config_path: configuration.config_path,
|
|
@@ -39,6 +38,7 @@ module Packwerk
|
|
|
39
38
|
config_path: T.nilable(String),
|
|
40
39
|
package_paths: T.nilable(T.any(T::Array[String], String)),
|
|
41
40
|
custom_associations: AssociationInspector::CustomAssociations,
|
|
41
|
+
associations_exclude: T::Array[String],
|
|
42
42
|
checkers: T::Array[Checker],
|
|
43
43
|
cache_enabled: T::Boolean,
|
|
44
44
|
).void
|
|
@@ -51,6 +51,7 @@ module Packwerk
|
|
|
51
51
|
config_path: nil,
|
|
52
52
|
package_paths: nil,
|
|
53
53
|
custom_associations: [],
|
|
54
|
+
associations_exclude: [],
|
|
54
55
|
checkers: Checker.all,
|
|
55
56
|
cache_enabled: false
|
|
56
57
|
)
|
|
@@ -59,6 +60,7 @@ module Packwerk
|
|
|
59
60
|
@package_paths = package_paths
|
|
60
61
|
@inflector = inflector
|
|
61
62
|
@custom_associations = custom_associations
|
|
63
|
+
@associations_exclude = associations_exclude
|
|
62
64
|
@checkers = checkers
|
|
63
65
|
@cache_enabled = cache_enabled
|
|
64
66
|
@cache_directory = cache_directory
|
|
@@ -128,9 +130,18 @@ module Packwerk
|
|
|
128
130
|
def constant_name_inspectors
|
|
129
131
|
[
|
|
130
132
|
ConstNodeInspector.new,
|
|
131
|
-
AssociationInspector.new(
|
|
133
|
+
AssociationInspector.new(
|
|
134
|
+
inflector: @inflector,
|
|
135
|
+
custom_associations: @custom_associations,
|
|
136
|
+
excluded_files: relative_files_for_globs(@associations_exclude),
|
|
137
|
+
),
|
|
132
138
|
]
|
|
133
139
|
end
|
|
140
|
+
|
|
141
|
+
sig { params(relative_globs: T::Array[String]).returns(FilesForProcessing::RelativeFileSet) }
|
|
142
|
+
def relative_files_for_globs(relative_globs)
|
|
143
|
+
Set.new(relative_globs.flat_map { |glob| Dir[glob] })
|
|
144
|
+
end
|
|
134
145
|
end
|
|
135
146
|
|
|
136
147
|
private_constant :RunContext
|
data/lib/packwerk/validator.rb
CHANGED
|
@@ -9,22 +9,36 @@ module Packwerk
|
|
|
9
9
|
module Validator
|
|
10
10
|
extend T::Sig
|
|
11
11
|
extend T::Helpers
|
|
12
|
+
extend ActiveSupport::Autoload
|
|
13
|
+
|
|
14
|
+
autoload :Result
|
|
12
15
|
|
|
13
16
|
abstract!
|
|
14
17
|
|
|
15
18
|
class << self
|
|
16
19
|
extend T::Sig
|
|
17
20
|
|
|
18
|
-
sig { params(base: Class).void }
|
|
21
|
+
sig { params(base: T::Class[T.anything]).void }
|
|
19
22
|
def included(base)
|
|
20
|
-
|
|
21
|
-
@validators ||= []
|
|
22
|
-
@validators << base
|
|
23
|
+
validators << base
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
sig { returns(T::Array[Validator]) }
|
|
26
27
|
def all
|
|
27
|
-
|
|
28
|
+
load_defaults
|
|
29
|
+
T.cast(validators.map(&:new), T::Array[Validator])
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
sig { void }
|
|
35
|
+
def load_defaults
|
|
36
|
+
require("packwerk/validators/dependency_validator")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
sig { returns(T::Array[T::Class[T.anything]]) }
|
|
40
|
+
def validators
|
|
41
|
+
@validators ||= T.let([], T.nilable(T::Array[T::Class[T.anything]]))
|
|
28
42
|
end
|
|
29
43
|
end
|
|
30
44
|
|
data/lib/packwerk/version.rb
CHANGED