11

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
Deepesh
  • 6,138
  • 1
  • 24
  • 41
martins
  • 9,669
  • 11
  • 57
  • 85

7 Answers7

11

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

Tim Diggins
  • 4,364
  • 3
  • 30
  • 49
  • 1
    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. :) – martins Mar 28 '17 at 10:55
  • @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!) – Tim Diggins Mar 29 '17 at 14:59
  • Works for me. Very useful! – BrunoF May 07 '18 at 18:25
  • `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 – Pak Feb 20 '19 at 12:57
  • 2
    `rubocop --force-exclusion` to take into .rubocop.yml exclusions – Kiryl Plyashkevich Dec 15 '20 at 06:53
  • Thanks @KirylPlyashkevich for the suggestion, I've updated the answer to include this. It also means I could drop the grep. Nice! – Tim Diggins Dec 17 '20 at 16:36
6

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
martins
  • 9,669
  • 11
  • 57
  • 85
5

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.

Martijn
  • 1,662
  • 15
  • 21
  • Could you please elaborate how to use it to only check what has changed in particular PR? Thank you. – Lev Denisov Dec 14 '16 at 17:21
  • 1
    Try `rubocop-git `, 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. – Martijn Dec 15 '16 at 12:45
2

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?! ;)

Al Chou
  • 21
  • 2
  • This means you can drop the `grep`, since you might want rubocop to run on `Gemfile`, or `.rake` files for example. – Sunny Sep 30 '22 at 08:47
1

Here's another alternative that compares the current branch to origin/master (should work with any repo hosting - just tried it on circleci with a bitbucket repo). It also passes a .rubocop.yml config file option (you can remove that part if you don't need it).

require 'spec_helper'

RSpec.describe 'Check that the files we have changed have correct syntax' do
  before do
    current_sha = 'origin/master..HEAD'
    @files = `git diff-tree --no-commit-id --name-only -r #{current_sha} | grep .rb`
    @files.tr!("\n", ' ')
  end

  it 'runs rubocop on changed ruby files' do
    if @files.empty?
      puts "Linting not performed. No ruby files changed."
    else
      puts "Running rubocop for changed files: #{@files}"
      result = system "bundle exec rubocop --config .rubocop.yml --fail-level warn #{@files}"
      expect(result).to be(true)
    end
  end
end

Original gist here: https://gist.github.com/djburdick/5104d15f612c15dde65f#gistcomment-2029606

Nico Brenner
  • 690
  • 7
  • 11
0

One simpler solution:

git diff origin/master --name-only | xargs rubocop --force-exclusion

Explanation: I rarely have master up to date locally, but doing git fetch updates origin/master so I want to diff against that. I can't get the other proposed solutions with diff-tree and origin/master to work.

Greg
  • 5,862
  • 1
  • 25
  • 52
0

Maybe you could leverage CircleCI’s dynamic configuration feature.

There is a specific guide on how to execute specific workflows or steps based on which files are modified (https://circleci.com/docs/using-dynamic-configuration/#execute-specific-workflows-or-steps-based-on-which-files-are-modified).

yaningo
  • 353
  • 2
  • 7