packwerk 2.1.0 → 2.1.1
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/Gemfile.lock +20 -17
- data/lib/packwerk/application_validator.rb +78 -39
- data/lib/packwerk/cache.rb +2 -1
- data/lib/packwerk/cli.rb +22 -13
- data/lib/packwerk/constant_discovery.rb +17 -1
- data/lib/packwerk/constant_name_inspector.rb +1 -1
- data/lib/packwerk/file_processor.rb +20 -15
- data/lib/packwerk/files_for_processing.rb +49 -22
- data/lib/packwerk/node_processor.rb +5 -5
- data/lib/packwerk/node_processor_factory.rb +3 -3
- data/lib/packwerk/offense.rb +10 -2
- data/lib/packwerk/package_set.rb +2 -2
- data/lib/packwerk/parse_run.rb +37 -17
- data/lib/packwerk/parsers/erb.rb +2 -0
- data/lib/packwerk/parsers/factory.rb +2 -0
- data/lib/packwerk/parsers/parser_interface.rb +17 -0
- data/lib/packwerk/parsers/ruby.rb +2 -0
- data/lib/packwerk/parsers.rb +1 -0
- data/lib/packwerk/reference_checking/checkers/checker.rb +1 -1
- data/lib/packwerk/reference_checking/reference_checker.rb +2 -1
- data/lib/packwerk/reference_extractor.rb +20 -9
- data/lib/packwerk/reference_offense.rb +8 -3
- data/lib/packwerk/result.rb +2 -2
- data/lib/packwerk/run_context.rb +47 -41
- data/lib/packwerk/spring_command.rb +1 -1
- data/lib/packwerk/version.rb +1 -1
- data/sorbet/config +1 -0
- data/sorbet/rbi/gems/tapioca@0.4.19.rbi +1 -1
- data/sorbet/tapioca/require.rb +1 -1
- metadata +3 -2
@@ -1,20 +1,40 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module Packwerk
|
5
5
|
class FilesForProcessing
|
6
|
+
extend T::Sig
|
7
|
+
|
6
8
|
class << self
|
7
|
-
|
8
|
-
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
sig do
|
12
|
+
params(
|
13
|
+
relative_file_paths: T::Array[String],
|
14
|
+
configuration: Configuration,
|
15
|
+
ignore_nested_packages: T::Boolean
|
16
|
+
).returns(T::Array[String])
|
17
|
+
end
|
18
|
+
def fetch(relative_file_paths:, configuration:, ignore_nested_packages: false)
|
19
|
+
new(relative_file_paths, configuration, ignore_nested_packages).files
|
9
20
|
end
|
10
21
|
end
|
11
22
|
|
12
|
-
|
13
|
-
|
23
|
+
sig do
|
24
|
+
params(
|
25
|
+
relative_file_paths: T::Array[String],
|
26
|
+
configuration: Configuration,
|
27
|
+
ignore_nested_packages: T::Boolean
|
28
|
+
).void
|
29
|
+
end
|
30
|
+
def initialize(relative_file_paths, configuration, ignore_nested_packages)
|
31
|
+
@relative_file_paths = relative_file_paths
|
14
32
|
@configuration = configuration
|
15
33
|
@ignore_nested_packages = ignore_nested_packages
|
34
|
+
@custom_files = T.let(nil, T.nilable(T::Array[String]))
|
16
35
|
end
|
17
36
|
|
37
|
+
sig { returns(T::Array[String]) }
|
18
38
|
def files
|
19
39
|
include_files = if custom_files.empty?
|
20
40
|
configured_included_files
|
@@ -27,50 +47,57 @@ module Packwerk
|
|
27
47
|
|
28
48
|
private
|
29
49
|
|
50
|
+
sig { returns(T::Array[String]) }
|
30
51
|
def custom_files
|
31
|
-
@custom_files ||= @
|
32
|
-
|
33
|
-
if File.file?(
|
34
|
-
|
52
|
+
@custom_files ||= @relative_file_paths.flat_map do |relative_file_path|
|
53
|
+
absolute_file_path = File.expand_path(relative_file_path, @configuration.root_path)
|
54
|
+
if File.file?(absolute_file_path)
|
55
|
+
absolute_file_path
|
35
56
|
else
|
36
|
-
custom_included_files(
|
57
|
+
custom_included_files(absolute_file_path)
|
37
58
|
end
|
38
59
|
end
|
39
60
|
end
|
40
61
|
|
41
|
-
|
62
|
+
sig { params(absolute_file_path: String).returns(T::Array[String]) }
|
63
|
+
def custom_included_files(absolute_file_path)
|
42
64
|
# Note, assuming include globs are always relative paths
|
43
65
|
absolute_includes = @configuration.include.map do |glob|
|
44
66
|
File.expand_path(glob, @configuration.root_path)
|
45
67
|
end
|
46
68
|
|
47
|
-
|
69
|
+
absolute_files = Dir.glob([File.join(absolute_file_path, "**", "*")]).select do |absolute_path|
|
48
70
|
absolute_includes.any? do |pattern|
|
49
|
-
File.fnmatch?(pattern,
|
71
|
+
File.fnmatch?(pattern, absolute_path, File::FNM_EXTGLOB)
|
50
72
|
end
|
51
73
|
end
|
52
74
|
|
53
75
|
if @ignore_nested_packages
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
76
|
+
nested_packages_absolute_file_paths = Dir.glob(File.join(absolute_file_path, "*", "**", "package.yml"))
|
77
|
+
nested_packages_absolute_globs = nested_packages_absolute_file_paths.map do |npp|
|
78
|
+
npp.gsub("package.yml", "**/*")
|
79
|
+
end
|
80
|
+
nested_packages_absolute_globs.each do |absolute_glob|
|
81
|
+
absolute_files -= Dir.glob(absolute_glob)
|
58
82
|
end
|
59
83
|
end
|
60
84
|
|
61
|
-
|
85
|
+
absolute_files
|
62
86
|
end
|
63
87
|
|
88
|
+
sig { returns(T::Array[String]) }
|
64
89
|
def configured_included_files
|
65
|
-
|
90
|
+
absolute_files_for_globs(@configuration.include)
|
66
91
|
end
|
67
92
|
|
93
|
+
sig { returns(T::Array[String]) }
|
68
94
|
def configured_excluded_files
|
69
|
-
|
95
|
+
absolute_files_for_globs(@configuration.exclude)
|
70
96
|
end
|
71
97
|
|
72
|
-
|
73
|
-
|
98
|
+
sig { params(relative_globs: T::Array[String]).returns(T::Array[String]) }
|
99
|
+
def absolute_files_for_globs(relative_globs)
|
100
|
+
relative_globs
|
74
101
|
.flat_map { |glob| Dir[File.expand_path(glob, @configuration.root_path)] }
|
75
102
|
.uniq
|
76
103
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module Packwerk
|
@@ -9,12 +9,12 @@ module Packwerk
|
|
9
9
|
sig do
|
10
10
|
params(
|
11
11
|
reference_extractor: ReferenceExtractor,
|
12
|
-
|
12
|
+
absolute_file: String,
|
13
13
|
).void
|
14
14
|
end
|
15
|
-
def initialize(reference_extractor:,
|
15
|
+
def initialize(reference_extractor:, absolute_file:)
|
16
16
|
@reference_extractor = reference_extractor
|
17
|
-
@
|
17
|
+
@absolute_file = absolute_file
|
18
18
|
end
|
19
19
|
|
20
20
|
sig do
|
@@ -25,7 +25,7 @@ module Packwerk
|
|
25
25
|
end
|
26
26
|
def call(node, ancestors)
|
27
27
|
return unless Node.method_call?(node) || Node.constant?(node)
|
28
|
-
@reference_extractor.reference_from_node(node, ancestors: ancestors,
|
28
|
+
@reference_extractor.reference_from_node(node, ancestors: ancestors, absolute_file: @absolute_file)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -9,11 +9,11 @@ module Packwerk
|
|
9
9
|
const :context_provider, Packwerk::ConstantDiscovery
|
10
10
|
const :constant_name_inspectors, T::Array[ConstantNameInspector]
|
11
11
|
|
12
|
-
sig { params(
|
13
|
-
def for(
|
12
|
+
sig { params(absolute_file: String, node: AST::Node).returns(NodeProcessor) }
|
13
|
+
def for(absolute_file:, node:)
|
14
14
|
::Packwerk::NodeProcessor.new(
|
15
15
|
reference_extractor: reference_extractor(node: node),
|
16
|
-
|
16
|
+
absolute_file: absolute_file,
|
17
17
|
)
|
18
18
|
end
|
19
19
|
|
data/lib/packwerk/offense.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "parser/source/map"
|
@@ -8,7 +8,14 @@ module Packwerk
|
|
8
8
|
extend T::Sig
|
9
9
|
extend T::Helpers
|
10
10
|
|
11
|
-
|
11
|
+
sig { returns(T.nilable(Node::Location)) }
|
12
|
+
attr_reader :location
|
13
|
+
|
14
|
+
sig { returns(String) }
|
15
|
+
attr_reader :file
|
16
|
+
|
17
|
+
sig { returns(String) }
|
18
|
+
attr_reader :message
|
12
19
|
|
13
20
|
sig do
|
14
21
|
params(file: String, message: String, location: T.nilable(Node::Location))
|
@@ -22,6 +29,7 @@ module Packwerk
|
|
22
29
|
|
23
30
|
sig { params(style: OutputStyle).returns(String) }
|
24
31
|
def to_s(style = OutputStyles::Plain.new)
|
32
|
+
location = self.location
|
25
33
|
if location
|
26
34
|
<<~EOS
|
27
35
|
#{style.filename}#{file}#{style.reset}:#{location.line}:#{location.column}
|
data/lib/packwerk/package_set.rb
CHANGED
@@ -92,10 +92,10 @@ module Packwerk
|
|
92
92
|
packages[name]
|
93
93
|
end
|
94
94
|
|
95
|
-
sig { params(file_path: T.any(Pathname, String)).returns(
|
95
|
+
sig { params(file_path: T.any(Pathname, String)).returns(Package) }
|
96
96
|
def package_from_path(file_path)
|
97
97
|
path_string = file_path.to_s
|
98
|
-
@package_from_path[path_string] ||= packages.values.find { |package| package.package_path?(path_string) }
|
98
|
+
@package_from_path[path_string] ||= T.must(packages.values.find { |package| package.package_path?(path_string) })
|
99
99
|
end
|
100
100
|
end
|
101
101
|
end
|
data/lib/packwerk/parse_run.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "benchmark"
|
@@ -8,8 +8,20 @@ module Packwerk
|
|
8
8
|
class ParseRun
|
9
9
|
extend T::Sig
|
10
10
|
|
11
|
+
ProcessFileProc = T.type_alias do
|
12
|
+
T.proc.params(path: String).returns(T::Array[Offense])
|
13
|
+
end
|
14
|
+
|
15
|
+
sig do
|
16
|
+
params(
|
17
|
+
absolute_files: T::Array[String],
|
18
|
+
configuration: Configuration,
|
19
|
+
progress_formatter: Formatters::ProgressFormatter,
|
20
|
+
offenses_formatter: OffensesFormatter,
|
21
|
+
).void
|
22
|
+
end
|
11
23
|
def initialize(
|
12
|
-
|
24
|
+
absolute_files:,
|
13
25
|
configuration:,
|
14
26
|
progress_formatter: Formatters::ProgressFormatter.new(StringIO.new),
|
15
27
|
offenses_formatter: Formatters::OffensesFormatter.new
|
@@ -17,9 +29,10 @@ module Packwerk
|
|
17
29
|
@configuration = configuration
|
18
30
|
@progress_formatter = progress_formatter
|
19
31
|
@offenses_formatter = offenses_formatter
|
20
|
-
@
|
32
|
+
@absolute_files = absolute_files
|
21
33
|
end
|
22
34
|
|
35
|
+
sig { returns(Result) }
|
23
36
|
def detect_stale_violations
|
24
37
|
offense_collection = find_offenses
|
25
38
|
|
@@ -29,6 +42,7 @@ module Packwerk
|
|
29
42
|
Result.new(message: message, status: result_status)
|
30
43
|
end
|
31
44
|
|
45
|
+
sig { returns(Result) }
|
32
46
|
def update_deprecations
|
33
47
|
offense_collection = find_offenses
|
34
48
|
offense_collection.dump_deprecated_references_files
|
@@ -41,6 +55,7 @@ module Packwerk
|
|
41
55
|
Result.new(message: message, status: offense_collection.errors.empty?)
|
42
56
|
end
|
43
57
|
|
58
|
+
sig { returns(Result) }
|
44
59
|
def check
|
45
60
|
offense_collection = find_offenses(show_errors: true)
|
46
61
|
|
@@ -55,23 +70,24 @@ module Packwerk
|
|
55
70
|
|
56
71
|
private
|
57
72
|
|
73
|
+
sig { params(show_errors: T::Boolean).returns(OffenseCollection) }
|
58
74
|
def find_offenses(show_errors: false)
|
59
75
|
offense_collection = OffenseCollection.new(@configuration.root_path)
|
60
|
-
@progress_formatter.started(@
|
76
|
+
@progress_formatter.started(@absolute_files)
|
61
77
|
|
62
78
|
run_context = Packwerk::RunContext.from_configuration(@configuration)
|
63
|
-
all_offenses = T.let([], T
|
79
|
+
all_offenses = T.let([], T::Array[Offense])
|
64
80
|
|
65
|
-
process_file = -> (
|
66
|
-
run_context.process_file(
|
81
|
+
process_file = T.let(-> (absolute_file) do
|
82
|
+
run_context.process_file(absolute_file: absolute_file).tap do |offenses|
|
67
83
|
failed = show_errors && offenses.any? { |offense| !offense_collection.listed?(offense) }
|
68
84
|
update_progress(failed: failed)
|
69
85
|
end
|
70
|
-
end
|
86
|
+
end, ProcessFileProc)
|
71
87
|
|
72
88
|
execution_time = Benchmark.realtime do
|
73
89
|
all_offenses = if @configuration.parallel?
|
74
|
-
Parallel.flat_map(@
|
90
|
+
Parallel.flat_map(@absolute_files, &process_file)
|
75
91
|
else
|
76
92
|
serial_find_offenses(&process_file)
|
77
93
|
end
|
@@ -83,18 +99,22 @@ module Packwerk
|
|
83
99
|
offense_collection
|
84
100
|
end
|
85
101
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
102
|
+
sig { params(block: ProcessFileProc).returns(T::Array[Offense]) }
|
103
|
+
def serial_find_offenses(&block)
|
104
|
+
all_offenses = T.let([], T::Array[Offense])
|
105
|
+
begin
|
106
|
+
@absolute_files.each do |absolute_file|
|
107
|
+
offenses = block.call(absolute_file)
|
108
|
+
all_offenses.concat(offenses)
|
109
|
+
end
|
110
|
+
rescue Interrupt
|
111
|
+
@progress_formatter.interrupted
|
112
|
+
all_offenses
|
91
113
|
end
|
92
114
|
all_offenses
|
93
|
-
rescue Interrupt
|
94
|
-
@progress_formatter.interrupted
|
95
|
-
all_offenses
|
96
115
|
end
|
97
116
|
|
117
|
+
sig { params(failed: T::Boolean).void }
|
98
118
|
def update_progress(failed: false)
|
99
119
|
if failed
|
100
120
|
@progress_formatter.mark_as_failed
|
data/lib/packwerk/parsers/erb.rb
CHANGED
@@ -6,6 +6,7 @@ require "singleton"
|
|
6
6
|
module Packwerk
|
7
7
|
module Parsers
|
8
8
|
class Factory
|
9
|
+
extend T::Sig
|
9
10
|
include Singleton
|
10
11
|
|
11
12
|
RUBY_REGEX = %r{
|
@@ -19,6 +20,7 @@ module Packwerk
|
|
19
20
|
ERB_REGEX = /\.erb\Z/
|
20
21
|
private_constant :ERB_REGEX
|
21
22
|
|
23
|
+
sig { params(path: String).returns(T.nilable(ParserInterface)) }
|
22
24
|
def for_path(path)
|
23
25
|
case path
|
24
26
|
when RUBY_REGEX
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Parsers
|
6
|
+
module ParserInterface
|
7
|
+
extend T::Helpers
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
interface!
|
11
|
+
|
12
|
+
sig { abstract.params(io: File, file_path: String).returns(T.untyped) }
|
13
|
+
def call(io:, file_path:)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/packwerk/parsers.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module Packwerk
|
@@ -6,6 +6,7 @@ module Packwerk
|
|
6
6
|
class ReferenceChecker
|
7
7
|
extend T::Sig
|
8
8
|
|
9
|
+
sig { params(checkers: T::Array[Checkers::Checker]).void }
|
9
10
|
def initialize(checkers)
|
10
11
|
@checkers = checkers
|
11
12
|
end
|
@@ -27,10 +27,10 @@ module Packwerk
|
|
27
27
|
params(
|
28
28
|
node: Parser::AST::Node,
|
29
29
|
ancestors: T::Array[Parser::AST::Node],
|
30
|
-
|
30
|
+
absolute_file: String
|
31
31
|
).returns(T.nilable(UnresolvedReference))
|
32
32
|
end
|
33
|
-
def reference_from_node(node, ancestors:,
|
33
|
+
def reference_from_node(node, ancestors:, absolute_file:)
|
34
34
|
constant_name = T.let(nil, T.nilable(String))
|
35
35
|
|
36
36
|
@constant_name_inspectors.each do |inspector|
|
@@ -39,7 +39,14 @@ module Packwerk
|
|
39
39
|
break if constant_name
|
40
40
|
end
|
41
41
|
|
42
|
-
|
42
|
+
if constant_name
|
43
|
+
reference_from_constant(
|
44
|
+
constant_name,
|
45
|
+
node: node,
|
46
|
+
ancestors: ancestors,
|
47
|
+
absolute_file: absolute_file
|
48
|
+
)
|
49
|
+
end
|
43
50
|
end
|
44
51
|
|
45
52
|
sig do
|
@@ -66,11 +73,15 @@ module Packwerk
|
|
66
73
|
current_namespace_path: unresolved_reference.namespace_path
|
67
74
|
)
|
68
75
|
|
69
|
-
next if constant
|
76
|
+
next if constant.nil?
|
77
|
+
|
78
|
+
package_for_constant = constant.package
|
79
|
+
|
80
|
+
next if package_for_constant.nil?
|
70
81
|
|
71
82
|
source_package = context_provider.package_from_path(unresolved_reference.relative_path)
|
72
83
|
|
73
|
-
next if source_package ==
|
84
|
+
next if source_package == package_for_constant
|
74
85
|
|
75
86
|
fully_qualified_references_and_offenses << Reference.new(
|
76
87
|
source_package,
|
@@ -90,21 +101,21 @@ module Packwerk
|
|
90
101
|
constant_name: String,
|
91
102
|
node: Parser::AST::Node,
|
92
103
|
ancestors: T::Array[Parser::AST::Node],
|
93
|
-
|
104
|
+
absolute_file: String
|
94
105
|
).returns(T.nilable(UnresolvedReference))
|
95
106
|
end
|
96
|
-
def reference_from_constant(constant_name, node:, ancestors:,
|
107
|
+
def reference_from_constant(constant_name, node:, ancestors:, absolute_file:)
|
97
108
|
namespace_path = Node.enclosing_namespace_path(node, ancestors: ancestors)
|
98
109
|
|
99
110
|
return if local_reference?(constant_name, Node.name_location(node), namespace_path)
|
100
111
|
|
101
|
-
|
112
|
+
relative_file = Pathname.new(absolute_file).relative_path_from(@root_path).to_s
|
102
113
|
location = Node.location(node)
|
103
114
|
|
104
115
|
UnresolvedReference.new(
|
105
116
|
constant_name,
|
106
117
|
namespace_path,
|
107
|
-
|
118
|
+
relative_file,
|
108
119
|
location
|
109
120
|
)
|
110
121
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module Packwerk
|
@@ -7,7 +7,11 @@ module Packwerk
|
|
7
7
|
extend T::Sig
|
8
8
|
extend T::Helpers
|
9
9
|
|
10
|
-
|
10
|
+
sig { returns(Reference) }
|
11
|
+
attr_reader :reference
|
12
|
+
|
13
|
+
sig { returns(ViolationType) }
|
14
|
+
attr_reader :violation_type
|
11
15
|
|
12
16
|
sig do
|
13
17
|
params(
|
@@ -25,10 +29,11 @@ module Packwerk
|
|
25
29
|
|
26
30
|
private
|
27
31
|
|
32
|
+
sig { params(reference: Reference, violation_type: ViolationType).returns(String) }
|
28
33
|
def build_message(reference, violation_type)
|
29
34
|
violation_message = case violation_type
|
30
35
|
when ViolationType::Privacy
|
31
|
-
source_desc =
|
36
|
+
source_desc = "'#{reference.source_package}'"
|
32
37
|
"Privacy violation: '#{reference.constant.name}' is private to '#{reference.constant.package}' but " \
|
33
38
|
"referenced from #{source_desc}.\n" \
|
34
39
|
"Is there a public entrypoint in '#{reference.constant.package.public_path}' that you can use instead?"
|