couve 0.7.0 → 0.9.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c6c4d593294316c9fe14a6a2d25d9029a5282c17ba53503a38b440c9b2c452f
4
- data.tar.gz: bc384881652d204bc8e7cbb337beb019bd3a49db0e3e01955865ee49660791df
3
+ metadata.gz: 4f4a216f249b8b99c5faf04dfef0f043f41c935955c2e0f4beedffe1aae99032
4
+ data.tar.gz: 124582fed81b0b3197a5a34219829b5f222a798e0afea8c3dc74f2d7bbc9d93b
5
5
  SHA512:
6
- metadata.gz: e90965ed10dd06d895bad918c452b5e9efeb6bc8d6b30ded117f3150c9fd1ddc51ff1e2cfbdaa4e3b28aa7bb186dc193632b6699ad0565849d38ac1f1b864272
7
- data.tar.gz: d1665ba532efcc3ed7177daa9e3cd3b101775e2647000800100187dc98932fe0b9f6ca4f8d5e14e4a653b68abe77e6b2fad1e6c0c951fb3690c818843b23ad06
6
+ metadata.gz: 2e195aa5e380cc001ddb50475e3ee46c97e60b8b160363a9f72f2563a22daa6c216b9f60327998ffd12efcf7eab34a0966ddb71080126c12081c36d11cd1d4d3
7
+ data.tar.gz: 957caf172202bc89862cb4e68c69975b157010846246615bbc7e9477eb27004c55f80a61a196206aa2cbf99a66117bf066a1d0d47ed398a03fb1ba29fa13e0f2
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.9.0] - 2026-06-09
4
+
5
+ - **Breaking:** raise the green threshold to 100%, so 🟢 now means the file is fully covered and `--fail-on-low-coverage` exits `1` for any reported file below 100% (previously 66.66%).
6
+ - Rate files and evaluate the coverage gate on the unrounded percentage, so a file at e.g. 99.996% no longer rounds up to 100% and slips past the gate.
7
+ - Display percentages with two decimal places in all cases (e.g. `60.00%`), flooring instead of rounding, so a partially covered file never displays as `100.00%`.
8
+
9
+ ## [0.8.0] - 2026-06-05
10
+
11
+ - Add `--fail-on-low-coverage` flag to exit with a non-zero status when any reported file is below 100% coverage.
12
+
3
13
  ## [0.7.0] - 2026-06-05
4
14
 
5
15
  - Add `--changed-files` to report the coverage status of a given list of files (e.g. a branch's changed files), including fully covered ones, instead of the project-wide below-100% view.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- couve (0.7.0)
4
+ couve (0.9.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -46,15 +46,15 @@ $ couve path/to/coverage.json path/to/report.html # HTML report
46
46
  $ couve path/to/coverage.json path/to/report.md # Markdown report
47
47
  ```
48
48
 
49
- The Markdown report renders as a GitHub-flavored table, with a colored rating indicator (🔴/🟡/🟢) reflecting each file's coverage level:
49
+ The Markdown report renders as a GitHub-flavored table, with a colored rating indicator reflecting each file's coverage level — 🔴 below 33.33%, 🟡 from there up to (but not including) 100%, and 🟢 only for fully covered files:
50
50
 
51
51
  ```markdown
52
52
  ## Coverage problems
53
53
 
54
54
  | Rating | Coverage | File | Not covered lines |
55
55
  | :---: | ---: | :--- | :--- |
56
- | 🔴 | 30% | app/models/foo.rb | 3, 8, 21 |
57
- | 🟡 | 50% | app/services/bar.rb | 5, 6 |
56
+ | 🔴 | 30.00% | app/models/foo.rb | 3, 8, 21 |
57
+ | 🟡 | 50.00% | app/services/bar.rb | 5, 6 |
58
58
  ```
59
59
 
60
60
  A typical CI setup keeps the HTML report as an artifact and posts the Markdown report to the pull request, e.g. with the GitHub CLI:
@@ -81,6 +81,27 @@ git diff --name-only origin/main...HEAD | couve coverage.json report.md --change
81
81
 
82
82
  Only files that appear both in the list and in the coverage data are shown; the rest of the project is left out. Coverage is still reported per file (the whole file's percentage and missed lines), not just the lines in your diff.
83
83
 
84
+ ### Failing the build on low coverage
85
+
86
+ By default couve always exits `0` — it only generates the report. Pass `--fail-on-low-coverage` to make couve exit `1` when any **reported** file is below 100% coverage, i.e. rated 🔴 or 🟡:
87
+
88
+ ```sh
89
+ couve coverage.json report.md --fail-on-low-coverage
90
+ ```
91
+
92
+ The report is written **before** couve exits, so the offending files stay visible in the report (and in any PR comment built from it). The offending files are also printed to standard error:
93
+
94
+ ```
95
+ couve: coverage below 100% in app/models/foo.rb, app/services/bar.rb
96
+ ```
97
+
98
+ The threshold is fixed at 100%; there is nothing to configure. Note that the default report only lists files below 100%, so project-wide the flag fails whenever the report is non-empty. In practice you'll want it together with `--changed-files`: when you only report the files you changed, only those files are evaluated, so a fully covered (or empty) changed-file set passes the build even if untouched files elsewhere are poorly covered.
99
+
100
+ ```sh
101
+ # Post the report to the PR, then redden the build if a changed file is under-covered:
102
+ git diff --name-only origin/main...HEAD | couve coverage.json report.md --changed-files - --fail-on-low-coverage
103
+ ```
104
+
84
105
  ## Development
85
106
 
86
107
  To contribute to Couve's development, follow these steps:
data/lib/couve/parser.rb CHANGED
@@ -3,7 +3,10 @@
3
3
  require "json"
4
4
 
5
5
  module Couve
6
- class Parser
6
+ class Parser # rubocop:disable Metrics/ClassLength
7
+ RED_THRESHOLD = 33.33
8
+ GREEN_THRESHOLD = 100
9
+
7
10
  def initialize(coverage, changed_files: nil)
8
11
  @coverage = JSON.parse(coverage, symbolize_names: true)
9
12
 
@@ -46,8 +49,8 @@ module Couve
46
49
 
47
50
  def to_markdown
48
51
  rows = @coverage[:source_files].map do |source_file|
49
- percentage = source_file[:covered_percent].round(2)
50
- indicator = percentage_indicator(percentage)
52
+ percentage = format("%.2f", source_file[:covered_percent].floor(2))
53
+ indicator = percentage_indicator(source_file[:covered_percent])
51
54
 
52
55
  "| #{indicator} | #{percentage}% | #{source_file[:name]} | #{not_covered_lines(source_file)} |"
53
56
  end
@@ -61,6 +64,16 @@ module Couve
61
64
  MARKDOWN
62
65
  end
63
66
 
67
+ def low_coverage_files
68
+ @coverage[:source_files]
69
+ .select { |source_file| source_file[:covered_percent] < GREEN_THRESHOLD }
70
+ .map { |source_file| source_file[:name] }
71
+ end
72
+
73
+ def low_coverage?
74
+ low_coverage_files.any?
75
+ end
76
+
64
77
  private
65
78
 
66
79
  # rubocop:disable Metrics/MethodLength
@@ -69,8 +82,8 @@ module Couve
69
82
  html = ["<tbody>"]
70
83
 
71
84
  @coverage[:source_files].each do |source_file|
72
- percentage = source_file[:covered_percent].round(2)
73
- bg_color = percentage_bar_color(percentage)
85
+ percentage = format("%.2f", source_file[:covered_percent].floor(2))
86
+ bg_color = percentage_bar_color(source_file[:covered_percent])
74
87
 
75
88
  html << " <tr>"
76
89
  html << " <td class=\"col-1\">"
@@ -98,9 +111,9 @@ module Couve
98
111
  # rubocop:enable Metrics/MethodLength
99
112
 
100
113
  def percentage_bar_color(percentage)
101
- if percentage < 33.33
114
+ if percentage < RED_THRESHOLD
102
115
  "bg-danger"
103
- elsif percentage < 66.66
116
+ elsif percentage < GREEN_THRESHOLD
104
117
  "bg-warning"
105
118
  else
106
119
  "bg-success"
@@ -108,9 +121,9 @@ module Couve
108
121
  end
109
122
 
110
123
  def percentage_indicator(percentage)
111
- if percentage < 33.33
124
+ if percentage < RED_THRESHOLD
112
125
  "🔴"
113
- elsif percentage < 66.66
126
+ elsif percentage < GREEN_THRESHOLD
114
127
  "🟡"
115
128
  else
116
129
  "🟢"
data/lib/couve/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Couve
4
- VERSION = "0.7.0"
4
+ VERSION = "0.9.0"
5
5
  end
data/lib/couve.rb CHANGED
@@ -7,7 +7,7 @@ require_relative "couve/parser"
7
7
 
8
8
  module Couve
9
9
  def self.start(argv = ARGV)
10
- coverage_file, output_file, changed_files_source = parse_arguments(argv)
10
+ coverage_file, output_file, changed_files_source, fail_on_low_coverage = parse_arguments(argv)
11
11
 
12
12
  coverage = File.read(coverage_file)
13
13
  parser = Couve::Parser.new(coverage, changed_files: changed_files(changed_files_source))
@@ -15,19 +15,30 @@ module Couve
15
15
  report = output_file.end_with?(".md") ? parser.to_markdown : parser.to_html
16
16
 
17
17
  File.open(output_file, "w") { |f| f.puts report }
18
+
19
+ return unless fail_on_low_coverage && parser.low_coverage?
20
+
21
+ warn "couve: coverage below #{Couve::Parser::GREEN_THRESHOLD}% in #{parser.low_coverage_files.join(", ")}"
22
+ exit 1
18
23
  end
19
24
 
20
- def self.parse_arguments(argv)
25
+ def self.parse_arguments(argv) # rubocop:disable Metrics/MethodLength
21
26
  argv = argv.dup
22
27
  changed_files_source = nil
28
+ fail_on_low_coverage = false
23
29
 
24
30
  OptionParser.new do |opts|
25
31
  opts.on("--changed-files PATH", "Only report the files listed in PATH (use - for stdin)") do |path|
26
32
  changed_files_source = path
27
33
  end
34
+
35
+ opts.on("--fail-on-low-coverage",
36
+ "Exit 1 if any reported file is below #{Couve::Parser::GREEN_THRESHOLD}% coverage") do
37
+ fail_on_low_coverage = true
38
+ end
28
39
  end.parse!(argv)
29
40
 
30
- [argv[0], argv[1], changed_files_source]
41
+ [argv[0], argv[1], changed_files_source, fail_on_low_coverage]
31
42
  end
32
43
 
33
44
  def self.changed_files(source)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: couve
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cezinha
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-06-05 00:00:00.000000000 Z
11
+ date: 2026-06-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: