modularization_statistics 1.34.0 → 1.35.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/modularization_statistics/private/datadog_reporter.rb +13 -278
- data/lib/modularization_statistics/private/metrics/files.rb +47 -0
- data/lib/modularization_statistics/private/metrics/nested_packs.rb +116 -0
- data/lib/modularization_statistics/private/metrics/packages.rb +109 -0
- data/lib/modularization_statistics/private/metrics/packages_by_team.rb +73 -0
- data/lib/modularization_statistics/private/metrics/protection_usage.rb +33 -0
- data/lib/modularization_statistics/private/metrics/public_usage.rb +36 -0
- data/lib/modularization_statistics/private/metrics.rb +49 -0
- data/sorbet/rbi/gems/{package_protections@0.64.0.rbi → package_protections@1.4.0.rbi} +56 -107
- data/sorbet/rbi/gems/{parse_packwerk@0.10.0.rbi → parse_packwerk@0.12.0.rbi} +6 -0
- metadata +11 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 75ff0dc32b62d7a71ef4670809d77dfd7bac173f95529de171fc22f90f93ba41
|
4
|
+
data.tar.gz: c453e59ffd54ee91e1d535ca55767506f82cc5f43db8418423ed44e80b646c3c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8087b6b892da279242b6e8e40daf3925ccc77e50d6f640712d9e1bc1554ef770785bf84d9fd83f77ab56cbccf2432bccf453139886749665956c5f317cebc7d
|
7
|
+
data.tar.gz: 35d695c1e0f5fd13a5d9e5f97fc2a5f582cfbe1536a479c34f49c81f4e50e2ea0ce4ce5b12c0aee80b11d9a2f998609322801787df7ccbf781cc3e985a064f23
|
@@ -2,14 +2,19 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'dogapi'
|
5
|
+
require 'modularization_statistics/private/metrics'
|
6
|
+
require 'modularization_statistics/private/metrics/files'
|
7
|
+
require 'modularization_statistics/private/metrics/public_usage'
|
8
|
+
require 'modularization_statistics/private/metrics/protection_usage'
|
9
|
+
require 'modularization_statistics/private/metrics/packages'
|
10
|
+
require 'modularization_statistics/private/metrics/packages_by_team'
|
11
|
+
require 'modularization_statistics/private/metrics/nested_packs'
|
5
12
|
|
6
13
|
module ModularizationStatistics
|
7
14
|
module Private
|
8
15
|
class DatadogReporter
|
9
16
|
extend T::Sig
|
10
17
|
|
11
|
-
UNKNOWN_OWNER = T.let('Unknown', String)
|
12
|
-
|
13
18
|
sig do
|
14
19
|
params(
|
15
20
|
source_code_files: T::Array[SourceCodeFile],
|
@@ -17,22 +22,14 @@ module ModularizationStatistics
|
|
17
22
|
).returns(T::Array[GaugeMetric])
|
18
23
|
end
|
19
24
|
def self.get_metrics(source_code_files:, app_name:)
|
20
|
-
all_metrics = T.let([], T::Array[GaugeMetric])
|
21
|
-
app_level_tag = Tag.for('app', app_name)
|
22
|
-
|
23
|
-
source_code_files.group_by { |file| file.team_owner&.name }.each do |team_name, files_for_team|
|
24
|
-
file_tags = tags_for_team(team_name) + [app_level_tag]
|
25
|
-
all_metrics += get_file_metrics('by_team', file_tags, files_for_team)
|
26
|
-
end
|
27
|
-
|
28
|
-
file_tags = [app_level_tag]
|
29
|
-
all_metrics += get_file_metrics('totals', file_tags, source_code_files)
|
30
|
-
|
31
25
|
packages = ParsePackwerk.all
|
32
|
-
all_metrics += get_package_metrics(packages, app_name)
|
33
|
-
all_metrics += get_package_metrics_by_team(packages, app_name)
|
34
26
|
|
35
|
-
|
27
|
+
[
|
28
|
+
*Metrics::Files.get_metrics(source_code_files, app_name),
|
29
|
+
*Metrics::Packages.get_package_metrics(packages, app_name),
|
30
|
+
*Metrics::PackagesByTeam.get_package_metrics_by_team(packages, app_name),
|
31
|
+
*Metrics::NestedPacks.get_nested_package_metrics(packages, app_name)
|
32
|
+
]
|
36
33
|
end
|
37
34
|
|
38
35
|
sig do
|
@@ -58,268 +55,6 @@ module ModularizationStatistics
|
|
58
55
|
end
|
59
56
|
end
|
60
57
|
end
|
61
|
-
|
62
|
-
sig { params(package: ParsePackwerk::Package, app_name: String).returns(T::Array[Tag]) }
|
63
|
-
def self.tags_for_package(package, app_name)
|
64
|
-
[
|
65
|
-
Tag.new(key: 'package', value: humanized_package_name(package.name)),
|
66
|
-
Tag.new(key: 'app', value: app_name),
|
67
|
-
*tags_for_team(CodeOwnership.for_package(package)&.name),
|
68
|
-
]
|
69
|
-
end
|
70
|
-
|
71
|
-
sig { params(team_name: T.nilable(String)).returns(T::Array[Tag]) }
|
72
|
-
def self.tags_for_team(team_name)
|
73
|
-
[Tag.for('team', team_name || UNKNOWN_OWNER)]
|
74
|
-
end
|
75
|
-
|
76
|
-
sig { params(team_name: T.nilable(String)).returns(T::Array[Tag]) }
|
77
|
-
def self.tags_for_to_team(team_name)
|
78
|
-
[Tag.for('to_team', team_name || UNKNOWN_OWNER)]
|
79
|
-
end
|
80
|
-
|
81
|
-
private_class_method :tags_for_package
|
82
|
-
|
83
|
-
sig do
|
84
|
-
params(
|
85
|
-
packages: T::Array[ParsePackwerk::Package],
|
86
|
-
app_name: String
|
87
|
-
).returns(T::Array[GaugeMetric])
|
88
|
-
end
|
89
|
-
def self.get_package_metrics(packages, app_name)
|
90
|
-
all_metrics = []
|
91
|
-
app_level_tag = Tag.for('app', app_name)
|
92
|
-
package_tags = T.let([app_level_tag], T::Array[Tag])
|
93
|
-
protected_packages = packages.map { |p| PackageProtections::ProtectedPackage.from(p) }
|
94
|
-
|
95
|
-
all_metrics << GaugeMetric.for('all_packages.count', packages.count, package_tags)
|
96
|
-
all_metrics << GaugeMetric.for('all_packages.dependencies.count', packages.sum { |package| package.dependencies.count }, package_tags)
|
97
|
-
all_metrics << GaugeMetric.for('all_packages.dependency_violations.count', protected_packages.sum { |package| file_count(package.violations.select(&:dependency?)) }, package_tags)
|
98
|
-
all_metrics << GaugeMetric.for('all_packages.privacy_violations.count', protected_packages.sum { |package| file_count(package.violations.select(&:privacy?)) }, package_tags)
|
99
|
-
all_metrics << GaugeMetric.for('all_packages.enforcing_dependencies.count', packages.count(&:enforces_dependencies?), package_tags)
|
100
|
-
all_metrics << GaugeMetric.for('all_packages.enforcing_privacy.count', packages.count(&:enforces_privacy?), package_tags)
|
101
|
-
|
102
|
-
all_metrics << GaugeMetric.for('all_packages.notify_on_package_yml_changes.count', packages.count { |p| p.metadata['notify_on_package_yml_changes'] }, package_tags)
|
103
|
-
all_metrics << GaugeMetric.for('all_packages.notify_on_new_violations.count', packages.count { |p| p.metadata['notify_on_new_violations'] }, package_tags)
|
104
|
-
|
105
|
-
all_metrics << GaugeMetric.for('all_packages.with_violations.count', protected_packages.count { |package| package.violations.any? }, package_tags)
|
106
|
-
all_metrics += self.get_public_usage_metrics('all_packages', packages, package_tags)
|
107
|
-
all_metrics << GaugeMetric.for('all_packages.has_readme.count', packages.count { |package| has_readme?(package) }, package_tags)
|
108
|
-
|
109
|
-
all_metrics += self.get_protections_metrics('all_packages', protected_packages, package_tags)
|
110
|
-
all_metrics << GaugeMetric.for('all_packages.package_based_file_ownership.count', packages.count { |package| !package.metadata['owner'].nil? }, package_tags)
|
111
|
-
|
112
|
-
inbound_violations_by_package = protected_packages.flat_map(&:violations).group_by(&:to_package_name)
|
113
|
-
|
114
|
-
protected_packages.each do |protected_package|
|
115
|
-
package = protected_package.original_package
|
116
|
-
package_tags = tags_for_package(package, app_name)
|
117
|
-
|
118
|
-
#
|
119
|
-
# VIOLATIONS (implicit dependencies)
|
120
|
-
#
|
121
|
-
outbound_violations = protected_package.violations
|
122
|
-
inbound_violations = inbound_violations_by_package[package.name] || []
|
123
|
-
all_dependency_violations = (outbound_violations + inbound_violations).select(&:dependency?)
|
124
|
-
all_privacy_violations = (outbound_violations + inbound_violations).select(&:privacy?)
|
125
|
-
|
126
|
-
all_metrics << GaugeMetric.for('by_package.dependency_violations.count', file_count(all_dependency_violations), package_tags)
|
127
|
-
all_metrics << GaugeMetric.for('by_package.privacy_violations.count', file_count(all_privacy_violations), package_tags)
|
128
|
-
|
129
|
-
all_metrics << GaugeMetric.for('by_package.outbound_dependency_violations.count', file_count(outbound_violations.select(&:dependency?)), package_tags)
|
130
|
-
all_metrics << GaugeMetric.for('by_package.inbound_dependency_violations.count', file_count(inbound_violations.select(&:dependency?)), package_tags)
|
131
|
-
|
132
|
-
all_metrics << GaugeMetric.for('by_package.outbound_privacy_violations.count', file_count(outbound_violations.select(&:privacy?)), package_tags)
|
133
|
-
all_metrics << GaugeMetric.for('by_package.inbound_privacy_violations.count', file_count(inbound_violations.select(&:privacy?)), package_tags)
|
134
|
-
|
135
|
-
all_metrics += self.get_public_usage_metrics('by_package', [package], package_tags)
|
136
|
-
|
137
|
-
protected_package.violations.group_by(&:to_package_name).each do |to_package_name, violations|
|
138
|
-
to_package = ParsePackwerk.find(to_package_name)
|
139
|
-
if to_package.nil?
|
140
|
-
raise StandardError, "Could not find matching package #{to_package_name}"
|
141
|
-
end
|
142
|
-
|
143
|
-
tags = package_tags + [Tag.for('to_package', humanized_package_name(to_package_name))] + tags_for_to_team(CodeOwnership.for_package(to_package)&.name)
|
144
|
-
all_metrics << GaugeMetric.for('by_package.outbound_dependency_violations.per_package.count', file_count(violations.select(&:dependency?)), tags)
|
145
|
-
all_metrics << GaugeMetric.for('by_package.outbound_privacy_violations.per_package.count', file_count(violations.select(&:privacy?)), tags)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
inbound_explicit_dependency_by_package = {}
|
150
|
-
packages.each do |package|
|
151
|
-
package.dependencies.each do |explicit_dependency|
|
152
|
-
inbound_explicit_dependency_by_package[explicit_dependency] ||= []
|
153
|
-
inbound_explicit_dependency_by_package[explicit_dependency] << package.name
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
packages.each do |package| # rubocop:disable Style/CombinableLoops
|
158
|
-
package_tags = tags_for_package(package, app_name)
|
159
|
-
|
160
|
-
#
|
161
|
-
# EXPLICIT DEPENDENCIES
|
162
|
-
#
|
163
|
-
package.dependencies.each do |explicit_dependency|
|
164
|
-
to_package = ParsePackwerk.find(explicit_dependency)
|
165
|
-
if to_package.nil?
|
166
|
-
raise StandardError, "Could not find matching package #{explicit_dependency}"
|
167
|
-
end
|
168
|
-
|
169
|
-
tags = package_tags + [Tag.for('to_package', humanized_package_name(explicit_dependency))] + tags_for_to_team(CodeOwnership.for_package(to_package)&.name)
|
170
|
-
all_metrics << GaugeMetric.for('by_package.outbound_explicit_dependencies.per_package.count', 1, tags)
|
171
|
-
end
|
172
|
-
|
173
|
-
all_metrics << GaugeMetric.for('by_package.outbound_explicit_dependencies.count', package.dependencies.count, package_tags)
|
174
|
-
all_metrics << GaugeMetric.for('by_package.inbound_explicit_dependencies.count', inbound_explicit_dependency_by_package[package.name]&.count || 0, package_tags)
|
175
|
-
end
|
176
|
-
|
177
|
-
all_metrics
|
178
|
-
end
|
179
|
-
|
180
|
-
sig do
|
181
|
-
params(
|
182
|
-
all_packages: T::Array[ParsePackwerk::Package],
|
183
|
-
app_name: String
|
184
|
-
).returns(T::Array[GaugeMetric])
|
185
|
-
end
|
186
|
-
def self.get_package_metrics_by_team(all_packages, app_name)
|
187
|
-
all_metrics = T.let([], T::Array[GaugeMetric])
|
188
|
-
app_level_tag = Tag.for('app', app_name)
|
189
|
-
all_protected_packages = all_packages.map { |p| PackageProtections::ProtectedPackage.from(p) }
|
190
|
-
all_protected_packages.group_by { |protected_package| CodeOwnership.for_package(protected_package.original_package)&.name }.each do |team_name, protected_packages_by_team|
|
191
|
-
# We look at `all_packages` because we care about ALL inbound violations across all teams
|
192
|
-
inbound_violations_by_package = all_protected_packages.flat_map(&:violations).group_by(&:to_package_name)
|
193
|
-
|
194
|
-
team_tags = tags_for_team(team_name) + [app_level_tag]
|
195
|
-
all_metrics << GaugeMetric.for('by_team.all_packages.count', protected_packages_by_team.count, team_tags)
|
196
|
-
all_metrics += self.get_protections_metrics('by_team', protected_packages_by_team, team_tags)
|
197
|
-
all_metrics += self.get_public_usage_metrics('by_team', protected_packages_by_team.map(&:original_package), team_tags)
|
198
|
-
|
199
|
-
all_metrics << GaugeMetric.for('by_team.notify_on_package_yml_changes.count', protected_packages_by_team.count { |p| p.metadata['notify_on_package_yml_changes'] }, team_tags)
|
200
|
-
all_metrics << GaugeMetric.for('by_team.notify_on_new_violations.count', protected_packages_by_team.count { |p| p.metadata['notify_on_new_violations'] }, team_tags)
|
201
|
-
|
202
|
-
#
|
203
|
-
# VIOLATIONS (implicit dependencies)
|
204
|
-
#
|
205
|
-
outbound_violations = protected_packages_by_team.flat_map(&:violations)
|
206
|
-
# Here we only look at packages_by_team because we only care about inbound violations onto packages for this team
|
207
|
-
inbound_violations = protected_packages_by_team.flat_map { |package| inbound_violations_by_package[package.name] || [] }
|
208
|
-
all_dependency_violations = (outbound_violations + inbound_violations).select(&:dependency?)
|
209
|
-
all_privacy_violations = (outbound_violations + inbound_violations).select(&:privacy?)
|
210
|
-
|
211
|
-
all_metrics << GaugeMetric.for('by_team.dependency_violations.count', file_count(all_dependency_violations), team_tags)
|
212
|
-
all_metrics << GaugeMetric.for('by_team.privacy_violations.count', file_count(all_privacy_violations), team_tags)
|
213
|
-
|
214
|
-
all_metrics << GaugeMetric.for('by_team.outbound_dependency_violations.count', file_count(outbound_violations.select(&:dependency?)), team_tags)
|
215
|
-
all_metrics << GaugeMetric.for('by_team.inbound_dependency_violations.count', file_count(inbound_violations.select(&:dependency?)), team_tags)
|
216
|
-
|
217
|
-
all_metrics << GaugeMetric.for('by_team.outbound_privacy_violations.count', file_count(outbound_violations.select(&:privacy?)), team_tags)
|
218
|
-
all_metrics << GaugeMetric.for('by_team.inbound_privacy_violations.count', file_count(inbound_violations.select(&:privacy?)), team_tags)
|
219
|
-
|
220
|
-
all_metrics << GaugeMetric.for('by_team.has_readme.count', protected_packages_by_team.count { |protected_package| has_readme?(protected_package.original_package) }, team_tags)
|
221
|
-
|
222
|
-
grouped_outbound_violations = outbound_violations.group_by do |violation|
|
223
|
-
to_package = ParsePackwerk.find(violation.to_package_name)
|
224
|
-
if to_package.nil?
|
225
|
-
raise StandardError, "Could not find matching package #{violation.to_package_name}"
|
226
|
-
end
|
227
|
-
|
228
|
-
CodeOwnership.for_package(to_package)&.name
|
229
|
-
end
|
230
|
-
|
231
|
-
grouped_outbound_violations.each do |to_team_name, violations|
|
232
|
-
tags = team_tags + tags_for_to_team(to_team_name)
|
233
|
-
all_metrics << GaugeMetric.for('by_team.outbound_dependency_violations.per_team.count', file_count(violations.select(&:dependency?)), tags)
|
234
|
-
all_metrics << GaugeMetric.for('by_team.outbound_privacy_violations.per_team.count', file_count(violations.select(&:privacy?)), tags)
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
all_metrics
|
239
|
-
end
|
240
|
-
|
241
|
-
private_class_method :get_package_metrics
|
242
|
-
|
243
|
-
sig do
|
244
|
-
params(
|
245
|
-
metric_name_suffix: String,
|
246
|
-
tags: T::Array[Tag],
|
247
|
-
files: T::Array[SourceCodeFile]
|
248
|
-
).returns(T::Array[GaugeMetric])
|
249
|
-
end
|
250
|
-
def self.get_file_metrics(metric_name_suffix, tags, files)
|
251
|
-
[
|
252
|
-
GaugeMetric.for("component_files.#{metric_name_suffix}", files.count(&:componentized_file?), tags),
|
253
|
-
GaugeMetric.for("packaged_files.#{metric_name_suffix}", files.count(&:packaged_file?), tags),
|
254
|
-
GaugeMetric.for("all_files.#{metric_name_suffix}", files.count, tags),
|
255
|
-
]
|
256
|
-
end
|
257
|
-
|
258
|
-
private_class_method :get_file_metrics
|
259
|
-
|
260
|
-
sig { params(prefix: String, protected_packages: T::Array[PackageProtections::ProtectedPackage], package_tags: T::Array[Tag]).returns(T::Array[GaugeMetric]) }
|
261
|
-
def self.get_protections_metrics(prefix, protected_packages, package_tags)
|
262
|
-
PackageProtections.all.flat_map do |protection|
|
263
|
-
PackageProtections::ViolationBehavior.each_value.map do |violation_behavior|
|
264
|
-
# https://github.com/Gusto/package_protections/pull/42 changed the public API of these violation behaviors.
|
265
|
-
# To preserve our ability to understand historical trends, we map to the old values.
|
266
|
-
# This allows our dashboards to continue to operate as expected.
|
267
|
-
# Note if we ever open source mod stats, we should probably inject this behavior so that new clients can see the new keys in their metrics.
|
268
|
-
violation_behavior_map = {
|
269
|
-
PackageProtections::ViolationBehavior::FailOnAny => 'fail_the_build_on_any_instances',
|
270
|
-
PackageProtections::ViolationBehavior::FailNever => 'no',
|
271
|
-
PackageProtections::ViolationBehavior::FailOnNew => 'fail_the_build_if_new_instances_appear',
|
272
|
-
}
|
273
|
-
violation_behavior_name = violation_behavior_map[violation_behavior]
|
274
|
-
metric_name = "#{prefix}.#{protection.identifier}.#{violation_behavior_name}.count"
|
275
|
-
count_of_packages = protected_packages.count { |p| p.violation_behavior_for(protection.identifier) == violation_behavior }
|
276
|
-
GaugeMetric.for(metric_name, count_of_packages, package_tags)
|
277
|
-
end
|
278
|
-
end
|
279
|
-
end
|
280
|
-
|
281
|
-
sig { params(prefix: String, packages: T::Array[ParsePackwerk::Package], package_tags: T::Array[Tag]).returns(T::Array[GaugeMetric]) }
|
282
|
-
def self.get_public_usage_metrics(prefix, packages, package_tags)
|
283
|
-
packages_except_for_root = packages.reject { |package| package.name == ParsePackwerk::ROOT_PACKAGE_NAME }
|
284
|
-
all_files = packages_except_for_root.flat_map do |package|
|
285
|
-
package.directory.glob('**/**.rb')
|
286
|
-
end
|
287
|
-
|
288
|
-
all_public_files = T.let([], T::Array[Pathname])
|
289
|
-
is_using_public_directory = 0
|
290
|
-
packages_except_for_root.each do |package|
|
291
|
-
public_files = package.directory.glob('app/public/**/**.rb')
|
292
|
-
all_public_files += public_files
|
293
|
-
is_using_public_directory += 1 if public_files.any?
|
294
|
-
end
|
295
|
-
|
296
|
-
# In Datadog, can divide public files by all files to get the ratio.
|
297
|
-
# This is not a metric that we are targeting -- its for observability and reflection only.
|
298
|
-
[
|
299
|
-
GaugeMetric.for("#{prefix}.all_files.count", all_files.count, package_tags),
|
300
|
-
GaugeMetric.for("#{prefix}.public_files.count", all_public_files.count, package_tags),
|
301
|
-
GaugeMetric.for("#{prefix}.using_public_directory.count", is_using_public_directory, package_tags),
|
302
|
-
]
|
303
|
-
end
|
304
|
-
|
305
|
-
sig { params(package: ParsePackwerk::Package).returns(T::Boolean) }
|
306
|
-
def self.has_readme?(package)
|
307
|
-
package.directory.join('README.md').exist?
|
308
|
-
end
|
309
|
-
|
310
|
-
sig { params(violations: T::Array[ParsePackwerk::Violation]).returns(Integer) }
|
311
|
-
def self.file_count(violations)
|
312
|
-
violations.sum { |v| v.files.count }
|
313
|
-
end
|
314
|
-
|
315
|
-
sig { params(name: String).returns(String) }
|
316
|
-
def self.humanized_package_name(name)
|
317
|
-
if name == ParsePackwerk::ROOT_PACKAGE_NAME
|
318
|
-
'root'
|
319
|
-
else
|
320
|
-
name
|
321
|
-
end
|
322
|
-
end
|
323
58
|
end
|
324
59
|
end
|
325
60
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module ModularizationStatistics
|
5
|
+
module Private
|
6
|
+
module Metrics
|
7
|
+
class Files
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig do
|
11
|
+
params(
|
12
|
+
source_code_files: T::Array[SourceCodeFile],
|
13
|
+
app_name: String
|
14
|
+
).returns(T::Array[GaugeMetric])
|
15
|
+
end
|
16
|
+
def self.get_metrics(source_code_files, app_name)
|
17
|
+
all_metrics = T.let([], T::Array[GaugeMetric])
|
18
|
+
app_level_tag = Tag.for('app', app_name)
|
19
|
+
|
20
|
+
source_code_files.group_by { |file| file.team_owner&.name }.each do |team_name, files_for_team|
|
21
|
+
file_tags = Metrics.tags_for_team(team_name) + [app_level_tag]
|
22
|
+
all_metrics += get_file_metrics('by_team', file_tags, files_for_team)
|
23
|
+
end
|
24
|
+
|
25
|
+
file_tags = [app_level_tag]
|
26
|
+
all_metrics += get_file_metrics('totals', file_tags, source_code_files)
|
27
|
+
all_metrics
|
28
|
+
end
|
29
|
+
|
30
|
+
sig do
|
31
|
+
params(
|
32
|
+
metric_name_suffix: String,
|
33
|
+
tags: T::Array[Tag],
|
34
|
+
files: T::Array[SourceCodeFile]
|
35
|
+
).returns(T::Array[GaugeMetric])
|
36
|
+
end
|
37
|
+
def self.get_file_metrics(metric_name_suffix, tags, files)
|
38
|
+
[
|
39
|
+
GaugeMetric.for("component_files.#{metric_name_suffix}", files.count(&:componentized_file?), tags),
|
40
|
+
GaugeMetric.for("packaged_files.#{metric_name_suffix}", files.count(&:packaged_file?), tags),
|
41
|
+
GaugeMetric.for("all_files.#{metric_name_suffix}", files.count, tags),
|
42
|
+
]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module ModularizationStatistics
|
5
|
+
module Private
|
6
|
+
module Metrics
|
7
|
+
class NestedPacks
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
class PackGroup < T::Struct
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
const :name, String
|
14
|
+
const :root, ParsePackwerk::Package
|
15
|
+
const :members, T::Array[ParsePackwerk::Package]
|
16
|
+
|
17
|
+
sig { params(packages: T::Array[ParsePackwerk::Package]).returns(T::Array[PackGroup]) }
|
18
|
+
def self.all_from(packages)
|
19
|
+
packs_by_group = {}
|
20
|
+
|
21
|
+
packages.each do |package|
|
22
|
+
# For a child pack, package.directory is `packs/fruits/apples` (i.e. the directory of the package.yml file).
|
23
|
+
# The package.directory.dirname is therefore `packs/fruits`.
|
24
|
+
# For a standalone pack, package.directory.dirname is `packs`
|
25
|
+
# A pack with no parent is in a pack group of its own name
|
26
|
+
root = ParsePackwerk.find(package.directory.dirname.to_s) || package
|
27
|
+
# Mark the parent pack and child pack as being in the pack group of the parent
|
28
|
+
packs_by_group[root.name] ||= { root: root, members: [] }
|
29
|
+
packs_by_group[root.name][:members] << package
|
30
|
+
end
|
31
|
+
|
32
|
+
packs_by_group.map do |name, pack_data|
|
33
|
+
PackGroup.new(
|
34
|
+
name: name,
|
35
|
+
root: pack_data[:root],
|
36
|
+
members: pack_data[:members],
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
sig { returns(Integer) }
|
42
|
+
def children_pack_count
|
43
|
+
members.count do |package|
|
44
|
+
package.name != root.name
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
sig { returns(T::Boolean) }
|
49
|
+
def has_parent?
|
50
|
+
children_pack_count > 0
|
51
|
+
end
|
52
|
+
|
53
|
+
sig { returns(T::Array[ParsePackwerk::Violation]) }
|
54
|
+
def cross_group_violations
|
55
|
+
all_violations = members.flat_map do |member|
|
56
|
+
ParsePackwerk::DeprecatedReferences.for(member).violations
|
57
|
+
end
|
58
|
+
|
59
|
+
all_violations.select do |violation|
|
60
|
+
!members.map(&:name).include?(violation.to_package_name)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
sig do
|
66
|
+
params(
|
67
|
+
packages: T::Array[ParsePackwerk::Package],
|
68
|
+
app_name: String
|
69
|
+
).returns(T::Array[GaugeMetric])
|
70
|
+
end
|
71
|
+
def self.get_nested_package_metrics(packages, app_name)
|
72
|
+
all_metrics = []
|
73
|
+
app_level_tag = Tag.for('app', app_name)
|
74
|
+
package_tags = T.let([app_level_tag], T::Array[Tag])
|
75
|
+
|
76
|
+
pack_groups = PackGroup.all_from(packages)
|
77
|
+
all_pack_groups_count = pack_groups.count
|
78
|
+
child_pack_count = pack_groups.sum(&:children_pack_count)
|
79
|
+
parent_pack_count = pack_groups.count(&:has_parent?)
|
80
|
+
all_cross_pack_group_violations = pack_groups.flat_map(&:cross_group_violations)
|
81
|
+
|
82
|
+
all_metrics << GaugeMetric.for('all_pack_groups.count', all_pack_groups_count, package_tags)
|
83
|
+
all_metrics << GaugeMetric.for('child_packs.count', child_pack_count, package_tags)
|
84
|
+
all_metrics << GaugeMetric.for('parent_packs.count', parent_pack_count, package_tags)
|
85
|
+
all_metrics << GaugeMetric.for('all_pack_groups.privacy_violations.count', Metrics.file_count(all_cross_pack_group_violations.select(&:privacy?)), package_tags)
|
86
|
+
all_metrics << GaugeMetric.for('all_pack_groups.dependency_violations.count', Metrics.file_count(all_cross_pack_group_violations.select(&:dependency?)), package_tags)
|
87
|
+
|
88
|
+
packs_by_group = {}
|
89
|
+
pack_groups.each do |pack_group|
|
90
|
+
pack_group.members.each do |member|
|
91
|
+
packs_by_group[member.name] = pack_group.name
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
pack_groups.each do |from_pack_group|
|
96
|
+
violations_by_to_pack_group = from_pack_group.cross_group_violations.group_by do |violation|
|
97
|
+
packs_by_group[violation.to_package_name]
|
98
|
+
end
|
99
|
+
violations_by_to_pack_group.each do |to_pack_group_name, violations|
|
100
|
+
tags = [
|
101
|
+
*package_tags,
|
102
|
+
Tag.for('pack_group', Metrics.humanized_package_name(from_pack_group.name)),
|
103
|
+
Tag.for('to_pack_group', Metrics.humanized_package_name(to_pack_group_name)),
|
104
|
+
]
|
105
|
+
|
106
|
+
all_metrics << GaugeMetric.for('by_pack_group.outbound_dependency_violations.per_pack_group.count', Metrics.file_count(violations.select(&:dependency?)), tags)
|
107
|
+
all_metrics << GaugeMetric.for('by_pack_group.outbound_privacy_violations.per_pack_group.count', Metrics.file_count(violations.select(&:privacy?)), tags)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
all_metrics
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module ModularizationStatistics
|
5
|
+
module Private
|
6
|
+
module Metrics
|
7
|
+
class Packages
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig do
|
11
|
+
params(
|
12
|
+
packages: T::Array[ParsePackwerk::Package],
|
13
|
+
app_name: String
|
14
|
+
).returns(T::Array[GaugeMetric])
|
15
|
+
end
|
16
|
+
def self.get_package_metrics(packages, app_name)
|
17
|
+
all_metrics = []
|
18
|
+
app_level_tag = Tag.for('app', app_name)
|
19
|
+
package_tags = T.let([app_level_tag], T::Array[Tag])
|
20
|
+
protected_packages = packages.map { |p| PackageProtections::ProtectedPackage.from(p) }
|
21
|
+
|
22
|
+
all_metrics << GaugeMetric.for('all_packages.count', packages.count, package_tags)
|
23
|
+
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', protected_packages.sum { |package| Metrics.file_count(package.violations.select(&:dependency?)) }, package_tags)
|
25
|
+
all_metrics << GaugeMetric.for('all_packages.privacy_violations.count', protected_packages.sum { |package| Metrics.file_count(package.violations.select(&:privacy?)) }, package_tags)
|
26
|
+
all_metrics << GaugeMetric.for('all_packages.enforcing_dependencies.count', packages.count(&:enforces_dependencies?), package_tags)
|
27
|
+
all_metrics << GaugeMetric.for('all_packages.enforcing_privacy.count', packages.count(&:enforces_privacy?), package_tags)
|
28
|
+
|
29
|
+
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
|
+
all_metrics << GaugeMetric.for('all_packages.notify_on_new_violations.count', packages.count { |p| p.metadata['notify_on_new_violations'] }, package_tags)
|
31
|
+
|
32
|
+
all_metrics << GaugeMetric.for('all_packages.with_violations.count', protected_packages.count { |package| package.violations.any? }, package_tags)
|
33
|
+
all_metrics += Metrics::PublicUsage.get_public_usage_metrics('all_packages', packages, package_tags)
|
34
|
+
all_metrics << GaugeMetric.for('all_packages.has_readme.count', packages.count { |package| Metrics.has_readme?(package) }, package_tags)
|
35
|
+
|
36
|
+
all_metrics += Metrics::ProtectionUsage.get_protections_metrics('all_packages', protected_packages, package_tags)
|
37
|
+
all_metrics << GaugeMetric.for('all_packages.package_based_file_ownership.count', packages.count { |package| !package.metadata['owner'].nil? }, package_tags)
|
38
|
+
|
39
|
+
inbound_violations_by_package = protected_packages.flat_map(&:violations).group_by(&:to_package_name)
|
40
|
+
|
41
|
+
protected_packages.each do |protected_package|
|
42
|
+
package = protected_package.original_package
|
43
|
+
package_tags = Metrics.tags_for_package(package, app_name)
|
44
|
+
|
45
|
+
#
|
46
|
+
# VIOLATIONS (implicit dependencies)
|
47
|
+
#
|
48
|
+
outbound_violations = protected_package.violations
|
49
|
+
inbound_violations = inbound_violations_by_package[package.name] || []
|
50
|
+
all_dependency_violations = (outbound_violations + inbound_violations).select(&:dependency?)
|
51
|
+
all_privacy_violations = (outbound_violations + inbound_violations).select(&:privacy?)
|
52
|
+
|
53
|
+
all_metrics << GaugeMetric.for('by_package.dependency_violations.count', Metrics.file_count(all_dependency_violations), package_tags)
|
54
|
+
all_metrics << GaugeMetric.for('by_package.privacy_violations.count', Metrics.file_count(all_privacy_violations), package_tags)
|
55
|
+
|
56
|
+
all_metrics << GaugeMetric.for('by_package.outbound_dependency_violations.count', Metrics.file_count(outbound_violations.select(&:dependency?)), package_tags)
|
57
|
+
all_metrics << GaugeMetric.for('by_package.inbound_dependency_violations.count', Metrics.file_count(inbound_violations.select(&:dependency?)), package_tags)
|
58
|
+
|
59
|
+
all_metrics << GaugeMetric.for('by_package.outbound_privacy_violations.count', Metrics.file_count(outbound_violations.select(&:privacy?)), package_tags)
|
60
|
+
all_metrics << GaugeMetric.for('by_package.inbound_privacy_violations.count', Metrics.file_count(inbound_violations.select(&:privacy?)), package_tags)
|
61
|
+
|
62
|
+
all_metrics += Metrics::PublicUsage.get_public_usage_metrics('by_package', [package], package_tags)
|
63
|
+
|
64
|
+
protected_package.violations.group_by(&:to_package_name).each do |to_package_name, violations|
|
65
|
+
to_package = ParsePackwerk.find(to_package_name)
|
66
|
+
if to_package.nil?
|
67
|
+
raise StandardError, "Could not find matching package #{to_package_name}"
|
68
|
+
end
|
69
|
+
|
70
|
+
tags = package_tags + [Tag.for('to_package', Metrics.humanized_package_name(to_package_name))] + Metrics.tags_for_to_team(CodeOwnership.for_package(to_package)&.name)
|
71
|
+
all_metrics << GaugeMetric.for('by_package.outbound_dependency_violations.per_package.count', Metrics.file_count(violations.select(&:dependency?)), tags)
|
72
|
+
all_metrics << GaugeMetric.for('by_package.outbound_privacy_violations.per_package.count', Metrics.file_count(violations.select(&:privacy?)), tags)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
inbound_explicit_dependency_by_package = {}
|
77
|
+
packages.each do |package|
|
78
|
+
package.dependencies.each do |explicit_dependency|
|
79
|
+
inbound_explicit_dependency_by_package[explicit_dependency] ||= []
|
80
|
+
inbound_explicit_dependency_by_package[explicit_dependency] << package.name
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
packages.each do |package| # rubocop:disable Style/CombinableLoops
|
85
|
+
package_tags = Metrics.tags_for_package(package, app_name)
|
86
|
+
|
87
|
+
#
|
88
|
+
# EXPLICIT DEPENDENCIES
|
89
|
+
#
|
90
|
+
package.dependencies.each do |explicit_dependency|
|
91
|
+
to_package = ParsePackwerk.find(explicit_dependency)
|
92
|
+
if to_package.nil?
|
93
|
+
raise StandardError, "Could not find matching package #{explicit_dependency}"
|
94
|
+
end
|
95
|
+
|
96
|
+
tags = package_tags + [Tag.for('to_package', Metrics.humanized_package_name(explicit_dependency))] + Metrics.tags_for_to_team(CodeOwnership.for_package(to_package)&.name)
|
97
|
+
all_metrics << GaugeMetric.for('by_package.outbound_explicit_dependencies.per_package.count', 1, tags)
|
98
|
+
end
|
99
|
+
|
100
|
+
all_metrics << GaugeMetric.for('by_package.outbound_explicit_dependencies.count', package.dependencies.count, package_tags)
|
101
|
+
all_metrics << GaugeMetric.for('by_package.inbound_explicit_dependencies.count', inbound_explicit_dependency_by_package[package.name]&.count || 0, package_tags)
|
102
|
+
end
|
103
|
+
|
104
|
+
all_metrics
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module ModularizationStatistics
|
5
|
+
module Private
|
6
|
+
module Metrics
|
7
|
+
class PackagesByTeam
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig do
|
11
|
+
params(
|
12
|
+
all_packages: T::Array[ParsePackwerk::Package],
|
13
|
+
app_name: String
|
14
|
+
).returns(T::Array[GaugeMetric])
|
15
|
+
end
|
16
|
+
def self.get_package_metrics_by_team(all_packages, app_name)
|
17
|
+
all_metrics = T.let([], T::Array[GaugeMetric])
|
18
|
+
app_level_tag = Tag.for('app', app_name)
|
19
|
+
all_protected_packages = all_packages.map { |p| PackageProtections::ProtectedPackage.from(p) }
|
20
|
+
all_protected_packages.group_by { |protected_package| CodeOwnership.for_package(protected_package.original_package)&.name }.each do |team_name, protected_packages_by_team|
|
21
|
+
# We look at `all_packages` because we care about ALL inbound violations across all teams
|
22
|
+
inbound_violations_by_package = all_protected_packages.flat_map(&:violations).group_by(&:to_package_name)
|
23
|
+
|
24
|
+
team_tags = Metrics.tags_for_team(team_name) + [app_level_tag]
|
25
|
+
all_metrics << GaugeMetric.for('by_team.all_packages.count', protected_packages_by_team.count, team_tags)
|
26
|
+
all_metrics += Metrics::ProtectionUsage.get_protections_metrics('by_team', protected_packages_by_team, team_tags)
|
27
|
+
all_metrics += Metrics::PublicUsage.get_public_usage_metrics('by_team', protected_packages_by_team.map(&:original_package), team_tags)
|
28
|
+
|
29
|
+
all_metrics << GaugeMetric.for('by_team.notify_on_package_yml_changes.count', protected_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', protected_packages_by_team.count { |p| p.metadata['notify_on_new_violations'] }, team_tags)
|
31
|
+
|
32
|
+
#
|
33
|
+
# VIOLATIONS (implicit dependencies)
|
34
|
+
#
|
35
|
+
outbound_violations = protected_packages_by_team.flat_map(&:violations)
|
36
|
+
# Here we only look at packages_by_team because we only care about inbound violations onto packages for this team
|
37
|
+
inbound_violations = protected_packages_by_team.flat_map { |package| inbound_violations_by_package[package.name] || [] }
|
38
|
+
all_dependency_violations = (outbound_violations + inbound_violations).select(&:dependency?)
|
39
|
+
all_privacy_violations = (outbound_violations + inbound_violations).select(&:privacy?)
|
40
|
+
|
41
|
+
all_metrics << GaugeMetric.for('by_team.dependency_violations.count', Metrics.file_count(all_dependency_violations), team_tags)
|
42
|
+
all_metrics << GaugeMetric.for('by_team.privacy_violations.count', Metrics.file_count(all_privacy_violations), team_tags)
|
43
|
+
|
44
|
+
all_metrics << GaugeMetric.for('by_team.outbound_dependency_violations.count', Metrics.file_count(outbound_violations.select(&:dependency?)), team_tags)
|
45
|
+
all_metrics << GaugeMetric.for('by_team.inbound_dependency_violations.count', Metrics.file_count(inbound_violations.select(&:dependency?)), team_tags)
|
46
|
+
|
47
|
+
all_metrics << GaugeMetric.for('by_team.outbound_privacy_violations.count', Metrics.file_count(outbound_violations.select(&:privacy?)), team_tags)
|
48
|
+
all_metrics << GaugeMetric.for('by_team.inbound_privacy_violations.count', Metrics.file_count(inbound_violations.select(&:privacy?)), team_tags)
|
49
|
+
|
50
|
+
all_metrics << GaugeMetric.for('by_team.has_readme.count', protected_packages_by_team.count { |protected_package| Metrics.has_readme?(protected_package.original_package) }, team_tags)
|
51
|
+
|
52
|
+
grouped_outbound_violations = outbound_violations.group_by do |violation|
|
53
|
+
to_package = ParsePackwerk.find(violation.to_package_name)
|
54
|
+
if to_package.nil?
|
55
|
+
raise StandardError, "Could not find matching package #{violation.to_package_name}"
|
56
|
+
end
|
57
|
+
|
58
|
+
CodeOwnership.for_package(to_package)&.name
|
59
|
+
end
|
60
|
+
|
61
|
+
grouped_outbound_violations.each do |to_team_name, violations|
|
62
|
+
tags = team_tags + Metrics.tags_for_to_team(to_team_name)
|
63
|
+
all_metrics << GaugeMetric.for('by_team.outbound_dependency_violations.per_team.count', Metrics.file_count(violations.select(&:dependency?)), tags)
|
64
|
+
all_metrics << GaugeMetric.for('by_team.outbound_privacy_violations.per_team.count', Metrics.file_count(violations.select(&:privacy?)), tags)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
all_metrics
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module ModularizationStatistics
|
5
|
+
module Private
|
6
|
+
module Metrics
|
7
|
+
class ProtectionUsage
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig { params(prefix: String, protected_packages: T::Array[PackageProtections::ProtectedPackage], package_tags: T::Array[Tag]).returns(T::Array[GaugeMetric]) }
|
11
|
+
def self.get_protections_metrics(prefix, protected_packages, package_tags)
|
12
|
+
PackageProtections.all.flat_map do |protection|
|
13
|
+
PackageProtections::ViolationBehavior.each_value.map do |violation_behavior|
|
14
|
+
# https://github.com/Gusto/package_protections/pull/42 changed the public API of these violation behaviors.
|
15
|
+
# To preserve our ability to understand historical trends, we map to the old values.
|
16
|
+
# This allows our dashboards to continue to operate as expected.
|
17
|
+
# Note if we ever open source mod stats, we should probably inject this behavior so that new clients can see the new keys in their metrics.
|
18
|
+
violation_behavior_map = {
|
19
|
+
PackageProtections::ViolationBehavior::FailOnAny => 'fail_the_build_on_any_instances',
|
20
|
+
PackageProtections::ViolationBehavior::FailNever => 'no',
|
21
|
+
PackageProtections::ViolationBehavior::FailOnNew => 'fail_the_build_if_new_instances_appear',
|
22
|
+
}
|
23
|
+
violation_behavior_name = violation_behavior_map[violation_behavior]
|
24
|
+
metric_name = "#{prefix}.#{protection.identifier}.#{violation_behavior_name}.count"
|
25
|
+
count_of_packages = protected_packages.count { |p| p.violation_behavior_for(protection.identifier) == violation_behavior }
|
26
|
+
GaugeMetric.for(metric_name, count_of_packages, package_tags)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module ModularizationStatistics
|
5
|
+
module Private
|
6
|
+
module Metrics
|
7
|
+
class PublicUsage
|
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_public_usage_metrics(prefix, packages, package_tags)
|
12
|
+
packages_except_for_root = packages.reject { |package| package.name == ParsePackwerk::ROOT_PACKAGE_NAME }
|
13
|
+
all_files = packages_except_for_root.flat_map do |package|
|
14
|
+
package.directory.glob('**/**.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
all_public_files = T.let([], T::Array[Pathname])
|
18
|
+
is_using_public_directory = 0
|
19
|
+
packages_except_for_root.each do |package|
|
20
|
+
public_files = package.directory.glob('app/public/**/**.rb')
|
21
|
+
all_public_files += public_files
|
22
|
+
is_using_public_directory += 1 if public_files.any?
|
23
|
+
end
|
24
|
+
|
25
|
+
# In Datadog, we can divide public files by all files to get the ratio.
|
26
|
+
# This is not a metric that we are targeting -- its for observability and reflection only.
|
27
|
+
[
|
28
|
+
GaugeMetric.for("#{prefix}.all_files.count", all_files.count, package_tags),
|
29
|
+
GaugeMetric.for("#{prefix}.public_files.count", all_public_files.count, package_tags),
|
30
|
+
GaugeMetric.for("#{prefix}.using_public_directory.count", is_using_public_directory, package_tags),
|
31
|
+
]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module ModularizationStatistics
|
5
|
+
module Private
|
6
|
+
module Metrics
|
7
|
+
extend T::Sig
|
8
|
+
UNKNOWN_OWNER = T.let('Unknown', String)
|
9
|
+
|
10
|
+
sig { params(team_name: T.nilable(String)).returns(T::Array[Tag]) }
|
11
|
+
def self.tags_for_team(team_name)
|
12
|
+
[Tag.for('team', team_name || UNKNOWN_OWNER)]
|
13
|
+
end
|
14
|
+
|
15
|
+
sig { params(package: ParsePackwerk::Package, app_name: String).returns(T::Array[Tag]) }
|
16
|
+
def self.tags_for_package(package, app_name)
|
17
|
+
[
|
18
|
+
Tag.new(key: 'package', value: humanized_package_name(package.name)),
|
19
|
+
Tag.new(key: 'app', value: app_name),
|
20
|
+
*Metrics.tags_for_team(CodeOwnership.for_package(package)&.name),
|
21
|
+
]
|
22
|
+
end
|
23
|
+
|
24
|
+
sig { params(team_name: T.nilable(String)).returns(T::Array[Tag]) }
|
25
|
+
def self.tags_for_to_team(team_name)
|
26
|
+
[Tag.for('to_team', team_name || Metrics::UNKNOWN_OWNER)]
|
27
|
+
end
|
28
|
+
|
29
|
+
sig { params(name: String).returns(String) }
|
30
|
+
def self.humanized_package_name(name)
|
31
|
+
if name == ParsePackwerk::ROOT_PACKAGE_NAME
|
32
|
+
'root'
|
33
|
+
else
|
34
|
+
name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
sig { params(violations: T::Array[ParsePackwerk::Violation]).returns(Integer) }
|
39
|
+
def self.file_count(violations)
|
40
|
+
violations.sum { |v| v.files.count }
|
41
|
+
end
|
42
|
+
|
43
|
+
sig { params(package: ParsePackwerk::Package).returns(T::Boolean) }
|
44
|
+
def self.has_readme?(package)
|
45
|
+
package.directory.join('README.md').exist?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -12,6 +12,9 @@ module PackageProtections
|
|
12
12
|
sig { void }
|
13
13
|
def bust_cache!; end
|
14
14
|
|
15
|
+
sig { params(blk: T.proc.params(arg0: ::PackageProtections::Private::Configuration).void).void }
|
16
|
+
def configure(&blk); end
|
17
|
+
|
15
18
|
sig do
|
16
19
|
params(
|
17
20
|
packages: T::Array[::ParsePackwerk::Package],
|
@@ -20,14 +23,6 @@ module PackageProtections
|
|
20
23
|
end
|
21
24
|
def get_offenses(packages:, new_violations:); end
|
22
25
|
|
23
|
-
sig do
|
24
|
-
params(
|
25
|
-
package_names: T::Array[::String],
|
26
|
-
all_packages: T::Array[::ParsePackwerk::Package]
|
27
|
-
).returns(T::Array[::ParsePackwerk::Package])
|
28
|
-
end
|
29
|
-
def packages_for_names(package_names, all_packages); end
|
30
|
-
|
31
26
|
sig { params(identifier: ::String).returns(T::Hash[T.untyped, T.untyped]) }
|
32
27
|
def private_cop_config(identifier); end
|
33
28
|
|
@@ -102,6 +97,9 @@ module PackageProtections::Private
|
|
102
97
|
sig { void }
|
103
98
|
def bust_cache!; end
|
104
99
|
|
100
|
+
sig { returns(::PackageProtections::Private::Configuration) }
|
101
|
+
def config; end
|
102
|
+
|
105
103
|
sig do
|
106
104
|
params(
|
107
105
|
packages: T::Array[::ParsePackwerk::Package],
|
@@ -113,14 +111,6 @@ module PackageProtections::Private
|
|
113
111
|
sig { params(name: ::String).returns(::PackageProtections::ProtectedPackage) }
|
114
112
|
def get_package_with_name(name); end
|
115
113
|
|
116
|
-
sig do
|
117
|
-
params(
|
118
|
-
package_names: T::Array[::String],
|
119
|
-
all_packages: T::Array[::ParsePackwerk::Package]
|
120
|
-
).returns(T::Array[::ParsePackwerk::Package])
|
121
|
-
end
|
122
|
-
def packages_for_names(package_names, all_packages); end
|
123
|
-
|
124
114
|
sig { params(identifier: ::String).returns(T::Hash[T.untyped, T.untyped]) }
|
125
115
|
def private_cop_config(identifier); end
|
126
116
|
|
@@ -195,6 +185,23 @@ class PackageProtections::Private::ColorizedString::Color < ::T::Enum
|
|
195
185
|
end
|
196
186
|
end
|
197
187
|
|
188
|
+
class PackageProtections::Private::Configuration
|
189
|
+
sig { void }
|
190
|
+
def initialize; end
|
191
|
+
|
192
|
+
sig { void }
|
193
|
+
def bust_cache!; end
|
194
|
+
|
195
|
+
sig { returns(T::Array[::PackageProtections::ProtectionInterface]) }
|
196
|
+
def default_protections; end
|
197
|
+
|
198
|
+
sig { returns(T::Array[::PackageProtections::ProtectionInterface]) }
|
199
|
+
def protections; end
|
200
|
+
|
201
|
+
sig { params(protections: T::Array[::PackageProtections::ProtectionInterface]).void }
|
202
|
+
def protections=(protections); end
|
203
|
+
end
|
204
|
+
|
198
205
|
class PackageProtections::Private::IncomingPrivacyProtection
|
199
206
|
include ::PackageProtections::ProtectionInterface
|
200
207
|
|
@@ -256,51 +263,6 @@ class PackageProtections::Private::MetadataModifiers
|
|
256
263
|
end
|
257
264
|
end
|
258
265
|
|
259
|
-
class PackageProtections::Private::MultipleNamespacesProtection
|
260
|
-
include ::PackageProtections::ProtectionInterface
|
261
|
-
include ::PackageProtections::RubocopProtectionInterface
|
262
|
-
|
263
|
-
sig do
|
264
|
-
override
|
265
|
-
.params(
|
266
|
-
packages: T::Array[::PackageProtections::ProtectedPackage]
|
267
|
-
).returns(T::Array[::PackageProtections::RubocopProtectionInterface::CopConfig])
|
268
|
-
end
|
269
|
-
def cop_configs(packages); end
|
270
|
-
|
271
|
-
sig { params(package: ::PackageProtections::ProtectedPackage).returns(T::Hash[T.untyped, T.untyped]) }
|
272
|
-
def custom_cop_config(package); end
|
273
|
-
|
274
|
-
sig do
|
275
|
-
override
|
276
|
-
.params(
|
277
|
-
protected_packages: T::Array[::PackageProtections::ProtectedPackage]
|
278
|
-
).returns(T::Array[::PackageProtections::Offense])
|
279
|
-
end
|
280
|
-
def get_offenses_for_existing_violations(protected_packages); end
|
281
|
-
|
282
|
-
sig { override.returns(::String) }
|
283
|
-
def humanized_protection_description; end
|
284
|
-
|
285
|
-
sig { override.returns(::String) }
|
286
|
-
def humanized_protection_name; end
|
287
|
-
|
288
|
-
sig { override.returns(::String) }
|
289
|
-
def identifier; end
|
290
|
-
|
291
|
-
sig do
|
292
|
-
override
|
293
|
-
.params(
|
294
|
-
behavior: ::PackageProtections::ViolationBehavior,
|
295
|
-
package: ::ParsePackwerk::Package
|
296
|
-
).returns(T.nilable(::String))
|
297
|
-
end
|
298
|
-
def unmet_preconditions_for_behavior(behavior, package); end
|
299
|
-
end
|
300
|
-
|
301
|
-
PackageProtections::Private::MultipleNamespacesProtection::COP_NAME = T.let(T.unsafe(nil), String)
|
302
|
-
PackageProtections::Private::MultipleNamespacesProtection::IDENTIFIER = T.let(T.unsafe(nil), String)
|
303
|
-
|
304
266
|
class PackageProtections::Private::OutgoingDependencyProtection
|
305
267
|
include ::PackageProtections::ProtectionInterface
|
306
268
|
|
@@ -359,48 +321,6 @@ class PackageProtections::Private::Output
|
|
359
321
|
end
|
360
322
|
end
|
361
323
|
|
362
|
-
class PackageProtections::Private::TypedApiProtection
|
363
|
-
include ::PackageProtections::ProtectionInterface
|
364
|
-
include ::PackageProtections::RubocopProtectionInterface
|
365
|
-
|
366
|
-
sig do
|
367
|
-
override
|
368
|
-
.params(
|
369
|
-
packages: T::Array[::PackageProtections::ProtectedPackage]
|
370
|
-
).returns(T::Array[::PackageProtections::RubocopProtectionInterface::CopConfig])
|
371
|
-
end
|
372
|
-
def cop_configs(packages); end
|
373
|
-
|
374
|
-
sig do
|
375
|
-
override
|
376
|
-
.params(
|
377
|
-
protected_packages: T::Array[::PackageProtections::ProtectedPackage]
|
378
|
-
).returns(T::Array[::PackageProtections::Offense])
|
379
|
-
end
|
380
|
-
def get_offenses_for_existing_violations(protected_packages); end
|
381
|
-
|
382
|
-
sig { override.returns(::String) }
|
383
|
-
def humanized_protection_description; end
|
384
|
-
|
385
|
-
sig { override.returns(::String) }
|
386
|
-
def humanized_protection_name; end
|
387
|
-
|
388
|
-
sig { override.returns(::String) }
|
389
|
-
def identifier; end
|
390
|
-
|
391
|
-
sig do
|
392
|
-
override
|
393
|
-
.params(
|
394
|
-
behavior: ::PackageProtections::ViolationBehavior,
|
395
|
-
package: ::ParsePackwerk::Package
|
396
|
-
).returns(T.nilable(::String))
|
397
|
-
end
|
398
|
-
def unmet_preconditions_for_behavior(behavior, package); end
|
399
|
-
end
|
400
|
-
|
401
|
-
PackageProtections::Private::TypedApiProtection::COP_NAME = T.let(T.unsafe(nil), String)
|
402
|
-
PackageProtections::Private::TypedApiProtection::IDENTIFIER = T.let(T.unsafe(nil), String)
|
403
|
-
|
404
324
|
class PackageProtections::Private::VisibilityProtection
|
405
325
|
include ::PackageProtections::ProtectionInterface
|
406
326
|
|
@@ -560,16 +480,26 @@ module PackageProtections::RubocopProtectionInterface
|
|
560
480
|
abstract!
|
561
481
|
|
562
482
|
sig do
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
).returns(T::Array[::PackageProtections::RubocopProtectionInterface::CopConfig])
|
483
|
+
params(
|
484
|
+
packages: T::Array[::PackageProtections::ProtectedPackage]
|
485
|
+
).returns(T::Array[::PackageProtections::RubocopProtectionInterface::CopConfig])
|
567
486
|
end
|
568
487
|
def cop_configs(packages); end
|
569
488
|
|
489
|
+
sig { abstract.returns(::String) }
|
490
|
+
def cop_name; end
|
491
|
+
|
570
492
|
sig { params(package: ::PackageProtections::ProtectedPackage).returns(T::Hash[T.untyped, T.untyped]) }
|
571
493
|
def custom_cop_config(package); end
|
572
494
|
|
495
|
+
sig do
|
496
|
+
override
|
497
|
+
.params(
|
498
|
+
protected_packages: T::Array[::PackageProtections::ProtectedPackage]
|
499
|
+
).returns(T::Array[::PackageProtections::Offense])
|
500
|
+
end
|
501
|
+
def get_offenses_for_existing_violations(protected_packages); end
|
502
|
+
|
573
503
|
sig do
|
574
504
|
override
|
575
505
|
.params(
|
@@ -578,6 +508,21 @@ module PackageProtections::RubocopProtectionInterface
|
|
578
508
|
end
|
579
509
|
def get_offenses_for_new_violations(new_violations); end
|
580
510
|
|
511
|
+
sig { abstract.returns(T::Array[::String]) }
|
512
|
+
def included_globs_for_pack; end
|
513
|
+
|
514
|
+
sig { abstract.params(file: ::String).returns(::String) }
|
515
|
+
def message_for_fail_on_any(file); end
|
516
|
+
|
517
|
+
sig do
|
518
|
+
override
|
519
|
+
.params(
|
520
|
+
behavior: ::PackageProtections::ViolationBehavior,
|
521
|
+
package: ::ParsePackwerk::Package
|
522
|
+
).returns(T.nilable(::String))
|
523
|
+
end
|
524
|
+
def unmet_preconditions_for_behavior(behavior, package); end
|
525
|
+
|
581
526
|
private
|
582
527
|
|
583
528
|
sig { params(rule: ::String).returns(T::Set[::String]) }
|
@@ -630,3 +575,7 @@ class PackageProtections::ViolationBehavior < ::T::Enum
|
|
630
575
|
def from_raw_value(value); end
|
631
576
|
end
|
632
577
|
end
|
578
|
+
|
579
|
+
module RuboCop; end
|
580
|
+
module RuboCop::Cop; end
|
581
|
+
module RuboCop::Cop::PackageProtections; end
|
@@ -9,9 +9,15 @@ module ParsePackwerk
|
|
9
9
|
sig { returns(T::Array[::ParsePackwerk::Package]) }
|
10
10
|
def all; end
|
11
11
|
|
12
|
+
sig { void }
|
13
|
+
def bust_cache!; end
|
14
|
+
|
12
15
|
sig { params(name: ::String).returns(T.nilable(::ParsePackwerk::Package)) }
|
13
16
|
def find(name); end
|
14
17
|
|
18
|
+
sig { params(file_path: T.any(::Pathname, ::String)).returns(T.nilable(::ParsePackwerk::Package)) }
|
19
|
+
def package_from_path(file_path); end
|
20
|
+
|
15
21
|
sig { params(package: ::ParsePackwerk::Package).void }
|
16
22
|
def write_package_yml!(package); end
|
17
23
|
|
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.35.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-08-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: code_teams
|
@@ -177,6 +177,13 @@ files:
|
|
177
177
|
- lib/modularization_statistics/gauge_metric.rb
|
178
178
|
- lib/modularization_statistics/private.rb
|
179
179
|
- lib/modularization_statistics/private/datadog_reporter.rb
|
180
|
+
- lib/modularization_statistics/private/metrics.rb
|
181
|
+
- lib/modularization_statistics/private/metrics/files.rb
|
182
|
+
- lib/modularization_statistics/private/metrics/nested_packs.rb
|
183
|
+
- lib/modularization_statistics/private/metrics/packages.rb
|
184
|
+
- lib/modularization_statistics/private/metrics/packages_by_team.rb
|
185
|
+
- lib/modularization_statistics/private/metrics/protection_usage.rb
|
186
|
+
- lib/modularization_statistics/private/metrics/public_usage.rb
|
180
187
|
- lib/modularization_statistics/private/source_code_file.rb
|
181
188
|
- lib/modularization_statistics/tag.rb
|
182
189
|
- lib/modularization_statistics/tags.rb
|
@@ -185,8 +192,8 @@ files:
|
|
185
192
|
- sorbet/rbi/gems/code_teams@1.0.0.rbi
|
186
193
|
- sorbet/rbi/gems/dogapi@1.45.0.rbi
|
187
194
|
- sorbet/rbi/gems/manual.rbi
|
188
|
-
- sorbet/rbi/gems/package_protections@
|
189
|
-
- sorbet/rbi/gems/parse_packwerk@0.
|
195
|
+
- sorbet/rbi/gems/package_protections@1.4.0.rbi
|
196
|
+
- sorbet/rbi/gems/parse_packwerk@0.12.0.rbi
|
190
197
|
- sorbet/rbi/gems/rspec@3.10.0.rbi
|
191
198
|
- sorbet/rbi/todo.rbi
|
192
199
|
homepage: https://github.com/rubyatscale/modularization_statistics
|