goodcheck 2.7.0 → 3.0.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 +21 -0
- data/LICENSE +1 -1
- data/README.md +7 -1
- data/lib/goodcheck.rb +5 -0
- data/lib/goodcheck/cli.rb +4 -6
- data/lib/goodcheck/commands/check.rb +1 -3
- data/lib/goodcheck/commands/config_loading.rb +9 -4
- data/lib/goodcheck/commands/test.rb +33 -28
- data/lib/goodcheck/config.rb +68 -1
- data/lib/goodcheck/config_loader.rb +28 -15
- data/lib/goodcheck/exit_status.rb +2 -0
- data/lib/goodcheck/import_loader.rb +39 -11
- data/lib/goodcheck/reporters/json.rb +2 -1
- data/lib/goodcheck/reporters/text.rb +3 -1
- data/lib/goodcheck/rule.rb +3 -1
- data/lib/goodcheck/unarchiver.rb +40 -0
- data/lib/goodcheck/version.rb +1 -1
- metadata +31 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa64d7f536fc7b357fbae7aa685827211236015ff9203bc7291847705e4a8189
|
4
|
+
data.tar.gz: bf23ca5741b3e424d064d45b9e16ccc28fbf53d299d8a4966ce7a5566c277a0f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d184c5e83feb1b656afdbb2aa71c724b33c584a674de2def35201bbd8a66275b5c2a45b466c477aee38689f53dc6e39a0ee4183a6ad61b3ba587485eeb7f3d89
|
7
|
+
data.tar.gz: 215b0532befa04bdbbdbb0ac24be805fde46ebe51bafda8d6193df1c3a183fbf0faad0495454b9dcfe6dec27015f817b5b52f99b4fa26936f57ac491fa923bf5
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,27 @@
|
|
2
2
|
|
3
3
|
## HEAD
|
4
4
|
|
5
|
+
## 3.0.0 (2021-06-14)
|
6
|
+
|
7
|
+
Breaking changes:
|
8
|
+
|
9
|
+
* Drop support of Ruby 2.4 [#169](https://github.com/sider/goodcheck/pull/169)
|
10
|
+
* `goodcheck test` exit with `3` on tests failed [#168](https://github.com/sider/goodcheck/pull/168)
|
11
|
+
(a potentially breaking change; the command previously exited with `1`)
|
12
|
+
|
13
|
+
Features:
|
14
|
+
|
15
|
+
* Import `.tar.gz` files [#190](https://github.com/sider/goodcheck/pull/190)
|
16
|
+
* Introduce `exclude_binary` option [#195](https://github.com/sider/goodcheck/pull/195)
|
17
|
+
* Add rule severity [#172](https://github.com/sider/goodcheck/pull/172)
|
18
|
+
|
19
|
+
Bugfixes and others:
|
20
|
+
|
21
|
+
* Update gemspec [#176](https://github.com/sider/goodcheck/pull/176)
|
22
|
+
* Update `psych` requirement allowing 4.0 [#183](https://github.com/sider/goodcheck/pull/183)
|
23
|
+
* Improve output error message on `RegexpError` [#188](https://github.com/sider/goodcheck/pull/188)
|
24
|
+
* Fix importing error with extended glob [#194](https://github.com/sider/goodcheck/pull/194)
|
25
|
+
|
5
26
|
## 2.7.0 (2020-12-02)
|
6
27
|
|
7
28
|
* Goodbye ActiveSupport [#155](https://github.com/sider/goodcheck/pull/155)
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -21,7 +21,13 @@ Goodcheck is provided as a Ruby gem. To install it, run:
|
|
21
21
|
$ gem install goodcheck
|
22
22
|
```
|
23
23
|
|
24
|
-
|
24
|
+
If you do not want to install it, you can run it via Docker instead:
|
25
|
+
|
26
|
+
```console
|
27
|
+
$ docker run -t --rm -v "$(pwd):/work" sider/goodcheck
|
28
|
+
```
|
29
|
+
|
30
|
+
Check out the [documentation](docusaurus/docs/getstarted.md) or [website](https://sider.github.io/goodcheck/) for more details.
|
25
31
|
|
26
32
|
## Development
|
27
33
|
|
data/lib/goodcheck.rb
CHANGED
data/lib/goodcheck/cli.rb
CHANGED
@@ -21,8 +21,6 @@ module Goodcheck
|
|
21
21
|
help: "Show help and quit"
|
22
22
|
}.freeze
|
23
23
|
|
24
|
-
DEFAULT_CONFIG_FILE = Pathname("goodcheck.yml").freeze
|
25
|
-
|
26
24
|
def run(args)
|
27
25
|
command = args.shift&.to_sym
|
28
26
|
|
@@ -58,7 +56,7 @@ module Goodcheck
|
|
58
56
|
end
|
59
57
|
|
60
58
|
def check(args)
|
61
|
-
config_path = DEFAULT_CONFIG_FILE
|
59
|
+
config_path = Pathname(DEFAULT_CONFIG_FILE)
|
62
60
|
targets = []
|
63
61
|
rules = []
|
64
62
|
formats = [:text, :json]
|
@@ -110,7 +108,7 @@ module Goodcheck
|
|
110
108
|
end
|
111
109
|
|
112
110
|
def test(args)
|
113
|
-
config_path = DEFAULT_CONFIG_FILE
|
111
|
+
config_path = Pathname(DEFAULT_CONFIG_FILE)
|
114
112
|
loglevel = ::Logger::ERROR
|
115
113
|
force_download = false
|
116
114
|
|
@@ -132,7 +130,7 @@ module Goodcheck
|
|
132
130
|
end
|
133
131
|
|
134
132
|
def init(args)
|
135
|
-
config_path = DEFAULT_CONFIG_FILE
|
133
|
+
config_path = Pathname(DEFAULT_CONFIG_FILE)
|
136
134
|
force = false
|
137
135
|
|
138
136
|
OptionParser.new("Usage: goodcheck init [options]") do |opts|
|
@@ -163,7 +161,7 @@ module Goodcheck
|
|
163
161
|
end
|
164
162
|
|
165
163
|
def pattern(args)
|
166
|
-
config_path = DEFAULT_CONFIG_FILE
|
164
|
+
config_path = Pathname(DEFAULT_CONFIG_FILE)
|
167
165
|
|
168
166
|
OptionParser.new do |opts|
|
169
167
|
opts.banner = "Usage: goodcheck pattern [options] ids..."
|
@@ -15,8 +15,6 @@ module Goodcheck
|
|
15
15
|
include HomePath
|
16
16
|
include ExitStatus
|
17
17
|
|
18
|
-
EXIT_MATCH = 2
|
19
|
-
|
20
18
|
def initialize(config_path:, rules:, targets:, reporter:, stderr:, home_path:, force_download:)
|
21
19
|
@config_path = config_path
|
22
20
|
@rules = rules
|
@@ -129,7 +127,7 @@ module Goodcheck
|
|
129
127
|
end
|
130
128
|
|
131
129
|
def excluded?(path)
|
132
|
-
config.
|
130
|
+
config.exclude_path?(path)
|
133
131
|
end
|
134
132
|
end
|
135
133
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Goodcheck
|
2
2
|
module Commands
|
3
3
|
module ConfigLoading
|
4
|
+
include ExitStatus
|
5
|
+
|
4
6
|
class ConfigFileNotFound < Error
|
5
7
|
attr_reader :path
|
6
8
|
|
@@ -31,20 +33,23 @@ module Goodcheck
|
|
31
33
|
yield
|
32
34
|
rescue ConfigFileNotFound => exn
|
33
35
|
stderr.puts "Configuration file not found: #{exn.path}"
|
34
|
-
|
36
|
+
EXIT_ERROR
|
37
|
+
rescue ConfigLoader::InvalidPattern => exn
|
38
|
+
stderr.puts exn.message
|
39
|
+
EXIT_ERROR
|
35
40
|
rescue Psych::Exception => exn
|
36
41
|
stderr.puts "Unexpected error happens while loading YAML file: #{exn.inspect}"
|
37
42
|
exn.backtrace.each do |trace_loc|
|
38
43
|
stderr.puts " #{trace_loc}"
|
39
44
|
end
|
40
|
-
|
45
|
+
EXIT_ERROR
|
41
46
|
rescue StrongJSON::Type::TypeError, StrongJSON::Type::UnexpectedAttributeError => exn
|
42
47
|
stderr.puts "Invalid config: #{exn.message}"
|
43
48
|
stderr.puts StrongJSON::ErrorReporter.new(path: exn.path).to_s
|
44
|
-
|
49
|
+
EXIT_ERROR
|
45
50
|
rescue Errno::ENOENT => exn
|
46
51
|
stderr.puts "#{exn}"
|
47
|
-
|
52
|
+
EXIT_ERROR
|
48
53
|
end
|
49
54
|
end
|
50
55
|
end
|
@@ -28,15 +28,15 @@ module Goodcheck
|
|
28
28
|
return EXIT_SUCCESS
|
29
29
|
end
|
30
30
|
|
31
|
-
validate_rule_uniqueness or return
|
32
|
-
validate_rules or return
|
31
|
+
validate_rule_uniqueness or return EXIT_TEST_FAILED
|
32
|
+
validate_rules or return EXIT_TEST_FAILED
|
33
33
|
|
34
34
|
EXIT_SUCCESS
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
38
|
def validate_rule_uniqueness
|
39
|
-
stdout.puts "Validating rule
|
39
|
+
stdout.puts "Validating rule ID uniqueness..."
|
40
40
|
|
41
41
|
duplicated_ids = []
|
42
42
|
|
@@ -47,31 +47,29 @@ module Goodcheck
|
|
47
47
|
end
|
48
48
|
|
49
49
|
if duplicated_ids.empty?
|
50
|
-
stdout.puts " OK
|
50
|
+
stdout.puts Rainbow(" OK! 👍").green
|
51
51
|
true
|
52
52
|
else
|
53
53
|
count = duplicated_ids.size
|
54
54
|
duplication = count == 1 ? 'duplication' : 'duplications'
|
55
|
-
stdout.puts
|
55
|
+
stdout.puts " Found #{Rainbow(count).bold} #{duplication}. 😱"
|
56
56
|
duplicated_ids.each do |id|
|
57
|
-
stdout.puts " #{id}"
|
57
|
+
stdout.puts " - #{Rainbow(id).background(:red)}"
|
58
58
|
end
|
59
59
|
false
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
63
|
def validate_rules
|
64
|
-
test_pass = true
|
65
64
|
success_count = 0
|
66
|
-
failure_count = 0
|
67
65
|
failed_rule_ids = Set[]
|
68
66
|
|
69
67
|
config.rules.each do |rule|
|
70
|
-
|
71
|
-
stdout.puts "Testing rule #{Rainbow(rule.id).cyan}..."
|
68
|
+
stdout.puts "Testing rule #{Rainbow(rule.id).cyan}..."
|
72
69
|
|
73
|
-
|
70
|
+
rule_ok = true
|
74
71
|
|
72
|
+
if rule.triggers.any? {|trigger| !trigger.passes.empty? || !trigger.fails.empty?}
|
75
73
|
rule.triggers.each.with_index do |trigger, index|
|
76
74
|
if !trigger.passes.empty? || !trigger.fails.empty?
|
77
75
|
if trigger.by_pattern?
|
@@ -89,21 +87,19 @@ module Goodcheck
|
|
89
87
|
end
|
90
88
|
|
91
89
|
unless pass_errors.empty?
|
92
|
-
test_pass = false
|
93
90
|
rule_ok = false
|
94
91
|
|
95
92
|
pass_errors.each do |_, index|
|
96
|
-
stdout.puts " #{index + 1}. #{Rainbow('pass').green} example matched
|
93
|
+
stdout.puts " #{index + 1}. #{Rainbow('pass').green} example matched. 😱"
|
97
94
|
failed_rule_ids << rule.id
|
98
95
|
end
|
99
96
|
end
|
100
97
|
|
101
98
|
unless fail_errors.empty?
|
102
|
-
test_pass = false
|
103
99
|
rule_ok = false
|
104
100
|
|
105
101
|
fail_errors.each do |_, index|
|
106
|
-
stdout.puts " #{index + 1}. #{Rainbow('fail').red} example didn
|
102
|
+
stdout.puts " #{index + 1}. #{Rainbow('fail').red} example didn’t match. 😱"
|
107
103
|
failed_rule_ids << rule.id
|
108
104
|
end
|
109
105
|
end
|
@@ -111,16 +107,27 @@ module Goodcheck
|
|
111
107
|
end
|
112
108
|
|
113
109
|
if rule.triggers.any?(&:skips_fail_examples?)
|
114
|
-
stdout.puts "
|
110
|
+
stdout.puts " The rule contains a `pattern` with `glob`, which is not supported by the test command. 🚨"
|
115
111
|
stdout.puts " Skips testing `fail` examples."
|
116
112
|
end
|
113
|
+
end
|
117
114
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
115
|
+
if rule.severity && !config.severity_allowed?(rule.severity)
|
116
|
+
allowed_severities = config.allowed_severities.map { |s| %("#{s}") }.join(', ')
|
117
|
+
stdout.puts Rainbow(" \"#{rule.severity}\" severity isn’t allowed. Must be one of #{allowed_severities}. 😱").red
|
118
|
+
rule_ok = false
|
119
|
+
failed_rule_ids << rule.id
|
120
|
+
end
|
121
|
+
|
122
|
+
if !rule.severity && config.severity_required?
|
123
|
+
stdout.puts Rainbow(" Severity is required. 😱").red
|
124
|
+
rule_ok = false
|
125
|
+
failed_rule_ids << rule.id
|
126
|
+
end
|
127
|
+
|
128
|
+
if rule_ok
|
129
|
+
stdout.puts Rainbow(" OK! 👍").green
|
130
|
+
success_count += 1
|
124
131
|
end
|
125
132
|
end
|
126
133
|
|
@@ -132,14 +139,12 @@ module Goodcheck
|
|
132
139
|
end
|
133
140
|
end
|
134
141
|
|
135
|
-
|
136
|
-
rule = rule_count == 1 ? "1 rule" : "#{rule_count} rules"
|
137
|
-
success = Rainbow(success_count == 1 ? "1 success" : "#{success_count} successes").green
|
138
|
-
failure = Rainbow(failure_count == 1 ? "1 failure" : "#{failure_count} failures").red
|
142
|
+
total = success_count + failed_rule_ids.size
|
139
143
|
stdout.puts ""
|
140
|
-
stdout.puts "
|
144
|
+
stdout.puts "#{Rainbow(total).bold} #{total == 1 ? 'rule' : 'rules'} tested: " \
|
145
|
+
"#{Rainbow(success_count.to_s + ' successful').green.bold}, #{Rainbow(failed_rule_ids.size.to_s + ' failed').red.bold}"
|
141
146
|
|
142
|
-
|
147
|
+
failed_rule_ids.empty?
|
143
148
|
end
|
144
149
|
|
145
150
|
def rule_matches_example?(rule, trigger, example)
|
data/lib/goodcheck/config.rb
CHANGED
@@ -1,11 +1,43 @@
|
|
1
1
|
module Goodcheck
|
2
2
|
class Config
|
3
|
+
DEFAULT_EXCLUDE_BINARY = false
|
4
|
+
|
5
|
+
# https://www.iana.org/assignments/media-types/media-types.xhtml
|
6
|
+
BINARY_MIME_TYPES = %w[
|
7
|
+
audio
|
8
|
+
font
|
9
|
+
image
|
10
|
+
model
|
11
|
+
multipart
|
12
|
+
video
|
13
|
+
].to_set.freeze
|
14
|
+
BINARY_MIME_FULLTYPES = %w[
|
15
|
+
application/gzip
|
16
|
+
application/illustrator
|
17
|
+
application/pdf
|
18
|
+
application/zip
|
19
|
+
].to_set.freeze
|
20
|
+
|
3
21
|
attr_reader :rules
|
4
22
|
attr_reader :exclude_paths
|
23
|
+
attr_reader :exclude_binary
|
24
|
+
alias exclude_binary? exclude_binary
|
25
|
+
attr_reader :allowed_severities
|
26
|
+
attr_reader :severity_required
|
27
|
+
alias severity_required? severity_required
|
5
28
|
|
6
|
-
def initialize(rules:, exclude_paths:)
|
29
|
+
def initialize(rules:, exclude_paths:, exclude_binary: DEFAULT_EXCLUDE_BINARY, severity: nil)
|
7
30
|
@rules = rules
|
8
31
|
@exclude_paths = exclude_paths
|
32
|
+
@exclude_binary = exclude_binary || DEFAULT_EXCLUDE_BINARY
|
33
|
+
severity ||= {}
|
34
|
+
@allowed_severities = Set.new(severity.fetch(:allow, []))
|
35
|
+
@severity_required = severity.fetch(:required, false)
|
36
|
+
end
|
37
|
+
|
38
|
+
def severity_allowed?(severity)
|
39
|
+
return true if allowed_severities.empty?
|
40
|
+
allowed_severities.include?(severity)
|
9
41
|
end
|
10
42
|
|
11
43
|
def each_rule(filter:, &block)
|
@@ -44,5 +76,40 @@ module Goodcheck
|
|
44
76
|
enum_for(:rules_for_path, path, rules_filter: rules_filter)
|
45
77
|
end
|
46
78
|
end
|
79
|
+
|
80
|
+
def exclude_path?(path)
|
81
|
+
excluded = exclude_paths.any? do |pattern|
|
82
|
+
path.fnmatch?(pattern, File::FNM_PATHNAME | File::FNM_EXTGLOB)
|
83
|
+
end
|
84
|
+
|
85
|
+
return true if excluded
|
86
|
+
return excluded unless exclude_binary?
|
87
|
+
return excluded unless path.file?
|
88
|
+
|
89
|
+
exclude_file_by_mime_type?(path)
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def exclude_file_by_mime_type?(file)
|
95
|
+
# NOTE: Lazy load to save memory
|
96
|
+
require "marcel"
|
97
|
+
|
98
|
+
fulltype = Marcel::MimeType.for(file)
|
99
|
+
type, subtype = fulltype.split("/")
|
100
|
+
|
101
|
+
case
|
102
|
+
when subtype.end_with?("+xml") # e.g. "image/svg+xml"
|
103
|
+
false
|
104
|
+
when BINARY_MIME_TYPES.include?(type)
|
105
|
+
Goodcheck.logger.debug "Exclude file: #{file} (#{fulltype})"
|
106
|
+
true
|
107
|
+
when BINARY_MIME_FULLTYPES.include?(fulltype)
|
108
|
+
Goodcheck.logger.debug "Exclude file: #{file} (#{fulltype})"
|
109
|
+
true
|
110
|
+
else
|
111
|
+
false
|
112
|
+
end
|
113
|
+
end
|
47
114
|
end
|
48
115
|
end
|
@@ -103,7 +103,8 @@ module Goodcheck
|
|
103
103
|
justification: optional(array_or(string)),
|
104
104
|
glob: optional(glob),
|
105
105
|
pass: optional(array_or(string)),
|
106
|
-
fail: optional(array_or(string))
|
106
|
+
fail: optional(array_or(string)),
|
107
|
+
severity: optional(string)
|
107
108
|
)
|
108
109
|
|
109
110
|
let :negative_rule, object(
|
@@ -113,14 +114,16 @@ module Goodcheck
|
|
113
114
|
justification: optional(array_or(string)),
|
114
115
|
glob: optional(glob),
|
115
116
|
pass: optional(array_or(string)),
|
116
|
-
fail: optional(array_or(string))
|
117
|
+
fail: optional(array_or(string)),
|
118
|
+
severity: optional(string)
|
117
119
|
)
|
118
120
|
|
119
121
|
let :nopattern_rule, object(
|
120
122
|
id: string,
|
121
123
|
message: string,
|
122
124
|
justification: optional(array_or(string)),
|
123
|
-
glob: glob
|
125
|
+
glob: glob,
|
126
|
+
severity: optional(string)
|
124
127
|
)
|
125
128
|
|
126
129
|
let :positive_trigger, object(
|
@@ -163,7 +166,8 @@ module Goodcheck
|
|
163
166
|
id: string,
|
164
167
|
message: string,
|
165
168
|
justification: optional(array_or(string)),
|
166
|
-
trigger: array_or(trigger)
|
169
|
+
trigger: array_or(trigger),
|
170
|
+
severity: optional(string)
|
167
171
|
)
|
168
172
|
|
169
173
|
let :rule, enum(positive_rule,
|
@@ -187,14 +191,17 @@ module Goodcheck
|
|
187
191
|
|
188
192
|
let :rules, array(rule)
|
189
193
|
|
190
|
-
let :
|
191
|
-
|
192
|
-
|
194
|
+
let :severity, object(
|
195
|
+
allow: optional(array(string)),
|
196
|
+
required: boolean?
|
197
|
+
)
|
193
198
|
|
194
199
|
let :config, object(
|
195
200
|
rules: optional(rules),
|
196
|
-
import: optional(
|
197
|
-
exclude: optional(
|
201
|
+
import: optional(array(string)),
|
202
|
+
exclude: optional(array_or(string)),
|
203
|
+
exclude_binary: boolean?,
|
204
|
+
severity: optional(severity)
|
198
205
|
)
|
199
206
|
end
|
200
207
|
|
@@ -224,22 +231,27 @@ module Goodcheck
|
|
224
231
|
load_import rules, import
|
225
232
|
end
|
226
233
|
|
227
|
-
|
228
|
-
|
229
|
-
|
234
|
+
Config.new(
|
235
|
+
rules: rules,
|
236
|
+
exclude_paths: Array(content[:exclude]),
|
237
|
+
exclude_binary: content[:exclude_binary],
|
238
|
+
severity: content[:severity]
|
239
|
+
)
|
230
240
|
end
|
231
241
|
|
232
242
|
def load_rules(rules, array)
|
233
243
|
array.each do |hash|
|
234
244
|
rules << load_rule(hash)
|
245
|
+
rescue RegexpError => exn
|
246
|
+
raise InvalidPattern, "Invalid pattern of the `#{hash.fetch(:id)}` rule in `#{path}`: #{exn.message}"
|
235
247
|
end
|
236
248
|
end
|
237
249
|
|
238
250
|
def load_import(rules, import)
|
239
251
|
Goodcheck.logger.info "Importing rules from #{import}"
|
240
252
|
|
241
|
-
import_loader.load(import) do |content|
|
242
|
-
json = JSON.parse(JSON.dump(YAML.
|
253
|
+
import_loader.load(import) do |content, filename|
|
254
|
+
json = JSON.parse(JSON.dump(YAML.safe_load(content, filename: filename)), symbolize_names: true)
|
243
255
|
|
244
256
|
Schema.rules.coerce json
|
245
257
|
load_rules(rules, json)
|
@@ -253,8 +265,9 @@ module Goodcheck
|
|
253
265
|
triggers = retrieve_triggers(hash)
|
254
266
|
justifications = array(hash[:justification])
|
255
267
|
message = hash[:message].chomp
|
268
|
+
severity = hash[:severity]
|
256
269
|
|
257
|
-
Rule.new(id: id, message: message, justifications: justifications, triggers: triggers)
|
270
|
+
Rule.new(id: id, message: message, justifications: justifications, triggers: triggers, severity: severity)
|
258
271
|
end
|
259
272
|
|
260
273
|
def retrieve_triggers(hash)
|
@@ -31,9 +31,13 @@ module Goodcheck
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def load(name, &block)
|
34
|
-
uri =
|
34
|
+
uri = begin
|
35
|
+
URI.parse(name)
|
36
|
+
rescue URI::InvalidURIError
|
37
|
+
nil
|
38
|
+
end
|
35
39
|
|
36
|
-
case uri
|
40
|
+
case uri&.scheme
|
37
41
|
when nil
|
38
42
|
load_file name, &block
|
39
43
|
when "file"
|
@@ -45,14 +49,20 @@ module Goodcheck
|
|
45
49
|
end
|
46
50
|
end
|
47
51
|
|
48
|
-
def load_file(path)
|
49
|
-
files =
|
52
|
+
def load_file(path, &block)
|
53
|
+
files = Pathname.glob(File.join(config_path.parent.to_path, path), File::FNM_DOTMATCH | File::FNM_EXTGLOB).sort
|
50
54
|
if files.empty?
|
51
55
|
raise FileNotFound.new(path)
|
52
56
|
else
|
53
57
|
files.each do |file|
|
54
58
|
Goodcheck.logger.info "Reading file: #{file}"
|
55
|
-
|
59
|
+
if unarchiver.tar_gz?(file)
|
60
|
+
unarchiver.tar_gz(file.read) do |content, filename|
|
61
|
+
block.call(content, filename)
|
62
|
+
end
|
63
|
+
else
|
64
|
+
block.call(file.read, file.to_path)
|
65
|
+
end
|
56
66
|
end
|
57
67
|
end
|
58
68
|
end
|
@@ -61,7 +71,7 @@ module Goodcheck
|
|
61
71
|
Digest::SHA2.hexdigest(uri.to_s)
|
62
72
|
end
|
63
73
|
|
64
|
-
def load_http(uri)
|
74
|
+
def load_http(uri, &block)
|
65
75
|
hash = cache_name(uri)
|
66
76
|
path = cache_path + hash
|
67
77
|
|
@@ -87,13 +97,19 @@ module Goodcheck
|
|
87
97
|
if download
|
88
98
|
path.rmtree if path.exist?
|
89
99
|
Goodcheck.logger.info "Downloading content..."
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
100
|
+
if unarchiver.tar_gz?(uri.path)
|
101
|
+
unarchiver.tar_gz(http_get(uri)) do |content, filename|
|
102
|
+
block.call(content, filename)
|
103
|
+
write_cache "#{uri}/#{filename}", content
|
104
|
+
end
|
105
|
+
else
|
106
|
+
content = http_get(uri)
|
107
|
+
block.call(content, uri.path)
|
108
|
+
write_cache uri, content
|
109
|
+
end
|
94
110
|
else
|
95
111
|
Goodcheck.logger.info "Reading content from cache..."
|
96
|
-
|
112
|
+
block.call(path.read, path.to_path)
|
97
113
|
end
|
98
114
|
end
|
99
115
|
|
@@ -117,5 +133,17 @@ module Goodcheck
|
|
117
133
|
raise "Error: HTTP GET #{uri.inspect} #{res.inspect}"
|
118
134
|
end
|
119
135
|
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
def unarchiver
|
140
|
+
@unarchiver ||=
|
141
|
+
begin
|
142
|
+
filter = ->(filename) {
|
143
|
+
%w[.yml .yaml].include?(File.extname(filename).downcase) && File.basename(filename) != DEFAULT_CONFIG_FILE
|
144
|
+
}
|
145
|
+
Unarchiver.new(file_filter: filter)
|
146
|
+
end
|
147
|
+
end
|
120
148
|
end
|
121
149
|
end
|
@@ -26,7 +26,8 @@ module Goodcheck
|
|
26
26
|
end_column: location.end_column
|
27
27
|
},
|
28
28
|
message: issue.rule.message,
|
29
|
-
justifications: issue.rule.justifications
|
29
|
+
justifications: issue.rule.justifications,
|
30
|
+
severity: issue.rule.severity
|
30
31
|
}
|
31
32
|
end
|
32
33
|
stdout.puts ::JSON.dump(json)
|
@@ -37,7 +37,9 @@ module Goodcheck
|
|
37
37
|
else
|
38
38
|
line.bytesize - start_column
|
39
39
|
end
|
40
|
-
|
40
|
+
rule = Rainbow("(#{issue.rule.id})").darkgray
|
41
|
+
severity = issue.rule.severity ? Rainbow("[#{issue.rule.severity}]").magenta : ""
|
42
|
+
stdout.puts "#{Rainbow(issue.path).cyan}:#{start_line}:#{start_column}: #{message} #{rule} #{severity}".strip
|
41
43
|
stdout.puts line.chomp
|
42
44
|
stdout.puts (" " * start_column_index) + Rainbow("^" + "~" * (column_size - 1)).yellow
|
43
45
|
else
|
data/lib/goodcheck/rule.rb
CHANGED
@@ -4,12 +4,14 @@ module Goodcheck
|
|
4
4
|
attr_reader :triggers
|
5
5
|
attr_reader :message
|
6
6
|
attr_reader :justifications
|
7
|
+
attr_reader :severity
|
7
8
|
|
8
|
-
def initialize(id:, triggers:, message:, justifications:)
|
9
|
+
def initialize(id:, triggers:, message:, justifications:, severity: nil)
|
9
10
|
@id = id
|
10
11
|
@triggers = triggers
|
11
12
|
@message = message
|
12
13
|
@justifications = justifications
|
14
|
+
@severity = severity
|
13
15
|
end
|
14
16
|
end
|
15
17
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Goodcheck
|
2
|
+
class Unarchiver
|
3
|
+
attr_reader :file_filter
|
4
|
+
|
5
|
+
def initialize(file_filter: ->(_filename) { true })
|
6
|
+
@file_filter = file_filter
|
7
|
+
end
|
8
|
+
|
9
|
+
def tar_gz?(filename)
|
10
|
+
name = filename.to_s.downcase
|
11
|
+
ext = ".tar.gz"
|
12
|
+
name.end_with?(ext) && name != ext
|
13
|
+
end
|
14
|
+
|
15
|
+
def tar_gz(content)
|
16
|
+
require "rubygems/package"
|
17
|
+
|
18
|
+
Gem::Package::TarReader.new(StringIO.new(gz(content))) do |tar_reader|
|
19
|
+
tar_reader.each do |file|
|
20
|
+
if file.file? && file_filter.call(file.full_name)
|
21
|
+
yield file.read, file.full_name
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def gz(content)
|
30
|
+
require "zlib"
|
31
|
+
|
32
|
+
io = Zlib::GzipReader.new(StringIO.new(content))
|
33
|
+
begin
|
34
|
+
io.read
|
35
|
+
ensure
|
36
|
+
io.close
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/goodcheck/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: goodcheck
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Sider Corporation
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-06-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -53,33 +53,39 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '5.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: simplecov
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '0.18'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '0.18'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: marcel
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '0
|
76
|
-
|
75
|
+
version: '1.0'
|
76
|
+
- - "<"
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '2.0'
|
79
|
+
type: :runtime
|
77
80
|
prerelease: false
|
78
81
|
version_requirements: !ruby/object:Gem::Requirement
|
79
82
|
requirements:
|
80
83
|
- - ">="
|
81
84
|
- !ruby/object:Gem::Version
|
82
|
-
version: '0
|
85
|
+
version: '1.0'
|
86
|
+
- - "<"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '2.0'
|
83
89
|
- !ruby/object:Gem::Dependency
|
84
90
|
name: strong_json
|
85
91
|
requirement: !ruby/object:Gem::Requirement
|
@@ -129,7 +135,7 @@ dependencies:
|
|
129
135
|
version: '3.1'
|
130
136
|
- - "<"
|
131
137
|
- !ruby/object:Gem::Version
|
132
|
-
version: '
|
138
|
+
version: '5.0'
|
133
139
|
type: :runtime
|
134
140
|
prerelease: false
|
135
141
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -139,10 +145,11 @@ dependencies:
|
|
139
145
|
version: '3.1'
|
140
146
|
- - "<"
|
141
147
|
- !ruby/object:Gem::Version
|
142
|
-
version: '
|
143
|
-
description:
|
148
|
+
version: '5.0'
|
149
|
+
description: Goodcheck is a regexp based linter that allows you to define custom rules
|
150
|
+
in a YAML file.
|
144
151
|
email:
|
145
|
-
-
|
152
|
+
- support@siderlabs.com
|
146
153
|
executables:
|
147
154
|
- goodcheck
|
148
155
|
extensions: []
|
@@ -177,11 +184,16 @@ files:
|
|
177
184
|
- lib/goodcheck/reporters/text.rb
|
178
185
|
- lib/goodcheck/rule.rb
|
179
186
|
- lib/goodcheck/trigger.rb
|
187
|
+
- lib/goodcheck/unarchiver.rb
|
180
188
|
- lib/goodcheck/version.rb
|
181
|
-
homepage: https://github.
|
189
|
+
homepage: https://sider.github.io/goodcheck/
|
182
190
|
licenses:
|
183
191
|
- MIT
|
184
|
-
metadata:
|
192
|
+
metadata:
|
193
|
+
homepage_uri: https://sider.github.io/goodcheck/
|
194
|
+
source_code_uri: https://github.com/sider/goodcheck
|
195
|
+
changelog_uri: https://github.com/sider/goodcheck/blob/master/CHANGELOG.md
|
196
|
+
bug_tracker_uri: https://github.com/sider/goodcheck/issues
|
185
197
|
post_install_message:
|
186
198
|
rdoc_options: []
|
187
199
|
require_paths:
|
@@ -190,15 +202,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
190
202
|
requirements:
|
191
203
|
- - ">="
|
192
204
|
- !ruby/object:Gem::Version
|
193
|
-
version: 2.
|
205
|
+
version: 2.5.0
|
194
206
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
195
207
|
requirements:
|
196
208
|
- - ">="
|
197
209
|
- !ruby/object:Gem::Version
|
198
210
|
version: '0'
|
199
211
|
requirements: []
|
200
|
-
rubygems_version: 3.
|
212
|
+
rubygems_version: 3.2.20
|
201
213
|
signing_key:
|
202
214
|
specification_version: 4
|
203
|
-
summary: Regexp based customizable linter
|
215
|
+
summary: Regexp based customizable linter.
|
204
216
|
test_files: []
|