nexocop 0.0.1
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/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: []
|