From cf153540a612e4227cb8f2c74011e380a37957df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20V=C3=A1squez?= Date: Fri, 14 Nov 2025 11:37:36 -0600 Subject: [PATCH 1/8] chore: update rubocop todo configuration Update rubocop todo file with latest offense counts and configurations --- test/lib/skunk/cli/options/argv_test.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/lib/skunk/cli/options/argv_test.rb b/test/lib/skunk/cli/options/argv_test.rb index 987b93b..4c8ee48 100644 --- a/test/lib/skunk/cli/options/argv_test.rb +++ b/test/lib/skunk/cli/options/argv_test.rb @@ -33,7 +33,6 @@ after do Skunk::Config.reset end - context "passing --formats option" do let(:argv) { ["--formats=json,html"] } From 49bc1e53a9170d703f0af4fc11ae5f648e096f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20V=C3=A1squez?= Date: Fri, 14 Nov 2025 16:56:40 -0600 Subject: [PATCH 2/8] refactor(config): centralize output path configuration and simplify printing Move output path configuration to Skunk::Config and remove file output logic from Application#print Update tests and documentation to reflect changes in output path handling Even if the default path where the JSON and HTML reports are generated are `tmp/rubycritic` you can now modify them. --- CHANGELOG.md | 1 + README.md | 6 +- bin/console | 3 +- lib/skunk/cli/application.rb | 8 +-- lib/skunk/cli/options/argv.rb | 69 ++++++------------- lib/skunk/config.rb | 13 ++++ lib/skunk/generators/console_report.rb | 16 ++++- lib/skunk/generators/html/overview.rb | 4 ++ lib/skunk/generators/json/simple.rb | 4 +- test/lib/skunk/cli/options/argv_test.rb | 26 ++++--- test/lib/skunk/commands/help_test.rb | 4 +- test/lib/skunk/config_test.rb | 9 +++ .../skunk/generators/console_report_test.rb | 15 ++++ .../skunk/generators/html/overview_test.rb | 26 +++++++ 14 files changed, 127 insertions(+), 77 deletions(-) create mode 100644 test/lib/skunk/generators/html/overview_test.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 91c4480..eb65b86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## main [(unreleased)](https://github.com/fastruby/skunk/compare/v0.5.4...HEAD) +* [FEATURE: Indicate `--out PATH` location](https://github.com/fastruby/skunk/pull/131) * [ENHANCEMENT: Support SimpleCov 1.0](https://github.com/fastruby/skunk/pull/140) * BUGFIX: Pin path_expander < 2.0 for Ruby 2.7 compatibility * [FEATURE: Add `--formats` CLI flag to select report formats (json, html, console)](https://github.com/fastruby/skunk/pull/130) diff --git a/README.md b/README.md index 1792f04..21a93ca 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Run `skunk -h` to check out the help options: ``` Usage: skunk [options] [paths] -b, --branch BRANCH Set branch to compare - -o, --out FILE Output report to file + -o, --out PATH Output report path -v, --version Show gem's version -h, --help Show this message ``` @@ -127,7 +127,9 @@ To only run skunk on specific folders, pass a list of directories in the command ### Generate JSON report in background -When the Skunk command is run, it will generate a JSON report file in the `RubyCritic::Config.root` location. +When the Skunk command is run, it will generate a JSON report file in the configured output path. + +Skunk also writes the console report to `skunk_console.txt` under the same output path. ### Comparing feature branches diff --git a/bin/console b/bin/console index 00c2b0a..623d374 100755 --- a/bin/console +++ b/bin/console @@ -13,5 +13,6 @@ puts ARGV.inspect require "skunk/cli/application" require "skunk/config" -Skunk::Config.formats = %i[json console html] +Skunk::Config.formats = %i[json console html] # supported output formats +Skunk::Config.root = "tmp/rubycritic" # default path to store generated JSON and HTML reports. Skunk::Cli::Application.new(ARGV).execute diff --git a/lib/skunk/cli/application.rb b/lib/skunk/cli/application.rb index d7c00b4..482178d 100644 --- a/lib/skunk/cli/application.rb +++ b/lib/skunk/cli/application.rb @@ -50,14 +50,8 @@ def warn_coverage_info warn "warning: Having no coverage metrics will make your SkunkScore worse." end - # :reek:NilCheck def print(message) - filename = @parsed_options[:output_filename] - if filename.nil? - $stdout.puts(message) - else - File.open(filename, "a") { |file| file << message } - end + $stdout.puts(message) end end end diff --git a/lib/skunk/cli/options/argv.rb b/lib/skunk/cli/options/argv.rb index 26825cd..e43038e 100644 --- a/lib/skunk/cli/options/argv.rb +++ b/lib/skunk/cli/options/argv.rb @@ -10,59 +10,32 @@ class Options # Extends RubyCritic::Cli::Options::Argv to parse a subset of the # parameters accepted by RubyCritic class Argv < RubyCritic::Cli::Options::Argv - # :reek:Attribute - attr_accessor :output_filename - def parse parser.new do |opts| opts.banner = "Usage: skunk [options] [paths]\n" - add_branch_option(opts) - add_output_option(opts) - add_formats_option(opts) - add_tail_options(opts) + opts.on("-b", "--branch BRANCH", "Set branch to compare") do |branch| + self.base_branch = String(branch) + set_current_branch + self.mode = :compare_branches + end + + opts.on("-o", "--out PATH", "Output report path") do |path| + Skunk::Config.root = path + end + + opts.on("-f", "--formats json,html,console", Array, "Output formats: json,html,console") do |list| + Skunk::Config.formats = Array(list).map(&:to_sym) + end + + opts.on_tail("-v", "--version", "Show gem's version") do + self.mode = :version + end + + opts.on_tail("-h", "--help", "Show this message") do + self.mode = :help + end end.parse!(@argv) end - - def to_h - super.merge(output_filename: output_filename) - end - - private - - def add_branch_option(opts) - opts.on("-b", "--branch BRANCH", "Set branch to compare") do |branch| - self.base_branch = String(branch) - set_current_branch - self.mode = :compare_branches - end - end - - def add_output_option(opts) - opts.on("-o", "--out FILE", "Output report to file") do |filename| - self.output_filename = filename - end - end - - def add_formats_option(opts) - opts.on( - "-f", - "--formats json,html,console", - Array, - "Output formats: json,html,console (default: console)" - ) do |list| - Skunk::Config.formats = Array(list).map(&:to_sym) - end - end - - def add_tail_options(opts) - opts.on_tail("-v", "--version", "Show gem's version") do - self.mode = :version - end - - opts.on_tail("-h", "--help", "Show this message") do - self.mode = :help - end - end end end end diff --git a/lib/skunk/config.rb b/lib/skunk/config.rb index 769590d..aeec57b 100644 --- a/lib/skunk/config.rb +++ b/lib/skunk/config.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "rubycritic/configuration" + module Skunk # Utility module for format validation module FormatValidator @@ -28,10 +30,12 @@ class Configuration def initialize @formats = [DEFAULT_FORMAT] + @root = RubyCritic::Config.root end def set(options = {}) self.formats = options[:formats] if options.key?(:formats) + self.root = options[:root] if options.key?(:root) end # Get the configured formats @@ -46,6 +50,14 @@ def formats=(format_list) @formats = [DEFAULT_FORMAT] if @formats.empty? end + def root + @root || File.expand_path("tmp/rubycritic", Dir.pwd) + end + + def root=(path) + @root = path.nil? || path.to_s.empty? ? nil : File.expand_path(path.to_s) + end + # Add a format to the existing list # @param format [Symbol] Format to add def add_format(format) @@ -77,6 +89,7 @@ def supported_formats # Reset to default configuration def reset @formats = [DEFAULT_FORMAT] + @root = RubyCritic::Config.root end end diff --git a/lib/skunk/generators/console_report.rb b/lib/skunk/generators/console_report.rb index 3b1958e..2154f0e 100644 --- a/lib/skunk/generators/console_report.rb +++ b/lib/skunk/generators/console_report.rb @@ -2,8 +2,11 @@ require "erb" require "terminal-table" +require "pathname" +require "fileutils" require "skunk/generators/console/simple" +require "skunk/config" module Skunk module Generator @@ -14,7 +17,10 @@ def initialize(analysed_modules) end def generate_report - puts generator.render + content = generator.render + puts content + FileUtils.mkdir_p(file_directory) + File.write(file_pathname, content) end private @@ -22,6 +28,14 @@ def generate_report def generator @generator ||= Skunk::Generator::Console::Simple.new(@analysed_modules) end + + def file_directory + @file_directory ||= Pathname.new(Skunk::Config.root) + end + + def file_pathname + Pathname.new(file_directory).join("skunk_console.txt") + end end end end diff --git a/lib/skunk/generators/html/overview.rb b/lib/skunk/generators/html/overview.rb index 78e7d2b..a3c60f8 100644 --- a/lib/skunk/generators/html/overview.rb +++ b/lib/skunk/generators/html/overview.rb @@ -49,6 +49,10 @@ def files FileData.new(module_data) end end + + def root_directory + @root_directory ||= Pathname.new(Skunk::Config.root) + end end end end diff --git a/lib/skunk/generators/json/simple.rb b/lib/skunk/generators/json/simple.rb index 0a686ce..353d767 100644 --- a/lib/skunk/generators/json/simple.rb +++ b/lib/skunk/generators/json/simple.rb @@ -2,7 +2,7 @@ require "pathname" -require "rubycritic/configuration" +require "skunk/config" require "skunk/rubycritic/analysed_modules_collection" module Skunk @@ -25,7 +25,7 @@ def data end def file_directory - @file_directory ||= Pathname.new(RubyCritic::Config.root) + @file_directory ||= Pathname.new(Skunk::Config.root) end def file_pathname diff --git a/test/lib/skunk/cli/options/argv_test.rb b/test/lib/skunk/cli/options/argv_test.rb index 4c8ee48..bcc421e 100644 --- a/test/lib/skunk/cli/options/argv_test.rb +++ b/test/lib/skunk/cli/options/argv_test.rb @@ -5,23 +5,21 @@ require "skunk/cli/options/argv" describe Skunk::Cli::Options::Argv do - describe "#output_filename" do - context "passing --out=FILE options" do - let(:argv) { ["--out=file.txt"] } + describe "--out path" do + after do + Skunk::Config.reset + end - it "parses passed filename" do - parser = Skunk::Cli::Options::Argv.new(argv) - parser.parse - _(parser.output_filename).must_equal "file.txt" - end + it "sets Skunk::Config.root to the provided path" do + parser = Skunk::Cli::Options::Argv.new(["--out=tmp/custom"]) + parser.parse + _(Skunk::Config.root).must_match(/tmp\/custom$/) end - context "not passing the --out option" do - it "is nil" do - parser = Skunk::Cli::Options::Argv.new([]) - parser.parse - _(parser.output_filename).must_be_nil - end + it "defaults to tmp/rubycritic when not provided" do + parser = Skunk::Cli::Options::Argv.new([]) + parser.parse + _(Skunk::Config.root).must_match(/tmp\/rubycritic$/) end end diff --git a/test/lib/skunk/commands/help_test.rb b/test/lib/skunk/commands/help_test.rb index 4f25c0d..6ff3d24 100644 --- a/test/lib/skunk/commands/help_test.rb +++ b/test/lib/skunk/commands/help_test.rb @@ -11,8 +11,8 @@ <<~HELP Usage: skunk [options] [paths] -b, --branch BRANCH Set branch to compare - -o, --out FILE Output report to file - -f, --formats json,html,console Output formats: json,html,console (default: console) + -o, --out PATH Output report path + -f, --formats json,html,console Output formats: json,html,console -v, --version Show gem's version -h, --help Show this message HELP diff --git a/test/lib/skunk/config_test.rb b/test/lib/skunk/config_test.rb index e708eae..d79f438 100644 --- a/test/lib/skunk/config_test.rb +++ b/test/lib/skunk/config_test.rb @@ -85,5 +85,14 @@ def test_reset Config.reset assert_equal [:console], Config.formats end + + def test_default_root + assert_match(/tmp\/rubycritic$/, Config.root) + end + + def test_set_root_expands_path + Config.root = "tmp/custom" + assert_equal File.expand_path("tmp/custom", Dir.pwd), Config.root + end end end diff --git a/test/lib/skunk/generators/console_report_test.rb b/test/lib/skunk/generators/console_report_test.rb index 6f880de..39c98e9 100644 --- a/test/lib/skunk/generators/console_report_test.rb +++ b/test/lib/skunk/generators/console_report_test.rb @@ -187,6 +187,21 @@ def test_generate_report_calls_generator_render assert_equal "test output\n", output mock_generator.verify end + + def test_generate_report_writes_console_file + Skunk::Config.root = "tmp/console_report" + begin + path = File.join(Skunk::Config.root, "skunk_console.txt") + File.delete(path) if File.exist?(path) + + @console_report.generate_report + + assert File.exist?(path) + assert_includes File.read(path), "SkunkScore Total" + ensure + FileUtils.rm_rf(Skunk::Config.root) + end + end end end end diff --git a/test/lib/skunk/generators/html/overview_test.rb b/test/lib/skunk/generators/html/overview_test.rb new file mode 100644 index 0000000..5f5d018 --- /dev/null +++ b/test/lib/skunk/generators/html/overview_test.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require "minitest/autorun" +require "skunk/generators/html/overview" +require "skunk/config" + +module Skunk + module Generator + module Html + class OverviewTest < Minitest::Test + def teardown + Skunk::Config.reset + end + + def test_root_directory_uses_skunk_config_root + Skunk::Config.root = "tmp/custom_html" + analysed_modules = Minitest::Mock.new + generator = Overview.new(analysed_modules) + + root = generator.send(:root_directory) + assert_equal File.expand_path("tmp/custom_html", Dir.pwd), root.to_s + end + end + end + end +end \ No newline at end of file From d1f5b57f9bc67acd2fc01fdf2ebd42b6bb08cf50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20V=C3=A1squez?= Date: Mon, 17 Nov 2025 15:20:17 -0600 Subject: [PATCH 3/8] Use same name on JSON, Console and HTML reports Skunk_report (txt,json,html) --- README.md | 2 +- lib/skunk/generators/console_report.rb | 2 +- lib/skunk/generators/html/overview.rb | 2 +- test/lib/skunk/generators/console_report_test.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 21a93ca..e16be5c 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ To only run skunk on specific folders, pass a list of directories in the command When the Skunk command is run, it will generate a JSON report file in the configured output path. -Skunk also writes the console report to `skunk_console.txt` under the same output path. +Skunk also writes the console report to `skunk_report.txt` under the same output path. ### Comparing feature branches diff --git a/lib/skunk/generators/console_report.rb b/lib/skunk/generators/console_report.rb index 2154f0e..774eae7 100644 --- a/lib/skunk/generators/console_report.rb +++ b/lib/skunk/generators/console_report.rb @@ -34,7 +34,7 @@ def file_directory end def file_pathname - Pathname.new(file_directory).join("skunk_console.txt") + Pathname.new(file_directory).join("skunk_report.txt") end end end diff --git a/lib/skunk/generators/html/overview.rb b/lib/skunk/generators/html/overview.rb index a3c60f8..fec1033 100644 --- a/lib/skunk/generators/html/overview.rb +++ b/lib/skunk/generators/html/overview.rb @@ -25,7 +25,7 @@ def initialize(analysed_modules) end def file_name - "skunk_overview.html" + "skunk_report.html" end def render diff --git a/test/lib/skunk/generators/console_report_test.rb b/test/lib/skunk/generators/console_report_test.rb index 39c98e9..c63e9d5 100644 --- a/test/lib/skunk/generators/console_report_test.rb +++ b/test/lib/skunk/generators/console_report_test.rb @@ -191,7 +191,7 @@ def test_generate_report_calls_generator_render def test_generate_report_writes_console_file Skunk::Config.root = "tmp/console_report" begin - path = File.join(Skunk::Config.root, "skunk_console.txt") + path = File.join(Skunk::Config.root, "skunk_report.txt") File.delete(path) if File.exist?(path) @console_report.generate_report From 9776e93c2cb7e10d1458d2bdbdbe867c5a31f7e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20V=C3=A1squez?= Date: Mon, 17 Nov 2025 15:57:13 -0600 Subject: [PATCH 4/8] test: fix share report sharing output --- lib/skunk/commands/status_sharer.rb | 6 ++++- test/lib/skunk/application_test.rb | 35 ++++++++++++++++------------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/lib/skunk/commands/status_sharer.rb b/lib/skunk/commands/status_sharer.rb index f46971a..653e21b 100644 --- a/lib/skunk/commands/status_sharer.rb +++ b/lib/skunk/commands/status_sharer.rb @@ -73,7 +73,11 @@ def share_enabled? # @return [Boolean] Check if share URL is empty def share_url_empty? - ENV["SHARE_URL"].to_s == "" + share_url == "" + end + + def share_url + ENV["SHARE_URL"].to_s end def payload diff --git a/test/lib/skunk/application_test.rb b/test/lib/skunk/application_test.rb index 8055646..5e2cc3f 100644 --- a/test/lib/skunk/application_test.rb +++ b/test/lib/skunk/application_test.rb @@ -2,6 +2,7 @@ require "test_helper" require "skunk/cli/application" +require "skunk/commands/default" require "rubycritic/core/analysed_module" require "minitest/stub_const" @@ -46,14 +47,15 @@ end context "when passing an environment variable SHARE=true" do - let(:argv) { ["--out=tmp/shared_report.txt", "samples/rubycritic"] } + let(:argv) { ["--out=tmp", "samples/rubycritic"] } let(:success_code) { 0 } - let(:shared_message) do - "Shared at: https://skunk.fastruby.io/j" - end + let(:generated_message) { "Generated with Skunk" } + let(:shared_message) { "Shared at: https://skunk.fastruby.io/j" } + let(:share_url) { "https://skunk.fastruby.io" } + let(:report_path) { "tmp/skunk_report.txt" } around do |example| - stub_request(:post, "https://skunk.fastruby.io/reports").to_return( + stub_request(:post, "#{share_url}/reports").to_return( status: 200, body: '{"id":"j"}', headers: { "Content-Type" => "application/json" } @@ -62,20 +64,21 @@ end it "share report to default server" do - FileUtils.rm("tmp/shared_report.txt", force: true) + FileUtils.rm(report_path, force: true) FileUtils.mkdir_p("tmp") - RubyCritic::AnalysedModule.stub_any_instance(:churn, 1) do - RubyCritic::AnalysedModule.stub_any_instance(:coverage, 100.0) do - Skunk::Command::Default.stub_any_instance(:share_enabled?, true) do - Skunk::Command::StatusSharer.stub_any_instance(:not_sharing?, false) do - Skunk::Command::StatusSharer.stub_any_instance(:share, "Shared at: https://skunk.fastruby.io/j") do - result = application.execute - _(result).must_equal success_code - output = File.read("tmp/shared_report.txt") - _(output).must_include(shared_message) - end + Skunk::Command::Default.stub_any_instance(:share_enabled?, true) do + Skunk::Command::StatusSharer.stub_any_instance(:share_url, share_url) do + Skunk::Command::StatusSharer.stub_any_instance(:share, "Shared at: #{share_url}/j") do + stdout = capture_stdout do + result = application.execute + _(result).must_equal success_code end + _(File.exist?(report_path)).must_equal true + file_output = File.read(report_path) + + _(file_output).must_include(generated_message) + _(stdout).must_include(shared_message) end end end From b66b365b3f0bd728c512e656090010d429783192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20V=C3=A1squez?= Date: Thu, 5 Feb 2026 23:27:39 -0600 Subject: [PATCH 5/8] refactor: apply review feedback on --out flag - Write reporter output with $stdout.puts directly instead of a private print wrapper in the application - Memoize the SHARE_URL lookups in StatusSharer and use String#empty? - Simplify Skunk::Config#root= now that to_s covers the nil case - Assert the default root against an explicit RubyCritic::Config.root instead of a path regex, restoring the prior value afterwards --- lib/skunk/cli/application.rb | 8 ++------ lib/skunk/commands/status_sharer.rb | 6 +++--- lib/skunk/config.rb | 3 ++- samples/rubycritic/compare/skunk_report.txt | 1 + test/lib/skunk/cli/options/argv_test.rb | 17 +++++++++++++---- test/lib/skunk/config_test.rb | 9 ++++++++- test/lib/skunk/generators/html/overview_test.rb | 2 +- 7 files changed, 30 insertions(+), 16 deletions(-) create mode 100644 samples/rubycritic/compare/skunk_report.txt diff --git a/lib/skunk/cli/application.rb b/lib/skunk/cli/application.rb index 482178d..06df963 100644 --- a/lib/skunk/cli/application.rb +++ b/lib/skunk/cli/application.rb @@ -31,10 +31,10 @@ def execute command = Skunk::CommandFactory.create(@parsed_options) reporter = command.execute - print(reporter.status_message) + $stdout.puts(reporter.status_message) if command.sharing? share_status_message = command.share(reporter) - print(share_status_message) + $stdout.puts(share_status_message) end reporter.status @@ -49,10 +49,6 @@ def warn_coverage_info warn "warning: Couldn't find coverage info at #{COVERAGE_FILE}." warn "warning: Having no coverage metrics will make your SkunkScore worse." end - - def print(message) - $stdout.puts(message) - end end end end diff --git a/lib/skunk/commands/status_sharer.rb b/lib/skunk/commands/status_sharer.rb index 653e21b..6e87d05 100644 --- a/lib/skunk/commands/status_sharer.rb +++ b/lib/skunk/commands/status_sharer.rb @@ -35,7 +35,7 @@ def share # :reek:UtilityFunction def base_url - ENV["SHARE_URL"] || DEFAULT_URL + @base_url ||= ENV["SHARE_URL"] || DEFAULT_URL end def json_summary @@ -73,11 +73,11 @@ def share_enabled? # @return [Boolean] Check if share URL is empty def share_url_empty? - share_url == "" + share_url.empty? end def share_url - ENV["SHARE_URL"].to_s + @share_url ||= ENV["SHARE_URL"].to_s end def payload diff --git a/lib/skunk/config.rb b/lib/skunk/config.rb index aeec57b..1cfb060 100644 --- a/lib/skunk/config.rb +++ b/lib/skunk/config.rb @@ -55,7 +55,8 @@ def root end def root=(path) - @root = path.nil? || path.to_s.empty? ? nil : File.expand_path(path.to_s) + path_str = path.to_s + @root = path_str.empty? ? nil : File.expand_path(path_str) end # Add a format to the existing list diff --git a/samples/rubycritic/compare/skunk_report.txt b/samples/rubycritic/compare/skunk_report.txt new file mode 100644 index 0000000..0866893 --- /dev/null +++ b/samples/rubycritic/compare/skunk_report.txt @@ -0,0 +1 @@ +test output \ No newline at end of file diff --git a/test/lib/skunk/cli/options/argv_test.rb b/test/lib/skunk/cli/options/argv_test.rb index bcc421e..04850fe 100644 --- a/test/lib/skunk/cli/options/argv_test.rb +++ b/test/lib/skunk/cli/options/argv_test.rb @@ -13,13 +13,22 @@ it "sets Skunk::Config.root to the provided path" do parser = Skunk::Cli::Options::Argv.new(["--out=tmp/custom"]) parser.parse - _(Skunk::Config.root).must_match(/tmp\/custom$/) + _(Skunk::Config.root).must_match(%r{tmp/custom$}) end it "defaults to tmp/rubycritic when not provided" do - parser = Skunk::Cli::Options::Argv.new([]) - parser.parse - _(Skunk::Config.root).must_match(/tmp\/rubycritic$/) + begin + prior_root = RubyCritic::Config.root + default_root = File.expand_path("tmp/rubycritic_default", Dir.pwd) + RubyCritic::Config.root = default_root + Skunk::Config.reset + parser = Skunk::Cli::Options::Argv.new([]) + parser.parse + _(Skunk::Config.root).must_equal default_root + ensure + RubyCritic::Config.root = prior_root || default_root + Skunk::Config.reset + end end end diff --git a/test/lib/skunk/config_test.rb b/test/lib/skunk/config_test.rb index d79f438..967e8e1 100644 --- a/test/lib/skunk/config_test.rb +++ b/test/lib/skunk/config_test.rb @@ -87,7 +87,14 @@ def test_reset end def test_default_root - assert_match(/tmp\/rubycritic$/, Config.root) + prior_root = RubyCritic::Config.root + default_root = File.expand_path("tmp/rubycritic_default", Dir.pwd) + RubyCritic::Config.root = default_root + Config.reset + assert_equal default_root, Config.root + ensure + RubyCritic::Config.root = prior_root || default_root + Config.reset end def test_set_root_expands_path diff --git a/test/lib/skunk/generators/html/overview_test.rb b/test/lib/skunk/generators/html/overview_test.rb index 5f5d018..05ecf5f 100644 --- a/test/lib/skunk/generators/html/overview_test.rb +++ b/test/lib/skunk/generators/html/overview_test.rb @@ -23,4 +23,4 @@ def test_root_directory_uses_skunk_config_root end end end -end \ No newline at end of file +end From 81f923a42c59b7f6d87ba01c7eb76cb78fc30ee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20V=C3=A1squez?= Date: Tue, 8 Sep 2026 12:18:57 -0600 Subject: [PATCH 6/8] style: restore RubyCritic root via an after hook in argv test RuboCop derives TargetRubyVersion from the gemspec's required_ruby_version (>= 2.4.0), so a begin/ensure spanning the whole block body trips Style/RedundantBegin on the RuboCop 1.31/1.50 resolved for Ruby 2.6 and 2.7, and the block level ensure that would replace it is 2.6+ syntax that RuboCop 1.81 rejects under the 2.4 parser. Move the restore into a minitest after hook, which is valid on every supported Ruby. --- test/lib/skunk/cli/options/argv_test.rb | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/test/lib/skunk/cli/options/argv_test.rb b/test/lib/skunk/cli/options/argv_test.rb index 04850fe..ba703d4 100644 --- a/test/lib/skunk/cli/options/argv_test.rb +++ b/test/lib/skunk/cli/options/argv_test.rb @@ -6,7 +6,12 @@ describe Skunk::Cli::Options::Argv do describe "--out path" do + before do + @prior_root = RubyCritic::Config.root + end + after do + RubyCritic::Config.root = @prior_root if @prior_root Skunk::Config.reset end @@ -17,18 +22,12 @@ end it "defaults to tmp/rubycritic when not provided" do - begin - prior_root = RubyCritic::Config.root - default_root = File.expand_path("tmp/rubycritic_default", Dir.pwd) - RubyCritic::Config.root = default_root - Skunk::Config.reset - parser = Skunk::Cli::Options::Argv.new([]) - parser.parse - _(Skunk::Config.root).must_equal default_root - ensure - RubyCritic::Config.root = prior_root || default_root - Skunk::Config.reset - end + default_root = File.expand_path("tmp/rubycritic_default", Dir.pwd) + RubyCritic::Config.root = default_root + Skunk::Config.reset + parser = Skunk::Cli::Options::Argv.new([]) + parser.parse + _(Skunk::Config.root).must_equal default_root end end From ab2a13e5e0b4c71353b2d055523373a48b43b922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20V=C3=A1squez?= Date: Tue, 8 Sep 2026 12:32:23 -0600 Subject: [PATCH 7/8] build: pin rubocop and drop stray sample report Every CI job resolves its own Gemfile.lock, so an unpinned rubocop meant Ruby 2.7 linted with 1.31, 2.6 with 1.50 and 3.x with 1.81, and a cop whose behavior changed between them failed the build on the old rubies only. Pin the version so the matrix lints against one rubocop. Rubies older than 3.1 cannot install the pin: reek and rubycritic hold parser below 3.3 there, while rubocop 1.60 and up require it. The Gemfile leaves rubocop out on those rubies and the Rakefile drops the lint task when the require fails, so 2.6, 2.7 and 3.0 still run the tests and reek. The gemspec listed rubocop as an unpinned development dependency, which is what drove the resolution, so drop it and let the Gemfile hold the pin. It stays a Gemfile-only dev tool, like it already was in practice. Also drop samples/rubycritic/compare/skunk_report.txt, a stray "test output" file no test reads. --- Gemfile | 8 +++++++- Rakefile | 15 +++++++++++---- samples/rubycritic/compare/skunk_report.txt | 1 - skunk.gemspec | 1 - test/lib/skunk/generators/console_report_test.rb | 7 +++++++ 5 files changed, 25 insertions(+), 7 deletions(-) delete mode 100644 samples/rubycritic/compare/skunk_report.txt diff --git a/Gemfile b/Gemfile index 6d1cc79..be2e125 100644 --- a/Gemfile +++ b/Gemfile @@ -8,4 +8,10 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } gemspec gem "reek" -gem "rubocop" + +# Pinned so the CI matrix lints against one RuboCop instead of each Ruby +# resolving whatever version it happens to support. Rubies older than 3.1 cannot +# install it: reek and rubycritic hold parser below 3.3 there, while RuboCop +# 1.60 and up require it, so those rubies get no RuboCop and the Rakefile drops +# the lint task for them. They still run the tests and reek. +gem "rubocop", "~> 1.81.0" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.1") diff --git a/Rakefile b/Rakefile index 7d65feb..defea95 100644 --- a/Rakefile +++ b/Rakefile @@ -2,7 +2,6 @@ require "bundler/gem_tasks" require "rake/testtask" -require "rubocop/rake_task" require "reek/rake/task" require "rubycritic/rake_task" @@ -12,12 +11,20 @@ Rake::TestTask.new do |task| task.pattern = "test/**/*_test.rb" end -RuboCop::RakeTask.new - Reek::Rake::Task.new RubyCritic::RakeTask.new do |task| task.paths = FileList["lib/**/*.rb"] end -task default: %i[test reek rubocop] +lint_tasks = [] + +begin + require "rubocop/rake_task" + RuboCop::RakeTask.new + lint_tasks << :rubocop +rescue LoadError + # RuboCop is not installed on the rubies the Gemfile skips the pin for. +end + +task default: %i[test reek] + lint_tasks diff --git a/samples/rubycritic/compare/skunk_report.txt b/samples/rubycritic/compare/skunk_report.txt deleted file mode 100644 index 0866893..0000000 --- a/samples/rubycritic/compare/skunk_report.txt +++ /dev/null @@ -1 +0,0 @@ -test output \ No newline at end of file diff --git a/skunk.gemspec b/skunk.gemspec index ad8f919..78133d5 100644 --- a/skunk.gemspec +++ b/skunk.gemspec @@ -50,7 +50,6 @@ Gem::Specification.new do |spec| spec.add_development_dependency "minitest-stub-const", "~> 0.6" spec.add_development_dependency "rake", "~> 13.0" spec.add_development_dependency "reek" - spec.add_development_dependency "rubocop" spec.add_development_dependency "simplecov", ">= 0.18", "< 2" spec.add_development_dependency "simplecov-console", "0.5.0" spec.add_development_dependency "webmock", "~> 3.20.0" diff --git a/test/lib/skunk/generators/console_report_test.rb b/test/lib/skunk/generators/console_report_test.rb index c63e9d5..9ac51a8 100644 --- a/test/lib/skunk/generators/console_report_test.rb +++ b/test/lib/skunk/generators/console_report_test.rb @@ -170,6 +170,10 @@ def test_generator_returns_console_simple_instance end def test_generate_report_calls_generator_render + # Write the report file under tmp so the run does not leave one behind + # in samples/ + Skunk::Config.root = "tmp/console_report_render" + # Test that generate_report calls the generator's render method @console_report.send(:generator) @@ -186,6 +190,9 @@ def test_generate_report_calls_generator_render assert_equal "test output\n", output mock_generator.verify + ensure + FileUtils.rm_rf(Skunk::Config.root) + Skunk::Config.reset end def test_generate_report_writes_console_file From 25e90a73feecd291f7b1b1238bda3034b60a90ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20V=C3=A1squez?= Date: Tue, 8 Sep 2026 12:53:53 -0600 Subject: [PATCH 8/8] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e16be5c..6003eb8 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,7 @@ You can choose one or more formats from the command line: ``` skunk --formats=json -skunk --f json,html +skunk -f json,html skunk --formats console,json ```