package_protections 2.3.1 → 2.5.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/README.md +6 -13
- data/lib/package_protections/private/configuration.rb +12 -16
- data/lib/package_protections/private.rb +0 -11
- data/lib/package_protections/rspec/application_fixture_helper.rb +2 -1
- data/lib/package_protections/rubocop_protection_interface.rb +4 -3
- data/lib/package_protections.rb +3 -10
- data/lib/rubocop/cop/package_protections/namespaced_under_package_name.rb +1 -1
- data/lib/rubocop/cop/package_protections/only_class_methods.rb +67 -0
- data/lib/rubocop/cop/package_protections/require_documented_public_apis.rb +70 -0
- data/lib/rubocop/cop/package_protections/typed_public_api.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 58f01e3852a51048fe302838f5c9cfae1d116789a0cf590484f1f5a38f018215
|
|
4
|
+
data.tar.gz: 9676ca99110ee87a03963b2391a7c449396bb2b8dfbaa4b0bc585c74632bbe8c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d90ddaf7d29c7bef8e50fd1c9e18e9921c24799b820dc6db5da2bfaf16ab3380612e2e320fb5198aa9085c448e58e826d437e5966fe25a2721db241cfdca1789
|
|
7
|
+
data.tar.gz: bbc96e01cac4e6232500c9466f37db3a522a4c50767f3e7f9c9bed60595055cd467cfa0ecfe98afd3ee3bf6de3be06e0b06a1f8d205314d4bcb760a071333ec2
|
data/README.md
CHANGED
|
@@ -54,21 +54,14 @@ This protection ensures that all files within `app/public` are typed at level `s
|
|
|
54
54
|
This helps ensure that your package is only creating one namespace (based on folder hierarchy). This helps organize the public API of your pack into one place.
|
|
55
55
|
This protection only looks at files in `packs/your_pack/app` (it ignores spec files).
|
|
56
56
|
This protection is implemented via Rubocop -- expect to see results for this when running `rubocop` however you normally do. To add to the TODO list, add to `.rubocop_todo.yml`
|
|
57
|
-
Lastly – this protection can be configured by setting `
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
protections:
|
|
63
|
-
# ... nothing changes here
|
|
64
|
-
global_namespaces:
|
|
65
|
-
- MyNamespace
|
|
66
|
-
- MyOtherNamespace
|
|
67
|
-
- MyThirdNamespace
|
|
68
|
-
# ... etc.
|
|
57
|
+
Lastly – this protection can be configured by setting `globally_permitted_namespaces`, e.g.:
|
|
58
|
+
```ruby
|
|
59
|
+
PackageProtections.configure do |config|
|
|
60
|
+
config.globally_permitted_namespaces = ['SomeGlobalNamespace']
|
|
61
|
+
end
|
|
69
62
|
```
|
|
70
63
|
|
|
71
|
-
|
|
64
|
+
If you've worked through all of the TODOs for this cop and are able to set the value to `fail_on_any`, you can also set `automatic_pack_namespace` which will support your pack having one global namespace without extra subdirectories. That is, instead of `packs/foo/app/services/foo/bar.rb`, you can use `packs/foo/app/services/bar.rb` and still have it define `Foo::Bar`. [See the `stimpack` README.md](https://github.com/rubyatscale/stimpack#readme) for more information.
|
|
72
65
|
|
|
73
66
|
### `prevent_other_packages_from_using_this_package_without_explicit_visibility`
|
|
74
67
|
*This is only available if your package has `enforce_privacy` set to `true`!*
|
|
@@ -5,32 +5,26 @@ module PackageProtections
|
|
|
5
5
|
class Configuration
|
|
6
6
|
extend T::Sig
|
|
7
7
|
|
|
8
|
-
sig {
|
|
9
|
-
|
|
8
|
+
sig { returns(T::Array[ProtectionInterface]) }
|
|
9
|
+
attr_accessor :protections
|
|
10
|
+
|
|
11
|
+
sig { returns(T::Array[String]) }
|
|
12
|
+
attr_accessor :globally_permitted_namespaces
|
|
10
13
|
|
|
11
|
-
sig {
|
|
12
|
-
|
|
14
|
+
sig { returns(T::Array[String]) }
|
|
15
|
+
attr_accessor :acceptable_parent_classes
|
|
13
16
|
|
|
14
17
|
sig { void }
|
|
15
18
|
def initialize
|
|
16
19
|
@protections = T.let(default_protections, T::Array[ProtectionInterface])
|
|
17
20
|
@globally_permitted_namespaces = T.let([], T::Array[String])
|
|
21
|
+
@acceptable_parent_classes = T.let([], T::Array[String])
|
|
18
22
|
end
|
|
19
|
-
|
|
20
|
-
sig { returns(T::Array[ProtectionInterface]) }
|
|
21
|
-
def protections
|
|
22
|
-
@protections
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
sig { returns(T::Array[String]) }
|
|
26
|
-
def globally_permitted_namespaces
|
|
27
|
-
@globally_permitted_namespaces
|
|
28
|
-
end
|
|
29
|
-
|
|
30
23
|
sig { void }
|
|
31
24
|
def bust_cache!
|
|
32
25
|
@protections = default_protections
|
|
33
26
|
@globally_permitted_namespaces = []
|
|
27
|
+
@acceptable_parent_classes = []
|
|
34
28
|
end
|
|
35
29
|
|
|
36
30
|
sig { returns(T::Array[ProtectionInterface]) }
|
|
@@ -40,7 +34,9 @@ module PackageProtections
|
|
|
40
34
|
Private::IncomingPrivacyProtection.new,
|
|
41
35
|
RuboCop::Cop::PackageProtections::TypedPublicApi.new,
|
|
42
36
|
RuboCop::Cop::PackageProtections::NamespacedUnderPackageName.new,
|
|
43
|
-
Private::VisibilityProtection.new
|
|
37
|
+
Private::VisibilityProtection.new,
|
|
38
|
+
RuboCop::Cop::PackageProtections::OnlyClassMethods.new,
|
|
39
|
+
RuboCop::Cop::PackageProtections::RequireDocumentedPublicApis.new
|
|
44
40
|
]
|
|
45
41
|
end
|
|
46
42
|
end
|
|
@@ -115,23 +115,12 @@ module PackageProtections
|
|
|
115
115
|
sig { void }
|
|
116
116
|
def self.bust_cache!
|
|
117
117
|
@protected_packages_indexed_by_name = nil
|
|
118
|
-
@private_cop_config = nil
|
|
119
118
|
PackageProtections.config.bust_cache!
|
|
120
119
|
# This comes explicitly after `PackageProtections.config.bust_cache!` because
|
|
121
120
|
# otherwise `PackageProtections.config` will attempt to reload the client configuratoin.
|
|
122
121
|
@loaded_client_configuration = false
|
|
123
122
|
end
|
|
124
123
|
|
|
125
|
-
sig { params(identifier: Identifier).returns(T::Hash[T.untyped, T.untyped]) }
|
|
126
|
-
def self.private_cop_config(identifier)
|
|
127
|
-
@private_cop_config ||= T.let(@private_cop_config, T.nilable(T::Hash[T.untyped, T.untyped]))
|
|
128
|
-
@private_cop_config ||= begin
|
|
129
|
-
protected_packages = all_protected_packages
|
|
130
|
-
protection = T.cast(PackageProtections.with_identifier(identifier), PackageProtections::RubocopProtectionInterface)
|
|
131
|
-
protected_packages.to_h { |p| [p.name, protection.custom_cop_config(p)] }
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
|
|
135
124
|
sig { returns(T::Array[T::Hash[T.untyped, T.untyped]]) }
|
|
136
125
|
def self.rubocop_todo_ymls
|
|
137
126
|
@rubocop_todo_ymls = T.let(@rubocop_todo_ymls, T.nilable(T::Array[T::Hash[T.untyped, T.untyped]]))
|
|
@@ -24,7 +24,8 @@ module ApplicationFixtureHelper
|
|
|
24
24
|
'prevent_other_packages_from_using_this_packages_internals' => 'fail_on_new',
|
|
25
25
|
'prevent_this_package_from_exposing_an_untyped_api' => 'fail_on_new',
|
|
26
26
|
'prevent_this_package_from_creating_other_namespaces' => 'fail_on_new',
|
|
27
|
-
'prevent_other_packages_from_using_this_package_without_explicit_visibility' => 'fail_never'
|
|
27
|
+
'prevent_other_packages_from_using_this_package_without_explicit_visibility' => 'fail_never',
|
|
28
|
+
'prevent_this_package_from_exposing_instance_method_public_apis' => 'fail_never'
|
|
28
29
|
}
|
|
29
30
|
protections_with_defaults = defaults.merge(protections)
|
|
30
31
|
metadata = { 'protections' => protections_with_defaults }
|
|
@@ -57,9 +57,9 @@ module PackageProtections
|
|
|
57
57
|
# but a default is provided.
|
|
58
58
|
############################################################################
|
|
59
59
|
sig do
|
|
60
|
-
|
|
60
|
+
returns(T::Hash[T.untyped, T.untyped])
|
|
61
61
|
end
|
|
62
|
-
def custom_cop_config
|
|
62
|
+
def custom_cop_config
|
|
63
63
|
{}
|
|
64
64
|
end
|
|
65
65
|
|
|
@@ -135,7 +135,8 @@ module PackageProtections
|
|
|
135
135
|
CopConfig.new(
|
|
136
136
|
name: cop_name,
|
|
137
137
|
enabled: include_paths.any?,
|
|
138
|
-
include_paths: include_paths
|
|
138
|
+
include_paths: include_paths,
|
|
139
|
+
metadata: custom_cop_config
|
|
139
140
|
)
|
|
140
141
|
]
|
|
141
142
|
end
|
data/lib/package_protections.rb
CHANGED
|
@@ -8,7 +8,7 @@ require 'set'
|
|
|
8
8
|
require 'parse_packwerk'
|
|
9
9
|
require 'rubocop'
|
|
10
10
|
require 'rubocop-sorbet'
|
|
11
|
-
require 'rubocop-
|
|
11
|
+
require 'rubocop-packs'
|
|
12
12
|
|
|
13
13
|
#
|
|
14
14
|
# Welcome to PackageProtections!
|
|
@@ -40,6 +40,8 @@ module PackageProtections
|
|
|
40
40
|
# Implementation of rubocop-based protections
|
|
41
41
|
require 'rubocop/cop/package_protections/namespaced_under_package_name'
|
|
42
42
|
require 'rubocop/cop/package_protections/typed_public_api'
|
|
43
|
+
require 'rubocop/cop/package_protections/only_class_methods'
|
|
44
|
+
require 'rubocop/cop/package_protections/require_documented_public_apis'
|
|
43
45
|
|
|
44
46
|
class << self
|
|
45
47
|
extend T::Sig
|
|
@@ -116,15 +118,6 @@ module PackageProtections
|
|
|
116
118
|
Private.rubocop_yml(root_pathname: root_pathname)
|
|
117
119
|
end
|
|
118
120
|
|
|
119
|
-
#
|
|
120
|
-
# Do not use this method -- it's meant to be used by Rubocop cops to get directory-specific
|
|
121
|
-
# parameters without needing to have directory-specific .rubocop.yml files.
|
|
122
|
-
#
|
|
123
|
-
sig { params(identifier: Identifier).returns(T::Hash[T.untyped, T.untyped]) }
|
|
124
|
-
def self.private_cop_config(identifier)
|
|
125
|
-
Private.private_cop_config(identifier)
|
|
126
|
-
end
|
|
127
|
-
|
|
128
121
|
sig { void }
|
|
129
122
|
def self.bust_cache!
|
|
130
123
|
Private.bust_cache!
|
|
@@ -6,7 +6,7 @@ require 'active_support/core_ext/string/inflections'
|
|
|
6
6
|
module RuboCop
|
|
7
7
|
module Cop
|
|
8
8
|
module PackageProtections
|
|
9
|
-
class NamespacedUnderPackageName <
|
|
9
|
+
class NamespacedUnderPackageName < Packs::NamespaceConvention
|
|
10
10
|
extend T::Sig
|
|
11
11
|
include ::PackageProtections::RubocopProtectionInterface
|
|
12
12
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module PackageProtections
|
|
6
|
+
class OnlyClassMethods < Packs::ClassMethodsAsPublicApis
|
|
7
|
+
extend T::Sig
|
|
8
|
+
include ::PackageProtections::RubocopProtectionInterface
|
|
9
|
+
|
|
10
|
+
IDENTIFIER = 'prevent_this_package_from_exposing_instance_method_public_apis'.freeze
|
|
11
|
+
|
|
12
|
+
sig { override.returns(String) }
|
|
13
|
+
def humanized_protection_description
|
|
14
|
+
<<~MESSAGE
|
|
15
|
+
Public API methods can only be static methods.
|
|
16
|
+
This is failing because these files are in `.rubocop_todo.yml` under `#{cop_name}`.
|
|
17
|
+
If you want to be able to ignore these files, you'll need to open the file's package's `package.yml` file and
|
|
18
|
+
change `#{IDENTIFIER}` to `#{::PackageProtections::ViolationBehavior::FailOnNew.serialize}`
|
|
19
|
+
MESSAGE
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
sig do
|
|
23
|
+
override.params(file: String).returns(String)
|
|
24
|
+
end
|
|
25
|
+
def message_for_fail_on_any(file)
|
|
26
|
+
"`#{file}` must only contain static (class or module level) methods"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
sig { override.returns(T::Array[String]) }
|
|
30
|
+
def included_globs_for_pack
|
|
31
|
+
[
|
|
32
|
+
'app/public/**/*'
|
|
33
|
+
]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
sig do
|
|
37
|
+
override.returns(T::Hash[T.untyped, T.untyped])
|
|
38
|
+
end
|
|
39
|
+
def custom_cop_config
|
|
40
|
+
{
|
|
41
|
+
'AcceptableParentClasses' => ::PackageProtections.config.acceptable_parent_classes
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
sig { override.returns(String) }
|
|
46
|
+
def identifier
|
|
47
|
+
IDENTIFIER
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
sig { override.returns(String) }
|
|
51
|
+
def humanized_protection_name
|
|
52
|
+
'Class Method Public APIs'
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
sig { override.returns(String) }
|
|
56
|
+
def cop_name
|
|
57
|
+
'PackageProtections/OnlyClassMethods'
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
sig { override.returns(::PackageProtections::ViolationBehavior) }
|
|
61
|
+
def default_behavior
|
|
62
|
+
::PackageProtections::ViolationBehavior::FailNever
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module PackageProtections
|
|
6
|
+
class RequireDocumentedPublicApis < Packs::RequireDocumentedPublicApis
|
|
7
|
+
extend T::Sig
|
|
8
|
+
include ::PackageProtections::RubocopProtectionInterface
|
|
9
|
+
|
|
10
|
+
IDENTIFIER = 'prevent_this_package_from_exposing_undocumented_public_apis'.freeze
|
|
11
|
+
|
|
12
|
+
include ::PackageProtections::RubocopProtectionInterface
|
|
13
|
+
|
|
14
|
+
sig { override.returns(String) }
|
|
15
|
+
def identifier
|
|
16
|
+
IDENTIFIER
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
sig { override.returns(T::Array[String]) }
|
|
20
|
+
def included_globs_for_pack
|
|
21
|
+
[
|
|
22
|
+
'app/public/**/*'
|
|
23
|
+
]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
sig { override.params(behavior: ::PackageProtections::ViolationBehavior, package: ParsePackwerk::Package).returns(T.nilable(String)) }
|
|
27
|
+
def unmet_preconditions_for_behavior(behavior, package)
|
|
28
|
+
if !behavior.fail_never?
|
|
29
|
+
readme_path = package.directory.join('README.md')
|
|
30
|
+
if !readme_path.exist?
|
|
31
|
+
"This package must have a readme at #{readme_path} to use this protection"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
sig do
|
|
37
|
+
override.params(file: String).returns(String)
|
|
38
|
+
end
|
|
39
|
+
def message_for_fail_on_any(file)
|
|
40
|
+
"`#{file}` must contain documentation on every method (between signature and method)"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
sig { override.returns(String) }
|
|
44
|
+
def cop_name
|
|
45
|
+
'PackageProtections/RequireDocumentedPublicApis'
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
sig { override.returns(String) }
|
|
49
|
+
def humanized_protection_name
|
|
50
|
+
'Documented Public APIs'
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
sig { override.returns(::PackageProtections::ViolationBehavior) }
|
|
54
|
+
def default_behavior
|
|
55
|
+
::PackageProtections::ViolationBehavior::FailNever
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
sig { override.returns(String) }
|
|
59
|
+
def humanized_protection_description
|
|
60
|
+
<<~MESSAGE
|
|
61
|
+
All public API must have a documentation comment (between the signature and method).
|
|
62
|
+
This is failing because these files are in `.rubocop_todo.yml` under `#{cop_name}`.
|
|
63
|
+
If you want to be able to ignore these files, you'll need to open the file's package's `package.yml` file and
|
|
64
|
+
change `#{IDENTIFIER}` to `#{::PackageProtections::ViolationBehavior::FailOnNew.serialize}`
|
|
65
|
+
MESSAGE
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -15,7 +15,7 @@ module RuboCop
|
|
|
15
15
|
#
|
|
16
16
|
# We can apply this same pattern if we want to use other cops in the context of package protections and prevent clashing.
|
|
17
17
|
#
|
|
18
|
-
class TypedPublicApi <
|
|
18
|
+
class TypedPublicApi < Packs::TypedPublicApi
|
|
19
19
|
extend T::Sig
|
|
20
20
|
|
|
21
21
|
include ::PackageProtections::ProtectionInterface
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: package_protections
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gusto Engineers
|
|
@@ -53,7 +53,7 @@ dependencies:
|
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '0'
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: rubocop-
|
|
56
|
+
name: rubocop-packs
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
59
|
- - ">="
|
|
@@ -206,6 +206,8 @@ files:
|
|
|
206
206
|
- lib/package_protections/rubocop_protection_interface.rb
|
|
207
207
|
- lib/package_protections/violation_behavior.rb
|
|
208
208
|
- lib/rubocop/cop/package_protections/namespaced_under_package_name.rb
|
|
209
|
+
- lib/rubocop/cop/package_protections/only_class_methods.rb
|
|
210
|
+
- lib/rubocop/cop/package_protections/require_documented_public_apis.rb
|
|
209
211
|
- lib/rubocop/cop/package_protections/typed_public_api.rb
|
|
210
212
|
homepage: https://github.com/rubyatscale/package_protections
|
|
211
213
|
licenses:
|