foodcritic 12.3.0 → 13.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/Gemfile +1 -1
  4. data/README.md +1 -1
  5. data/chef_dsl_metadata/{chef_12.21.14.json → chef_13.7.16.json} +1339 -1052
  6. data/features/step_definitions/cookbook_steps.rb +0 -18
  7. data/features/support/command_helpers.rb +0 -2
  8. data/foodcritic.gemspec +1 -1
  9. data/lib/foodcritic/chef.rb +2 -2
  10. data/lib/foodcritic/linter.rb +2 -2
  11. data/lib/foodcritic/notifications.rb +14 -3
  12. data/lib/foodcritic/rules/fc025.rb +3 -1
  13. data/lib/foodcritic/rules/fc026.rb +1 -1
  14. data/lib/foodcritic/rules/fc037.rb +2 -1
  15. data/lib/foodcritic/rules/fc044.rb +2 -2
  16. data/lib/foodcritic/rules/fc064.rb +1 -1
  17. data/lib/foodcritic/rules/fc065.rb +1 -1
  18. data/lib/foodcritic/rules/fc115.rb +9 -0
  19. data/lib/foodcritic/rules/fc117.rb +9 -0
  20. data/lib/foodcritic/rules/fc118.rb +7 -0
  21. data/lib/foodcritic/rules/fc119.rb +16 -0
  22. data/lib/foodcritic/template.rb +2 -2
  23. data/lib/foodcritic/version.rb +1 -1
  24. data/spec/functional/fc007_spec.rb +1 -1
  25. data/spec/functional/fc009_spec.rb +8 -13
  26. data/spec/functional/fc025_spec.rb +60 -0
  27. data/spec/functional/fc026_spec.rb +66 -0
  28. data/spec/functional/fc037_spec.rb +75 -0
  29. data/spec/functional/fc038_spec.rb +16 -11
  30. data/spec/functional/fc115_spec.rb +24 -0
  31. data/spec/functional/fc117_spec.rb +23 -0
  32. data/spec/functional/fc118_spec.rb +17 -0
  33. data/spec/functional/fc119_spec.rb +27 -0
  34. data/spec/regression/regression_spec.rb +2 -2
  35. data/spec/unit/linter_spec.rb +1 -1
  36. metadata +17 -14
  37. data/chef_dsl_metadata/chef_12.15.19.json +0 -18720
  38. data/chef_dsl_metadata/chef_12.16.42.json +0 -18848
  39. data/chef_dsl_metadata/chef_12.17.44.json +0 -19330
  40. data/chef_dsl_metadata/chef_12.18.31.json +0 -19738
  41. data/chef_dsl_metadata/chef_12.19.36.json +0 -20061
  42. data/chef_dsl_metadata/chef_12.20.3.json +0 -20067
  43. data/features/025_check_for_deprecated_gem_install.feature +0 -30
  44. data/features/026_check_for_conditional_block_string.feature +0 -20
@@ -409,20 +409,6 @@ Given 'a cookbook recipe that has a confusingly named local variable "default"'
409
409
  }
410
410
  end
411
411
 
412
- Given /a cookbook recipe that (install|upgrade)s (a gem|multiple gems)(.*)$/ do |action, arity, approach|
413
- if arity == "a gem"
414
- if approach.empty?
415
- recipe_installs_gem(:simple, action.to_sym)
416
- else
417
- recipe_installs_gem(:compile_time, action.to_sym)
418
- end
419
- elsif approach.include? "array"
420
- recipe_installs_gem(:compile_time_from_array, action.to_sym)
421
- else
422
- recipe_installs_gem(:compile_time_from_word_list, action.to_sym)
423
- end
424
- end
425
-
426
412
  Given "a cookbook recipe that refers to a hidden template" do
427
413
  write_recipe %q{
428
414
  template '/etc/.s3cfg' do
@@ -1779,10 +1765,6 @@ Then "the node access warning 001 should warn on lines 2 and 10 in that order" d
1779
1765
  expect_output(expected_warnings.join("\n"))
1780
1766
  end
1781
1767
 
1782
- Then "the prefer chef_gem to manual install warning 025 should be shown" do
1783
- expect_warning("FC025", :line => nil)
1784
- end
1785
-
1786
1768
  Then "the recipe filename should be displayed" do
1787
1769
  expect_output "cookbooks/example/recipes/default.rb"
1788
1770
  end
@@ -35,8 +35,6 @@ module FoodCritic
35
35
  "FC021" => "Resource condition in provider may not behave as expected",
36
36
  "FC022" => "Resource condition within loop may not behave as expected",
37
37
  "FC024" => "Consider adding platform equivalents",
38
- "FC025" => "Prefer chef_gem to compile-time gem install",
39
- "FC026" => "Conditional execution block attribute contains only string",
40
38
  "FC027" => "Resource sets internal attribute",
41
39
  "FC028" => "Incorrect #platform? usage",
42
40
  "FC029" => "No leading cookbook name in recipe metadata",
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
10
10
  s.homepage = "http://foodcritic.io"
11
11
  s.license = "MIT"
12
12
  s.executables << "foodcritic"
13
- s.required_ruby_version = ">= 2.2.2"
13
+ s.required_ruby_version = ">= 2.3"
14
14
 
15
15
  s.files = Dir["chef_dsl_metadata/*.json"] +
16
16
  Dir["lib/**/*.rb"] +
@@ -52,8 +52,8 @@ module FoodCritic
52
52
  Linter::DEFAULT_CHEF_VERSION
53
53
  end
54
54
  metadata_path = [version, version.sub(/\.[a-z].*/, ""),
55
- Linter::DEFAULT_CHEF_VERSION].map do |version|
56
- metadata_path(version)
55
+ Linter::DEFAULT_CHEF_VERSION].map do |ver|
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)
@@ -9,7 +9,7 @@ module FoodCritic
9
9
 
10
10
  # The default version that will be used to determine relevant rules. This
11
11
  # can be over-ridden at the command line with the `--chef-version` option.
12
- DEFAULT_CHEF_VERSION = "13.6.0"
12
+ DEFAULT_CHEF_VERSION = "13.7.16"
13
13
  attr_reader :chef_version
14
14
 
15
15
  # Perform a lint check. This method is intended for use by the command-line
@@ -285,7 +285,7 @@ module FoodCritic
285
285
  if m.respond_to?(:node_name)
286
286
  match(m)
287
287
  elsif m.respond_to?(:xpath)
288
- m.to_a.map { |m| match(m) }
288
+ m.to_a.map { |n| match(n) }
289
289
  else
290
290
  m
291
291
  end
@@ -126,10 +126,21 @@ module FoodCritic
126
126
  resource_hash_references(notify).empty?
127
127
  end
128
128
 
129
+ # return the notification action as either a symbol or string or nil if it's a variable.
130
+ # Yes you can notify an action as a string but it's wrong and we want to return it as
131
+ # a string so we can tell people not to do that.
129
132
  def notification_action(notify)
130
- notify.xpath('descendant::symbol[1]/ident/@value |
131
- descendant::dyna_symbol[1]/xstring_add/
132
- tstring_content/@value').first.to_s.to_sym
133
+ is_variable = true unless notify.xpath("args_add_block/args_add//args_add[aref or vcall or call or var_ref]").empty?
134
+ string_val = notify.xpath("descendant::args_add/string_literal/string_add/tstring_content/@value").first
135
+ symbol_val = notify.xpath('descendant::args_add/args_add//symbol/ident/@value |
136
+ descendant::dyna_symbol[1]/xstring_add/tstring_content/@value').first
137
+
138
+ # 1) return a nil if the action is a variable like node['foo']['bar']
139
+ # 2) return the symbol if it exists
140
+ # 3) return the string since we're positive that we're not a symbol or variable
141
+ return nil if is_variable
142
+ return symbol_val.value.to_sym unless symbol_val.nil?
143
+ string_val.value
133
144
  end
134
145
 
135
146
  def notification_nodes(ast, &block)
@@ -1,8 +1,10 @@
1
1
  rule "FC025", "Prefer chef_gem to compile-time gem install" do
2
2
  tags %w{correctness deprecated}
3
3
  recipe do |ast|
4
+ # when Ruby 2.4 support goes away this can be simplified to remove the
5
+ # do_block/stmts_add/command case which is Ruby 2.4
4
6
  gem_install = ast.xpath("//stmts_add/assign[method_add_block[command/ident/
5
- @value='gem_package'][do_block/stmts_add/command[ident/@value='action']
7
+ @value='gem_package'][do_block/bodystmt/stmts_add/command[ident/@value='action'] | do_block/stmts_add/command[ident/@value='action']
6
8
  [descendant::ident/@value='nothing']]]")
7
9
  gem_install.map do |install|
8
10
  gem_var = install.xpath("var_field/ident/@value")
@@ -6,7 +6,7 @@ rule "FC026", "Conditional execution block attribute contains only string" do
6
6
  end.flatten.compact.select do |condition|
7
7
  condition.respond_to?(:xpath) &&
8
8
  !condition.xpath("descendant::string_literal").empty? &&
9
- !condition.xpath("stmts_add/string_literal").empty? &&
9
+ !condition.xpath("(stmts_add|bodystmt/stmts_add)/string_literal").empty? && # stmts_add can go away with Ruby 2.4
10
10
  condition.xpath('descendant::stmts_add[count(ancestor::
11
11
  string_literal) = 0]').size == 1
12
12
  end
@@ -7,7 +7,8 @@ rule "FC037", "Invalid notification action" do
7
7
  when :notifies then n[:resource_type]
8
8
  when :subscribes then resource_type(resource).to_sym
9
9
  end
10
- n[:action].size > 0 && !resource_action?(type, n[:action])
10
+ # either the action is a string or it's not nil (means it was a variable) and not valid
11
+ n[:action].is_a?(String) || (!n[:action].nil? && !resource_action?(type, n[:action]))
11
12
  end
12
13
  end
13
14
  end
@@ -9,8 +9,8 @@ rule "FC044", "Avoid bare attribute keys" do
9
9
  [count(child::kw) = 0]/ident').select do |v|
10
10
 
11
11
  local_declared = v.xpath("ancestor::*[self::brace_block or self::do_block]
12
- /block_var/descendant::ident/@value").map do |v|
13
- v.to_s
12
+ /block_var/descendant::ident/@value").map do |val|
13
+ val.to_s
14
14
  end
15
15
 
16
16
  (v["value"] != "secure_password") &&
@@ -1,5 +1,5 @@
1
1
  rule "FC064", "Ensure issues_url is set in metadata" do
2
- tags %w{metadata supermarket chef12}
2
+ tags %w{metadata supermarket}
3
3
  metadata do |ast, filename|
4
4
  [file_match(filename)] unless field(ast, "issues_url").any?
5
5
  end
@@ -1,5 +1,5 @@
1
1
  rule "FC065", "Ensure source_url is set in metadata" do
2
- tags %w{metadata supermarket chef12}
2
+ tags %w{metadata supermarket}
3
3
  metadata do |ast, filename|
4
4
  [file_match(filename)] unless field(ast, "source_url").any?
5
5
  end
@@ -0,0 +1,9 @@
1
+ rule "FC115", "Custom resource contains a name_property that is required" do
2
+ tags %w{correctness}
3
+ recipe do |ast|
4
+ ast.xpath("//command[ident/@value='property'
5
+ and descendant::bare_assoc_hash/assoc_new[label/@value='name_property:' and kw/@value='true']
6
+ and descendant::bare_assoc_hash/assoc_new[label/@value='required:' and kw/@value='true']
7
+ and descendant::symbol_literal/symbol/ident/@value!='name']")
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ rule "FC117", "Do not use kind_of in custom resource properties" do
2
+ tags %w{correctness}
3
+ resource do |ast|
4
+ # Make sure we're in a custom resource not an LWRP
5
+ if ast.xpath("//command/ident/@value='action'")
6
+ ast.xpath("//command[ident/@value='property'][args_add_block//label/@value='kind_of:']")
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ rule "FC118", "Resource property setting name_attribute vs. name_property" do
2
+ tags %w{correctness}
3
+ recipe do |ast|
4
+ ast.xpath("//command[ident/@value='property'
5
+ and descendant::bare_assoc_hash/assoc_new/label/@value='name_attribute:']")
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ rule "FC119", "windows_task :change action no longer exists in Chef 13" do
2
+ tags %w{deprecation chef13}
3
+ recipe do |ast|
4
+ matches = []
5
+ find_resources(ast).each do |resource|
6
+ # if it's a ruby_block check for the :create action
7
+ matches << resource if resource_attribute(resource, "action") == :change && resource_type(resource) == "windows_task"
8
+
9
+ # no matter what check notification
10
+ notifications(resource).any? do |notification|
11
+ matches << resource if notification[:resource_type] == :windows_task && notification[:action] == :change
12
+ end
13
+ end
14
+ matches
15
+ end
16
+ end
@@ -38,8 +38,8 @@ module FoodCritic
38
38
  def expressions(template_code)
39
39
  expr_lines = expressions_with_lines(template_code)
40
40
  expr_lines.map do |expr, line|
41
- e = @expressions.find { |e| e[:code] == expr }
42
- { code: expr, type: e[:type], line: line } if e
41
+ ex = @expressions.find { |e| e[:code] == expr }
42
+ { code: expr, type: ex[:type], line: line } if ex
43
43
  end.compact
44
44
  end
45
45
 
@@ -1,4 +1,4 @@
1
1
  module FoodCritic
2
2
  # The current version of foodcritic
3
- VERSION = "12.3.0"
3
+ VERSION = "13.0.0"
4
4
  end
@@ -76,7 +76,7 @@ describe "FC007" do
76
76
  end
77
77
 
78
78
  context "with an include from the same cookbook" do
79
- recipe_file 'incldue_recipe "test::other"'
79
+ recipe_file 'include_recipe "test::other"'
80
80
  it { is_expected.to_not violate_rule }
81
81
 
82
82
  context "with the shorthand syntax" do
@@ -1,32 +1,27 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe "FC009" do
4
- context "on chef 13.0.133 with a cookbook that new dsc_resource attributes" do
5
- foodcritic_command("--chef-version", "13.0.133", "--no-progress", ".")
4
+ context "on chef 13.6.4 with a cookbook that new dsc_resource attributes" do
5
+ foodcritic_command("--chef-version", "13.6.4", "--no-progress", ".")
6
6
  recipe_file <<-EOH
7
- dsc_resource 'foo' do
8
- resource :something
9
- module_name 'foo'
10
- module_version '1.0.0.0'
7
+ ifconfig 'foo' do
8
+ ethtool_opts 'something'
11
9
  end
12
10
  EOH
13
11
  it { is_expected.not_to violate_rule }
14
12
  end
15
13
 
16
- context "on chef 12.18.31 with a cookbook that new dsc_resource attributes" do
17
- foodcritic_command("--chef-version", "12.18.31", "--no-progress", ".")
14
+ context "on chef 13.0.113 with a cookbook that new dsc_resource attributes" do
15
+ foodcritic_command("--chef-version", "13.0.113", "--no-progress", ".")
18
16
  recipe_file <<-EOH
19
- dsc_resource 'foo' do
20
- resource :something
21
- module_name 'foo'
22
- module_version '1.0.0.0'
17
+ ifconfig 'foo' do
18
+ ethtool_opts 'something'
23
19
  end
24
20
  EOH
25
21
  it { is_expected.to violate_rule }
26
22
  end
27
23
 
28
24
  context "when the resource attribute is actually a raise" do
29
- foodcritic_command("--chef-version", "12.18.31", "--no-progress", ".")
30
25
  recipe_file <<-EOH
31
26
  package package_name do
32
27
  provider case node["platform_family"]
@@ -0,0 +1,60 @@
1
+ require "spec_helper"
2
+
3
+ describe "FC025" do
4
+ context "with a recipe that does a blockless gem_package install" do
5
+ recipe_file <<-EOH
6
+ gem_package 'foo'
7
+ EOH
8
+ it { is_expected.not_to violate_rule }
9
+ end
10
+
11
+ context "with a recipe that does a gem_package install" do
12
+ recipe_file <<-EOH
13
+ gem_package 'foo' do
14
+ action :install
15
+ EOH
16
+ it { is_expected.not_to violate_rule }
17
+ end
18
+
19
+ context "with a recipe that does a gem_package install with a nothing action" do
20
+ recipe_file <<-EOH
21
+ gem_package 'foo' do
22
+ action :nothing
23
+ EOH
24
+ it { is_expected.not_to violate_rule }
25
+ end
26
+
27
+ context "with a recipe that does a compile_time gem install from an array" do
28
+ recipe_file <<-EOH
29
+ %w{bencode i18n transmission-simple}.each do |pkg|
30
+ r = gem_package pkg do
31
+ action :nothing
32
+ end
33
+ r.run_action(:install)
34
+ end
35
+ EOH
36
+ it { is_expected.to violate_rule }
37
+ end
38
+
39
+ context "with a recipe that does a compile_time gem install" do
40
+ recipe_file <<-EOH
41
+ r = gem_package "activesupport" do
42
+ version '2.3.11'
43
+ action :nothing
44
+ end
45
+ r.run_action(:install)
46
+ EOH
47
+ it { is_expected.to violate_rule }
48
+ end
49
+
50
+ context "with a recipe that does a compile_time gem upgrade" do
51
+ recipe_file <<-EOH
52
+ r = gem_package "activesupport" do
53
+ version '2.3.11'
54
+ action :nothing
55
+ end
56
+ r.run_action(:upgrade)
57
+ EOH
58
+ it { is_expected.to violate_rule }
59
+ end
60
+ end
@@ -0,0 +1,66 @@
1
+ require "spec_helper"
2
+
3
+ describe "FC026" do
4
+ context "with a recipe that has a bracketed conditional that shouldn't be in a block" do
5
+ recipe_file <<-EOH
6
+ file 'foo' do
7
+ not_if { "ls foo" }
8
+ end
9
+ EOH
10
+ it { is_expected.to violate_rule }
11
+ end
12
+
13
+ context "with a recipe that has a conditional that shouldn't be in a block" do
14
+ recipe_file <<-EOH
15
+ file 'foo' do
16
+ not_if do "ls foo" end
17
+ end
18
+ EOH
19
+ it { is_expected.to violate_rule }
20
+ end
21
+
22
+ context "with a recipe that has a conditional with a variable that shouldn't be in a block" do
23
+ recipe_file <<-EOH
24
+ file 'foo' do
25
+ only_if { "ls \#{node['foo']['path']}" }
26
+ end
27
+ EOH
28
+ it { is_expected.to violate_rule }
29
+ end
30
+
31
+ context "with a recipe that has a conditional with a method that shouldn't be in a block" do
32
+ recipe_file <<-EOH
33
+ file 'foo' do
34
+ not_if { "ls \#{foo.method()}" }
35
+ end
36
+ EOH
37
+ it { is_expected.to violate_rule }
38
+ end
39
+
40
+ context "with a recipe that has a conditional of foo.bar" do
41
+ recipe_file <<-EOH
42
+ file 'foo' do
43
+ only_if { foo.bar }
44
+ end
45
+ EOH
46
+ it { is_expected.not_to violate_rule }
47
+ end
48
+
49
+ context "with a recipe that has a conditional of foo.to_s" do
50
+ recipe_file <<-EOH
51
+ file 'foo' do
52
+ not_if { foo.to_s }
53
+ end
54
+ EOH
55
+ it { is_expected.not_to violate_rule }
56
+ end
57
+
58
+ context "with a recipe that has a conditional of File.exists?('/etc/foo')" do
59
+ recipe_file <<-EOH
60
+ file 'foo' do
61
+ not_if { File.exists?("/etc/foo") }
62
+ end
63
+ EOH
64
+ it { is_expected.not_to violate_rule }
65
+ end
66
+ end
@@ -0,0 +1,75 @@
1
+ require "spec_helper"
2
+
3
+ describe "FC037" do
4
+ context "with a resource that notifies using an action that is a string" do
5
+ recipe_file <<-EOF
6
+ file '/tmp/b.txt' do
7
+ content 'content'
8
+ notifies 'restart', 'service[httpd]', :delayed
9
+ end
10
+ EOF
11
+ it { is_expected.to violate_rule }
12
+ end
13
+
14
+ context "with a resource that notifies using an action as a symbol" do
15
+ recipe_file <<-EOF
16
+ file '/tmp/a.txt' do
17
+ content 'content'
18
+ notifies :restart, 'service[httpd]', :delayed
19
+ end
20
+ EOF
21
+ it { is_expected.not_to violate_rule }
22
+ end
23
+
24
+ context "with a resource that notifies using an action as a :'symbol'" do
25
+ recipe_file <<-EOF
26
+ file '/tmp/a.txt' do
27
+ content 'content'
28
+ notifies :'restart', 'service[httpd]', :delayed
29
+ end
30
+ EOF
31
+ it { is_expected.not_to violate_rule }
32
+ end
33
+
34
+ context "with a resource that notifies using an action that is an attribute" do
35
+ recipe_file <<-EOF
36
+ file '/tmp/a.txt' do
37
+ content 'content'
38
+ notifies node['foo']['bar'], 'service[httpd]', :delayed
39
+ end
40
+ EOF
41
+ it { is_expected.not_to violate_rule }
42
+ end
43
+
44
+ context "with a resource that notifies using an action that is a resource property" do
45
+ recipe_file <<-EOF
46
+ file '/tmp/a.txt' do
47
+ content 'content'
48
+ notifies new_resource.bob, 'service[httpd]', :delayed
49
+ end
50
+ EOF
51
+ it { is_expected.not_to violate_rule }
52
+ end
53
+
54
+ context "with a resource that notifies using an action that is a variable" do
55
+ recipe_file <<-EOF
56
+ file '/tmp/a.txt' do
57
+ content 'content'
58
+ notifies foo, 'service[httpd]', :delayed
59
+ end
60
+ EOF
61
+ it { is_expected.not_to violate_rule }
62
+ end
63
+
64
+ context "with a resource that notifies with a variable in a loop" do
65
+ recipe_file <<-EOF
66
+ file '/tmp/a.txt' do
67
+ content 'content'
68
+ Array(node['foo']['bar']).each do |action|
69
+ notifies action, 'service[httpd]', :delayed
70
+ end
71
+ end
72
+ EOF
73
+ it { is_expected.not_to violate_rule }
74
+ end
75
+ end