modularization_statistics 1.37.0 → 1.39.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 +4 -4
- data/lib/modularization_statistics/private/metrics/packages.rb +9 -11
- data/lib/modularization_statistics/private/metrics/packages_by_team.rb +11 -11
- data/lib/modularization_statistics/private/metrics/protection_usage.rb +119 -3
- data/lib/modularization_statistics/private/metrics/rubocop_protections_exclusions.rb +5 -2
- data/sorbet/rbi/gems/{parse_packwerk@0.12.0.rbi → parse_packwerk@0.14.0.rbi} +10 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c11bc9704708d387bff6b5d7245832c6d57c9291a7dcc306513e3942afb53311
|
4
|
+
data.tar.gz: 7d5403d37a5811f5ef1798a3a8bfb96b304124ada289b75dba14db0844fd252e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c82a234d983ddc50d494f6eb8933633a0b61a7e82783d375f5d22b02c1e12fb476f9fb70ca35885ecadc879dd1fc94179cf6db3b1d52f47501afe38c6349651
|
7
|
+
data.tar.gz: ec00762292cdec0eb441ad424bdd5e80733a9acd418925f82c92142988df9d4b3b6843b8e4de1d9b8190e48cf23f6818b2e4f7176bf263a6541fa490181c38b0
|
@@ -17,36 +17,34 @@ module ModularizationStatistics
|
|
17
17
|
all_metrics = []
|
18
18
|
app_level_tag = Tag.for('app', app_name)
|
19
19
|
package_tags = T.let([app_level_tag], T::Array[Tag])
|
20
|
-
protected_packages = packages.map { |p| PackageProtections::ProtectedPackage.from(p) }
|
21
20
|
|
22
21
|
all_metrics << GaugeMetric.for('all_packages.count', packages.count, package_tags)
|
23
22
|
all_metrics << GaugeMetric.for('all_packages.dependencies.count', packages.sum { |package| package.dependencies.count }, package_tags)
|
24
|
-
all_metrics << GaugeMetric.for('all_packages.dependency_violations.count',
|
25
|
-
all_metrics << GaugeMetric.for('all_packages.privacy_violations.count',
|
23
|
+
all_metrics << GaugeMetric.for('all_packages.dependency_violations.count', packages.sum { |package| Metrics.file_count(package.violations.select(&:dependency?)) }, package_tags)
|
24
|
+
all_metrics << GaugeMetric.for('all_packages.privacy_violations.count', packages.sum { |package| Metrics.file_count(package.violations.select(&:privacy?)) }, package_tags)
|
26
25
|
all_metrics << GaugeMetric.for('all_packages.enforcing_dependencies.count', packages.count(&:enforces_dependencies?), package_tags)
|
27
26
|
all_metrics << GaugeMetric.for('all_packages.enforcing_privacy.count', packages.count(&:enforces_privacy?), package_tags)
|
28
27
|
|
29
28
|
all_metrics << GaugeMetric.for('all_packages.notify_on_package_yml_changes.count', packages.count { |p| p.metadata['notify_on_package_yml_changes'] }, package_tags)
|
30
29
|
all_metrics << GaugeMetric.for('all_packages.notify_on_new_violations.count', packages.count { |p| p.metadata['notify_on_new_violations'] }, package_tags)
|
31
30
|
|
32
|
-
all_metrics << GaugeMetric.for('all_packages.with_violations.count',
|
31
|
+
all_metrics << GaugeMetric.for('all_packages.with_violations.count', packages.count { |package| package.violations.any? }, package_tags)
|
33
32
|
all_metrics += Metrics::PublicUsage.get_public_usage_metrics('all_packages', packages, package_tags)
|
34
33
|
all_metrics << GaugeMetric.for('all_packages.has_readme.count', packages.count { |package| Metrics.has_readme?(package) }, package_tags)
|
35
34
|
|
36
|
-
all_metrics += Metrics::ProtectionUsage.get_protections_metrics('all_packages',
|
37
|
-
all_metrics += Metrics::RubocopProtectionsExclusions.get_rubocop_exclusions('all_packages',
|
35
|
+
all_metrics += Metrics::ProtectionUsage.get_protections_metrics('all_packages', packages, package_tags)
|
36
|
+
all_metrics += Metrics::RubocopProtectionsExclusions.get_rubocop_exclusions('all_packages', packages, package_tags)
|
38
37
|
all_metrics << GaugeMetric.for('all_packages.package_based_file_ownership.count', packages.count { |package| !package.metadata['owner'].nil? }, package_tags)
|
39
38
|
|
40
|
-
inbound_violations_by_package =
|
39
|
+
inbound_violations_by_package = packages.flat_map(&:violations).group_by(&:to_package_name)
|
41
40
|
|
42
|
-
|
43
|
-
package = protected_package.original_package
|
41
|
+
packages.each do |package|
|
44
42
|
package_tags = Metrics.tags_for_package(package, app_name)
|
45
43
|
|
46
44
|
#
|
47
45
|
# VIOLATIONS (implicit dependencies)
|
48
46
|
#
|
49
|
-
outbound_violations =
|
47
|
+
outbound_violations = package.violations
|
50
48
|
inbound_violations = inbound_violations_by_package[package.name] || []
|
51
49
|
all_dependency_violations = (outbound_violations + inbound_violations).select(&:dependency?)
|
52
50
|
all_privacy_violations = (outbound_violations + inbound_violations).select(&:privacy?)
|
@@ -62,7 +60,7 @@ module ModularizationStatistics
|
|
62
60
|
|
63
61
|
all_metrics += Metrics::PublicUsage.get_public_usage_metrics('by_package', [package], package_tags)
|
64
62
|
|
65
|
-
|
63
|
+
package.violations.group_by(&:to_package_name).each do |to_package_name, violations|
|
66
64
|
to_package = ParsePackwerk.find(to_package_name)
|
67
65
|
if to_package.nil?
|
68
66
|
raise StandardError, "Could not find matching package #{to_package_name}"
|
@@ -16,25 +16,25 @@ module ModularizationStatistics
|
|
16
16
|
def self.get_package_metrics_by_team(all_packages, app_name)
|
17
17
|
all_metrics = T.let([], T::Array[GaugeMetric])
|
18
18
|
app_level_tag = Tag.for('app', app_name)
|
19
|
-
|
20
|
-
|
19
|
+
|
20
|
+
all_packages.group_by { |package| CodeOwnership.for_package(package)&.name }.each do |team_name, packages_by_team|
|
21
21
|
# We look at `all_packages` because we care about ALL inbound violations across all teams
|
22
|
-
inbound_violations_by_package =
|
22
|
+
inbound_violations_by_package = all_packages.flat_map(&:violations).group_by(&:to_package_name)
|
23
23
|
|
24
24
|
team_tags = Metrics.tags_for_team(team_name) + [app_level_tag]
|
25
|
-
all_metrics << GaugeMetric.for('by_team.all_packages.count',
|
26
|
-
all_metrics += Metrics::ProtectionUsage.get_protections_metrics('by_team',
|
27
|
-
all_metrics += Metrics::PublicUsage.get_public_usage_metrics('by_team',
|
25
|
+
all_metrics << GaugeMetric.for('by_team.all_packages.count', packages_by_team.count, team_tags)
|
26
|
+
all_metrics += Metrics::ProtectionUsage.get_protections_metrics('by_team', packages_by_team, team_tags)
|
27
|
+
all_metrics += Metrics::PublicUsage.get_public_usage_metrics('by_team', packages_by_team, team_tags)
|
28
28
|
|
29
|
-
all_metrics << GaugeMetric.for('by_team.notify_on_package_yml_changes.count',
|
30
|
-
all_metrics << GaugeMetric.for('by_team.notify_on_new_violations.count',
|
29
|
+
all_metrics << GaugeMetric.for('by_team.notify_on_package_yml_changes.count', packages_by_team.count { |p| p.metadata['notify_on_package_yml_changes'] }, team_tags)
|
30
|
+
all_metrics << GaugeMetric.for('by_team.notify_on_new_violations.count', packages_by_team.count { |p| p.metadata['notify_on_new_violations'] }, team_tags)
|
31
31
|
|
32
32
|
#
|
33
33
|
# VIOLATIONS (implicit dependencies)
|
34
34
|
#
|
35
|
-
outbound_violations =
|
35
|
+
outbound_violations = packages_by_team.flat_map(&:violations)
|
36
36
|
# Here we only look at packages_by_team because we only care about inbound violations onto packages for this team
|
37
|
-
inbound_violations =
|
37
|
+
inbound_violations = packages_by_team.flat_map { |package| inbound_violations_by_package[package.name] || [] }
|
38
38
|
all_dependency_violations = (outbound_violations + inbound_violations).select(&:dependency?)
|
39
39
|
all_privacy_violations = (outbound_violations + inbound_violations).select(&:privacy?)
|
40
40
|
|
@@ -47,7 +47,7 @@ module ModularizationStatistics
|
|
47
47
|
all_metrics << GaugeMetric.for('by_team.outbound_privacy_violations.count', Metrics.file_count(outbound_violations.select(&:privacy?)), team_tags)
|
48
48
|
all_metrics << GaugeMetric.for('by_team.inbound_privacy_violations.count', Metrics.file_count(inbound_violations.select(&:privacy?)), team_tags)
|
49
49
|
|
50
|
-
all_metrics << GaugeMetric.for('by_team.has_readme.count',
|
50
|
+
all_metrics << GaugeMetric.for('by_team.has_readme.count', packages_by_team.count { |package| Metrics.has_readme?(package) }, team_tags)
|
51
51
|
|
52
52
|
grouped_outbound_violations = outbound_violations.group_by do |violation|
|
53
53
|
to_package = ParsePackwerk.find(violation.to_package_name)
|
@@ -7,8 +7,9 @@ module ModularizationStatistics
|
|
7
7
|
class ProtectionUsage
|
8
8
|
extend T::Sig
|
9
9
|
|
10
|
-
sig { params(prefix: String,
|
11
|
-
def self.get_protections_metrics(prefix,
|
10
|
+
sig { params(prefix: String, packages: T::Array[ParsePackwerk::Package], package_tags: T::Array[Tag]).returns(T::Array[GaugeMetric]) }
|
11
|
+
def self.get_protections_metrics(prefix, packages, package_tags)
|
12
|
+
protected_packages = packages.map { |p| PackageProtections::ProtectedPackage.from(p) }
|
12
13
|
PackageProtections.all.flat_map do |protection|
|
13
14
|
PackageProtections::ViolationBehavior.each_value.map do |violation_behavior|
|
14
15
|
# https://github.com/Gusto/package_protections/pull/42 changed the public API of these violation behaviors.
|
@@ -22,11 +23,126 @@ module ModularizationStatistics
|
|
22
23
|
}
|
23
24
|
violation_behavior_name = violation_behavior_map[violation_behavior]
|
24
25
|
metric_name = "#{prefix}.#{protection.identifier}.#{violation_behavior_name}.count"
|
25
|
-
count_of_packages = protected_packages.count
|
26
|
+
count_of_packages = protected_packages.count do |p|
|
27
|
+
#
|
28
|
+
# This is temporarily in place until we migrate off of `package_protections` in favor of `rubocop-packs`.
|
29
|
+
# At that point, we want to delete this branch and instead it we'd probably have two separate branches.
|
30
|
+
# One branch would look at `enforce_x` and `metadata.strictly_enforce_x`.
|
31
|
+
# The other branch would look at `.pack_rubocop.yml`.
|
32
|
+
# Later on, we could generalize this so that it automatically incorporates new cops from `rubocop-packs`,
|
33
|
+
# or even new packwerk plugins.
|
34
|
+
#
|
35
|
+
# Regardless, we'll want to keep the way we are naming these behaviors for now to preserve historical trends in the data.
|
36
|
+
#
|
37
|
+
if p.metadata['protections']
|
38
|
+
p.violation_behavior_for(protection.identifier) == violation_behavior
|
39
|
+
else
|
40
|
+
should_count_package?(p.original_package, protection, violation_behavior)
|
41
|
+
end
|
42
|
+
end
|
26
43
|
GaugeMetric.for(metric_name, count_of_packages, package_tags)
|
27
44
|
end
|
28
45
|
end
|
29
46
|
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Later, when we remove package protections, we can make this simpler by iterating over
|
50
|
+
# packwerk checkers and rubocop packs specifically. That would let us use a common, simple
|
51
|
+
# strategy to get metrics for both of them. For the first iteration, we'll want to continue
|
52
|
+
# to map the old names of things to the "protection" names. After that, I think we will want to
|
53
|
+
# extract that mapping into a tool that transforms the metrics that can be optionally turned off
|
54
|
+
# so that we can see metrics that are more closely connected to the new API.
|
55
|
+
# e.g. instead of `all_packages.prevent_this_package_from_violating_its_stated_dependencies.fail_on_any.count`, we'd see
|
56
|
+
# e.g. instead of `all_packages.checkers.enforce_dependencies.strict.count`, we'd see
|
57
|
+
# e.g. instead of `all_packages.prevent_this_package_from_creating_other_namespaces.fail_on_new.count`, we'd see
|
58
|
+
# e.g. instead of `all_packages.cops.packs_namespaceconvention.true.count`, we'd see
|
59
|
+
#
|
60
|
+
sig do
|
61
|
+
params(
|
62
|
+
package: ParsePackwerk::Package,
|
63
|
+
protection: PackageProtections::ProtectionInterface,
|
64
|
+
violation_behavior: PackageProtections::ViolationBehavior
|
65
|
+
).returns(T::Boolean)
|
66
|
+
end
|
67
|
+
def self.should_count_package?(package, protection, violation_behavior)
|
68
|
+
if protection.identifier == 'prevent_this_package_from_violating_its_stated_dependencies'
|
69
|
+
strict_mode = package.metadata['strictly_enforce_dependencies']
|
70
|
+
enabled = package.enforces_dependencies?
|
71
|
+
|
72
|
+
case violation_behavior
|
73
|
+
when PackageProtections::ViolationBehavior::FailOnAny
|
74
|
+
!!strict_mode
|
75
|
+
when PackageProtections::ViolationBehavior::FailNever
|
76
|
+
!enabled
|
77
|
+
when PackageProtections::ViolationBehavior::FailOnNew
|
78
|
+
enabled && !strict_mode
|
79
|
+
else
|
80
|
+
T.absurd(violation_behavior)
|
81
|
+
end
|
82
|
+
elsif protection.identifier == 'prevent_other_packages_from_using_this_packages_internals'
|
83
|
+
strict_mode = package.metadata['strictly_enforce_privacy']
|
84
|
+
enabled = package.enforces_privacy?
|
85
|
+
|
86
|
+
case violation_behavior
|
87
|
+
when PackageProtections::ViolationBehavior::FailOnAny
|
88
|
+
!!strict_mode
|
89
|
+
when PackageProtections::ViolationBehavior::FailNever
|
90
|
+
!enabled
|
91
|
+
when PackageProtections::ViolationBehavior::FailOnNew
|
92
|
+
enabled && !strict_mode
|
93
|
+
else
|
94
|
+
T.absurd(violation_behavior)
|
95
|
+
end
|
96
|
+
elsif protection.identifier == 'prevent_other_packages_from_using_this_package_without_explicit_visibility'
|
97
|
+
case violation_behavior
|
98
|
+
when PackageProtections::ViolationBehavior::FailOnAny
|
99
|
+
# We'd probably not want to support this right away
|
100
|
+
false
|
101
|
+
when PackageProtections::ViolationBehavior::FailNever
|
102
|
+
# We'd need to add this to `parse_packwerk` so that we can get other arbitrary top-level keys.
|
103
|
+
# Alternatively we can put this in `metadata` for the time being to unblock us.
|
104
|
+
# package.config['enforce_visibility']
|
105
|
+
!package.metadata['enforce_visibility']
|
106
|
+
when PackageProtections::ViolationBehavior::FailOnNew
|
107
|
+
!!package.metadata['enforce_visibility']
|
108
|
+
else
|
109
|
+
T.absurd(violation_behavior)
|
110
|
+
end
|
111
|
+
else
|
112
|
+
# Otherwise, we're in a rubocop case
|
113
|
+
rubocop_yml_file = package.directory.join('.rubocop.yml')
|
114
|
+
return false if !rubocop_yml_file.exist?
|
115
|
+
rubocop_yml = YAML.load_file(rubocop_yml_file)
|
116
|
+
protection = T.cast(protection, PackageProtections::RubocopProtectionInterface)
|
117
|
+
# We will likely want a rubocop-packs API for this, to be able to ask if a cop is enabled for a pack.
|
118
|
+
# It's possible we will want to allow these to be enabled at the top-level `.rubocop.yml`,
|
119
|
+
# in which case we wouldn't get the right metrics with this approach. However, we can also accept
|
120
|
+
# that as a current limitation.
|
121
|
+
cop_map = {
|
122
|
+
'PackageProtections/TypedPublicApi' => 'Packs/TypedPublicApi',
|
123
|
+
'PackageProtections/NamespacedUnderPackageName' => 'Packs/NamespaceConvention',
|
124
|
+
'PackageProtections/OnlyClassMethods' => 'Packs/ClassMethodsAsPublicApis',
|
125
|
+
'PackageProtections/RequireDocumentedPublicApis' => 'Packs/RequireDocumentedPublicApis',
|
126
|
+
}
|
127
|
+
# We want to use the cop names from `rubocop-packs`. Eventually, we'll just literate over these
|
128
|
+
# cop names directly, or ask `rubocop-packs` for the list of cops to care about.
|
129
|
+
cop_config = rubocop_yml[cop_map[protection.cop_name]]
|
130
|
+
return false if cop_config.nil?
|
131
|
+
enabled = cop_config['Enabled']
|
132
|
+
strict_mode = cop_config['FailureMode'] == 'strict'
|
133
|
+
|
134
|
+
case violation_behavior
|
135
|
+
when PackageProtections::ViolationBehavior::FailOnAny
|
136
|
+
!!strict_mode
|
137
|
+
when PackageProtections::ViolationBehavior::FailNever
|
138
|
+
!enabled
|
139
|
+
when PackageProtections::ViolationBehavior::FailOnNew
|
140
|
+
enabled && !strict_mode
|
141
|
+
else
|
142
|
+
T.absurd(violation_behavior)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
30
146
|
end
|
31
147
|
end
|
32
148
|
end
|
@@ -7,8 +7,10 @@ module ModularizationStatistics
|
|
7
7
|
class RubocopProtectionsExclusions
|
8
8
|
extend T::Sig
|
9
9
|
|
10
|
-
sig { params(prefix: String,
|
11
|
-
def self.get_rubocop_exclusions(prefix,
|
10
|
+
sig { params(prefix: String, packages: T::Array[ParsePackwerk::Package], package_tags: T::Array[Tag]).returns(T::Array[GaugeMetric]) }
|
11
|
+
def self.get_rubocop_exclusions(prefix, packages, package_tags)
|
12
|
+
protected_packages = packages.map { |p| PackageProtections::ProtectedPackage.from(p) }
|
13
|
+
|
12
14
|
rubocop_based_package_protections = T.cast(PackageProtections.all.select { |p| p.is_a?(PackageProtections::RubocopProtectionInterface) }, T::Array[PackageProtections::RubocopProtectionInterface])
|
13
15
|
rubocop_based_package_protections.flat_map do |rubocop_based_package_protection|
|
14
16
|
metric_name = "#{prefix}.#{rubocop_based_package_protection.identifier}.rubocop_exclusions.count"
|
@@ -17,6 +19,7 @@ module ModularizationStatistics
|
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
22
|
+
# TODO: `rubocop-packs` may want to expose API for this
|
20
23
|
sig { params(package: ParsePackwerk::Package, protection: PackageProtections::RubocopProtectionInterface).returns(Integer) }
|
21
24
|
def self.exclude_count_for_package_and_protection(package, protection)
|
22
25
|
rubocop_todo = package.directory.join('.rubocop_todo.yml')
|
@@ -15,7 +15,7 @@ module ParsePackwerk
|
|
15
15
|
sig { params(name: ::String).returns(T.nilable(::ParsePackwerk::Package)) }
|
16
16
|
def find(name); end
|
17
17
|
|
18
|
-
sig { params(file_path: T.any(::Pathname, ::String)).returns(
|
18
|
+
sig { params(file_path: T.any(::Pathname, ::String)).returns(::ParsePackwerk::Package) }
|
19
19
|
def package_from_path(file_path); end
|
20
20
|
|
21
21
|
sig { params(package: ::ParsePackwerk::Package).void }
|
@@ -51,6 +51,7 @@ end
|
|
51
51
|
|
52
52
|
ParsePackwerk::DEFAULT_EXCLUDE_GLOBS = T.let(T.unsafe(nil), Array)
|
53
53
|
ParsePackwerk::DEFAULT_PACKAGE_PATHS = T.let(T.unsafe(nil), Array)
|
54
|
+
ParsePackwerk::DEFAULT_PUBLIC_PATH = T.let(T.unsafe(nil), String)
|
54
55
|
ParsePackwerk::DEPENDENCIES = T.let(T.unsafe(nil), String)
|
55
56
|
ParsePackwerk::DEPRECATED_REFERENCES_YML_NAME = T.let(T.unsafe(nil), String)
|
56
57
|
|
@@ -81,6 +82,7 @@ end
|
|
81
82
|
|
82
83
|
ParsePackwerk::PACKAGE_YML_NAME = T.let(T.unsafe(nil), String)
|
83
84
|
ParsePackwerk::PACKWERK_YML_NAME = T.let(T.unsafe(nil), String)
|
85
|
+
ParsePackwerk::PUBLIC_PATH = T.let(T.unsafe(nil), String)
|
84
86
|
|
85
87
|
class ParsePackwerk::Package < ::T::Struct
|
86
88
|
const :dependencies, T::Array[::String]
|
@@ -88,6 +90,7 @@ class ParsePackwerk::Package < ::T::Struct
|
|
88
90
|
const :enforce_privacy, T::Boolean
|
89
91
|
const :metadata, T::Hash[T.untyped, T.untyped]
|
90
92
|
const :name, ::String
|
93
|
+
const :public_path, ::String, default: T.unsafe(nil)
|
91
94
|
|
92
95
|
sig { returns(::Pathname) }
|
93
96
|
def directory; end
|
@@ -98,6 +101,12 @@ class ParsePackwerk::Package < ::T::Struct
|
|
98
101
|
sig { returns(T::Boolean) }
|
99
102
|
def enforces_privacy?; end
|
100
103
|
|
104
|
+
sig { returns(::Pathname) }
|
105
|
+
def public_directory; end
|
106
|
+
|
107
|
+
sig { returns(T::Array[::ParsePackwerk::Violation]) }
|
108
|
+
def violations; end
|
109
|
+
|
101
110
|
sig { returns(::Pathname) }
|
102
111
|
def yml; end
|
103
112
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: modularization_statistics
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.39.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gusto Engineers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: code_teams
|
@@ -194,7 +194,7 @@ files:
|
|
194
194
|
- sorbet/rbi/gems/dogapi@1.45.0.rbi
|
195
195
|
- sorbet/rbi/gems/manual.rbi
|
196
196
|
- sorbet/rbi/gems/package_protections@1.4.0.rbi
|
197
|
-
- sorbet/rbi/gems/parse_packwerk@0.
|
197
|
+
- sorbet/rbi/gems/parse_packwerk@0.14.0.rbi
|
198
198
|
- sorbet/rbi/gems/rspec@3.10.0.rbi
|
199
199
|
- sorbet/rbi/todo.rbi
|
200
200
|
homepage: https://github.com/rubyatscale/modularization_statistics
|
@@ -220,7 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
220
220
|
- !ruby/object:Gem::Version
|
221
221
|
version: '0'
|
222
222
|
requirements: []
|
223
|
-
rubygems_version: 3.
|
223
|
+
rubygems_version: 3.1.6
|
224
224
|
signing_key:
|
225
225
|
specification_version: 4
|
226
226
|
summary: A gem to collect statistics about modularization progress in a Rails application
|