diff --git a/.rubocop.yml b/.rubocop.yml index 1e2b453..9d6d2f7 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -16,6 +16,9 @@ Style/HashTransformValues: Metrics: Enabled: false +Layout/EndOfLine: + EnforcedStyle: lf + plugins: - rubocop-rake - rubocop-rspec diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 041c32f..ffdd64c 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,15 +1,14 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2025-07-05 14:01:58 UTC using RuboCop version 1.77.0. +# on 2026-01-30 00:01:30 UTC using RuboCop version 1.84.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offense count: 1 -# Configuration parameters: EnforcedStyle, AllowedGems, Include. +# Configuration parameters: EnforcedStyle, AllowedGems. # SupportedStyles: Gemfile, gems.rb, gemspec -# Include: **/*.gemspec, **/Gemfile, **/gems.rb Gemspec/DevelopmentDependencies: Exclude: - 'ruby-link-checker.gemspec' @@ -38,7 +37,8 @@ Layout/EmptyLineAfterMagicComment: # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Width, AllowedPatterns. +# Configuration parameters: Width, EnforcedStyleAlignWith, AllowedPatterns. +# SupportedStylesAlignWith: start_of_line, relative_to_receiver Layout/IndentationWidth: Exclude: - 'spec/ruby-link-checker/checker_spec.rb' @@ -141,7 +141,7 @@ RSpec/EmptyLineAfterExample: Exclude: - 'spec/ruby-link-checker/config_spec.rb' -# Offense count: 13 +# Offense count: 14 # Configuration parameters: CountAsOne. RSpec/ExampleLength: Max: 9 @@ -191,9 +191,9 @@ RSpec/NamedSubject: RSpec/NestedGroups: Max: 6 -# Offense count: 6 -# Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata. -# Include: **/*_spec.rb +# Offense count: 7 +# Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata, InflectorPath, EnforcedInflector. +# SupportedInflectors: default, active_support RSpec/SpecFilePathFormat: Exclude: - '**/spec/routing/**/*' @@ -201,6 +201,7 @@ RSpec/SpecFilePathFormat: - 'spec/ruby-link-checker/checker_spec.rb' - 'spec/ruby-link-checker/config_spec.rb' - 'spec/ruby-link-checker/net/http/checker_spec.rb' + - 'spec/ruby-link-checker/tasks_spec.rb' - 'spec/ruby-link-checker/typhoeus/hydra/checker_spec.rb' - 'spec/ruby-link-checker/version_spec.rb' @@ -307,6 +308,12 @@ Style/NumericPredicate: - 'spec/**/*' - 'lib/ruby-link-checker/tasks.rb' +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Style/RescueModifier: + Exclude: + - 'lib/ruby-link-checker/tasks.rb' + # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: Mode. @@ -344,7 +351,7 @@ Style/SuperWithArgsParentheses: # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. +# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings. # URISchemes: http, https Layout/LineLength: Max: 123 diff --git a/CHANGELOG.md b/CHANGELOG.md index b5bbdff..06ca3e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ### 0.3.1 (Next) +* [#13](https://github.com/dblock/ruby-link-checker/pull/13): Fix `SystemStackError` caused by infinite recursion in `_handle_result` - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/Copilot). * [#14](https://github.com/dblock/ruby-link-checker/pull/14): Migrate Danger to use danger-pr-comment workflow - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/Copilot). * [#14](https://github.com/dblock/ruby-link-checker/pull/14): Removed Code Climate - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/Copilot). * [#15](https://github.com/dblock/ruby-link-checker/pull/15): Added Codecov - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/Copilot). diff --git a/lib/ruby-link-checker/tasks.rb b/lib/ruby-link-checker/tasks.rb index 506f7f9..dd9afc0 100644 --- a/lib/ruby-link-checker/tasks.rb +++ b/lib/ruby-link-checker/tasks.rb @@ -103,8 +103,10 @@ def _handle_result(result) execute! end rescue StandardError => e - logger.error("#{self}##{__method__}") { e } - _handle_result ResultError.new(result.uri, result.method, result.result_uri, e, options) + logger.error("#{self}##{__method__}") { e } rescue nil + error_result = ResultError.new(result.uri, result.method, result.result_uri, e, options) + result! error_result + error! error_result end end end diff --git a/spec/ruby-link-checker/tasks_spec.rb b/spec/ruby-link-checker/tasks_spec.rb new file mode 100644 index 0000000..3de3dca --- /dev/null +++ b/spec/ruby-link-checker/tasks_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe LinkChecker::Tasks do + describe '#_handle_result' do + # Reproduces issue #12: SystemStackError when logging causes repeated errors + # https://github.com/dblock/ruby-link-checker/issues/12 + context 'when logger.info raises an error repeatedly' do + let(:checker) { LinkChecker::Net::HTTP::Checker.new(methods: ['GET']) } + let(:task_klass) { LinkChecker::Net::HTTP::Task } + let(:url) { 'https://www.example.org' } + + before do + stub_request(:get, url).to_return(status: 200, body: '', headers: {}) + end + + it 'does not cause infinite recursion leading to SystemStackError' do + tasks = described_class.new(checker, task_klass, url, ['GET']) + + # Stub logger to always raise an error on info, simulating the scenario + # from issue #12 where logging causes a stack overflow due to the + # rescue block in _handle_result recursively calling itself + allow(tasks).to receive(:logger).and_return( + instance_double(LinkChecker::Logger).tap do |logger| + allow(logger).to receive(:info).and_raise(StandardError, 'stack level too deep') + allow(logger).to receive(:error) + end + ) + + # Should not raise SystemStackError - the error should be handled gracefully + expect { tasks.execute! }.not_to raise_error + end + end + end +end