Skip to content

Commit 5509292

Browse files
author
Alex Evanczuk
authored
Allow stats to be tagged with max_enforcements tag (#31)
* make tests pass * write more failing tests * fix tests * Bump version * add code ownership config to rspec * tighten sig for Tag property
1 parent 1931335 commit 5509292

6 files changed

Lines changed: 108 additions & 16 deletions

File tree

Gemfile.lock

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
pack_stats (0.0.4)
4+
pack_stats (0.0.5)
55
code_ownership
66
code_teams
77
dogapi
@@ -13,24 +13,24 @@ PATH
1313
GEM
1414
remote: https://rubygems.org/
1515
specs:
16-
activesupport (7.0.4.2)
16+
activesupport (7.0.4.3)
1717
concurrent-ruby (~> 1.0, >= 1.0.2)
1818
i18n (>= 1.6, < 2)
1919
minitest (>= 5.1)
2020
tzinfo (~> 2.0)
2121
ast (2.4.2)
22-
code_ownership (1.31.0)
22+
code_ownership (1.32.17)
2323
code_teams (~> 1.0)
2424
packs
2525
sorbet-runtime
26-
code_teams (1.0.0)
26+
code_teams (1.0.1)
2727
sorbet-runtime
2828
coderay (1.1.3)
2929
concurrent-ruby (1.2.2)
3030
diff-lcs (1.5.0)
3131
dogapi (1.45.0)
3232
multi_json
33-
i18n (1.12.0)
33+
i18n (1.13.0)
3434
concurrent-ruby (~> 1.0)
3535
json (2.6.3)
3636
method_source (1.0.0)
@@ -40,7 +40,7 @@ GEM
4040
packs (0.0.6)
4141
sorbet-runtime
4242
parallel (1.22.1)
43-
parse_packwerk (0.18.0)
43+
parse_packwerk (0.19.1)
4444
sorbet-runtime
4545
parser (3.1.1.0)
4646
ast (~> 2.4.1)
@@ -54,7 +54,7 @@ GEM
5454
parser (>= 2.6.4.0)
5555
sorbet-runtime (>= 0.5.9204)
5656
unparser
57-
regexp_parser (2.7.0)
57+
regexp_parser (2.8.0)
5858
rexml (3.2.5)
5959
rspec (3.11.0)
6060
rspec-core (~> 3.11.0)
@@ -81,7 +81,7 @@ GEM
8181
unicode-display_width (>= 1.4.0, < 3.0)
8282
rubocop-ast (1.24.1)
8383
parser (>= 3.1.1.0)
84-
rubocop-packs (0.0.35)
84+
rubocop-packs (0.0.38)
8585
activesupport
8686
packs
8787
parse_packwerk

lib/pack_stats.rb

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ module PackStats
3939
# See note on get_metrics
4040
packaged_source_code_locations: T.nilable(T::Array[Pathname]),
4141
# See note on get_metrics
42-
use_gusto_legacy_names: T::Boolean
42+
use_gusto_legacy_names: T::Boolean,
43+
# See note on get_metrics
44+
max_enforcements_tag_value: T::Boolean
4345
).void
4446
end
4547
def self.report_to_datadog!(
@@ -50,14 +52,16 @@ def self.report_to_datadog!(
5052
report_time: Time.now, # rubocop:disable Rails/TimeZone
5153
verbose: false,
5254
packaged_source_code_locations: [],
53-
use_gusto_legacy_names: false
55+
use_gusto_legacy_names: false,
56+
max_enforcements_tag_value: false
5457
)
5558

5659
all_metrics = self.get_metrics(
5760
source_code_pathnames: source_code_pathnames,
5861
componentized_source_code_locations: componentized_source_code_locations,
5962
app_name: app_name,
6063
use_gusto_legacy_names: use_gusto_legacy_names,
64+
max_enforcements_tag_value: max_enforcements_tag_value,
6165
)
6266

6367
# This helps us debug what metrics are being sent
@@ -89,16 +93,25 @@ def self.report_to_datadog!(
8993
# Gusto uses this to preserve historical trends in Dashboards as the names of
9094
# things changed, but new dashboards can use names that better match current tooling conventions.
9195
# The behavior of setting this parameter to true might change without warning
92-
use_gusto_legacy_names: T::Boolean
96+
use_gusto_legacy_names: T::Boolean,
97+
# You can set this to `true` to tag all metrics with `max_enforcements:true`.
98+
# This is useful if you want to submit two sets of metrics:
99+
# Once with the violation counts as configured in the app
100+
# Another time with the violation counts after turning on all enforcements and running `bin/packwerk update`.
101+
max_enforcements_tag_value: T::Boolean
93102
).returns(T::Array[GaugeMetric])
94103
end
95104
def self.get_metrics(
96105
source_code_pathnames:,
97106
componentized_source_code_locations:,
98107
app_name:,
99108
packaged_source_code_locations: [],
100-
use_gusto_legacy_names: false
109+
use_gusto_legacy_names: false,
110+
max_enforcements_tag_value: false
101111
)
112+
113+
GaugeMetric.set_max_enforcements_tag(max_enforcements_tag_value)
114+
102115
all_metrics = Private::DatadogReporter.get_metrics(
103116
source_code_files: source_code_files(
104117
source_code_pathnames: source_code_pathnames,

lib/pack_stats/gauge_metric.rb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ def self.for(metric_name, count, tags)
1717
raise StandardError.new("Metrics names must not exceed 200 characters: #{name}") # rubocop:disable Style/RaiseArgs
1818
end
1919

20+
all_tags = [*tags, max_enforcements_tag]
2021
new(
2122
name: name,
2223
count: count,
23-
tags: tags
24+
tags: all_tags
2425
)
2526
end
2627

@@ -35,5 +36,16 @@ def ==(other)
3536
other.count == self.count &&
3637
other.tags == self.tags
3738
end
39+
40+
sig { params(tag_value: T::Boolean).void }
41+
def self.set_max_enforcements_tag(tag_value)
42+
@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+
end
45+
46+
sig { returns(Tag) }
47+
def self.max_enforcements_tag
48+
@max_enforcements_tag || Tag.new(key: 'max_enforcements', value: 'false')
49+
end
3850
end
3951
end

pack_stats.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Gem::Specification.new do |spec|
22
spec.name = 'pack_stats'
3-
spec.version = '0.0.4'
3+
spec.version = '0.0.5'
44
spec.authors = ['Gusto Engineers']
55
spec.email = ['dev@gusto.com']
66

spec/pack_stats_legacy_api_spec.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ module PackStats # rubocop:disable RSpec/DescribedClassModuleWrapping
3737
'modularization.some_metric',
3838
[[report_time, 11]],
3939
type: 'gauge',
40-
tags: ['mykey:myvalue', 'myotherkey:myothervalue']
40+
tags: ['mykey:myvalue', 'myotherkey:myothervalue', 'max_enforcements:false']
4141
)
4242
report_to_datadog
4343
end
@@ -64,6 +64,7 @@ module PackStats # rubocop:disable RSpec/DescribedClassModuleWrapping
6464
- packs/*
6565
- packs/*/*
6666
YML
67+
write_file('config/code_ownership.yml', YAML.dump({}))
6768
end
6869

6970
context 'in empty app' do

spec/pack_stats_spec.rb

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ module PackStats # rubocop:disable RSpec/DescribedClassModuleWrapping
44
RSpec.describe PackStats do
55
before do
66
ParsePackwerk.bust_cache!
7+
write_file('config/code_ownership.yml', YAML.dump({}))
78
end
89

910
describe 'PackStats.report_to_datadog!' do
@@ -33,7 +34,7 @@ module PackStats # rubocop:disable RSpec/DescribedClassModuleWrapping
3334
'modularization.some_metric',
3435
[[report_time, 11]],
3536
type: 'gauge',
36-
tags: ['mykey:myvalue', 'myotherkey:myothervalue']
37+
tags: ['mykey:myvalue', 'myotherkey:myothervalue', 'max_enforcements:false']
3738
)
3839
report_to_datadog
3940
end
@@ -1073,6 +1074,71 @@ module PackStats # rubocop:disable RSpec/DescribedClassModuleWrapping
10731074
expect(metrics).to include_metric GaugeMetric.for('all_packages.rubocops.packs_rootnamespaceispackname.exclusions.count', 6, Tags.for(['app:MyApp']))
10741075
end
10751076
end
1077+
1078+
context 'when getting metrics after turning all protections to max' do
1079+
let(:subject) do
1080+
PackStats.get_metrics(
1081+
app_name: 'MyApp',
1082+
source_code_pathnames: Pathname.glob('**/**.rb'),
1083+
componentized_source_code_locations: [Pathname.new('components')],
1084+
max_enforcements_tag_value: true
1085+
)
1086+
end
1087+
1088+
include_context 'only one team'
1089+
1090+
before do
1091+
write_file('empty_file.rb')
1092+
write_file('packs/only_package/app/some_package_file.rb')
1093+
write_file('packs/only_package/package.yml', <<~CONTENTS)
1094+
enforce_dependencies: true
1095+
enforce_privacy: true
1096+
CONTENTS
1097+
1098+
write_file('packs/only_package/spec/some_package_file_spec.rb')
1099+
end
1100+
1101+
it 'emits the right metrics' do
1102+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.component_files.by_team', count: 0, tags: Tags.for(['team:Some team', 'app:MyApp', 'max_enforcements:true']))
1103+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.packaged_files.by_team', count: 2, tags: Tags.for(['team:Some team', 'app:MyApp', 'max_enforcements:true']))
1104+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_files.by_team', count: 3, tags: Tags.for(['team:Some team', 'app:MyApp', 'max_enforcements:true']))
1105+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.component_files.totals', count: 0, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1106+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.packaged_files.totals', count: 2, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1107+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_files.totals', count: 3, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1108+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.count', count: 2, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1109+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.dependencies.count', count: 0, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1110+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.dependency_violations.count', count: 0, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1111+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.privacy_violations.count', count: 0, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1112+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.enforcing_dependencies.count', count: 2, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1113+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.enforcing_privacy.count', count: 2, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1114+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.with_violations.count', count: 0, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1115+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.packwerk_checkers.enforce_dependencies.strict.count', count: 0, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1116+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.packwerk_checkers.enforce_dependencies.true.count', count: 2, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1117+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.packwerk_checkers.enforce_privacy.strict.count', count: 0, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1118+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.packwerk_checkers.enforce_privacy.true.count', count: 2, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1119+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.rubocops.packs_typedpublicapis.strict.count', count: 0, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1120+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.rubocops.packs_typedpublicapis.true.count', count: 0, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1121+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.rubocops.packs_rootnamespaceispackname.strict.count', count: 0, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1122+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.package_based_file_ownership.count', count: 0, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1123+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.using_public_directory.count', count: 0, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1124+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.by_package.dependency_violations.count', count: 0, tags: Tags.for(['package:packs/only_package', 'app:MyApp', 'team:Unknown', 'max_enforcements:true']))
1125+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.by_package.privacy_violations.count', count: 0, tags: Tags.for(['package:packs/only_package', 'app:MyApp', 'team:Unknown', 'max_enforcements:true']))
1126+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.by_package.outbound_dependency_violations.count', count: 0, tags: Tags.for(['package:packs/only_package', 'app:MyApp', 'team:Unknown', 'max_enforcements:true']))
1127+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.by_package.inbound_dependency_violations.count', count: 0, tags: Tags.for(['package:packs/only_package', 'app:MyApp', 'team:Unknown', 'max_enforcements:true']))
1128+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.by_package.outbound_privacy_violations.count', count: 0, tags: Tags.for(['package:packs/only_package', 'app:MyApp', 'team:Unknown', 'max_enforcements:true']))
1129+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.by_package.inbound_privacy_violations.count', count: 0, tags: Tags.for(['package:packs/only_package', 'app:MyApp', 'team:Unknown', 'max_enforcements:true']))
1130+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.by_package.outbound_explicit_dependencies.count', count: 0, tags: Tags.for(['package:packs/only_package', 'app:MyApp', 'team:Unknown', 'max_enforcements:true']))
1131+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.by_package.inbound_explicit_dependencies.count', count: 0, tags: Tags.for(['package:packs/only_package', 'app:MyApp', 'team:Unknown', 'max_enforcements:true']))
1132+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.using_public_directory.count', count: 0, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1133+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.all_files.count', count: 2, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1134+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.all_packages.public_files.count', count: 0, tags: Tags.for(['app:MyApp', 'max_enforcements:true']))
1135+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.by_team.using_public_directory.count', count: 0, tags: Tags.for(['app:MyApp', 'team:Unknown', 'max_enforcements:true']))
1136+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.by_team.all_files.count', count: 2, tags: Tags.for(['app:MyApp', 'team:Unknown', 'max_enforcements:true']))
1137+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.by_team.public_files.count', count: 0, tags: Tags.for(['app:MyApp', 'team:Unknown', 'max_enforcements:true']))
1138+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.by_package.using_public_directory.count', count: 0, tags: Tags.for(['package:packs/only_package', 'app:MyApp', 'team:Unknown', 'max_enforcements:true']))
1139+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.by_package.all_files.count', count: 2, tags: Tags.for(['package:packs/only_package', 'app:MyApp', 'team:Unknown', 'max_enforcements:true']))
1140+
expect(metrics).to include_metric GaugeMetric.new(name: 'modularization.by_package.public_files.count', count: 0, tags: Tags.for(['package:packs/only_package', 'app:MyApp', 'team:Unknown', 'max_enforcements:true'])) end
1141+
end
10761142
end
10771143
end
10781144
end

0 commit comments

Comments
 (0)