Module: SimpleCov::CLI::Merge
- Defined in:
- lib/simplecov/cli/merge.rb
Overview
‘simplecov merge <files…>` — wrap SimpleCov::ResultMerger so a CI matrix that produces one .resultset.json per worker can stitch them together from the shell instead of dropping a Rake task into every project. Requires the full simplecov library to be on the load path; lazy-required so the read-only subcommands above don’t pay for ResultMerger (and its Coverage runtime guard).
Class Method Summary collapse
- .commit(opts, result, stdout) ⇒ Object
- .duplicate_warning(command_name, paths) ⇒ Object
- .error(stderr, message) ⇒ Object
- .parse(args) ⇒ Object
- .parse_input(path, stderr) ⇒ Object
- .parse_input_error(stderr, path, reason) ⇒ Object
-
.parse_inputs(files, stderr) ⇒ Object
Validate every input file up-front and return a => parsed hash.
- .run(args, stdout:, stderr:) ⇒ Object
- .valid_inputs?(files, stderr) ⇒ Boolean
-
.warn_about_duplicate_command_names(parsed, stderr) ⇒ Object
When two input files share a command_name, ResultMerger folds them together with last-write-wins on the timestamp — easy to mistake for “no merge happened.” Surface the overlap so the operator can rename the workers or accept the merge knowingly.
- .write(path, result) ⇒ Object
Class Method Details
.commit(opts, result, stdout) ⇒ Object
30 31 32 33 34 |
# File 'lib/simplecov/cli/merge.rb', line 30 def commit(opts, result, stdout) verb = opts[:dry_run] ? "would write" : "wrote" write(opts[:output], result) unless opts[:dry_run] stdout.puts("simplecov merge: #{verb} #{opts[:output]}") unless opts[:quiet] end |
.duplicate_warning(command_name, paths) ⇒ Object
98 99 100 101 102 |
# File 'lib/simplecov/cli/merge.rb', line 98 def duplicate_warning(command_name, paths) "simplecov merge: warning — command_name #{command_name.inspect} " \ "appears in #{paths.size} input files (#{paths.join(', ')}); " \ "entries will be merged" end |
.error(stderr, message) ⇒ Object
110 111 112 113 |
# File 'lib/simplecov/cli/merge.rb', line 110 def error(stderr, ) stderr.puts("simplecov merge: #{}") 1 end |
.parse(args) ⇒ Object
43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/simplecov/cli/merge.rb', line 43 def parse(args) opts = {output: SimpleCov::CLI.default_resultset, honor_timeout: false, dry_run: false, quiet: false} files = OptionParser.new do |o| o.on("--output PATH") { |v| opts[:output] = v } o.on("--honor-timeout") { opts[:honor_timeout] = true } o.on("--dry-run") { opts[:dry_run] = true } o.on("-q", "--quiet") { opts[:quiet] = true } end.parse(args) opts.merge(files: files) end |
.parse_input(path, stderr) ⇒ Object
67 68 69 70 71 72 73 74 75 76 |
# File 'lib/simplecov/cli/merge.rb', line 67 def parse_input(path, stderr) return parse_input_error(stderr, path, "not found") unless File.exist?(path) data = JSON.parse(File.read(path)) return data if data.is_a?(Hash) && !data.empty? parse_input_error(stderr, path, "has no resultset entries") rescue JSON::ParserError => e parse_input_error(stderr, path, "isn't valid JSON (#{e.})") end |
.parse_input_error(stderr, path, reason) ⇒ Object
78 79 80 81 |
# File 'lib/simplecov/cli/merge.rb', line 78 def parse_input_error(stderr, path, reason) stderr.puts("simplecov merge: input file #{path.inspect} #{reason}") nil end |
.parse_inputs(files, stderr) ⇒ Object
Validate every input file up-front and return a => parsed hash. Surfacing per-file errors here turns ResultMerger’s generic “no mergeable results” into a message that points at the specific input causing the failure.
59 60 61 62 63 64 65 |
# File 'lib/simplecov/cli/merge.rb', line 59 def parse_inputs(files, stderr) files.each_with_object({}) do |path, memo| data = parse_input(path, stderr) or return nil memo[path] = data end end |
.run(args, stdout:, stderr:) ⇒ Object
17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/simplecov/cli/merge.rb', line 17 def run(args, stdout:, stderr:, **) opts = parse(args) return error(stderr, "missing input files") if opts[:files].empty? return 1 unless valid_inputs?(opts[:files], stderr) require "simplecov" result = SimpleCov::ResultMerger.merge_results(*opts[:files], ignore_timeout: !opts[:honor_timeout]) return error(stderr, "no mergeable results in input files") unless result commit(opts, result, stdout) 0 end |
.valid_inputs?(files, stderr) ⇒ Boolean
36 37 38 39 40 41 |
# File 'lib/simplecov/cli/merge.rb', line 36 def valid_inputs?(files, stderr) parsed = parse_inputs(files, stderr) or return false warn_about_duplicate_command_names(parsed, stderr) true end |
.warn_about_duplicate_command_names(parsed, stderr) ⇒ Object
When two input files share a command_name, ResultMerger folds them together with last-write-wins on the timestamp — easy to mistake for “no merge happened.” Surface the overlap so the operator can rename the workers or accept the merge knowingly.
87 88 89 90 91 92 93 94 95 96 |
# File 'lib/simplecov/cli/merge.rb', line 87 def warn_about_duplicate_command_names(parsed, stderr) files_per_command = parsed.each_with_object({}) do |(path, data), memo| data.each_key { |command_name| (memo[command_name] ||= []) << path } end files_per_command.each do |command_name, paths| next if paths.size < 2 stderr.puts(duplicate_warning(command_name, paths)) end end |
.write(path, result) ⇒ Object
104 105 106 107 108 |
# File 'lib/simplecov/cli/merge.rb', line 104 def write(path, result) require "fileutils" FileUtils.mkdir_p(File.dirname(path)) File.write(path, JSON.pretty_generate(result.to_hash)) end |