rubocop-git2 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 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