goodcheck 2.7.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8c2de636a6040f67abd7f2a24da8cd3519d76490724c0897ec4da3e8594e9a88
4
- data.tar.gz: 690af43e3de713f7185fe3d080d8d99b647423e389b941981d18c2a84e049169
3
+ metadata.gz: fa64d7f536fc7b357fbae7aa685827211236015ff9203bc7291847705e4a8189
4
+ data.tar.gz: bf23ca5741b3e424d064d45b9e16ccc28fbf53d299d8a4966ce7a5566c277a0f
5
5
  SHA512:
6
- metadata.gz: 8e6d05a4a287d13d41fcb42af646d259aa50b1cb0c8b0c39d14aa09ce96fa97839331dbf192f1048737f56b5a253ef590187add746f82251c3acb8e45ceaaa7a
7
- data.tar.gz: aeec81a86feffac3321ad616c10f5f319a37dd6efce177a446e0a316c35db1fdc8de405d5c2afd5a7e450a9faafb359578ea92a1283411bfc988f78722c35eb5
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
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2019 Sleeek Corporation
3
+ Copyright (c) 2019 Sider Corporation
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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
- Check out the [documentation](docusaurus/docs/getstarted.md) for more details.
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
@@ -33,3 +33,8 @@ require "goodcheck/commands/init"
33
33
  require "goodcheck/commands/test"
34
34
  require "goodcheck/import_loader"
35
35
  require "goodcheck/commands/pattern"
36
+ require "goodcheck/unarchiver"
37
+
38
+ module Goodcheck
39
+ DEFAULT_CONFIG_FILE = "goodcheck.yml".freeze
40
+ end
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.exclude_paths.any? {|pattern| path.fnmatch?(pattern, File::FNM_PATHNAME | File::FNM_EXTGLOB) }
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
- 1
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
- 1
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
- 1
49
+ EXIT_ERROR
45
50
  rescue Errno::ENOENT => exn
46
51
  stderr.puts "#{exn}"
47
- 1
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 EXIT_ERROR
32
- validate_rules or return EXIT_ERROR
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 id uniqueness..."
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(Rainbow(" Found #{count} #{duplication}.😞").red)
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
- if rule.triggers.any? {|trigger| !trigger.passes.empty? || !trigger.fails.empty?}
71
- stdout.puts "Testing rule #{Rainbow(rule.id).cyan}..."
68
+ stdout.puts "Testing rule #{Rainbow(rule.id).cyan}..."
72
69
 
73
- rule_ok = true
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't match.😱"
102
+ stdout.puts " #{index + 1}. #{Rainbow('fail').red} example didnt 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 " 🚨 The rule contains a `pattern` with `glob`, which is not supported by the test command."
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
- if rule_ok
119
- stdout.puts " OK!🎉"
120
- success_count += 1
121
- else
122
- failure_count += 1
123
- end
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
- rule_count = success_count + failure_count
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 "Tested #{rule}, #{success}, #{failure}"
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
- test_pass
147
+ failed_rule_ids.empty?
143
148
  end
144
149
 
145
150
  def rule_matches_example?(rule, trigger, example)
@@ -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 :import_target, string
191
- let :imports, array(import_target)
192
- let :exclude, array_or(string)
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(imports),
197
- exclude: optional(exclude)
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
- exclude_paths = Array(content[:exclude])
228
-
229
- Config.new(rules: rules, exclude_paths: exclude_paths)
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.load(content, filename: import)), symbolize_names: true)
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)
@@ -2,5 +2,7 @@ module Goodcheck
2
2
  module ExitStatus
3
3
  EXIT_SUCCESS = 0
4
4
  EXIT_ERROR = 1
5
+ EXIT_MATCH = 2
6
+ EXIT_TEST_FAILED = 3
5
7
  end
6
8
  end
@@ -31,9 +31,13 @@ module Goodcheck
31
31
  end
32
32
 
33
33
  def load(name, &block)
34
- uri = URI.parse(name)
34
+ uri = begin
35
+ URI.parse(name)
36
+ rescue URI::InvalidURIError
37
+ nil
38
+ end
35
39
 
36
- case uri.scheme
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 = Dir.glob(File.join(config_path.parent.to_path, path), File::FNM_DOTMATCH | File::FNM_EXTGLOB).sort
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
- yield File.read(file)
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
- content = http_get uri
91
- Goodcheck.logger.debug "Downloaded content: #{content[0, 1024].inspect}#{content.size > 1024 ? "..." : ""}"
92
- yield content
93
- write_cache uri, content
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
- yield path.read
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
- stdout.puts "#{Rainbow(issue.path).cyan}:#{start_line}:#{start_column}: #{message}"
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Goodcheck
2
- VERSION = "2.7.0".freeze
2
+ VERSION = "3.0.0".freeze
3
3
  end
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: 2.7.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
- - Soutaro Matsumoto
7
+ - Sider Corporation
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-02 00:00:00.000000000 Z
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: minitest-reporters
56
+ name: simplecov
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '1.4'
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: '1.4'
68
+ version: '0.18'
69
69
  - !ruby/object:Gem::Dependency
70
- name: simplecov
70
+ name: marcel
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '0.18'
76
- type: :development
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.18'
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: '4.0'
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: '4.0'
143
- description: Regexp based customizable linter
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
- - matsumoto@soutaro.com
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.com/sider/goodcheck
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.4.0
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.1.4
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: []