How to run Rubocop only on the changed files in a pull request?

10,121

Solution 1

I fixed it by querying api.github.com. This will run rubocop on all files that has been changed between current_sha and the master branch.

require 'spec_helper'

describe 'Check that the files we have changed have correct syntax' do
  before do
    current_sha = `git rev-parse --verify HEAD`.strip!
    token = 'YOUR GITHUB TOKEN'
    url = 'https://api.github.com/repos/orwapp/orwapp/compare/' \
          "master...#{current_sha}?access_token=#{token}"
    files = `curl -i #{url} | grep filename | cut -f2 -d: | grep \.rb | tr '"', '\ '`
    files.tr!("\n", ' ')
    @report = 'nada'
    if files.present?
      puts "Changed files: #{files}"

      @report = `rubocop #{files}`
      puts "Report: #{@report}"
    end
  end

  it { expect(@report.match('Offenses')).to be_falsey }
end

Solution 2

You don't have to use github api, or even ruby (unless you want to wrap the responses) you can just run:

git fetch && git diff-tree -r --no-commit-id --name-only master@\{u\} head | xargs ls -1 2>/dev/null | xargs rubocop --force-exclusion

see http://www.red56.uk/2017/03/26/running-rubocop-on-changed-files/ for longer write-up of this

Solution 3

I found https://github.com/m4i/rubocop-git which works very well. However it works on your git diff (optionally with --cached) so it does not allow you to compare branches.

Solution 4

I don't have high enough reputation to comment on an answer, so I am posting an answer to add a refinement I found useful:

git fetch && git diff-tree -r --no-commit-id --name-only master@\{u\} HEAD | xargs ls -1 2>/dev/null | grep '\.rb$' | xargs bundle exec rubocop --force-exclusion

The addition of --force-exclusion makes RuboCop respect the Exclude declarations in its config file (here using the default ./.rubocop.yml). You put those declarations in for a reason, right?! ;)

Share:
10,121
martins
Author by

martins

Rails developer digging into ReactJS.

Updated on July 22, 2022

Comments

  • martins
    martins almost 2 years

    I have created spec/lint/rubocop_spec.rb which runs Rubocop style checker on the files changed between current branch and master. This works when I test locally but not when the test run on the build server Circle.ci. I suspect it is because only the branch in question is downloaded, so it does not find any differences between master. Is there a better way than git co master && git pull origin master? Can I query the Github API perhaps to get the files changed listed?

    require 'spec_helper'
    
    describe 'Check that the files we have changed have correct syntax' do
      before do
        current_sha = `git rev-parse --verify HEAD`.strip!
        files = `git diff master #{current_sha} --name-only | grep .rb`
        files.tr!("\n", ' ')
        @report = 'nada'
        if files.present?
          puts "Changed files: #{files}"
    
          @report = `rubocop #{files}`
          puts "Report: #{@report}"
        end
      end
    
      it { @report.match('Offenses').should_not be true }
    end
    
  • Lev Denisov
    Lev Denisov over 7 years
    Could you please elaborate how to use it to only check what has changed in particular PR? Thank you.
  • Martijn
    Martijn over 7 years
    Try rubocop-git <first_commit> <last_commit>, for example: rubocop-git master master~5 to check the last 5 commits in master. You'd need an additional git invocation to find out the first commit in the branch.
  • martins
    martins about 7 years
    How will this work if the local master is not updated? Using the github api assures that the diff is checked on the latest version of master. :)
  • Tim Diggins
    Tim Diggins about 7 years
    @martins Two reasons this works: 1) This does a git fetch first and 2) it uses master@{u} so the diff-tree is comparing the fetched remote branch which master is tracking (normally origin/master, but you might have a weird setup) rather than the local version. (I think git is a kind of an api to github, but with automatic caching!)
  • BrunoF
    BrunoF almost 6 years
    Works for me. Very useful!
  • Pak
    Pak about 5 years
    git fetch && git diff-tree -r --no-commit-id --name-only master@\{u\} HEAD | xargs ls -1 2>/dev/null | grep '\.rb$' | xargs bundle exec rubocop worked for me, head was ambiguous
  • Kiryl Plyashkevich
    Kiryl Plyashkevich over 3 years
    rubocop --force-exclusion to take into .rubocop.yml exclusions
  • Tim Diggins
    Tim Diggins over 3 years
    Thanks @KirylPlyashkevich for the suggestion, I've updated the answer to include this. It also means I could drop the grep. Nice!