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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a0d44cafa4c07aed6fb4248234f60f70e6a0f9124b7ad6184f515feb22ef972a
4
- data.tar.gz: 3a1ae5714c23a52c63c2cff8616a9f739e3bb68a44abd8e76e35b864e792c2c7
3
+ metadata.gz: 75ff0dc32b62d7a71ef4670809d77dfd7bac173f95529de171fc22f90f93ba41
4
+ data.tar.gz: c453e59ffd54ee91e1d535ca55767506f82cc5f43db8418423ed44e80b646c3c
5
5
  SHA512:
6
- metadata.gz: 750643b2ff0d4705f69c66f049be32d070e50189173e2b44dd3975fde11ed8052c182b24729831e5e4553bc5f23c8d88f76ee5a0b70a85f5f1842c855bbd0d7e
7
- data.tar.gz: 434a4e77901c9af6783de0db879daaaf1bb4918835f1a147a0cc5aa0ff7c4c35ba12e306e23db3a65a9b875b97c8503b6e49a2c93b2e2c4014c9c1d01845afd2
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
- all_metrics
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
- abstract
564
- .params(
565
- packages: T::Array[::PackageProtections::ProtectedPackage]
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.34.0
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-06-14 00:00:00.000000000 Z
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@0.64.0.rbi
189
- - sorbet/rbi/gems/parse_packwerk@0.10.0.rbi
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