Feb 3, 2021 - Thong Kuah    

How we automatically fixed thousands of Ruby 2.7 deprecation warnings

The upgrade to Ruby 2.7 for GitLab involved thousands of deprecation warnings across hundreds of files. Here's how we fixed most of them.

Ruby 3.0 was just released on Dec. 25, 2020, with some new features and some breaking changes. GitLab was at Ruby 2.6, and we wanted to upgrade to Ruby 2.7 in preparation to eventually upgrade to Ruby 3.

In Ruby 3.0, positional and keyword arguments will be separated. To help developers prepare for this, in Ruby 2.7, warnings were added. In GitLab, we discovered we have thousands of such warnings across hundreds of files:

warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call

Boring solutions

To address this warning, the obvious, and boring solution was to simply add ** to the last keyword argument. For the most part, this is what we did. However, while this was under way, we also developed a RuboCop check that could detect, and automatically fix the keyword arguments. The benefit for this approach was that we can autocorrect any existing warnings en masse.

The tricky part about this is that RuboCop is designed to statically analyze Ruby code, whereas the warnings were generated by Ruby at runtime.

A way forward

After some research, we found a way to utilize our comprehensive RSpec test suite to gather all the warnings using the Deprecation Toolkit gem. We also considered using the warning gem at one point, but preferred Deprecation Toolkit as the results were easier to process.

Deprecation Toolkit supports RSpec out of the box, so it was really simple to configure. It also has a simple YAML-based file format to record all deprecations. We then adapted this to record deprecation warnings for Ruby 2.7 last keyword arguments with:

  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

Lastly, we wrote a new RuboCop check, called Lint/LastKeywordArgument, that checks against the YAML files generated by Deprecation Toolkit, and generates offenses. Now we can very quickly, statically check the whole GitLab codebase, and even autocorrect! You can see how Deprecation Toolkit and the LastKeywordArgument check was put together in this merge request. You can see a sample output from running the LastKeywordArgument cop check:

LastKeywordArgument RuboCop offenses Sample output from running the LastKeywordArgument cop check

Automatically fix everything

Now we have an automatic RuboCop check, which can also autocorrect, we create merge requests to autocorrect! For example, we autocorrected 62 instances across 39 spec files. Automation for the win!

We then went one step further, and integrated this in our GitLab CI pipelines. Using the artifacts feature of GitLab CI, we gathered the deprecations directory from all RSpec jobs (we have about 400 such jobs). After all the RSpec jobs have passed, we then made a post-test job to check the results with the LastKeywordArgument cop. Below is a snippet of the GitLab CI .gitlab-ci.yml configuration:

stages:
  - test
  - post-test

# This inherited job is used by all RSpec jobs
.rspec-base:
  stage: test
  artifacts:
    - deprecations/

# GitLab CI job artifacts from previous stages are passed to this job
rspec:deprecations:
  stage: post-test
  script:
    - bundle exec rubocop --only Lint/LastKeywordArgument --parallel
  artifacts:
    - deprecations/

This enabled us to have a single job where we can see all deprecation warnings.

Conclusion

With this measure we went from about 30,000 warnings related to keyword arguments to about 800 remaining warnings, largely stemming from dependencies. Feel free to follow our progress in GitLab issue #257438, and contribute to fix the remaining warnings if you are interested!

Cover image by Daria Nepriakhina on Unsplash

Free eBook: The benefits of single application CI/CD Download the ebook to learn how you can utilize CI/CD without the costly integrations or plug-in maintenance. Learn more
Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license