@@ -11,6 +11,13 @@ module Packwerk
11
11
class ApplicationValidator
12
12
extend T ::Sig
13
13
14
+ # This is a temporary API as we migrate validators to their own files.
15
+ # Later, we can expose an API to get package sets to pass into validators when testing
16
+ # This API would likely just be `PackageSet.load_all_from(configruation)`, but we might want to clean
17
+ # up that API a bit (it looks like there are some unnecessary input variables).
18
+ sig { returns ( PackageSet ) }
19
+ attr_reader :package_set
20
+
14
21
sig do
15
22
params (
16
23
config_file_path : String ,
@@ -22,26 +29,18 @@ def initialize(config_file_path:, configuration:, environment:)
22
29
@config_file_path = config_file_path
23
30
@configuration = configuration
24
31
@environment = environment
25
- @package_set = T . let ( PackageSet . load_all_from ( @configuration . root_path , package_pathspec : package_glob ) ,
26
- PackageSet )
27
- end
28
-
29
- class Result < T ::Struct
30
- extend T ::Sig
31
-
32
- const :ok , T ::Boolean
33
- const :error_value , T . nilable ( String )
32
+ package_set = PackageSet . load_all_from (
33
+ @configuration . root_path ,
34
+ package_pathspec : Helpers . package_glob ( configuration )
35
+ )
34
36
35
- sig { returns ( T ::Boolean ) }
36
- def ok?
37
- ok
38
- end
37
+ @package_set = T . let ( package_set , PackageSet )
39
38
end
40
39
41
40
sig { returns ( Result ) }
42
41
def check_all
43
42
results = [
44
- check_package_manifests_for_privacy ,
43
+ CheckPackageManifestsForPrivacy . call ( @package_set , @configuration ) ,
45
44
check_package_manifest_syntax ,
46
45
check_application_structure ,
47
46
check_acyclic_graph ,
@@ -50,50 +49,23 @@ def check_all
50
49
check_root_package_exists ,
51
50
]
52
51
53
- merge_results ( results )
54
- end
55
-
56
- sig { returns ( Result ) }
57
- def check_package_manifests_for_privacy
58
- privacy_settings = package_manifests_settings_for ( "enforce_privacy" )
59
-
60
- resolver = ConstantResolver . new (
61
- root_path : @configuration . root_path ,
62
- load_paths : @configuration . load_paths
63
- )
64
-
65
- results = T . let ( [ ] , T ::Array [ Result ] )
66
-
67
- privacy_settings . each do |config_file_path , setting |
68
- next unless setting . is_a? ( Array )
69
-
70
- constants = setting
71
-
72
- results += assert_constants_can_be_loaded ( constants , config_file_path )
73
-
74
- constant_locations = constants . map { |c | [ c , resolver . resolve ( c ) &.location ] }
75
-
76
- constant_locations . each do |name , location |
77
- results << if location
78
- check_private_constant_location ( name , location , config_file_path )
79
- else
80
- private_constant_unresolvable ( name , config_file_path )
81
- end
82
- end
83
- end
84
-
85
- merge_results ( results , separator : "\n ---\n " )
52
+ Helpers . merge_results ( results )
86
53
end
87
54
88
55
sig { returns ( Result ) }
89
56
def check_package_manifest_syntax
90
57
errors = [ ]
91
58
92
- package_manifests . each do |f |
59
+ Helpers . package_manifests ( @configuration ) . each do |f |
93
60
hash = YAML . load_file ( f )
94
61
next unless hash
95
62
96
- known_keys = [ "enforce_privacy" , "enforce_dependencies" , "public_path" , "dependencies" , "metadata" ]
63
+ known_keys = [
64
+ *CheckPackageManifestsForPrivacy . permitted_keys ,
65
+ "enforce_dependencies" ,
66
+ "dependencies" ,
67
+ "metadata" ,
68
+ ]
97
69
unknown_keys = hash . keys - known_keys
98
70
99
71
unless unknown_keys . empty?
@@ -102,24 +74,12 @@ def check_package_manifest_syntax
102
74
"open an issue in https://github.com/Shopify/packwerk"
103
75
end
104
76
105
- if hash . key? ( "enforce_privacy" )
106
- unless [ TrueClass , FalseClass , Array ] . include? ( hash [ "enforce_privacy" ] . class )
107
- errors << "Invalid 'enforce_privacy' option in #{ f . inspect } : #{ hash [ "enforce_privacy" ] . inspect } "
108
- end
109
- end
110
-
111
77
if hash . key? ( "enforce_dependencies" )
112
78
unless [ TrueClass , FalseClass ] . include? ( hash [ "enforce_dependencies" ] . class )
113
79
errors << "Invalid 'enforce_dependencies' option in #{ f . inspect } : #{ hash [ "enforce_dependencies" ] . inspect } "
114
80
end
115
81
end
116
82
117
- if hash . key? ( "public_path" )
118
- unless hash [ "public_path" ] . is_a? ( String )
119
- errors << "'public_path' option must be a string in #{ f . inspect } : #{ hash [ "public_path" ] . inspect } "
120
- end
121
- end
122
-
123
83
next unless hash . key? ( "dependencies" )
124
84
next if hash [ "dependencies" ] . is_a? ( Array )
125
85
@@ -173,8 +133,8 @@ def check_acyclic_graph
173
133
174
134
sig { returns ( Result ) }
175
135
def check_package_manifest_paths
176
- all_package_manifests = package_manifests ( "**/" )
177
- package_paths_package_manifests = package_manifests ( package_glob )
136
+ all_package_manifests = Helpers . package_manifests ( @configuration , "**/" )
137
+ package_paths_package_manifests = Helpers . package_manifests ( @configuration , Helpers . package_glob ( @configuration ) )
178
138
179
139
difference = all_package_manifests - package_paths_package_manifests
180
140
@@ -194,7 +154,7 @@ def check_package_manifest_paths
194
154
195
155
sig { returns ( Result ) }
196
156
def check_valid_package_dependencies
197
- packages_dependencies = package_manifests_settings_for ( "dependencies" )
157
+ packages_dependencies = Helpers . package_manifests_settings_for ( @configuration , "dependencies" )
198
158
. delete_if { |_ , deps | deps . nil? }
199
159
200
160
packages_with_invalid_dependencies =
@@ -231,7 +191,7 @@ def check_valid_package_dependencies
231
191
sig { returns ( Result ) }
232
192
def check_root_package_exists
233
193
root_package_path = File . join ( @configuration . root_path , "package.yml" )
234
- all_packages_manifests = package_manifests ( package_glob )
194
+ all_packages_manifests = Helpers . package_manifests ( @configuration , Helpers . package_glob ( @configuration ) )
235
195
236
196
if all_packages_manifests . include? ( root_package_path )
237
197
Result . new ( ok : true )
@@ -263,35 +223,14 @@ def build_cycle_strings(cycles)
263
223
end
264
224
end
265
225
266
- sig { params ( setting : T . untyped ) . returns ( T . untyped ) }
267
- def package_manifests_settings_for ( setting )
268
- package_manifests . map { |f | [ f , ( YAML . load_file ( File . join ( f ) ) || { } ) [ setting ] ] }
269
- end
270
-
271
226
sig { params ( list : T . untyped ) . returns ( T . untyped ) }
272
227
def format_yaml_strings ( list )
273
228
list . sort . map { |p | "- \" #{ p } \" " } . join ( "\n " )
274
229
end
275
230
276
- sig { returns ( T . any ( T ::Array [ String ] , String ) ) }
277
- def package_glob
278
- @configuration . package_paths || "**"
279
- end
280
-
281
- sig { params ( glob_pattern : T . any ( T ::Array [ String ] , String ) ) . returns ( T ::Array [ String ] ) }
282
- def package_manifests ( glob_pattern = package_glob )
283
- PackageSet . package_paths ( @configuration . root_path , glob_pattern , @configuration . exclude )
284
- . map { |f | File . realpath ( f ) }
285
- end
286
-
287
231
sig { params ( paths : T ::Array [ String ] ) . returns ( T ::Array [ Pathname ] ) }
288
232
def relative_paths ( paths )
289
- paths . map { |path | relative_path ( path ) }
290
- end
291
-
292
- sig { params ( path : String ) . returns ( Pathname ) }
293
- def relative_path ( path )
294
- Pathname . new ( path ) . relative_path_from ( @configuration . root_path )
233
+ paths . map { |path | Helpers . relative_path ( @configuration , path ) }
295
234
end
296
235
297
236
sig { params ( path : T . untyped ) . returns ( T ::Boolean ) }
@@ -302,65 +241,5 @@ def invalid_package_path?(path)
302
241
package_path = File . join ( @configuration . root_path , path , PackageSet ::PACKAGE_CONFIG_FILENAME )
303
242
!File . file? ( package_path )
304
243
end
305
-
306
- sig { params ( constants : T . untyped , config_file_path : String ) . returns ( T ::Array [ Result ] ) }
307
- def assert_constants_can_be_loaded ( constants , config_file_path )
308
- constants . map do |constant |
309
- if !constant . start_with? ( "::" )
310
- Result . new (
311
- ok : false ,
312
- error_value : "'#{ constant } ', listed in the 'enforce_privacy' option in #{ config_file_path } , is invalid.\n " \
313
- "Private constants need to be prefixed with the top-level namespace operator `::`."
314
- )
315
- else
316
- constant . try ( &:constantize ) && Result . new ( ok : true )
317
- end
318
- end
319
- end
320
-
321
- sig { params ( name : T . untyped , config_file_path : T . untyped ) . returns ( Result ) }
322
- def private_constant_unresolvable ( name , config_file_path )
323
- explicit_filepath = ( name . start_with? ( "::" ) ? name [ 2 ..-1 ] : name ) . underscore + ".rb"
324
-
325
- Result . new (
326
- ok : false ,
327
- error_value : "'#{ name } ', listed in #{ config_file_path } , could not be resolved.\n " \
328
- "This is probably because it is an autovivified namespace - a namespace module that doesn't have a\n " \
329
- "file explicitly defining it. Packwerk currently doesn't support declaring autovivified namespaces as\n " \
330
- "private. Add a #{ explicit_filepath } file to explicitly define the constant."
331
- )
332
- end
333
-
334
- sig { params ( name : T . untyped , location : T . untyped , config_file_path : T . untyped ) . returns ( Result ) }
335
- def check_private_constant_location ( name , location , config_file_path )
336
- declared_package = @package_set . package_from_path ( relative_path ( config_file_path ) )
337
- constant_package = @package_set . package_from_path ( location )
338
-
339
- if constant_package == declared_package
340
- Result . new ( ok : true )
341
- else
342
- Result . new (
343
- ok : false ,
344
- error_value : "'#{ name } ' is declared as private in the '#{ declared_package } ' package but appears to be " \
345
- "defined\n in the '#{ constant_package } ' package. Packwerk resolved it to #{ location } ."
346
- )
347
- end
348
- end
349
-
350
- sig do
351
- params ( results : T ::Array [ Result ] , separator : String , errors_headline : String ) . returns ( Result )
352
- end
353
- def merge_results ( results , separator : "\n ===\n " , errors_headline : "" )
354
- results . reject! ( &:ok? )
355
-
356
- if results . empty?
357
- Result . new ( ok : true )
358
- else
359
- Result . new (
360
- ok : false ,
361
- error_value : errors_headline + results . map ( &:error_value ) . join ( separator )
362
- )
363
- end
364
- end
365
244
end
366
245
end
0 commit comments