foodcritic 15.1.0 → 16.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -2
- data/chef_dsl_metadata/{chef_14.8.12.json → chef_14.14.29.json} +394 -169
- data/chef_dsl_metadata/{chef_13.12.3.json → chef_15.4.45.json} +15454 -2631
- data/foodcritic.gemspec +2 -2
- data/lib/foodcritic.rb +2 -1
- data/lib/foodcritic/api.rb +55 -42
- data/lib/foodcritic/chef.rb +5 -3
- data/lib/foodcritic/command_line.rb +78 -52
- data/lib/foodcritic/domain.rb +7 -9
- data/lib/foodcritic/dsl.rb +4 -1
- data/lib/foodcritic/gerkin/tag.rb +55 -0
- data/lib/foodcritic/gerkin/tag_expression.rb +66 -0
- data/lib/foodcritic/linter.rb +24 -8
- data/lib/foodcritic/notifications.rb +3 -1
- data/lib/foodcritic/output.rb +1 -1
- data/lib/foodcritic/rules/fc001.rb +6 -6
- data/lib/foodcritic/rules/fc004.rb +1 -0
- data/lib/foodcritic/rules/fc006.rb +9 -9
- data/lib/foodcritic/rules/fc007.rb +3 -1
- data/lib/foodcritic/rules/fc016.rb +1 -0
- data/lib/foodcritic/rules/fc019.rb +7 -6
- data/lib/foodcritic/rules/fc022.rb +24 -25
- data/lib/foodcritic/rules/fc024.rb +16 -13
- data/lib/foodcritic/rules/fc029.rb +1 -0
- data/lib/foodcritic/rules/fc031.rb +1 -1
- data/lib/foodcritic/rules/fc032.rb +2 -2
- data/lib/foodcritic/rules/fc033.rb +2 -2
- data/lib/foodcritic/rules/fc034.rb +5 -2
- data/lib/foodcritic/rules/fc039.rb +12 -12
- data/lib/foodcritic/rules/fc040.rb +1 -1
- data/lib/foodcritic/rules/fc044.rb +8 -12
- data/lib/foodcritic/rules/fc048.rb +1 -0
- data/lib/foodcritic/rules/fc121.rb +6 -10
- data/lib/foodcritic/rules/fc123.rb +16 -0
- data/lib/foodcritic/template.rb +3 -6
- data/lib/foodcritic/version.rb +1 -1
- metadata +34 -32
data/foodcritic.gemspec
CHANGED
@@ -4,7 +4,7 @@ require "foodcritic/version"
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "foodcritic"
|
6
6
|
s.version = FoodCritic::VERSION
|
7
|
-
s.description = "
|
7
|
+
s.description = "A code linting tool for Chef Infra cookbooks."
|
8
8
|
s.summary = "foodcritic-#{s.version}"
|
9
9
|
s.authors = ["Andrew Crump"]
|
10
10
|
s.homepage = "http://foodcritic.io"
|
@@ -17,7 +17,6 @@ Gem::Specification.new do |s|
|
|
17
17
|
Dir["misc/**/*"]
|
18
18
|
s.files += Dir["Gemfile", "foodcritic.gemspec", "LICENSE"]
|
19
19
|
|
20
|
-
s.add_dependency("cucumber-core", ">= 1.3", "< 4.0")
|
21
20
|
s.add_dependency("nokogiri", ">= 1.5", "< 2.0")
|
22
21
|
s.add_dependency("rake")
|
23
22
|
s.add_dependency("treetop", "~> 1.4")
|
@@ -25,6 +24,7 @@ Gem::Specification.new do |s|
|
|
25
24
|
s.add_dependency("erubis")
|
26
25
|
s.add_dependency("rufus-lru", "~> 1.0")
|
27
26
|
|
27
|
+
s.add_development_dependency "cucumber-core", ">= 1.3", "< 4.0"
|
28
28
|
s.add_development_dependency "rspec", "~> 3.5"
|
29
29
|
s.add_development_dependency "fuubar", "~> 2.0"
|
30
30
|
s.add_development_dependency "rspec-command", "~> 1.0"
|
data/lib/foodcritic.rb
CHANGED
@@ -2,7 +2,6 @@ Encoding.default_external = Encoding::UTF_8
|
|
2
2
|
Encoding.default_internal = Encoding::UTF_8
|
3
3
|
|
4
4
|
require "pathname"
|
5
|
-
require "cucumber/core"
|
6
5
|
require "treetop"
|
7
6
|
require "ripper"
|
8
7
|
require "ffi_yajl"
|
@@ -22,3 +21,5 @@ require_relative "foodcritic/output"
|
|
22
21
|
require_relative "foodcritic/rake_task"
|
23
22
|
require_relative "foodcritic/template"
|
24
23
|
require_relative "foodcritic/version"
|
24
|
+
require_relative "foodcritic/gerkin/tag"
|
25
|
+
require_relative "foodcritic/gerkin/tag_expression"
|
data/lib/foodcritic/api.rb
CHANGED
@@ -29,7 +29,7 @@ module FoodCritic
|
|
29
29
|
options = { type: :any, ignore_calls: false }.merge!(options)
|
30
30
|
return [] unless ast.respond_to?(:xpath)
|
31
31
|
|
32
|
-
unless
|
32
|
+
unless %i{any string symbol vivified}.include?(options[:type])
|
33
33
|
raise ArgumentError, "Node type not recognised"
|
34
34
|
end
|
35
35
|
|
@@ -56,9 +56,7 @@ module FoodCritic
|
|
56
56
|
|
57
57
|
# get list of items in the dir and intersect with metadata array.
|
58
58
|
# until we get an interfact (we have a metadata) walk up the dir structure
|
59
|
-
until (Dir.entries(file) & %w{metadata.rb metadata.json}).any?
|
60
|
-
file = File.dirname(file)
|
61
|
-
end
|
59
|
+
file = File.dirname(file) until (Dir.entries(file) & %w{metadata.rb metadata.json}).any?
|
62
60
|
|
63
61
|
file
|
64
62
|
end
|
@@ -69,9 +67,7 @@ module FoodCritic
|
|
69
67
|
# @since 7.0.0
|
70
68
|
# @return [String] the value of the metadata field
|
71
69
|
def metadata_field(file, field)
|
72
|
-
until (file.split(File::SEPARATOR) & standard_cookbook_subdirs).empty?
|
73
|
-
file = File.absolute_path(File.dirname(file.to_s))
|
74
|
-
end
|
70
|
+
file = File.absolute_path(File.dirname(file.to_s)) until (file.split(File::SEPARATOR) & standard_cookbook_subdirs).empty?
|
75
71
|
file = File.dirname(file) unless File.extname(file).empty?
|
76
72
|
|
77
73
|
md_path = File.join(file, "metadata.rb")
|
@@ -80,6 +76,7 @@ module FoodCritic
|
|
80
76
|
command[ident/@value='#{field}']/
|
81
77
|
descendant::tstring_content/@value").to_s
|
82
78
|
raise "Cant read #{field} from #{md_path}" if value.to_s.empty?
|
79
|
+
|
83
80
|
return value
|
84
81
|
else
|
85
82
|
raise "Cant find #{md_path}"
|
@@ -98,9 +95,7 @@ module FoodCritic
|
|
98
95
|
begin
|
99
96
|
metadata_field(file, "name")
|
100
97
|
rescue RuntimeError
|
101
|
-
until (file.split(File::SEPARATOR) & standard_cookbook_subdirs).empty?
|
102
|
-
file = File.absolute_path(File.dirname(file.to_s))
|
103
|
-
end
|
98
|
+
file = File.absolute_path(File.dirname(file.to_s)) until (file.split(File::SEPARATOR) & standard_cookbook_subdirs).empty?
|
104
99
|
file = File.dirname(file) unless File.extname(file).empty?
|
105
100
|
File.basename(file)
|
106
101
|
end
|
@@ -156,6 +151,7 @@ module FoodCritic
|
|
156
151
|
if field_name.nil? || field_name.to_s.empty?
|
157
152
|
raise ArgumentError, "Field name cannot be nil or empty"
|
158
153
|
end
|
154
|
+
|
159
155
|
ast.xpath("(.//command[ident/@value='#{field_name}']|.//fcall[ident/@value='#{field_name}']/..)")
|
160
156
|
end
|
161
157
|
|
@@ -163,13 +159,14 @@ module FoodCritic
|
|
163
159
|
def field_value(ast, field_name)
|
164
160
|
field(ast, field_name).xpath('.//args_add_block//tstring_content
|
165
161
|
[count(ancestor::args_add) = 1][count(ancestor::string_add) = 1]
|
166
|
-
/@value').map
|
162
|
+
/@value').map(&:to_s).last
|
167
163
|
end
|
168
164
|
|
169
165
|
# Create a match for a specified file. Use this if the presence of the file
|
170
166
|
# triggers the warning rather than content.
|
171
167
|
def file_match(file)
|
172
168
|
raise ArgumentError, "Filename cannot be nil" if file.nil?
|
169
|
+
|
173
170
|
{ filename: file, matched: file, line: 1, column: 1 }
|
174
171
|
end
|
175
172
|
|
@@ -189,10 +186,11 @@ module FoodCritic
|
|
189
186
|
def find_resources(ast, options = {})
|
190
187
|
options = { type: :any }.merge!(options)
|
191
188
|
return [] unless ast.respond_to?(:xpath)
|
189
|
+
|
192
190
|
scope_type = ""
|
193
191
|
unless options[:type] == :any
|
194
|
-
type_array =
|
195
|
-
scope_type = "[#{type_array.join(
|
192
|
+
type_array = Array(options[:type]).map { |x| "@value='#{x}'" }
|
193
|
+
scope_type = "[#{type_array.join(" or ")}]"
|
196
194
|
end
|
197
195
|
|
198
196
|
# TODO: Include nested resources (provider actions)
|
@@ -241,6 +239,7 @@ module FoodCritic
|
|
241
239
|
# Searches with a query formed from a subexpression will be ignored.
|
242
240
|
def literal_searches(ast)
|
243
241
|
return [] unless ast.respond_to?(:xpath)
|
242
|
+
|
244
243
|
ast.xpath("//method_add_arg[fcall/ident/@value = 'search' and
|
245
244
|
count(descendant::string_embexpr) = 0]/descendant::tstring_content")
|
246
245
|
end
|
@@ -250,23 +249,31 @@ module FoodCritic
|
|
250
249
|
raise_unless_xpath!(node)
|
251
250
|
pos = node.xpath("descendant::pos").first
|
252
251
|
return nil if pos.nil?
|
252
|
+
|
253
253
|
{ matched: node.respond_to?(:name) ? node.name : "",
|
254
254
|
line: pos["line"].to_i, column: pos["column"].to_i }
|
255
255
|
end
|
256
256
|
|
257
|
+
# Returns a global LRU cache holding the AST of a file
|
258
|
+
# @since 16.1
|
259
|
+
# @param size [Integer] the size of the cache (will be resized)
|
260
|
+
def ast_cache(size = nil)
|
261
|
+
@@ast_cache ||= Rufus::Lru::Hash.new(size || 5)
|
262
|
+
if size && @@ast_cache.maxsize != size
|
263
|
+
@@ast_cache.maxsize = size
|
264
|
+
end
|
265
|
+
@@ast_cache
|
266
|
+
end
|
267
|
+
|
257
268
|
# Read the AST for the given Ruby source file
|
258
269
|
def read_ast(file)
|
259
|
-
|
260
|
-
if @ast_cache.include?(file)
|
261
|
-
@ast_cache[file]
|
262
|
-
else
|
263
|
-
@ast_cache[file] = uncached_read_ast(file)
|
264
|
-
end
|
270
|
+
ast_cache[file] ||= uncached_read_ast(file)
|
265
271
|
end
|
266
272
|
|
267
273
|
# Retrieve a single-valued attribute from the specified resource.
|
268
274
|
def resource_attribute(resource, name)
|
269
275
|
raise ArgumentError, "Attribute name cannot be empty" if name.empty?
|
276
|
+
|
270
277
|
resource_attributes(resource)[name.to_s]
|
271
278
|
end
|
272
279
|
|
@@ -300,7 +307,8 @@ module FoodCritic
|
|
300
307
|
if name.xpath("descendant::string_add").size == 1 &&
|
301
308
|
name.xpath("descendant::string_literal").size == 1 &&
|
302
309
|
name.xpath(
|
303
|
-
"descendant::*[self::call or self::string_embexpr]"
|
310
|
+
"descendant::*[self::call or self::string_embexpr]"
|
311
|
+
).empty?
|
304
312
|
name.xpath("descendant::tstring_content/@value").to_s
|
305
313
|
else
|
306
314
|
name
|
@@ -314,7 +322,7 @@ module FoodCritic
|
|
314
322
|
# Resources in an AST, keyed by type.
|
315
323
|
def resources_by_type(ast)
|
316
324
|
raise_unless_xpath!(ast)
|
317
|
-
result = Hash.new { |hash, key| hash[key] =
|
325
|
+
result = Hash.new { |hash, key| hash[key] = [] }
|
318
326
|
find_resources(ast).each do |resource|
|
319
327
|
result[resource_type(resource)] << resource
|
320
328
|
end
|
@@ -328,6 +336,7 @@ module FoodCritic
|
|
328
336
|
if type.empty?
|
329
337
|
raise ArgumentError, "Provided AST node is not a resource"
|
330
338
|
end
|
339
|
+
|
331
340
|
type
|
332
341
|
end
|
333
342
|
|
@@ -344,6 +353,7 @@ module FoodCritic
|
|
344
353
|
# Searches performed by the provided AST.
|
345
354
|
def searches(ast)
|
346
355
|
return [] unless ast.respond_to?(:xpath)
|
356
|
+
|
347
357
|
ast.xpath("//fcall/ident[@value = 'search']")
|
348
358
|
end
|
349
359
|
|
@@ -392,9 +402,10 @@ module FoodCritic
|
|
392
402
|
|
393
403
|
def templates_included(all_templates, template_path, depth = 1)
|
394
404
|
raise RecursedTooFarError.new(template_path) if depth > 10
|
405
|
+
|
395
406
|
partials = read_ast(template_path).xpath('//*[self::command or
|
396
407
|
child::fcall][descendant::ident/@value="render"]//args_add/
|
397
|
-
string_literal//tstring_content/@value').map
|
408
|
+
string_literal//tstring_content/@value').map(&:to_s)
|
398
409
|
Array(template_path) + partials.map do |included_partial|
|
399
410
|
partial_path = Array(all_templates).find do |path|
|
400
411
|
(Pathname.new(template_path).dirname + included_partial).to_s == path
|
@@ -410,10 +421,10 @@ module FoodCritic
|
|
410
421
|
def template_paths(recipe_path)
|
411
422
|
Dir.glob(Pathname.new(recipe_path).dirname.dirname + "templates" +
|
412
423
|
"**/*", File::FNM_DOTMATCH).select do |path|
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
424
|
+
File.file?(path)
|
425
|
+
end.reject do |path|
|
426
|
+
File.basename(path) == ".DS_Store" || File.extname(path) == ".swp"
|
427
|
+
end
|
417
428
|
end
|
418
429
|
|
419
430
|
# Give a filename path it returns the hash of the JSON contents
|
@@ -441,11 +452,11 @@ module FoodCritic
|
|
441
452
|
atts = {}
|
442
453
|
resource.xpath("do_block/descendant::method_add_block[
|
443
454
|
count(ancestor::do_block) = 1][brace_block | do_block]").each do |batt|
|
444
|
-
|
445
|
-
|
446
|
-
|
455
|
+
att_name = batt.xpath("string(method_add_arg/fcall/ident/@value)")
|
456
|
+
if att_name && !att_name.empty? && batt.children.length > 1
|
457
|
+
atts[att_name] = batt.children[1]
|
458
|
+
end
|
447
459
|
end
|
448
|
-
end
|
449
460
|
atts
|
450
461
|
end
|
451
462
|
|
@@ -517,7 +528,7 @@ module FoodCritic
|
|
517
528
|
# those exist in cookbooks, but are not longer part of chef 14+
|
518
529
|
# this prevents false positives in FC019 anytime node.set is found
|
519
530
|
def node_method?(meth, cookbook_dir)
|
520
|
-
|
531
|
+
chef_node_methods.include?(meth) || meth == :set || meth == :set_unless ||
|
521
532
|
patched_node_method?(meth, cookbook_dir)
|
522
533
|
end
|
523
534
|
|
@@ -525,14 +536,15 @@ module FoodCritic
|
|
525
536
|
atts = {}
|
526
537
|
resource.xpath('do_block/descendant::*[self::command or
|
527
538
|
self::method_add_arg][count(ancestor::do_block) >= 1]').each do |att|
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
539
|
+
blocks = att.xpath("ancestor::method_add_block/method_add_arg/fcall")
|
540
|
+
next if blocks.any? { |a| block_depth(a) > block_depth(resource) }
|
541
|
+
|
542
|
+
att_name = att.xpath('string(ident/@value |
|
543
|
+
fcall/ident[@value="variables"]/@value)')
|
544
|
+
unless att_name.empty?
|
545
|
+
atts[att_name] = extract_attribute_value(att, options)
|
546
|
+
end
|
534
547
|
end
|
535
|
-
end
|
536
548
|
atts
|
537
549
|
end
|
538
550
|
|
@@ -573,6 +585,7 @@ module FoodCritic
|
|
573
585
|
class AttFilter
|
574
586
|
def is_att_type(value)
|
575
587
|
return [] unless value.respond_to?(:select)
|
588
|
+
|
576
589
|
value.select do |n|
|
577
590
|
%w{
|
578
591
|
automatic_attrs
|
@@ -599,7 +612,7 @@ module FoodCritic
|
|
599
612
|
|
600
613
|
def standard_attribute_access(ast, options)
|
601
614
|
if options[:type] == :any
|
602
|
-
|
615
|
+
%i{string symbol}.map do |type|
|
603
616
|
standard_attribute_access(ast, options.merge(type: type))
|
604
617
|
end.inject(:+)
|
605
618
|
else
|
@@ -644,13 +657,13 @@ module FoodCritic
|
|
644
657
|
call.xpath("aref/args_add_block").size == 0 &&
|
645
658
|
(call.xpath("descendant::ident").size > 1 &&
|
646
659
|
!node_method?(call.xpath("ident/@value").to_s.to_sym,
|
647
|
-
|
660
|
+
options[:cookbook_dir]))
|
648
661
|
end.sort
|
649
662
|
end
|
650
663
|
|
651
664
|
def word_list_values(ast, xpath = nil)
|
652
665
|
# Find the node for the field argument variable. (e.g. given `foo d`, find `d`)
|
653
|
-
var_ref = ast.xpath("#{xpath ? xpath +
|
666
|
+
var_ref = ast.xpath("#{xpath ? xpath + "/" : ""}descendant::var_ref/ident")
|
654
667
|
if var_ref.empty?
|
655
668
|
# The field is either a static value, or took no arguments, or something
|
656
669
|
# more complex than we care to evaluate.
|
@@ -659,7 +672,7 @@ module FoodCritic
|
|
659
672
|
# Look back out the tree for a method_add_block which contains a block
|
660
673
|
# variable matching the field argument variable, and then drill down to
|
661
674
|
# all the literal content nodes.
|
662
|
-
ast.xpath(%Q{ancestor::method_add_block[//block_var//ident/@value='#{var_ref.first[
|
675
|
+
ast.xpath(%Q{ancestor::method_add_block[//block_var//ident/@value='#{var_ref.first["value"]}']/call//tstring_content})
|
663
676
|
end
|
664
677
|
end
|
665
678
|
end
|
data/lib/foodcritic/chef.rb
CHANGED
@@ -56,12 +56,12 @@ module FoodCritic
|
|
56
56
|
metadata_path(ver)
|
57
57
|
end.find { |m| File.exist?(m) }
|
58
58
|
@dsl_metadata ||= FFI_Yajl::Parser.parse(IO.read(metadata_path),
|
59
|
-
|
59
|
+
symbolize_keys: true)
|
60
60
|
end
|
61
61
|
|
62
62
|
def metadata_path(chef_version)
|
63
63
|
File.join(File.dirname(__FILE__), "..", "..",
|
64
|
-
|
64
|
+
"chef_dsl_metadata/chef_#{chef_version}.json")
|
65
65
|
end
|
66
66
|
|
67
67
|
def resource_check?(key, resource_type, field)
|
@@ -94,9 +94,11 @@ module FoodCritic
|
|
94
94
|
grammar_paths.inject(nil) do |parser, lucene_grammar|
|
95
95
|
begin
|
96
96
|
break parser unless parser.nil?
|
97
|
+
|
97
98
|
# Don't instantiate custom nodes
|
98
99
|
Treetop.load_from_string(
|
99
|
-
IO.read(lucene_grammar).gsub(/<[^>]+>/, "")
|
100
|
+
IO.read(lucene_grammar).gsub(/<[^>]+>/, "")
|
101
|
+
)
|
100
102
|
LuceneParser.new
|
101
103
|
rescue
|
102
104
|
# Silently swallow and try the next grammar
|
@@ -24,74 +24,79 @@ module FoodCritic
|
|
24
24
|
environment_paths: [],
|
25
25
|
exclude_paths: ["test/**/*", "spec/**/*", "features/**/*"],
|
26
26
|
progress: true,
|
27
|
+
ast_cache_size: 5,
|
27
28
|
}
|
28
29
|
@parser = OptionParser.new do |opts|
|
29
30
|
opts.banner = "foodcritic [cookbook_paths]"
|
30
31
|
opts.on("-t", "--tags TAGS",
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
"Check against (or exclude ~) rules with the "\
|
33
|
+
"specified tags.") do |t|
|
34
|
+
@options[:tags] << t
|
35
|
+
end
|
35
36
|
opts.on("-l", "--list",
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
"List all enabled rules and their descriptions.") do |t|
|
38
|
+
@options[:list] = t
|
39
|
+
end
|
39
40
|
opts.on("-f", "--epic-fail TAGS",
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
"Fail the build based on tags. Default of 'any' to fail "\
|
42
|
+
"on all warnings.") do |t|
|
43
|
+
@options[:fail_tags] << t
|
44
|
+
end
|
44
45
|
opts.on("-c", "--chef-version VERSION",
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
"Only check against rules valid for this version "\
|
47
|
+
"of Chef.") do |c|
|
48
|
+
@options[:chef_version] = c
|
49
|
+
end
|
49
50
|
opts.on("-r", "--rule-file PATH",
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
"Specify file with rules to be used or ignored ") do |c|
|
52
|
+
@options[:rule_file] = c
|
53
|
+
end
|
54
|
+
opts.on("-s", "--ast-cache-size NUM", Integer,
|
55
|
+
"Change the size of the AST cache.") do |s|
|
56
|
+
@options[:ast_cache_size] = s
|
57
|
+
end
|
53
58
|
opts.on("-B", "--cookbook-path PATH",
|
54
|
-
|
55
|
-
|
56
|
-
|
59
|
+
"Cookbook path(s) to check.") do |b|
|
60
|
+
@options[:cookbook_paths] << b
|
61
|
+
end
|
57
62
|
opts.on("-C", "--[no-]context",
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
63
|
+
"Show lines matched against rather than the "\
|
64
|
+
"default summary.") do |c|
|
65
|
+
@options[:context] = c
|
66
|
+
end
|
62
67
|
opts.on("-E", "--environment-path PATH",
|
63
|
-
|
64
|
-
|
65
|
-
|
68
|
+
"Environment path(s) to check.") do |e|
|
69
|
+
@options[:environment_paths] << e
|
70
|
+
end
|
66
71
|
opts.on("-I", "--include PATH",
|
67
|
-
|
68
|
-
|
69
|
-
|
72
|
+
"Additional rule file path(s) to load.") do |i|
|
73
|
+
@options[:include_rules] << i
|
74
|
+
end
|
70
75
|
opts.on("-G", "--search-gems",
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
76
|
+
"Search rubygems for rule files with the path "\
|
77
|
+
"foodcritic/rules/**/*.rb") do |g|
|
78
|
+
@options[:search_gems] = true
|
79
|
+
end
|
75
80
|
opts.on("-P", "--[no-]progress",
|
76
|
-
|
77
|
-
|
78
|
-
|
81
|
+
"Show progress of files being checked. default: true") do |q|
|
82
|
+
@options[:progress] = q
|
83
|
+
end
|
79
84
|
opts.on("-R", "--role-path PATH",
|
80
|
-
|
81
|
-
|
82
|
-
|
85
|
+
"Role path(s) to check.") do |r|
|
86
|
+
@options[:role_paths] << r
|
87
|
+
end
|
83
88
|
opts.on("-S", "--search-grammar PATH",
|
84
|
-
|
85
|
-
|
86
|
-
|
89
|
+
"Specify grammar to use when validating search syntax.") do |s|
|
90
|
+
@options[:search_grammar] = s
|
91
|
+
end
|
87
92
|
opts.on("-V", "--version",
|
88
|
-
|
89
|
-
|
90
|
-
|
93
|
+
"Display the foodcritic version.") do |v|
|
94
|
+
@options[:version] = true
|
95
|
+
end
|
91
96
|
opts.on("-X", "--exclude PATH",
|
92
|
-
|
93
|
-
|
94
|
-
|
97
|
+
"Exclude path(s) from being linted. PATH is relative to the cookbook, not an absolute PATH. Default test/**/*,spec/**/*,features/**/*") do |e|
|
98
|
+
options[:exclude_paths] << e
|
99
|
+
end
|
95
100
|
end
|
96
101
|
# -v is not implemented but OptionParser gives the Foodcritic's version
|
97
102
|
# if that flag is passed
|
@@ -106,6 +111,26 @@ module FoodCritic
|
|
106
111
|
end
|
107
112
|
end
|
108
113
|
|
114
|
+
# The end of life message.
|
115
|
+
#
|
116
|
+
# @return [String] A text to warn end users about foodcritic's end of life
|
117
|
+
def show_end_of_life_msg
|
118
|
+
"
|
119
|
+
*******************************************************************************
|
120
|
+
WARNING: Foodcritic will be end of life in April 2020
|
121
|
+
*******************************************************************************
|
122
|
+
Please use Cookstyle to ensure all of your Chef Infra code meets the latest
|
123
|
+
best practices.
|
124
|
+
|
125
|
+
Cookstyle is a code linting tool that helps you to write better Chef Infra
|
126
|
+
Cookbooks by detecting and automatically correct cookbook code.
|
127
|
+
|
128
|
+
For more information, use the command:
|
129
|
+
|
130
|
+
cookstyle -h
|
131
|
+
"
|
132
|
+
end
|
133
|
+
|
109
134
|
# Show the command help to the end user?
|
110
135
|
#
|
111
136
|
# @return [Boolean] True if help should be shown.
|
@@ -113,11 +138,11 @@ module FoodCritic
|
|
113
138
|
@args.length == 1 && @args.first == "--help"
|
114
139
|
end
|
115
140
|
|
116
|
-
# The help text.
|
141
|
+
# The help text plus the end of life message.
|
117
142
|
#
|
118
143
|
# @return [String] Help text describing the command-line options available.
|
119
144
|
def help
|
120
|
-
@parser.help
|
145
|
+
@parser.help + show_end_of_life_msg
|
121
146
|
end
|
122
147
|
|
123
148
|
# Show the current version to the end user?
|
@@ -157,6 +182,7 @@ module FoodCritic
|
|
157
182
|
def valid_grammar?
|
158
183
|
return true unless options.key?(:search_grammar)
|
159
184
|
return false unless File.exist?(options[:search_grammar])
|
185
|
+
|
160
186
|
search = FoodCritic::Chef::Search.new
|
161
187
|
search.create_parser([options[:search_grammar]])
|
162
188
|
search.parser?
|