cs-rubocop-git 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.travis.yml +32 -0
- data/Appraisals +31 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +26 -0
- data/README.md +46 -0
- data/Rakefile +15 -0
- data/bin/rubocop-git +5 -0
- data/gemfiles/0.24.gemfile +7 -0
- data/gemfiles/0.25.gemfile +7 -0
- data/gemfiles/0.26.gemfile +7 -0
- data/gemfiles/0.27.gemfile +7 -0
- data/gemfiles/0.28.gemfile +7 -0
- data/gemfiles/0.29.gemfile +7 -0
- data/gemfiles/0.30.gemfile +7 -0
- data/gemfiles/0.31.gemfile +7 -0
- data/gemfiles/0.32.gemfile +7 -0
- data/gemfiles/0.33.gemfile +7 -0
- data/gemfiles/0.34.gemfile +7 -0
- data/gemfiles/0.35.gemfile +7 -0
- data/gemfiles/0.36.gemfile +7 -0
- data/gemfiles/0.37.gemfile +7 -0
- data/gemfiles/0.38.gemfile +7 -0
- data/gemfiles/0.39.gemfile +7 -0
- data/gemfiles/0.40.gemfile +7 -0
- data/gemfiles/0.41.gemfile +7 -0
- data/gemfiles/0.42.gemfile +7 -0
- data/gemfiles/0.43.gemfile +7 -0
- data/gemfiles/0.44.gemfile +7 -0
- data/gemfiles/0.45.gemfile +7 -0
- data/gemfiles/0.46.gemfile +7 -0
- data/gemfiles/0.47.gemfile +7 -0
- data/gemfiles/0.48.gemfile +7 -0
- data/hound.yml +254 -0
- data/lib/rubocop/git.rb +19 -0
- data/lib/rubocop/git/cli.rb +67 -0
- data/lib/rubocop/git/commit.rb +22 -0
- data/lib/rubocop/git/commit_file.rb +55 -0
- data/lib/rubocop/git/diff_parser.rb +31 -0
- data/lib/rubocop/git/file_violation.rb +5 -0
- data/lib/rubocop/git/line.rb +8 -0
- data/lib/rubocop/git/options.rb +84 -0
- data/lib/rubocop/git/patch.rb +36 -0
- data/lib/rubocop/git/pseudo_pull_request.rb +36 -0
- data/lib/rubocop/git/pseudo_resource.rb +24 -0
- data/lib/rubocop/git/runner.rb +70 -0
- data/lib/rubocop/git/style_checker.rb +43 -0
- data/lib/rubocop/git/style_guide.rb +98 -0
- data/lib/rubocop/git/version.rb +5 -0
- data/rubocop-git.gemspec +27 -0
- data/test/rubocop/git/cli_test.rb +12 -0
- data/test/rubocop/git/options_test.rb +18 -0
- data/test/rubocop/git/runner_test.rb +23 -0
- data/test/test_helper.rb +2 -0
- metadata +172 -0
data/lib/rubocop/git.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubocop/git/version'
|
2
|
+
require 'rubocop'
|
3
|
+
|
4
|
+
module RuboCop
|
5
|
+
module Git
|
6
|
+
autoload :Commit, 'rubocop/git/commit'
|
7
|
+
autoload :CommitFile, 'rubocop/git/commit_file'
|
8
|
+
autoload :DiffParser, 'rubocop/git/diff_parser'
|
9
|
+
autoload :FileViolation, 'rubocop/git/file_violation'
|
10
|
+
autoload :Line, 'rubocop/git/line'
|
11
|
+
autoload :Options, 'rubocop/git/options'
|
12
|
+
autoload :Patch, 'rubocop/git/patch'
|
13
|
+
autoload :PseudoPullRequest, 'rubocop/git/pseudo_pull_request'
|
14
|
+
autoload :PseudoResource, 'rubocop/git/pseudo_resource'
|
15
|
+
autoload :Runner, 'rubocop/git/runner'
|
16
|
+
autoload :StyleChecker, 'rubocop/git/style_checker'
|
17
|
+
autoload :StyleGuide, 'rubocop/git/style_guide'
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'rubocop/git'
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
module RuboCop
|
5
|
+
module Git
|
6
|
+
class CLI
|
7
|
+
def run(args = ARGV)
|
8
|
+
@options = Options.new
|
9
|
+
parse_arguments(args)
|
10
|
+
Runner.new.run(@options)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def parse_arguments(args)
|
16
|
+
@options.commits = option_parser.parse(args)
|
17
|
+
rescue OptionParser::InvalidOption, Options::Invalid => ex
|
18
|
+
warn "ERROR: #{ex.message}"
|
19
|
+
$stderr.puts
|
20
|
+
warn option_parser
|
21
|
+
exit 1
|
22
|
+
end
|
23
|
+
|
24
|
+
def option_parser
|
25
|
+
@option_parser ||= OptionParser.new do |opt|
|
26
|
+
opt.banner << ' [[commit] commit]'
|
27
|
+
|
28
|
+
opt.on('-c', '--config FILE',
|
29
|
+
'Specify configuration file') do |config|
|
30
|
+
@options.config = config
|
31
|
+
end
|
32
|
+
|
33
|
+
opt.on('-r', '--require FILE',
|
34
|
+
'Require Ruby file') do |file|
|
35
|
+
require file
|
36
|
+
end
|
37
|
+
|
38
|
+
opt.on('-d', '--debug', 'Display debug info') do
|
39
|
+
@options.rubocop[:debug] = true
|
40
|
+
end
|
41
|
+
|
42
|
+
opt.on('-D', '--display-cop-names',
|
43
|
+
'Display cop names in offense messages') do
|
44
|
+
@options.rubocop[:display_cop_names] = true
|
45
|
+
end
|
46
|
+
|
47
|
+
opt.on('--cached', 'git diff --cached') do
|
48
|
+
@options.cached = true
|
49
|
+
end
|
50
|
+
|
51
|
+
opt.on('--staged', 'synonym of --cached') do
|
52
|
+
@options.cached = true
|
53
|
+
end
|
54
|
+
|
55
|
+
opt.on('--hound', 'Hound compatibility mode') do
|
56
|
+
@options.hound = true
|
57
|
+
end
|
58
|
+
|
59
|
+
opt.on('--version', 'Display version') do
|
60
|
+
puts RuboCop::Git::VERSION
|
61
|
+
exit 0
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Git
|
5
|
+
# ref. https://github.com/thoughtbot/hound/blob/d2f3933/app/models/commit.rb
|
6
|
+
class Commit
|
7
|
+
def initialize(options)
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def file_content(filename)
|
12
|
+
if @options.cached
|
13
|
+
`git show :#{filename.shellescape}`
|
14
|
+
elsif @options.commit_last
|
15
|
+
`git show #{@options.commit_last.shellescape}:#{filename.shellescape}`
|
16
|
+
else
|
17
|
+
File.read(filename)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module RuboCop::Git
|
2
|
+
# copy from https://github.com/thoughtbot/hound/blob/d2f3933/app/models/commit_file.rb
|
3
|
+
class CommitFile
|
4
|
+
def initialize(file, commit)
|
5
|
+
@file = file
|
6
|
+
@commit = commit
|
7
|
+
end
|
8
|
+
|
9
|
+
def absolute_path
|
10
|
+
@file.absolute_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def filename
|
14
|
+
@file.filename
|
15
|
+
end
|
16
|
+
|
17
|
+
def content
|
18
|
+
@content ||= begin
|
19
|
+
unless removed?
|
20
|
+
@commit.file_content(filename)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def relevant_line?(line_number)
|
26
|
+
modified_lines.detect do |modified_line|
|
27
|
+
modified_line.line_number == line_number
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def removed?
|
32
|
+
@file.status == 'removed'
|
33
|
+
end
|
34
|
+
|
35
|
+
def ruby?
|
36
|
+
filename.match(/.*\.rb$/)
|
37
|
+
end
|
38
|
+
|
39
|
+
def modified_lines
|
40
|
+
@modified_lines ||= patch.additions
|
41
|
+
end
|
42
|
+
|
43
|
+
def modified_line_at(line_number)
|
44
|
+
modified_lines.detect do |modified_line|
|
45
|
+
modified_line.line_number == line_number
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def patch
|
52
|
+
Patch.new(@file.patch)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module RuboCop
|
2
|
+
module Git
|
3
|
+
class DiffParser
|
4
|
+
class << self
|
5
|
+
def parse(diff)
|
6
|
+
new.parse(diff)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse(diff)
|
11
|
+
files = []
|
12
|
+
in_patch = false
|
13
|
+
|
14
|
+
diff.each_line do |line|
|
15
|
+
case line
|
16
|
+
when /^diff --git/
|
17
|
+
in_patch = false
|
18
|
+
when %r{^\+{3} b/(?<path>[^\t\n\r]+)}
|
19
|
+
files << PseudoResource.new(Regexp.last_match[:path])
|
20
|
+
when /^@@/
|
21
|
+
in_patch = true
|
22
|
+
end
|
23
|
+
|
24
|
+
files.last.patch << line if in_patch
|
25
|
+
end
|
26
|
+
|
27
|
+
files
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module RuboCop
|
2
|
+
module Git
|
3
|
+
class Options
|
4
|
+
class Invalid < StandardError; end
|
5
|
+
|
6
|
+
HOUND_DEFAULT_CONFIG_FILE =
|
7
|
+
File.expand_path('../../../../hound.yml', __FILE__)
|
8
|
+
|
9
|
+
attr_accessor :config
|
10
|
+
attr_reader :cached, :hound, :rubocop
|
11
|
+
|
12
|
+
def initialize(hash_options = nil)
|
13
|
+
@config = nil
|
14
|
+
@cached = false
|
15
|
+
@hound = false
|
16
|
+
@rubocop = {}
|
17
|
+
@commits = []
|
18
|
+
|
19
|
+
from_hash(hash_options) if hash_options
|
20
|
+
end
|
21
|
+
|
22
|
+
def cached=(cached_)
|
23
|
+
if cached_ && !@commits.empty?
|
24
|
+
fail Invalid, 'cached and commit cannot be specified together'
|
25
|
+
end
|
26
|
+
@cached = !!cached_
|
27
|
+
end
|
28
|
+
|
29
|
+
def hound=(hound_)
|
30
|
+
@hound = !!hound_
|
31
|
+
end
|
32
|
+
|
33
|
+
def rubocop=(rubocop_)
|
34
|
+
unless rubocop_.is_a?(Hash)
|
35
|
+
fail Invalid, "invalid rubocop: #{rubocop_.inspect}"
|
36
|
+
end
|
37
|
+
@rubocop = rubocop_
|
38
|
+
end
|
39
|
+
|
40
|
+
def commits=(commits)
|
41
|
+
unless commits.is_a?(Array) && commits.length <= 2
|
42
|
+
fail Invalid, "invalid commits: #{commits.inspect}"
|
43
|
+
end
|
44
|
+
if !commits.empty? && cached
|
45
|
+
fail Invalid, 'cached and commit cannot be specified together'
|
46
|
+
end
|
47
|
+
@commits = commits
|
48
|
+
end
|
49
|
+
|
50
|
+
def config_file
|
51
|
+
if hound
|
52
|
+
HOUND_DEFAULT_CONFIG_FILE
|
53
|
+
elsif config
|
54
|
+
config
|
55
|
+
elsif File.exist?(RuboCop::ConfigLoader::DOTFILE)
|
56
|
+
RuboCop::ConfigLoader::DOTFILE
|
57
|
+
else
|
58
|
+
RuboCop::ConfigLoader::DEFAULT_FILE
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def commit_first
|
63
|
+
@commits.first
|
64
|
+
end
|
65
|
+
|
66
|
+
def commit_last
|
67
|
+
@commits.length == 1 ? false : @commits.last
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def from_hash(hash_options)
|
73
|
+
hash_options = hash_options.dup
|
74
|
+
%w(config cached hound rubocop commits).each do |key|
|
75
|
+
value = hash_options.delete(key) || hash_options.delete(key.to_sym)
|
76
|
+
public_send("#{key}=", value)
|
77
|
+
end
|
78
|
+
unless hash_options.empty?
|
79
|
+
fail Invalid, "invalid keys: #{hash_options.keys.join(' ')}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module RuboCop::Git
|
2
|
+
# copy from https://github.com/thoughtbot/hound/blob/d2f3933/app/models/patch.rb
|
3
|
+
class Patch
|
4
|
+
RANGE_INFORMATION_LINE = /^@@ .+\+(?<line_number>\d+),/
|
5
|
+
MODIFIED_LINE = /^\+(?!\+|\+)/
|
6
|
+
NOT_REMOVED_LINE = /^[^-]/
|
7
|
+
|
8
|
+
def initialize(body)
|
9
|
+
@body = body || ''
|
10
|
+
end
|
11
|
+
|
12
|
+
def additions
|
13
|
+
line_number = 0
|
14
|
+
|
15
|
+
lines.each_with_index.inject([]) do |additions, (content, patch_position)|
|
16
|
+
case content
|
17
|
+
when RANGE_INFORMATION_LINE
|
18
|
+
line_number = Regexp.last_match[:line_number].to_i
|
19
|
+
when MODIFIED_LINE
|
20
|
+
additions << Line.new(content, line_number, patch_position)
|
21
|
+
line_number += 1
|
22
|
+
when NOT_REMOVED_LINE
|
23
|
+
line_number += 1
|
24
|
+
end
|
25
|
+
|
26
|
+
additions
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def lines
|
33
|
+
@body.lines
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module RuboCop
|
2
|
+
module Git
|
3
|
+
# ref. https://github.com/thoughtbot/hound/blob/d2f3933/app/models/pull_request.rb
|
4
|
+
class PseudoPullRequest
|
5
|
+
HOUND_CONFIG_FILE = '.hound.yml'
|
6
|
+
|
7
|
+
def initialize(files, options)
|
8
|
+
@files = files
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def pull_request_files
|
13
|
+
@files.map do |file|
|
14
|
+
build_commit_file(file)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def config
|
19
|
+
return unless @options.hound
|
20
|
+
File.read(HOUND_CONFIG_FILE)
|
21
|
+
rescue Errno::ENOENT
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def build_commit_file(file)
|
28
|
+
CommitFile.new(file, head_commit)
|
29
|
+
end
|
30
|
+
|
31
|
+
def head_commit
|
32
|
+
@head_commit ||= Commit.new(@options)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RuboCop
|
2
|
+
module Git
|
3
|
+
class PseudoResource
|
4
|
+
attr_reader :patch, :pwd, :file_relative_path
|
5
|
+
|
6
|
+
alias_method :filename, :file_relative_path
|
7
|
+
|
8
|
+
def initialize(file_relative_path, pwd = Dir.pwd)
|
9
|
+
@file_relative_path = file_relative_path
|
10
|
+
@pwd = pwd
|
11
|
+
@patch = ''
|
12
|
+
end
|
13
|
+
|
14
|
+
def absolute_path
|
15
|
+
filename
|
16
|
+
File.join(pwd, filename)
|
17
|
+
end
|
18
|
+
|
19
|
+
def status
|
20
|
+
'modified'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Git
|
5
|
+
# ref. https://github.com/thoughtbot/hound/blob/d2f3933/app/services/build_runner.rb
|
6
|
+
class Runner
|
7
|
+
def run(options)
|
8
|
+
options = Options.new(options) unless options.is_a?(Options)
|
9
|
+
|
10
|
+
@options = options
|
11
|
+
@files = DiffParser.parse(git_diff(options))
|
12
|
+
|
13
|
+
display_violations($stdout)
|
14
|
+
|
15
|
+
exit(1) if violated?
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def violations
|
21
|
+
@violations ||= style_checker.violations
|
22
|
+
end
|
23
|
+
|
24
|
+
def style_checker
|
25
|
+
StyleChecker.new(pull_request.pull_request_files,
|
26
|
+
@options.rubocop,
|
27
|
+
@options.config_file,
|
28
|
+
pull_request.config)
|
29
|
+
end
|
30
|
+
|
31
|
+
def pull_request
|
32
|
+
@pull_request ||= PseudoPullRequest.new(@files, @options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def git_diff(options)
|
36
|
+
args = %w(diff --diff-filter=AMCR --find-renames --find-copies)
|
37
|
+
|
38
|
+
args << '--cached' if options.cached
|
39
|
+
args << options.commit_first.shellescape if options.commit_first
|
40
|
+
args << options.commit_last.shellescape if options.commit_last
|
41
|
+
|
42
|
+
`git #{args.join(' ')}`
|
43
|
+
end
|
44
|
+
|
45
|
+
def display_violations(io)
|
46
|
+
formatter = RuboCop::Formatter::ClangStyleFormatter.new(io)
|
47
|
+
formatter.started(nil)
|
48
|
+
|
49
|
+
violations.map do |violation|
|
50
|
+
offenses = violation.offenses
|
51
|
+
offenses = offenses.reject(&:disabled?) if offenses.first.respond_to?(:disabled?)
|
52
|
+
formatter.file_finished(
|
53
|
+
violation.filename,
|
54
|
+
offenses.compact.sort.freeze
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
formatter.finished(@files.map(&:filename).freeze)
|
59
|
+
end
|
60
|
+
|
61
|
+
def violated?
|
62
|
+
violations.any? do |violation|
|
63
|
+
offenses = violation.offenses
|
64
|
+
offenses = offenses.reject(&:disabled?) if offenses.first.respond_to?(:disabled?)
|
65
|
+
offenses && offenses.length > 0
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|