modularization_statistics 1.36.0 → 1.38.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/datadog_reporter.rb +1 -0
- data/lib/modularization_statistics/private/metrics/packages.rb +9 -10
- data/lib/modularization_statistics/private/metrics/packages_by_team.rb +11 -11
- data/lib/modularization_statistics/private/metrics/protection_usage.rb +44 -3
- data/lib/modularization_statistics/private/metrics/rubocop_protections_exclusions.rb +37 -0
- data/sorbet/config +1 -0
- data/sorbet/rbi/gems/{parse_packwerk@0.12.0.rbi → parse_packwerk@0.14.0.rbi} +10 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ab5b40d2d974d45aae986a4a0418e8d50984c4d4d39e963815dfd5fb875efc6
|
4
|
+
data.tar.gz: 91a571c247677445e408cd8aa0d2cc0a4adc2e5daed55ce4a6a8cc2e69429b81
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3454d7e5f523f3d6030e1b1789fa1d0b363fbcc6c7069170797c4efd4256a463a77d43dbfa94da1fce24b13283592eb016f7923591b28406038ed9e44fd0eb5
|
7
|
+
data.tar.gz: 5718efcffc45331f23b185f8b7565301cd35d8c1735eca4e9723b3086725ec30b7b6c399c16fdcc0788795fa4d39654c7bfa8348ec8b6188d3522584e55de147
|
@@ -6,6 +6,7 @@ require 'modularization_statistics/private/metrics'
|
|
6
6
|
require 'modularization_statistics/private/metrics/files'
|
7
7
|
require 'modularization_statistics/private/metrics/public_usage'
|
8
8
|
require 'modularization_statistics/private/metrics/protection_usage'
|
9
|
+
require 'modularization_statistics/private/metrics/rubocop_protections_exclusions'
|
9
10
|
require 'modularization_statistics/private/metrics/packages'
|
10
11
|
require 'modularization_statistics/private/metrics/packages_by_team'
|
11
12
|
require 'modularization_statistics/private/metrics/nested_packs'
|
@@ -17,35 +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',
|
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)
|
37
37
|
all_metrics << GaugeMetric.for('all_packages.package_based_file_ownership.count', packages.count { |package| !package.metadata['owner'].nil? }, package_tags)
|
38
38
|
|
39
|
-
inbound_violations_by_package =
|
39
|
+
inbound_violations_by_package = packages.flat_map(&:violations).group_by(&:to_package_name)
|
40
40
|
|
41
|
-
|
42
|
-
package = protected_package.original_package
|
41
|
+
packages.each do |package|
|
43
42
|
package_tags = Metrics.tags_for_package(package, app_name)
|
44
43
|
|
45
44
|
#
|
46
45
|
# VIOLATIONS (implicit dependencies)
|
47
46
|
#
|
48
|
-
outbound_violations =
|
47
|
+
outbound_violations = package.violations
|
49
48
|
inbound_violations = inbound_violations_by_package[package.name] || []
|
50
49
|
all_dependency_violations = (outbound_violations + inbound_violations).select(&:dependency?)
|
51
50
|
all_privacy_violations = (outbound_violations + inbound_violations).select(&:privacy?)
|
@@ -61,7 +60,7 @@ module ModularizationStatistics
|
|
61
60
|
|
62
61
|
all_metrics += Metrics::PublicUsage.get_public_usage_metrics('by_package', [package], package_tags)
|
63
62
|
|
64
|
-
|
63
|
+
package.violations.group_by(&:to_package_name).each do |to_package_name, violations|
|
65
64
|
to_package = ParsePackwerk.find(to_package_name)
|
66
65
|
if to_package.nil?
|
67
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,7 +23,47 @@ 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
|
+
case violation_behavior
|
41
|
+
when PackageProtections::ViolationBehavior::FailOnAny
|
42
|
+
# There is not yet an implementation for `FailOnAny` for systems that don't use package protections
|
43
|
+
false
|
44
|
+
when PackageProtections::ViolationBehavior::FailNever
|
45
|
+
if protection.identifier == 'prevent_this_package_from_violating_its_stated_dependencies'
|
46
|
+
!p.original_package.enforces_dependencies?
|
47
|
+
elsif protection.identifier == 'prevent_other_packages_from_using_this_packages_internals'
|
48
|
+
!p.original_package.enforces_privacy?
|
49
|
+
else
|
50
|
+
# This is not applicable if you're not using package protections
|
51
|
+
true
|
52
|
+
end
|
53
|
+
when PackageProtections::ViolationBehavior::FailOnNew
|
54
|
+
if protection.identifier == 'prevent_this_package_from_violating_its_stated_dependencies'
|
55
|
+
p.original_package.enforces_dependencies?
|
56
|
+
elsif protection.identifier == 'prevent_other_packages_from_using_this_packages_internals'
|
57
|
+
p.original_package.enforces_privacy?
|
58
|
+
else
|
59
|
+
# This is not applicable if you're not using package protections
|
60
|
+
false
|
61
|
+
end
|
62
|
+
else
|
63
|
+
T.absurd(violation_behavior)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
26
67
|
GaugeMetric.for(metric_name, count_of_packages, package_tags)
|
27
68
|
end
|
28
69
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module ModularizationStatistics
|
5
|
+
module Private
|
6
|
+
module Metrics
|
7
|
+
class RubocopProtectionsExclusions
|
8
|
+
extend T::Sig
|
9
|
+
|
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
|
+
|
14
|
+
rubocop_based_package_protections = T.cast(PackageProtections.all.select { |p| p.is_a?(PackageProtections::RubocopProtectionInterface) }, T::Array[PackageProtections::RubocopProtectionInterface])
|
15
|
+
rubocop_based_package_protections.flat_map do |rubocop_based_package_protection|
|
16
|
+
metric_name = "#{prefix}.#{rubocop_based_package_protection.identifier}.rubocop_exclusions.count"
|
17
|
+
all_exclusions_count = ParsePackwerk.all.sum { |package| exclude_count_for_package_and_protection(package, rubocop_based_package_protection)}
|
18
|
+
GaugeMetric.for(metric_name, all_exclusions_count, package_tags)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# TODO: `rubocop-packs` may want to expose API for this
|
23
|
+
sig { params(package: ParsePackwerk::Package, protection: PackageProtections::RubocopProtectionInterface).returns(Integer) }
|
24
|
+
def self.exclude_count_for_package_and_protection(package, protection)
|
25
|
+
rubocop_todo = package.directory.join('.rubocop_todo.yml')
|
26
|
+
if rubocop_todo.exist?
|
27
|
+
loaded_rubocop_todo = YAML.load_file(rubocop_todo)
|
28
|
+
cop_config = loaded_rubocop_todo.fetch(protection.cop_name, {})
|
29
|
+
cop_config.fetch('Exclude', []).count
|
30
|
+
else
|
31
|
+
0
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/sorbet/config
CHANGED
@@ -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.38.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-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: code_teams
|
@@ -184,6 +184,7 @@ files:
|
|
184
184
|
- lib/modularization_statistics/private/metrics/packages_by_team.rb
|
185
185
|
- lib/modularization_statistics/private/metrics/protection_usage.rb
|
186
186
|
- lib/modularization_statistics/private/metrics/public_usage.rb
|
187
|
+
- lib/modularization_statistics/private/metrics/rubocop_protections_exclusions.rb
|
187
188
|
- lib/modularization_statistics/private/source_code_file.rb
|
188
189
|
- lib/modularization_statistics/tag.rb
|
189
190
|
- lib/modularization_statistics/tags.rb
|
@@ -193,7 +194,7 @@ files:
|
|
193
194
|
- sorbet/rbi/gems/dogapi@1.45.0.rbi
|
194
195
|
- sorbet/rbi/gems/manual.rbi
|
195
196
|
- sorbet/rbi/gems/package_protections@1.4.0.rbi
|
196
|
-
- sorbet/rbi/gems/parse_packwerk@0.
|
197
|
+
- sorbet/rbi/gems/parse_packwerk@0.14.0.rbi
|
197
198
|
- sorbet/rbi/gems/rspec@3.10.0.rbi
|
198
199
|
- sorbet/rbi/todo.rbi
|
199
200
|
homepage: https://github.com/rubyatscale/modularization_statistics
|
@@ -219,7 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
219
220
|
- !ruby/object:Gem::Version
|
220
221
|
version: '0'
|
221
222
|
requirements: []
|
222
|
-
rubygems_version: 3.
|
223
|
+
rubygems_version: 3.1.6
|
223
224
|
signing_key:
|
224
225
|
specification_version: 4
|
225
226
|
summary: A gem to collect statistics about modularization progress in a Rails application
|