From 7067714c26e84717e27443f6c00abbd27e6756bc Mon Sep 17 00:00:00 2001 From: Mariusz Siklodi <9211589+siklodi-mariusz@users.noreply.github.com> Date: Wed, 24 Jun 2026 14:16:34 +0300 Subject: [PATCH] Support float maximum_decrease and rename YAML key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit threshold_score was coerced to an integer in argv.rb (Integer(...)) and configuration.rb (.to_i), so the smallest non-zero tolerance was a full point. Its sibling minimum_score — the same kind of 2-decimal score value — is already coerced as a float. Make threshold_score consistent by parsing it as a float (Float(...) / .to_f), so values like 0.5 work. Also rename the YAML config key from to to match the existing CLI flag. The legacy key still works but emits a deprecation warning; when both keys are present, takes precedence. The internal Config.threshold_score attribute is left as-is to limit churn. --- CHANGELOG.md | 3 + README.md | 4 +- lib/rubycritic/cli/options/argv.rb | 2 +- lib/rubycritic/cli/options/file.rb | 15 +++- lib/rubycritic/configuration.rb | 2 +- test/lib/rubycritic/cli/options/file_test.rb | 90 ++++++++++++++++++++ test/lib/rubycritic/commands/compare_test.rb | 25 ++++++ 7 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 test/lib/rubycritic/cli/options/file_test.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 1316935f..1f768290 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ * [CHANGE] Replace Aruba with direct API calls in specs (by [@faisal][]) * [CHANGE] Replace all Cucumber features with Minitest/Spec specs (by [@faisal][]) * [BUGFIX] Add `lang="en"` to the report's `` element, give the menu-toggle anchor an `aria-label`, and make the per-rating summary IDs unique. Fixes 17 WCAG 2.1 AA structural errors on `overview.html`. (by [@MarcusAl][]) +* [FEATURE] `--maximum-decrease` / `maximum_decrease` now accepts float values (e.g. `0.5`), mirroring how `minimum_score` is handled (by [@siklodi-mariusz][]) +* [CHANGE] Rename the `threshold_score` YAML key to `maximum_decrease` to match the `--maximum-decrease` CLI flag. The old `threshold_score` key still works but emits a deprecation warning; when both are present, `maximum_decrease` wins (by [@siklodi-mariusz][]) # v5.0.0 / 2026-01-26 [(commits)](https://github.com/whitesmith/rubycritic/compare/v4.12.0...v5.0.0) @@ -507,3 +509,4 @@ [@exoego]: https://github.com/exoego [@raff-s]: https://github.com/raff-s [@MarcusAl]: https://github.com/MarcusAl +[@siklodi-mariusz]: https://github.com/siklodi-mariusz diff --git a/README.md b/README.md index a7d6bb86..5b413fdd 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ $ rubycritic --help - `lint` 2. See [custom formatters docs](/docs/formatters.md) 3. Faster, analyses diffs w.r.t base_branch (default: main), see `-b` -4. Works only with `-b`, default: 0 +4. Works only with `-b`. Accepts a FLOAT (ex: 0.5), default: 0 You also can use a config file. Just create a `.rubycritic.yml` on your project root path. @@ -144,7 +144,7 @@ mode_ci: branch: 'production' # default is main path: '/tmp/mycustompath' # Set path where report will be saved (tmp/rubycritic by default) coverage_path: '/tmp/coverage' # Set path where SimpleCov coverage will be saved (./coverage by default) -threshold_score: 10 # default is 0 +maximum_decrease: 0.5 # FLOAT, default is 0.0 deduplicate_symlinks: true # default is false suppress_ratings: true # default is false no_browser: true # default is false diff --git a/lib/rubycritic/cli/options/argv.rb b/lib/rubycritic/cli/options/argv.rb index b1a18b1f..6cabb89e 100644 --- a/lib/rubycritic/cli/options/argv.rb +++ b/lib/rubycritic/cli/options/argv.rb @@ -32,7 +32,7 @@ def parse '--maximum-decrease [MAX_DECREASE]', 'Set a threshold for score difference between two branches (works only with -b)' ) do |threshold_score| - self.threshold_score = Integer(threshold_score) + self.threshold_score = Float(threshold_score) end formats = [] diff --git a/lib/rubycritic/cli/options/file.rb b/lib/rubycritic/cli/options/file.rb index a6cda245..09ddbcc5 100644 --- a/lib/rubycritic/cli/options/file.rb +++ b/lib/rubycritic/cli/options/file.rb @@ -64,8 +64,21 @@ def coverage_path options['coverage_path'] end + # The YAML key is `maximum_decrease` (matching the `--maximum-decrease` + # CLI flag). The legacy `threshold_score` key is still honoured for + # backward compatibility but is deprecated. When both are present, the + # new `maximum_decrease` key takes precedence. def threshold_score - options['threshold_score'] + warn_threshold_score_deprecation if options.key?('threshold_score') + + options.fetch('maximum_decrease') { options['threshold_score'] } + end + + def warn_threshold_score_deprecation + warn( + '[DEPRECATION] The `threshold_score` key in .rubycritic.yml is deprecated ' \ + 'and will be removed in a future release. Please use `maximum_decrease` instead.' + ) end def deduplicate_symlinks? diff --git a/lib/rubycritic/configuration.rb b/lib/rubycritic/configuration.rb index 31f7a288..e679b67d 100644 --- a/lib/rubycritic/configuration.rb +++ b/lib/rubycritic/configuration.rb @@ -20,7 +20,7 @@ def set(options) self.open_with = options[:open_with] self.no_browser = options[:no_browser] self.coverage_path = options[:coverage_path] - self.threshold_score = options[:threshold_score].to_i + self.threshold_score = options[:threshold_score].to_f setup_paths_for_targets(options) if options[:paths] setup_analysis_targets(options) setup_version_control(options) diff --git a/test/lib/rubycritic/cli/options/file_test.rb b/test/lib/rubycritic/cli/options/file_test.rb new file mode 100644 index 00000000..07a9e104 --- /dev/null +++ b/test/lib/rubycritic/cli/options/file_test.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +require 'test_helper' +require 'tmpdir' +require 'fileutils' +require 'yaml' +require 'rubycritic/cli/options/file' + +describe RubyCritic::Cli::Options::File do + before do + @dir = Dir.mktmpdir('rubycritic-file-options-') + end + + after do + FileUtils.remove_entry(@dir) if @dir && File.directory?(@dir) + end + + def options_for(config) + path = File.join(@dir, '.rubycritic.yml') + File.write(path, YAML.dump(config)) + options = RubyCritic::Cli::Options::File.new(path) + options.parse + options + end + + def capture_stderr + original = $stderr + $stderr = StringIO.new + yield + $stderr.string + ensure + $stderr = original + end + + describe 'maximum_decrease YAML key' do + it 'reads a float maximum_decrease value' do + options = options_for('maximum_decrease' => 0.5) + + _(options.to_h[:threshold_score]).must_equal 0.5 + end + + it 'does not emit a deprecation warning for the canonical key' do + options = options_for('maximum_decrease' => 0.5) + + warning = capture_stderr { options.to_h } + + _(warning).must_equal '' + end + end + + describe 'legacy threshold_score YAML key' do + it 'is still honoured for backward compatibility' do + options = options_for('threshold_score' => 0.5) + + result = nil + capture_stderr { result = options.to_h } + + _(result[:threshold_score]).must_equal 0.5 + end + + it 'emits a deprecation warning pointing to maximum_decrease' do + options = options_for('threshold_score' => 10) + + warning = capture_stderr { options.to_h } + + _(warning).must_include 'DEPRECATION' + _(warning).must_include 'threshold_score' + _(warning).must_include 'maximum_decrease' + end + end + + describe 'when both keys are present' do + it 'prefers the canonical maximum_decrease key' do + options = options_for('maximum_decrease' => 0.5, 'threshold_score' => 10) + + result = nil + capture_stderr { result = options.to_h } + + _(result[:threshold_score]).must_equal 0.5 + end + + it 'still warns about the deprecated threshold_score key' do + options = options_for('maximum_decrease' => 0.5, 'threshold_score' => 10) + + warning = capture_stderr { options.to_h } + + _(warning).must_include 'DEPRECATION' + end + end +end diff --git a/test/lib/rubycritic/commands/compare_test.rb b/test/lib/rubycritic/commands/compare_test.rb index d7c8d354..7453cb81 100644 --- a/test/lib/rubycritic/commands/compare_test.rb +++ b/test/lib/rubycritic/commands/compare_test.rb @@ -98,6 +98,31 @@ def abort(str); end end end + describe 'float threshold (maximum_decrease)' do + it 'parses a fractional -t value as a float instead of coercing to an integer' do + options = ['-b', 'base_branch', '-t', '0.5', 'test/samples/compare_file.rb'] + options = RubyCritic::Cli::Options.new(options).parse.to_h + + _(options[:threshold_score]).must_equal 0.5 + end + + it 'respects a fractional threshold when comparing branch scores' do + options = ['-b', 'base_branch', '-t', '0.5', 'test/samples/compare_file.rb'] + options = RubyCritic::Cli::Options.new(options).parse.to_h + RubyCritic::Config.set(options) + comparison = RubyCritic::Command::Compare.new(options) + + RubyCritic::Config.base_branch_score = 92.31 + RubyCritic::Config.feature_branch_score = 92.0 + # difference is 0.31, below the 0.5 threshold -> build must not fail + _(comparison.send(:threshold_reached?)).must_equal false + + RubyCritic::Config.feature_branch_score = 91.5 + # difference is 0.81, above the 0.5 threshold -> build must fail + _(comparison.send(:threshold_reached?)).must_equal true + end + end + describe 'with default options passing two branches' do before do options = ['-b', 'base_branch', '-t', '10', 'test/samples/compare_file.rb']