nexocop 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/nexocop +110 -0
- data/lib/nexocop.rb +5 -0
- data/lib/nexocop/git.rb +59 -0
- data/lib/nexocop/options.rb +35 -0
- data/lib/nexocop/rubocop.rb +56 -0
- data/lib/nexocop/shell.rb +31 -0
- data/lib/nexocop/version.rb +11 -0
- metadata +137 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1def61da2ee28929e12ef3c9397ab4581d03953d
|
4
|
+
data.tar.gz: 7cd746e821d8b0ea33da7b035c56abd6ac751ad9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cadcce64b1ce92491cee48d4bd917f6ad7f072757be0f3bcbab437fa410d61aa7ef27061626edf11e192ab58d5ebbe3053cc9b86af534f041bbba4a224aa4bcf
|
7
|
+
data.tar.gz: fe1dfafe43828b7a9dfdaed923b56d8e9092cc13bc2e187824919fc4a9dc40c32389116162f02463ead5e46259a59aa588f8e38d30cc31455ac73d8e1dd297ec
|
data/bin/nexocop
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'rainbow/refinement'
|
5
|
+
using Rainbow
|
6
|
+
|
7
|
+
require 'nexocop'
|
8
|
+
|
9
|
+
module Nexocop
|
10
|
+
#
|
11
|
+
# Mutates the argument by adding the file path to each offense
|
12
|
+
#
|
13
|
+
def self.add_path_to_offenses!(part_json)
|
14
|
+
part_json['files'].each do |file|
|
15
|
+
file['offenses'].map! do |offense|
|
16
|
+
offense['path'] = file['path']
|
17
|
+
offense
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Mutates the argument by removing files that
|
24
|
+
# weren't changed by this branch
|
25
|
+
#
|
26
|
+
def self.filter_files_not_in_diff!(part_json, changed_lines)
|
27
|
+
part_json['files'].select! do |file|
|
28
|
+
changed_lines.key?(file['path'])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Mutates the argument by removing files that
|
34
|
+
# have no offenses
|
35
|
+
#
|
36
|
+
def self.filter_files_without_offenses!(part_json)
|
37
|
+
part_json['files'].select! do |file|
|
38
|
+
file['offenses'].count.positive?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Mutates the argument by removing offenses that
|
44
|
+
# weren't added by this branch
|
45
|
+
#
|
46
|
+
def self.filter_offenses_not_in_diff!(part_json, changed_lines)
|
47
|
+
part_json['files'].each do |file|
|
48
|
+
file['offenses'].select! do |offense|
|
49
|
+
Rubocop.offense_in_diff?(offense, changed_lines)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# Mutates the argument by adjusting the summary numbers
|
56
|
+
# to be accurate based on our trimming
|
57
|
+
#
|
58
|
+
def self.update_summary!(part_json)
|
59
|
+
part_json['summary']['offense_count'] = Rubocop.count_offenses(part_json)
|
60
|
+
part_json['summary']['target_file_count'] = Rubocop.count_files(part_json)
|
61
|
+
part_json
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Only include offenses pertaining to code that has changed
|
66
|
+
#
|
67
|
+
def self.trim_results(full_json)
|
68
|
+
changed_lines = Git.changed_lines
|
69
|
+
part_json = full_json.dup
|
70
|
+
add_path_to_offenses!(part_json)
|
71
|
+
filter_files_not_in_diff!(part_json, changed_lines)
|
72
|
+
filter_offenses_not_in_diff!(part_json, changed_lines)
|
73
|
+
filter_files_without_offenses!(part_json)
|
74
|
+
update_summary!(part_json)
|
75
|
+
part_json
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.run(json_file:, rubocop_args:)
|
79
|
+
unless Rubocop.available?
|
80
|
+
puts 'Could not find rubocop! Please make sure it is installed'.red
|
81
|
+
exit 1
|
82
|
+
end
|
83
|
+
|
84
|
+
res = Rubocop.run(rubocop_args)
|
85
|
+
|
86
|
+
if res.success?
|
87
|
+
puts 'Rubocop found no violations!'.green
|
88
|
+
exit 0
|
89
|
+
elsif !File.exist?(json_file)
|
90
|
+
puts "Rubocop found violations but did not save the file at #{json_file} as expected!".red
|
91
|
+
exit 2
|
92
|
+
else
|
93
|
+
part_json = trim_results(JSON.parse(File.read(json_file)))
|
94
|
+
File.write(json_file, JSON.pretty_generate(part_json))
|
95
|
+
if Rubocop.has_offenses?(part_json)
|
96
|
+
count = Rubocop.count_offenses(part_json)
|
97
|
+
violations_that_need = count == 1 ? 'violation that needs' : 'violations that need'
|
98
|
+
puts "Found #{count} #{violations_that_need} to be fixed:\n".yellow
|
99
|
+
puts JSON.pretty_generate(part_json)
|
100
|
+
exit 3
|
101
|
+
else
|
102
|
+
puts 'No offenses found in the new code!'.green
|
103
|
+
exit 0
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
args = Nexocop::Options.parse_args(ARGV)
|
110
|
+
Nexocop.run(json_file: args.json_outfile, rubocop_args: args.rubocop_args)
|
data/lib/nexocop.rb
ADDED
data/lib/nexocop/git.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubocop'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
module Nexocop
|
7
|
+
module Git
|
8
|
+
# Get an array of line numbers that have been changed
|
9
|
+
#
|
10
|
+
# For example, you will get a structure that looks like this:
|
11
|
+
#
|
12
|
+
# {
|
13
|
+
# 'Dockerfile' => [[12, 12], [15, 15]],
|
14
|
+
# 'docker-compose.yml' => [[24, 24], [52, 52]],
|
15
|
+
# 'bin/nexocop' => [[1, 31]]
|
16
|
+
# }
|
17
|
+
#
|
18
|
+
def self.changed_lines(git_diff = nil)
|
19
|
+
git_diff ||= Sh.run_command('git diff --unified=0 origin/master').stdout
|
20
|
+
lines = {}
|
21
|
+
cur_file = ''
|
22
|
+
git_diff.split("\n").each do |line|
|
23
|
+
# look for filenames and update cur_file, or for count lines
|
24
|
+
if filename?(line)
|
25
|
+
cur_file = parse_filename(line)
|
26
|
+
elsif count_line?(line)
|
27
|
+
lines[cur_file] ||= []
|
28
|
+
lines[cur_file].push(parse_count_line(line))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
lines
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.filename?(line)
|
35
|
+
line.start_with?('+++ b/')
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.parse_filename(line)
|
39
|
+
line.gsub(%r{^\+\+\+\sb/}, '')
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.count_line?(line)
|
43
|
+
line =~ /@@.*@@/
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Extract line numbers from this, return array of length 2 with
|
48
|
+
# beginning line num and ending line num respectively
|
49
|
+
#
|
50
|
+
def self.parse_count_line(line)
|
51
|
+
pos_block = line.split('@@')[1].strip.split('+')[1].split(',').map(&:to_i)
|
52
|
+
if pos_block.count == 1
|
53
|
+
[pos_block[0], pos_block[0]]
|
54
|
+
else
|
55
|
+
[pos_block[0], pos_block[0] + pos_block[1]]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubocop'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
module Nexocop
|
7
|
+
module Options
|
8
|
+
def self.default_json_file
|
9
|
+
'nexocop.json'
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.parse_args(args)
|
13
|
+
# We need to know where the json file will be. If one isn't specified,
|
14
|
+
# set it explicitly so that we know it is there
|
15
|
+
|
16
|
+
# Use Rubocop's parser so we stay synced
|
17
|
+
options, _paths = RuboCop::Options.new.parse(args)
|
18
|
+
rubocop_args = args.dup
|
19
|
+
json_outfile = nil
|
20
|
+
|
21
|
+
if options[:formatters] && options[:formatters].count { |f| f[0]['json'] } == 1
|
22
|
+
json_outfile = options[:formatters].select { |f| f[0]['json'] }[0][1]
|
23
|
+
else
|
24
|
+
options[:formatters] = [] unless options[:formatters]
|
25
|
+
json_outfile = default_json_file
|
26
|
+
rubocop_args.push(%w[--format json -o].push(default_json_file)).flatten!
|
27
|
+
end
|
28
|
+
|
29
|
+
OpenStruct.new(
|
30
|
+
json_outfile: json_outfile,
|
31
|
+
rubocop_args: rubocop_args
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubocop'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
module Nexocop
|
7
|
+
module Rubocop
|
8
|
+
def self.available?
|
9
|
+
!rubocop_cmd.empty?
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.rubocop_cmd
|
13
|
+
if Sh.run_command('which bundle >/dev/null 2>&1').success?
|
14
|
+
'bundle exec rubocop'
|
15
|
+
elsif Sh.run_command('which rubocop >/dev/null 2>&1').success?
|
16
|
+
'rubocop'
|
17
|
+
else
|
18
|
+
''
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.run(rubocop_args)
|
23
|
+
Sh.run_command("#{rubocop_cmd} #{rubocop_args.join(' ')}")
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.offense_in_diff?(offense, changed_lines)
|
27
|
+
# make sure this offense is in a file that has changed
|
28
|
+
return false unless changed_lines.key?(offense['path'])
|
29
|
+
|
30
|
+
changed_lines[offense['path']].any? do |line_range|
|
31
|
+
start_line = offense['location']['start_line']
|
32
|
+
last_line = offense['location']['last_line']
|
33
|
+
range = (line_range[0]..line_range[1])
|
34
|
+
(start_line..last_line).to_a.any? { |line_num| range.include?(line_num) }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.has_offenses?(rubocop_json)
|
39
|
+
rubocop_json['files']
|
40
|
+
.map { |file| file['offenses'] }
|
41
|
+
.map(&:count)
|
42
|
+
.any?(&:positive?)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.count_offenses(rubocop_json)
|
46
|
+
rubocop_json['files']
|
47
|
+
.map { |file| file['offenses'] }
|
48
|
+
.map(&:count)
|
49
|
+
.reduce(:+)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.count_files(rubocop_json)
|
53
|
+
rubocop_json['files'].count
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
module Nexocop
|
6
|
+
module Sh
|
7
|
+
def self.run_command(command)
|
8
|
+
stdout = `#{command}`
|
9
|
+
OpenStruct.new(
|
10
|
+
success?: $?.exitstatus.zero?,
|
11
|
+
exitstatus: $?.exitstatus,
|
12
|
+
stdout: stdout
|
13
|
+
)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module Bash
|
18
|
+
def self.escape_double_quotes(str)
|
19
|
+
str.gsub('"', '\\"')
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.run_command(command)
|
23
|
+
stdout = `bash -c "#{escape_double_quotes(command)}"`
|
24
|
+
OpenStruct.new(
|
25
|
+
success?: $?.exitstatus.zero?,
|
26
|
+
exitstatus: $?.exitstatus,
|
27
|
+
stdout: stdout
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nexocop
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ben Porter
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-10-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rainbow
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rubocop
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.59'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.59'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: thor
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.20'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.20'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: byebug
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '12.3'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '12.3'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.8'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.8'
|
97
|
+
description: Nexocop makes it trivial to add rubocop linting to your project that
|
98
|
+
will only check linting against lines that have changed in git. Rubocop normally
|
99
|
+
is not git aware. This gem makes it git aware.
|
100
|
+
email: BenjaminPorter86@gmail.com
|
101
|
+
executables:
|
102
|
+
- nexocop
|
103
|
+
extensions: []
|
104
|
+
extra_rdoc_files: []
|
105
|
+
files:
|
106
|
+
- bin/nexocop
|
107
|
+
- lib/nexocop.rb
|
108
|
+
- lib/nexocop/git.rb
|
109
|
+
- lib/nexocop/options.rb
|
110
|
+
- lib/nexocop/rubocop.rb
|
111
|
+
- lib/nexocop/shell.rb
|
112
|
+
- lib/nexocop/version.rb
|
113
|
+
homepage: https://github.com/FreedomBen/nexocop
|
114
|
+
licenses:
|
115
|
+
- MIT
|
116
|
+
metadata: {}
|
117
|
+
post_install_message:
|
118
|
+
rdoc_options: []
|
119
|
+
require_paths:
|
120
|
+
- lib
|
121
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 2.3.0
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
requirements: []
|
132
|
+
rubyforge_project:
|
133
|
+
rubygems_version: 2.5.2.3
|
134
|
+
signing_key:
|
135
|
+
specification_version: 4
|
136
|
+
summary: Easy rubocop wrapping for git diffs
|
137
|
+
test_files: []
|