pack_stats 0.0.6 → 0.1.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: 24c2ef393916e7e08d2b7c227d747eb07e34ddc1c544c423f48e5797ca9a6483
4
- data.tar.gz: 819e2a8238a33a664f7586701892d26bc265adb17afb54943ce1a156a58b62d0
3
+ metadata.gz: fb012a138ad533b84c50a2349e006b2ad0fd4d1a6e4170c8883e17a709051377
4
+ data.tar.gz: fd679a7ea57cf9508e9573ea1746fc31b2ac23070eed7f2b9375a6efc7d991c4
5
5
  SHA512:
6
- metadata.gz: ce1303c2c2be91aa338b89df57af5f9a59ff76615b95f0733ded2f3543c052c10e47ee3c1e6b01d9b9820edac47609077113d1749211f69c90a8f4cc923fc499
7
- data.tar.gz: 32f66cbfa1c998c65591d34c0a076a421e13cd01de7c130d49fa28fae1512396aed7261f27fe458f43f347bbc41abc70253af9c1590ceb4fc8652f0dcb52b84b
6
+ metadata.gz: f224241cc9183501c6be5738a4ee61f53a863610ac883587dd7e43f0fa2c409fc2bd52a5ef97e1b840e8699346b79618397caecc937133a8f3f3d2108433e49c
7
+ data.tar.gz: 7ebcf204658cf0cfdf96f86de7fb8f4e302eca96d5ecac23f65a07702680e674b2ed7396b499ef50be75143dd3289f34b2d0e1b1e0a8b66c5f9f04f222c77fe5
data/README.md CHANGED
@@ -46,42 +46,51 @@ PackStats.report_to_datadog!(
46
46
 
47
47
  It's recommended to run this in CI on the main/development branch so each new commit has metrics emitted for it.
48
48
 
49
- # Tracking Privacy and Dependency Violations Reliably
50
- With [`packwerk`](https://github.com/Shopify/packwerk), privacy and dependency violations do not show up until a package has set `enforce_privacy` and `enforce_dependency` (respectively) to `true`. As such, when you're first starting off, you'll see no violations, and then periodic large increases as teams start using these protections. If you're interested in looking at privacy and dependency violations over time as if all packages were enforcing dependencies and privacy the whole time, we recommend setting these values to be true before running modularization statistics in your CI.
51
-
52
49
  ```ruby
53
50
  require 'pack_stats'
54
51
 
55
- namespace(:modularization) do
52
+ def report(verbose:, max_enforcements: false)
53
+ ignored_paths = Pathname.glob('spec/fixtures/**/**')
54
+ source_code_pathnames = Pathname.glob('{app,components,lib,packs,spec}/**/**').select(&:file?) - ignored_paths
55
+
56
+ PackStats.report_to_datadog!(
57
+ datadog_client: Dogapi::Client.new(ENV.fetch('DATADOG_API_KEY')),
58
+ app_name: Rails.application.class.module_parent_name,
59
+ source_code_pathnames: source_code_pathnames,
60
+ verbose: verbose,
61
+ max_enforcements: max_enforcements
62
+ )
63
+ end
64
+
65
+ namespace(:pack_stats) do
56
66
  desc(
57
- 'Publish modularization stats to datadog. ' \
58
- 'Example: bin/rails "modularization:upload_statistics"'
67
+ 'Publish pack_stats to datadog. ' \
68
+ 'Example: bin/rails "pack_stats:upload"'
59
69
  )
60
- task(:upload_statistics, [:verbose] => :environment) do |_, args|
61
- ignored_paths = Pathname.glob('spec/fixtures/**/**')
62
- source_code_pathnames = Pathname.glob('{app,components,lib,packs,spec}/**/**').select(&:file?) - ignored_paths
70
+ task(:upload, [:verbose] => :environment) do |_, args|
71
+ verbose = args[:verbose] == 'true' || false
72
+
73
+ # First send without any changes, tagging metrics with max_enforcements:false
74
+ report(verbose: verbose, max_enforcements: false)
63
75
 
64
- # To correctly track violations, we rewrite all `package.yml` files with
65
- # `enforce_dependencies` and `enforce_privacy` set to true, then update deprecations.
76
+ # At Gusto, it's useful to be able to view the dashboard as if all enforce_x were set to true.
77
+ # To do this, we rewrite all `package.yml` files with `enforce_dependencies` and `enforce_privacy`
78
+ # set to true, then bin/packwerk update-todo.
66
79
  old_packages = ParsePackwerk.all
67
80
  old_packages.each do |package|
68
81
  new_package = package.with(enforce_dependencies: true, enforce_privacy: true)
69
82
  ParsePackwerk.write_package_yml!(new_package)
70
83
  end
71
84
 
72
- Packwerk::Cli.new.execute_command(['update-deprecations'])
85
+ Packwerk::Cli.new.execute_command(['update-todo'])
73
86
 
74
87
  # Now we reset it back so that the protection values are the same as the native packwerk configuration
75
88
  old_packages.each do |package|
76
89
  ParsePackwerk.write_package_yml!(package)
77
90
  end
78
-
79
- PackStats.report_to_datadog!(
80
- datadog_client: Dogapi::Client.new(ENV.fetch('DATADOG_API_KEY')),
81
- app_name: Rails.application.class.module_parent_name,
82
- source_code_pathnames: source_code_pathnames,
83
- verbose: args[:verbose] == 'true' || false
84
- )
91
+
92
+ # Then send after maxing out enforcements, tagging metrics with max_enforcements:true
93
+ report(verbose: verbose, max_enforcements: true)
85
94
  end
86
95
  end
87
96
  ```
@@ -98,7 +107,7 @@ Gusto has two dashboards that we've created to view these metrics. We've also ex
98
107
 
99
108
  This helps answer questions like:
100
109
  - How are we doing on reducing dependency and privacy violations in your monolith overall?
101
- - How are we doing overall on adopting package protections?
110
+ - How are we doing overall on adopting packwerk?
102
111
 
103
112
  [Dashboard JSON](docs/executive_summary.json)
104
113
 
@@ -7,6 +7,7 @@ require 'pack_stats/private/metrics/files'
7
7
  require 'pack_stats/private/metrics/public_usage'
8
8
  require 'pack_stats/private/metrics/packwerk_checker_usage'
9
9
  require 'pack_stats/private/metrics/rubocop_usage'
10
+ require 'pack_stats/private/metrics/dependencies'
10
11
  require 'pack_stats/private/metrics/packages'
11
12
  require 'pack_stats/private/metrics/packages_by_team'
12
13
 
@@ -0,0 +1,47 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module PackStats
5
+ module Private
6
+ module Metrics
7
+ class Dependencies
8
+ extend T::Sig
9
+
10
+ sig { params(prefix: String, packages: T::Array[ParsePackwerk::Package], app_name: String).returns(T::Array[GaugeMetric]) }
11
+ def self.get_metrics(prefix, packages, app_name)
12
+ all_metrics = T.let([], T::Array[GaugeMetric])
13
+ inbound_explicit_dependency_by_package = {}
14
+ packages.each do |package|
15
+ package.dependencies.each do |explicit_dependency|
16
+ inbound_explicit_dependency_by_package[explicit_dependency] ||= []
17
+ inbound_explicit_dependency_by_package[explicit_dependency] << package.name
18
+ end
19
+ end
20
+
21
+ packages.each do |package| # rubocop:disable Style/CombinableLoops
22
+ package_tags = Metrics.tags_for_package(package, app_name)
23
+
24
+ #
25
+ # EXPLICIT DEPENDENCIES
26
+ #
27
+ package.dependencies.each do |explicit_dependency|
28
+ to_package = ParsePackwerk.find(explicit_dependency)
29
+ if to_package.nil?
30
+ raise StandardError, "Could not find matching package #{explicit_dependency}"
31
+ end
32
+
33
+ owner = Private.package_owner(to_package)
34
+ tags = package_tags + [Tag.for('other_package', Metrics.humanized_package_name(explicit_dependency))] + Metrics.tags_for_other_team(owner)
35
+ all_metrics << GaugeMetric.for('by_package.dependencies.by_other_package.count', 1, tags)
36
+ end
37
+
38
+ all_metrics << GaugeMetric.for('by_package.dependencies.count', package.dependencies.count, package_tags)
39
+ all_metrics << GaugeMetric.for('by_package.depended_on.count', inbound_explicit_dependency_by_package[package.name]&.count || 0, package_tags)
40
+ end
41
+
42
+ all_metrics
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -20,12 +20,13 @@ module PackStats
20
20
 
21
21
  all_metrics << GaugeMetric.for('all_packages.count', packages.count, package_tags)
22
22
  all_metrics << GaugeMetric.for('all_packages.dependencies.count', packages.sum { |package| package.dependencies.count }, package_tags)
23
- all_metrics << GaugeMetric.for('all_packages.dependency_violations.count', packages.sum { |package| Metrics.file_count(package.violations.select(&:dependency?)) }, package_tags)
24
- all_metrics << GaugeMetric.for('all_packages.privacy_violations.count', packages.sum { |package| Metrics.file_count(package.violations.select(&:privacy?)) }, package_tags)
25
- all_metrics << GaugeMetric.for('all_packages.enforcing_dependencies.count', packages.count(&:enforces_dependencies?), package_tags)
26
- all_metrics << GaugeMetric.for('all_packages.enforcing_privacy.count', packages.count(&:enforces_privacy?), package_tags)
27
23
 
28
- all_metrics << GaugeMetric.for('all_packages.with_violations.count', packages.count { |package| package.violations.any? }, package_tags)
24
+ PackwerkCheckerUsage::CHECKERS.each do |checker|
25
+ violation_count = packages.sum { |package| Metrics.file_count(package.violations.select{|v| v.type == checker.violation_type}) }
26
+ tags = package_tags + [checker.violation_type_tag]
27
+ all_metrics << GaugeMetric.for("all_packages.violations.count", violation_count, tags)
28
+ end
29
+
29
30
  all_metrics += Metrics::PublicUsage.get_public_usage_metrics('all_packages', packages, package_tags)
30
31
  all_metrics << GaugeMetric.for('all_packages.has_readme.count', packages.count { |package| Metrics.has_readme?(package) }, package_tags)
31
32
 
@@ -37,66 +38,52 @@ module PackStats
37
38
 
38
39
  packages.each do |package|
39
40
  package_tags = Metrics.tags_for_package(package, app_name)
41
+ all_metrics += Metrics::PublicUsage.get_public_usage_metrics('by_package', [package], package_tags)
40
42
 
41
- #
42
- # VIOLATIONS (implicit dependencies)
43
- #
44
43
  outbound_violations = package.violations
45
44
  inbound_violations = inbound_violations_by_package[package.name] || []
46
- all_dependency_violations = (outbound_violations + inbound_violations).select(&:dependency?)
47
- all_privacy_violations = (outbound_violations + inbound_violations).select(&:privacy?)
48
-
49
- all_metrics << GaugeMetric.for('by_package.dependency_violations.count', Metrics.file_count(all_dependency_violations), package_tags)
50
- all_metrics << GaugeMetric.for('by_package.privacy_violations.count', Metrics.file_count(all_privacy_violations), package_tags)
51
-
52
- all_metrics << GaugeMetric.for('by_package.outbound_dependency_violations.count', Metrics.file_count(outbound_violations.select(&:dependency?)), package_tags)
53
- all_metrics << GaugeMetric.for('by_package.inbound_dependency_violations.count', Metrics.file_count(inbound_violations.select(&:dependency?)), package_tags)
54
45
 
55
- all_metrics << GaugeMetric.for('by_package.outbound_privacy_violations.count', Metrics.file_count(outbound_violations.select(&:privacy?)), package_tags)
56
- all_metrics << GaugeMetric.for('by_package.inbound_privacy_violations.count', Metrics.file_count(inbound_violations.select(&:privacy?)), package_tags)
57
-
58
- all_metrics += Metrics::PublicUsage.get_public_usage_metrics('by_package', [package], package_tags)
59
-
60
- package.violations.group_by(&:to_package_name).each do |to_package_name, violations|
61
- to_package = ParsePackwerk.find(to_package_name)
62
- if to_package.nil?
63
- raise StandardError, "Could not find matching package #{to_package_name}"
46
+ PackwerkCheckerUsage::CHECKERS.each do |checker|
47
+ direction = checker.direction
48
+
49
+ case direction
50
+ when PackwerkCheckerUsage::Direction::Outbound
51
+ all_violations_of_type = outbound_violations.select { |v| v.type == checker.violation_type }
52
+
53
+ packages.each do |other_package|
54
+ violations = package.violations.select{|v| v.to_package_name == other_package.name && v.type == checker.violation_type }
55
+
56
+ tags = package_tags + [
57
+ Tag.for('other_package', Metrics.humanized_package_name(other_package.name)),
58
+ *Metrics.tags_for_other_team(Private.package_owner(other_package)),
59
+ checker.violation_type_tag
60
+ ]
61
+
62
+ all_metrics << GaugeMetric.for("by_package.violations.by_other_package.count", Metrics.file_count(violations), tags)
63
+ end
64
+ when PackwerkCheckerUsage::Direction::Inbound
65
+ all_violations_of_type = inbound_violations.select { |v| v.type == checker.violation_type }
66
+
67
+ packages.each do |other_package|
68
+ violations = other_package.violations.select{|v| v.to_package_name == package.name && v.type == checker.violation_type }
69
+ tags = package_tags + [
70
+ Tag.for('other_package', Metrics.humanized_package_name(other_package.name)),
71
+ *Metrics.tags_for_other_team(Private.package_owner(other_package)),
72
+ checker.violation_type_tag
73
+ ]
74
+
75
+ all_metrics << GaugeMetric.for("by_package.violations.by_other_package.count", Metrics.file_count(violations), tags)
76
+ end
77
+ else
78
+ T.absurd(direction)
64
79
  end
65
80
 
66
- tags = package_tags + [Tag.for('to_package', Metrics.humanized_package_name(to_package_name))] + Metrics.tags_for_to_team(Private.package_owner(to_package))
67
- all_metrics << GaugeMetric.for('by_package.outbound_dependency_violations.per_package.count', Metrics.file_count(violations.select(&:dependency?)), tags)
68
- all_metrics << GaugeMetric.for('by_package.outbound_privacy_violations.per_package.count', Metrics.file_count(violations.select(&:privacy?)), tags)
81
+ tags = package_tags + [checker.violation_type_tag]
82
+ all_metrics << GaugeMetric.for("by_package.violations.count", Metrics.file_count(all_violations_of_type), tags)
69
83
  end
70
84
  end
71
85
 
72
- inbound_explicit_dependency_by_package = {}
73
- packages.each do |package|
74
- package.dependencies.each do |explicit_dependency|
75
- inbound_explicit_dependency_by_package[explicit_dependency] ||= []
76
- inbound_explicit_dependency_by_package[explicit_dependency] << package.name
77
- end
78
- end
79
-
80
- packages.each do |package| # rubocop:disable Style/CombinableLoops
81
- package_tags = Metrics.tags_for_package(package, app_name)
82
-
83
- #
84
- # EXPLICIT DEPENDENCIES
85
- #
86
- package.dependencies.each do |explicit_dependency|
87
- to_package = ParsePackwerk.find(explicit_dependency)
88
- if to_package.nil?
89
- raise StandardError, "Could not find matching package #{explicit_dependency}"
90
- end
91
-
92
- owner = Private.package_owner(to_package)
93
- tags = package_tags + [Tag.for('to_package', Metrics.humanized_package_name(explicit_dependency))] + Metrics.tags_for_to_team(owner)
94
- all_metrics << GaugeMetric.for('by_package.outbound_explicit_dependencies.per_package.count', 1, tags)
95
- end
96
-
97
- all_metrics << GaugeMetric.for('by_package.outbound_explicit_dependencies.count', package.dependencies.count, package_tags)
98
- all_metrics << GaugeMetric.for('by_package.inbound_explicit_dependencies.count', inbound_explicit_dependency_by_package[package.name]&.count || 0, package_tags)
99
- end
86
+ all_metrics += Metrics::Dependencies.get_metrics('by_package', packages, app_name)
100
87
 
101
88
  all_metrics
102
89
  end
@@ -18,47 +18,49 @@ module PackStats
18
18
  app_level_tag = Tag.for('app', app_name)
19
19
 
20
20
 
21
- all_packages.group_by { |package| Private.package_owner(package) }.each do |team_name, packages_by_team|
21
+ all_packages.group_by { |package| Private.package_owner(package) }.each do |team_name, packages_for_team|
22
+ team_tags = Metrics.tags_for_team(team_name) + [app_level_tag]
23
+ all_metrics << GaugeMetric.for('by_team.all_packages.count', packages_for_team.count, team_tags)
24
+ all_metrics += Metrics::PackwerkCheckerUsage.get_checker_metrics('by_team', packages_for_team, team_tags)
25
+ all_metrics += Metrics::PublicUsage.get_public_usage_metrics('by_team', packages_for_team, team_tags)
26
+ all_metrics << GaugeMetric.for('by_team.has_readme.count', packages_for_team.count { |package| Metrics.has_readme?(package) }, team_tags)
27
+
28
+ outbound_violations = packages_for_team.flat_map(&:violations)
22
29
  # We look at `all_packages` because we care about ALL inbound violations across all teams
23
30
  inbound_violations_by_package = all_packages.flat_map(&:violations).group_by(&:to_package_name)
31
+ # Here we only look at packages_for_team because we only care about inbound violations onto packages for this team
32
+ inbound_violations = packages_for_team.flat_map { |package| inbound_violations_by_package[package.name] || [] }
24
33
 
25
- team_tags = Metrics.tags_for_team(team_name) + [app_level_tag]
26
- all_metrics << GaugeMetric.for('by_team.all_packages.count', packages_by_team.count, team_tags)
27
- all_metrics += Metrics::PackwerkCheckerUsage.get_checker_metrics('by_team', packages_by_team, team_tags)
28
- all_metrics += Metrics::PublicUsage.get_public_usage_metrics('by_team', packages_by_team, team_tags)
29
- #
30
- # VIOLATIONS (implicit dependencies)
31
- #
32
- outbound_violations = packages_by_team.flat_map(&:violations)
33
- # Here we only look at packages_by_team because we only care about inbound violations onto packages for this team
34
- inbound_violations = packages_by_team.flat_map { |package| inbound_violations_by_package[package.name] || [] }
35
- all_dependency_violations = (outbound_violations + inbound_violations).select(&:dependency?)
36
- all_privacy_violations = (outbound_violations + inbound_violations).select(&:privacy?)
37
-
38
- all_metrics << GaugeMetric.for('by_team.dependency_violations.count', Metrics.file_count(all_dependency_violations), team_tags)
39
- all_metrics << GaugeMetric.for('by_team.privacy_violations.count', Metrics.file_count(all_privacy_violations), team_tags)
34
+ PackwerkCheckerUsage::CHECKERS.each do |checker|
35
+ direction = checker.direction
36
+ case direction
37
+ when PackwerkCheckerUsage::Direction::Outbound
38
+ all_violations_of_type = outbound_violations.select { |v| v.type == checker.violation_type }
40
39
 
41
- all_metrics << GaugeMetric.for('by_team.outbound_dependency_violations.count', Metrics.file_count(outbound_violations.select(&:dependency?)), team_tags)
42
- all_metrics << GaugeMetric.for('by_team.inbound_dependency_violations.count', Metrics.file_count(inbound_violations.select(&:dependency?)), team_tags)
40
+ violation_count = packages_for_team.sum { |package| Metrics.file_count(package.violations.select{|v| v.type == checker.violation_type}) }
41
+ tags = team_tags + [checker.violation_type_tag]
42
+ all_metrics << GaugeMetric.for("by_team.violations.count", violation_count, tags)
43
43
 
44
- all_metrics << GaugeMetric.for('by_team.outbound_privacy_violations.count', Metrics.file_count(outbound_violations.select(&:privacy?)), team_tags)
45
- all_metrics << GaugeMetric.for('by_team.inbound_privacy_violations.count', Metrics.file_count(inbound_violations.select(&:privacy?)), team_tags)
44
+ all_packages.group_by { |package| Private.package_owner(package) }.each do |other_team_name, other_teams_packages|
45
+ violations = outbound_violations.select{|v| other_teams_packages.map(&:name).include?(v.to_package_name) && v.type == checker.violation_type}
46
+ tags = team_tags + Metrics.tags_for_other_team(other_team_name) + [checker.violation_type_tag]
47
+ all_metrics << GaugeMetric.for("by_team.violations.by_other_team.count", Metrics.file_count(violations), tags)
48
+ end
49
+ when PackwerkCheckerUsage::Direction::Inbound
50
+ all_violations_of_type = inbound_violations.select { |v| v.type == checker.violation_type }
46
51
 
47
- all_metrics << GaugeMetric.for('by_team.has_readme.count', packages_by_team.count { |package| Metrics.has_readme?(package) }, team_tags)
52
+ violation_count = packages_for_team.sum { |package| Metrics.file_count(package.violations.select{|v| v.type == checker.violation_type}) }
53
+ tags = team_tags + [checker.violation_type_tag]
54
+ all_metrics << GaugeMetric.for("by_team.violations.count", violation_count, tags)
48
55
 
49
- grouped_outbound_violations = outbound_violations.group_by do |violation|
50
- to_package = ParsePackwerk.find(violation.to_package_name)
51
- if to_package.nil?
52
- raise StandardError, "Could not find matching package #{violation.to_package_name}"
56
+ all_packages.group_by { |package| Private.package_owner(package) }.each do |other_team_name, other_teams_packages|
57
+ violations = other_teams_packages.flat_map(&:violations).select{|v| packages_for_team.map(&:name).include?(v.to_package_name) && v.type == checker.violation_type}
58
+ tags = team_tags + Metrics.tags_for_other_team(other_team_name) + [checker.violation_type_tag]
59
+ all_metrics << GaugeMetric.for("by_team.violations.by_other_team.count", Metrics.file_count(violations), tags)
60
+ end
61
+ else
62
+ T.absurd(direction)
53
63
  end
54
-
55
- Private.package_owner(to_package)
56
- end
57
-
58
- grouped_outbound_violations.each do |to_team_name, violations|
59
- tags = team_tags + Metrics.tags_for_to_team(to_team_name)
60
- all_metrics << GaugeMetric.for('by_team.outbound_dependency_violations.per_team.count', Metrics.file_count(violations.select(&:dependency?)), tags)
61
- all_metrics << GaugeMetric.for('by_team.outbound_privacy_violations.per_team.count', Metrics.file_count(violations.select(&:privacy?)), tags)
62
64
  end
63
65
  end
64
66
 
@@ -8,37 +8,56 @@ module PackStats
8
8
  module Metrics
9
9
  class PackwerkCheckerUsage
10
10
  extend T::Sig
11
-
11
+
12
+ # Some violations (e.g. dependency, visibility, architecture) matter for the referencing (outbound) package.
13
+ # Other violations (e.g. privacy) matter for the referenced (inbound) package.
14
+ class Direction < T::Enum
15
+ enums do
16
+ Inbound = new
17
+ Outbound = new
18
+ end
19
+ end
20
+
12
21
  # Later, we might find a way we can get this directly from `packwerk`
13
22
  class PackwerkChecker < T::Struct
14
- const :setting, String
23
+ extend T::Sig
24
+
25
+ const :key, String
26
+ const :violation_type, String
27
+ const :direction, Direction
28
+
29
+ sig { returns(Tag) }
30
+ def violation_type_tag
31
+ Tag.new(
32
+ key: 'violation_type',
33
+ value: violation_type
34
+ )
35
+ end
15
36
  end
16
37
 
38
+ CHECKERS = T.let([
39
+ PackwerkChecker.new(key: 'enforce_dependencies', violation_type: 'dependency', direction: Direction::Outbound),
40
+ PackwerkChecker.new(key: 'enforce_privacy', violation_type: 'privacy', direction: Direction::Inbound),
41
+ PackwerkChecker.new(key: 'enforce_architecture', violation_type: 'architecture', direction: Direction::Outbound),
42
+ PackwerkChecker.new(key: 'enforce_visibility', violation_type: 'visibility', direction: Direction::Outbound),
43
+ ], T::Array[PackwerkChecker])
44
+
17
45
  sig { params(prefix: String, packages: T::Array[ParsePackwerk::Package], package_tags: T::Array[Tag]).returns(T::Array[GaugeMetric]) }
18
46
  def self.get_checker_metrics(prefix, packages, package_tags)
19
47
  metrics = T.let([], T::Array[GaugeMetric])
20
48
 
21
- checkers = [
22
- PackwerkChecker.new(setting: 'enforce_dependencies'),
23
- PackwerkChecker.new(setting: 'enforce_privacy')
24
- ]
25
-
26
- checkers.each do |checker|
27
- ['false', 'true', 'strict'].each do |enabled_mode|
28
- count_of_packages = ParsePackwerk.all.count do |package|
29
- checker_setting = YAML.load_file(package.yml)[checker.setting]
30
- case enabled_mode
31
- when 'false'
32
- !checker_setting
33
- when 'true'
34
- checker_setting && checker_setting != 'strict'
35
- when 'strict'
36
- checker_setting == 'strict'
37
- end
38
- end
39
-
40
- metric_name = "#{prefix}.packwerk_checkers.#{checker.setting}.#{enabled_mode}.count"
41
- metrics << GaugeMetric.for(metric_name, count_of_packages, package_tags)
49
+ CHECKERS.each do |checker|
50
+ checker_values = packages.map do |package|
51
+ YAML.load_file(package.yml)[checker.key]
52
+ end
53
+
54
+ checker_values_tally = checker_values.map(&:to_s).tally
55
+
56
+ ['false', 'true', 'strict'].each do |possible_value|
57
+ count = checker_values_tally.fetch(possible_value, 0)
58
+ metric_name = "#{prefix}.packwerk_checkers.#{possible_value}.count"
59
+ tags = package_tags + [checker.violation_type_tag]
60
+ metrics << GaugeMetric.for(metric_name, count, tags)
42
61
  end
43
62
  end
44
63
 
@@ -22,8 +22,8 @@ module PackStats
22
22
  end
23
23
 
24
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)]
25
+ def self.tags_for_other_team(team_name)
26
+ [Tag.for('other_team', team_name || Metrics::UNKNOWN_OWNER)]
27
27
  end
28
28
 
29
29
  sig { params(name: String).returns(String) }
@@ -4,51 +4,6 @@ module PackStats
4
4
  module Private
5
5
  extend T::Sig
6
6
 
7
- METRIC_REPLACEMENTS = T.let({
8
- # enforce_dependencies
9
- 'packwerk_checkers.enforce_dependencies.false' => 'prevent_this_package_from_violating_its_stated_dependencies.no',
10
- 'packwerk_checkers.enforce_dependencies.true' => 'prevent_this_package_from_violating_its_stated_dependencies.fail_the_build_if_new_instances_appear',
11
- 'packwerk_checkers.enforce_dependencies.strict' => 'prevent_this_package_from_violating_its_stated_dependencies.fail_the_build_on_any_instances',
12
- # enforce_privacy
13
- 'packwerk_checkers.enforce_privacy.false' => 'prevent_other_packages_from_using_this_packages_internals.no',
14
- 'packwerk_checkers.enforce_privacy.true' => 'prevent_other_packages_from_using_this_packages_internals.fail_the_build_if_new_instances_appear',
15
- 'packwerk_checkers.enforce_privacy.strict' => 'prevent_other_packages_from_using_this_packages_internals.fail_the_build_on_any_instances',
16
- # Packs/TypedPublicApis
17
- 'rubocops.packs_typedpublicapis.false' => 'prevent_this_package_from_exposing_an_untyped_api.no',
18
- 'rubocops.packs_typedpublicapis.true' => 'prevent_this_package_from_exposing_an_untyped_api.fail_the_build_if_new_instances_appear',
19
- 'rubocops.packs_typedpublicapis.strict' => 'prevent_this_package_from_exposing_an_untyped_api.fail_the_build_on_any_instances',
20
- 'rubocops.packs_typedpublicapis.exclusions' => 'prevent_this_package_from_exposing_an_untyped_api.rubocop_exclusions',
21
- # Packs/RootNamespaceIsPackName
22
- 'rubocops.packs_rootnamespaceispackname.false' => 'prevent_this_package_from_creating_other_namespaces.no',
23
- 'rubocops.packs_rootnamespaceispackname.true' => 'prevent_this_package_from_creating_other_namespaces.fail_the_build_if_new_instances_appear',
24
- 'rubocops.packs_rootnamespaceispackname.strict' => 'prevent_this_package_from_creating_other_namespaces.fail_the_build_on_any_instances',
25
- 'rubocops.packs_rootnamespaceispackname.exclusions' => 'prevent_this_package_from_creating_other_namespaces.rubocop_exclusions',
26
- # Packs/ClassMethodsAsPublicApis
27
- 'rubocops.packs_classmethodsaspublicapis.false' => 'prevent_this_package_from_exposing_instance_method_public_apis.no',
28
- 'rubocops.packs_classmethodsaspublicapis.true' => 'prevent_this_package_from_exposing_instance_method_public_apis.fail_the_build_if_new_instances_appear',
29
- 'rubocops.packs_classmethodsaspublicapis.strict' => 'prevent_this_package_from_exposing_instance_method_public_apis.fail_the_build_on_any_instances',
30
- 'rubocops.packs_classmethodsaspublicapis.exclusions' => 'prevent_this_package_from_exposing_instance_method_public_apis.rubocop_exclusions',
31
- # Packs/DocumentedPublicApis
32
- 'rubocops.packs_documentedpublicapis.false' => 'prevent_this_package_from_exposing_undocumented_public_apis.no',
33
- 'rubocops.packs_documentedpublicapis.true' => 'prevent_this_package_from_exposing_undocumented_public_apis.fail_the_build_if_new_instances_appear',
34
- 'rubocops.packs_documentedpublicapis.strict' => 'prevent_this_package_from_exposing_undocumented_public_apis.fail_the_build_on_any_instances',
35
- 'rubocops.packs_documentedpublicapis.exclusions' => 'prevent_this_package_from_exposing_undocumented_public_apis.rubocop_exclusions',
36
- }, T::Hash[String, String])
37
-
38
- sig { params(metrics: T::Array[GaugeMetric]).returns(T::Array[GaugeMetric]) }
39
- def self.convert_metrics_to_legacy(metrics)
40
- metrics.map do |metric|
41
- new_metric = metric
42
- METRIC_REPLACEMENTS.each do |current_name, legacy_name|
43
- new_metric = new_metric.with(
44
- name: new_metric.name.gsub(current_name, legacy_name)
45
- )
46
- end
47
-
48
- new_metric
49
- end
50
- end
51
-
52
7
  sig { params(package: ParsePackwerk::Package).returns(T.nilable(String) )}
53
8
  def self.package_owner(package)
54
9
  pack = Packs.find(package.name)
data/lib/pack_stats.rb CHANGED
@@ -39,8 +39,6 @@ module PackStats
39
39
  # See note on get_metrics
40
40
  packaged_source_code_locations: T.nilable(T::Array[Pathname]),
41
41
  # See note on get_metrics
42
- use_gusto_legacy_names: T::Boolean,
43
- # See note on get_metrics
44
42
  max_enforcements_tag_value: T::Boolean
45
43
  ).void
46
44
  end
@@ -52,7 +50,6 @@ module PackStats
52
50
  report_time: Time.now, # rubocop:disable Rails/TimeZone
53
51
  verbose: false,
54
52
  packaged_source_code_locations: [],
55
- use_gusto_legacy_names: false,
56
53
  max_enforcements_tag_value: false
57
54
  )
58
55
 
@@ -60,7 +57,6 @@ module PackStats
60
57
  source_code_pathnames: source_code_pathnames,
61
58
  componentized_source_code_locations: componentized_source_code_locations,
62
59
  app_name: app_name,
63
- use_gusto_legacy_names: use_gusto_legacy_names,
64
60
  max_enforcements_tag_value: max_enforcements_tag_value,
65
61
  )
66
62
 
@@ -71,10 +67,6 @@ module PackStats
71
67
  end
72
68
  end
73
69
 
74
- if use_gusto_legacy_names
75
- all_metrics = Private.convert_metrics_to_legacy(all_metrics)
76
- end
77
-
78
70
  Private::DatadogReporter.report!(
79
71
  datadog_client: datadog_client,
80
72
  report_time: report_time,
@@ -89,11 +81,6 @@ module PackStats
89
81
  app_name: String,
90
82
  # This field is deprecated
91
83
  packaged_source_code_locations: T.nilable(T::Array[Pathname]),
92
- # It is not recommended to set this to true.
93
- # Gusto uses this to preserve historical trends in Dashboards as the names of
94
- # things changed, but new dashboards can use names that better match current tooling conventions.
95
- # The behavior of setting this parameter to true might change without warning
96
- use_gusto_legacy_names: T::Boolean,
97
84
  # You can set this to `true` to tag all metrics with `max_enforcements:true`.
98
85
  # This is useful if you want to submit two sets of metrics:
99
86
  # Once with the violation counts as configured in the app
@@ -106,25 +93,18 @@ module PackStats
106
93
  componentized_source_code_locations:,
107
94
  app_name:,
108
95
  packaged_source_code_locations: [],
109
- use_gusto_legacy_names: false,
110
96
  max_enforcements_tag_value: false
111
97
  )
112
98
 
113
99
  GaugeMetric.set_max_enforcements_tag(max_enforcements_tag_value)
114
100
 
115
- all_metrics = Private::DatadogReporter.get_metrics(
101
+ Private::DatadogReporter.get_metrics(
116
102
  source_code_files: source_code_files(
117
103
  source_code_pathnames: source_code_pathnames,
118
104
  componentized_source_code_locations: componentized_source_code_locations,
119
105
  ),
120
106
  app_name: app_name
121
107
  )
122
-
123
- if use_gusto_legacy_names
124
- all_metrics = Private.convert_metrics_to_legacy(all_metrics)
125
- end
126
-
127
- all_metrics
128
108
  end
129
109
 
130
110
  sig do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pack_stats
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.1.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: 2023-05-05 00:00:00.000000000 Z
11
+ date: 2023-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: code_teams
@@ -192,6 +192,7 @@ files:
192
192
  - lib/pack_stats/private.rb
193
193
  - lib/pack_stats/private/datadog_reporter.rb
194
194
  - lib/pack_stats/private/metrics.rb
195
+ - lib/pack_stats/private/metrics/dependencies.rb
195
196
  - lib/pack_stats/private/metrics/files.rb
196
197
  - lib/pack_stats/private/metrics/packages.rb
197
198
  - lib/pack_stats/private/metrics/packages_by_team.rb