pack_stats 0.2.1 → 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 +4 -4
- data/lib/pack_stats/gauge_metric.rb +7 -6
- data/lib/pack_stats/private/datadog_reporter.rb +19 -12
- data/lib/pack_stats/private/metrics/dependencies.rb +18 -7
- data/lib/pack_stats/private/metrics/files.rb +8 -6
- data/lib/pack_stats/private/metrics/packages.rb +61 -28
- data/lib/pack_stats/private/metrics/packages_by_team.rb +50 -26
- data/lib/pack_stats/private/metrics/packwerk_checker_usage.rb +26 -12
- data/lib/pack_stats/private/metrics/public_usage.rb +8 -4
- data/lib/pack_stats/private/metrics.rb +11 -11
- data/lib/pack_stats/private/source_code_file.rb +1 -0
- data/lib/pack_stats/private.rb +3 -1
- data/lib/pack_stats/tag.rb +4 -2
- data/lib/pack_stats/tags.rb +4 -3
- data/lib/pack_stats.rb +44 -41
- metadata +31 -19
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8053c602af521d61db8a3e333a61b30b5ee7443a96cc45f2932c77b13f34a2df
|
|
4
|
+
data.tar.gz: f64229355da5b0f4d398bbcc7a6810f638170574539b05e1b5195bfae3d9f85d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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
|
|
23
|
-
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.
|
|
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:
|
|
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:
|
|
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
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
9
|
-
require
|
|
10
|
-
require
|
|
11
|
-
require
|
|
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
|
-
)
|
|
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
|
-
)
|
|
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(
|
|
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
|
|
11
|
-
|
|
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|
|
|
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 +
|
|
35
|
-
|
|
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(
|
|
39
|
-
all_metrics <<
|
|
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
|
-
)
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
)
|
|
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
|
-
)
|
|
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(
|
|
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(
|
|
22
|
-
all_metrics <<
|
|
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
|
|
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(
|
|
31
|
-
all_metrics <<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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 <<
|
|
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
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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 <<
|
|
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(
|
|
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
|
-
)
|
|
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(
|
|
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(
|
|
24
|
-
all_metrics += Metrics::PackwerkCheckerUsage.get_checker_metrics(
|
|
25
|
-
all_metrics += Metrics::PublicUsage.get_public_usage_metrics(
|
|
26
|
-
all_metrics <<
|
|
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
|
|
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
|
-
|
|
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
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
67
|
+
|
|
52
68
|
when PackwerkCheckerUsage::Direction::Inbound
|
|
53
|
-
|
|
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
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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,23 +27,39 @@ module PackStats
|
|
|
29
27
|
sig { returns(Tag) }
|
|
30
28
|
def violation_type_tag
|
|
31
29
|
Tag.new(
|
|
32
|
-
key:
|
|
30
|
+
key: "violation_type",
|
|
33
31
|
value: violation_type
|
|
34
32
|
)
|
|
35
33
|
end
|
|
36
34
|
end
|
|
37
35
|
|
|
38
|
-
CHECKERS = T.let(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
+
)
|
|
45
54
|
|
|
46
|
-
sig
|
|
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
|
|
47
60
|
def self.get_checker_metrics(prefix, packages, package_tags)
|
|
48
61
|
metrics = T.let([], T::Array[GaugeMetric])
|
|
62
|
+
possible_values = ["false", "true", "strict"]
|
|
49
63
|
|
|
50
64
|
CHECKERS.each do |checker|
|
|
51
65
|
checker_values = packages.map do |package|
|
|
@@ -54,7 +68,7 @@ module PackStats
|
|
|
54
68
|
|
|
55
69
|
checker_values_tally = checker_values.map(&:to_s).tally
|
|
56
70
|
|
|
57
|
-
|
|
71
|
+
possible_values.each do |possible_value|
|
|
58
72
|
count = checker_values_tally.fetch(possible_value, 0)
|
|
59
73
|
metric_name = "#{prefix}.packwerk_checkers.#{possible_value}.count"
|
|
60
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
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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:
|
|
20
|
-
Tag.new(key:
|
|
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[
|
|
24
|
+
layer = package.config["layer"]
|
|
25
25
|
if layer
|
|
26
|
-
tags << Tag.new(key:
|
|
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(
|
|
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
|
-
|
|
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(
|
|
53
|
+
package.directory.join("README.md").exist?
|
|
54
54
|
end
|
|
55
55
|
end
|
|
56
56
|
end
|
data/lib/pack_stats/private.rb
CHANGED
|
@@ -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
|
data/lib/pack_stats/tag.rb
CHANGED
|
@@ -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
|
|
18
|
-
value:
|
|
19
|
+
key:,
|
|
20
|
+
value:
|
|
19
21
|
)
|
|
20
22
|
end
|
|
21
23
|
|
data/lib/pack_stats/tags.rb
CHANGED
|
@@ -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
|
|
15
|
-
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
|
-
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
9
|
-
require
|
|
10
|
-
require
|
|
11
|
-
require
|
|
12
|
-
require
|
|
13
|
-
require
|
|
14
|
-
require
|
|
15
|
-
require
|
|
16
|
-
require
|
|
17
|
-
require
|
|
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 =
|
|
23
|
+
ROOT_PACKAGE_NAME = "root"
|
|
23
24
|
|
|
24
25
|
DEFAULT_COMPONENTIZED_SOURCE_CODE_LOCATIONS = T.let(
|
|
25
26
|
[
|
|
26
|
-
Pathname.new(
|
|
27
|
-
Pathname.new(
|
|
28
|
-
].freeze,
|
|
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
|
-
)
|
|
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,
|
|
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
|
|
58
|
-
componentized_source_code_locations
|
|
59
|
-
app_name
|
|
60
|
-
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
|
|
68
|
+
puts("Sending metric: #{metric}")
|
|
67
69
|
end
|
|
68
70
|
end
|
|
69
71
|
|
|
70
72
|
Private::DatadogReporter.report!(
|
|
71
|
-
datadog_client
|
|
72
|
-
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
|
-
)
|
|
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
|
|
104
|
-
componentized_source_code_locations:
|
|
105
|
+
source_code_pathnames:,
|
|
106
|
+
componentized_source_code_locations:
|
|
105
107
|
),
|
|
106
|
-
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
|
-
)
|
|
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
|
-
|
|
122
|
-
|
|
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
|
|
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.
|
|
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:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
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:
|
|
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:
|
|
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: :
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
153
|
+
name: rubocop-gusto
|
|
155
154
|
requirement: !ruby/object:Gem::Requirement
|
|
156
155
|
requirements:
|
|
157
|
-
- - "
|
|
156
|
+
- - ">="
|
|
158
157
|
- !ruby/object:Gem::Version
|
|
159
|
-
version: '
|
|
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: '
|
|
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
|
-
|
|
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:
|
|
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.
|
|
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.
|