Class: FeatureMap::Cli
- Inherits:
-
Object
- Object
- FeatureMap::Cli
- Defined in:
- lib/feature_map/cli.rb
Class Method Summary collapse
- .additional_metrics!(argv) ⇒ Object
- .apply_assignments!(argv) ⇒ Object
- .docs!(argv) ⇒ Object
- .for_feature(argv) ⇒ Object
-
.for_file(argv) ⇒ Object
For now, this just returns feature assignment Later, this could also return feature assignment errors about that file.
- .run!(argv) ⇒ Object
- .test_coverage!(argv) ⇒ Object
- .test_pyramid!(argv) ⇒ Object
Class Method Details
.additional_metrics!(argv) ⇒ Object
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
# File 'lib/feature_map/cli.rb', line 252 def self.additional_metrics!(argv) parser = OptionParser.new do |opts| opts. = <<~MSG Usage: bin/featuremap additional_metrics Should be run after metrics and test coverage files have been generated MSG opts.on('--help', 'Shows this prompt') do puts opts exit end end args = parser.order!(argv) parser.parse!(args) FeatureMap.generate_additional_metrics! end |
.apply_assignments!(argv) ⇒ Object
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/feature_map/cli.rb', line 50 def self.apply_assignments!(argv) parser = OptionParser.new do |opts| opts. = <<~MSG Usage: bin/featuremap apply_assignments [assignments.csv]. Note: Expects two fields with no header: dir/filepath,feature Supports assignments in the following filetypes: cls,html,js,jsx,rb,ts,tsx,xml MSG opts.on('--help', 'Shows this prompt') do puts opts exit end end args = parser.order!(argv) parser.parse!(args) non_flag_args = argv.reject { |arg| arg.start_with?('--') } assignments_file_path = non_flag_args[0] raise 'Please specify assignments.csv file' if assignments_file_path.nil? FeatureMap.apply_assignments!(assignments_file_path) end |
.docs!(argv) ⇒ Object
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/feature_map/cli.rb', line 117 def self.docs!(argv) = {} parser = OptionParser.new do |opts| opts. = 'Usage: bin/featuremap docs [options] [target_commit_sha]' opts.on('-s', '--skip-stage', 'Skips staging the .feature_map/assignments.yml file') do [:skip_stage] = true end opts.on('--skip-validate', 'Skip the execution of the validate command, using the existing feature output files') do [:skip_validate] = true end opts.on('--skip-additional-metrics', 'Skip the execution of the additional_metrics command, using the existing feature output files') do [:skip_additional_metrics] = true end opts.on('--help', 'Shows this prompt') do puts opts exit end end args = parser.order!(argv) parser.parse!(args) non_flag_args = argv.reject { |arg| arg.start_with?('--') } custom_git_ref = non_flag_args[0] FeatureMap.validate!(stage_changes: ![:skip_stage]) unless [:skip_validate] FeatureMap.generate_additional_metrics! unless [:skip_additional_metrics] FeatureMap.generate_docs!(custom_git_ref) puts OutputColor.green('FeatureMap documentaiton site generated.') puts 'Open .feature_map/docs/index.html in a browser to view the documentation site.' end |
.for_feature(argv) ⇒ Object
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
# File 'lib/feature_map/cli.rb', line 319 def self.for_feature(argv) parser = OptionParser.new do |opts| opts. = 'Usage: bin/featuremap for_feature \'Onboarding\'' opts.on('--help', 'Shows this prompt') do puts opts exit end end features = argv.reject { |arg| arg.start_with?('--') } args = parser.order!(argv) parser.parse!(args) if features.count != 1 raise 'Please pass in one feature. Use `bin/featuremap for_feature --help` for more info' end puts FeatureMap.for_feature(features.first) end |
.for_file(argv) ⇒ Object
For now, this just returns feature assignment Later, this could also return feature assignment errors about that file.
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/feature_map/cli.rb', line 272 def self.for_file(argv) = {} # Long-term, we probably want to use something like `thor` so we don't have to implement logic # like this. In the short-term, this is a simple way for us to use the built-in OptionParser # while having an ergonomic CLI. files = argv.reject { |arg| arg.start_with?('--') } parser = OptionParser.new do |opts| opts. = 'Usage: bin/featuremap for_file [options]' opts.on('--json', 'Output as JSON') do [:json] = true end opts.on('--help', 'Shows this prompt') do puts opts exit end end args = parser.order!(argv) parser.parse!(args) if files.count != 1 raise 'Please pass in one file. Use `bin/featuremap for_file --help` for more info' end feature = FeatureMap.for_file(files.first) feature_name = feature&.name || 'Unassigned' feature_yml = feature&.config_yml || 'Unassigned' if [:json] json = { feature_name: feature_name, feature_yml: feature_yml } puts json.to_json else puts <<~MSG Feature: #{feature_name} Feature YML: #{feature_yml} MSG end end |
.run!(argv) ⇒ Object
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/feature_map/cli.rb', line 9 def self.run!(argv) command = argv.shift if command == 'apply_assignments' apply_assignments!(argv) elsif command == 'validate' validate!(argv) elsif command == 'docs' docs!(argv) elsif command == 'test_coverage' test_coverage!(argv) elsif command == 'test_pyramid' test_pyramid!(argv) elsif command == 'additional_metrics' additional_metrics!(argv) elsif command == 'for_file' for_file(argv) elsif command == 'for_feature' for_feature(argv) elsif [nil, 'help'].include?(command) puts <<~USAGE Usage: bin/featuremap <subcommand> Subcommands: apply_assignments - applies specified feature assignments to source files docs - generates feature documentation for_feature - find assignment information for a feature for_file - find feature assignment for a single file test_coverage - generates per-feature test coverage statistics test_pyramid - generates per-feature test pyramid (unit, integration, regression) statistics additional_metrics - generates additional metrics per-feature (e.g. health score) validate - run all validations ################################################## help - display help information about feature_map ################################################## USAGE else puts "'#{command}' is not a feature_map command. See `bin/featuremap help`." end end |
.test_coverage!(argv) ⇒ Object
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 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 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/feature_map/cli.rb', line 155 def self.test_coverage!(argv) = { source: :codecov, simplecov_paths: [] } parser = OptionParser.new do |opts| opts. = <<~MSG Usage: bin/featuremap test_coverage [options] [code_cov_commit_sha]. Options: --use-simplecov Use SimpleCov instead of CodeCov --simplecov-path PATH Path to a SimpleCov resultset.json file (can be specified multiple times) Note:#{' '} - CodeCov mode requires environment variable `CODECOV_API_TOKEN` - CodeCov mode uses the provided commit SHA or defaults to the latest commit on main - SimpleCov mode requires at least one path to be specified with --simplecov-path - SimpleCov and CodeCov modes cannot be used together MSG opts.on('--use-simplecov', 'Use SimpleCov instead of CodeCov') do [:source] = :simplecov end opts.on('--simplecov-path PATH', 'Use SimpleCov JSON resultset file instead of CodeCov. May be specified multiple times.') do |path| [:simplecov_paths] << path end opts.on('--help', 'Shows this prompt') do puts opts exit end end args = parser.order!(argv) parser.parse!(args) non_flag_args = argv.reject { |arg| arg.start_with?('--') } custom_commit_sha = non_flag_args[0] case [:source] when :codecov code_cov_token = ENV.fetch('CODECOV_API_TOKEN', '') raise 'Please specify a CodeCov API token in your environment as `CODECOV_API_TOKEN`' if code_cov_token.empty? # If no commit SHA was provided in the CLI command args, use the most recent commit of the main branch in the upstream remote. commit_sha = custom_commit_sha || `git log -1 --format=%H origin/main`.chomp puts "Pulling test coverage statistics for commit #{commit_sha}" FeatureMap.gather_test_coverage!(commit_sha, code_cov_token) when :simplecov missing_paths = [:simplecov_paths].reject { |path| File.exist?(path) } raise 'Error: When using --use-simplecov, you must specify at least one path with --simplecov-path.' if [:simplecov_paths].empty? raise "SimpleCov results file not found: #{missing_paths.join(', ')}" if missing_paths.any? raise 'Error: Cannot specify a commit SHA when using --simplecov. These options are incompatible.' if custom_commit_sha puts "Gathering test coverage statistics from SimpleCov files: #{[:simplecov_paths].join(', ')}" FeatureMap.gather_simplecov_test_coverage!([:simplecov_paths]) else raise 'Invalid source' end puts OutputColor.green('FeatureMap test coverage statistics collected.') puts 'View the resulting test coverage for each feature in .feature_map/test-coverage.yml' end |
.test_pyramid!(argv) ⇒ Object
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/feature_map/cli.rb', line 222 def self.test_pyramid!(argv) parser = OptionParser.new do |opts| opts. = <<~MSG Usage: bin/featuremap test_pyramid [unit_path] [integration_path] [regression_path] [regression_assignments_path]. Paths should point to files containing json-formatted rspec test summaries. These can be generated via rspec's `-f j` flag. Regression test summary and assignments file path are optional. MSG opts.on('--help', 'Shows this prompt') do puts opts exit end end args = parser.order!(argv) parser.parse!(args) non_flag_args = argv.reject { |arg| arg.start_with?('--') } file_paths = non_flag_args.first(4) if file_paths.compact.size == 2 unit_path, integration_path = file_paths elsif file_paths.compact.size == 4 unit_path, integration_path, regression_path, regression_assignments_path = file_paths else raise 'Please specify at least the [unit_path] and [integration_path] arguments. If regression test details are provided both the [regression_path] and [regression_assignments_path] arguments must be populated.' end FeatureMap.generate_test_pyramid!(unit_path, integration_path, regression_path, regression_assignments_path) end |