2
2
require 'pathname'
3
3
require 'json'
4
4
require 'digest/sha1'
5
+ require 'open3'
5
6
6
7
module CC
7
8
module Engine
8
9
class Golangci
9
10
ALLOWED_EXTENSIONS = %w[ .go ]
11
+ ISSUE_IDENTIFIER_REGEXP = /^([^\s ]+): (.*)/ . freeze
10
12
11
13
def initialize ( engine_config )
12
14
@engine_config = engine_config || { }
13
15
@files_to_analyze = [ ]
14
16
@dirs_to_analyze = [ ]
15
17
end
16
18
17
- attr_reader :engine_config , :dirs_to_analyze , :files_to_analyze
19
+ attr_reader :engine_config
18
20
19
21
def run
20
- build_paths_to_analyze
22
+ issues = analyze_paths . compact
21
23
22
- run_for_paths ( dirs_to_analyze )
23
- run_for_paths ( files_to_analyze )
24
+ puts output_issues ( issues )
24
25
end
25
26
26
- def run_for_paths ( paths )
27
- data = IO . popen ( command_env , command ( paths ) ) . read
28
- begin
29
- data = JSON . parse ( data )
30
- rescue JSON ::ParserError
31
- warn "Error parsing golangci-lint's output:"
32
- warn data
33
- exit!
27
+ def analyze_paths
28
+ # run the linter for each include_paths path
29
+ # we do this because the linter is very strict and loud about the paths to analyze we provide as arguments
30
+ # this method is noticeably slower than running the linter just once
31
+
32
+ include_paths . flat_map do |path |
33
+ real_path = Pathname . new ( path ) . realpath
34
+
35
+ if real_path . directory?
36
+ path += "..."
37
+ else
38
+ next unless ALLOWED_EXTENSIONS . include? ( real_path . extname )
39
+ end
40
+
41
+ issues = run_command ( path ) [ "Issues" ]
42
+ next unless issues . is_a? ( Array ) && issues . length > 0
43
+
44
+ issues . map { |issue | process_issue ( issue ) }
34
45
end
46
+ end
47
+
48
+ def run_command ( path )
49
+ data = IO . popen ( command_env , command ( path ) ) . read
50
+ return { } if data . nil? || data . empty?
35
51
36
- issues = data [ "Issues" ]
37
- return unless issues . is_a? ( Array ) && issues . length > 0
38
-
39
- puts data [ 'Issues' ] . map { |issue | "#{ convert_issue ( issue ) } \0 " } . join
52
+ JSON . parse ( data )
53
+ rescue JSON ::ParserError
54
+ warn "Error parsing golangci-lint's output:"
55
+ warn "#{ data } "
56
+ exit!
40
57
end
41
58
42
- def command ( paths )
43
- [ "/usr/local/bin/golangci-lint" , "run" , "--out-format" , "json" , * paths ]
59
+ def command ( path )
60
+ [ "/usr/local/bin/golangci-lint" , "run" , "--out-format" , "json" , path ]
44
61
end
45
62
46
63
def command_env
47
64
{ "CGO_ENABLED" => "0" }
48
65
end
49
66
50
- ISSUE_IDENTIFIER_REGEXP = /^([^\s ]+): (.*)/ . freeze
67
+ def output_issues ( issues )
68
+ issues . uniq { |issue | issue [ :fingerprint ] } . map { |issue | issue . to_json + "\0 " } . join
69
+ end
51
70
52
- def convert_issue ( issue )
71
+ def process_issue ( issue )
53
72
text = issue [ 'Text' ]
54
73
# Data coming from linters is not standardised, so it may be quite
55
74
# complicated to extract a check_name and description from it. Here we
@@ -69,7 +88,7 @@ def convert_issue(issue)
69
88
categories : categories_for_linter ( linter_name ) ,
70
89
fingerprint : fingerprint_issue ( issue ) ,
71
90
location : locate_issue ( issue )
72
- } . to_json
91
+ }
73
92
end
74
93
75
94
def categories_for_linter ( linter )
@@ -104,28 +123,8 @@ def fingerprint_issue(issue)
104
123
105
124
private
106
125
107
- def build_paths_to_analyze
108
- # golangci-lint surfaces errors when analyzing directories and files in the same run,
109
- # so we need to split the analysis into two different runs: one for directories and one for files
110
-
111
- include_paths = engine_config [ "include_paths" ] || [ "./" ]
112
-
113
- include_paths . each do |path |
114
- begin
115
- pathname = Pathname . new ( path ) . realpath
116
-
117
- if pathname . directory?
118
- # golangci-lint allows adding ... to a directory path to analyze it recursively
119
- # we want to do this for all directories
120
-
121
- @dirs_to_analyze << ( pathname + "..." ) . to_s
122
- else
123
- @files_to_analyze << pathname . to_s if ALLOWED_EXTENSIONS . include? ( pathname . extname )
124
- end
125
- rescue Errno ::ENOENT
126
- nil
127
- end
128
- end . compact
126
+ def include_paths
127
+ @include_paths ||= engine_config [ "include_paths" ]
129
128
end
130
129
end
131
130
end
0 commit comments