pack_stats 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 257c72796b9a65f1d67e0f6f96998fcd2929717ff09526a95e4bea8f144bb903
4
- data.tar.gz: 79cf5e5557eb13cd286a2724e1c7b9fdb9308845830c3327ec9d99e6a0c4b27a
3
+ metadata.gz: 8053c602af521d61db8a3e333a61b30b5ee7443a96cc45f2932c77b13f34a2df
4
+ data.tar.gz: f64229355da5b0f4d398bbcc7a6810f638170574539b05e1b5195bfae3d9f85d
5
5
  SHA512:
6
- metadata.gz: 8456c2d22795b7ac537a4d64dc4e52607b44642f2a7e1af6d059863de5805ce1ad8f735ff5122f2ac876632ea6bef1bbbaab58c458b310a74dbb2b2083aa4698
7
- data.tar.gz: 42d5b073d4771726a0f15b8622894e07c58b348d571a5b1ca3b0e478e5b8580fb37a4b88922ed18951fbcb9c3f6d6f482e0435b93350c2b820d87e10aabd1726
6
+ metadata.gz: 6ffd883823bba83d22f392f3cacc5dbd9e54264fc69bb48c489656f5cc01c3c202d22773a03fc446568c65027eb2d3f95c3a5f709eafa22acedd5c27f8c5fb49
7
+ data.tar.gz: 50af4cdd4389c7588d6de7b8eecb2f94697e4cd372578d58916edbbd6ca602a1634e3f985a6243e8630d5cc14b74383bb671134fa9ceca8631d4fbf113497f4d
@@ -1,4 +1,5 @@
1
1
  # typed: strict
2
+ # frozen_string_literal: true
2
3
 
3
4
  module PackStats
4
5
  class GaugeMetric < T::Struct
@@ -14,20 +15,20 @@ module PackStats
14
15
  # https://docs.datadoghq.com/metrics/custom_metrics/#naming-custom-metrics
15
16
  # Metric names must not exceed 200 characters. Fewer than 100 is preferred from a UI perspective
16
17
  if name.length > 200
17
- raise StandardError.new("Metrics names must not exceed 200 characters: #{name}") # rubocop:disable Style/RaiseArgs
18
+ raise StandardError, "Metrics names must not exceed 200 characters: #{name}"
18
19
  end
19
20
 
20
21
  all_tags = [*tags, max_enforcements_tag]
21
22
  new(
22
- name: name,
23
- count: count,
23
+ name:,
24
+ count:,
24
25
  tags: all_tags
25
26
  )
26
27
  end
27
28
 
28
29
  sig { returns(String) }
29
30
  def to_s
30
- "#{name} with count #{count}, with tags #{tags.map(&:to_s).join(', ')}"
31
+ "#{name} with count #{count}, with tags #{tags.join(", ")}"
31
32
  end
32
33
 
33
34
  sig { params(other: GaugeMetric).returns(T::Boolean) }
@@ -40,12 +41,12 @@ module PackStats
40
41
  sig { params(tag_value: T::Boolean).void }
41
42
  def self.set_max_enforcements_tag(tag_value)
42
43
  @max_enforcements_tag = T.let(@max_enforcements_tag, T.nilable(Tag))
43
- @max_enforcements_tag = Tag.new(key: 'max_enforcements', value: tag_value ? 'true' : 'false')
44
+ @max_enforcements_tag = Tag.new(key: "max_enforcements", value: tag_value ? "true" : "false")
44
45
  end
45
46
 
46
47
  sig { returns(Tag) }
47
48
  def self.max_enforcements_tag
48
- @max_enforcements_tag || Tag.new(key: 'max_enforcements', value: 'false')
49
+ @max_enforcements_tag || Tag.new(key: "max_enforcements", value: "false")
49
50
  end
50
51
  end
51
52
  end
@@ -1,14 +1,14 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'dogapi'
5
- require 'pack_stats/private/metrics'
6
- require 'pack_stats/private/metrics/files'
7
- require 'pack_stats/private/metrics/public_usage'
8
- require 'pack_stats/private/metrics/packwerk_checker_usage'
9
- require 'pack_stats/private/metrics/dependencies'
10
- require 'pack_stats/private/metrics/packages'
11
- require 'pack_stats/private/metrics/packages_by_team'
4
+ require "dogapi"
5
+ require "pack_stats/private/metrics"
6
+ require "pack_stats/private/metrics/files"
7
+ require "pack_stats/private/metrics/public_usage"
8
+ require "pack_stats/private/metrics/packwerk_checker_usage"
9
+ require "pack_stats/private/metrics/dependencies"
10
+ require "pack_stats/private/metrics/packages"
11
+ require "pack_stats/private/metrics/packages_by_team"
12
12
 
13
13
  module PackStats
14
14
  module Private
@@ -19,7 +19,8 @@ module PackStats
19
19
  params(
20
20
  source_code_files: T::Array[SourceCodeFile],
21
21
  app_name: String
22
- ).returns(T::Array[GaugeMetric])
22
+ )
23
+ .returns(T::Array[GaugeMetric])
23
24
  end
24
25
  def self.get_metrics(source_code_files:, app_name:)
25
26
  packages = ParsePackwerk.all
@@ -27,7 +28,7 @@ module PackStats
27
28
  [
28
29
  *Metrics::Files.get_metrics(source_code_files, app_name),
29
30
  *Metrics::Packages.get_package_metrics(packages, app_name),
30
- *Metrics::PackagesByTeam.get_package_metrics_by_team(packages, app_name),
31
+ *Metrics::PackagesByTeam.get_package_metrics_by_team(packages, app_name)
31
32
  ]
32
33
  end
33
34
 
@@ -38,7 +39,8 @@ module PackStats
38
39
  # to ensure they fall into the same bucket.
39
40
  report_time: Time,
40
41
  metrics: T::Array[GaugeMetric]
41
- ).void
42
+ )
43
+ .void
42
44
  end
43
45
  def self.report!(datadog_client:, report_time:, metrics:)
44
46
  #
@@ -49,7 +51,12 @@ module PackStats
49
51
  metrics.each_slice(1000).each do |metric_slice|
50
52
  datadog_client.batch_metrics do
51
53
  metric_slice.each do |metric|
52
- datadog_client.emit_points(metric.name, [[report_time, metric.count]], type: 'gauge', tags: metric.tags.map(&:to_s))
54
+ datadog_client.emit_points(
55
+ metric.name,
56
+ [[report_time, metric.count]],
57
+ type: "gauge",
58
+ tags: metric.tags.map(&:to_s)
59
+ )
53
60
  end
54
61
  end
55
62
  end
@@ -7,8 +7,12 @@ module PackStats
7
7
  class Dependencies
8
8
  extend T::Sig
9
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)
10
+ sig do
11
+ params(_prefix: String, packages: T::Array[ParsePackwerk::Package], app_name: String).returns(
12
+ T::Array[GaugeMetric]
13
+ )
14
+ end
15
+ def self.get_metrics(_prefix, packages, app_name)
12
16
  all_metrics = T.let([], T::Array[GaugeMetric])
13
17
  inbound_explicit_dependency_by_package = {}
14
18
  packages.each do |package|
@@ -18,7 +22,7 @@ module PackStats
18
22
  end
19
23
  end
20
24
 
21
- packages.each do |package| # rubocop:disable Style/CombinableLoops
25
+ packages.each do |package|
22
26
  package_tags = Metrics.tags_for_package(package, app_name)
23
27
 
24
28
  #
@@ -31,12 +35,19 @@ module PackStats
31
35
  end
32
36
 
33
37
  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)
38
+ tags = package_tags +
39
+ [Tag.for("other_package", Metrics.humanized_package_name(explicit_dependency))] +
40
+ Metrics.tags_for_other_team(owner)
41
+ all_metrics << GaugeMetric.for("by_package.dependencies.by_other_package.count", 1, tags)
36
42
  end
37
43
 
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)
44
+ all_metrics << GaugeMetric.for("by_package.dependencies.count", package.dependencies.count, package_tags)
45
+ all_metrics <<
46
+ GaugeMetric.for(
47
+ "by_package.depended_on.count",
48
+ inbound_explicit_dependency_by_package[package.name]&.count || 0,
49
+ package_tags
50
+ )
40
51
  end
41
52
 
42
53
  all_metrics
@@ -11,19 +11,20 @@ module PackStats
11
11
  params(
12
12
  source_code_files: T::Array[SourceCodeFile],
13
13
  app_name: String
14
- ).returns(T::Array[GaugeMetric])
14
+ )
15
+ .returns(T::Array[GaugeMetric])
15
16
  end
16
17
  def self.get_metrics(source_code_files, app_name)
17
18
  all_metrics = T.let([], T::Array[GaugeMetric])
18
- app_level_tag = Tag.for('app', app_name)
19
+ app_level_tag = Tag.for("app", app_name)
19
20
 
20
21
  source_code_files.group_by { |file| file.team_owner&.name }.each do |team_name, files_for_team|
21
22
  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
+ all_metrics += get_file_metrics("by_team", file_tags, files_for_team)
23
24
  end
24
25
 
25
26
  file_tags = [app_level_tag]
26
- all_metrics += get_file_metrics('totals', file_tags, source_code_files)
27
+ all_metrics += get_file_metrics("totals", file_tags, source_code_files)
27
28
  all_metrics
28
29
  end
29
30
 
@@ -32,13 +33,14 @@ module PackStats
32
33
  metric_name_suffix: String,
33
34
  tags: T::Array[Tag],
34
35
  files: T::Array[SourceCodeFile]
35
- ).returns(T::Array[GaugeMetric])
36
+ )
37
+ .returns(T::Array[GaugeMetric])
36
38
  end
37
39
  def self.get_file_metrics(metric_name_suffix, tags, files)
38
40
  [
39
41
  GaugeMetric.for("component_files.#{metric_name_suffix}", files.count(&:componentized_file?), tags),
40
42
  GaugeMetric.for("packaged_files.#{metric_name_suffix}", files.count(&:packaged_file?), tags),
41
- GaugeMetric.for("all_files.#{metric_name_suffix}", files.count, tags),
43
+ GaugeMetric.for("all_files.#{metric_name_suffix}", files.count, tags)
42
44
  ]
43
45
  end
44
46
  end
@@ -11,33 +11,52 @@ module PackStats
11
11
  params(
12
12
  packages: T::Array[ParsePackwerk::Package],
13
13
  app_name: String
14
- ).returns(T::Array[GaugeMetric])
14
+ )
15
+ .returns(T::Array[GaugeMetric])
15
16
  end
16
17
  def self.get_package_metrics(packages, app_name)
17
18
  all_metrics = []
18
- app_level_tag = Tag.for('app', app_name)
19
+ app_level_tag = Tag.for("app", app_name)
19
20
  package_tags = T.let([app_level_tag], T::Array[Tag])
20
21
 
21
- all_metrics << GaugeMetric.for('all_packages.count', packages.count, package_tags)
22
- all_metrics << GaugeMetric.for('all_packages.dependencies.count', packages.sum { |package| package.dependencies.count }, package_tags)
22
+ all_metrics << GaugeMetric.for("all_packages.count", packages.count, package_tags)
23
+ all_metrics <<
24
+ GaugeMetric.for(
25
+ "all_packages.dependencies.count",
26
+ packages.sum { |package| package.dependencies.count },
27
+ package_tags
28
+ )
23
29
 
24
30
  PackwerkCheckerUsage::CHECKERS.each do |checker|
25
- violation_count = packages.sum { |package| Metrics.file_count(package.violations.select{|v| v.type == checker.violation_type}) }
31
+ violation_count = packages.sum do |package|
32
+ Metrics.file_count(package.violations.select { |v| v.type == checker.violation_type })
33
+ end
34
+
26
35
  tags = package_tags + [checker.violation_type_tag]
27
36
  all_metrics << GaugeMetric.for("all_packages.violations.count", violation_count, tags)
28
37
  end
29
38
 
30
- all_metrics += Metrics::PublicUsage.get_public_usage_metrics('all_packages', packages, package_tags)
31
- all_metrics << GaugeMetric.for('all_packages.has_readme.count', packages.count { |package| Metrics.has_readme?(package) }, package_tags)
32
-
33
- all_metrics += Metrics::PackwerkCheckerUsage.get_checker_metrics('all_packages', packages, package_tags)
34
- all_metrics << GaugeMetric.for('all_packages.package_based_file_ownership.count', packages.count { |package| !package.metadata['owner'].nil? }, package_tags)
39
+ all_metrics += Metrics::PublicUsage.get_public_usage_metrics("all_packages", packages, package_tags)
40
+ all_metrics <<
41
+ GaugeMetric.for(
42
+ "all_packages.has_readme.count",
43
+ packages.count { |package| Metrics.has_readme?(package) },
44
+ package_tags
45
+ )
46
+
47
+ all_metrics += Metrics::PackwerkCheckerUsage.get_checker_metrics("all_packages", packages, package_tags)
48
+ all_metrics <<
49
+ GaugeMetric.for(
50
+ "all_packages.package_based_file_ownership.count",
51
+ packages.count { |package| !package.metadata["owner"].nil? },
52
+ package_tags
53
+ )
35
54
 
36
55
  inbound_violations_by_package = packages.flat_map(&:violations).group_by(&:to_package_name)
37
56
 
38
57
  packages.each do |package|
39
58
  package_tags = Metrics.tags_for_package(package, app_name)
40
- all_metrics += Metrics::PublicUsage.get_public_usage_metrics('by_package', [package], package_tags)
59
+ all_metrics += Metrics::PublicUsage.get_public_usage_metrics("by_package", [package], package_tags)
41
60
 
42
61
  outbound_violations = package.violations
43
62
  inbound_violations = inbound_violations_by_package[package.name] || []
@@ -47,32 +66,45 @@ module PackStats
47
66
 
48
67
  case direction
49
68
  when PackwerkCheckerUsage::Direction::Outbound
50
- all_violations_of_type = outbound_violations.select { |v| v.type == checker.violation_type }
69
+ all_violations_of_type = outbound_violations.select { |v| v.type == checker.violation_type }
51
70
 
52
71
  packages.each do |other_package|
53
- violations = package.violations.select{|v| v.to_package_name == other_package.name && v.type == checker.violation_type }
72
+ violations = package.violations.select do |v|
73
+ v.to_package_name == other_package.name && v.type == checker.violation_type
74
+ end
54
75
 
55
- tags = package_tags + [
56
- Tag.for('other_package', Metrics.humanized_package_name(other_package.name)),
57
- *Metrics.tags_for_other_team(Private.package_owner(other_package)),
58
- checker.violation_type_tag
59
- ]
76
+ tags = package_tags +
77
+ [
78
+ Tag.for("other_package", Metrics.humanized_package_name(other_package.name)),
79
+ *Metrics.tags_for_other_team(Private.package_owner(other_package)),
80
+ checker.violation_type_tag
81
+ ]
60
82
 
61
83
  count = Metrics.file_count(violations)
62
84
  if count > 0
63
- all_metrics << GaugeMetric.for("by_package.violations.by_other_package.count", Metrics.file_count(violations), tags)
85
+ all_metrics <<
86
+ GaugeMetric.for(
87
+ "by_package.violations.by_other_package.count",
88
+ Metrics.file_count(violations),
89
+ tags
90
+ )
64
91
  end
65
92
  end
93
+
66
94
  when PackwerkCheckerUsage::Direction::Inbound
67
- all_violations_of_type = inbound_violations.select { |v| v.type == checker.violation_type }
95
+ all_violations_of_type = inbound_violations.select { |v| v.type == checker.violation_type }
68
96
 
69
97
  packages.each do |other_package|
70
- violations = other_package.violations.select{|v| v.to_package_name == package.name && v.type == checker.violation_type }
71
- tags = package_tags + [
72
- Tag.for('other_package', Metrics.humanized_package_name(other_package.name)),
73
- *Metrics.tags_for_other_team(Private.package_owner(other_package)),
74
- checker.violation_type_tag
75
- ]
98
+ violations = other_package.violations.select do |v|
99
+ v.to_package_name == package.name && v.type == checker.violation_type
100
+ end
101
+
102
+ tags = package_tags +
103
+ [
104
+ Tag.for("other_package", Metrics.humanized_package_name(other_package.name)),
105
+ *Metrics.tags_for_other_team(Private.package_owner(other_package)),
106
+ checker.violation_type_tag
107
+ ]
76
108
 
77
109
  count = Metrics.file_count(violations)
78
110
  if count > 0
@@ -84,11 +116,12 @@ module PackStats
84
116
  end
85
117
 
86
118
  tags = package_tags + [checker.violation_type_tag]
87
- all_metrics << GaugeMetric.for("by_package.violations.count", Metrics.file_count(all_violations_of_type), tags)
119
+ all_metrics <<
120
+ GaugeMetric.for("by_package.violations.count", Metrics.file_count(all_violations_of_type), tags)
88
121
  end
89
122
  end
90
123
 
91
- all_metrics += Metrics::Dependencies.get_metrics('by_package', packages, app_name)
124
+ all_metrics += Metrics::Dependencies.get_metrics("by_package", packages, app_name)
92
125
 
93
126
  all_metrics
94
127
  end
@@ -11,59 +11,83 @@ module PackStats
11
11
  params(
12
12
  all_packages: T::Array[ParsePackwerk::Package],
13
13
  app_name: String
14
- ).returns(T::Array[GaugeMetric])
14
+ )
15
+ .returns(T::Array[GaugeMetric])
15
16
  end
16
17
  def self.get_package_metrics_by_team(all_packages, app_name)
17
18
  all_metrics = T.let([], T::Array[GaugeMetric])
18
- app_level_tag = Tag.for('app', app_name)
19
+ app_level_tag = Tag.for("app", app_name)
19
20
 
20
-
21
21
  all_packages.group_by { |package| Private.package_owner(package) }.each do |team_name, packages_for_team|
22
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)
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 <<
27
+ GaugeMetric.for(
28
+ "by_team.has_readme.count",
29
+ packages_for_team.count { |package| Metrics.has_readme?(package) },
30
+ team_tags
31
+ )
27
32
 
28
33
  outbound_violations = packages_for_team.flat_map(&:violations)
29
34
  # We look at `all_packages` because we care about ALL inbound violations across all teams
30
35
  inbound_violations_by_package = all_packages.flat_map(&:violations).group_by(&:to_package_name)
31
36
  # 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] || [] }
37
+ inbound_violations = packages_for_team.flat_map do |package|
38
+ inbound_violations_by_package[package.name] || []
39
+ end
33
40
 
34
41
  PackwerkCheckerUsage::CHECKERS.each do |checker|
35
42
  direction = checker.direction
36
43
  case direction
37
44
  when PackwerkCheckerUsage::Direction::Outbound
38
- all_violations_of_type = outbound_violations.select { |v| v.type == checker.violation_type }
45
+ outbound_violations.select { |v| v.type == checker.violation_type }
46
+
47
+ violation_count = packages_for_team.sum do |package|
48
+ Metrics.file_count(package.violations.select { |v| v.type == checker.violation_type })
49
+ end
39
50
 
40
- violation_count = packages_for_team.sum { |package| Metrics.file_count(package.violations.select{|v| v.type == checker.violation_type}) }
41
51
  tags = team_tags + [checker.violation_type_tag]
42
52
  all_metrics << GaugeMetric.for("by_team.violations.count", violation_count, tags)
43
53
 
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
- count = Metrics.file_count(violations)
48
- if count > 0
49
- all_metrics << GaugeMetric.for("by_team.violations.by_other_team.count", count, tags)
54
+ all_packages
55
+ .group_by { |package| Private.package_owner(package) }
56
+ .each do |other_team_name, other_teams_packages|
57
+ violations = outbound_violations.select do |v|
58
+ other_teams_packages.map(&:name).include?(v.to_package_name) && v.type == checker.violation_type
59
+ end
60
+
61
+ tags = team_tags + Metrics.tags_for_other_team(other_team_name) + [checker.violation_type_tag]
62
+ count = Metrics.file_count(violations)
63
+ if count > 0
64
+ all_metrics << GaugeMetric.for("by_team.violations.by_other_team.count", count, tags)
65
+ end
50
66
  end
51
- end
67
+
52
68
  when PackwerkCheckerUsage::Direction::Inbound
53
- all_violations_of_type = inbound_violations.select { |v| v.type == checker.violation_type }
69
+ inbound_violations.select { |v| v.type == checker.violation_type }
70
+
71
+ violation_count = packages_for_team.sum do |package|
72
+ Metrics.file_count(package.violations.select { |v| v.type == checker.violation_type })
73
+ end
54
74
 
55
- violation_count = packages_for_team.sum { |package| Metrics.file_count(package.violations.select{|v| v.type == checker.violation_type}) }
56
75
  tags = team_tags + [checker.violation_type_tag]
57
76
  all_metrics << GaugeMetric.for("by_team.violations.count", violation_count, tags)
58
77
 
59
- all_packages.group_by { |package| Private.package_owner(package) }.each do |other_team_name, other_teams_packages|
60
- violations = other_teams_packages.flat_map(&:violations).select{|v| packages_for_team.map(&:name).include?(v.to_package_name) && v.type == checker.violation_type}
61
- tags = team_tags + Metrics.tags_for_other_team(other_team_name) + [checker.violation_type_tag]
62
- count = Metrics.file_count(violations)
63
- if count > 0
64
- all_metrics << GaugeMetric.for("by_team.violations.by_other_team.count", count, tags)
78
+ all_packages
79
+ .group_by { |package| Private.package_owner(package) }
80
+ .each do |other_team_name, other_teams_packages|
81
+ violations = other_teams_packages.flat_map(&:violations).select do |v|
82
+ packages_for_team.map(&:name).include?(v.to_package_name) && v.type == checker.violation_type
83
+ end
84
+
85
+ tags = team_tags + Metrics.tags_for_other_team(other_team_name) + [checker.violation_type_tag]
86
+ count = Metrics.file_count(violations)
87
+ if count > 0
88
+ all_metrics << GaugeMetric.for("by_team.violations.by_other_team.count", count, tags)
89
+ end
65
90
  end
66
- end
67
91
  else
68
92
  T.absurd(direction)
69
93
  end
@@ -1,8 +1,6 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'rubocop-packs'
5
-
6
4
  module PackStats
7
5
  module Private
8
6
  module Metrics
@@ -29,22 +27,39 @@ module PackStats
29
27
  sig { returns(Tag) }
30
28
  def violation_type_tag
31
29
  Tag.new(
32
- key: 'violation_type',
30
+ key: "violation_type",
33
31
  value: violation_type
34
32
  )
35
33
  end
36
34
  end
37
35
 
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_layers', violation_type: 'layer', direction: Direction::Outbound),
42
- PackwerkChecker.new(key: 'enforce_visibility', violation_type: 'visibility', direction: Direction::Outbound),
43
- ], T::Array[PackwerkChecker])
36
+ CHECKERS = T.let(
37
+ [
38
+ PackwerkChecker.new(
39
+ key: "enforce_dependencies",
40
+ violation_type: "dependency",
41
+ direction: Direction::Outbound
42
+ ),
43
+ PackwerkChecker.new(key: "enforce_privacy", violation_type: "privacy", direction: Direction::Inbound),
44
+ PackwerkChecker.new(
45
+ key: "enforce_folder_privacy",
46
+ violation_type: "folder_privacy",
47
+ direction: Direction::Inbound
48
+ ),
49
+ PackwerkChecker.new(key: "enforce_layers", violation_type: "layer", direction: Direction::Outbound),
50
+ PackwerkChecker.new(key: "enforce_visibility", violation_type: "visibility", direction: Direction::Outbound)
51
+ ].freeze,
52
+ T::Array[PackwerkChecker]
53
+ )
44
54
 
45
- sig { params(prefix: String, packages: T::Array[ParsePackwerk::Package], package_tags: T::Array[Tag]).returns(T::Array[GaugeMetric]) }
55
+ sig do
56
+ params(prefix: String, packages: T::Array[ParsePackwerk::Package], package_tags: T::Array[Tag]).returns(
57
+ T::Array[GaugeMetric]
58
+ )
59
+ end
46
60
  def self.get_checker_metrics(prefix, packages, package_tags)
47
61
  metrics = T.let([], T::Array[GaugeMetric])
62
+ possible_values = ["false", "true", "strict"]
48
63
 
49
64
  CHECKERS.each do |checker|
50
65
  checker_values = packages.map do |package|
@@ -53,7 +68,7 @@ module PackStats
53
68
 
54
69
  checker_values_tally = checker_values.map(&:to_s).tally
55
70
 
56
- ['false', 'true', 'strict'].each do |possible_value|
71
+ possible_values.each do |possible_value|
57
72
  count = checker_values_tally.fetch(possible_value, 0)
58
73
  metric_name = "#{prefix}.packwerk_checkers.#{possible_value}.count"
59
74
  tags = package_tags + [checker.violation_type_tag]
@@ -7,17 +7,21 @@ module PackStats
7
7
  class PublicUsage
8
8
  extend T::Sig
9
9
 
10
- sig { params(prefix: String, packages: T::Array[ParsePackwerk::Package], package_tags: T::Array[Tag]).returns(T::Array[GaugeMetric]) }
10
+ sig do
11
+ params(prefix: String, packages: T::Array[ParsePackwerk::Package], package_tags: T::Array[Tag]).returns(
12
+ T::Array[GaugeMetric]
13
+ )
14
+ end
11
15
  def self.get_public_usage_metrics(prefix, packages, package_tags)
12
16
  packages_except_for_root = packages.reject { |package| package.name == ParsePackwerk::ROOT_PACKAGE_NAME }
13
17
  all_files = packages_except_for_root.flat_map do |package|
14
- package.directory.glob('**/**.rb')
18
+ package.directory.glob("**/**.rb")
15
19
  end
16
20
 
17
21
  all_public_files = T.let([], T::Array[Pathname])
18
22
  is_using_public_directory = 0
19
23
  packages_except_for_root.each do |package|
20
- public_files = package.directory.glob('app/public/**/**.rb')
24
+ public_files = package.directory.glob("app/public/**/**.rb")
21
25
  all_public_files += public_files
22
26
  is_using_public_directory += 1 if public_files.any?
23
27
  end
@@ -27,7 +31,7 @@ module PackStats
27
31
  [
28
32
  GaugeMetric.for("#{prefix}.all_files.count", all_files.count, package_tags),
29
33
  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),
34
+ GaugeMetric.for("#{prefix}.using_public_directory.count", is_using_public_directory, package_tags)
31
35
  ]
32
36
  end
33
37
  end
@@ -5,25 +5,25 @@ module PackStats
5
5
  module Private
6
6
  module Metrics
7
7
  extend T::Sig
8
- UNKNOWN_OWNER = T.let('Unknown', String)
8
+
9
+ UNKNOWN_OWNER = "Unknown"
9
10
 
10
11
  sig { params(team_name: T.nilable(String)).returns(T::Array[Tag]) }
11
12
  def self.tags_for_team(team_name)
12
- [Tag.for('team', team_name || UNKNOWN_OWNER)]
13
+ [Tag.for("team", team_name || UNKNOWN_OWNER)]
13
14
  end
14
15
 
15
16
  sig { params(package: ParsePackwerk::Package, app_name: String).returns(T::Array[Tag]) }
16
17
  def self.tags_for_package(package, app_name)
17
-
18
18
  tags = [
19
- Tag.new(key: 'package', value: humanized_package_name(package.name)),
20
- Tag.new(key: 'app', value: app_name),
21
- *Metrics.tags_for_team(Private.package_owner(package)),
19
+ Tag.new(key: "package", value: humanized_package_name(package.name)),
20
+ Tag.new(key: "app", value: app_name),
21
+ *Metrics.tags_for_team(Private.package_owner(package))
22
22
  ]
23
23
 
24
- layer = package.config['layer']
24
+ layer = package.config["layer"]
25
25
  if layer
26
- tags << Tag.new(key: 'layer', value: layer)
26
+ tags << Tag.new(key: "layer", value: layer)
27
27
  end
28
28
 
29
29
  tags
@@ -31,13 +31,13 @@ module PackStats
31
31
 
32
32
  sig { params(team_name: T.nilable(String)).returns(T::Array[Tag]) }
33
33
  def self.tags_for_other_team(team_name)
34
- [Tag.for('other_team', team_name || Metrics::UNKNOWN_OWNER)]
34
+ [Tag.for("other_team", team_name || Metrics::UNKNOWN_OWNER)]
35
35
  end
36
36
 
37
37
  sig { params(name: String).returns(String) }
38
38
  def self.humanized_package_name(name)
39
39
  if name == ParsePackwerk::ROOT_PACKAGE_NAME
40
- 'root'
40
+ "root"
41
41
  else
42
42
  name
43
43
  end
@@ -50,7 +50,7 @@ module PackStats
50
50
 
51
51
  sig { params(package: ParsePackwerk::Package).returns(T::Boolean) }
52
52
  def self.has_readme?(package)
53
- package.directory.join('README.md').exist?
53
+ package.directory.join("README.md").exist?
54
54
  end
55
55
  end
56
56
  end
@@ -1,4 +1,5 @@
1
1
  # typed: strict
2
+ # frozen_string_literal: true
2
3
 
3
4
  module PackStats
4
5
  module Private
@@ -1,13 +1,15 @@
1
1
  # typed: strict
2
+ # frozen_string_literal: true
2
3
 
3
4
  module PackStats
4
5
  module Private
5
6
  extend T::Sig
6
7
 
7
- sig { params(package: ParsePackwerk::Package).returns(T.nilable(String) )}
8
+ sig { params(package: ParsePackwerk::Package).returns(T.nilable(String)) }
8
9
  def self.package_owner(package)
9
10
  pack = Packs.find(package.name)
10
11
  return nil if pack.nil?
12
+
11
13
  CodeOwnership.for_package(pack)&.name
12
14
  end
13
15
  end
@@ -1,8 +1,10 @@
1
1
  # typed: strict
2
+ # frozen_string_literal: true
2
3
 
3
4
  module PackStats
4
5
  class Tag < T::Struct
5
6
  extend T::Sig
7
+
6
8
  const :key, String
7
9
  const :value, String
8
10
 
@@ -14,8 +16,8 @@ module PackStats
14
16
  sig { params(key: String, value: String).returns(Tag) }
15
17
  def self.for(key, value)
16
18
  new(
17
- key: key,
18
- value: value
19
+ key:,
20
+ value:
19
21
  )
20
22
  end
21
23
 
@@ -1,4 +1,5 @@
1
1
  # typed: strict
2
+ # frozen_string_literal: true
2
3
 
3
4
  module PackStats
4
5
  module Tags
@@ -7,12 +8,12 @@ module PackStats
7
8
  sig { params(colon_delimited_tag_strings: T::Array[String]).returns(T::Array[Tag]) }
8
9
  def self.for(colon_delimited_tag_strings)
9
10
  colon_delimited_tag_strings.map do |colon_delimited_tag_string|
10
- key, value = colon_delimited_tag_string.split(':')
11
+ key, value = colon_delimited_tag_string.split(":")
11
12
  raise StandardError, "Improperly formatted tag `#{colon_delimited_tag_string}`" if key.nil? || value.nil?
12
13
 
13
14
  Tag.new(
14
- key: key,
15
- value: value
15
+ key:,
16
+ value:
16
17
  )
17
18
  end
18
19
  end
data/lib/pack_stats.rb CHANGED
@@ -1,31 +1,33 @@
1
1
  # typed: strict
2
-
3
- require 'sorbet-runtime'
4
- require 'json'
5
- require 'yaml'
6
- require 'benchmark'
7
- require 'code_teams'
8
- require 'code_ownership'
9
- require 'pathname'
10
- require 'packs'
11
- require 'pack_stats/private'
12
- require 'pack_stats/private/source_code_file'
13
- require 'pack_stats/private/datadog_reporter'
14
- require 'parse_packwerk'
15
- require 'pack_stats/tag'
16
- require 'pack_stats/tags'
17
- require 'pack_stats/gauge_metric'
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+ require "json"
6
+ require "yaml"
7
+ require "benchmark"
8
+ require "code_teams"
9
+ require "code_ownership"
10
+ require "pathname"
11
+ require "packs"
12
+ require "pack_stats/private"
13
+ require "pack_stats/private/source_code_file"
14
+ require "pack_stats/private/datadog_reporter"
15
+ require "parse_packwerk"
16
+ require "pack_stats/tag"
17
+ require "pack_stats/tags"
18
+ require "pack_stats/gauge_metric"
18
19
 
19
20
  module PackStats
20
21
  extend T::Sig
21
22
 
22
- ROOT_PACKAGE_NAME = T.let('root'.freeze, String)
23
+ ROOT_PACKAGE_NAME = "root"
23
24
 
24
25
  DEFAULT_COMPONENTIZED_SOURCE_CODE_LOCATIONS = T.let(
25
26
  [
26
- Pathname.new('components'),
27
- Pathname.new('gems'),
28
- ].freeze, T::Array[Pathname]
27
+ Pathname.new("components"),
28
+ Pathname.new("gems")
29
+ ].freeze,
30
+ T::Array[Pathname]
29
31
  )
30
32
 
31
33
  sig do
@@ -40,36 +42,36 @@ module PackStats
40
42
  packaged_source_code_locations: T.nilable(T::Array[Pathname]),
41
43
  # See note on get_metrics
42
44
  max_enforcements_tag_value: T::Boolean
43
- ).void
45
+ )
46
+ .void
44
47
  end
45
48
  def self.report_to_datadog!(
46
49
  datadog_client:,
47
50
  app_name:,
48
51
  source_code_pathnames:,
49
52
  componentized_source_code_locations: DEFAULT_COMPONENTIZED_SOURCE_CODE_LOCATIONS,
50
- report_time: Time.now, # rubocop:disable Rails/TimeZone
53
+ report_time: Time.now,
51
54
  verbose: false,
52
55
  packaged_source_code_locations: [],
53
56
  max_enforcements_tag_value: false
54
57
  )
55
-
56
58
  all_metrics = self.get_metrics(
57
- source_code_pathnames: source_code_pathnames,
58
- componentized_source_code_locations: componentized_source_code_locations,
59
- app_name: app_name,
60
- max_enforcements_tag_value: max_enforcements_tag_value,
59
+ source_code_pathnames:,
60
+ componentized_source_code_locations:,
61
+ app_name:,
62
+ max_enforcements_tag_value:
61
63
  )
62
64
 
63
65
  # This helps us debug what metrics are being sent
64
66
  if verbose
65
67
  all_metrics.each do |metric|
66
- puts "Sending metric: #{metric}"
68
+ puts("Sending metric: #{metric}")
67
69
  end
68
70
  end
69
71
 
70
72
  Private::DatadogReporter.report!(
71
- datadog_client: datadog_client,
72
- report_time: report_time,
73
+ datadog_client:,
74
+ report_time:,
73
75
  metrics: all_metrics
74
76
  )
75
77
  end
@@ -86,7 +88,8 @@ module PackStats
86
88
  # Once with the violation counts as configured in the app
87
89
  # Another time with the violation counts after turning on all enforcements and running `bin/packwerk update`.
88
90
  max_enforcements_tag_value: T::Boolean
89
- ).returns(T::Array[GaugeMetric])
91
+ )
92
+ .returns(T::Array[GaugeMetric])
90
93
  end
91
94
  def self.get_metrics(
92
95
  source_code_pathnames:,
@@ -95,31 +98,31 @@ module PackStats
95
98
  packaged_source_code_locations: [],
96
99
  max_enforcements_tag_value: false
97
100
  )
98
-
99
101
  GaugeMetric.set_max_enforcements_tag(max_enforcements_tag_value)
100
102
 
101
103
  Private::DatadogReporter.get_metrics(
102
104
  source_code_files: source_code_files(
103
- source_code_pathnames: source_code_pathnames,
104
- componentized_source_code_locations: componentized_source_code_locations,
105
+ source_code_pathnames:,
106
+ componentized_source_code_locations:
105
107
  ),
106
- app_name: app_name
108
+ app_name:
107
109
  )
108
110
  end
109
111
 
110
112
  sig do
111
113
  params(
112
114
  source_code_pathnames: T::Array[Pathname],
113
- componentized_source_code_locations: T::Array[Pathname],
114
- ).returns(T::Array[Private::SourceCodeFile])
115
+ componentized_source_code_locations: T::Array[Pathname]
116
+ )
117
+ .returns(T::Array[Private::SourceCodeFile])
115
118
  end
116
119
  def self.source_code_files(
117
120
  source_code_pathnames:,
118
121
  componentized_source_code_locations:
119
122
  )
120
-
121
- # Sorbet has the wrong signatures for `Pathname#find`, whoops!
122
- componentized_file_set = Set.new(componentized_source_code_locations.select(&:exist?).flat_map { |pathname| T.unsafe(pathname).find.to_a })
123
+ componentized_file_set = Set.new(
124
+ componentized_source_code_locations.select(&:exist?).flat_map { |pathname| pathname.find.to_a }
125
+ )
123
126
 
124
127
  packaged_file_set = Packs.all.flat_map do |pack|
125
128
  pack.relative_path.find.to_a
@@ -132,7 +135,7 @@ module PackStats
132
135
  packaged_file = packaged_file_set.include?(pathname)
133
136
 
134
137
  Private::SourceCodeFile.new(
135
- pathname: pathname,
138
+ pathname:,
136
139
  team_owner: CodeOwnership.for_file(pathname.to_s),
137
140
  is_componentized_file: componentized_file,
138
141
  is_packaged_file: packaged_file
metadata CHANGED
@@ -1,17 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pack_stats
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gusto Engineers
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-08-29 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
- name: code_teams
13
+ name: code_ownership
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
16
  - - ">="
@@ -25,7 +24,7 @@ dependencies:
25
24
  - !ruby/object:Gem::Version
26
25
  version: '0'
27
26
  - !ruby/object:Gem::Dependency
28
- name: code_ownership
27
+ name: code_teams
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - ">="
@@ -95,13 +94,13 @@ dependencies:
95
94
  - !ruby/object:Gem::Version
96
95
  version: '0'
97
96
  - !ruby/object:Gem::Dependency
98
- name: rubocop-packs
97
+ name: base64
99
98
  requirement: !ruby/object:Gem::Requirement
100
99
  requirements:
101
100
  - - ">="
102
101
  - !ruby/object:Gem::Version
103
102
  version: '0'
104
- type: :runtime
103
+ type: :development
105
104
  prerelease: false
106
105
  version_requirements: !ruby/object:Gem::Requirement
107
106
  requirements:
@@ -109,7 +108,7 @@ dependencies:
109
108
  - !ruby/object:Gem::Version
110
109
  version: '0'
111
110
  - !ruby/object:Gem::Dependency
112
- name: base64
111
+ name: pry
113
112
  requirement: !ruby/object:Gem::Requirement
114
113
  requirements:
115
114
  - - ">="
@@ -123,7 +122,7 @@ dependencies:
123
122
  - !ruby/object:Gem::Version
124
123
  version: '0'
125
124
  - !ruby/object:Gem::Dependency
126
- name: pry
125
+ name: rake
127
126
  requirement: !ruby/object:Gem::Requirement
128
127
  requirements:
129
128
  - - ">="
@@ -137,7 +136,7 @@ dependencies:
137
136
  - !ruby/object:Gem::Version
138
137
  version: '0'
139
138
  - !ruby/object:Gem::Dependency
140
- name: rake
139
+ name: rspec
141
140
  requirement: !ruby/object:Gem::Requirement
142
141
  requirements:
143
142
  - - ">="
@@ -151,19 +150,33 @@ dependencies:
151
150
  - !ruby/object:Gem::Version
152
151
  version: '0'
153
152
  - !ruby/object:Gem::Dependency
154
- name: rspec
153
+ name: rubocop-gusto
155
154
  requirement: !ruby/object:Gem::Requirement
156
155
  requirements:
157
- - - "~>"
156
+ - - ">="
158
157
  - !ruby/object:Gem::Version
159
- version: '3.0'
158
+ version: '0'
160
159
  type: :development
161
160
  prerelease: false
162
161
  version_requirements: !ruby/object:Gem::Requirement
163
162
  requirements:
164
- - - "~>"
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ - !ruby/object:Gem::Dependency
167
+ name: rubocop-rubyfmt
168
+ requirement: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
165
171
  - !ruby/object:Gem::Version
166
- version: '3.0'
172
+ version: '0'
173
+ type: :development
174
+ prerelease: false
175
+ version_requirements: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
167
180
  - !ruby/object:Gem::Dependency
168
181
  name: sorbet
169
182
  requirement: !ruby/object:Gem::Requirement
@@ -223,7 +236,7 @@ metadata:
223
236
  source_code_uri: https://github.com/rubyatscale/pack_stats
224
237
  changelog_uri: https://github.com/rubyatscale/pack_stats/releases
225
238
  allowed_push_host: https://rubygems.org
226
- post_install_message:
239
+ rubygems_mfa_required: 'true'
227
240
  rdoc_options: []
228
241
  require_paths:
229
242
  - lib
@@ -231,15 +244,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
231
244
  requirements:
232
245
  - - ">="
233
246
  - !ruby/object:Gem::Version
234
- version: 2.6.0
247
+ version: '3.3'
235
248
  required_rubygems_version: !ruby/object:Gem::Requirement
236
249
  requirements:
237
250
  - - ">="
238
251
  - !ruby/object:Gem::Version
239
252
  version: '0'
240
253
  requirements: []
241
- rubygems_version: 3.5.11
242
- signing_key:
254
+ rubygems_version: 3.6.9
243
255
  specification_version: 4
244
256
  summary: A gem to collect statistics about modularization progress in a Rails application
245
257
  using packwerk.