couve 0.4.0 → 0.6.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: d2d77a7f4ff4d71e6a6b44dbbce503b7e24db2781949c690fe0dbea9abc1e925
4
- data.tar.gz: 6907bd5e49f9018a56fe693b53f31d320691fc2237d04b68a8d9e29c007295b1
3
+ metadata.gz: 91781d6bb92f7f023f6ab39ea4d0f7f707318bef6d09ef8cced4a9c2810e78c2
4
+ data.tar.gz: ff8ad3ff9c1a7c7c2318ab23d53db3a45f629d9b6c2f44104bf7c5d89beccb3b
5
5
  SHA512:
6
- metadata.gz: 2cf6728d574b655ef2a6e4632a2d103cccb2eeaba0ad1d620bc3d92db5f0a2b98b5480f4e7137b7099aba3cd4ca0b16247b003a7e84cba47a340c3785d200992
7
- data.tar.gz: 2d2c6c9e145e31e61b47c4fb1cf4350f4974a8d4ac34e448588925b5805ac0a57bfb59145f59298987749c512135a6456a699293cc57da079e034c9ef79217d4
6
+ metadata.gz: b921af295d7211b7db8d8fd3e3bea3628f64529ccc031a46a02fc88f42fada6b76acc21f4309f80d2faaea2783fa02783519c3503f67a2d44cfe3054b7c5831b
7
+ data.tar.gz: fb3836c3e78b7c686f84b559e7344398bc1bed5e6ffca394c6025a4a3389a0b01914f7ab945fbd3a65f697a57b3598da7b8772c919c5b5c20499691c620111f5
data/.rubocop.yml CHANGED
@@ -1,5 +1,30 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.6
2
+ TargetRubyVersion: 3.0.3
3
+ NewCops: enable
4
+ SuggestExtensions: false
5
+ Exclude:
6
+ - 'bin/**/*'
7
+ - 'config/**/*'
8
+ - 'log/**/*'
9
+ - 'tmp/**/*'
10
+ - 'public/**/*'
11
+ - 'vendor/**/*'
12
+ - 'db/**/*'
13
+ - 'spec/spec_helper.rb'
14
+ - 'spec/rails_helper.rb'
15
+
16
+ Bundler/OrderedGems:
17
+ Enabled: false
18
+
19
+ Gemspec/RequiredRubyVersion:
20
+ Enabled: false
21
+
22
+ Layout/LineLength:
23
+ Max: 120
24
+
25
+ Metrics/BlockLength:
26
+ Exclude:
27
+ - 'spec/**/*.rb'
3
28
 
4
29
  Style/StringLiterals:
5
30
  Enabled: true
@@ -9,12 +34,8 @@ Style/StringLiteralsInInterpolation:
9
34
  Enabled: true
10
35
  EnforcedStyle: double_quotes
11
36
 
12
- Layout/LineLength:
13
- Max: 120
37
+ Style/SymbolArray:
38
+ EnforcedStyle: brackets
14
39
 
15
40
  Style/Documentation:
16
41
  Enabled: false
17
-
18
- Metrics/BlockLength:
19
- Exclude:
20
- - 'spec/**/*.rb'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.6.0] - 2026-06-04
4
+
5
+ - Add Markdown output format, selected from the output file extension (`.md`). HTML remains the default.
6
+
3
7
  ## [0.1.0] - 2023-07-19
4
8
 
5
9
  - Initial release
data/Gemfile CHANGED
@@ -5,8 +5,10 @@ source "https://rubygems.org"
5
5
  # Specify your gem's dependencies in couve.gemspec
6
6
  gemspec
7
7
 
8
- gem "rake", "~> 13.0"
9
- gem "rspec", "~> 3.0"
10
- gem "rubocop", "~> 1.21"
11
- gem "guard", "~> 2.18"
12
- gem "guard-rspec", "~> 4.7"
8
+ gem "rake", "~> 13.0", group: [:test, :development]
9
+ gem "rspec", "~> 3.0", group: [:test, :development]
10
+ gem "rubocop", "~> 1.21", group: [:test, :development]
11
+ gem "guard", "~> 2.18", group: :development
12
+ gem "guard-rspec", "~> 4.7", group: :development, require: false
13
+ gem "nokogiri", "~> 1.15", group: [:test, :development]
14
+ gem "byebug", group: :development
data/Gemfile.lock CHANGED
@@ -1,12 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- couve (0.1.0)
4
+ couve (0.6.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
9
  ast (2.4.2)
10
+ byebug (11.1.3)
10
11
  coderay (1.1.3)
11
12
  diff-lcs (1.5.0)
12
13
  ffi (1.15.5)
@@ -32,7 +33,11 @@ GEM
32
33
  rb-inotify (~> 0.9, >= 0.9.10)
33
34
  lumberjack (1.2.9)
34
35
  method_source (1.0.0)
36
+ mini_portile2 (2.8.9)
35
37
  nenv (0.3.0)
38
+ nokogiri (1.15.3)
39
+ mini_portile2 (~> 2.8.2)
40
+ racc (~> 1.4)
36
41
  notiffany (0.1.3)
37
42
  nenv (~> 0.1)
38
43
  shellany (~> 0.0)
@@ -83,12 +88,16 @@ GEM
83
88
  unicode-display_width (2.4.2)
84
89
 
85
90
  PLATFORMS
91
+ arm64-darwin-23
86
92
  x86_64-darwin-21
93
+ x86_64-linux
87
94
 
88
95
  DEPENDENCIES
96
+ byebug
89
97
  couve!
90
98
  guard (~> 2.18)
91
99
  guard-rspec (~> 4.7)
100
+ nokogiri (~> 1.15)
92
101
  rake (~> 13.0)
93
102
  rspec (~> 3.0)
94
103
  rubocop (~> 1.21)
data/Guardfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # A sample Guardfile
2
4
  # More info at https://github.com/guard/guard#readme
3
5
 
@@ -18,5 +20,5 @@
18
20
  guard :rspec, cmd: "rspec" do
19
21
  watch(%r{^spec/.+_spec\.rb$})
20
22
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
21
- watch('spec/spec_helper.rb') { "spec" }
23
+ watch("spec/spec_helper.rb") { "spec" }
22
24
  end
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
- # Couve
1
+ # Couve - Generate Human Readable CodeClimate Test Reporter
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/couve`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
3
+ Couve is a Ruby gem that aims to simplify the generation of human-readable reports for the CodeClimate test-reporter gem. With Couve, you can quickly and easily generate insightful reports based on the test coverage data in a human-friendly format.
6
4
 
7
5
  ## Installation
8
6
 
@@ -22,18 +20,96 @@ Or install it yourself as:
22
20
 
23
21
  ## Usage
24
22
 
25
- TODO: Write usage instructions here
23
+ 1. Install the `couve` gem.
24
+
25
+ ```ruby
26
+ gem install couve
27
+ ```
28
+
29
+ 2. Run the following command in your terminal, providing the path to your JSON coverage file and the desired output file.
30
+
31
+ ```
32
+ $ couve path/to/coverage.json path/to/output.html
33
+ ```
34
+
35
+ Couve will process the coverage data and generate a human-readable HTML report, providing insights into your project's test coverage.
36
+
37
+ ### Output formats
38
+
39
+ Couve picks the output format from the output file's extension:
40
+
41
+ - `.html` (or anything else) — a self-contained, styled **HTML** report. Great for uploading as a CI artifact.
42
+ - `.md` — a **Markdown** report. Great for posting as a pull request comment.
43
+
44
+ ```
45
+ $ couve path/to/coverage.json path/to/report.html # HTML report
46
+ $ couve path/to/coverage.json path/to/report.md # Markdown report
47
+ ```
48
+
49
+ The Markdown report renders as a GitHub-flavored table, with a colored rating indicator (🔴/🟡/🟢) reflecting each file's coverage level:
50
+
51
+ ```markdown
52
+ ## Coverage problems
53
+
54
+ | Rating | Coverage | File | Not covered lines |
55
+ | :---: | ---: | :--- | :--- |
56
+ | 🔴 | 30% | app/models/foo.rb | 3, 8, 21 |
57
+ | 🟡 | 50% | app/services/bar.rb | 5, 6 |
58
+ ```
59
+
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:
61
+
62
+ ```sh
63
+ couve coverage.json coverage.html # upload as a CI artifact
64
+ couve coverage.json coverage.md # post to the PR
65
+ gh pr comment "$PR_NUMBER" --body-file coverage.md
66
+ ```
26
67
 
27
68
  ## Development
28
69
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
70
+ To contribute to Couve's development, follow these steps:
30
71
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
72
+ 1. Clone the repository from GitHub:
73
+
74
+ ```
75
+ $ git clone https://github.com/asseinfo/couve.git
76
+ ```
77
+
78
+ 2. Install the gem dependencies by running:
79
+
80
+ ```
81
+ $ bin/setup
82
+ ```
83
+
84
+ 3. Run the tests to ensure everything is set up correctly:
85
+
86
+ ```
87
+ $ rake spec
88
+ ```
89
+
90
+ 4. You can also use the interactive prompt to experiment with the code:
91
+
92
+ ```
93
+ $ bin/console
94
+ ```
32
95
 
33
96
  ## Contributing
34
97
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/couve.
98
+ We welcome bug reports and pull requests from the community. If you encounter any issues with Couve or have suggestions for improvements, please open an issue on [GitHub](https://github.com/asseinfo/couve) to let us know.
36
99
 
37
- ## License
100
+ If you'd like to contribute directly, please follow these steps:
101
+
102
+ 1. Fork the repository on GitHub.
103
+
104
+ 2. Create a new branch from the `main` branch.
38
105
 
39
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
106
+ 3. Make your changes and commit them with descriptive commit messages.
107
+
108
+ 4. Push your changes to your fork.
109
+
110
+ 5. Submit a pull request to the `main` branch of the original repository.
111
+
112
+ We appreciate your contributions and will review and merge pull requests as appropriate.
113
+
114
+ ## License
115
+ Couve is released under the [MIT License](https://opensource.org/licenses/MIT), which allows you to use, modify, and distribute the gem freely. See the LICENSE file for more details.
data/Rakefile CHANGED
@@ -9,4 +9,4 @@ require "rubocop/rake_task"
9
9
 
10
10
  RuboCop::RakeTask.new
11
11
 
12
- task default: %i[spec rubocop]
12
+ task default: [:spec, :rubocop]
data/bin/rubocop ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rubocop' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("rubocop", "rubocop")
data/couve.gemspec CHANGED
@@ -7,6 +7,7 @@ Gem::Specification.new do |spec|
7
7
  spec.version = Couve::VERSION
8
8
  spec.authors = ["Cezinha"]
9
9
  spec.email = ["cesar@asseinfo.com.br"]
10
+ spec.metadata = { "rubygems_mfa_required" => "true" }
10
11
 
11
12
  spec.summary = "Generate a human readable report for CodeClimate test-reporter gem."
12
13
 
@@ -20,6 +21,7 @@ Gem::Specification.new do |spec|
20
21
  spec.bindir = "exe"
21
22
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
22
23
  spec.require_paths = ["lib", "lib/couve"]
24
+ spec.required_ruby_version = ">= 3"
23
25
 
24
26
  # Uncomment to register a new dependency of your gem
25
27
  # spec.add_dependency "example-gem", "~> 1.0"
data/lib/couve/parser.rb CHANGED
@@ -1,67 +1,126 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "json"
2
4
 
3
- class Couve::Parser
4
- def initialize(coverage)
5
- @coverage = JSON.parse(coverage, symbolize_names: true)
6
- @coverage[:source_files].reject! { |file| file[:covered_percent] == 100 }
7
- @coverage[:source_files].sort_by! { |file| file[:covered_percent] }
8
- end
5
+ module Couve
6
+ class Parser
7
+ def initialize(coverage)
8
+ @coverage = JSON.parse(coverage, symbolize_names: true)
9
+ @coverage[:source_files].reject! { |file| file[:covered_percent] == 100 }
10
+ @coverage[:source_files].sort_by! { |file| file[:covered_percent] }
11
+ end
9
12
 
10
- def to_html
11
- <<~HTML
12
- <html>
13
- <body>
14
- <div class="container mt-5">
15
- <h1 class="display-5">
16
- Coverage problems
17
- </h1>
18
-
19
- <table class="table table-hover mt-5">
20
- <thead>
21
- <tr>
22
- <th class="col-1 text-end">Coverage</th>
23
- <th class="col-8">File</th>
24
- <th class="col-3">Not covered lines</th>
25
- </tr>
26
- </thead>
27
- #{body}
28
- </table>
29
- </div>
30
-
31
- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
32
- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
33
- </body>
34
- </html>
35
- HTML
36
- end
13
+ def to_html
14
+ <<~HTML
15
+ <html>
16
+ <body>
17
+ <div class="container mt-5">
18
+ <h1 class="display-5">
19
+ Coverage problems
20
+ </h1>
37
21
 
38
- private
22
+ <table class="table table-hover mt-5">
23
+ <thead>
24
+ <tr>
25
+ <th class="col-1" colspan="2">Coverage</th>
26
+ <th class="col-7">File</th>
27
+ <th class="col-3">Not covered lines</th>
28
+ </tr>
29
+ </thead>
30
+ #{body}
31
+ </table>
32
+ </div>
39
33
 
40
- def body
41
- html = ["<tbody>"]
34
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
35
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
36
+ </body>
37
+ </html>
38
+ HTML
39
+ end
42
40
 
43
- @coverage[:source_files].each do |source_file|
44
- html << " <tr>"
45
- html << " <td class=\"col-1 text-end\">#{source_file[:covered_percent].round(2)}%</td>"
46
- html << " <td class=\"col-8 text-break\">#{source_file[:name]}</td>"
47
- html << " <td class=\"col-3 text-break\">#{not_covered_lines(source_file)}</td>"
48
- html << " </tr>"
41
+ def to_markdown
42
+ rows = @coverage[:source_files].map do |source_file|
43
+ percentage = source_file[:covered_percent].round(2)
44
+ indicator = percentage_indicator(percentage)
45
+
46
+ "| #{indicator} | #{percentage}% | #{source_file[:name]} | #{not_covered_lines(source_file)} |"
47
+ end
48
+
49
+ <<~MARKDOWN
50
+ ## Coverage problems
51
+
52
+ | Rating | Coverage | File | Not covered lines |
53
+ | :---: | ---: | :--- | :--- |
54
+ #{rows.join("\n")}
55
+ MARKDOWN
49
56
  end
50
57
 
51
- html << "</tbody>"
58
+ private
52
59
 
53
- html.join("\n ")
54
- end
60
+ # rubocop:disable Metrics/MethodLength
61
+ # rubocop:disable Metrics/AbcSize
62
+ def body
63
+ html = ["<tbody>"]
55
64
 
56
- def not_covered_lines(source_file)
57
- total_lines = JSON.parse(source_file[:coverage])
65
+ @coverage[:source_files].each do |source_file|
66
+ percentage = source_file[:covered_percent].round(2)
67
+ bg_color = percentage_bar_color(percentage)
58
68
 
59
- not_covered = total_lines.each_with_index.map do |line, index|
60
- next if line != 0
69
+ html << " <tr>"
70
+ html << " <td class=\"col-1\">"
71
+ html << " <div class=\"progress\">"
72
+ html << " <div"
73
+ html << " class=\"progress-bar #{bg_color}\""
74
+ html << " role=\"progressbar\""
75
+ html << " style=\"width: #{percentage}%;\""
76
+ html << " aria-valuenow=\"#{percentage}\""
77
+ html << " aria-valuemin=\"0\" aria-valuemax=\"100\">"
78
+ html << " </div>"
79
+ html << " </div>"
80
+ html << " </td>"
81
+ html << " <td class=\"col-1\">#{percentage}%</td>"
82
+ html << " <td class=\"col-8 text-break\">#{source_file[:name]}</td>"
83
+ html << " <td class=\"col-3 text-break\">#{not_covered_lines(source_file)}</td>"
84
+ html << " </tr>"
85
+ end
61
86
 
62
- index + 1
87
+ html << "</tbody>"
88
+
89
+ html.join("\n ")
90
+ end
91
+ # rubocop:enable Metrics/AbcSize
92
+ # rubocop:enable Metrics/MethodLength
93
+
94
+ def percentage_bar_color(percentage)
95
+ if percentage < 33.33
96
+ "bg-danger"
97
+ elsif percentage < 66.66
98
+ "bg-warning"
99
+ else
100
+ "bg-success"
101
+ end
102
+ end
103
+
104
+ def percentage_indicator(percentage)
105
+ if percentage < 33.33
106
+ "🔴"
107
+ elsif percentage < 66.66
108
+ "🟡"
109
+ else
110
+ "🟢"
111
+ end
63
112
  end
64
113
 
65
- not_covered.compact.join(", ")
114
+ def not_covered_lines(source_file)
115
+ total_lines = JSON.parse(source_file[:coverage])
116
+
117
+ not_covered = total_lines.each_with_index.map do |line, index|
118
+ next if line != 0
119
+
120
+ index + 1
121
+ end
122
+
123
+ not_covered.compact.join(", ")
124
+ end
66
125
  end
67
126
  end
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.4.0"
4
+ VERSION = "0.6.0"
5
5
  end
data/lib/couve.rb CHANGED
@@ -11,8 +11,10 @@ module Couve
11
11
  coverage = File.read(coverage_file)
12
12
  parser = Couve::Parser.new(coverage)
13
13
 
14
+ report = output_file.end_with?(".md") ? parser.to_markdown : parser.to_html
15
+
14
16
  File.open(output_file, "w") do |f|
15
- f.puts parser.to_html
17
+ f.puts report
16
18
  end
17
19
  end
18
20
  end
data/tmp/.keep ADDED
File without changes
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.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cezinha
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-07-19 00:00:00.000000000 Z
11
+ date: 2026-06-04 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -30,15 +30,18 @@ files:
30
30
  - bin/_guard-core
31
31
  - bin/console
32
32
  - bin/guard
33
+ - bin/rubocop
33
34
  - bin/setup
34
35
  - couve.gemspec
35
36
  - exe/couve
36
37
  - lib/couve.rb
37
38
  - lib/couve/parser.rb
38
39
  - lib/couve/version.rb
40
+ - tmp/.keep
39
41
  homepage:
40
42
  licenses: []
41
- metadata: {}
43
+ metadata:
44
+ rubygems_mfa_required: 'true'
42
45
  post_install_message:
43
46
  rdoc_options: []
44
47
  require_paths:
@@ -48,14 +51,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
48
51
  requirements:
49
52
  - - ">="
50
53
  - !ruby/object:Gem::Version
51
- version: '0'
54
+ version: '3'
52
55
  required_rubygems_version: !ruby/object:Gem::Requirement
53
56
  requirements:
54
57
  - - ">="
55
58
  - !ruby/object:Gem::Version
56
59
  version: '0'
57
60
  requirements: []
58
- rubygems_version: 3.2.32
61
+ rubygems_version: 3.5.16
59
62
  signing_key:
60
63
  specification_version: 4
61
64
  summary: Generate a human readable report for CodeClimate test-reporter gem.