foodcritic 3.0.3 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +6 -14
  2. data/CHANGELOG.md +65 -0
  3. data/README.md +1 -1
  4. data/chef_dsl_metadata/chef_11.10.0.json +10272 -0
  5. data/chef_dsl_metadata/chef_11.10.2.json +10272 -0
  6. data/chef_dsl_metadata/chef_11.10.4.json +10272 -0
  7. data/chef_dsl_metadata/chef_11.6.2.json +9566 -0
  8. data/chef_dsl_metadata/chef_11.8.0.json +10074 -0
  9. data/chef_dsl_metadata/chef_11.8.2.json +10074 -0
  10. data/features/001_check_node_access.feature +60 -0
  11. data/features/003_check_for_chef_server.feature +5 -0
  12. data/features/006_check_file_mode.feature +1 -0
  13. data/features/022_check_for_dodgy_conditions_within_loop.feature +10 -0
  14. data/features/040_check_raw_git_usage.feature +6 -0
  15. data/features/047_check_for_attribute_assignment_without_precedence.feature +2 -0
  16. data/features/build_framework_support.feature +10 -0
  17. data/features/exclude_paths_to_lint.feature +19 -0
  18. data/features/show_lines_matched.feature +4 -4
  19. data/features/sort_warnings.feature +1 -1
  20. data/features/step_definitions/cookbook_steps.rb +117 -24
  21. data/features/support/command_helpers.rb +45 -12
  22. data/features/support/env.rb +5 -0
  23. data/lib/foodcritic/api.rb +122 -99
  24. data/lib/foodcritic/ast.rb +6 -7
  25. data/lib/foodcritic/chef.rb +24 -23
  26. data/lib/foodcritic/command_line.rb +49 -41
  27. data/lib/foodcritic/domain.rb +12 -10
  28. data/lib/foodcritic/dsl.rb +4 -7
  29. data/lib/foodcritic/error_checker.rb +0 -3
  30. data/lib/foodcritic/linter.rb +45 -43
  31. data/lib/foodcritic/notifications.rb +32 -32
  32. data/lib/foodcritic/output.rb +3 -6
  33. data/lib/foodcritic/rake_task.rb +9 -10
  34. data/lib/foodcritic/rules.rb +278 -240
  35. data/lib/foodcritic/template.rb +6 -13
  36. data/lib/foodcritic/version.rb +1 -1
  37. data/lib/foodcritic/xml.rb +1 -3
  38. data/man/foodcritic.1 +6 -2
  39. data/man/foodcritic.1.ronn +4 -1
  40. data/spec/foodcritic/linter_spec.rb +2 -2
  41. data/spec/regression/expected-output.txt +296 -1
  42. metadata +160 -138
@@ -1,9 +1,8 @@
1
1
  module FoodCritic
2
-
3
2
  # This module contains the logic for the parsing of
4
- # [Chef Notifications](http://wiki.opscode.com/display/chef/Resources#Resources-Notifications).
3
+ # [Chef Notifications]
4
+ # (http://docs.opscode.com/resource_common.html#notifications).
5
5
  module Notifications
6
-
7
6
  # Extracts notification details from the provided AST, returning an
8
7
  # array of notification hashes.
9
8
  #
@@ -28,31 +27,33 @@ module FoodCritic
28
27
 
29
28
  # Chef supports two styles of notification.
30
29
  notified_resource = if new_style_notification?(notify)
31
- # `notifies :restart, "service[foo]"`
32
- new_style_notification(notify)
33
- else
34
- # `notifies :restart, resources(:service => "foo")`
35
- old_style_notification(notify)
36
- end
30
+ # `notifies :restart, "service[foo]"`
31
+ new_style_notification(notify)
32
+ else
33
+ # `notifies :restart, resources(service: "foo")`
34
+ old_style_notification(notify)
35
+ end
37
36
 
38
37
  # Ignore if the notification was not parsed
39
38
  next unless notified_resource
40
39
 
41
40
  # Now merge the extract notification details with the attributes
42
41
  # that are common to both styles of notification.
43
- notified_resource.merge({
44
- # The `:type` of notification: `:subscribes` or `:notifies`.
45
- :type => notification_type(notify),
42
+ notified_resource.merge(
43
+ {
44
+ # The `:type` of notification: `:subscribes` or `:notifies`.
45
+ type: notification_type(notify),
46
46
 
47
- # The `:style` of notification: `:new` or `:old`.
48
- :style => new_style_notification?(notify) ? :new : :old,
47
+ # The `:style` of notification: `:new` or `:old`.
48
+ style: new_style_notification?(notify) ? :new : :old,
49
49
 
50
- # The target resource action.
51
- :action => notification_action(notify),
50
+ # The target resource action.
51
+ action: notification_action(notify),
52
52
 
53
- # The notification timing. Either `:immediate` or `:delayed`.
54
- :timing => notification_timing(notify)
55
- })
53
+ # The notification timing. Either `:immediate` or `:delayed`.
54
+ timing: notification_timing(notify)
55
+ }
56
+ )
56
57
  end.compact
57
58
  end
58
59
 
@@ -61,7 +62,6 @@ module FoodCritic
61
62
  # Extract the `:resource_name` and `:resource_type` from a new-style
62
63
  # notification.
63
64
  def new_style_notification(notify)
64
-
65
65
  # Given `notifies :restart, "service[foo]"` the target is the
66
66
  # `"service[foo]"` string portion.
67
67
  target_path = 'args_add_block/args_add/descendant::
@@ -75,7 +75,7 @@ module FoodCritic
75
75
 
76
76
  # Convert the captured resource type and name to symbols.
77
77
  resource_type, resource_name =
78
- match.captures.tap{|m| m[0] = m[0].to_sym}
78
+ match.captures.tap { |m| m[0] = m[0].to_sym }
79
79
 
80
80
  # Normally the `resource_name` will be a simple string. However in the
81
81
  # case where it has an embedded sub-expression then we will return the
@@ -86,7 +86,7 @@ module FoodCritic
86
86
  resource_name =
87
87
  notify.xpath('args_add_block/args_add/string_literal')
88
88
  end
89
- {:resource_name => resource_name, :resource_type => resource_type}
89
+ { resource_name: resource_name, resource_type: resource_type }
90
90
  end
91
91
 
92
92
  # Extract the `:resource_name` and `:resource_type` from an old-style
@@ -97,11 +97,12 @@ module FoodCritic
97
97
  resource_name = resources.xpath('string_add[1][count(../
98
98
  descendant::string_add) = 1]/tstring_content/@value').to_s
99
99
  resource_name = resources if resource_name.empty?
100
- {:resource_name => resource_name, :resource_type => resource_type}
100
+ { resource_name: resource_name, resource_type: resource_type }
101
101
  end
102
102
 
103
103
  def notification_timing(notify)
104
- # The notification timing should be the last symbol on the notifies element.
104
+ # The notification timing should be the last symbol
105
+ # on the notifies element.
105
106
  timing = notify.xpath('args_add_block/args_add/symbol_literal[last()]/
106
107
  symbol/ident[1]/@value')
107
108
  if timing.empty?
@@ -109,12 +110,11 @@ module FoodCritic
109
110
  :delayed
110
111
  else
111
112
  case timing.first.to_s.to_sym
112
- # Both forms are valid, but we return `:immediate` for both to avoid
113
- # the caller having to recognise both.
114
- when :immediately, :immediate then :immediate
115
-
116
- # Pass the timing through unmodified if we don't recognise it.
117
- else timing.first.to_s.to_sym
113
+ # Both forms are valid, but we return `:immediate` for both to avoid
114
+ # the caller having to recognise both.
115
+ when :immediately, :immediate then :immediate
116
+ # Pass the timing through unmodified if we don't recognise it.
117
+ else timing.first.to_s.to_sym
118
118
  end
119
119
  end
120
120
  end
@@ -125,7 +125,8 @@ module FoodCritic
125
125
 
126
126
  def notification_action(notify)
127
127
  notify.xpath('descendant::symbol[1]/ident/@value |
128
- descendant::dyna_symbol[1]/xstring_add/tstring_content/@value').first.to_s.to_sym
128
+ descendant::dyna_symbol[1]/xstring_add/
129
+ tstring_content/@value').first.to_s.to_sym
129
130
  end
130
131
 
131
132
  def notification_nodes(ast, &block)
@@ -142,6 +143,5 @@ module FoodCritic
142
143
  ast.xpath('descendant::method_add_arg[fcall/ident/
143
144
  @value="resources"]/descendant::assoc_new')
144
145
  end
145
-
146
146
  end
147
147
  end
@@ -1,7 +1,6 @@
1
1
  require 'set'
2
2
 
3
3
  module FoodCritic
4
-
5
4
  # Default output showing a summary view.
6
5
  class SummaryOutput
7
6
  # Output a summary view only listing the matching rules, file and line
@@ -15,7 +14,6 @@ module FoodCritic
15
14
 
16
15
  # Display rule matches with surrounding context.
17
16
  class ContextOutput
18
-
19
17
  # Output the review showing matching lines with context.
20
18
  #
21
19
  # @param [Review] review The review to output.
@@ -32,7 +30,7 @@ module FoodCritic
32
30
 
33
31
  key_by_file_and_line(review).each do |fn, warnings|
34
32
  print_fn.call fn
35
- unless File.exists?(fn)
33
+ unless File.exist?(fn)
36
34
  print_rule.call warnings[1].to_a.join("\n")
37
35
  next
38
36
  end
@@ -52,7 +50,8 @@ module FoodCritic
52
50
  # Find the first warning within our context
53
51
  context_warns = context_set & warn_lines
54
52
  next_warn = context_warns.min
55
- # We may need to interrupt the trailing context of a previous warning
53
+ # We may need to interrupt the trailing context
54
+ # of a previous warning
56
55
  next_warn = file.lineno if warn_lines.include? file.lineno
57
56
 
58
57
  # Display a warning
@@ -120,7 +119,5 @@ module FoodCritic
120
119
  puts text
121
120
  end
122
121
  end
123
-
124
122
  end
125
-
126
123
  end
@@ -16,24 +16,23 @@ module FoodCritic
16
16
  end
17
17
 
18
18
  def options
19
- {:fail_tags => ['correctness'], # differs to default cmd-line behaviour
20
- :cookbook_paths => @files,
21
- :exclude_paths => ['test/**/*', 'spec/**/*', 'features/**/*']
19
+ {
20
+ fail_tags: ['correctness'], # differs to default cmd-line behaviour
21
+ cookbook_paths: @files,
22
+ exclude_paths: ['test/**/*', 'spec/**/*', 'features/**/*'],
23
+ context: false,
22
24
  }.merge(@options)
23
25
  end
24
26
 
25
27
  def define
26
- desc "Lint Chef cookbooks"
28
+ desc 'Lint Chef cookbooks' unless ::Rake.application.last_comment
27
29
  task(name) do
28
30
  result = FoodCritic::Linter.new.check(options)
29
- if result.warnings.any?
30
- puts result
31
- end
32
-
33
- fail result.to_s if result.failed?
31
+ printer = options[:context] ? ContextOutput.new : SummaryOutput.new
32
+ printer.output(result) if result.warnings.any?
33
+ abort if result.failed?
34
34
  end
35
35
  end
36
-
37
36
  end
38
37
  end
39
38
  end
@@ -1,16 +1,24 @@
1
1
  # This file contains all of the rules that ship with foodcritic.
2
2
  #
3
- # * Foodcritic rules perform static code analysis - rather than the cookbook code
4
- # being loaded by the interpreter it is parsed into a tree (AST) that is then
5
- # passed to each rule.
3
+ # * Foodcritic rules perform static code analysis - rather than the cookbook
4
+ # code being loaded by the interpreter it is parsed into a tree (AST) that is
5
+ # then passed to each rule.
6
6
  # * Rules can use a number of API functions that ship with foodcritic to make
7
7
  # sense of the parse tree.
8
8
  # * Rules can also use XPath to query the AST. A rule can consist of a XPath
9
9
  # query only, as any nodes returned from a `recipe` block will be converted
10
10
  # into warnings.
11
11
 
12
- rule "FC002", "Avoid string interpolation where not required" do
13
- tags %w{style strings}
12
+ rule 'FC001',
13
+ 'Use strings in preference to symbols to access node attributes' do
14
+ tags %w(style attributes)
15
+ recipe do |ast|
16
+ attribute_access(ast, type: :symbol)
17
+ end
18
+ end
19
+
20
+ rule 'FC002', 'Avoid string interpolation where not required' do
21
+ tags %w(style strings)
14
22
  recipe do |ast|
15
23
  ast.xpath(%q{//*[self::string_literal | self::assoc_new]/string_add[
16
24
  count(descendant::string_embexpr) = 1 and
@@ -18,36 +26,36 @@ rule "FC002", "Avoid string interpolation where not required" do
18
26
  end
19
27
  end
20
28
 
21
- rule "FC003",
22
- "Check whether you are running with chef server before using" +
23
- " server-specific features" do
24
- tags %w{portability solo}
25
- recipe do |ast,filename|
26
- unless checks_for_chef_solo?(ast) or chef_solo_search_supported?(filename)
29
+ rule 'FC003',
30
+ 'Check whether you are running with chef server before using'\
31
+ ' server-specific features' do
32
+ tags %w(portability solo)
33
+ recipe do |ast, filename|
34
+ unless checks_for_chef_solo?(ast) || chef_solo_search_supported?(filename)
27
35
  searches(ast)
28
36
  end
29
37
  end
30
38
  end
31
39
 
32
- rule "FC004", "Use a service resource to start and stop services" do
33
- tags %w{style services}
40
+ rule 'FC004', 'Use a service resource to start and stop services' do
41
+ tags %w(style services)
34
42
  recipe do |ast|
35
- find_resources(ast, :type => 'execute').find_all do |cmd|
43
+ find_resources(ast, type: 'execute').find_all do |cmd|
36
44
  cmd_str = (resource_attribute(cmd, 'command') || resource_name(cmd)).to_s
37
45
  (cmd_str.include?('/etc/init.d') || ['service ', '/sbin/service ',
38
46
  'start ', 'stop ', 'invoke-rc.d '].any? do |service_cmd|
39
47
  cmd_str.start_with?(service_cmd)
40
- end) && %w{start stop restart reload}.any?{|a| cmd_str.include?(a)}
48
+ end) && %w(start stop restart reload).any? { |a| cmd_str.include?(a) }
41
49
  end
42
50
  end
43
51
  end
44
52
 
45
- rule "FC005", "Avoid repetition of resource declarations" do
46
- tags %w{style}
53
+ rule 'FC005', 'Avoid repetition of resource declarations' do
54
+ tags %w(style)
47
55
  recipe do |ast|
48
56
  resources = find_resources(ast).map do |res|
49
- resource_attributes(res).merge({:type => resource_type(res),
50
- :ast => res})
57
+ resource_attributes(res).merge({ type: resource_type(res),
58
+ ast: res })
51
59
  end.chunk do |res|
52
60
  res[:type] +
53
61
  res[:ast].xpath("ancestor::*[self::if | self::unless | self::elsif |
@@ -55,74 +63,80 @@ rule "FC005", "Avoid repetition of resource declarations" do
55
63
  descendant::pos[position() = 1]").to_s +
56
64
  res[:ast].xpath("ancestor::method_add_block/command[
57
65
  ident/@value='action']/args_add_block/descendant::ident/@value").to_s
58
- end.reject{|res| res[1].size < 3}
66
+ end.reject { |res| res[1].size < 3 }
59
67
  resources.map do |cont_res|
60
68
  first_resource = cont_res[1][0][:ast]
61
69
  # we have contiguous resources of the same type, but do they share the
62
70
  # same attributes?
63
71
  sorted_atts = cont_res[1].map do |atts|
64
- atts.delete_if{|k| k == :ast}.to_a.sort do |x,y|
72
+ atts.delete_if { |k| k == :ast }.to_a.sort do |x, y|
65
73
  x.first.to_s <=> y.first.to_s
66
74
  end
67
75
  end
68
76
  first_resource if sorted_atts.all? do |att|
69
- (att - sorted_atts.inject{|atts,a| atts & a}).length == 1
77
+ (att - sorted_atts.inject { |atts, a| atts & a }).length == 1
70
78
  end
71
79
  end.compact
72
80
  end
73
81
  end
74
82
 
75
- rule "FC006",
76
- "Mode should be quoted or fully specified when setting file permissions" do
77
- tags %w{correctness files}
83
+ rule 'FC006',
84
+ 'Mode should be quoted or fully specified when '\
85
+ 'setting file permissions' do
86
+ tags %w(correctness files)
78
87
  recipe do |ast|
79
88
  ast.xpath(%q{//ident[@value='mode']/parent::command/
80
- descendant::int[string-length(@value) < 5 and not(starts-with(@value, "0")
81
- and string-length(@value) = 4)]/ancestor::method_add_block})
89
+ descendant::int[string-length(@value) < 5
90
+ and not(starts-with(@value, "0")
91
+ and string-length(@value) = 4)][count(ancestor::aref) = 0]/
92
+ ancestor::method_add_block})
82
93
  end
83
94
  end
84
95
 
85
- rule "FC007", "Ensure recipe dependencies are reflected in cookbook metadata" do
86
- tags %w{correctness metadata}
87
- recipe do |ast,filename|
88
- metadata_path =Pathname.new(
96
+ rule 'FC007', 'Ensure recipe dependencies are reflected '\
97
+ 'in cookbook metadata' do
98
+ tags %w(correctness metadata)
99
+ recipe do |ast, filename|
100
+ metadata_path = Pathname.new(
89
101
  File.join(File.dirname(filename), '..', 'metadata.rb')).cleanpath
90
- next unless File.exists? metadata_path
91
- actual_included = included_recipes(ast, :with_partial_names => false)
102
+ next unless File.exist? metadata_path
103
+ actual_included = included_recipes(ast, with_partial_names: false)
92
104
  undeclared = actual_included.keys.map do |recipe|
93
105
  recipe.split('::').first
94
106
  end - [cookbook_name(filename)] -
95
107
  declared_dependencies(read_ast(metadata_path))
96
108
  actual_included.map do |recipe, include_stmts|
97
109
  if undeclared.include?(recipe) ||
98
- undeclared.any?{|u| recipe.start_with?("#{u}::")}
110
+ undeclared.any? { |u| recipe.start_with?("#{u}::") }
99
111
  include_stmts
100
112
  end
101
113
  end.flatten.compact
102
114
  end
103
115
  end
104
116
 
105
- rule "FC008", "Generated cookbook metadata needs updating" do
106
- tags %w{style metadata}
107
- metadata do |ast,filename|
108
- {'maintainer' => 'YOUR_COMPANY_NAME',
109
- 'maintainer_email' => 'YOUR_EMAIL'}.map do |field,value|
110
- ast.xpath(%Q{//command[ident/@value='#{field}']/
111
- descendant::tstring_content[@value='#{value}']})
117
+ rule 'FC008', 'Generated cookbook metadata needs updating' do
118
+ tags %w(style metadata)
119
+ metadata do |ast, filename|
120
+ {
121
+ 'maintainer' => 'YOUR_COMPANY_NAME',
122
+ 'maintainer_email' => 'YOUR_EMAIL'
123
+ }.map do |field, value|
124
+ ast.xpath(%Q(//command[ident/@value='#{field}']/
125
+ descendant::tstring_content[@value='#{value}']))
112
126
  end
113
127
  end
114
128
  end
115
129
 
116
- rule "FC009", "Resource attribute not recognised" do
117
- tags %w{correctness}
130
+ rule 'FC009', 'Resource attribute not recognised' do
131
+ tags %w(correctness)
118
132
  recipe do |ast|
119
133
  matches = []
120
- resource_attributes_by_type(ast).each do |type,resources|
134
+ resource_attributes_by_type(ast).each do |type, resources|
121
135
  resources.each do |resource|
122
136
  resource.keys.map(&:to_sym).reject do |att|
123
137
  resource_attribute?(type.to_sym, att)
124
138
  end.each do |invalid_att|
125
- matches << find_resources(ast, :type => type).find do |res|
139
+ matches << find_resources(ast, type: type).find do |res|
126
140
  resource_attributes(res).include?(invalid_att.to_s)
127
141
  end
128
142
  end
@@ -132,36 +146,36 @@ rule "FC009", "Resource attribute not recognised" do
132
146
  end
133
147
  end
134
148
 
135
- rule "FC010", "Invalid search syntax" do
136
- tags %w{correctness search}
149
+ rule 'FC010', 'Invalid search syntax' do
150
+ tags %w(correctness search)
137
151
  recipe do |ast|
138
152
  # This only works for literal search strings
139
- literal_searches(ast).reject{|search| valid_query?(search['value'])}
153
+ literal_searches(ast).reject { |search| valid_query?(search['value']) }
140
154
  end
141
155
  end
142
156
 
143
- rule "FC011", "Missing README in markdown format" do
144
- tags %w{style readme}
157
+ rule 'FC011', 'Missing README in markdown format' do
158
+ tags %w(style readme)
145
159
  cookbook do |filename|
146
- unless File.exists?(File.join(filename, 'README.md'))
160
+ unless File.exist?(File.join(filename, 'README.md'))
147
161
  [file_match(File.join(filename, 'README.md'))]
148
162
  end
149
163
  end
150
164
  end
151
165
 
152
- rule "FC012", "Use Markdown for README rather than RDoc" do
153
- tags %w{style readme}
166
+ rule 'FC012', 'Use Markdown for README rather than RDoc' do
167
+ tags %w(style readme)
154
168
  cookbook do |filename|
155
- if File.exists?(File.join(filename, 'README.rdoc'))
169
+ if File.exist?(File.join(filename, 'README.rdoc'))
156
170
  [file_match(File.join(filename, 'README.rdoc'))]
157
171
  end
158
172
  end
159
173
  end
160
174
 
161
- rule "FC013", "Use file_cache_path rather than hard-coding tmp paths" do
162
- tags %w{style files}
175
+ rule 'FC013', 'Use file_cache_path rather than hard-coding tmp paths' do
176
+ tags %w(style files)
163
177
  recipe do |ast|
164
- find_resources(ast, :type => 'remote_file').find_all do |download|
178
+ find_resources(ast, type: 'remote_file').find_all do |download|
165
179
  path = (resource_attribute(download, 'path') ||
166
180
  resource_name(download)).to_s
167
181
  path.start_with?('/tmp/')
@@ -169,47 +183,47 @@ rule "FC013", "Use file_cache_path rather than hard-coding tmp paths" do
169
183
  end
170
184
  end
171
185
 
172
- rule "FC014", "Consider extracting long ruby_block to library" do
173
- tags %w{style libraries}
186
+ rule 'FC014', 'Consider extracting long ruby_block to library' do
187
+ tags %w(style libraries)
174
188
  recipe do |ast|
175
- find_resources(ast, :type => 'ruby_block').find_all do |rb|
189
+ find_resources(ast, type: 'ruby_block').find_all do |rb|
176
190
  lines = rb.xpath("descendant::fcall[ident/@value='block']/../../
177
- descendant::*[@line]/@line").map{|n| n.value.to_i}.sort
178
- (! lines.empty?) && (lines.last - lines.first) > 15
191
+ descendant::*[@line]/@line").map { |n| n.value.to_i }.sort
192
+ (!lines.empty?) && (lines.last - lines.first) > 15
179
193
  end
180
194
  end
181
195
  end
182
196
 
183
- rule "FC015", "Consider converting definition to a LWRP" do
184
- tags %w{style definitions lwrp}
185
- applies_to {|version| version >= gem_version("0.7.12")}
197
+ rule 'FC015', 'Consider converting definition to a LWRP' do
198
+ tags %w(style definitions lwrp)
199
+ applies_to { |version| version >= gem_version('0.7.12') }
186
200
  cookbook do |dir|
187
201
  Dir[File.join(dir, 'definitions', '*.rb')].reject do |entry|
188
202
  ['.', '..'].include? entry
189
- end.map{|entry| file_match(entry)}
203
+ end.map { |entry| file_match(entry) }
190
204
  end
191
205
  end
192
206
 
193
- rule "FC016", "LWRP does not declare a default action" do
194
- tags %w{correctness lwrp}
195
- applies_to {|version| version >= gem_version("0.7.12")}
207
+ rule 'FC016', 'LWRP does not declare a default action' do
208
+ tags %w(correctness lwrp)
209
+ applies_to { |version| version >= gem_version('0.7.12') }
196
210
  resource do |ast, filename|
197
211
  unless ["//ident/@value='default_action'",
198
212
  "//def/bodystmt/descendant::assign/
199
- var_field/ivar/@value='@action'"].any? {|expr| ast.xpath(expr)}
213
+ var_field/ivar/@value='@action'"].any? { |expr| ast.xpath(expr) }
200
214
  [file_match(filename)]
201
215
  end
202
216
  end
203
217
  end
204
218
 
205
- rule "FC017", "LWRP does not notify when updated" do
206
- tags %w{correctness lwrp}
219
+ rule 'FC017', 'LWRP does not notify when updated' do
220
+ tags %w(correctness lwrp)
207
221
  applies_to do |version|
208
- version >= gem_version("0.7.12")
222
+ version >= gem_version('0.7.12')
209
223
  end
210
224
  provider do |ast, filename|
211
225
 
212
- use_inline_resources = ! ast.xpath('//*[self::vcall or self::var_ref]/ident
226
+ use_inline_resources = !ast.xpath('//*[self::vcall or self::var_ref]/ident
213
227
  [@value="use_inline_resources"]').empty?
214
228
 
215
229
  unless use_inline_resources
@@ -219,11 +233,11 @@ rule "FC017", "LWRP does not notify when updated" do
219
233
  actions.reject do |action|
220
234
  blk = action.xpath('ancestor::command[1]/
221
235
  following-sibling::*[self::do_block or self::brace_block]')
222
- empty = ! blk.xpath('stmts_add/void_stmt').empty?
223
- converge_by = ! blk.xpath('descendant::*[self::command or self::fcall]
236
+ empty = !blk.xpath('stmts_add/void_stmt').empty?
237
+ converge_by = !blk.xpath('descendant::*[self::command or self::fcall]
224
238
  /ident[@value="converge_by"]').empty?
225
239
 
226
- updated_by_last_action = ! blk.xpath('descendant::*[self::call or
240
+ updated_by_last_action = !blk.xpath('descendant::*[self::call or
227
241
  self::command_call]/*[self::vcall or self::var_ref/ident/
228
242
  @value="new_resource"]/../ident[@value="updated_by_last_action"]
229
243
  ').empty?
@@ -235,9 +249,9 @@ rule "FC017", "LWRP does not notify when updated" do
235
249
  end
236
250
  end
237
251
 
238
- rule "FC018", "LWRP uses deprecated notification syntax" do
239
- tags %w{style lwrp deprecated}
240
- applies_to {|version| version >= gem_version("0.9.10")}
252
+ rule 'FC018', 'LWRP uses deprecated notification syntax' do
253
+ tags %w(style lwrp deprecated)
254
+ applies_to { |version| version >= gem_version('0.9.10') }
241
255
  provider do |ast|
242
256
  ast.xpath("//assign/var_field/ivar[@value='@updated']").map do |class_var|
243
257
  match(class_var)
@@ -246,37 +260,44 @@ rule "FC018", "LWRP uses deprecated notification syntax" do
246
260
  end
247
261
  end
248
262
 
249
- rule "FC019", "Access node attributes in a consistent manner" do
250
- tags %w{style attributes}
263
+ rule 'FC019', 'Access node attributes in a consistent manner' do
264
+ tags %w(style attributes)
251
265
  cookbook do |cookbook_dir|
252
266
  asts = {}; files = Dir["#{cookbook_dir}/*/*.rb"].reject do |file|
253
- relative_path = Pathname.new(file).relative_path_from(Pathname.new(cookbook_dir))
267
+ relative_path = Pathname.new(file).relative_path_from(
268
+ Pathname.new(cookbook_dir))
254
269
  relative_path.to_s.split(File::SEPARATOR).include?('spec')
255
270
  end.map do |file|
256
- {:path => file, :ast => read_ast(file)}
271
+ { path: file, ast: read_ast(file) }
257
272
  end
258
273
  types = [:string, :symbol, :vivified].map do |type|
259
- {:access_type => type, :count => files.map do |file|
260
- attribute_access(file[:ast], :type => type, :ignore_calls => true,
261
- :cookbook_dir => cookbook_dir, :ignore => 'run_state').tap do |ast|
262
- unless ast.empty?
263
- (asts[type] ||= []) << {:ast => ast, :path => file[:path]}
264
- end
265
- end.size
266
- end.inject(:+)}
267
- end.reject{|type| type[:count] == 0}
274
+ {
275
+ access_type: type, count: files.map do |file|
276
+ attribute_access(file[:ast], type: type, ignore_calls: true,
277
+ cookbook_dir: cookbook_dir, ignore: 'run_state').tap do |ast|
278
+ unless ast.empty?
279
+ (asts[type] ||= []) << { ast: ast, path: file[:path] }
280
+ end
281
+ end.size
282
+ end.inject(:+)
283
+ }
284
+ end.reject { |type| type[:count] == 0 }
268
285
  if asts.size > 1
269
- least_used = asts[types.min{|a,b| a[:count] <=> b[:count]}[:access_type]]
286
+ least_used = asts[types.min do |a, b|
287
+ a[:count] <=> b[:count]
288
+ end[:access_type]]
270
289
  least_used.map do |file|
271
- file[:ast].map{|ast| match(ast).merge(:filename => file[:path])}.flatten
290
+ file[:ast].map do |ast|
291
+ match(ast).merge(filename: file[:path])
292
+ end.flatten
272
293
  end
273
294
  end
274
295
  end
275
296
  end
276
297
 
277
- rule "FC021", "Resource condition in provider may not behave as expected" do
278
- tags %w{correctness lwrp}
279
- applies_to {|version| version >= gem_version("0.10.6")}
298
+ rule 'FC021', 'Resource condition in provider may not behave as expected' do
299
+ tags %w(correctness lwrp)
300
+ applies_to { |version| version >= gem_version('0.10.6') }
280
301
  provider do |ast|
281
302
  find_resources(ast).map do |resource|
282
303
  condition = resource.xpath(%q{//method_add_block/
@@ -289,27 +310,34 @@ rule "FC021", "Resource condition in provider may not behave as expected" do
289
310
  end
290
311
  end
291
312
 
292
- rule "FC022", "Resource condition within loop may not behave as expected" do
293
- tags %w{correctness}
294
- applies_to {|version| version >= gem_version("0.10.6")}
313
+ rule 'FC022', 'Resource condition within loop may not behave as expected' do
314
+ tags %w(correctness)
315
+ applies_to { |version| version >= gem_version('0.10.6') }
295
316
  recipe do |ast|
296
- ast.xpath("//call[ident/@value='each']/../do_block").map do |loop|
297
- block_vars = loop.xpath("block_var/params/child::*").map do |n|
317
+ ast.xpath("//call[ident/@value='each']/../do_block[count(ancestor::
318
+ method_add_block/method_add_arg/fcall/ident[@value='only_if' or
319
+ @value = 'not_if']) = 0]").map do |lp|
320
+ block_vars = lp.xpath('block_var/params/child::*').map do |n|
298
321
  n.name.sub(/^ident/, '')
299
- end + loop.xpath("block_var/params/child::*/descendant::ident").map do |v|
322
+ end + lp.xpath('block_var/params/child::*/descendant::ident').map do |v|
300
323
  v['value']
301
324
  end
302
- find_resources(loop).map do |resource|
325
+ find_resources(lp).map do |resource|
303
326
  # if any of the parameters to the block are used in a condition then we
304
327
  # have a match
305
328
  unless (block_vars &
306
329
  (resource.xpath(%q{descendant::ident[@value='not_if' or
307
330
  @value='only_if']/ancestor::*[self::method_add_block or
308
- self::command][1]/descendant::ident/@value}).map{|a| a.value})).empty?
331
+ self::command][1]/descendant::ident/@value}).map do |a|
332
+ a.value
333
+ end)).empty?
309
334
  c = resource.xpath('command[count(descendant::string_embexpr) = 0]')
335
+ if resource.xpath('command/ident/@value').first.value == 'define'
336
+ next
337
+ end
310
338
  resource unless c.empty? || block_vars.any? do |var|
311
- ! resource.xpath(%Q{command/args_add_block/args_add/
312
- var_ref/ident[@value='#{var}']}).empty?
339
+ !resource.xpath(%Q(command/args_add_block/args_add/
340
+ var_ref/ident[@value='#{var}'])).empty?
313
341
  end
314
342
  end
315
343
  end
@@ -317,8 +345,8 @@ rule "FC022", "Resource condition within loop may not behave as expected" do
317
345
  end
318
346
  end
319
347
 
320
- rule "FC023", "Prefer conditional attributes" do
321
- tags %w{style}
348
+ rule 'FC023', 'Prefer conditional attributes' do
349
+ tags %w(style)
322
350
  recipe do |ast|
323
351
  ast.xpath(%q{//method_add_block[command/ident][count(descendant::ident
324
352
  [@value='only_if' or @value='not_if']) = 0]/ancestor::*[self::if or
@@ -329,24 +357,27 @@ rule "FC023", "Prefer conditional attributes" do
329
357
  end
330
358
  end
331
359
 
332
- rule "FC024", "Consider adding platform equivalents" do
333
- tags %w{portability}
334
- RHEL = %w{amazon centos redhat scientific}
360
+ rule 'FC024', 'Consider adding platform equivalents' do
361
+ tags %w(portability)
362
+ RHEL = %w(amazon centos redhat scientific)
335
363
  recipe do |ast, filename|
336
364
  next if Pathname.new(filename).basename.to_s == 'metadata.rb'
337
365
  metadata_path = Pathname.new(
338
366
  File.join(File.dirname(filename), '..', 'metadata.rb')).cleanpath
339
- md_platforms = if File.exists?(metadata_path)
340
- supported_platforms(read_ast(metadata_path)).map{|p| p[:platform]}
341
- else
342
- []
343
- end
367
+ md_platforms = if File.exist?(metadata_path)
368
+ supported_platforms(read_ast(
369
+ metadata_path)).map { |p| p[:platform] }
370
+ else
371
+ []
372
+ end
344
373
  md_platforms = RHEL if md_platforms.empty?
345
374
 
346
- ['//method_add_arg[fcall/ident/@value="platform?"]/arg_paren/args_add_block',
347
- "//when"].map do |expr|
375
+ ['//method_add_arg[fcall/ident/@value="platform?"]/
376
+ arg_paren/args_add_block',
377
+ '//when'].map do |expr|
348
378
  ast.xpath(expr).map do |whn|
349
- platforms = whn.xpath("args_add/descendant::tstring_content").map do |p|
379
+ platforms = whn.xpath('args_add/
380
+ descendant::tstring_content').map do |p|
350
381
  p['value']
351
382
  end.sort
352
383
  unless platforms.size == 1 || (md_platforms & platforms).empty?
@@ -358,16 +389,17 @@ rule "FC024", "Consider adding platform equivalents" do
358
389
  end
359
390
  end
360
391
 
361
- rule "FC025", "Prefer chef_gem to compile-time gem install" do
362
- tags %w{style deprecated}
363
- applies_to {|version| version >= gem_version("0.10.10")}
392
+ rule 'FC025', 'Prefer chef_gem to compile-time gem install' do
393
+ tags %w(style deprecated)
394
+ applies_to { |version| version >= gem_version('0.10.10') }
364
395
  recipe do |ast|
365
396
  gem_install = ast.xpath("//stmts_add/assign[method_add_block[command/ident/
366
397
  @value='gem_package'][do_block/stmts_add/command[ident/@value='action']
367
398
  [descendant::ident/@value='nothing']]]")
368
399
  gem_install.map do |install|
369
- gem_var = install.xpath("var_field/ident/@value")
370
- unless ast.xpath("//method_add_arg[call/var_ref/ident/@value='#{gem_var}']
400
+ gem_var = install.xpath('var_field/ident/@value')
401
+ unless ast.xpath("//method_add_arg[call/
402
+ var_ref/ident/@value='#{gem_var}']
371
403
  [arg_paren/descendant::ident/@value='install' or
372
404
  arg_paren/descendant::ident/@value='upgrade']").empty?
373
405
  gem_install
@@ -376,34 +408,34 @@ rule "FC025", "Prefer chef_gem to compile-time gem install" do
376
408
  end
377
409
  end
378
410
 
379
- rule "FC026", "Conditional execution block attribute contains only string" do
380
- tags %w{correctness}
381
- applies_to {|version| version >= gem_version("0.7.4")}
411
+ rule 'FC026', 'Conditional execution block attribute contains only string' do
412
+ tags %w(correctness)
413
+ applies_to { |version| version >= gem_version('0.7.4') }
382
414
  recipe do |ast|
383
- find_resources(ast).map{|r| resource_attributes(r)}.map do |resource|
415
+ find_resources(ast).map { |r| resource_attributes(r) }.map do |resource|
384
416
  [resource['not_if'], resource['only_if']]
385
417
  end.flatten.compact.select do |condition|
386
- condition.respond_to?(:xpath) and
387
- ! condition.xpath('descendant::string_literal').empty? and
388
- ! condition.xpath('stmts_add/string_literal').empty? and
418
+ condition.respond_to?(:xpath) &&
419
+ !condition.xpath('descendant::string_literal').empty? &&
420
+ !condition.xpath('stmts_add/string_literal').empty? &&
389
421
  condition.xpath('descendant::stmts_add[count(ancestor::
390
422
  string_literal) = 0]').size == 1
391
423
  end
392
424
  end
393
425
  end
394
426
 
395
- rule "FC027", "Resource sets internal attribute" do
396
- tags %w{correctness}
427
+ rule 'FC027', 'Resource sets internal attribute' do
428
+ tags %w(correctness)
397
429
  recipe do |ast|
398
- find_resources(ast, :type => :service).map do |service|
430
+ find_resources(ast, type: :service).map do |service|
399
431
  service unless (resource_attributes(service).keys &
400
432
  ['enabled', 'running']).empty?
401
433
  end.compact
402
434
  end
403
435
  end
404
436
 
405
- rule "FC028", "Incorrect #platform? usage" do
406
- tags %w{correctness}
437
+ rule 'FC028', 'Incorrect #platform? usage' do
438
+ tags %w(correctness)
407
439
  recipe do |ast|
408
440
  ast.xpath(%q{//*[self::call | self::command_call]
409
441
  [(var_ref|vcall)/ident/@value='node']
@@ -411,44 +443,44 @@ rule "FC028", "Incorrect #platform? usage" do
411
443
  end
412
444
  end
413
445
 
414
- rule "FC029", "No leading cookbook name in recipe metadata" do
415
- tags %w{correctness metadata}
416
- metadata do |ast,filename|
446
+ rule 'FC029', 'No leading cookbook name in recipe metadata' do
447
+ tags %w(correctness metadata)
448
+ metadata do |ast, filename|
417
449
  ast.xpath('//command[ident/@value="recipe"]').map do |declared_recipe|
418
450
  next unless declared_recipe.xpath('count(//vcall|//var_ref)').to_i == 0
419
451
  recipe_name = declared_recipe.xpath('args_add_block/
420
452
  descendant::tstring_content[1]/@value').to_s
421
453
  unless recipe_name.empty? ||
422
454
  recipe_name.split('::').first == cookbook_name(filename.to_s)
423
- declared_recipe
455
+ declared_recipe
424
456
  end
425
457
  end.compact
426
458
  end
427
459
  end
428
460
 
429
- rule "FC030", "Cookbook contains debugger breakpoints" do
430
- tags %w{annoyances}
461
+ rule 'FC030', 'Cookbook contains debugger breakpoints' do
462
+ tags %w(annoyances)
431
463
  def pry_bindings(ast)
432
464
  ast.xpath('//call[(vcall|var_ref)/ident/@value="binding"]
433
465
  [ident/@value="pry"]')
434
466
  end
435
- recipe{|ast| pry_bindings(ast)}
436
- library{|ast| pry_bindings(ast)}
437
- metadata{|ast| pry_bindings(ast)}
438
- template{|ast| pry_bindings(ast)}
467
+ recipe { |ast| pry_bindings(ast) }
468
+ library { |ast| pry_bindings(ast) }
469
+ metadata { |ast| pry_bindings(ast) }
470
+ template { |ast| pry_bindings(ast) }
439
471
  end
440
472
 
441
- rule "FC031", "Cookbook without metadata file" do
442
- tags %w{correctness metadata}
473
+ rule 'FC031', 'Cookbook without metadata file' do
474
+ tags %w(correctness metadata)
443
475
  cookbook do |filename|
444
- if ! File.exists?(File.join(filename, 'metadata.rb'))
476
+ if !File.exist?(File.join(filename, 'metadata.rb'))
445
477
  [file_match(File.join(filename, 'metadata.rb'))]
446
478
  end
447
479
  end
448
480
  end
449
481
 
450
- rule "FC032", "Invalid notification timing" do
451
- tags %w{correctness notifications}
482
+ rule 'FC032', 'Invalid notification timing' do
483
+ tags %w(correctness notifications)
452
484
  recipe do |ast|
453
485
  find_resources(ast).select do |resource|
454
486
  notifications(resource).any? do |notification|
@@ -458,16 +490,16 @@ rule "FC032", "Invalid notification timing" do
458
490
  end
459
491
  end
460
492
 
461
- rule "FC033", "Missing template" do
462
- tags %w{correctness}
463
- recipe do |ast,filename|
464
- find_resources(ast, :type => :template).reject do |resource|
493
+ rule 'FC033', 'Missing template' do
494
+ tags %w(correctness)
495
+ recipe do |ast, filename|
496
+ find_resources(ast, type: :template).reject do |resource|
465
497
  resource_attributes(resource)['local'] ||
466
498
  resource_attributes(resource)['cookbook']
467
499
  end.map do |resource|
468
500
  file = template_file(resource_attributes(resource,
469
- :return_expressions => true))
470
- {:resource => resource, :file => file}
501
+ return_expressions: true))
502
+ { resource: resource, file: file }
471
503
  end.reject do |resource|
472
504
  resource[:file].respond_to?(:xpath)
473
505
  end.select do |resource|
@@ -479,82 +511,85 @@ rule "FC033", "Missing template" do
479
511
  end
480
512
  File.join(relative_path.reverse) == resource[:file]
481
513
  end
482
- end.map{|resource| resource[:resource]}
514
+ end.map { |resource| resource[:resource] }
483
515
  end
484
516
  end
485
517
 
486
- rule "FC034", "Unused template variables" do
487
- tags %w{correctness}
488
- recipe do |ast,filename|
518
+ rule 'FC034', 'Unused template variables' do
519
+ tags %w(correctness)
520
+ recipe do |ast, filename|
489
521
  Array(resource_attributes_by_type(ast)['template']).select do |t|
490
- t['variables'] and t['variables'].respond_to?(:xpath)
522
+ t['variables'] && t['variables'].respond_to?(:xpath)
491
523
  end.map do |resource|
492
524
  all_templates = template_paths(filename)
493
- template_path = all_templates.find do |path|
525
+ template_paths = all_templates.select do |path|
494
526
  File.basename(path) == template_file(resource)
495
527
  end
496
- next unless template_path
528
+ next unless template_paths.any?
497
529
  passed_vars = resource['variables'].xpath(
498
- 'symbol/ident/@value').map{|tv| tv.to_s}
530
+ 'symbol/ident/@value').map { |tv| tv.to_s }
499
531
 
500
- begin
501
- template_vars = templates_included(
502
- all_templates, template_path).map do |template|
532
+ unused_vars_exist = template_paths.all? do |template_path|
533
+ begin
534
+ template_vars = templates_included(
535
+ all_templates, template_path).map do |template|
503
536
  read_ast(template).xpath('//var_ref/ivar/@value').map do |v|
504
537
  v.to_s.sub(/^@/, '')
505
538
  end
506
- end.flatten
507
-
508
- file_match(template_path) unless (passed_vars - template_vars).empty?
509
- rescue RecursedTooFarError
510
- []
539
+ end.flatten
540
+ ! (passed_vars - template_vars).empty?
541
+ rescue RecursedTooFarError
542
+ false
543
+ end
511
544
  end
545
+ file_match(template_paths.first) if unused_vars_exist
512
546
  end.compact
513
547
  end
514
548
  end
515
549
 
516
- rule "FC037", "Invalid notification action" do
517
- tags %w{correctness}
550
+ rule 'FC037', 'Invalid notification action' do
551
+ tags %w(correctness)
518
552
  recipe do |ast|
519
553
  find_resources(ast).select do |resource|
520
554
  notifications(resource).any? do |n|
521
555
  type = case n[:type]
522
- when :notifies then n[:resource_type]
523
- when :subscribes then resource_type(resource).to_sym
524
- end
525
- n[:action].size > 0 and ! resource_action?(type, n[:action])
556
+ when :notifies then n[:resource_type]
557
+ when :subscribes then resource_type(resource).to_sym
558
+ end
559
+ n[:action].size > 0 && !resource_action?(type, n[:action])
526
560
  end
527
561
  end
528
562
  end
529
563
  end
530
564
 
531
- rule "FC038", "Invalid resource action" do
532
- tags %w{correctness}
565
+ rule 'FC038', 'Invalid resource action' do
566
+ tags %w(correctness)
533
567
  recipe do |ast|
534
568
  find_resources(ast).select do |resource|
535
569
  actions = resource_attributes(resource)['action']
536
570
  if actions.respond_to?(:xpath)
537
- actions = actions.xpath('descendant::array/descendant::symbol/ident/@value')
571
+ actions = actions.xpath('descendant::array/
572
+ descendant::symbol/ident/@value')
538
573
  else
539
574
  actions = Array(actions)
540
575
  end
541
- actions.reject{|a| a.to_s.empty?}.any? do |action|
542
- ! resource_action?(resource_type(resource), action)
576
+ actions.reject { |a| a.to_s.empty? }.any? do |action|
577
+ !resource_action?(resource_type(resource), action)
543
578
  end
544
579
  end
545
580
  end
546
581
  end
547
582
 
548
- rule "FC039", "Node method cannot be accessed with key" do
549
- tags %w{correctness}
583
+ rule 'FC039', 'Node method cannot be accessed with key' do
584
+ tags %w(correctness)
550
585
  recipe do |ast|
551
- [{:type => :string, :path => '@value'},
552
- {:type => :symbol, :path => 'ident/@value'}].map do |access_type|
553
- attribute_access(ast, :type => access_type[:type]).select do |att|
586
+ [{ type: :string, path: '@value' },
587
+ { type: :symbol, path: 'ident/@value' }].map do |access_type|
588
+ attribute_access(ast, type: access_type[:type]).select do |att|
554
589
  att_name = att.xpath(access_type[:path]).to_s.to_sym
555
590
  att_name != :tags && chef_node_methods.include?(att_name)
556
591
  end.select do |att|
557
- ! att.xpath('ancestor::args_add_block[position() = 1]
592
+ !att.xpath('ancestor::args_add_block[position() = 1]
558
593
  [preceding-sibling::vcall | preceding-sibling::var_ref]').empty?
559
594
  end.select do |att|
560
595
  att_type = att.xpath('ancestor::args_add_block[position() = 1]
@@ -565,100 +600,103 @@ rule "FC039", "Node method cannot be accessed with key" do
565
600
  end
566
601
  end
567
602
 
568
- rule "FC040", "Execute resource used to run git commands" do
569
- tags %w{style recipe etsy}
603
+ rule 'FC040', 'Execute resource used to run git commands' do
604
+ tags %w(style recipe etsy)
570
605
  recipe do |ast|
571
- possible_git_commands = %w{ clone fetch pull checkout reset }
572
- find_resources(ast, :type => 'execute').select do |cmd|
606
+ possible_git_commands = %w( clone fetch pull checkout reset )
607
+ find_resources(ast, type: 'execute').select do |cmd|
573
608
  cmd_str = (resource_attribute(cmd, 'command') || resource_name(cmd)).to_s
574
609
 
575
- git_cmd = cmd_str.match(/git ([a-z]+)/)
576
- break false if git_cmd.nil?
577
-
578
- !git_cmd.captures.nil? && possible_git_commands.include?(git_cmd.captures[0])
610
+ actual_git_commands = cmd_str.scan(/git ([a-z]+)/).map { |c| c.first }
611
+ (possible_git_commands & actual_git_commands).any?
579
612
  end
580
613
  end
581
614
  end
582
615
 
583
- rule "FC041", "Execute resource used to run curl or wget commands" do
584
- tags %w{style recipe etsy}
616
+ rule 'FC041', 'Execute resource used to run curl or wget commands' do
617
+ tags %w(style recipe etsy)
585
618
  recipe do |ast|
586
- find_resources(ast, :type => 'execute').select do |cmd|
619
+ find_resources(ast, type: 'execute').select do |cmd|
587
620
  cmd_str = (resource_attribute(cmd, 'command') || resource_name(cmd)).to_s
588
621
  (cmd_str.include?('curl ') || cmd_str.include?('wget '))
589
622
  end
590
623
  end
591
624
  end
592
625
 
593
- rule "FC042", "Prefer include_recipe to require_recipe" do
594
- tags %w{deprecated}
626
+ rule 'FC042', 'Prefer include_recipe to require_recipe' do
627
+ tags %w(deprecated)
595
628
  recipe do |ast|
596
629
  ast.xpath('//command[ident/@value="require_recipe"]')
597
630
  end
598
631
  end
599
632
 
600
- rule "FC043", "Prefer new notification syntax" do
601
- tags %w{style notifications deprecated}
602
- applies_to {|version| version >= gem_version("0.9.10")}
633
+ rule 'FC043', 'Prefer new notification syntax' do
634
+ tags %w(style notifications deprecated)
635
+ applies_to { |version| version >= gem_version('0.9.10') }
603
636
  recipe do |ast|
604
637
  find_resources(ast).select do |resource|
605
- notifications(resource).any?{|notify| notify[:style] == :old}
638
+ notifications(resource).any? { |notify| notify[:style] == :old }
606
639
  end
607
640
  end
608
641
  end
609
642
 
610
- rule "FC044", "Avoid bare attribute keys" do
611
- tags %w{style}
643
+ rule 'FC044', 'Avoid bare attribute keys' do
644
+ tags %w(style)
612
645
  attributes do |ast|
613
- declared = ast.xpath('//descendant::var_field/ident/@value').map{|v| v.to_s}
646
+ declared = ast.xpath('//descendant::var_field/ident/@value').map do |v|
647
+ v.to_s
648
+ end
614
649
  ast.xpath('//assign/*[self::vcall or self::var_ref]
615
- [count(child::kw) = 0]/ident').select do |v|
616
- (v['value'] != 'secure_password') &&
617
- ! declared.include?(v['value']) &&
618
- ! v.xpath("ancestor::*[self::brace_block or self::do_block]/block_var/
619
- descendant::ident/@value='#{v['value']}'")
650
+ [count(child::kw) = 0]/ident').select do |v|
651
+ (v['value'] != 'secure_password') &&
652
+ !declared.include?(v['value']) &&
653
+ !v.xpath("ancestor::*[self::brace_block or self::do_block]/block_var/
654
+ descendant::ident/@value='#{v['value']}'")
620
655
  end
621
656
  end
622
657
  end
623
658
 
624
- rule "FC045", "Consider setting cookbook name in metadata" do
625
- tags %w{annoyances metadata}
659
+ rule 'FC045', 'Consider setting cookbook name in metadata' do
660
+ tags %w(annoyances metadata)
626
661
  metadata do |ast, filename|
627
662
  unless ast.xpath('descendant::stmts_add/command/ident/@value="name"')
628
663
  [file_match(filename)]
629
664
  end
630
665
  end
631
666
  cookbook do |filename|
632
- if ! File.exists?(File.join(filename, 'metadata.rb'))
667
+ if !File.exist?(File.join(filename, 'metadata.rb'))
633
668
  [file_match(File.join(filename, 'metadata.rb'))]
634
669
  end
635
670
  end
636
671
  end
637
672
 
638
- rule "FC046", "Attribute assignment uses assign unless nil" do
673
+ rule 'FC046', 'Attribute assignment uses assign unless nil' do
639
674
  attributes do |ast|
640
- attribute_access(ast).map{|a| a.xpath('ancestor::opassign/op[@value="||="]')}
675
+ attribute_access(ast).map do |a|
676
+ a.xpath('ancestor::opassign/op[@value="||="]')
677
+ end
641
678
  end
642
679
  end
643
680
 
644
- rule "FC047", "Attribute assignment does not specify precedence" do
645
- tags %w{attributes correctness}
681
+ rule 'FC047', 'Attribute assignment does not specify precedence' do
682
+ tags %w(attributes correctness)
646
683
  recipe do |ast|
647
684
  attribute_access(ast).map do |att|
648
685
  exclude_att_types = '[count(following-sibling::ident[
649
686
  is_att_type(@value) or @value = "run_state"]) = 0]'
650
- att.xpath(%Q{ancestor::assign[*[self::field | self::aref_field]
687
+ att.xpath(%Q(ancestor::assign[*[self::field | self::aref_field]
651
688
  [descendant::*[self::vcall | self::var_ref][ident/@value="node"]
652
- #{exclude_att_types}]]}, AttFilter.new) +
653
- att.xpath(%Q{ancestor::binary[@value="<<"]/*[position() = 1][self::aref]
689
+ #{exclude_att_types}]]), AttFilter.new) +
690
+ att.xpath(%Q{ancestor::binary[@value="<<"]/*[position() = 1]
691
+ [self::aref]
654
692
  [descendant::*[self::vcall | self::var_ref]#{exclude_att_types}
655
693
  /ident/@value="node"]}, AttFilter.new)
656
694
  end
657
695
  end
658
696
  end
659
697
 
660
- rule "FC048", "Prefer Mixlib::ShellOut" do
661
- tags %w{style processes}
698
+ rule 'FC048', 'Prefer Mixlib::ShellOut' do
699
+ tags %w(style processes)
662
700
  recipe do |ast|
663
701
  ast.xpath('//xstring_literal | //*[self::command or self::fcall]/
664
702
  ident[@value="system"][count(following-sibling::args_add_block/
@@ -666,29 +704,29 @@ rule "FC048", "Prefer Mixlib::ShellOut" do
666
704
  end
667
705
  end
668
706
 
669
- rule "FC049", "Role name does not match containing file name" do
670
- tags %w{style roles}
707
+ rule 'FC049', 'Role name does not match containing file name' do
708
+ tags %w(style roles)
671
709
  role do |ast, filename|
672
710
  role_name_specified = field_value(ast, :name)
673
711
  role_name_file = Pathname.new(filename).basename.sub_ext('').to_s
674
- if role_name_specified and role_name_specified != role_name_file
712
+ if role_name_specified && role_name_specified != role_name_file
675
713
  field(ast, :name)
676
714
  end
677
715
  end
678
716
  end
679
717
 
680
- rule "FC050", "Name includes invalid characters" do
681
- tags %w{correctness environments roles}
718
+ rule 'FC050', 'Name includes invalid characters' do
719
+ tags %w(correctness environments roles)
682
720
  def invalid_name(ast)
683
721
  field(ast, :name) unless field_value(ast, :name) =~ /^[a-zA-Z0-9_\-]+$/
684
722
  end
685
- environment{|ast| invalid_name(ast)}
686
- role{|ast| invalid_name(ast)}
723
+ environment { |ast| invalid_name(ast) }
724
+ role { |ast| invalid_name(ast) }
687
725
  end
688
726
 
689
- rule "FC051", "Template partials loop indefinitely" do
690
- tags %w{correctness}
691
- recipe do |_,filename|
727
+ rule 'FC051', 'Template partials loop indefinitely' do
728
+ tags %w(correctness)
729
+ recipe do |_, filename|
692
730
  cbk_templates = template_paths(filename)
693
731
 
694
732
  cbk_templates.select do |template|
@@ -698,6 +736,6 @@ rule "FC051", "Template partials loop indefinitely" do
698
736
  rescue RecursedTooFarError
699
737
  true
700
738
  end
701
- end.map{|t| file_match(t)}
739
+ end.map { |t| file_match(t) }
702
740
  end
703
741
  end