diff --git a/.gitignore b/.gitignore
index 30cb231e83f1c598336dd4cfa06f74cb3226cecb..93fb0b1144b5301bf1d1471b8c097282687e9820 100644
--- a/.gitignore
+++ b/.gitignore
@@ -77,6 +77,7 @@ eslint-report.html
 /.gitlab_kas_secret
 /webpack-report/
 /crystalball/
+/deprecations/
 /knapsack/
 /rspec_flaky/
 /locale/**/LC_MESSAGES
diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml
index 0ec875864411db4c8fcb123012c66b5c401fcf5a..3962be8d84d9edd830103bfae31e12a78f784a5b 100644
--- a/.gitlab/ci/rails.gitlab-ci.yml
+++ b/.gitlab/ci/rails.gitlab-ci.yml
@@ -21,7 +21,7 @@
     RUBY_GC_MALLOC_LIMIT: 67108864
     RUBY_GC_MALLOC_LIMIT_MAX: 134217728
     CRYSTALBALL: "true"
-    RECORD_KEYWORD_WARNINGS: "true"
+    RECORD_DEPRECATIONS: "true"
   needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets"]
   script:
     - *base-script
@@ -32,13 +32,13 @@
     paths:
       - coverage/
       - crystalball/
+      - deprecations/
       - knapsack/
       - rspec_flaky/
       - rspec_profiling/
       - tmp/capybara/
       - tmp/memory_test/
       - tmp/feature_flags/
-      - tmp/keyword_warn.txt
       - log/*.log
     reports:
       junit: junit_rspec.xml
diff --git a/Gemfile b/Gemfile
index 5829328bd47a7b6c09ce98126b26145a6cee07d3..63a13c50373254272a62f45a2b6e83bda9b7a33b 100644
--- a/Gemfile
+++ b/Gemfile
@@ -351,7 +351,7 @@ group :development do
 end
 
 group :development, :test do
-  gem 'warning', '~> 1.1', require: false
+  gem 'deprecation_toolkit', '~> 1.5.1', require: false
   gem 'bullet', '~> 6.1.0'
   gem 'pry-byebug', '~> 3.9.0', platform: :mri
   gem 'pry-rails', '~> 0.3.9'
diff --git a/Gemfile.lock b/Gemfile.lock
index 285befad21914cb81b451a82e0b1f1680be56831..f5d7c409c0cfcaa240aa40769eec8eee5102757c 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -224,6 +224,8 @@ GEM
     declarative-option (0.1.0)
     default_value_for (3.3.0)
       activerecord (>= 3.2.0, < 6.1)
+    deprecation_toolkit (1.5.1)
+      activesupport (>= 4.2)
     derailed_benchmarks (1.7.0)
       benchmark-ips (~> 2)
       get_process_mem (~> 0)
@@ -1222,7 +1224,6 @@ GEM
     vmstat (2.3.0)
     warden (1.2.8)
       rack (>= 2.0.6)
-    warning (1.1.0)
     webauthn (2.3.0)
       android_key_attestation (~> 0.3.0)
       awrence (~> 1.1)
@@ -1303,6 +1304,7 @@ DEPENDENCIES
   database_cleaner (~> 1.7.0)
   deckar01-task_list (= 2.3.1)
   default_value_for (~> 3.3.0)
+  deprecation_toolkit (~> 1.5.1)
   derailed_benchmarks
   device_detector
   devise (~> 4.7.2)
@@ -1519,7 +1521,6 @@ DEPENDENCIES
   validates_hostname (~> 1.0.10)
   version_sorter (~> 2.2.4)
   vmstat (~> 2.3.0)
-  warning (~> 1.1)
   webauthn (~> 2.3)
   webmock (~> 3.9.1)
   wikicloth (= 0.8.1)
diff --git a/rubocop/cop/lint/last_keyword_argument.rb b/rubocop/cop/lint/last_keyword_argument.rb
index d437395b8c2a4e82218c00d9fa784cb42a8a88c7..d8f8d03b5529edb2affb60d9430cc6e457b1e015 100644
--- a/rubocop/cop/lint/last_keyword_argument.rb
+++ b/rubocop/cop/lint/last_keyword_argument.rb
@@ -3,9 +3,18 @@
 module RuboCop
   module Cop
     module Lint
+      # This cop only works if there are files from deprecation_toolkit. You can
+      # generate these files by:
+      #
+      # 1. Running specs with RECORD_DEPRECATIONS=1
+      # 1. Downloading the complete set of deprecations/ files from a CI
+      # pipeline (see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47720)
       class LastKeywordArgument < Cop
         MSG = 'Using the last argument as keyword parameters is deprecated'.freeze
 
+        DEPRECATIONS_GLOB = File.expand_path('../../../deprecations/**/*.yml', __dir__)
+        KEYWORD_DEPRECATION_STR = 'maybe ** should be added to the call'
+
         def on_send(node)
           arg = node.arguments.last
           return unless arg
@@ -48,13 +57,11 @@ def self.keyword_warnings
         end
 
         def self.keywords_list
-          return [] unless File.exist?(keywords_file_path)
-
-          File.read(keywords_file_path).split("----\n")
-        end
+          hash = Dir.glob(DEPRECATIONS_GLOB).each_with_object({}) do |file, hash|
+            hash.merge!(YAML.safe_load(File.read(file)))
+          end
 
-        def self.keywords_file_path
-          File.expand_path('../../../tmp/keyword_warn.txt', __dir__)
+          hash.values.flatten.select { |str| str.include?(KEYWORD_DEPRECATION_STR) }.uniq
         end
       end
     end
diff --git a/spec/deprecation_toolkit_env.rb b/spec/deprecation_toolkit_env.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bc90f67f0db88b94b737314e9ecc037a7f0bf31f
--- /dev/null
+++ b/spec/deprecation_toolkit_env.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+if ENV.key?('RECORD_DEPRECATIONS')
+  require 'deprecation_toolkit'
+  require 'deprecation_toolkit/rspec'
+  DeprecationToolkit::Configuration.test_runner = :rspec
+  DeprecationToolkit::Configuration.deprecation_path = 'deprecations'
+  DeprecationToolkit::Configuration.behavior = DeprecationToolkit::Behaviors::Record
+
+  # Enable ruby deprecations for keywords, it's suppressed by default in Ruby 2.7.2
+  Warning[:deprecated] = true
+
+  kwargs_warnings = [
+    # Taken from https://github.com/jeremyevans/ruby-warning/blob/1.1.0/lib/warning.rb#L18
+    %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}
+  ]
+  DeprecationToolkit::Configuration.warnings_treated_as_deprecation = kwargs_warnings
+end
diff --git a/spec/rubocop/cop/lint/last_keyword_argument_spec.rb b/spec/rubocop/cop/lint/last_keyword_argument_spec.rb
index ae1b442e9b54b00bfc39f6b6e9117c3053e63b61..f942390569bf6615657cb2f363e8b8bc1124383a 100644
--- a/spec/rubocop/cop/lint/last_keyword_argument_spec.rb
+++ b/spec/rubocop/cop/lint/last_keyword_argument_spec.rb
@@ -13,8 +13,9 @@
     described_class.instance_variable_set(:@keyword_warnings, nil)
   end
 
-  context 'file does not exist' do
+  context 'deprecation files does not exist' do
     before do
+      allow(Dir).to receive(:glob).and_return([])
       allow(File).to receive(:exist?).and_return(false)
     end
 
@@ -25,18 +26,37 @@
     end
   end
 
-  context 'file does exist' do
+  context 'deprecation files does exist' do
+    let(:create_spec_yaml) do
+      <<~YAML
+      ---
+      test_mutations/boards/lists/create#resolve_with_proper_permissions_backlog_list_creates_one_and_only_one_backlog:
+      - |
+        DEPRECATION WARNING: /Users/tkuah/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/batch-loader-1.4.0/lib/batch_loader/graphql.rb:38: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
+        /Users/tkuah/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/batch-loader-1.4.0/lib/batch_loader.rb:26: warning: The called method `batch' is defined here
+      test_mutations/boards/lists/create#ready?_raises_an_error_if_required_arguments_are_missing:
+      - |
+        DEPRECATION WARNING: /Users/tkuah/code/ee-gdk/gitlab/create_service.rb:1: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
+        /Users/tkuah/code/ee-gdk/gitlab/user.rb:17: warning: The called method `call' is defined here
+      YAML
+    end
+
+    let(:projects_spec_yaml) do
+      <<~YAML
+      ---
+      test_api/projects_get_/projects_when_unauthenticated_behaves_like_projects_response_returns_an_array_of_projects:
+      - |
+        DEPRECATION WARNING: /Users/tkuah/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/state_machines-activerecord-0.6.0/lib/state_machines/integrations/active_record.rb:511: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
+        /Users/tkuah/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/activerecord-6.0.3.3/lib/active_record/suppressor.rb:43: warning: The called method `save' is defined here
+      - |
+        DEPRECATION WARNING: /Users/tkuah/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/rack-2.2.3/lib/rack/builder.rb:158: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
+        /Users/tkuah/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/grape-1.4.0/lib/grape/middleware/error.rb:30: warning: The called method `initialize' is defined here
+      YAML
+    end
+
     before do
-      allow(File).to receive(:exist?).and_return(true)
-
-      allow(File).to receive(:read).and_return(<<~DATA)
-----
-create_service.rb:1: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
-user.rb:17: warning: The called method `call' is defined here
-----
-/Users/tkuah/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/batch-loader-1.4.0/lib/batch_loader/graphql.rb:38: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
-/Users/tkuah/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/batch-loader-1.4.0/lib/batch_loader.rb:26: warning: The called method `batch' is defined here
-      DATA
+      allow(Dir).to receive(:glob).and_return(['deprecations/service/create_spec.yml', 'deprecations/api/projects_spec.yml'])
+      allow(File).to receive(:read).and_return(create_spec_yaml, projects_spec_yaml)
     end
 
     it 'registers an offense' do
diff --git a/spec/ruby_keyword_warning.rb b/spec/ruby_keyword_warning.rb
deleted file mode 100644
index 5b53fcd9c769dc1811ac18d14512c9292a6ded1e..0000000000000000000000000000000000000000
--- a/spec/ruby_keyword_warning.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# frozen_string_literal: true
-
-if ENV['RECORD_KEYWORD_WARNINGS']
-  require 'warning'
-
-  Warning[:deprecated] = true
-
-  # The warnings are emitted with two calls of Warning.warn.
-  # In an attempt to group these two calls we use the `----` separator.
-
-  keyword_regex = /: warning: (?:Using the last argument (?:for `.+' )?as keyword parameters is deprecated; maybe \*\* should be added to the call)\n\z/
-  method_called_regex = /: warning: (?:The called method (?:`.+' )?is defined here)\n\z/
-  actions = {
-    keyword_regex => proc do |warning|
-      File.open(File.expand_path('../tmp/keyword_warn.txt', __dir__), "a") do |file|
-        file.write("----\n")
-        file.write(warning)
-
-        # keep ruby behaviour of warning in stderr
-        $stderr.puts(warning) # rubocop:disable Style/StderrPuts
-      end
-    end,
-    method_called_regex => proc do |warning|
-      File.open(File.expand_path('../tmp/keyword_warn.txt', __dir__), "a") do |file|
-        file.write(warning)
-
-        # keep ruby behaviour of warning in stderr
-        $stderr.puts(warning) # rubocop:disable Style/StderrPuts
-      end
-    end
-  }
-  Warning.process('', actions)
-end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index b391551ecc15b78280ddbe864fa61e7ef3330324..49775940584bf0a22f403aaf03befe252d464bae 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -8,7 +8,7 @@
   abort 'Aborting...'
 end
 
-require './spec/ruby_keyword_warning'
+require './spec/deprecation_toolkit_env'
 
 require './spec/simplecov_env'
 SimpleCovEnv.start!