package_protections 2.3.1 → 2.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 918d945d5ceb1f2df5f544a50c74ebc8977d36819c4d5204866e90e8518115fc
4
- data.tar.gz: e80077db49323a725e67d41c861e78da371f7e5de986c9240e9de1a4308f6686
3
+ metadata.gz: 58f01e3852a51048fe302838f5c9cfae1d116789a0cf590484f1f5a38f018215
4
+ data.tar.gz: 9676ca99110ee87a03963b2391a7c449396bb2b8dfbaa4b0bc585c74632bbe8c
5
5
  SHA512:
6
- metadata.gz: 81569a675bd20e6993b018d71f29cffb45d90621b9e6f036a746d89c8674b1d43e9a6d7bed6df7e4689ce3e008dc0a89b8506da9f959daa0611cf43d9adb2dc2
7
- data.tar.gz: 3c290042abe91b8bbd285dae9091db68729fb99dd8f0b861c99cbac131f9705caf581d6c117e582e34b73ec38ae3b9f5ac70b885cab41974cd89f6295f03662c
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 `global_namespaces` within the `package.yml`, e.g.:
58
- ```
59
- enforce_privacy: true
60
- enforce_dependencies: true
61
- metadata:
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
- It's encouraged to limit the number of global namespaces your package exposes, and to make sure your global namespaces are as specific to your domain as possible.
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 { params(protections: T::Array[ProtectionInterface]).void }
9
- attr_writer :protections
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 { params(globally_permitted_namespaces: T::Array[String]).void }
12
- attr_writer :globally_permitted_namespaces
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
- params(package: ProtectedPackage).returns(T::Hash[T.untyped, T.untyped])
60
+ returns(T::Hash[T.untyped, T.untyped])
61
61
  end
62
- def custom_cop_config(package)
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
@@ -8,7 +8,7 @@ require 'set'
8
8
  require 'parse_packwerk'
9
9
  require 'rubocop'
10
10
  require 'rubocop-sorbet'
11
- require 'rubocop-modularization'
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 < Modularization::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 < Modularization::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.3.1
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-modularization
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: