Module: Polyrun::Reporting::FailureMerge
- Defined in:
- lib/polyrun/reporting/failure_merge.rb
Overview
Merge per-worker / per-shard failure fragments (JSONL or RSpec JSON) into one report. Fragment basenames align with Coverage::CollectorFragmentMeta (worker index and optional matrix shard).
Constant Summary collapse
- DEFAULT_FRAGMENT_DIR =
"tmp/polyrun_failures".freeze
- FRAGMENT_GLOB =
"polyrun-failure-fragment-*.jsonl".freeze
Class Method Summary collapse
- .collect_rows(paths) ⇒ Object
- .default_fragment_glob(dir = nil) ⇒ Object
- .failures_from_rspec_examples(examples) ⇒ Object
-
.merge_files!(paths, output:, format: "jsonl") ⇒ Integer
Count of failure rows merged.
- .merge_fragment_paths(quiet: false) ⇒ Object
- .parse_jsonl_line!(path, line_number, line) ⇒ Object
- .rows_from_jsonl_file(path) ⇒ Object
- .rows_from_path(path) ⇒ Object
- .rspec_example_to_row(ex) ⇒ Object
Class Method Details
.collect_rows(paths) ⇒ Object
54 55 56 57 58 59 60 |
# File 'lib/polyrun/reporting/failure_merge.rb', line 54 def collect_rows(paths) rows = [] paths.each do |p| rows.concat(rows_from_path(p)) end rows end |
.default_fragment_glob(dir = nil) ⇒ Object
14 15 16 17 |
# File 'lib/polyrun/reporting/failure_merge.rb', line 14 def default_fragment_glob(dir = nil) root = File.(dir || DEFAULT_FRAGMENT_DIR, Dir.pwd) File.join(root, FRAGMENT_GLOB) end |
.failures_from_rspec_examples(examples) ⇒ Object
109 110 111 112 113 114 115 116 |
# File 'lib/polyrun/reporting/failure_merge.rb', line 109 def failures_from_rspec_examples(examples) examples.each_with_object([]) do |ex, acc| next unless ex.is_a?(Hash) next unless ex["status"].to_s == "failed" acc << rspec_example_to_row(ex) end end |
.merge_files!(paths, output:, format: "jsonl") ⇒ Integer
Returns count of failure rows merged.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/polyrun/reporting/failure_merge.rb', line 30 def merge_files!(paths, output:, format: "jsonl") fmt = format.to_s.downcase rows = collect_rows(paths) out_abs = File.(output) FileUtils.mkdir_p(File.dirname(out_abs)) case fmt when "json" doc = { "meta" => { "polyrun_merge" => true, "inputs" => paths.map { |p| File.(p) }, "failure_count" => rows.size }, "failures" => rows } File.write(out_abs, JSON.generate(doc)) when "jsonl" File.write(out_abs, rows.map { |h| JSON.generate(h) }.join("\n") + (rows.empty? ? "" : "\n")) else raise Polyrun::Error, "merge-failures: unknown format #{fmt.inspect} (use jsonl or json)" end rows.size end |
.merge_fragment_paths(quiet: false) ⇒ Object
19 20 21 22 23 24 |
# File 'lib/polyrun/reporting/failure_merge.rb', line 19 def merge_fragment_paths(quiet: false) p = default_fragment_glob Dir.glob(p).sort.tap do |paths| Polyrun::Log.warn "merge-failures: no files matched #{p}" if paths.empty? && !quiet end end |
.parse_jsonl_line!(path, line_number, line) ⇒ Object
102 103 104 105 106 107 |
# File 'lib/polyrun/reporting/failure_merge.rb', line 102 def parse_jsonl_line!(path, line_number, line) JSON.parse(line) rescue JSON::ParserError => e raise Polyrun::Error, "merge-failures: invalid JSONL at #{path} line #{line_number}: #{e.}" end |
.rows_from_jsonl_file(path) ⇒ Object
91 92 93 94 95 96 97 98 99 100 |
# File 'lib/polyrun/reporting/failure_merge.rb', line 91 def rows_from_jsonl_file(path) acc = [] File.readlines(path, chomp: true).each_with_index do |line, idx| line = line.strip next if line.empty? acc << parse_jsonl_line!(path, idx + 1, line) end acc end |
.rows_from_path(path) ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/polyrun/reporting/failure_merge.rb', line 62 def rows_from_path(path) ext = File.extname(path).downcase if ext == ".jsonl" return rows_from_jsonl_file(path) end text = File.read(path) data = begin JSON.parse(text) rescue JSON::ParserError => e raise Polyrun::Error, "merge-failures: #{path} is not valid JSON: #{e.}" end if data.is_a?(Hash) && data["examples"].is_a?(Array) return failures_from_rspec_examples(data["examples"]) end hint = if data.is_a?(Hash) keys = data.keys "got JSON object with keys: #{keys.take(12).join(", ")}" + ((keys.size > 12) ? ", …" : "") else "got #{data.class}" end raise Polyrun::Error, "merge-failures: #{path} is not RSpec JSON (expected top-level \"examples\" array). #{hint}. " \ "Use RSpec --format json, or polyrun failure JSONL (.jsonl fragments)." end |
.rspec_example_to_row(ex) ⇒ Object
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/polyrun/reporting/failure_merge.rb', line 118 def rspec_example_to_row(ex) ex = ex.transform_keys(&:to_s) exc = ex["exception"] || {} exc = exc.transform_keys(&:to_s) if exc.is_a?(Hash) { "id" => ex["id"], "full_description" => ex["full_description"], "location" => (ex["file_path"] && ex["line_number"]) ? "#{ex["file_path"]}:#{ex["line_number"]}" : ex["full_description"], "file_path" => ex["file_path"], "line_number" => ex["line_number"], "message" => exc["message"] || ex["full_description"], "exception_class" => exc["class"], "source" => "rspec_json" }.compact end |