1# frozen_string_literal: true 2 3require 'deprecation_toolkit' 4require 'deprecation_toolkit/rspec' 5 6module DeprecationToolkitEnv 7 module DeprecationBehaviors 8 class SelectiveRaise 9 attr_reader :disallowed_deprecations_proc 10 11 class RaiseDisallowedDeprecation < StandardError 12 def initialize(test, current_deprecations) 13 message = <<~EOF 14 Disallowed deprecations detected while running test #{test}: 15 16 #{current_deprecations.deprecations.join("\n")} 17 EOF 18 19 super(message) 20 end 21 end 22 23 def initialize(disallowed_deprecations_proc) 24 @disallowed_deprecations_proc = disallowed_deprecations_proc 25 end 26 27 # Note: trigger does not get called if the current_deprecations matches recorded_deprecations 28 # See https://github.com/Shopify/deprecation_toolkit/blob/2398f38acb62220fb79a6cd720f61d9cea26bc06/lib/deprecation_toolkit/test_triggerer.rb#L8-L11 29 def trigger(test, current_deprecations, recorded_deprecations) 30 if selected_for_raise?(current_deprecations) 31 raise RaiseDisallowedDeprecation.new(test, current_deprecations) 32 elsif ENV['RECORD_DEPRECATIONS'] 33 record(test, current_deprecations, recorded_deprecations) 34 end 35 end 36 37 private 38 39 def selected_for_raise?(current_deprecations) 40 disallowed_deprecations_proc.call(current_deprecations.deprecations_without_stacktrace) 41 end 42 43 def record(test, current_deprecations, recorded_deprecations) 44 ::DeprecationToolkit::Behaviors::Record.trigger(test, current_deprecations, recorded_deprecations) 45 end 46 end 47 end 48 49 # Taken from https://github.com/jeremyevans/ruby-warning/blob/1.1.0/lib/warning.rb#L18 50 # Note: When a spec fails due to this warning, please update the spec to address the deprecation. 51 def self.kwargs_warning 52 %r{warning: (?:Using the last argument (?:for `.+' )?as keyword parameters is deprecated; maybe \*\* should be added to the call|Passing the keyword argument (?:for `.+' )?as the last hash parameter is deprecated|Splitting the last argument (?:for `.+' )?into positional and keyword parameters is deprecated|The called method (?:`.+' )?is defined here)\n\z} 53 end 54 55 # Note: No new exceptions should be added here, unless they are in external dependencies. 56 # In this case, we recommend to add a silence together with an issue to patch or update 57 # the dependency causing the problem. 58 # See https://gitlab.com/gitlab-org/gitlab/-/commit/aea37f506bbe036378998916d374966c031bf347#note_647515736 59 # 60 # - ruby/lib/grpc/generic/interceptors.rb: https://gitlab.com/gitlab-org/gitlab/-/issues/339305 61 def self.allowed_kwarg_warning_paths 62 %w[ 63 ruby/lib/grpc/generic/interceptors.rb 64 ] 65 end 66 67 def self.configure! 68 # Enable ruby deprecations for keywords, it's suppressed by default in Ruby 2.7 69 Warning[:deprecated] = true 70 71 DeprecationToolkit::Configuration.test_runner = :rspec 72 DeprecationToolkit::Configuration.deprecation_path = 'deprecations' 73 DeprecationToolkit::Configuration.warnings_treated_as_deprecation = [kwargs_warning] 74 75 disallowed_deprecations = -> (deprecations) do 76 deprecations.any? do |deprecation| 77 kwargs_warning.match?(deprecation) && 78 allowed_kwarg_warning_paths.none? { |path| deprecation.include?(path) } 79 end 80 end 81 82 DeprecationToolkit::Configuration.behavior = DeprecationBehaviors::SelectiveRaise.new(disallowed_deprecations) 83 end 84end 85