DEV Community

Sahil Gadimbayli
Sahil Gadimbayli

Posted on

Custom binstub for Rubocop to run only on changed files

Salud to Dev.to community! A couple of weeks back I have posted first time here in Dev.to, motivating myself to post articles about Ruby, Rails, Javascript and Development in general.

Today's short write up will be to run Rubocop only on changed files and creating binstubs.

When starting a project, setting up and following style guides is pretty easy. However, when we inherit a legacy codebase or try to apply them on already existing medium to large scale applications, we quickly get frustrated as there are already hundreds if not thousands of failures that we have to fix.

A better way to do this is to progressively run robocop as we continue with our development. Each time you touch part of your codebase, Rubocop will only analyze and lint the given changed files.

So, let's start.

First, we will have to create a custom binstub to run Rubocop instead of default rubocop command. Then we will be writing logic inside the binstub to find changed files using git and apply robocop on them.(Aneeta has a handy article about this here)

To create a binstub Bundler provides bundler --binstubs gemname but we will not need any of the boilerplate, so let's just create an empty file named rubocop in our bin/ directory.

Once, we have created the file, we will have to write logic to compare current changes to the master branch(or the branch of your choice) as below:

#!/usr/bin/env ruby
# frozen_string_literal: true

class RubocopRunner
  COMPARE_TO_BRANCH = 'master'
  def self.check_branch_validity
    current_branch = `git branch | grep \\* | cut -d ' ' -f2`.strip
    if current_branch.empty? || current_branch == COMPARE_TO_BRANCH
      puts "No git found, or you are still on #{COMPARE_TO_BRANCH} branch."
      exit 0
    end
  end

  def self.check_non_committed_changes
    system("git ls-files -m | xargs ls -1 2>/dev/null | grep '\.rb$' | xargs bundle exec rubocop #{ARGV.join(' ')}")
  end

  def self.check_committed_changes
    system("git diff-tree -r --no-commit-id --name-only head origin/master | xargs bundle exec rubocop #{ARGV.join(' ')}")
  end
end

RubocopRunner.check_branch_validity
RubocopRunner.check_non_committed_changes
RubocopRunner.check_committed_changes
Enter fullscreen mode Exit fullscreen mode

Now we can run bundle exec bin/rubocop and see it only apply on changed files. And don't forget to add your .rubocop.yml configuration file to root directory of your project.

Hope it helps, and let me know if there is anything that does not work as expected!

See you soon!

Top comments (2)

Collapse
 
earonesty profile image
earonesty • Edited

You could use github.com/AtakamaLLC/lint-diffs, which works with any languages in your repo, in addition to ruby, (even bash scripts and README files), and works with any type of source control.

Collapse
 
alexmreis profile image
Alex Reis

Thank you so much for this, exactly what I was looking for to introduce Rubocop on an existing project!