如何在拉取请求中仅对更改的文件运行Rubocop?

12

我已经创建了 spec/lint/rubocop_spec.rb,可以在当前分支和主分支之间更改的文件上运行 Rubocop 样式检查器。当我在本地测试时它有效,但是在构建服务器 Circle.ci 上运行测试时无效。 我怀疑这是因为只下载了相关的分支,所以它找不到与主分支之间的任何差异。是否有比 git co master && git pull origin master 更好的方法? 也许我可以查询 Github API 来获取更改的文件列表吗?

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
7个回答

12

你不必使用GitHub API,甚至不需要使用Ruby(除非你想要包装响应),你只需运行:

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

请查看http://www.red56.uk/2017/03/26/running-rubocop-on-changed-files/以获取更详细的说明。


1
如果本地主分支没有更新,这将如何工作?使用Github API可以确保差异在最新的主分支版本上进行检查。 :) - martins
@martins 之所以奏效有两个原因:1)它会先进行 git fetch,2)它使用 master@{u},使得 diff-tree 比较的是已获取的远程分支而不是本地版本(master 通常跟踪 origin/master,但你可能有一个奇怪的设置)。(我认为 git 是一种自动缓存的 github api!) - Tim Diggins
可以用。非常有用! - BrunoF
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 对我有用,因为 head 不明确。 - Pak
2
rubocop --force-exclusion 命令会考虑 .rubocop.yml 文件中的排除项。 - Kiryl Plyashkevich
感谢@KirylPlyashkevich的建议,我已更新答案以包含此内容。这也意味着我可以省略grep。不错! - Tim Diggins

5
我通过查询api.github.com解决了这个问题。这会在当前SHA和主分支之间更改的所有文件上运行rubocop。
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

我理解得对吗?你为了节省 Rubocop 运行时间的几秒钟,只是检查当前分支和主分支之间的差异...然后通过使用 curl 下载整个仓库的主分支状态?我是说,从技术上讲,你解决了没有正确差异的问题,但代价是什么?为什么不直接对所有文件运行 Rubocop?从时间上来说,你应该是一样或者更好,而且作为额外的好处,每次都可以检查所有文件... - undefined

4

我发现 https://github.com/m4i/rubocop-git 很好用。然而,它只能在你的 git diff 中使用(可选择使用 --cached),因此它不允许您比较分支。


请问您能否详细说明如何使用它来仅检查特定PR中发生了哪些更改?谢谢。 - Lev Denisov
1
尝试使用 rubocop-git <first_commit> <last_commit> 命令,例如:rubocop-git master master~5 来检查 master 分支中最近的 5 次提交。你需要额外执行一个 git 命令来找到该分支的第一次提交。 - Martijn

2

我没有足够的声望来评论一个答案,所以我发表了一个答案来增加我发现有用的细节:

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

添加--force-exclusion选项使RuboCop尊重其配置文件中的Exclude声明(这里使用默认的./.rubocop.yml)。您添加这些声明是有原因的,对吧?!;)

这意味着您可以放弃使用 grep,因为您可能希望 rubocop 运行在例如 Gemfile.rake 文件上。 - Sunny

1
一个更简单的解决方案:
git diff origin/master --name-only | xargs rubocop --force-exclusion

解释:我很少在本地更新master,但是执行git fetch会更新origin/master,所以我想对其进行差异比较。我无法使用diff-treeorigin/master的其他建议解决方案。

这正是我一直在寻找的东西——在提交之前,在我本地修改的文件上运行rubocop。谢谢! - undefined

1
这里有另一种选择,它将当前分支与origin/master进行比较(适用于任何仓库托管 - 我刚在circleci上尝试了一下bitbucket仓库)。它还传递了一个.rubocop.yml配置文件选项(如果不需要可以删除该部分)。
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

这里是原始的要点: https://gist.github.com/djburdick/5104d15f612c15dde65f#gistcomment-2029606


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接