rubocop-git2 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 971494e62d00b8014fc19631734832640325d7cdda40489802c2fae1fd500004
4
+ data.tar.gz: 27194c03771bd5855c83331fb25dfdd77e0e507be9f49af740a2021c5a97bcef
5
+ SHA512:
6
+ metadata.gz: '009a9ab5e998a0e3e1e837ed28ad773c319f198233c6d7e447c4a09ae793af43297043441730df0b041ff17db35022ed9b6ab9f814cfdc444e1606c381a0ba8c'
7
+ data.tar.gz: 2cc49969985c8ff0e9b98d51bf83a4897ef22d0acd224ac946bef250ca3d37280164d64b43f65347a2830739a532e635cd84e2f1c38708e3784008e8a73edecb
@@ -0,0 +1,24 @@
1
+ name: tests
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+
9
+ strategy:
10
+ matrix:
11
+ ruby: [ '2.6', '3.2' ]
12
+
13
+ steps:
14
+ - uses: actions/checkout@v3
15
+ with:
16
+ fetch-depth: 0 # i.e. full history, needed for tests
17
+ - name: Set up Ruby ${{ matrix.ruby }}
18
+ uses: ruby/setup-ruby@v1
19
+ with:
20
+ ruby-version: ${{ matrix.ruby }}
21
+ - name: Install dependencies
22
+ run: bundle install --jobs 4
23
+ - name: Test with Rake
24
+ run: bundle exec rake
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ /gemfiles/*.gemfile.lock
data/.rubocop.yml ADDED
@@ -0,0 +1,20 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ TargetRubyVersion: 2.6 # really 2.4, but 2.6 is lowest supported by rubocop
4
+
5
+ Layout/DotPosition:
6
+ Enabled: false
7
+ Metrics/BlockLength:
8
+ Exclude: ['test/**/*.rb']
9
+ Style/Documentation:
10
+ Enabled: false
11
+ Style/FrozenStringLiteralComment:
12
+ Enabled: false
13
+ Style/ClassAndModuleChildren:
14
+ Enabled: false
15
+ Style/IfUnlessModifier:
16
+ Enabled: false
17
+ Style/SelectByRegexp:
18
+ Enabled: false
19
+ Style/StringLiterals:
20
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
+ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [0.1.4] - 2023-03-24
8
+
9
+ First release as `rubocop-git2`.
10
+
11
+ ### Added
12
+
13
+ - support for `--only` flag
14
+ - support for `--format` flag
15
+ - via https://github.com/m4i/rubocop-git/pull/45
16
+
17
+ ### Fixed
18
+
19
+ - fixed many cops erroring ("An error occurred while ... was inspecting ...")
20
+ - incompatibility with some git configurations
21
+ - c.f. https://github.com/m4i/rubocop-git/issues/20
22
+ - via https://github.com/m4i/rubocop-git/pull/32
23
+ - fix had been merged into the original repo but was never released
24
+ - include and exclude behavior not matching rubocop's
25
+ - c.f. https://github.com/m4i/rubocop-git/issues/30
26
+ - partially via https://github.com/m4i/rubocop-git/pull/33
27
+ - enabled/disabled cops not being respected
28
+ - c.f. https://github.com/m4i/rubocop-git/issues/38
29
+ - c.f. https://github.com/m4i/rubocop-git/issues/39
30
+ - partially via https://github.com/m4i/rubocop-git/pull/47
31
+
32
+ ## [0.1.3] - 2017-04-08
33
+
34
+ Last release of the original `rubocop-git`.
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'bundler'
6
+ gem 'minitest'
7
+ gem 'rake'
data/LICENSE.txt ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2014 Masaki Takeuchi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ Some code is:
25
+ Copyright (c) 2014 thoughtbot, inc.
26
+ Released under the MIT License in Hound, https://github.com/thoughtbot/hound
data/README.md ADDED
@@ -0,0 +1,24 @@
1
+ **NOTE: this is a drop-in replacement for the unmaintained https://github.com/m4i/rubocop-git**
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/rubocop-git2.svg)](http://badge.fury.io/rb/rubocop-git2)
4
+ [![Build Status](https://github.com/jaynetics/rubocop-git2/workflows/tests/badge.svg)](https://github.com/jaynetics/rubocop-git2/actions)
5
+
6
+ # RuboCop::Git
7
+
8
+ RuboCop for git diff.
9
+
10
+ ## Installation
11
+
12
+ Add or install `rubocop-git2`. NOT YET RELEASED - will happen soon!
13
+
14
+ ## Usage
15
+
16
+ Usage: rubocop-git [options] [[commit] commit]
17
+ -c, --config FILE Specify configuration file
18
+ -r, --require FILE Require Ruby file
19
+ -d, --debug Display debug info
20
+ -D, --display-cop-names Display cop names in offense messages
21
+ --only COP1,COP2 Run only specific cops or departments
22
+ --cached git diff --cached
23
+ --staged synonym of --cached
24
+ --hound Hound compatibility mode
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ task default: %i[test selftest]
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << 'test'
8
+ t.libs << 'lib'
9
+ t.test_files = FileList['test/**/*_test.rb']
10
+ end
11
+
12
+ desc 'Run RuboCop::Git over itself'
13
+ task :selftest do
14
+ require_relative 'lib/rubocop/git'
15
+ RuboCop::Git::Runner.new.run(commits: ['v0.0.4'])
16
+ end
data/bin/rubocop-git ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/rubocop/git/cli'
4
+
5
+ RuboCop::Git::CLI.new.run
data/hound.yml ADDED
@@ -0,0 +1,254 @@
1
+ AllCops:
2
+ Exclude:
3
+ - db/schema.rb
4
+
5
+ AccessorMethodName:
6
+ Enabled: false
7
+
8
+ ActionFilter:
9
+ Enabled: false
10
+
11
+ Alias:
12
+ Enabled: false
13
+
14
+ ArrayJoin:
15
+ Enabled: false
16
+
17
+ AsciiComments:
18
+ Enabled: false
19
+
20
+ AsciiIdentifiers:
21
+ Enabled: false
22
+
23
+ Attr:
24
+ Enabled: false
25
+
26
+ BlockNesting:
27
+ Enabled: false
28
+
29
+ CaseEquality:
30
+ Enabled: false
31
+
32
+ CharacterLiteral:
33
+ Enabled: false
34
+
35
+ ClassAndModuleChildren:
36
+ Enabled: false
37
+
38
+ ClassLength:
39
+ Enabled: false
40
+
41
+ ClassVars:
42
+ Enabled: false
43
+
44
+ CollectionMethods:
45
+ PreferredMethods:
46
+ find: detect
47
+ reduce: inject
48
+ collect: map
49
+ find_all: select
50
+
51
+ ColonMethodCall:
52
+ Enabled: false
53
+
54
+ CommentAnnotation:
55
+ Enabled: false
56
+
57
+ CyclomaticComplexity:
58
+ Enabled: false
59
+
60
+ Delegate:
61
+ Enabled: false
62
+
63
+ DeprecatedHashMethods:
64
+ Enabled: false
65
+
66
+ Documentation:
67
+ Enabled: false
68
+
69
+ DotPosition:
70
+ EnforcedStyle: trailing
71
+
72
+ DoubleNegation:
73
+ Enabled: false
74
+
75
+ EachWithObject:
76
+ Enabled: false
77
+
78
+ EmptyLiteral:
79
+ Enabled: false
80
+
81
+ Encoding:
82
+ Enabled: false
83
+
84
+ EvenOdd:
85
+ Enabled: false
86
+
87
+ FileName:
88
+ Enabled: false
89
+
90
+ FlipFlop:
91
+ Enabled: false
92
+
93
+ FormatString:
94
+ Enabled: false
95
+
96
+ GlobalVars:
97
+ Enabled: false
98
+
99
+ GuardClause:
100
+ Enabled: false
101
+
102
+ IfUnlessModifier:
103
+ Enabled: false
104
+
105
+ IfWithSemicolon:
106
+ Enabled: false
107
+
108
+ Lambda:
109
+ Enabled: false
110
+
111
+ LambdaCall:
112
+ Enabled: false
113
+
114
+ LineEndConcatenation:
115
+ Enabled: false
116
+
117
+ LineLength:
118
+ Max: 80
119
+
120
+ MethodLength:
121
+ Enabled: false
122
+
123
+ ModuleFunction:
124
+ Enabled: false
125
+
126
+ NegatedIf:
127
+ Enabled: false
128
+
129
+ NegatedWhile:
130
+ Enabled: false
131
+
132
+ NilComparison:
133
+ Enabled: false
134
+
135
+ Not:
136
+ Enabled: false
137
+
138
+ NumericLiterals:
139
+ Enabled: false
140
+
141
+ OneLineConditional:
142
+ Enabled: false
143
+
144
+ OpMethod:
145
+ Enabled: false
146
+
147
+ ParameterLists:
148
+ Enabled: false
149
+
150
+ PercentLiteralDelimiters:
151
+ PreferredDelimiters:
152
+ '%': '{}'
153
+
154
+ PerlBackrefs:
155
+ Enabled: false
156
+
157
+ PredicateName:
158
+ NamePrefixBlacklist:
159
+ - is_
160
+
161
+ Proc:
162
+ Enabled: false
163
+
164
+ RaiseArgs:
165
+ Enabled: false
166
+
167
+ RegexpLiteral:
168
+ Enabled: false
169
+
170
+ SelfAssignment:
171
+ Enabled: false
172
+
173
+ SingleLineBlockParams:
174
+ Enabled: false
175
+
176
+ SingleLineMethods:
177
+ Enabled: false
178
+
179
+ SignalException:
180
+ Enabled: false
181
+
182
+ SpecialGlobalVars:
183
+ Enabled: false
184
+
185
+ StringLiterals:
186
+ EnforcedStyle: double_quotes
187
+
188
+ VariableInterpolation:
189
+ Enabled: false
190
+
191
+ TrailingComma:
192
+ Enabled: false
193
+
194
+ TrivialAccessors:
195
+ Enabled: false
196
+
197
+ VariableInterpolation:
198
+ Enabled: false
199
+
200
+ WhenThen:
201
+ Enabled: false
202
+
203
+ WhileUntilModifier:
204
+ Enabled: false
205
+
206
+ WordArray:
207
+ Enabled: false
208
+
209
+ # Lint
210
+
211
+ AmbiguousOperator:
212
+ Enabled: false
213
+
214
+ AmbiguousRegexpLiteral:
215
+ Enabled: false
216
+
217
+ AssignmentInCondition:
218
+ Enabled: false
219
+
220
+ ConditionPosition:
221
+ Enabled: false
222
+
223
+ DeprecatedClassMethods:
224
+ Enabled: false
225
+
226
+ ElseLayout:
227
+ Enabled: false
228
+
229
+ HandleExceptions:
230
+ Enabled: false
231
+
232
+ InvalidCharacterLiteral:
233
+ Enabled: false
234
+
235
+ LiteralInCondition:
236
+ Enabled: false
237
+
238
+ LiteralInInterpolation:
239
+ Enabled: false
240
+
241
+ Loop:
242
+ Enabled: false
243
+
244
+ ParenthesesAsGroupedExpression:
245
+ Enabled: false
246
+
247
+ RequireParentheses:
248
+ Enabled: false
249
+
250
+ UnderscorePrefixedVariableName:
251
+ Enabled: false
252
+
253
+ Void:
254
+ Enabled: false
@@ -0,0 +1,76 @@
1
+ require_relative '../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('--only COP1,COP2', Array) do |args|
48
+ @options.rubocop[:only] = args
49
+ end
50
+
51
+ opt.on('--cached', 'git diff --cached') do
52
+ @options.cached = true
53
+ end
54
+
55
+ opt.on('--staged', 'synonym of --cached') do
56
+ @options.cached = true
57
+ end
58
+
59
+ opt.on('--hound', 'Hound compatibility mode') do
60
+ @options.hound = true
61
+ end
62
+
63
+ opt.on('-f', '--format FORMAT',
64
+ 'Choose an output formatter') do |format|
65
+ @options.format = format
66
+ end
67
+
68
+ opt.on('--version', 'Display version') do
69
+ puts RuboCop::Git::VERSION
70
+ exit 0
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ 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,47 @@
1
+ module RuboCop::Git
2
+ # c.f. 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_line_at(line_number).nil?
27
+ end
28
+
29
+ def removed?
30
+ @file.status == 'removed'
31
+ end
32
+
33
+ def modified_lines
34
+ @modified_lines ||= patch.changed_lines
35
+ end
36
+
37
+ def modified_line_at(line_number)
38
+ modified_lines[line_number]
39
+ end
40
+
41
+ private
42
+
43
+ def patch
44
+ Patch.new(@file.patch)
45
+ end
46
+ end
47
+ 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,5 @@
1
+ module RuboCop::Git
2
+ # ref. https://github.com/thoughtbot/hound/blob/d2f3933/app/models/file_violation.rb
3
+ class FileViolation < Struct.new(:filename, :offenses)
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ module RuboCop::Git
2
+ # similar to https://github.com/thoughtbot/hound/blob/d2f3933/app/models/line.rb
3
+ class Line < Struct.new(:content, :line_number, :patch_position)
4
+ def ==(other_line)
5
+ content == other_line.content
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,94 @@
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, :format
11
+
12
+ def initialize(hash_options = nil)
13
+ @config = nil
14
+ @cached = false
15
+ @hound = false
16
+ @format = RuboCop::Formatter::ClangStyleFormatter
17
+ @rubocop = {}
18
+ @commits = []
19
+
20
+ from_hash(hash_options) if hash_options
21
+ end
22
+
23
+ def cached=(cached_)
24
+ if cached_ && !@commits.empty?
25
+ fail Invalid, 'cached and commit cannot be specified together'
26
+ end
27
+ @cached = !!cached_
28
+ end
29
+
30
+ def hound=(hound_)
31
+ @hound = !!hound_
32
+ end
33
+
34
+ def rubocop=(rubocop_)
35
+ unless rubocop_.nil? || rubocop_.is_a?(Hash)
36
+ fail Invalid, "invalid rubocop: #{rubocop_.inspect}"
37
+ end
38
+ @rubocop = rubocop_.to_h
39
+ end
40
+
41
+ def commits=(commits)
42
+ unless commits.is_a?(Array) && commits.length <= 2
43
+ fail Invalid, "invalid commits: #{commits.inspect}"
44
+ end
45
+ if !commits.empty? && cached
46
+ fail Invalid, 'cached and commit cannot be specified together'
47
+ end
48
+ @commits = commits
49
+ end
50
+
51
+ def format=(format)
52
+ formatters =
53
+ RuboCop::Formatter::FormatterSet::BUILTIN_FORMATTERS_FOR_KEYS
54
+ formatter_key = formatters.keys.find do |key|
55
+ key.start_with?(format)
56
+ end
57
+ @format = formatters[formatter_key] if formatter_key
58
+ end
59
+
60
+ def config_file
61
+ if hound
62
+ HOUND_DEFAULT_CONFIG_FILE
63
+ elsif config
64
+ config
65
+ elsif File.exist?(RuboCop::ConfigLoader::DOTFILE)
66
+ RuboCop::ConfigLoader::DOTFILE
67
+ else
68
+ RuboCop::ConfigLoader::DEFAULT_FILE
69
+ end
70
+ end
71
+
72
+ def commit_first
73
+ @commits.first
74
+ end
75
+
76
+ def commit_last
77
+ @commits.length == 1 ? false : @commits.last
78
+ end
79
+
80
+ private
81
+
82
+ def from_hash(hash_options)
83
+ hash_options = hash_options.dup
84
+ %w(config cached hound rubocop commits).each do |key|
85
+ value = hash_options.delete(key) || hash_options.delete(key.to_sym)
86
+ public_send("#{key}=", value)
87
+ end
88
+ unless hash_options.empty?
89
+ fail Invalid, "invalid keys: #{hash_options.keys.join(' ')}"
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,36 @@
1
+ module RuboCop::Git
2
+ # copy from https://github.com/thoughtbot/hound/blob/5269fa5/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 changed_lines # rubocop:disable Metrics/MethodLength
13
+ line_number = 0
14
+ lines.
15
+ each_with_object({}).
16
+ with_index do |(content, hash), patch_position|
17
+ case content
18
+ when RANGE_INFORMATION_LINE
19
+ line_number = Regexp.last_match[:line_number].to_i
20
+ when MODIFIED_LINE
21
+ line = Line.new(content, line_number, patch_position)
22
+ hash[line_number] = line
23
+ line_number += 1
24
+ when NOT_REMOVED_LINE
25
+ line_number += 1
26
+ end
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def lines
33
+ @body.each_line
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 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,75 @@
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 initialize(exit_on_offence: true)
8
+ @exit_on_offence = exit_on_offence
9
+ end
10
+
11
+ def run(options = {})
12
+ options = Options.new(options) unless options.is_a?(Options)
13
+
14
+ @options = options
15
+ @files = DiffParser.parse(git_diff(options))
16
+
17
+ display_violations($stdout)
18
+
19
+ ok = violations_with_valid_offences.none?
20
+ exit(1) if @exit_on_offence && !ok
21
+ ok
22
+ end
23
+
24
+ private
25
+
26
+ def violations
27
+ @violations ||= style_checker.violations
28
+ end
29
+
30
+ def violations_with_valid_offences
31
+ @violations_with_valid_offences ||= violations.select { |v| valid_offences(v).any? }
32
+ end
33
+
34
+ def valid_offences(violation)
35
+ offenses = violation.offenses
36
+ offenses = offenses.reject(&:disabled?) if offenses.first.respond_to?(:disabled?)
37
+ offenses.compact.sort.freeze
38
+ end
39
+
40
+ def style_checker
41
+ StyleChecker.new(
42
+ pull_request.pull_request_files,
43
+ @options.rubocop,
44
+ @options.config_file,
45
+ pull_request.config
46
+ )
47
+ end
48
+
49
+ def pull_request
50
+ @pull_request ||= PseudoPullRequest.new(@files, @options)
51
+ end
52
+
53
+ def git_diff(options)
54
+ args = %w(diff --diff-filter=AMCR --find-renames --find-copies)
55
+
56
+ args << '--cached' if options.cached
57
+ args << options.commit_first.shellescape if options.commit_first
58
+ args << options.commit_last.shellescape if options.commit_last
59
+
60
+ `git #{args.join(' ')}`
61
+ end
62
+
63
+ def display_violations(io)
64
+ formatter = @options.format.new(io)
65
+ formatter.started(@files)
66
+
67
+ violations.map do |violation|
68
+ formatter.file_finished(violation.filename, valid_offences(violation))
69
+ end
70
+
71
+ formatter.finished(@files.map(&:filename).freeze)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,43 @@
1
+ module RuboCop::Git
2
+ # ref. https://github.com/thoughtbot/hound/blob/d2f3933/app/models/style_checker.rb
3
+ class StyleChecker
4
+ def initialize(modified_files,
5
+ rubocop_options,
6
+ config_file,
7
+ custom_config = nil)
8
+ @modified_files = modified_files
9
+ @rubocop_options = rubocop_options
10
+ @config_file = config_file
11
+ @custom_config = custom_config
12
+ end
13
+
14
+ def violations
15
+ file_violations = @modified_files.map do |modified_file|
16
+ FileViolation.new(modified_file.absolute_path, offenses(modified_file))
17
+ end
18
+
19
+ file_violations.select do |file_violation|
20
+ file_violation.offenses.any?
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def offenses(modified_file)
27
+ violations = style_guide.violations(modified_file)
28
+ violations_on_changed_lines(modified_file, violations)
29
+ end
30
+
31
+ def violations_on_changed_lines(modified_file, violations)
32
+ violations.select do |violation|
33
+ modified_file.relevant_line?(violation.line)
34
+ end
35
+ end
36
+
37
+ def style_guide
38
+ @style_guide ||= StyleGuide.new(@rubocop_options,
39
+ @config_file,
40
+ @custom_config)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,91 @@
1
+ module RuboCop::Git
2
+ # ref. https://github.com/thoughtbot/hound/blob/d2f3933/app/models/style_guide.rb
3
+ class StyleGuide
4
+ def initialize(rubocop_options, config_file, override_config_content = nil)
5
+ @rubocop_options = rubocop_options
6
+ @config_file = config_file
7
+ @override_config_content = override_config_content
8
+ end
9
+
10
+ def violations(file)
11
+ if ignored_file?(file)
12
+ []
13
+ else
14
+ parsed_source = parse_source(file)
15
+ team = RuboCop::Cop::Team.new(enabled_cops, config, rubocop_options)
16
+ team.inspect_file(parsed_source)
17
+ end
18
+ end
19
+
20
+ def inspect
21
+ "#<#{self.class.name}>"
22
+ end
23
+
24
+ private
25
+
26
+ def enabled_cops
27
+ registry.enabled(config)
28
+ end
29
+
30
+ def registry
31
+ @registry ||= begin
32
+ cops = RuboCop::Cop::Registry.all
33
+ if (only = rubocop_options[:only])
34
+ cops = cops.select { |c| c.match?(only) }
35
+ end
36
+ RuboCop::Cop::Registry.new(cops, rubocop_options)
37
+ end
38
+ end
39
+
40
+ def ignored_file?(file)
41
+ !included_file?(file) || file.removed? || excluded_file?(file)
42
+ end
43
+
44
+ def included_file?(file)
45
+ config.file_to_include?(file.absolute_path)
46
+ end
47
+
48
+ def excluded_file?(file)
49
+ config.file_to_exclude?(file.absolute_path)
50
+ end
51
+
52
+ def parse_source(file)
53
+ source = RuboCop::ProcessedSource.new(
54
+ file.content,
55
+ config.target_ruby_version,
56
+ file.absolute_path
57
+ )
58
+ source.config = config
59
+ source.registry = registry
60
+ source
61
+ end
62
+
63
+ def config
64
+ @config ||= begin
65
+ config = RuboCop::ConfigLoader.configuration_from_file(@config_file)
66
+ combined_config = RuboCop::ConfigLoader.merge(config, override_config)
67
+ RuboCop::Config.new(combined_config, "")
68
+ end
69
+ end
70
+
71
+ def rubocop_options
72
+ if config["ShowCopNames"]
73
+ { debug: true }
74
+ else
75
+ {}
76
+ end.merge(@rubocop_options)
77
+ end
78
+
79
+ def override_config
80
+ if @override_config_content
81
+ config_content = YAML.load(@override_config_content)
82
+ override_config = RuboCop::Config.new(config_content, "")
83
+ override_config.add_missing_namespaces
84
+ override_config.make_excludes_absolute
85
+ override_config
86
+ else
87
+ {}
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,5 @@
1
+ module RuboCop
2
+ module Git
3
+ VERSION = '0.1.4'.freeze
4
+ end
5
+ end
@@ -0,0 +1,17 @@
1
+ require_relative 'git/version'
2
+ require 'rubocop'
3
+
4
+ module RuboCop::Git
5
+ autoload :Commit, "#{__dir__}/git/commit"
6
+ autoload :CommitFile, "#{__dir__}/git/commit_file"
7
+ autoload :DiffParser, "#{__dir__}/git/diff_parser"
8
+ autoload :FileViolation, "#{__dir__}/git/file_violation"
9
+ autoload :Line, "#{__dir__}/git/line"
10
+ autoload :Options, "#{__dir__}/git/options"
11
+ autoload :Patch, "#{__dir__}/git/patch"
12
+ autoload :PseudoPullRequest, "#{__dir__}/git/pseudo_pull_request"
13
+ autoload :PseudoResource, "#{__dir__}/git/pseudo_resource"
14
+ autoload :Runner, "#{__dir__}/git/runner"
15
+ autoload :StyleChecker, "#{__dir__}/git/style_checker"
16
+ autoload :StyleGuide, "#{__dir__}/git/style_guide"
17
+ end
@@ -0,0 +1,21 @@
1
+ require File.join(File.expand_path(__dir__), 'lib', 'rubocop', 'git', 'version')
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'rubocop-git2'
5
+ spec.version = RuboCop::Git::VERSION
6
+ spec.authors = ['Masaki Takeuchi', 'Janosch Müller']
7
+ spec.email = ['janosch84@gmail.com']
8
+ spec.summary = %q{RuboCop for git diff.}
9
+ spec.description = %q{RuboCop for git diff.}
10
+ spec.homepage = 'https://github.com/jaynetics/rubocop-git2'
11
+ spec.license = 'MIT'
12
+
13
+ spec.files = `git ls-files -z`.split("\x0")
14
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
15
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
16
+ spec.require_paths = ['lib']
17
+
18
+ spec.required_ruby_version = '>= 2.6.0'
19
+
20
+ spec.add_dependency 'rubocop', '~> 1.0'
21
+ end
data/test/bad_ruby.txt ADDED
@@ -0,0 +1,3 @@
1
+ # should be ignored because filename doesn't match default includes
2
+
3
+ MY_CONST = "haha, mutable!"
@@ -0,0 +1,12 @@
1
+ require_relative '../../test_helper'
2
+ require 'rubocop/git/cli'
3
+
4
+ describe RuboCop::Git::CLI do
5
+ it 'fail with invalid options' do
6
+ _ do
7
+ _out, _err = capture_io do
8
+ RuboCop::Git::CLI.new.run(['--gruß'])
9
+ end
10
+ end.must_raise(SystemExit)
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ require_relative '../../test_helper'
2
+ require 'rubocop/git/options'
3
+
4
+ describe RuboCop::Git::Options do
5
+ it 'fail with no options' do
6
+ _ do
7
+ RuboCop::Git::Options.new({})
8
+ end.must_raise(RuboCop::Git::Options::Invalid)
9
+ end
10
+
11
+ it 'can pass string hash options' do
12
+ RuboCop::Git::Options.new('rubocop' => {}, 'commits' => [])
13
+ end
14
+
15
+ it 'can pass symbol hash options' do
16
+ RuboCop::Git::Options.new(rubocop: {}, commits: [])
17
+ end
18
+ end
@@ -0,0 +1,43 @@
1
+ require_relative '../../test_helper'
2
+ require 'rubocop/git/runner'
3
+
4
+ describe RuboCop::Git::Runner do
5
+ it 'exit with violations' do
6
+ options = RuboCop::Git::Options.new
7
+ options.commits = ["v0.0.4", "v0.0.5"]
8
+ _ do
9
+ capture_io do
10
+ RuboCop::Git::Runner.new.run(options)
11
+ end
12
+ end.must_raise(SystemExit)
13
+ end
14
+
15
+ it 'enables cops passed via --only flag' do
16
+ options = RuboCop::Git::Options.new
17
+ options.commits = ["v0.0.0"]
18
+ out, _err = capture_io do
19
+ res = RuboCop::Git::Runner.new(exit_on_offence: false).run(options)
20
+ res.must_equal(false)
21
+ end
22
+ _(out).must_match('Style/MutableConstant')
23
+ _(out).wont_match('Style/FrozenStringLiteralComment')
24
+ _(out).wont_match('bad_ruby.txt') # check that default `Include:` applies
25
+
26
+ options.rubocop[:only] = ['Style/FrozenStringLiteralComment']
27
+ out, _err = capture_io do
28
+ res = RuboCop::Git::Runner.new(exit_on_offence: false).run(options)
29
+ res.must_equal(false)
30
+ end
31
+ _(out).wont_match('Style/MutableConstant')
32
+ _(out).must_match('Style/FrozenStringLiteralComment')
33
+ _(out).must_match('Rakefile') # check that default `Include:` applies
34
+ end
35
+
36
+ it 'fail with no options' do
37
+ _ do
38
+ _out, _err = capture_io do
39
+ RuboCop::Git::Runner.new.run({})
40
+ end
41
+ end.must_raise(RuboCop::Git::Options::Invalid)
42
+ end
43
+ end
@@ -0,0 +1,2 @@
1
+ require 'minitest/autorun'
2
+ require 'rubocop/git'
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubocop-git2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
5
+ platform: ruby
6
+ authors:
7
+ - Masaki Takeuchi
8
+ - Janosch Müller
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2023-03-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rubocop
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.0'
28
+ description: RuboCop for git diff.
29
+ email:
30
+ - janosch84@gmail.com
31
+ executables:
32
+ - rubocop-git
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - ".github/workflows/tests.yml"
37
+ - ".gitignore"
38
+ - ".rubocop.yml"
39
+ - CHANGELOG.md
40
+ - Gemfile
41
+ - LICENSE.txt
42
+ - README.md
43
+ - Rakefile
44
+ - bin/rubocop-git
45
+ - hound.yml
46
+ - lib/rubocop/git.rb
47
+ - lib/rubocop/git/cli.rb
48
+ - lib/rubocop/git/commit.rb
49
+ - lib/rubocop/git/commit_file.rb
50
+ - lib/rubocop/git/diff_parser.rb
51
+ - lib/rubocop/git/file_violation.rb
52
+ - lib/rubocop/git/line.rb
53
+ - lib/rubocop/git/options.rb
54
+ - lib/rubocop/git/patch.rb
55
+ - lib/rubocop/git/pseudo_pull_request.rb
56
+ - lib/rubocop/git/pseudo_resource.rb
57
+ - lib/rubocop/git/runner.rb
58
+ - lib/rubocop/git/style_checker.rb
59
+ - lib/rubocop/git/style_guide.rb
60
+ - lib/rubocop/git/version.rb
61
+ - rubocop-git.gemspec
62
+ - test/bad_ruby.txt
63
+ - test/rubocop/git/cli_test.rb
64
+ - test/rubocop/git/options_test.rb
65
+ - test/rubocop/git/runner_test.rb
66
+ - test/test_helper.rb
67
+ homepage: https://github.com/jaynetics/rubocop-git2
68
+ licenses:
69
+ - MIT
70
+ metadata: {}
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: 2.6.0
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubygems_version: 3.4.1
87
+ signing_key:
88
+ specification_version: 4
89
+ summary: RuboCop for git diff.
90
+ test_files:
91
+ - test/bad_ruby.txt
92
+ - test/rubocop/git/cli_test.rb
93
+ - test/rubocop/git/options_test.rb
94
+ - test/rubocop/git/runner_test.rb
95
+ - test/test_helper.rb