Module: FeatureMap
- Defined in:
- lib/feature_map/commit.rb,
lib/feature_map.rb,
lib/feature_map/cli.rb,
lib/feature_map/mapper.rb,
lib/feature_map/private.rb,
lib/feature_map/constants.rb,
lib/feature_map/validator.rb,
lib/feature_map/code_features.rb,
lib/feature_map/configuration.rb,
lib/feature_map/private/code_cov.rb,
lib/feature_map/private/glob_cache.rb,
lib/feature_map/code_features/plugin.rb,
lib/feature_map/private/metrics_file.rb,
lib/feature_map/private/todo_inspector.rb,
lib/feature_map/private/assignments_file.rb,
lib/feature_map/private/extension_loader.rb,
lib/feature_map/private/feature_assigner.rb,
lib/feature_map/private/health_calculator.rb,
lib/feature_map/private/test_pyramid_file.rb,
lib/feature_map/private/documentation_site.rb,
lib/feature_map/private/test_coverage_file.rb,
lib/feature_map/private/test_pyramid/mapper.rb,
lib/feature_map/private/assignment_applicator.rb,
lib/feature_map/code_features/plugins/identity.rb,
lib/feature_map/private/additional_metrics_file.rb,
lib/feature_map/private/lines_of_code_calculator.rb,
lib/feature_map/private/test_pyramid/jest_mapper.rb,
lib/feature_map/private/test_pyramid/rspec_mapper.rb,
lib/feature_map/private/feature_metrics_calculator.rb,
lib/feature_map/private/feature_plugins/assignment.rb,
lib/feature_map/private/release_notification_builder.rb,
lib/feature_map/private/percentile_metrics_calculator.rb,
lib/feature_map/private/validations/features_up_to_date.rb,
lib/feature_map/private/validations/files_have_features.rb,
lib/feature_map/private/assignment_mappers/feature_globs.rb,
lib/feature_map/private/cyclomatic_complexity_calculator.rb,
lib/feature_map/private/assignment_mappers/file_annotations.rb,
lib/feature_map/private/validations/files_have_unique_features.rb,
lib/feature_map/private/assignment_mappers/directory_assignment.rb,
lib/feature_map/private/assignment_mappers/feature_definition_assignment.rb
Overview
frozen_string_literal: true
Defined Under Namespace
Modules: CodeFeatures, Constants, Mapper, Validator
Classes: Cli, Commit, Configuration, InvalidFeatureMapConfigurationError
Constant Summary
collapse
- ALL_TEAMS_KEY =
'All Teams'
- NO_FEATURE_KEY =
'No Feature'
Class Method Summary
collapse
-
.apply_assignments!(assignments_file_path) ⇒ Object
-
.bust_caches! ⇒ Object
Generally, you should not ever need to do this, because once your ruby process loads, cached content should not change.
-
.configuration ⇒ Object
-
.first_assigned_file_for_backtrace(backtrace, excluded_features: []) ⇒ Object
Given a backtrace from either ‘Exception#backtrace` or `caller`, find the first assigned file in it, useful for figuring out which file is being blamed.
-
.for_backtrace(backtrace, excluded_features: []) ⇒ Object
Given a backtrace from either ‘Exception#backtrace` or `caller`, find the first line that corresponds to a file with an assigned feature.
-
.for_class(klass) ⇒ Object
-
.for_feature(feature) ⇒ Object
-
.for_file(file) ⇒ Object
-
.gather_simplecov_test_coverage!(simplecov_resultsets) ⇒ Object
-
.gather_test_coverage!(commit_sha, code_cov_token) ⇒ Object
-
.generate_additional_metrics! ⇒ Object
-
.generate_docs!(git_ref) ⇒ Object
-
.generate_release_notification(commits_by_feature) ⇒ Object
Generates a block kit message grouping the provided commits into sections for each feature impacted by the cheanges.
-
.generate_test_pyramid!(unit_path, integration_path, regression_path, regression_assignments_path) ⇒ Object
-
.group_commits(commits) ⇒ Object
Groups the provided list of commits (e.g. the changes being deployed in a release) by both the feature they impact and the teams responsible for these features.
-
.remove_file_annotation!(filename) ⇒ Object
-
.validate!(autocorrect: true, stage_changes: true, files: nil) ⇒ Object
Class Method Details
.apply_assignments!(assignments_file_path) ⇒ Object
22
23
24
|
# File 'lib/feature_map.rb', line 22
def apply_assignments!(assignments_file_path)
Private.apply_assignments!(assignments_file_path)
end
|
.bust_caches! ⇒ Object
Generally, you should not ever need to do this, because once your ruby process loads, cached content should not change. Namely, the set of files, and directories which are tracked for feature assignment should not change. The primary reason this is helpful is for clients of FeatureMap who want to test their code, and each test context has different feature assignments and tracked files.
222
223
224
225
226
227
|
# File 'lib/feature_map.rb', line 222
def self.bust_caches!
@for_file = nil
@memoized_values = nil
Private.bust_caches!
Mapper.all.each(&:bust_caches!)
end
|
.configuration ⇒ Object
229
230
231
|
# File 'lib/feature_map.rb', line 229
def self.configuration
Private.configuration
end
|
.first_assigned_file_for_backtrace(backtrace, excluded_features: []) ⇒ Object
Given a backtrace from either ‘Exception#backtrace` or `caller`, find the first assigned file in it, useful for figuring out which file is being blamed.
121
122
123
124
125
126
127
128
129
|
# File 'lib/feature_map.rb', line 121
def first_assigned_file_for_backtrace(backtrace, excluded_features: [])
backtrace_with_feature_assignments(backtrace).each do |(feature, file)|
if feature && !excluded_features.include?(feature)
return [feature, file]
end
end
nil
end
|
.for_backtrace(backtrace, excluded_features: []) ⇒ Object
Given a backtrace from either ‘Exception#backtrace` or `caller`, find the first line that corresponds to a file with an assigned feature
115
116
117
|
# File 'lib/feature_map.rb', line 115
def for_backtrace(backtrace, excluded_features: [])
first_assigned_file_for_backtrace(backtrace, excluded_features: excluded_features)&.first
end
|
.for_class(klass) ⇒ Object
163
164
165
166
167
168
169
170
171
172
173
174
175
176
|
# File 'lib/feature_map.rb', line 163
def for_class(klass)
@memoized_values ||= {}
if @memoized_values.key?(klass.to_s)
@memoized_values[klass.to_s]
else
path = Private.path_from_klass(klass)
return nil if path.nil?
value_to_memoize = for_file(path)
@memoized_values[klass.to_s] = value_to_memoize
value_to_memoize
end
end
|
.for_feature(feature) ⇒ Object
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
# File 'lib/feature_map.rb', line 43
def for_feature(feature)
feature = CodeFeatures.find(feature)
feature_report = []
feature_report << "# Report for `#{feature.name}` Feature"
Private.glob_cache.raw_cache_contents.each do |mapper_description, glob_to_assigned_feature_map|
feature_report << "## #{mapper_description}"
file_assignments_for_mapper = []
glob_to_assigned_feature_map.each do |glob, assigned_feature|
next if assigned_feature != feature
file_assignments_for_mapper << "- #{glob}"
end
if file_assignments_for_mapper.empty?
feature_report << 'This feature does not have any files in this category.'
else
feature_report += file_assignments_for_mapper.sort
end
feature_report << ''
end
feature_report.join("\n")
end
|
.for_file(file) ⇒ Object
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
# File 'lib/feature_map.rb', line 26
def for_file(file)
@for_file ||= {}
return nil if file.start_with?('./')
return @for_file[file] if @for_file.key?(file)
Private.load_configuration!
feature = nil
Mapper.all.each do |mapper|
feature = mapper.map_file_to_feature(file)
break if feature
end
@for_file[file] = feature
end
|
.gather_simplecov_test_coverage!(simplecov_resultsets) ⇒ Object
101
102
103
|
# File 'lib/feature_map.rb', line 101
def gather_simplecov_test_coverage!(simplecov_resultsets)
Private.gather_simplecov_test_coverage!(simplecov_resultsets)
end
|
.gather_test_coverage!(commit_sha, code_cov_token) ⇒ Object
105
106
107
|
# File 'lib/feature_map.rb', line 105
def gather_test_coverage!(commit_sha, code_cov_token)
Private.gather_test_coverage!(commit_sha, code_cov_token)
end
|
.generate_additional_metrics! ⇒ Object
109
110
111
|
# File 'lib/feature_map.rb', line 109
def generate_additional_metrics!
Private.generate_additional_metrics!
end
|
.generate_docs!(git_ref) ⇒ Object
93
94
95
|
# File 'lib/feature_map.rb', line 93
def generate_docs!(git_ref)
Private.generate_docs!(git_ref)
end
|
.generate_release_notification(commits_by_feature) ⇒ Object
Generates a block kit message grouping the provided commits into sections for each feature impacted by the cheanges.
213
214
215
|
# File 'lib/feature_map.rb', line 213
def generate_release_notification(commits_by_feature)
Private.generate_release_notification(commits_by_feature)
end
|
.generate_test_pyramid!(unit_path, integration_path, regression_path, regression_assignments_path) ⇒ Object
97
98
99
|
# File 'lib/feature_map.rb', line 97
def generate_test_pyramid!(unit_path, integration_path, regression_path, regression_assignments_path)
Private.generate_test_pyramid!(unit_path, integration_path, regression_path, regression_assignments_path)
end
|
.group_commits(commits) ⇒ Object
Groups the provided list of commits (e.g. the changes being deployed in a release) by both the feature they impact and the teams responsible for these features. Returns a hash with keys for each team with features modified within these commits and values that are a hash of features to the set of commits that impact each feature.
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
|
# File 'lib/feature_map.rb', line 182
def group_commits(commits)
commits.each_with_object({}) do |commit, hash|
commit_features = commit.files.map do |file|
feature = FeatureMap.for_file(file)
next nil unless feature
teams = Private.all_teams_for_feature(feature)
team_names = teams.empty? ? [ALL_TEAMS_KEY] : teams.map(&:name)
team_names.sort.each do |team_name|
hash[team_name] ||= {}
hash[team_name][feature.name] ||= []
hash[team_name][feature.name] << commit unless hash[team_name][feature.name].include?(commit)
end
feature
end
next unless commit_features.compact.empty?
hash[ALL_TEAMS_KEY] ||= {}
hash[ALL_TEAMS_KEY][NO_FEATURE_KEY] ||= []
hash[ALL_TEAMS_KEY][NO_FEATURE_KEY] << commit
end
end
|
.remove_file_annotation!(filename) ⇒ Object
73
74
75
|
# File 'lib/feature_map.rb', line 73
def self.remove_file_annotation!(filename)
Private::AssignmentMappers::FileAnnotations.new.remove_file_annotation!(filename)
end
|
.validate!(autocorrect: true, stage_changes: true, files: nil) ⇒ Object
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
# File 'lib/feature_map.rb', line 77
def validate!(
autocorrect: true,
stage_changes: true,
files: nil
)
Private.load_configuration!
tracked_file_subset = if files
files.select { |f| Private.file_tracked?(f) }
else
Private.tracked_files
end
Private.validate!(files: tracked_file_subset, autocorrect: autocorrect, stage_changes: stage_changes)
end
|