nexocop 0.0.1

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
+ 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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Gem.find_files('nexocop/**/*.rb').each do |path|
4
+ require path.gsub(/\.rb$/, '') unless path =~ /bot.*cli/
5
+ end
@@ -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
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nexocop
4
+ def self.version
5
+ '0.0.1'
6
+ end
7
+
8
+ def self.date
9
+ '2018-10-02'
10
+ end
11
+ 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: []