package_protections 0.64.0
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 +149 -0
- data/config/default.yml +8 -0
- data/lib/package_protections/offense.rb +17 -0
- data/lib/package_protections/per_file_violation.rb +39 -0
- data/lib/package_protections/private/colorized_string.rb +97 -0
- data/lib/package_protections/private/incoming_privacy_protection.rb +118 -0
- data/lib/package_protections/private/metadata_modifiers.rb +27 -0
- data/lib/package_protections/private/multiple_namespaces_protection.rb +128 -0
- data/lib/package_protections/private/outgoing_dependency_protection.rb +117 -0
- data/lib/package_protections/private/output.rb +24 -0
- data/lib/package_protections/private/typed_api_protection.rb +106 -0
- data/lib/package_protections/private/visibility_protection.rb +186 -0
- data/lib/package_protections/private.rb +155 -0
- data/lib/package_protections/protected_package.rb +101 -0
- data/lib/package_protections/protection_interface.rb +69 -0
- data/lib/package_protections/rubocop_protection_interface.rb +84 -0
- data/lib/package_protections/violation_behavior.rb +69 -0
- data/lib/package_protections.rb +135 -0
- data/lib/rubocop/cop/package_protections/namespaced_under_package_name.rb +108 -0
- metadata +220 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# typed: strict
|
4
|
+
module PackageProtections
|
5
|
+
module ProtectionInterface
|
6
|
+
extend T::Sig
|
7
|
+
extend T::Helpers
|
8
|
+
|
9
|
+
abstract!
|
10
|
+
|
11
|
+
requires_ancestor { Kernel }
|
12
|
+
|
13
|
+
sig do
|
14
|
+
params(
|
15
|
+
protected_packages: T::Array[ProtectedPackage],
|
16
|
+
new_violations: T::Array[PerFileViolation]
|
17
|
+
).returns(T::Array[Offense])
|
18
|
+
end
|
19
|
+
def get_offenses(protected_packages, new_violations)
|
20
|
+
[
|
21
|
+
# First we get all offenses for new violations
|
22
|
+
*get_offenses_for_new_violations(new_violations),
|
23
|
+
# Then we separately look at TODO lists and add violations if there are no issues
|
24
|
+
*get_offenses_for_existing_violations(protected_packages)
|
25
|
+
].sort_by(&:message)
|
26
|
+
end
|
27
|
+
|
28
|
+
sig { abstract.params(behavior: ViolationBehavior, package: ParsePackwerk::Package).returns(T.nilable(String)) }
|
29
|
+
def unmet_preconditions_for_behavior(behavior, package); end
|
30
|
+
|
31
|
+
sig { params(behavior: ViolationBehavior, package: ParsePackwerk::Package).returns(T::Boolean) }
|
32
|
+
def supports_violation_behavior?(behavior, package)
|
33
|
+
unmet_preconditions_for_behavior(behavior, package).nil?
|
34
|
+
end
|
35
|
+
|
36
|
+
sig { returns(ViolationBehavior) }
|
37
|
+
def default_behavior
|
38
|
+
# The default behavior here is that we simply return the `fail_on_new` protection.
|
39
|
+
# In some cases, this protection may not actually be supported. For example, `OutgoingPrivacyProtection` raises if the user has `enforce_privacy: false`
|
40
|
+
# Error messages should provide enough clarity to either:
|
41
|
+
# A) Know which protection to explicitly set to fail_never
|
42
|
+
# B) Know how to change conditions to allow protection to be supported (i.e. set enforce dependencies to be true)
|
43
|
+
ViolationBehavior::FailOnNew
|
44
|
+
end
|
45
|
+
|
46
|
+
sig do
|
47
|
+
abstract.params(
|
48
|
+
new_violations: T::Array[PerFileViolation]
|
49
|
+
).returns(T::Array[Offense])
|
50
|
+
end
|
51
|
+
def get_offenses_for_new_violations(new_violations); end
|
52
|
+
|
53
|
+
sig do
|
54
|
+
abstract.params(
|
55
|
+
protected_packages: T::Array[ProtectedPackage]
|
56
|
+
).returns(T::Array[Offense])
|
57
|
+
end
|
58
|
+
def get_offenses_for_existing_violations(protected_packages); end
|
59
|
+
|
60
|
+
sig { abstract.returns(String) }
|
61
|
+
def identifier; end
|
62
|
+
|
63
|
+
sig { abstract.returns(String) }
|
64
|
+
def humanized_protection_name; end
|
65
|
+
|
66
|
+
sig { abstract.returns(String) }
|
67
|
+
def humanized_protection_description; end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# typed: strict
|
4
|
+
module PackageProtections
|
5
|
+
module RubocopProtectionInterface
|
6
|
+
include ProtectionInterface
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Helpers
|
9
|
+
|
10
|
+
abstract!
|
11
|
+
|
12
|
+
sig do
|
13
|
+
abstract
|
14
|
+
.params(packages: T::Array[ProtectedPackage])
|
15
|
+
.returns(T::Array[CopConfig])
|
16
|
+
end
|
17
|
+
def cop_configs(packages); end
|
18
|
+
|
19
|
+
sig do
|
20
|
+
params(package: ProtectedPackage).returns(T::Hash[T.untyped, T.untyped])
|
21
|
+
end
|
22
|
+
def custom_cop_config(package)
|
23
|
+
{}
|
24
|
+
end
|
25
|
+
|
26
|
+
class CopConfig < T::Struct
|
27
|
+
extend T::Sig
|
28
|
+
const :name, String
|
29
|
+
const :enabled, T::Boolean, default: true
|
30
|
+
const :include_paths, T::Array[String], default: []
|
31
|
+
const :exclude_paths, T::Array[String], default: []
|
32
|
+
|
33
|
+
sig { returns(String) }
|
34
|
+
def to_rubocop_yml_compatible_format
|
35
|
+
cop_config = { 'Enabled' => enabled }
|
36
|
+
|
37
|
+
if include_paths.any?
|
38
|
+
cop_config['Include'] = include_paths
|
39
|
+
end
|
40
|
+
|
41
|
+
if exclude_paths.any?
|
42
|
+
cop_config['Exclude'] = exclude_paths
|
43
|
+
end
|
44
|
+
|
45
|
+
{ name => cop_config }.to_yaml.gsub("---\n", '')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
sig do
|
50
|
+
override.params(
|
51
|
+
new_violations: T::Array[PerFileViolation]
|
52
|
+
).returns(T::Array[Offense])
|
53
|
+
end
|
54
|
+
def get_offenses_for_new_violations(new_violations)
|
55
|
+
[]
|
56
|
+
end
|
57
|
+
|
58
|
+
sig { void }
|
59
|
+
def self.bust_rubocop_todo_yml_cache
|
60
|
+
@rubocop_todo_yml = nil
|
61
|
+
end
|
62
|
+
|
63
|
+
sig { returns(T.untyped) }
|
64
|
+
def self.rubocop_todo_yml
|
65
|
+
@rubocop_todo_yml = T.let(@rubocop_todo_yml, T.untyped)
|
66
|
+
@rubocop_todo_yml ||= begin
|
67
|
+
todo_file = Pathname.new('.rubocop_todo.yml')
|
68
|
+
if todo_file.exist?
|
69
|
+
YAML.load_file(todo_file)
|
70
|
+
else
|
71
|
+
{}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
sig { params(rule: String).returns(T::Set[String]) }
|
79
|
+
def exclude_for_rule(rule)
|
80
|
+
rule_config = RubocopProtectionInterface.rubocop_todo_yml[rule] || {}
|
81
|
+
Set.new(rule_config['Exclude'] || [])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# typed: strict
|
4
|
+
module PackageProtections
|
5
|
+
class IncorrectPublicApiUsageError < StandardError; end
|
6
|
+
|
7
|
+
class ViolationBehavior < T::Enum
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
enums do
|
11
|
+
FailOnAny = new('fail_on_any')
|
12
|
+
FailOnNew = new('fail_on_new')
|
13
|
+
FailNever = new('fail_never')
|
14
|
+
end
|
15
|
+
|
16
|
+
sig { params(value: T.untyped).returns(ViolationBehavior) }
|
17
|
+
def self.from_raw_value(value)
|
18
|
+
ViolationBehavior.deserialize(value.to_s)
|
19
|
+
rescue KeyError
|
20
|
+
# Let's not encourage "unknown." That's mostly considered an internal value if nothing is specified.
|
21
|
+
acceptable_values = ViolationBehavior.values.map(&:serialize) - ['unknown']
|
22
|
+
raise IncorrectPublicApiUsageError.new("The metadata value #{value} is not a valid behavior. Double check your spelling! Acceptable values are #{acceptable_values}. See https://github.com/bigrails/package_protections#readme for more info") # rubocop:disable Style/RaiseArgs
|
23
|
+
end
|
24
|
+
|
25
|
+
sig { returns(T::Boolean) }
|
26
|
+
def fail_on_any?
|
27
|
+
case self
|
28
|
+
when FailOnAny then true
|
29
|
+
when FailOnNew then false
|
30
|
+
when FailNever then false
|
31
|
+
else
|
32
|
+
T.absurd(self)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
sig { returns(T::Boolean) }
|
37
|
+
def enabled?
|
38
|
+
case self
|
39
|
+
when FailOnAny then true
|
40
|
+
when FailOnNew then true
|
41
|
+
when FailNever then false
|
42
|
+
else
|
43
|
+
T.absurd(self)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
sig { returns(T::Boolean) }
|
48
|
+
def fail_on_new?
|
49
|
+
case self
|
50
|
+
when FailOnAny then false
|
51
|
+
when FailOnNew then true
|
52
|
+
when FailNever then false
|
53
|
+
else
|
54
|
+
T.absurd(self)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
sig { returns(T::Boolean) }
|
59
|
+
def fail_never?
|
60
|
+
case self
|
61
|
+
when FailOnAny then false
|
62
|
+
when FailOnNew then false
|
63
|
+
when FailNever then true
|
64
|
+
else
|
65
|
+
T.absurd(self)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# typed: strict
|
4
|
+
require 'sorbet-runtime'
|
5
|
+
require 'open3'
|
6
|
+
require 'set'
|
7
|
+
require 'parse_packwerk'
|
8
|
+
require 'rubocop'
|
9
|
+
require 'rubocop-sorbet'
|
10
|
+
|
11
|
+
#
|
12
|
+
# Welcome to PackageProtections!
|
13
|
+
# See https://github.com/bigrails/package_protections#readme for more info
|
14
|
+
#
|
15
|
+
# This file is a reference for the available API to `package_protections`, but all implementation details are private
|
16
|
+
# (which is why we delegate to `Private` for the actual implementation).
|
17
|
+
#
|
18
|
+
module PackageProtections
|
19
|
+
extend T::Sig
|
20
|
+
|
21
|
+
PROTECTIONS_TODO_YML = 'protections_todo.yml'
|
22
|
+
EXPECTED_PACK_DIRECTORIES = T.let(%w[packs packages gems components], T::Array[String])
|
23
|
+
|
24
|
+
# A protection identifier is just a string that identifies the name of the protection within a `package.yml`
|
25
|
+
Identifier = T.type_alias { String }
|
26
|
+
|
27
|
+
# This is currently the only handled exception that `PackageProtections` will throw.
|
28
|
+
class IncorrectPublicApiUsageError < StandardError; end
|
29
|
+
|
30
|
+
require 'package_protections/offense'
|
31
|
+
require 'package_protections/violation_behavior'
|
32
|
+
require 'package_protections/protected_package'
|
33
|
+
require 'package_protections/per_file_violation'
|
34
|
+
require 'package_protections/protection_interface'
|
35
|
+
require 'package_protections/rubocop_protection_interface'
|
36
|
+
require 'package_protections/private'
|
37
|
+
|
38
|
+
# Implementation of rubocop-based protections
|
39
|
+
require 'rubocop/cop/package_protections/namespaced_under_package_name'
|
40
|
+
|
41
|
+
#
|
42
|
+
# These are all of the concrete implementations of the `ProtectionInterface`.
|
43
|
+
# This list is added to when a new protection is created, but in the future,
|
44
|
+
# this list may be injected by the client.
|
45
|
+
#
|
46
|
+
sig { returns(T::Array[ProtectionInterface]) }
|
47
|
+
def self.all
|
48
|
+
[
|
49
|
+
Private::OutgoingDependencyProtection.new,
|
50
|
+
Private::IncomingPrivacyProtection.new,
|
51
|
+
Private::TypedApiProtection.new,
|
52
|
+
Private::MultipleNamespacesProtection.new,
|
53
|
+
Private::VisibilityProtection.new
|
54
|
+
]
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# This is a fast way to get a protection given an identifier
|
59
|
+
#
|
60
|
+
sig { params(identifier: Identifier).returns(ProtectionInterface) }
|
61
|
+
def self.with_identifier(identifier)
|
62
|
+
@map ||= T.let(@map, T.nilable(T::Hash[Identifier, ProtectionInterface]))
|
63
|
+
@map ||= all.map { |protection| [protection.identifier, protection] }.to_h
|
64
|
+
@map.fetch(identifier)
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
# This returns an array of a `Offense` which is how we represent the outcome of attempting to protect one or more packages,
|
69
|
+
# each of which is configured with different violation behaviors for each protection.
|
70
|
+
#
|
71
|
+
sig do
|
72
|
+
params(
|
73
|
+
packages: T::Array[ParsePackwerk::Package],
|
74
|
+
new_violations: T::Array[PerFileViolation]
|
75
|
+
).returns(T::Array[Offense])
|
76
|
+
end
|
77
|
+
def self.get_offenses(packages:, new_violations:)
|
78
|
+
Private.get_offenses(
|
79
|
+
packages: packages,
|
80
|
+
new_violations: new_violations
|
81
|
+
).compact
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# PackageProtections.set_defaults! sets any unset protections to their default enforcement
|
86
|
+
#
|
87
|
+
sig do
|
88
|
+
params(
|
89
|
+
packages: T::Array[ParsePackwerk::Package],
|
90
|
+
protection_identifiers: T::Array[String],
|
91
|
+
verbose: T::Boolean
|
92
|
+
).void
|
93
|
+
end
|
94
|
+
def self.set_defaults!(packages, protection_identifiers: PackageProtections.all.map(&:identifier), verbose: true)
|
95
|
+
Private.set_defaults!(packages, protection_identifiers: protection_identifiers, verbose: verbose)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Why do we use Bundler.root here?
|
99
|
+
# The reason is that this function is evaluated in `.client_rubocop.yml`, so when rubocop evaluates the
|
100
|
+
# YML and parses the ERB, the working directory is inside this gem. We need to make sure it's at the root of the
|
101
|
+
# application so that we can find all of the packwerk packages.
|
102
|
+
# We use Bundler.root to get the root because:
|
103
|
+
# A) We expect it to be reliable
|
104
|
+
# B) We expect the client to be running bundle exec rubocop, which is typically done at the root, and also means
|
105
|
+
# the client already has this dependency.
|
106
|
+
sig { params(root_pathname: Pathname).returns(String) }
|
107
|
+
def self.rubocop_yml(root_pathname: Bundler.root)
|
108
|
+
Private.rubocop_yml(root_pathname: root_pathname)
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Do not use this method -- it's meant to be used by Rubocop cops to get directory-specific
|
113
|
+
# parameters without needing to have directory-specific .rubocop.yml files.
|
114
|
+
#
|
115
|
+
sig { params(identifier: Identifier).returns(T::Hash[T.untyped, T.untyped]) }
|
116
|
+
def self.private_cop_config(identifier)
|
117
|
+
Private.private_cop_config(identifier)
|
118
|
+
end
|
119
|
+
|
120
|
+
sig do
|
121
|
+
params(
|
122
|
+
package_names: T::Array[String],
|
123
|
+
all_packages: T::Array[ParsePackwerk::Package]
|
124
|
+
).returns(T::Array[ParsePackwerk::Package])
|
125
|
+
end
|
126
|
+
def self.packages_for_names(package_names, all_packages)
|
127
|
+
Private.packages_for_names(package_names, all_packages)
|
128
|
+
end
|
129
|
+
|
130
|
+
sig { void }
|
131
|
+
def self.bust_cache!
|
132
|
+
Private.bust_cache!
|
133
|
+
RubocopProtectionInterface.bust_rubocop_todo_yml_cache
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# typed: ignore
|
2
|
+
|
3
|
+
# For String#camelize
|
4
|
+
require 'active_support/core_ext/string/inflections'
|
5
|
+
|
6
|
+
module RuboCop
|
7
|
+
module Cop
|
8
|
+
module PackageProtections
|
9
|
+
class NamespacedUnderPackageName < Base
|
10
|
+
include RangeHelp
|
11
|
+
|
12
|
+
def on_new_investigation
|
13
|
+
absolute_filepath = Pathname.new(processed_source.file_path)
|
14
|
+
relative_filepath = absolute_filepath.relative_path_from(Pathname.pwd)
|
15
|
+
relative_filename = relative_filepath.to_s
|
16
|
+
|
17
|
+
# This cop only works for files in `app`
|
18
|
+
return if !relative_filename.include?('app/')
|
19
|
+
|
20
|
+
match = relative_filename.match(%r{((#{::PackageProtections::EXPECTED_PACK_DIRECTORIES.join("|")})/.*?)/})
|
21
|
+
package_name = match && match[1]
|
22
|
+
|
23
|
+
return if package_name.nil?
|
24
|
+
|
25
|
+
return if relative_filepath.extname != '.rb'
|
26
|
+
|
27
|
+
# Zeitwerk establishes a standard convention by which namespaces are defined.
|
28
|
+
# The package protections namespace checker is coupled to a specific assumption about how auto-loading works.
|
29
|
+
#
|
30
|
+
# Namely, we expect the following autoload paths: `packs/**/app/**/`
|
31
|
+
# Examples:
|
32
|
+
# 1) `packs/package_1/app/public/package_1/my_constant.rb` produces constant `Package1::MyConstant`
|
33
|
+
# 2) `packs/package_1/app/services/package_1/my_service.rb` produces constant `Package1::MyService`
|
34
|
+
# 3) `packs/package_1/app/services/package_1.rb` produces constant `Package1`
|
35
|
+
# 4) `packs/package_1/app/public/package_1.rb` produces constant `Package1`
|
36
|
+
#
|
37
|
+
# Described another way, we expect any part of the directory labeled NAMESPACE to establish a portion of the fully qualified runtime constant:
|
38
|
+
# `packs/**/app/**/NAMESPACE1/NAMESPACE2/[etc]`
|
39
|
+
#
|
40
|
+
# Therefore, for our implementation, we substitute out the non-namespace producing portions of the filename to count the number of namespaces.
|
41
|
+
# Note this will *not work* properly in applications that have different assumptions about autoloading.
|
42
|
+
package_last_name = package_name.split('/').last
|
43
|
+
path_without_package_base = relative_filename.gsub(%r{#{package_name}/app/}, '')
|
44
|
+
if path_without_package_base.include?('concerns')
|
45
|
+
autoload_folder_name = path_without_package_base.split('/').first(2).join('/')
|
46
|
+
else
|
47
|
+
autoload_folder_name = path_without_package_base.split('/').first
|
48
|
+
end
|
49
|
+
|
50
|
+
remaining_file_path = path_without_package_base.gsub(%r{\A#{autoload_folder_name}/}, '')
|
51
|
+
actual_namespace = get_actual_namespace(remaining_file_path, relative_filepath, package_name)
|
52
|
+
allowed_namespaces = get_allowed_namespaces(package_name)
|
53
|
+
if allowed_namespaces.include?(actual_namespace)
|
54
|
+
# No problem!
|
55
|
+
elsif allowed_namespaces.count == 1
|
56
|
+
single_allowed_namespace = allowed_namespaces.first
|
57
|
+
if relative_filepath.to_s.include?('app/')
|
58
|
+
app_or_lib = 'app'
|
59
|
+
elsif relative_filepath.to_s.include?('lib/')
|
60
|
+
app_or_lib = 'lib'
|
61
|
+
end
|
62
|
+
|
63
|
+
absolute_desired_path = root_pathname.join(package_name, app_or_lib, T.must(autoload_folder_name), single_allowed_namespace.underscore, remaining_file_path)
|
64
|
+
|
65
|
+
relative_desired_path = absolute_desired_path.relative_path_from(root_pathname)
|
66
|
+
|
67
|
+
add_offense(
|
68
|
+
source_range(processed_source.buffer, 1, 0),
|
69
|
+
message: format(
|
70
|
+
'`%<package_name>s` prevents modules/classes that are not submodules of the package namespace. Should be namespaced under `%<expected_namespace>s` with path `%<expected_path>s`. See https://go/packwerk_cheatsheet_namespaces for more info.',
|
71
|
+
package_name: package_name,
|
72
|
+
expected_namespace: package_last_name.camelize,
|
73
|
+
expected_path: relative_desired_path
|
74
|
+
)
|
75
|
+
)
|
76
|
+
else
|
77
|
+
add_offense(
|
78
|
+
source_range(processed_source.buffer, 1, 0),
|
79
|
+
message: format(
|
80
|
+
'`%<package_name>s` prevents modules/classes that are not submodules of one of the allowed namespaces in `%<package_yml>s`. See https://go/packwerk_cheatsheet_namespaces for more info.',
|
81
|
+
package_name: package_name,
|
82
|
+
package_yml: "#{package_name}/package.yml"
|
83
|
+
)
|
84
|
+
)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def get_allowed_namespaces(package_name)
|
91
|
+
cop_config = ::PackageProtections.private_cop_config('prevent_this_package_from_creating_other_namespaces')
|
92
|
+
allowed_global_namespaces = cop_config[package_name]['GlobalNamespaces'] || [package_name.split('/').last.camelize]
|
93
|
+
Set.new(allowed_global_namespaces)
|
94
|
+
end
|
95
|
+
|
96
|
+
def get_actual_namespace(remaining_file_path, relative_filepath, package_name)
|
97
|
+
# If the remaining file path is a ruby file (not a directory), then it establishes a global namespace
|
98
|
+
# Otherwise, per Zeitwerk's conventions as listed above, its a directory that establishes another global namespace
|
99
|
+
remaining_file_path.split('/').first.gsub('.rb', '').camelize
|
100
|
+
end
|
101
|
+
|
102
|
+
def root_pathname
|
103
|
+
Pathname.pwd
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
metadata
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: package_protections
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.64.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gusto Engineers
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-05-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: parse_packwerk
|
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: rubocop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop-sorbet
|
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: sorbet-runtime
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
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: rake
|
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: rspec
|
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: rubocop-rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: sorbet
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
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: Package protections for Rails apps
|
168
|
+
email:
|
169
|
+
- stephan.hagemann@gusto.com
|
170
|
+
executables: []
|
171
|
+
extensions: []
|
172
|
+
extra_rdoc_files: []
|
173
|
+
files:
|
174
|
+
- README.md
|
175
|
+
- config/default.yml
|
176
|
+
- lib/package_protections.rb
|
177
|
+
- lib/package_protections/offense.rb
|
178
|
+
- lib/package_protections/per_file_violation.rb
|
179
|
+
- lib/package_protections/private.rb
|
180
|
+
- lib/package_protections/private/colorized_string.rb
|
181
|
+
- lib/package_protections/private/incoming_privacy_protection.rb
|
182
|
+
- lib/package_protections/private/metadata_modifiers.rb
|
183
|
+
- lib/package_protections/private/multiple_namespaces_protection.rb
|
184
|
+
- lib/package_protections/private/outgoing_dependency_protection.rb
|
185
|
+
- lib/package_protections/private/output.rb
|
186
|
+
- lib/package_protections/private/typed_api_protection.rb
|
187
|
+
- lib/package_protections/private/visibility_protection.rb
|
188
|
+
- lib/package_protections/protected_package.rb
|
189
|
+
- lib/package_protections/protection_interface.rb
|
190
|
+
- lib/package_protections/rubocop_protection_interface.rb
|
191
|
+
- lib/package_protections/violation_behavior.rb
|
192
|
+
- lib/rubocop/cop/package_protections/namespaced_under_package_name.rb
|
193
|
+
homepage: https://github.com/bigrails/package_protections
|
194
|
+
licenses:
|
195
|
+
- MIT
|
196
|
+
metadata:
|
197
|
+
homepage_uri: https://github.com/bigrails/package_protections
|
198
|
+
source_code_uri: https://github.com/bigrails/parse_packwerk
|
199
|
+
changelog_uri: https://github.com/bigrails/parse_packwerk/releases
|
200
|
+
allowed_push_host: https://rubygems.org
|
201
|
+
post_install_message:
|
202
|
+
rdoc_options: []
|
203
|
+
require_paths:
|
204
|
+
- lib
|
205
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
206
|
+
requirements:
|
207
|
+
- - ">="
|
208
|
+
- !ruby/object:Gem::Version
|
209
|
+
version: 2.5.0
|
210
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
211
|
+
requirements:
|
212
|
+
- - ">="
|
213
|
+
- !ruby/object:Gem::Version
|
214
|
+
version: '0'
|
215
|
+
requirements: []
|
216
|
+
rubygems_version: 3.3.7
|
217
|
+
signing_key:
|
218
|
+
specification_version: 4
|
219
|
+
summary: Package protections for Rails apps
|
220
|
+
test_files: []
|