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 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: []