packwerk-extensions 0.0.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 +7 -0
- data/README.md +66 -0
- data/lib/packwerk/privacy/checker.rb +99 -0
- data/lib/packwerk/privacy/package.rb +51 -0
- data/lib/packwerk/privacy/validator.rb +133 -0
- data/lib/packwerk/visibility/checker.rb +93 -0
- data/lib/packwerk/visibility/package.rb +25 -0
- data/lib/packwerk/visibility/validator.rb +57 -0
- data/lib/packwerk-extensions.rb +18 -0
- metadata +209 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: dcb5ef4db4ff6cf134b8707feb0cb4009c8e38db338c407bcbf1fa88da19f79e
|
4
|
+
data.tar.gz: 59e1d1382dba41b33924eb82e679a830ccd642446a52748afabcd2d86b7f64b2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3873288311271201955075edbf6fe17af585cf62efd1b5d925908e1f7791a45cf1b19e6c4bce29ab5d2bc624ed3eaacece6bf0a153f98051261408a8c2ec1d58
|
7
|
+
data.tar.gz: 4651d546fa1aff7e1c4521c5311711d7598d9f2defb621132172d1a3058a3d7b15bddfde40e67f8e658490df1616ff90ad14e2fc45e18d0383a7f67f3331c4a2
|
data/README.md
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# packwerk-extensions
|
2
|
+
|
3
|
+
`packwerk-extensions` is a home for checker extensions for packwerk.
|
4
|
+
|
5
|
+
#### Enforcing privacy boundary
|
6
|
+
|
7
|
+
The privacy checker extension was originally extracted from [packwerk](https://github.com/Shopify/packwerk).
|
8
|
+
|
9
|
+
A package's privacy boundary is violated when there is a reference to the package's private constants from a source outside the package.
|
10
|
+
|
11
|
+
There are two ways you can enforce privacy for your package:
|
12
|
+
|
13
|
+
1. Enforce privacy for all external sources
|
14
|
+
|
15
|
+
```yaml
|
16
|
+
# components/merchandising/package.yml
|
17
|
+
enforce_privacy: true # will make everything private that is not in
|
18
|
+
# the components/merchandising/app/public folder
|
19
|
+
```
|
20
|
+
|
21
|
+
Setting `enforce_privacy` to true will make all references to private constants in your package a violation.
|
22
|
+
|
23
|
+
2. Enforce privacy for specific constants
|
24
|
+
|
25
|
+
```yaml
|
26
|
+
# components/merchandising/package.yml
|
27
|
+
enforce_privacy:
|
28
|
+
- "::Merchandising::Product"
|
29
|
+
- "::SomeNamespace" # enforces privacy for the namespace and
|
30
|
+
# everything nested in it
|
31
|
+
```
|
32
|
+
|
33
|
+
It will be a privacy violation when a file outside of the `components/merchandising` package tries to reference `Merchandising::Product`.
|
34
|
+
|
35
|
+
##### Using public folders
|
36
|
+
You may enforce privacy either way mentioned above and still expose a public API for your package by placing constants in the public folder, which by default is `app/public`. The constants in the public folder will be made available for use by the rest of the application.
|
37
|
+
|
38
|
+
##### Defining your own public folder
|
39
|
+
|
40
|
+
You may prefer to override the default public folder, you can do so on a per-package basis by defining a `public_path`.
|
41
|
+
|
42
|
+
Example:
|
43
|
+
|
44
|
+
```yaml
|
45
|
+
public_path: my/custom/path/
|
46
|
+
```
|
47
|
+
|
48
|
+
### Package Privacy violation
|
49
|
+
A constant that is private to its package has been referenced from outside of the package. Constants are declared private in their package’s `package.yml`.
|
50
|
+
|
51
|
+
See: [USAGE.md - Enforcing privacy boundary](USAGE.md#Enforcing-privacy-boundary)
|
52
|
+
|
53
|
+
#### Interpreting Privacy violation
|
54
|
+
|
55
|
+
> /Users/JaneDoe/src/github.com/sample-project/user/app/controllers/labels_controller.rb:170:30
|
56
|
+
> Privacy violation: '::Billing::CarrierInvoiceTransaction' is private to 'billing' but referenced from 'user'.
|
57
|
+
> Is there a public entrypoint in 'billing/app/public/' that you can use instead?
|
58
|
+
>
|
59
|
+
> Inference details: 'Billing::CarrierInvoiceTransaction' refers to ::Billing::CarrierInvoiceTransaction which seems to be defined in billing/app/models/billing/carrier_invoice_transaction.rb.
|
60
|
+
|
61
|
+
There has been a privacy violation of the package `billing` in the package `user`, through the use of the constant `Billing::CarrierInvoiceTransaction` in the file `user/app/controllers/labels_controller.rb`.
|
62
|
+
|
63
|
+
##### Suggestions
|
64
|
+
You may be accessing the implementation of a piece of functionality that is supposed to be accessed through a public interface on the package. Try to use the public interface instead. A package’s public interface should be defined in its `app/public` folder and documented.
|
65
|
+
|
66
|
+
The functionality you’re looking for may not be intended to be reused across packages at all. If there is no public interface for it but you have a good reason to use it from outside of its package, find the people responsible for the package and discuss a solution with them.
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Privacy
|
6
|
+
# Checks whether a given reference references a private constant of another package.
|
7
|
+
class Checker
|
8
|
+
extend T::Sig
|
9
|
+
include Packwerk::Checker
|
10
|
+
|
11
|
+
VIOLATION_TYPE = T.let('privacy', String)
|
12
|
+
|
13
|
+
sig { override.returns(String) }
|
14
|
+
def violation_type
|
15
|
+
VIOLATION_TYPE
|
16
|
+
end
|
17
|
+
|
18
|
+
sig do
|
19
|
+
override
|
20
|
+
.params(reference: Packwerk::Reference)
|
21
|
+
.returns(T::Boolean)
|
22
|
+
end
|
23
|
+
def invalid_reference?(reference)
|
24
|
+
constant_package = reference.constant.package
|
25
|
+
privacy_package = Package.from(constant_package)
|
26
|
+
|
27
|
+
return false if privacy_package.public_path?(reference.constant.location)
|
28
|
+
|
29
|
+
privacy_option = privacy_package.enforce_privacy
|
30
|
+
return false if enforcement_disabled?(privacy_option)
|
31
|
+
|
32
|
+
return false unless privacy_option == true || privacy_option == 'strict' ||
|
33
|
+
explicitly_private_constant?(reference.constant, explicitly_private_constants: T.unsafe(privacy_option))
|
34
|
+
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
sig do
|
39
|
+
override
|
40
|
+
.params(listed_offense: Packwerk::ReferenceOffense)
|
41
|
+
.returns(T::Boolean)
|
42
|
+
end
|
43
|
+
def strict_mode_violation?(listed_offense)
|
44
|
+
publishing_package = listed_offense.reference.constant.package
|
45
|
+
publishing_package.config['enforce_privacy'] == 'strict'
|
46
|
+
end
|
47
|
+
|
48
|
+
sig do
|
49
|
+
override
|
50
|
+
.params(reference: Packwerk::Reference)
|
51
|
+
.returns(String)
|
52
|
+
end
|
53
|
+
def message(reference)
|
54
|
+
source_desc = "'#{reference.package}'"
|
55
|
+
|
56
|
+
message = <<~MESSAGE
|
57
|
+
Privacy violation: '#{reference.constant.name}' is private to '#{reference.constant.package}' but referenced from #{source_desc}.
|
58
|
+
Is there a public entrypoint in '#{Package.from(reference.constant.package).public_path}' that you can use instead?
|
59
|
+
|
60
|
+
#{standard_help_message(reference)}
|
61
|
+
MESSAGE
|
62
|
+
|
63
|
+
message.chomp
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
sig do
|
69
|
+
params(
|
70
|
+
constant: ConstantDiscovery::ConstantContext,
|
71
|
+
explicitly_private_constants: T::Array[String]
|
72
|
+
).returns(T::Boolean)
|
73
|
+
end
|
74
|
+
def explicitly_private_constant?(constant, explicitly_private_constants:)
|
75
|
+
explicitly_private_constants.include?(constant.name) ||
|
76
|
+
# nested constants
|
77
|
+
explicitly_private_constants.any? { |epc| constant.name.start_with?("#{epc}::") }
|
78
|
+
end
|
79
|
+
|
80
|
+
sig do
|
81
|
+
params(privacy_option: T.nilable(T.any(T::Boolean, String, T::Array[String])))
|
82
|
+
.returns(T::Boolean)
|
83
|
+
end
|
84
|
+
def enforcement_disabled?(privacy_option)
|
85
|
+
[false, nil].include?(privacy_option)
|
86
|
+
end
|
87
|
+
|
88
|
+
sig { params(reference: Reference).returns(String) }
|
89
|
+
def standard_help_message(reference)
|
90
|
+
standard_message = <<~MESSAGE.chomp
|
91
|
+
Inference details: this is a reference to #{reference.constant.name} which seems to be defined in #{reference.constant.location}.
|
92
|
+
To receive help interpreting or resolving this error message, see: https://github.com/Shopify/packwerk/blob/main/TROUBLESHOOT.md#Troubleshooting-violations
|
93
|
+
MESSAGE
|
94
|
+
|
95
|
+
standard_message.chomp
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Privacy
|
6
|
+
class Package < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
const :public_path, String
|
10
|
+
const :user_defined_public_path, T.nilable(String)
|
11
|
+
const :enforce_privacy, T.nilable(T.any(T::Boolean, String, T::Array[String]))
|
12
|
+
|
13
|
+
sig { params(path: String).returns(T::Boolean) }
|
14
|
+
def public_path?(path)
|
15
|
+
path.start_with?(public_path)
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
extend T::Sig
|
20
|
+
|
21
|
+
sig { params(package: ::Packwerk::Package).returns(Package) }
|
22
|
+
def from(package)
|
23
|
+
Package.new(
|
24
|
+
public_path: public_path_for(package),
|
25
|
+
user_defined_public_path: user_defined_public_path(package),
|
26
|
+
enforce_privacy: package.config['enforce_privacy']
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
sig { params(package: ::Packwerk::Package).returns(T.nilable(String)) }
|
31
|
+
def user_defined_public_path(package)
|
32
|
+
return unless package.config['public_path']
|
33
|
+
return package.config['public_path'] if package.config['public_path'].end_with?('/')
|
34
|
+
|
35
|
+
"#{package.config['public_path']}/"
|
36
|
+
end
|
37
|
+
|
38
|
+
sig { params(package: ::Packwerk::Package).returns(String) }
|
39
|
+
def public_path_for(package)
|
40
|
+
unprefixed_public_path = user_defined_public_path(package) || 'app/public/'
|
41
|
+
|
42
|
+
if package.root?
|
43
|
+
unprefixed_public_path
|
44
|
+
else
|
45
|
+
File.join(package.name, unprefixed_public_path)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Privacy
|
6
|
+
class Validator
|
7
|
+
extend T::Sig
|
8
|
+
include Packwerk::Validator
|
9
|
+
|
10
|
+
sig { override.params(package_set: PackageSet, configuration: Configuration).returns(ApplicationValidator::Result) }
|
11
|
+
def call(package_set, configuration)
|
12
|
+
privacy_settings = package_manifests_settings_for(configuration, 'enforce_privacy')
|
13
|
+
|
14
|
+
resolver = ConstantResolver.new(
|
15
|
+
root_path: configuration.root_path,
|
16
|
+
load_paths: configuration.load_paths
|
17
|
+
)
|
18
|
+
|
19
|
+
results = T.let([], T::Array[ApplicationValidator::Result])
|
20
|
+
|
21
|
+
privacy_settings.each do |config_file_path, setting|
|
22
|
+
results << check_enforce_privacy_setting(config_file_path, setting)
|
23
|
+
next unless setting.is_a?(Array)
|
24
|
+
|
25
|
+
constants = setting
|
26
|
+
|
27
|
+
results += assert_constants_can_be_loaded(constants, config_file_path)
|
28
|
+
|
29
|
+
constant_locations = constants.map { |c| [c, resolver.resolve(c)&.location] }
|
30
|
+
|
31
|
+
constant_locations.each do |name, location|
|
32
|
+
results << if location
|
33
|
+
check_private_constant_location(configuration, package_set, name, location, config_file_path)
|
34
|
+
else
|
35
|
+
private_constant_unresolvable(name, config_file_path)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
public_path_settings = package_manifests_settings_for(configuration, 'public_path')
|
41
|
+
public_path_settings.each do |config_file_path, setting|
|
42
|
+
results << check_public_path(config_file_path, setting)
|
43
|
+
end
|
44
|
+
|
45
|
+
merge_results(results, separator: "\n---\n")
|
46
|
+
end
|
47
|
+
|
48
|
+
sig { override.returns(T::Array[String]) }
|
49
|
+
def permitted_keys
|
50
|
+
%w[public_path enforce_privacy]
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
sig do
|
56
|
+
params(config_file_path: String, setting: T.untyped).returns(ApplicationValidator::Result)
|
57
|
+
end
|
58
|
+
def check_public_path(config_file_path, setting)
|
59
|
+
if setting.is_a?(String) || setting.nil?
|
60
|
+
ApplicationValidator::Result.new(ok: true)
|
61
|
+
else
|
62
|
+
ApplicationValidator::Result.new(
|
63
|
+
ok: false,
|
64
|
+
error_value: "'public_path' option must be a string in #{config_file_path.inspect}: #{setting.inspect}"
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
sig do
|
70
|
+
params(config_file_path: String, setting: T.untyped).returns(ApplicationValidator::Result)
|
71
|
+
end
|
72
|
+
def check_enforce_privacy_setting(config_file_path, setting)
|
73
|
+
if [TrueClass, FalseClass, Array, NilClass].include?(setting.class)
|
74
|
+
ApplicationValidator::Result.new(ok: true)
|
75
|
+
else
|
76
|
+
ApplicationValidator::Result.new(
|
77
|
+
ok: false,
|
78
|
+
error_value: "Invalid 'enforce_privacy' option in #{config_file_path.inspect}: #{setting.inspect}"
|
79
|
+
)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
sig do
|
84
|
+
params(configuration: Configuration, package_set: PackageSet, name: T.untyped, location: T.untyped,
|
85
|
+
config_file_path: T.untyped).returns(ApplicationValidator::Result)
|
86
|
+
end
|
87
|
+
def check_private_constant_location(configuration, package_set, name, location, config_file_path)
|
88
|
+
declared_package = package_set.package_from_path(relative_path(configuration, config_file_path))
|
89
|
+
constant_package = package_set.package_from_path(location)
|
90
|
+
|
91
|
+
if constant_package == declared_package
|
92
|
+
ApplicationValidator::Result.new(ok: true)
|
93
|
+
else
|
94
|
+
ApplicationValidator::Result.new(
|
95
|
+
ok: false,
|
96
|
+
error_value: "'#{name}' is declared as private in the '#{declared_package}' package but appears to be " \
|
97
|
+
"defined\nin the '#{constant_package}' package. Packwerk resolved it to #{location}."
|
98
|
+
)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
sig { params(constants: T.untyped, config_file_path: String).returns(T::Array[ApplicationValidator::Result]) }
|
103
|
+
def assert_constants_can_be_loaded(constants, config_file_path)
|
104
|
+
constants.map do |constant|
|
105
|
+
if constant.start_with?('::')
|
106
|
+
constant.try(&:constantize) && ApplicationValidator::Result.new(ok: true)
|
107
|
+
else
|
108
|
+
error_value = "'#{constant}', listed in the 'enforce_privacy' option " \
|
109
|
+
"in #{config_file_path}, is invalid.\nPrivate constants need to be " \
|
110
|
+
'prefixed with the top-level namespace operator `::`.'
|
111
|
+
ApplicationValidator::Result.new(
|
112
|
+
ok: false,
|
113
|
+
error_value: error_value
|
114
|
+
)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
sig { params(name: T.untyped, config_file_path: T.untyped).returns(ApplicationValidator::Result) }
|
120
|
+
def private_constant_unresolvable(name, config_file_path)
|
121
|
+
explicit_filepath = "#{(name.start_with?('::') ? name[2..] : name).underscore}.rb"
|
122
|
+
|
123
|
+
ApplicationValidator::Result.new(
|
124
|
+
ok: false,
|
125
|
+
error_value: "'#{name}', listed in #{config_file_path}, could not be resolved.\n" \
|
126
|
+
"This is probably because it is an autovivified namespace - a namespace module that doesn't have a\n" \
|
127
|
+
"file explicitly defining it. Packwerk currently doesn't support declaring autovivified namespaces as\n" \
|
128
|
+
"private. Add a #{explicit_filepath} file to explicitly define the constant."
|
129
|
+
)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Visibility
|
6
|
+
# Checks whether a given reference references a constant from a package that does not permit visibility
|
7
|
+
class Checker
|
8
|
+
extend T::Sig
|
9
|
+
include Packwerk::Checker
|
10
|
+
|
11
|
+
VIOLATION_TYPE = T.let('visibility', String)
|
12
|
+
|
13
|
+
sig { override.returns(String) }
|
14
|
+
def violation_type
|
15
|
+
VIOLATION_TYPE
|
16
|
+
end
|
17
|
+
|
18
|
+
sig do
|
19
|
+
override
|
20
|
+
.params(reference: Packwerk::Reference)
|
21
|
+
.returns(T::Boolean)
|
22
|
+
end
|
23
|
+
def invalid_reference?(reference)
|
24
|
+
constant_package = reference.constant.package
|
25
|
+
visibility_package = Package.from(constant_package)
|
26
|
+
visibility_option = visibility_package.enforce_visibility
|
27
|
+
return false if enforcement_disabled?(visibility_option)
|
28
|
+
|
29
|
+
!visibility_package.visible_to.include?(reference.package.name)
|
30
|
+
end
|
31
|
+
|
32
|
+
sig do
|
33
|
+
override
|
34
|
+
.params(listed_offense: Packwerk::ReferenceOffense)
|
35
|
+
.returns(T::Boolean)
|
36
|
+
end
|
37
|
+
def strict_mode_violation?(listed_offense)
|
38
|
+
publishing_package = listed_offense.reference.constant.package
|
39
|
+
publishing_package.config['enforce_visibility'] == 'strict'
|
40
|
+
end
|
41
|
+
|
42
|
+
sig do
|
43
|
+
override
|
44
|
+
.params(reference: Packwerk::Reference)
|
45
|
+
.returns(String)
|
46
|
+
end
|
47
|
+
def message(reference)
|
48
|
+
source_desc = "'#{reference.package}'"
|
49
|
+
|
50
|
+
message = <<~MESSAGE
|
51
|
+
Visibility violation: '#{reference.constant.name}' belongs to '#{reference.constant.package}', which is not visible to #{source_desc}.
|
52
|
+
Is there a different package to use instead, or should '#{reference.constant.package}' also be visible to #{source_desc}?
|
53
|
+
|
54
|
+
#{standard_help_message(reference)}
|
55
|
+
MESSAGE
|
56
|
+
|
57
|
+
message.chomp
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
sig do
|
63
|
+
params(
|
64
|
+
constant: ConstantDiscovery::ConstantContext,
|
65
|
+
explicitly_private_constants: T::Array[String]
|
66
|
+
).returns(T::Boolean)
|
67
|
+
end
|
68
|
+
def explicitly_private_constant?(constant, explicitly_private_constants:)
|
69
|
+
explicitly_private_constants.include?(constant.name) ||
|
70
|
+
# nested constants
|
71
|
+
explicitly_private_constants.any? { |epc| constant.name.start_with?("#{epc}::") }
|
72
|
+
end
|
73
|
+
|
74
|
+
sig do
|
75
|
+
params(visibility_option: T.nilable(T.any(T::Boolean, String)))
|
76
|
+
.returns(T::Boolean)
|
77
|
+
end
|
78
|
+
def enforcement_disabled?(visibility_option)
|
79
|
+
[false, nil].include?(visibility_option)
|
80
|
+
end
|
81
|
+
|
82
|
+
sig { params(reference: Reference).returns(String) }
|
83
|
+
def standard_help_message(reference)
|
84
|
+
standard_message = <<~MESSAGE.chomp
|
85
|
+
Inference details: this is a reference to #{reference.constant.name} which seems to be defined in #{reference.constant.location}.
|
86
|
+
To receive help interpreting or resolving this error message, see: https://github.com/Shopify/packwerk/blob/main/TROUBLESHOOT.md#Troubleshooting-violations
|
87
|
+
MESSAGE
|
88
|
+
|
89
|
+
standard_message.chomp
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Visibility
|
6
|
+
class Package < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
const :visible_to, T::Array[String]
|
10
|
+
const :enforce_visibility, T.nilable(T.any(T::Boolean, String))
|
11
|
+
|
12
|
+
class << self
|
13
|
+
extend T::Sig
|
14
|
+
|
15
|
+
sig { params(package: ::Packwerk::Package).returns(Package) }
|
16
|
+
def from(package)
|
17
|
+
Package.new(
|
18
|
+
visible_to: package.config['visible_to'] || [],
|
19
|
+
enforce_visibility: package.config['enforce_visibility']
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Visibility
|
6
|
+
class Validator
|
7
|
+
extend T::Sig
|
8
|
+
include Packwerk::Validator
|
9
|
+
|
10
|
+
sig { override.params(package_set: PackageSet, configuration: Configuration).returns(ApplicationValidator::Result) }
|
11
|
+
def call(package_set, configuration)
|
12
|
+
visible_settings = package_manifests_settings_for(configuration, 'visible_to')
|
13
|
+
results = T.let([], T::Array[ApplicationValidator::Result])
|
14
|
+
|
15
|
+
all_package_names = package_set.map(&:name).to_set
|
16
|
+
|
17
|
+
package_manifests_settings_for(configuration, 'enforce_visibility').each do |config, setting|
|
18
|
+
next if setting.nil?
|
19
|
+
|
20
|
+
next if [TrueClass, FalseClass, 'strict'].include?(setting.class)
|
21
|
+
|
22
|
+
results << ApplicationValidator::Result.new(
|
23
|
+
ok: false,
|
24
|
+
error_value: "\tInvalid 'enforce_visibility' option: #{setting.inspect} in #{config.inspect}"
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
visible_settings.each do |config_file_path, setting|
|
29
|
+
next if setting.nil?
|
30
|
+
|
31
|
+
if setting.is_a?(Array)
|
32
|
+
packages_not_found = setting.to_set - all_package_names
|
33
|
+
|
34
|
+
if packages_not_found.any?
|
35
|
+
results << ApplicationValidator::Result.new(
|
36
|
+
ok: false,
|
37
|
+
error_value: "'visible_to' option must only contain valid packages in #{config_file_path.inspect}. Invalid packages: #{packages_not_found.to_a.inspect}"
|
38
|
+
)
|
39
|
+
end
|
40
|
+
else
|
41
|
+
results << ApplicationValidator::Result.new(
|
42
|
+
ok: false,
|
43
|
+
error_value: "'visible_to' option must be an array in #{config_file_path.inspect}."
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
merge_results(results, separator: "\n---\n")
|
49
|
+
end
|
50
|
+
|
51
|
+
sig { override.returns(T::Array[String]) }
|
52
|
+
def permitted_keys
|
53
|
+
%w[visible_to enforce_visibility]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'sorbet-runtime'
|
5
|
+
require 'packwerk'
|
6
|
+
|
7
|
+
require 'packwerk/privacy/checker'
|
8
|
+
require 'packwerk/privacy/package'
|
9
|
+
require 'packwerk/privacy/validator'
|
10
|
+
|
11
|
+
require 'packwerk/visibility/checker'
|
12
|
+
require 'packwerk/visibility/package'
|
13
|
+
require 'packwerk/visibility/validator'
|
14
|
+
|
15
|
+
module Packwerk
|
16
|
+
module Extensions
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: packwerk-extensions
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gusto Engineers
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-12-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: packwerk
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.2.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.2.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sorbet-runtime
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.5.10520
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.5.10520
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: zeitwerk
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: mocha
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: sorbet
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 0.5.10520
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 0.5.10520
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: sorbet-static
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - '='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 0.5.10520
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - '='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 0.5.10520
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: tapioca
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
description: A collection of extensions for packwerk packages.
|
168
|
+
email:
|
169
|
+
- dev@gusto.com
|
170
|
+
executables: []
|
171
|
+
extensions: []
|
172
|
+
extra_rdoc_files: []
|
173
|
+
files:
|
174
|
+
- README.md
|
175
|
+
- lib/packwerk-extensions.rb
|
176
|
+
- lib/packwerk/privacy/checker.rb
|
177
|
+
- lib/packwerk/privacy/package.rb
|
178
|
+
- lib/packwerk/privacy/validator.rb
|
179
|
+
- lib/packwerk/visibility/checker.rb
|
180
|
+
- lib/packwerk/visibility/package.rb
|
181
|
+
- lib/packwerk/visibility/validator.rb
|
182
|
+
homepage: https://github.com/rubyatscale/packwerk-extensions
|
183
|
+
licenses:
|
184
|
+
- MIT
|
185
|
+
metadata:
|
186
|
+
homepage_uri: https://github.com/rubyatscale/packwerk-extensions
|
187
|
+
source_code_uri: https://github.com/rubyatscale/packwerk-extensions
|
188
|
+
changelog_uri: https://github.com/rubyatscale/packwerk-extensions/releases
|
189
|
+
allowed_push_host: https://rubygems.org
|
190
|
+
post_install_message:
|
191
|
+
rdoc_options: []
|
192
|
+
require_paths:
|
193
|
+
- lib
|
194
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
195
|
+
requirements:
|
196
|
+
- - ">="
|
197
|
+
- !ruby/object:Gem::Version
|
198
|
+
version: 2.6.0
|
199
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
200
|
+
requirements:
|
201
|
+
- - ">="
|
202
|
+
- !ruby/object:Gem::Version
|
203
|
+
version: '0'
|
204
|
+
requirements: []
|
205
|
+
rubygems_version: 3.1.6
|
206
|
+
signing_key:
|
207
|
+
specification_version: 4
|
208
|
+
summary: A collection of extensions for packwerk packages.
|
209
|
+
test_files: []
|