feature_map 1.2.2 → 1.2.3

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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -1
  3. data/bin/featuremap +0 -1
  4. data/lib/feature_map/cli.rb +0 -2
  5. data/lib/feature_map/code_features/plugin.rb +2 -21
  6. data/lib/feature_map/code_features/plugins/identity.rb +1 -8
  7. data/lib/feature_map/code_features.rb +1 -31
  8. data/lib/feature_map/commit.rb +0 -19
  9. data/lib/feature_map/configuration.rb +40 -17
  10. data/lib/feature_map/constants.rb +3 -5
  11. data/lib/feature_map/mapper.rb +0 -26
  12. data/lib/feature_map/output_color.rb +0 -11
  13. data/lib/feature_map/private/additional_metrics_file.rb +9 -103
  14. data/lib/feature_map/private/assignment_applicator.rb +0 -12
  15. data/lib/feature_map/private/assignment_mappers/directory_assignment.rb +4 -26
  16. data/lib/feature_map/private/assignment_mappers/feature_definition_assignment.rb +1 -21
  17. data/lib/feature_map/private/assignment_mappers/feature_globs.rb +7 -40
  18. data/lib/feature_map/private/assignment_mappers/file_annotations.rb +20 -44
  19. data/lib/feature_map/private/assignments_file.rb +8 -54
  20. data/lib/feature_map/private/code_cov.rb +2 -29
  21. data/lib/feature_map/private/cyclomatic_complexity_calculator.rb +1 -7
  22. data/lib/feature_map/private/docs/index.html +2 -2
  23. data/lib/feature_map/private/documentation_site.rb +0 -16
  24. data/lib/feature_map/private/extension_loader.rb +0 -3
  25. data/lib/feature_map/private/feature_assigner.rb +0 -4
  26. data/lib/feature_map/private/feature_metrics_calculator.rb +2 -16
  27. data/lib/feature_map/private/feature_plugins/assignment.rb +0 -6
  28. data/lib/feature_map/private/glob_cache.rb +2 -29
  29. data/lib/feature_map/private/health_calculator.rb +122 -0
  30. data/lib/feature_map/private/lines_of_code_calculator.rb +10 -21
  31. data/lib/feature_map/private/metrics_file.rb +1 -25
  32. data/lib/feature_map/private/percentile_metrics_calculator.rb +117 -0
  33. data/lib/feature_map/private/release_notification_builder.rb +1 -13
  34. data/lib/feature_map/private/test_coverage_file.rb +12 -39
  35. data/lib/feature_map/private/test_pyramid_file.rb +0 -41
  36. data/lib/feature_map/private/todo_inspector.rb +16 -30
  37. data/lib/feature_map/private/validations/features_up_to_date.rb +1 -6
  38. data/lib/feature_map/private/validations/files_have_features.rb +2 -7
  39. data/lib/feature_map/private/validations/files_have_unique_features.rb +1 -6
  40. data/lib/feature_map/private.rb +7 -44
  41. data/lib/feature_map/validator.rb +0 -13
  42. data/lib/feature_map.rb +8 -49
  43. metadata +4 -44
data/lib/feature_map.rb CHANGED
@@ -1,9 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # typed: strict
4
-
5
3
  require 'set'
6
- require 'sorbet-runtime'
4
+
7
5
  require 'json'
8
6
  require 'yaml'
9
7
  require 'feature_map/commit'
@@ -20,23 +18,11 @@ module FeatureMap
20
18
 
21
19
  module_function
22
20
 
23
- extend T::Sig
24
- extend T::Helpers
25
-
26
- requires_ancestor { Kernel }
27
- GlobsToAssignedFeatureMap = T.type_alias { T::Hash[String, CodeFeatures::Feature] }
28
-
29
- UpdatedFeaturesByTeam = T.type_alias { T::Hash[String, CommitsByFeature] }
30
- CommitsByFeature = T.type_alias { T::Hash[String, T::Array[Commit]] }
31
-
32
- sig { params(assignments_file_path: String).void }
33
21
  def apply_assignments!(assignments_file_path)
34
22
  Private.apply_assignments!(assignments_file_path)
35
23
  end
36
24
 
37
- sig { params(file: String).returns(T.nilable(CodeFeatures::Feature)) }
38
25
  def for_file(file)
39
- @for_file ||= T.let(@for_file, T.nilable(T::Hash[String, T.nilable(CodeFeatures::Feature)]))
40
26
  @for_file ||= {}
41
27
 
42
28
  return nil if file.start_with?('./')
@@ -44,8 +30,7 @@ module FeatureMap
44
30
 
45
31
  Private.load_configuration!
46
32
 
47
- feature = T.let(nil, T.nilable(CodeFeatures::Feature))
48
-
33
+ feature = nil
49
34
  Mapper.all.each do |mapper|
50
35
  feature = mapper.map_file_to_feature(file)
51
36
  break if feature # TODO: what if there are multiple features? Should we respond with an error instead of the first match?
@@ -54,10 +39,9 @@ module FeatureMap
54
39
  @for_file[file] = feature
55
40
  end
56
41
 
57
- sig { params(feature: T.any(CodeFeatures::Feature, String)).returns(String) }
58
42
  def for_feature(feature)
59
- feature = T.must(CodeFeatures.find(feature)) if feature.is_a?(String)
60
- feature_report = T.let([], T::Array[String])
43
+ feature = CodeFeatures.find(feature)
44
+ feature_report = []
61
45
 
62
46
  feature_report << "# Report for `#{feature.name}` Feature"
63
47
 
@@ -85,18 +69,10 @@ module FeatureMap
85
69
  class InvalidFeatureMapConfigurationError < StandardError
86
70
  end
87
71
 
88
- sig { params(filename: String).void }
89
72
  def self.remove_file_annotation!(filename)
90
73
  Private::AssignmentMappers::FileAnnotations.new.remove_file_annotation!(filename)
91
74
  end
92
75
 
93
- sig do
94
- params(
95
- autocorrect: T::Boolean,
96
- stage_changes: T::Boolean,
97
- files: T.nilable(T::Array[String])
98
- ).void
99
- end
100
76
  def validate!(
101
77
  autocorrect: true,
102
78
  stage_changes: true,
@@ -113,43 +89,30 @@ module FeatureMap
113
89
  Private.validate!(files: tracked_file_subset, autocorrect: autocorrect, stage_changes: stage_changes)
114
90
  end
115
91
 
116
- sig { params(git_ref: T.nilable(String)).void }
117
92
  def generate_docs!(git_ref)
118
93
  Private.generate_docs!(git_ref)
119
94
  end
120
95
 
121
- sig do
122
- params(
123
- unit_path: String,
124
- integration_path: String,
125
- regression_path: T.nilable(String),
126
- regression_assignments_path: T.nilable(String)
127
- ).void
128
- end
129
96
  def generate_test_pyramid!(unit_path, integration_path, regression_path, regression_assignments_path)
130
97
  Private.generate_test_pyramid!(unit_path, integration_path, regression_path, regression_assignments_path)
131
98
  end
132
99
 
133
- sig { params(commit_sha: String, code_cov_token: String).void }
134
100
  def gather_test_coverage!(commit_sha, code_cov_token)
135
101
  Private.gather_test_coverage!(commit_sha, code_cov_token)
136
102
  end
137
103
 
138
- sig { void }
139
104
  def generate_additional_metrics!
140
105
  Private.generate_additional_metrics!
141
106
  end
142
107
 
143
108
  # Given a backtrace from either `Exception#backtrace` or `caller`, find the
144
109
  # first line that corresponds to a file with an assigned feature
145
- sig { params(backtrace: T.nilable(T::Array[String]), excluded_features: T::Array[CodeFeatures::Feature]).returns(T.nilable(CodeFeatures::Feature)) }
146
110
  def for_backtrace(backtrace, excluded_features: [])
147
111
  first_assigned_file_for_backtrace(backtrace, excluded_features: excluded_features)&.first
148
112
  end
149
113
 
150
114
  # Given a backtrace from either `Exception#backtrace` or `caller`, find the
151
115
  # first assigned file in it, useful for figuring out which file is being blamed.
152
- sig { params(backtrace: T.nilable(T::Array[String]), excluded_features: T::Array[CodeFeatures::Feature]).returns(T.nilable([CodeFeatures::Feature, String])) }
153
116
  def first_assigned_file_for_backtrace(backtrace, excluded_features: [])
154
117
  backtrace_with_feature_assignments(backtrace).each do |(feature, file)|
155
118
  if feature && !excluded_features.include?(feature)
@@ -160,7 +123,6 @@ module FeatureMap
160
123
  nil
161
124
  end
162
125
 
163
- sig { params(backtrace: T.nilable(T::Array[String])).returns(T::Enumerable[[T.nilable(CodeFeatures::Feature), String]]) }
164
126
  def backtrace_with_feature_assignments(backtrace)
165
127
  return [] unless backtrace
166
128
 
@@ -183,7 +145,7 @@ module FeatureMap
183
145
  match = line.match(backtrace_line)
184
146
  next unless match
185
147
 
186
- file = T.must(match[:file])
148
+ file = match[:file]
187
149
 
188
150
  [
189
151
  FeatureMap.for_file(file),
@@ -193,9 +155,7 @@ module FeatureMap
193
155
  end
194
156
  private_class_method(:backtrace_with_feature_assignments)
195
157
 
196
- sig { params(klass: T.nilable(T.any(T::Class[T.anything], Module))).returns(T.nilable(CodeFeatures::Feature)) }
197
158
  def for_class(klass)
198
- @memoized_values ||= T.let(@memoized_values, T.nilable(T::Hash[String, T.nilable(CodeFeatures::Feature)]))
199
159
  @memoized_values ||= {}
200
160
  # We use key because the memoized value could be `nil`
201
161
  if @memoized_values.key?(klass.to_s)
@@ -213,7 +173,7 @@ module FeatureMap
213
173
  # Groups the provided list of commits (e.g. the changes being deployed in a release) by both the feature they impact
214
174
  # and the teams responsible for these features. Returns a hash with keys for each team with features modified within
215
175
  # these commits and values that are a hash of features to the set of commits that impact each feature.
216
- sig { params(commits: T::Array[Commit]).returns(UpdatedFeaturesByTeam) }
176
+
217
177
  def group_commits(commits)
218
178
  commits.each_with_object({}) do |commit, hash|
219
179
  commit_features = commit.files.map do |file|
@@ -244,7 +204,7 @@ module FeatureMap
244
204
 
245
205
  # Generates a block kit message grouping the provided commits into sections for each feature impacted by the
246
206
  # cheanges.
247
- sig { params(commits_by_feature: CommitsByFeature).returns(T::Array[T::Hash[String, T.untyped]]) }
207
+
248
208
  def generate_release_notification(commits_by_feature)
249
209
  Private.generate_release_notification(commits_by_feature)
250
210
  end
@@ -253,7 +213,7 @@ module FeatureMap
253
213
  # Namely, the set of files, and directories which are tracked for feature assignment should not change.
254
214
  # The primary reason this is helpful is for clients of FeatureMap who want to test their code, and each test context
255
215
  # has different feature assignments and tracked files.
256
- sig { void }
216
+
257
217
  def self.bust_caches!
258
218
  @for_file = nil
259
219
  @memoized_values = nil
@@ -261,7 +221,6 @@ module FeatureMap
261
221
  Mapper.all.each(&:bust_caches!)
262
222
  end
263
223
 
264
- sig { returns(Configuration) }
265
224
  def self.configuration
266
225
  Private.configuration
267
226
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: feature_map
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 1.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Beyond Finance
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-02-25 00:00:00.000000000 Z
11
+ date: 2025-03-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: code_ownership
@@ -66,20 +66,6 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.0'
69
- - !ruby/object:Gem::Dependency
70
- name: sorbet-runtime
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '0.5'
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '0.5'
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: uri
85
71
  requirement: !ruby/object:Gem::Requirement
@@ -164,34 +150,6 @@ dependencies:
164
150
  - - "~>"
165
151
  - !ruby/object:Gem::Version
166
152
  version: '3.0'
167
- - !ruby/object:Gem::Dependency
168
- name: sorbet
169
- requirement: !ruby/object:Gem::Requirement
170
- requirements:
171
- - - "~>"
172
- - !ruby/object:Gem::Version
173
- version: '0.5'
174
- type: :development
175
- prerelease: false
176
- version_requirements: !ruby/object:Gem::Requirement
177
- requirements:
178
- - - "~>"
179
- - !ruby/object:Gem::Version
180
- version: '0.5'
181
- - !ruby/object:Gem::Dependency
182
- name: tapioca
183
- requirement: !ruby/object:Gem::Requirement
184
- requirements:
185
- - - "~>"
186
- - !ruby/object:Gem::Version
187
- version: '0.16'
188
- type: :development
189
- prerelease: false
190
- version_requirements: !ruby/object:Gem::Requirement
191
- requirements:
192
- - - "~>"
193
- - !ruby/object:Gem::Version
194
- version: '0.16'
195
153
  - !ruby/object:Gem::Dependency
196
154
  name: webmock
197
155
  requirement: !ruby/object:Gem::Requirement
@@ -247,8 +205,10 @@ files:
247
205
  - lib/feature_map/private/feature_metrics_calculator.rb
248
206
  - lib/feature_map/private/feature_plugins/assignment.rb
249
207
  - lib/feature_map/private/glob_cache.rb
208
+ - lib/feature_map/private/health_calculator.rb
250
209
  - lib/feature_map/private/lines_of_code_calculator.rb
251
210
  - lib/feature_map/private/metrics_file.rb
211
+ - lib/feature_map/private/percentile_metrics_calculator.rb
252
212
  - lib/feature_map/private/release_notification_builder.rb
253
213
  - lib/feature_map/private/test_coverage_file.rb
254
214
  - lib/feature_map/private/test_pyramid_file.rb