danger-warnings 0.0.1 → 0.1.0
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 +4 -4
- data/CHANGELOG.md +11 -0
- data/Dangerfile +5 -0
- data/README.md +33 -5
- data/lib/warnings/gem_version.rb +1 -1
- data/lib/warnings/{markdown_util.rb → helper/message_util.rb} +31 -7
- data/lib/warnings/helper/severity_util.rb +45 -0
- data/lib/warnings/parser/bandit_parser.rb +2 -8
- data/lib/warnings/parser/parser.rb +23 -15
- data/lib/warnings/parser/parser_factory.rb +5 -1
- data/lib/warnings/parser/pylint_parser.rb +38 -0
- data/lib/warnings/parser/rubocop_parser.rb +77 -0
- data/lib/warnings/plugin.rb +1 -1
- data/lib/warnings/{issue.rb → report/issue.rb} +2 -7
- data/lib/warnings/{reporter.rb → report/reporter.rb} +4 -9
- data/spec/assets/empty.txt +0 -0
- data/spec/assets/pylint.txt +582 -0
- data/spec/assets/rubocop.json +265 -0
- data/spec/assets/rubocop.txt +27 -0
- data/spec/assets/rubocop_multi_offenses.json +142 -0
- data/spec/helper/message_util_spec.rb +108 -0
- data/spec/helper/severity_util_spec.rb +70 -0
- data/spec/parser/bandit_parser_spec.rb +8 -36
- data/spec/parser/parser_factory_spec.rb +24 -12
- data/spec/parser/pylint_parser_spec.rb +57 -0
- data/spec/parser/rubocop_parser_spec.rb +94 -0
- data/spec/{reporter_spec.rb → report/reporter_spec.rb} +42 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/spec_helper/assets.rb +53 -0
- data/spec/warnings_spec.rb +0 -5
- metadata +31 -14
- data/lib/warnings/severity.rb +0 -12
- data/spec/assets/assets.rb +0 -8
- data/spec/markdown_util_spec.rb +0 -65
- data/spec/severity_spec.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90dcfaf900785427cae7f5996651fd006b6fc4fee2f67eb0674e330ff171ccab
|
4
|
+
data.tar.gz: f9c6800309d84929707bde221549d6ad3da3b84ddf51171cbad5f314132feccd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 184a35b56e0e5c80ae92f169df055033e26d88f7d09db9310987933582fab388c9579939b57ff886d789bc486d941b166b3ae17d6df8411fcfd4695f876c17a6
|
7
|
+
data.tar.gz: e942fad54a71a5f1bf3cdb31675228db17744ae7abf3777b80c4160a4739489250b19611422c7c469c69156d5113c430ac2300bc5f26545d1669cc178ae67d43
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [0.1.0] - 2019-01-23
|
10
|
+
### Added
|
11
|
+
- PylintParser - Support Pylint formatted reports
|
12
|
+
- RuboCopParser - Support RuboCop formatted reports
|
13
|
+
|
14
|
+
### Changed
|
15
|
+
- Renamed issue.id to issue.category
|
16
|
+
|
17
|
+
### Removed
|
18
|
+
- Remove issue.name
|
19
|
+
|
9
20
|
## [0.0.1] - 2019-01-21
|
10
21
|
### Added
|
11
22
|
- Initial release
|
data/Dangerfile
ADDED
data/README.md
CHANGED
@@ -10,6 +10,17 @@
|
|
10
10
|
|
11
11
|
</br>
|
12
12
|
|
13
|
+
<div align="center">
|
14
|
+
<!-- Version -->
|
15
|
+
<a href="https://badge.fury.io/rb/danger-warnings">
|
16
|
+
<img src="https://badge.fury.io/rb/danger-warnings.svg" alt="Version" />
|
17
|
+
</a>
|
18
|
+
<!-- Downloads -->
|
19
|
+
<a href="https://badge.fury.io/rb/danger-warnings">
|
20
|
+
<img src="https://img.shields.io/gem/dt/danger-warnings.svg" alt="Downloads" />
|
21
|
+
</a>
|
22
|
+
</div>
|
23
|
+
|
13
24
|
<div align="center">
|
14
25
|
<!-- Build Status -->
|
15
26
|
<a href="https://travis-ci.org/Kyaak/danger-warnings">
|
@@ -79,8 +90,10 @@
|
|
79
90
|
|
80
91
|
</br>
|
81
92
|
|
82
|
-
This [danger](https://github.com/danger/danger) plugin provides a uniform report format for various
|
83
|
-
The purpose is a simple to use plugin regardless of the
|
93
|
+
This [danger](https://github.com/danger/danger) plugin provides a uniform report format for various [tools](#parsers). <br>
|
94
|
+
The purpose is a simple to use plugin regardless of the tool used to find issues in your project :detective:
|
95
|
+
|
96
|
+
This plugin was inspired by the work of [warnings-ng-plugin](https://github.com/jenkinsci/warnings-ng-plugin) :bowing_man:
|
84
97
|
|
85
98
|
## Table of Contents
|
86
99
|
- [How it looks like](#how-does-it-look)
|
@@ -206,8 +219,23 @@ All [default](#override-default-settings) fields can be passed as parameters to
|
|
206
219
|
|
207
220
|
These will override the configuration for this report **only**.
|
208
221
|
|
222
|
+
#### What it does not
|
223
|
+
It is not the responsibility of this plugin to exclude / include files or directories. We will only process the result and present it to you.
|
224
|
+
Something like this belongs to your tool configuration before running it.
|
225
|
+
|
209
226
|
## Parsers
|
210
227
|
|
211
|
-
|
212
|
-
|
213
|
-
|
228
|
+
Find a list with supported report formats and their parsers.
|
229
|
+
|
230
|
+
If your desired parser is not explicitly named, look into your tools documentation - maybe you can format
|
231
|
+
the report in a different style (and give it a custom name when calling `warnings.report`).
|
232
|
+
|
233
|
+
`any` file format means that the file is most likely read line by line, so the extension is not important.
|
234
|
+
|
235
|
+
Your parser is missing and you cannot export into another format? -> [Create an Issue](https://github.com/Kyaak/danger-warnings/issues)
|
236
|
+
|
237
|
+
|Number|Name|ID|File Format|Formatter|
|
238
|
+
|:---:|:---|:---|:---:|:----:|
|
239
|
+
|1|[Bandit](https://github.com/PyCQA/bandit)|bandit|json|json
|
240
|
+
|2|[Pylint](https://github.com/PyCQA/pylint)|pylint|any|parseable
|
241
|
+
|3|[RuboCop](https://github.com/rubocop-hq/rubocop)|rubocop|json, any|json, simple
|
data/lib/warnings/gem_version.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
require_relative 'issue'
|
1
|
+
require_relative '../report/issue'
|
2
2
|
|
3
3
|
module Warnings
|
4
|
-
# Utility class to write the markdown
|
5
|
-
module
|
6
|
-
TABLE_HEADER = 'Severity|File|Message'.freeze
|
4
|
+
# Utility class to write the markdown and inline reports.
|
5
|
+
module MessageUtil
|
6
|
+
TABLE_HEADER = '|Severity|File|Message|'.freeze
|
7
7
|
COLUMN_SEPARATOR = '|'.freeze
|
8
|
-
TABLE_SEPARATOR = "---#{COLUMN_SEPARATOR}---#{COLUMN_SEPARATOR}
|
8
|
+
TABLE_SEPARATOR = "#{COLUMN_SEPARATOR}---#{COLUMN_SEPARATOR}---#{COLUMN_SEPARATOR}---#{COLUMN_SEPARATOR}".freeze
|
9
9
|
LINE_SEPARATOR = "\n".freeze
|
10
10
|
|
11
11
|
module_function
|
@@ -15,12 +15,20 @@ module Warnings
|
|
15
15
|
# @param name [String] The name of the report to be printed.
|
16
16
|
# @param issues [Array<Issue>] List of parsed issues.
|
17
17
|
# @return [String] String in danger markdown format.
|
18
|
-
def
|
18
|
+
def markdown(name, issues)
|
19
19
|
result = header_name(name)
|
20
20
|
result << header
|
21
21
|
result << issues(issues)
|
22
22
|
end
|
23
23
|
|
24
|
+
# Create an inline comment containing all issue information.
|
25
|
+
#
|
26
|
+
# @param issue [Issue] The issue to report.
|
27
|
+
# @return String Text to add as comment.
|
28
|
+
def inline(issue)
|
29
|
+
"#{issue.severity.to_s.capitalize}\n#{meta_information(issue)}\n#{issue.message}"
|
30
|
+
end
|
31
|
+
|
24
32
|
# Create the report name string.
|
25
33
|
#
|
26
34
|
# @param report_name [String] The name of the report.
|
@@ -47,15 +55,31 @@ module Warnings
|
|
47
55
|
def issues(issues)
|
48
56
|
result = ''
|
49
57
|
issues.each do |issue|
|
58
|
+
result << COLUMN_SEPARATOR.dup
|
50
59
|
result << issue.severity.to_s.capitalize
|
51
60
|
result << COLUMN_SEPARATOR
|
52
61
|
result << "#{issue.file_name}:#{issue.line}"
|
53
62
|
result << COLUMN_SEPARATOR
|
54
|
-
result << "
|
63
|
+
result << "#{meta_information(issue)} #{issue.message}"
|
64
|
+
result << COLUMN_SEPARATOR
|
55
65
|
result << LINE_SEPARATOR
|
56
66
|
end
|
57
67
|
# rubocop:enable Metrics/AbcSize
|
58
68
|
result
|
59
69
|
end
|
70
|
+
|
71
|
+
# Combine meta information about the issue.
|
72
|
+
# Meta information are considered infos about the check itself.
|
73
|
+
# e.g. category
|
74
|
+
#
|
75
|
+
# @param issue [Issue] Issue to extract information.
|
76
|
+
# @return String combined information.
|
77
|
+
def meta_information(issue)
|
78
|
+
return unless issue.category
|
79
|
+
|
80
|
+
result = '['
|
81
|
+
result << issue.category
|
82
|
+
result << ']'
|
83
|
+
end
|
60
84
|
end
|
61
85
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Warnings
|
2
|
+
# Defines severity levels and provides helper methods.
|
3
|
+
module SeverityUtil
|
4
|
+
LOW = :low
|
5
|
+
MEDIUM = :medium
|
6
|
+
HIGH = :high
|
7
|
+
|
8
|
+
module_function
|
9
|
+
|
10
|
+
# Map a common shortened severity [R/C/W/E/F0000] to a defined severity level.
|
11
|
+
#
|
12
|
+
# @param name [String] The shortened severity without '[]'
|
13
|
+
# @return [Symbol] Mapped severity level.
|
14
|
+
def rcwef_short(name)
|
15
|
+
char = name.chars.first.downcase
|
16
|
+
case char
|
17
|
+
when 'r', 'c'
|
18
|
+
LOW
|
19
|
+
when 'w'
|
20
|
+
MEDIUM
|
21
|
+
when 'e', 'f'
|
22
|
+
HIGH
|
23
|
+
else
|
24
|
+
LOW
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Map a common full severity to a defined severity level.
|
29
|
+
#
|
30
|
+
# @param name [String] The shortened severity without '[]'
|
31
|
+
# @return [Symbol] Mapped severity level.
|
32
|
+
def rcwef_full(name)
|
33
|
+
case name.downcase
|
34
|
+
when 'refactor', 'convention'
|
35
|
+
LOW
|
36
|
+
when 'warning'
|
37
|
+
MEDIUM
|
38
|
+
when 'error', 'fatal'
|
39
|
+
HIGH
|
40
|
+
else
|
41
|
+
LOW
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -1,18 +1,13 @@
|
|
1
1
|
require_relative 'parser'
|
2
|
-
require_relative '../issue'
|
2
|
+
require_relative '../report/issue'
|
3
3
|
|
4
4
|
module Warnings
|
5
5
|
# Parser class for bandit generated json files.
|
6
6
|
class BanditParser < Parser
|
7
7
|
RESULTS_KEY = 'results'.freeze
|
8
|
-
FILE_TYPES = %i(json).freeze
|
9
8
|
NAME = 'Bandit'.freeze
|
10
9
|
ERROR_MISSING_KEY = "Missing bandit key '#{RESULTS_KEY}'.".freeze
|
11
10
|
|
12
|
-
def file_types
|
13
|
-
FILE_TYPES
|
14
|
-
end
|
15
|
-
|
16
11
|
def parse(file)
|
17
12
|
json_hash = json(file)
|
18
13
|
results_hash = json_hash[RESULTS_KEY]
|
@@ -33,8 +28,7 @@ module Warnings
|
|
33
28
|
issue.severity = to_severity(hash['issue_severity'])
|
34
29
|
issue.message = hash['issue_text']
|
35
30
|
issue.line = hash['line_number']
|
36
|
-
issue.
|
37
|
-
issue.name = hash['test_name']
|
31
|
+
issue.category = "#{hash['test_id']}-#{hash['test_name']}"
|
38
32
|
@issues << issue
|
39
33
|
end
|
40
34
|
|
@@ -5,15 +5,12 @@ module Warnings
|
|
5
5
|
# Base parser class to define common methods.
|
6
6
|
class Parser
|
7
7
|
ERROR_FILE_NOT_EXIST = 'File \'%s\' does not exist.'.freeze
|
8
|
-
|
8
|
+
ERROR_EXT_NOT_JSON = '%s is not a json file.'.freeze
|
9
|
+
EXT_JSON = 'json'.freeze
|
9
10
|
# All issues found by the parser.
|
10
11
|
#
|
11
12
|
# @return [Array<Issue>]
|
12
13
|
attr_accessor :issues
|
13
|
-
# Defines all supported file types for the parser.
|
14
|
-
#
|
15
|
-
# @return [Array<Symbol>] Array of file types.
|
16
|
-
abstract_method :file_types
|
17
14
|
# Execute the parser.
|
18
15
|
# Read the file and create an array of issues.
|
19
16
|
#
|
@@ -30,15 +27,36 @@ module Warnings
|
|
30
27
|
|
31
28
|
protected
|
32
29
|
|
30
|
+
# Check if the file is a json file.
|
31
|
+
#
|
32
|
+
# @param file_path [String] Path to a file to be read as json.
|
33
|
+
# @return [Bool] Whether the file is a json or not.
|
34
|
+
def json?(file_path)
|
35
|
+
File.extname(file_path).delete('.').downcase.eql?(EXT_JSON)
|
36
|
+
end
|
37
|
+
|
33
38
|
# Parse a file as json content.
|
34
39
|
#
|
35
40
|
# @param file_path [String] Path to a file to be read as json.
|
36
41
|
# @return [String] Hash of json values.
|
37
42
|
def json(file_path)
|
43
|
+
raise(format(ERROR_EXT_NOT_JSON, file_path)) unless json?(file_path)
|
44
|
+
|
38
45
|
content = read_file(file_path)
|
39
46
|
JSON.parse(content)
|
40
47
|
end
|
41
48
|
|
49
|
+
# Read the file into memory and serve each line.
|
50
|
+
#
|
51
|
+
# @param file_path [String] Path to a file to be read.
|
52
|
+
# @raise If file does not exist.
|
53
|
+
# @return [String] Array of line contents.
|
54
|
+
def read_lines(file_path)
|
55
|
+
raise(format(ERROR_FILE_NOT_EXIST, file_path)) unless File.exist?(file_path)
|
56
|
+
|
57
|
+
File.readlines(file_path, chomp: true)
|
58
|
+
end
|
59
|
+
|
42
60
|
private
|
43
61
|
|
44
62
|
# Evaluate and read the file into memory.
|
@@ -47,19 +65,9 @@ module Warnings
|
|
47
65
|
# @raise If file does not exist or ist empty.
|
48
66
|
# @return [String] File content.
|
49
67
|
def read_file(file_path)
|
50
|
-
check_extname(file_path)
|
51
68
|
raise(format(ERROR_FILE_NOT_EXIST, file_path)) unless File.exist?(file_path)
|
52
69
|
|
53
70
|
File.read(file_path)
|
54
71
|
end
|
55
|
-
|
56
|
-
# Evaluate the files extension name.
|
57
|
-
#
|
58
|
-
# @param file_path [String] Path to a file to be evaluated.
|
59
|
-
# @raise If file extension is not supported by the current parser.
|
60
|
-
def check_extname(file_path)
|
61
|
-
ext = File.extname(file_path).delete('.')
|
62
|
-
raise(format(ERROR_EXT_NOT_SUPPORTED, ext, self.class.name)) unless file_types.include?(ext.to_sym)
|
63
|
-
end
|
64
72
|
end
|
65
73
|
end
|
@@ -1,11 +1,15 @@
|
|
1
1
|
require_relative 'bandit_parser'
|
2
|
+
require_relative 'pylint_parser'
|
3
|
+
require_relative 'rubocop_parser'
|
2
4
|
|
3
5
|
module Warnings
|
4
6
|
# Factory class for supported parsers.
|
5
7
|
class ParserFactory
|
6
8
|
ERROR_NOT_SUPPORTED = 'Parser \'%s\' not supported.'.freeze
|
7
9
|
AVAILABLE_PARSERS = {
|
8
|
-
bandit: BanditParser
|
10
|
+
bandit: BanditParser,
|
11
|
+
pylint: PylintParser,
|
12
|
+
rubocop: RubocopParser
|
9
13
|
}.freeze
|
10
14
|
|
11
15
|
# Create a new parser implementation.
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative 'parser'
|
2
|
+
require_relative '../report/issue'
|
3
|
+
require_relative '../helper/severity_util'
|
4
|
+
|
5
|
+
module Warnings
|
6
|
+
# Parser class for pylint formatted files.
|
7
|
+
class PylintParser < Parser
|
8
|
+
NAME = 'Pylint'.freeze
|
9
|
+
ISSUE_PATTERN = /(.*):(\d+):\s*\[(\w\d+)\]\s*(.*)/.freeze
|
10
|
+
|
11
|
+
def parse(file)
|
12
|
+
read_lines(file).each do |line|
|
13
|
+
match = line.scan(ISSUE_PATTERN)
|
14
|
+
store_issue(match[0]) unless match.empty?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def name
|
19
|
+
NAME
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Match the regex result and store it as issue implementation.
|
25
|
+
#
|
26
|
+
# @param match [Array<String>] The regex matches for a single issue.
|
27
|
+
# @return Void
|
28
|
+
def store_issue(match)
|
29
|
+
issue = Issue.new
|
30
|
+
issue.file_name = match[0]
|
31
|
+
issue.line = match[1]
|
32
|
+
issue.category = match[2]
|
33
|
+
issue.severity = SeverityUtil.rcwef_short(issue.category)
|
34
|
+
issue.message = match[3]
|
35
|
+
@issues << issue
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require_relative 'parser'
|
2
|
+
require_relative '../report/issue'
|
3
|
+
require_relative '../helper/severity_util'
|
4
|
+
|
5
|
+
module Warnings
|
6
|
+
# Parser class for rubocop reports.
|
7
|
+
class RubocopParser < Parser
|
8
|
+
NAME = 'RuboCop'.freeze
|
9
|
+
FILE_PATTERN = /==\s(.*)\s==/.freeze
|
10
|
+
ISSUE_PATTERN = /(\w):\s*(\d+):\s*\d+:\s(.*)/.freeze
|
11
|
+
|
12
|
+
def parse(file)
|
13
|
+
if json?(file)
|
14
|
+
extract_json_issues(file)
|
15
|
+
else
|
16
|
+
extract_pattern_issues(file)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def name
|
21
|
+
NAME
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def extract_json_issues(file)
|
27
|
+
json_hash = json(file)
|
28
|
+
files = json_hash['files']
|
29
|
+
files.each(&method(:store_json_issue))
|
30
|
+
end
|
31
|
+
|
32
|
+
def extract_pattern_issues(file)
|
33
|
+
last_file = nil
|
34
|
+
read_lines(file).each do |line|
|
35
|
+
file_match = line.scan(FILE_PATTERN)
|
36
|
+
unless file_match.empty?
|
37
|
+
last_file = file_match[0][0]
|
38
|
+
next
|
39
|
+
end
|
40
|
+
issue_match = line.scan(ISSUE_PATTERN)
|
41
|
+
next if issue_match.empty?
|
42
|
+
|
43
|
+
issue_content = issue_match[0]
|
44
|
+
issue = {
|
45
|
+
severity: issue_content[0],
|
46
|
+
line: issue_content[1],
|
47
|
+
message: issue_content[2]
|
48
|
+
}
|
49
|
+
store_simple_issue(last_file, issue)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def store_json_issue(file_hash)
|
54
|
+
offenses = file_hash['offenses']
|
55
|
+
return if offenses.empty?
|
56
|
+
|
57
|
+
offenses.each do |offense|
|
58
|
+
issue = Issue.new
|
59
|
+
issue.file_name = file_hash['path']
|
60
|
+
issue.line = offense['location']['line']
|
61
|
+
issue.category = offense['cop_name']
|
62
|
+
issue.severity = SeverityUtil.rcwef_full(offense['severity'])
|
63
|
+
issue.message = offense['message']
|
64
|
+
@issues << issue
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def store_simple_issue(file, issue_hash)
|
69
|
+
issue = Issue.new
|
70
|
+
issue.file_name = file
|
71
|
+
issue.line = issue_hash[:line].to_i
|
72
|
+
issue.severity = SeverityUtil.rcwef_full(issue_hash[:severity])
|
73
|
+
issue.message = issue_hash[:message]
|
74
|
+
@issues << issue
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|