foodcritic 7.0.1 → 7.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/Gemfile +10 -10
- data/Rakefile +21 -21
- data/bin/foodcritic +1 -1
- data/chef_dsl_metadata/chef_12.12.13.json +17809 -0
- data/chef_dsl_metadata/chef_12.13.37.json +18021 -0
- data/features/step_definitions/cookbook_steps.rb +456 -457
- data/features/support/command_helpers.rb +120 -120
- data/features/support/cookbook_helpers.rb +87 -87
- data/features/support/env.rb +6 -6
- data/foodcritic.gemspec +22 -22
- data/lib/foodcritic.rb +20 -20
- data/lib/foodcritic/api.rb +94 -91
- data/lib/foodcritic/chef.rb +14 -14
- data/lib/foodcritic/command_line.rb +35 -35
- data/lib/foodcritic/domain.rb +4 -4
- data/lib/foodcritic/dsl.rb +1 -2
- data/lib/foodcritic/linter.rb +31 -32
- data/lib/foodcritic/notifications.rb +5 -5
- data/lib/foodcritic/output.rb +5 -5
- data/lib/foodcritic/rake_task.rb +7 -7
- data/lib/foodcritic/rules.rb +234 -234
- data/lib/foodcritic/template.rb +1 -1
- data/lib/foodcritic/version.rb +1 -1
- data/lib/foodcritic/xml.rb +5 -5
- data/spec/foodcritic/api_spec.rb +275 -275
- data/spec/foodcritic/chef_spec.rb +11 -11
- data/spec/foodcritic/command_line_spec.rb +7 -7
- data/spec/foodcritic/domain_spec.rb +20 -20
- data/spec/foodcritic/linter_spec.rb +10 -11
- data/spec/foodcritic/template_spec.rb +8 -8
- data/spec/regression/regression_spec.rb +3 -3
- data/spec/regression_helpers.rb +10 -10
- data/spec/spec_helper.rb +6 -6
- metadata +5 -3
data/lib/foodcritic/output.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "set"
|
2
2
|
|
3
3
|
module FoodCritic
|
4
4
|
# Default output showing a summary view.
|
@@ -62,10 +62,10 @@ module FoodCritic
|
|
62
62
|
|
63
63
|
# Display any relevant lines
|
64
64
|
if warn_lines.include? file.lineno
|
65
|
-
print
|
65
|
+
print "%4i|" % file.lineno
|
66
66
|
print_line.call line.chomp
|
67
67
|
elsif not context_warns.empty?
|
68
|
-
print
|
68
|
+
print "%4i|" % file.lineno
|
69
69
|
puts line.chomp
|
70
70
|
end
|
71
71
|
end
|
@@ -106,8 +106,8 @@ module FoodCritic
|
|
106
106
|
return
|
107
107
|
end
|
108
108
|
|
109
|
-
colors = %w
|
110
|
-
attrs = %w
|
109
|
+
colors = %w{black red green yellow blue magenta cyan white}
|
110
|
+
attrs = %w{reset bold dim underscore blink reverse hidden}
|
111
111
|
escape = "\033[%sm"
|
112
112
|
fmt = []
|
113
113
|
fmt << 30 + colors.index(fg.to_s) if fg
|
data/lib/foodcritic/rake_task.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "rake"
|
2
|
+
require "rake/tasklib"
|
3
3
|
|
4
4
|
module FoodCritic
|
5
5
|
module Rake
|
@@ -14,9 +14,8 @@ module FoodCritic
|
|
14
14
|
define
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
17
|
def define
|
19
|
-
desc
|
18
|
+
desc "Lint Chef cookbooks" unless ::Rake.application.last_description
|
20
19
|
task(name) do
|
21
20
|
puts "Starting Foodcritic linting..."
|
22
21
|
result = FoodCritic::Linter.new.check(default_options.merge(options))
|
@@ -28,13 +27,14 @@ module FoodCritic
|
|
28
27
|
end
|
29
28
|
|
30
29
|
private
|
30
|
+
|
31
31
|
def default_options
|
32
32
|
{
|
33
|
-
fail_tags: [
|
33
|
+
fail_tags: ["correctness"], # differs to default cmd-line behaviour
|
34
34
|
cookbook_paths: files,
|
35
|
-
exclude_paths: [
|
35
|
+
exclude_paths: ["test/**/*", "spec/**/*", "features/**/*"],
|
36
36
|
context: false,
|
37
|
-
chef_version: FoodCritic::Linter::DEFAULT_CHEF_VERSION
|
37
|
+
chef_version: FoodCritic::Linter::DEFAULT_CHEF_VERSION,
|
38
38
|
}
|
39
39
|
end
|
40
40
|
end
|
data/lib/foodcritic/rules.rb
CHANGED
@@ -9,16 +9,16 @@
|
|
9
9
|
# query only, as any nodes returned from a `recipe` block will be converted
|
10
10
|
# into warnings.
|
11
11
|
|
12
|
-
rule
|
13
|
-
|
14
|
-
tags %w
|
12
|
+
rule "FC001",
|
13
|
+
"Use strings in preference to symbols to access node attributes" do
|
14
|
+
tags %w{style attributes}
|
15
15
|
recipe do |ast|
|
16
16
|
attribute_access(ast, type: :symbol)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
rule
|
21
|
-
tags %w
|
20
|
+
rule "FC002", "Avoid string interpolation where not required" do
|
21
|
+
tags %w{style strings}
|
22
22
|
recipe do |ast|
|
23
23
|
ast.xpath(%q{//*[self::string_literal | self::assoc_new]/string_add[
|
24
24
|
count(descendant::string_embexpr) = 1 and
|
@@ -26,10 +26,10 @@ rule 'FC002', 'Avoid string interpolation where not required' do
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
rule
|
30
|
-
|
31
|
-
|
32
|
-
tags %w
|
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
33
|
recipe do |ast, filename|
|
34
34
|
unless checks_for_chef_solo?(ast) || chef_solo_search_supported?(filename)
|
35
35
|
searches(ast)
|
@@ -37,32 +37,32 @@ rule 'FC003',
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
rule
|
41
|
-
tags %w
|
40
|
+
rule "FC004", "Use a service resource to start and stop services" do
|
41
|
+
tags %w{style services}
|
42
42
|
recipe do |ast|
|
43
|
-
find_resources(ast, type:
|
44
|
-
cmd_str = (resource_attribute(cmd,
|
45
|
-
(cmd_str.include?(
|
46
|
-
|
47
|
-
|
48
|
-
|
43
|
+
find_resources(ast, type: "execute").find_all do |cmd|
|
44
|
+
cmd_str = (resource_attribute(cmd, "command") || resource_name(cmd)).to_s
|
45
|
+
(cmd_str.include?("/etc/init.d") || ["service ", "/sbin/service ",
|
46
|
+
"start ", "stop ", "invoke-rc.d "].any? do |service_cmd|
|
47
|
+
cmd_str.start_with?(service_cmd)
|
48
|
+
end) && %w{start stop restart reload}.any? { |a| cmd_str.include?(a) }
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
rule
|
54
|
-
tags %w
|
53
|
+
rule "FC005", "Avoid repetition of resource declarations" do
|
54
|
+
tags %w{style}
|
55
55
|
recipe do |ast|
|
56
56
|
resources = find_resources(ast).map do |res|
|
57
57
|
resource_attributes(res).merge({ type: resource_type(res),
|
58
|
-
|
58
|
+
ast: res })
|
59
59
|
end.chunk do |res|
|
60
60
|
res[:type] +
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
61
|
+
res[:ast].xpath("ancestor::*[self::if | self::unless | self::elsif |
|
62
|
+
self::else | self::when | self::method_add_block/call][position() = 1]/
|
63
|
+
descendant::pos[position() = 1]").to_s +
|
64
|
+
res[:ast].xpath("ancestor::method_add_block/command[
|
65
|
+
ident/@value='action']/args_add_block/descendant::ident/@value").to_s
|
66
66
|
end.reject { |res| res[1].size < 3 }
|
67
67
|
resources.map do |cont_res|
|
68
68
|
first_resource = cont_res[1][0][:ast]
|
@@ -80,10 +80,10 @@ rule 'FC005', 'Avoid repetition of resource declarations' do
|
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
83
|
-
rule
|
84
|
-
|
85
|
-
|
86
|
-
tags %w
|
83
|
+
rule "FC006",
|
84
|
+
"Mode should be quoted or fully specified when "\
|
85
|
+
"setting file permissions" do
|
86
|
+
tags %w{correctness files}
|
87
87
|
recipe do |ast|
|
88
88
|
ast.xpath(%q{//ident[@value='mode']/parent::command/
|
89
89
|
descendant::int[string-length(@value) < 5
|
@@ -93,42 +93,42 @@ rule 'FC006',
|
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
|
-
rule
|
97
|
-
|
98
|
-
tags %w
|
96
|
+
rule "FC007", "Ensure recipe dependencies are reflected "\
|
97
|
+
"in cookbook metadata" do
|
98
|
+
tags %w{correctness metadata}
|
99
99
|
recipe do |ast, filename|
|
100
100
|
metadata_path = Pathname.new(
|
101
|
-
File.join(File.dirname(filename),
|
101
|
+
File.join(File.dirname(filename), "..", "metadata.rb")).cleanpath
|
102
102
|
next unless File.exist? metadata_path
|
103
103
|
actual_included = included_recipes(ast, with_partial_names: false)
|
104
104
|
undeclared = actual_included.keys.map do |recipe|
|
105
|
-
recipe.split(
|
105
|
+
recipe.split("::").first
|
106
106
|
end - [cookbook_name(filename)] -
|
107
|
-
|
107
|
+
declared_dependencies(read_ast(metadata_path))
|
108
108
|
actual_included.map do |recipe, include_stmts|
|
109
109
|
if undeclared.include?(recipe) ||
|
110
|
-
|
110
|
+
undeclared.any? { |u| recipe.start_with?("#{u}::") }
|
111
111
|
include_stmts
|
112
112
|
end
|
113
113
|
end.flatten.compact
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
117
|
-
rule
|
118
|
-
tags %w
|
117
|
+
rule "FC008", "Generated cookbook metadata needs updating" do
|
118
|
+
tags %w{style metadata}
|
119
119
|
metadata do |ast, filename|
|
120
120
|
{
|
121
|
-
|
122
|
-
|
121
|
+
"maintainer" => "YOUR_COMPANY_NAME",
|
122
|
+
"maintainer_email" => "YOUR_EMAIL",
|
123
123
|
}.map do |field, value|
|
124
|
-
ast.xpath(%Q
|
125
|
-
descendant::tstring_content[@value='#{value}'])
|
124
|
+
ast.xpath(%Q{//command[ident/@value='#{field}']/
|
125
|
+
descendant::tstring_content[@value='#{value}']})
|
126
126
|
end
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
130
|
-
rule
|
131
|
-
tags %w
|
130
|
+
rule "FC009", "Resource attribute not recognised" do
|
131
|
+
tags %w{correctness}
|
132
132
|
recipe do |ast|
|
133
133
|
matches = []
|
134
134
|
resource_attributes_by_type(ast).each do |type, resources|
|
@@ -146,47 +146,47 @@ rule 'FC009', 'Resource attribute not recognised' do
|
|
146
146
|
end
|
147
147
|
end
|
148
148
|
|
149
|
-
rule
|
150
|
-
tags %w
|
149
|
+
rule "FC010", "Invalid search syntax" do
|
150
|
+
tags %w{correctness search}
|
151
151
|
recipe do |ast|
|
152
152
|
# This only works for literal search strings
|
153
|
-
literal_searches(ast).reject { |search| valid_query?(search[
|
153
|
+
literal_searches(ast).reject { |search| valid_query?(search["value"]) }
|
154
154
|
end
|
155
155
|
end
|
156
156
|
|
157
|
-
rule
|
158
|
-
tags %w
|
157
|
+
rule "FC011", "Missing README in markdown format" do
|
158
|
+
tags %w{style readme}
|
159
159
|
cookbook do |filename|
|
160
|
-
unless File.exist?(File.join(filename,
|
161
|
-
[file_match(File.join(filename,
|
160
|
+
unless File.exist?(File.join(filename, "README.md"))
|
161
|
+
[file_match(File.join(filename, "README.md"))]
|
162
162
|
end
|
163
163
|
end
|
164
164
|
end
|
165
165
|
|
166
|
-
rule
|
167
|
-
tags %w
|
166
|
+
rule "FC012", "Use Markdown for README rather than RDoc" do
|
167
|
+
tags %w{style readme}
|
168
168
|
cookbook do |filename|
|
169
|
-
if File.exist?(File.join(filename,
|
170
|
-
[file_match(File.join(filename,
|
169
|
+
if File.exist?(File.join(filename, "README.rdoc"))
|
170
|
+
[file_match(File.join(filename, "README.rdoc"))]
|
171
171
|
end
|
172
172
|
end
|
173
173
|
end
|
174
174
|
|
175
|
-
rule
|
176
|
-
tags %w
|
175
|
+
rule "FC013", "Use file_cache_path rather than hard-coding tmp paths" do
|
176
|
+
tags %w{style files}
|
177
177
|
recipe do |ast|
|
178
|
-
find_resources(ast, type:
|
179
|
-
path = (resource_attribute(download,
|
178
|
+
find_resources(ast, type: "remote_file").find_all do |download|
|
179
|
+
path = (resource_attribute(download, "path") ||
|
180
180
|
resource_name(download)).to_s
|
181
|
-
path.start_with?(
|
181
|
+
path.start_with?("/tmp/")
|
182
182
|
end
|
183
183
|
end
|
184
184
|
end
|
185
185
|
|
186
|
-
rule
|
187
|
-
tags %w
|
186
|
+
rule "FC014", "Consider extracting long ruby_block to library" do
|
187
|
+
tags %w{style libraries}
|
188
188
|
recipe do |ast|
|
189
|
-
find_resources(ast, type:
|
189
|
+
find_resources(ast, type: "ruby_block").find_all do |rb|
|
190
190
|
lines = rb.xpath("descendant::fcall[ident/@value='block']/../../
|
191
191
|
descendant::*[@line]/@line").map { |n| n.value.to_i }.sort
|
192
192
|
(!lines.empty?) && (lines.last - lines.first) > 15
|
@@ -194,17 +194,17 @@ rule 'FC014', 'Consider extracting long ruby_block to library' do
|
|
194
194
|
end
|
195
195
|
end
|
196
196
|
|
197
|
-
rule
|
198
|
-
tags %w
|
197
|
+
rule "FC015", "Consider converting definition to a Custom Resource" do
|
198
|
+
tags %w{style definitions lwrp}
|
199
199
|
cookbook do |dir|
|
200
|
-
Dir[File.join(dir,
|
201
|
-
[
|
200
|
+
Dir[File.join(dir, "definitions", "*.rb")].reject do |entry|
|
201
|
+
[".", ".."].include? entry
|
202
202
|
end.map { |entry| file_match(entry) }
|
203
203
|
end
|
204
204
|
end
|
205
205
|
|
206
|
-
rule
|
207
|
-
tags %w
|
206
|
+
rule "FC016", "LWRP does not declare a default action" do
|
207
|
+
tags %w{correctness lwrp}
|
208
208
|
resource do |ast, filename|
|
209
209
|
unless ["//ident/@value='default_action'",
|
210
210
|
"//def/bodystmt/descendant::assign/
|
@@ -214,8 +214,8 @@ rule 'FC016', 'LWRP does not declare a default action' do
|
|
214
214
|
end
|
215
215
|
end
|
216
216
|
|
217
|
-
rule
|
218
|
-
tags %w
|
217
|
+
rule "FC017", "LWRP does not notify when updated" do
|
218
|
+
tags %w{correctness lwrp}
|
219
219
|
provider do |ast, filename|
|
220
220
|
|
221
221
|
use_inline_resources = !ast.xpath('//*[self::vcall or self::var_ref]/ident
|
@@ -228,7 +228,7 @@ rule 'FC017', 'LWRP does not notify when updated' do
|
|
228
228
|
actions.reject do |action|
|
229
229
|
blk = action.xpath('ancestor::command[1]/
|
230
230
|
following-sibling::*[self::do_block or self::brace_block]')
|
231
|
-
empty = !blk.xpath(
|
231
|
+
empty = !blk.xpath("stmts_add/void_stmt").empty?
|
232
232
|
converge_by = !blk.xpath('descendant::*[self::command or self::fcall]
|
233
233
|
/ident[@value="converge_by"]').empty?
|
234
234
|
|
@@ -244,8 +244,8 @@ rule 'FC017', 'LWRP does not notify when updated' do
|
|
244
244
|
end
|
245
245
|
end
|
246
246
|
|
247
|
-
rule
|
248
|
-
tags %w
|
247
|
+
rule "FC018", "LWRP uses deprecated notification syntax" do
|
248
|
+
tags %w{style lwrp deprecated}
|
249
249
|
provider do |ast|
|
250
250
|
ast.xpath("//assign/var_field/ivar[@value='@updated']").map do |class_var|
|
251
251
|
match(class_var)
|
@@ -254,13 +254,13 @@ rule 'FC018', 'LWRP uses deprecated notification syntax' do
|
|
254
254
|
end
|
255
255
|
end
|
256
256
|
|
257
|
-
rule
|
258
|
-
tags %w
|
257
|
+
rule "FC019", "Access node attributes in a consistent manner" do
|
258
|
+
tags %w{style attributes}
|
259
259
|
cookbook do |cookbook_dir|
|
260
260
|
asts = {}; files = Dir["#{cookbook_dir}/*/*.rb"].reject do |file|
|
261
261
|
relative_path = Pathname.new(file).relative_path_from(
|
262
262
|
Pathname.new(cookbook_dir))
|
263
|
-
relative_path.to_s.split(File::SEPARATOR).include?(
|
263
|
+
relative_path.to_s.split(File::SEPARATOR).include?("spec")
|
264
264
|
end.map do |file|
|
265
265
|
{ path: file, ast: read_ast(file) }
|
266
266
|
end
|
@@ -268,7 +268,7 @@ rule 'FC019', 'Access node attributes in a consistent manner' do
|
|
268
268
|
{
|
269
269
|
access_type: type, count: files.map do |file|
|
270
270
|
attribute_access(file[:ast], type: type, ignore_calls: true,
|
271
|
-
|
271
|
+
cookbook_dir: cookbook_dir, ignore: "run_state").tap do |ast|
|
272
272
|
unless ast.empty?
|
273
273
|
(asts[type] ||= []) << { ast: ast, path: file[:path] }
|
274
274
|
end
|
@@ -289,8 +289,8 @@ rule 'FC019', 'Access node attributes in a consistent manner' do
|
|
289
289
|
end
|
290
290
|
end
|
291
291
|
|
292
|
-
rule
|
293
|
-
tags %w
|
292
|
+
rule "FC021", "Resource condition in provider may not behave as expected" do
|
293
|
+
tags %w{correctness lwrp}
|
294
294
|
provider do |ast|
|
295
295
|
find_resources(ast).map do |resource|
|
296
296
|
condition = resource.xpath(%q{//method_add_block/
|
@@ -303,16 +303,16 @@ rule 'FC021', 'Resource condition in provider may not behave as expected' do
|
|
303
303
|
end
|
304
304
|
end
|
305
305
|
|
306
|
-
rule
|
307
|
-
tags %w
|
306
|
+
rule "FC022", "Resource condition within loop may not behave as expected" do
|
307
|
+
tags %w{correctness}
|
308
308
|
recipe do |ast|
|
309
309
|
ast.xpath("//call[ident/@value='each']/../do_block[count(ancestor::
|
310
310
|
method_add_block/method_add_arg/fcall/ident[@value='only_if' or
|
311
311
|
@value = 'not_if']) = 0]").map do |lp|
|
312
|
-
block_vars = lp.xpath(
|
313
|
-
n.name.sub(/^ident/,
|
314
|
-
end + lp.xpath(
|
315
|
-
v[
|
312
|
+
block_vars = lp.xpath("block_var/params/child::*").map do |n|
|
313
|
+
n.name.sub(/^ident/, "")
|
314
|
+
end + lp.xpath("block_var/params/child::*/descendant::ident").map do |v|
|
315
|
+
v["value"]
|
316
316
|
end
|
317
317
|
find_resources(lp).map do |resource|
|
318
318
|
# if any of the parameters to the block are used in a condition then we
|
@@ -323,13 +323,13 @@ rule 'FC022', 'Resource condition within loop may not behave as expected' do
|
|
323
323
|
self::command][1]/descendant::ident/@value}).map do |a|
|
324
324
|
a.value
|
325
325
|
end)).empty?
|
326
|
-
c = resource.xpath(
|
327
|
-
if resource.xpath(
|
326
|
+
c = resource.xpath("command[count(descendant::string_embexpr) = 0]")
|
327
|
+
if resource.xpath("command/ident/@value").first.value == "define"
|
328
328
|
next
|
329
329
|
end
|
330
330
|
resource unless c.empty? || block_vars.any? do |var|
|
331
|
-
!resource.xpath(%Q
|
332
|
-
var_ref/ident[@value='#{var}'])
|
331
|
+
!resource.xpath(%Q{command/args_add_block/args_add/
|
332
|
+
var_ref/ident[@value='#{var}']}).empty?
|
333
333
|
end
|
334
334
|
end
|
335
335
|
end
|
@@ -337,8 +337,8 @@ rule 'FC022', 'Resource condition within loop may not behave as expected' do
|
|
337
337
|
end
|
338
338
|
end
|
339
339
|
|
340
|
-
rule
|
341
|
-
tags %w
|
340
|
+
rule "FC023", "Prefer conditional attributes" do
|
341
|
+
tags %w{style}
|
342
342
|
recipe do |ast|
|
343
343
|
ast.xpath(%q{//method_add_block[command/ident][count(descendant::ident
|
344
344
|
[@value='only_if' or @value='not_if']) = 0]/ancestor::*[self::if or
|
@@ -349,13 +349,13 @@ rule 'FC023', 'Prefer conditional attributes' do
|
|
349
349
|
end
|
350
350
|
end
|
351
351
|
|
352
|
-
rule
|
353
|
-
tags %w
|
354
|
-
RHEL = %w
|
352
|
+
rule "FC024", "Consider adding platform equivalents" do
|
353
|
+
tags %w{portability}
|
354
|
+
RHEL = %w{amazon centos redhat scientific oracle}
|
355
355
|
recipe do |ast, filename|
|
356
|
-
next if Pathname.new(filename).basename.to_s ==
|
356
|
+
next if Pathname.new(filename).basename.to_s == "metadata.rb"
|
357
357
|
metadata_path = Pathname.new(
|
358
|
-
File.join(File.dirname(filename),
|
358
|
+
File.join(File.dirname(filename), "..", "metadata.rb")).cleanpath
|
359
359
|
md_platforms = if File.exist?(metadata_path)
|
360
360
|
supported_platforms(read_ast(
|
361
361
|
metadata_path)).map { |p| p[:platform] }
|
@@ -366,29 +366,29 @@ rule 'FC024', 'Consider adding platform equivalents' do
|
|
366
366
|
|
367
367
|
['//method_add_arg[fcall/ident/@value="platform?"]/
|
368
368
|
arg_paren/args_add_block',
|
369
|
-
|
369
|
+
"//when"].map do |expr|
|
370
370
|
ast.xpath(expr).map do |whn|
|
371
371
|
platforms = whn.xpath('args_add/
|
372
372
|
descendant::tstring_content').map do |p|
|
373
|
-
p[
|
373
|
+
p["value"]
|
374
374
|
end.sort
|
375
375
|
unless platforms.size == 1 || (md_platforms & platforms).empty?
|
376
376
|
whn unless (platforms & RHEL).empty? ||
|
377
|
-
|
377
|
+
((md_platforms & RHEL) - (platforms & RHEL)).empty?
|
378
378
|
end
|
379
379
|
end.compact
|
380
380
|
end.flatten
|
381
381
|
end
|
382
382
|
end
|
383
383
|
|
384
|
-
rule
|
385
|
-
tags %w
|
384
|
+
rule "FC025", "Prefer chef_gem to compile-time gem install" do
|
385
|
+
tags %w{style deprecated}
|
386
386
|
recipe do |ast|
|
387
387
|
gem_install = ast.xpath("//stmts_add/assign[method_add_block[command/ident/
|
388
388
|
@value='gem_package'][do_block/stmts_add/command[ident/@value='action']
|
389
389
|
[descendant::ident/@value='nothing']]]")
|
390
390
|
gem_install.map do |install|
|
391
|
-
gem_var = install.xpath(
|
391
|
+
gem_var = install.xpath("var_field/ident/@value")
|
392
392
|
unless ast.xpath("//method_add_arg[call/
|
393
393
|
var_ref/ident/@value='#{gem_var}']
|
394
394
|
[arg_paren/descendant::ident/@value='install' or
|
@@ -399,33 +399,33 @@ rule 'FC025', 'Prefer chef_gem to compile-time gem install' do
|
|
399
399
|
end
|
400
400
|
end
|
401
401
|
|
402
|
-
rule
|
403
|
-
tags %w
|
402
|
+
rule "FC026", "Conditional execution block attribute contains only string" do
|
403
|
+
tags %w{correctness}
|
404
404
|
recipe do |ast|
|
405
405
|
find_resources(ast).map { |r| resource_attributes(r) }.map do |resource|
|
406
|
-
[resource[
|
406
|
+
[resource["not_if"], resource["only_if"]]
|
407
407
|
end.flatten.compact.select do |condition|
|
408
408
|
condition.respond_to?(:xpath) &&
|
409
|
-
|
410
|
-
!condition.xpath(
|
409
|
+
!condition.xpath("descendant::string_literal").empty? &&
|
410
|
+
!condition.xpath("stmts_add/string_literal").empty? &&
|
411
411
|
condition.xpath('descendant::stmts_add[count(ancestor::
|
412
412
|
string_literal) = 0]').size == 1
|
413
413
|
end
|
414
414
|
end
|
415
415
|
end
|
416
416
|
|
417
|
-
rule
|
418
|
-
tags %w
|
417
|
+
rule "FC027", "Resource sets internal attribute" do
|
418
|
+
tags %w{correctness}
|
419
419
|
recipe do |ast|
|
420
420
|
find_resources(ast, type: :service).map do |service|
|
421
421
|
service unless (resource_attributes(service).keys &
|
422
|
-
|
422
|
+
%w{enabled running}).empty?
|
423
423
|
end.compact
|
424
424
|
end
|
425
425
|
end
|
426
426
|
|
427
|
-
rule
|
428
|
-
tags %w
|
427
|
+
rule "FC028", 'Incorrect #platform? usage' do
|
428
|
+
tags %w{correctness}
|
429
429
|
recipe do |ast|
|
430
430
|
ast.xpath(%q{//*[self::call | self::command_call]
|
431
431
|
[(var_ref|vcall)/ident/@value='node']
|
@@ -433,23 +433,23 @@ rule 'FC028', 'Incorrect #platform? usage' do
|
|
433
433
|
end
|
434
434
|
end
|
435
435
|
|
436
|
-
rule
|
437
|
-
tags %w
|
436
|
+
rule "FC029", "No leading cookbook name in recipe metadata" do
|
437
|
+
tags %w{correctness metadata}
|
438
438
|
metadata do |ast, filename|
|
439
439
|
ast.xpath('//command[ident/@value="recipe"]').map do |declared_recipe|
|
440
|
-
next unless declared_recipe.xpath(
|
440
|
+
next unless declared_recipe.xpath("count(//vcall|//var_ref)").to_i == 0
|
441
441
|
recipe_name = declared_recipe.xpath('args_add_block/
|
442
442
|
descendant::tstring_content[1]/@value').to_s
|
443
443
|
unless recipe_name.empty? ||
|
444
|
-
|
444
|
+
recipe_name.split("::").first == cookbook_name(filename.to_s)
|
445
445
|
declared_recipe
|
446
446
|
end
|
447
447
|
end.compact
|
448
448
|
end
|
449
449
|
end
|
450
450
|
|
451
|
-
rule
|
452
|
-
tags %w
|
451
|
+
rule "FC030", "Cookbook contains debugger breakpoints" do
|
452
|
+
tags %w{annoyances}
|
453
453
|
def pry_bindings(ast)
|
454
454
|
ast.xpath('//call[(vcall|var_ref)/ident/@value="binding"]
|
455
455
|
[ident/@value="pry"]')
|
@@ -460,22 +460,22 @@ rule 'FC030', 'Cookbook contains debugger breakpoints' do
|
|
460
460
|
template { |ast| pry_bindings(ast) }
|
461
461
|
end
|
462
462
|
|
463
|
-
rule
|
464
|
-
tags %w
|
463
|
+
rule "FC031", "Cookbook without metadata file" do
|
464
|
+
tags %w{correctness metadata}
|
465
465
|
cookbook do |filename|
|
466
|
-
if !File.exist?(File.join(filename,
|
467
|
-
[file_match(File.join(filename,
|
466
|
+
if !File.exist?(File.join(filename, "metadata.rb"))
|
467
|
+
[file_match(File.join(filename, "metadata.rb"))]
|
468
468
|
end
|
469
469
|
end
|
470
470
|
end
|
471
471
|
|
472
|
-
rule
|
473
|
-
tags %w
|
472
|
+
rule "FC032", "Invalid notification timing" do
|
473
|
+
tags %w{correctness notifications}
|
474
474
|
recipe do |ast|
|
475
|
-
valid_timings = if resource_attribute?(
|
476
|
-
|
477
|
-
|
478
|
-
|
475
|
+
valid_timings = if resource_attribute?("file", "notifies_before") then
|
476
|
+
[:delayed, :immediate, :before]
|
477
|
+
else
|
478
|
+
[:delayed, :immediate]
|
479
479
|
end
|
480
480
|
find_resources(ast).select do |resource|
|
481
481
|
notifications(resource).any? do |notification|
|
@@ -485,12 +485,12 @@ rule 'FC032', 'Invalid notification timing' do
|
|
485
485
|
end
|
486
486
|
end
|
487
487
|
|
488
|
-
rule
|
489
|
-
tags %w
|
488
|
+
rule "FC033", "Missing template" do
|
489
|
+
tags %w{correctness}
|
490
490
|
recipe do |ast, filename|
|
491
491
|
find_resources(ast, type: :template).reject do |resource|
|
492
|
-
resource_attributes(resource)[
|
493
|
-
resource_attributes(resource)[
|
492
|
+
resource_attributes(resource)["local"] ||
|
493
|
+
resource_attributes(resource)["cookbook"]
|
494
494
|
end.map do |resource|
|
495
495
|
file = template_file(resource_attributes(resource,
|
496
496
|
return_expressions: true))
|
@@ -502,7 +502,7 @@ rule 'FC033', 'Missing template' do
|
|
502
502
|
relative_path = []
|
503
503
|
Pathname.new(path).ascend do |template_path|
|
504
504
|
relative_path << template_path.basename
|
505
|
-
break if template_path.dirname.dirname.basename.to_s ==
|
505
|
+
break if template_path.dirname.dirname.basename.to_s == "templates"
|
506
506
|
end
|
507
507
|
File.join(relative_path.reverse) == resource[:file]
|
508
508
|
end
|
@@ -510,26 +510,26 @@ rule 'FC033', 'Missing template' do
|
|
510
510
|
end
|
511
511
|
end
|
512
512
|
|
513
|
-
rule
|
514
|
-
tags %w
|
513
|
+
rule "FC034", "Unused template variables" do
|
514
|
+
tags %w{correctness}
|
515
515
|
recipe do |ast, filename|
|
516
|
-
Array(resource_attributes_by_type(ast)[
|
517
|
-
t[
|
516
|
+
Array(resource_attributes_by_type(ast)["template"]).select do |t|
|
517
|
+
t["variables"] && t["variables"].respond_to?(:xpath)
|
518
518
|
end.map do |resource|
|
519
519
|
all_templates = template_paths(filename)
|
520
520
|
template_paths = all_templates.select do |path|
|
521
521
|
File.basename(path) == template_file(resource)
|
522
522
|
end
|
523
523
|
next unless template_paths.any?
|
524
|
-
passed_vars = resource[
|
525
|
-
|
524
|
+
passed_vars = resource["variables"].xpath(
|
525
|
+
"symbol/ident/@value").map { |tv| tv.to_s }
|
526
526
|
|
527
527
|
unused_vars_exist = template_paths.all? do |template_path|
|
528
528
|
begin
|
529
529
|
template_vars = templates_included(
|
530
530
|
all_templates, template_path).map do |template|
|
531
|
-
read_ast(template).xpath(
|
532
|
-
v.to_s.sub(/^@/,
|
531
|
+
read_ast(template).xpath("//var_ref/ivar/@value").map do |v|
|
532
|
+
v.to_s.sub(/^@/, "")
|
533
533
|
end
|
534
534
|
end.flatten
|
535
535
|
! (passed_vars - template_vars).empty?
|
@@ -542,8 +542,8 @@ rule 'FC034', 'Unused template variables' do
|
|
542
542
|
end
|
543
543
|
end
|
544
544
|
|
545
|
-
rule
|
546
|
-
tags %w
|
545
|
+
rule "FC037", "Invalid notification action" do
|
546
|
+
tags %w{correctness}
|
547
547
|
recipe do |ast|
|
548
548
|
find_resources(ast).select do |resource|
|
549
549
|
notifications(resource).any? do |n|
|
@@ -557,11 +557,11 @@ rule 'FC037', 'Invalid notification action' do
|
|
557
557
|
end
|
558
558
|
end
|
559
559
|
|
560
|
-
rule
|
561
|
-
tags %w
|
560
|
+
rule "FC038", "Invalid resource action" do
|
561
|
+
tags %w{correctness}
|
562
562
|
recipe do |ast|
|
563
563
|
find_resources(ast).select do |resource|
|
564
|
-
actions = resource_attributes(resource)[
|
564
|
+
actions = resource_attributes(resource)["action"]
|
565
565
|
if actions.respond_to?(:xpath)
|
566
566
|
actions = actions.xpath('descendant::array/
|
567
567
|
descendant::symbol/ident/@value')
|
@@ -575,11 +575,11 @@ rule 'FC038', 'Invalid resource action' do
|
|
575
575
|
end
|
576
576
|
end
|
577
577
|
|
578
|
-
rule
|
579
|
-
tags %w
|
578
|
+
rule "FC039", "Node method cannot be accessed with key" do
|
579
|
+
tags %w{correctness}
|
580
580
|
recipe do |ast|
|
581
|
-
[{ type: :string, path:
|
582
|
-
{ type: :symbol, path:
|
581
|
+
[{ type: :string, path: "@value" },
|
582
|
+
{ type: :symbol, path: "ident/@value" }].map do |access_type|
|
583
583
|
attribute_access(ast, type: access_type[:type]).select do |att|
|
584
584
|
att_name = att.xpath(access_type[:path]).to_s.to_sym
|
585
585
|
att_name != :tags && chef_node_methods.include?(att_name)
|
@@ -595,12 +595,12 @@ rule 'FC039', 'Node method cannot be accessed with key' do
|
|
595
595
|
end
|
596
596
|
end
|
597
597
|
|
598
|
-
rule
|
599
|
-
tags %w
|
598
|
+
rule "FC040", "Execute resource used to run git commands" do
|
599
|
+
tags %w{style recipe etsy}
|
600
600
|
recipe do |ast|
|
601
|
-
possible_git_commands = %w
|
602
|
-
find_resources(ast, type:
|
603
|
-
cmd_str = (resource_attribute(cmd,
|
601
|
+
possible_git_commands = %w{ clone fetch pull checkout reset }
|
602
|
+
find_resources(ast, type: "execute").select do |cmd|
|
603
|
+
cmd_str = (resource_attribute(cmd, "command") || resource_name(cmd)).to_s
|
604
604
|
|
605
605
|
actual_git_commands = cmd_str.scan(/git ([a-z]+)/).map { |c| c.first }
|
606
606
|
(possible_git_commands & actual_git_commands).any?
|
@@ -608,25 +608,25 @@ rule 'FC040', 'Execute resource used to run git commands' do
|
|
608
608
|
end
|
609
609
|
end
|
610
610
|
|
611
|
-
rule
|
612
|
-
tags %w
|
611
|
+
rule "FC041", "Execute resource used to run curl or wget commands" do
|
612
|
+
tags %w{style recipe etsy}
|
613
613
|
recipe do |ast|
|
614
|
-
find_resources(ast, type:
|
615
|
-
cmd_str = (resource_attribute(cmd,
|
616
|
-
(cmd_str.match(/^curl.*(-o|>|--output).*$/) || cmd_str.include?(
|
614
|
+
find_resources(ast, type: "execute").select do |cmd|
|
615
|
+
cmd_str = (resource_attribute(cmd, "command") || resource_name(cmd)).to_s
|
616
|
+
(cmd_str.match(/^curl.*(-o|>|--output).*$/) || cmd_str.include?("wget "))
|
617
617
|
end
|
618
618
|
end
|
619
619
|
end
|
620
620
|
|
621
|
-
rule
|
622
|
-
tags %w
|
621
|
+
rule "FC042", "Prefer include_recipe to require_recipe" do
|
622
|
+
tags %w{deprecated}
|
623
623
|
recipe do |ast|
|
624
624
|
ast.xpath('//command[ident/@value="require_recipe"]')
|
625
625
|
end
|
626
626
|
end
|
627
627
|
|
628
|
-
rule
|
629
|
-
tags %w
|
628
|
+
rule "FC043", "Prefer new notification syntax" do
|
629
|
+
tags %w{style notifications deprecated}
|
630
630
|
recipe do |ast|
|
631
631
|
find_resources(ast).select do |resource|
|
632
632
|
notifications(resource).any? { |notify| notify[:style] == :old }
|
@@ -634,10 +634,10 @@ rule 'FC043', 'Prefer new notification syntax' do
|
|
634
634
|
end
|
635
635
|
end
|
636
636
|
|
637
|
-
rule
|
638
|
-
tags %w
|
637
|
+
rule "FC044", "Avoid bare attribute keys" do
|
638
|
+
tags %w{style}
|
639
639
|
attributes do |ast|
|
640
|
-
declared = ast.xpath(
|
640
|
+
declared = ast.xpath("//descendant::var_field/ident/@value").map do |v|
|
641
641
|
v.to_s
|
642
642
|
end
|
643
643
|
|
@@ -646,32 +646,32 @@ rule 'FC044', 'Avoid bare attribute keys' do
|
|
646
646
|
|
647
647
|
local_declared = v.xpath("ancestor::*[self::brace_block or self::do_block]
|
648
648
|
/block_var/descendant::ident/@value").map do |v|
|
649
|
-
|
650
|
-
|
649
|
+
v.to_s
|
650
|
+
end
|
651
651
|
|
652
|
-
(v[
|
653
|
-
!(declared + local_declared).uniq.include?(v[
|
652
|
+
(v["value"] != "secure_password") &&
|
653
|
+
!(declared + local_declared).uniq.include?(v["value"]) &&
|
654
654
|
!v.xpath("ancestor::*[self::brace_block or self::do_block]/block_var/
|
655
655
|
descendant::ident/@value='#{v['value']}'")
|
656
656
|
end
|
657
657
|
end
|
658
658
|
end
|
659
659
|
|
660
|
-
rule
|
661
|
-
tags %w
|
660
|
+
rule "FC045", "Metadata does not contain cookbook name" do
|
661
|
+
tags %w{correctness metadata chef12}
|
662
662
|
metadata do |ast, filename|
|
663
663
|
unless ast.xpath('descendant::stmts_add/command/ident/@value="name"')
|
664
664
|
[file_match(filename)]
|
665
665
|
end
|
666
666
|
end
|
667
667
|
cookbook do |filename|
|
668
|
-
if !File.exist?(File.join(filename,
|
669
|
-
[file_match(File.join(filename,
|
668
|
+
if !File.exist?(File.join(filename, "metadata.rb"))
|
669
|
+
[file_match(File.join(filename, "metadata.rb"))]
|
670
670
|
end
|
671
671
|
end
|
672
672
|
end
|
673
673
|
|
674
|
-
rule
|
674
|
+
rule "FC046", "Attribute assignment uses assign unless nil" do
|
675
675
|
attributes do |ast|
|
676
676
|
attribute_access(ast).map do |a|
|
677
677
|
a.xpath('ancestor::opassign/op[@value="||="]')
|
@@ -679,15 +679,15 @@ rule 'FC046', 'Attribute assignment uses assign unless nil' do
|
|
679
679
|
end
|
680
680
|
end
|
681
681
|
|
682
|
-
rule
|
683
|
-
tags %w
|
682
|
+
rule "FC047", "Attribute assignment does not specify precedence" do
|
683
|
+
tags %w{attributes correctness chef11}
|
684
684
|
recipe do |ast|
|
685
685
|
attribute_access(ast).map do |att|
|
686
686
|
exclude_att_types = '[count(following-sibling::ident[
|
687
687
|
is_att_type(@value) or @value = "run_state"]) = 0]'
|
688
|
-
att.xpath(%Q
|
688
|
+
att.xpath(%Q{ancestor::assign[*[self::field | self::aref_field]
|
689
689
|
[descendant::*[self::vcall | self::var_ref][ident/@value="node"]
|
690
|
-
#{exclude_att_types}]]
|
690
|
+
#{exclude_att_types}]]}, AttFilter.new) +
|
691
691
|
att.xpath(%Q{ancestor::binary[@value="<<"]/*[position() = 1]
|
692
692
|
[self::aref]
|
693
693
|
[descendant::*[self::vcall | self::var_ref]#{exclude_att_types}
|
@@ -696,33 +696,33 @@ rule 'FC047', 'Attribute assignment does not specify precedence' do
|
|
696
696
|
end
|
697
697
|
end
|
698
698
|
|
699
|
-
rule
|
700
|
-
tags %w
|
699
|
+
rule "FC048", "Prefer Mixlib::ShellOut" do
|
700
|
+
tags %w{style processes}
|
701
701
|
recipe do |ast|
|
702
|
-
xstring_literal = ast.xpath(
|
702
|
+
xstring_literal = ast.xpath("//xstring_literal")
|
703
703
|
next xstring_literal if xstring_literal.any?
|
704
704
|
|
705
705
|
ast.xpath('//*[self::command or self::fcall]/ident[@value="system"]').select do |x|
|
706
|
-
resource_name = x.xpath(
|
707
|
-
next false if resource_name.any? && resource_name.all? { |r| resource_attribute?(r.to_s,
|
706
|
+
resource_name = x.xpath("ancestor::do_block/preceding-sibling::command/ident/@value")
|
707
|
+
next false if resource_name.any? && resource_name.all? { |r| resource_attribute?(r.to_s, "system") }
|
708
708
|
next x.xpath('count(following-sibling::args_add_block/descendant::kw[@value="true" or @value="false"]) = 0')
|
709
709
|
end
|
710
710
|
end
|
711
711
|
end
|
712
712
|
|
713
|
-
rule
|
714
|
-
tags %w
|
713
|
+
rule "FC049", "Role name does not match containing file name" do
|
714
|
+
tags %w{style roles}
|
715
715
|
role do |ast, filename|
|
716
716
|
role_name_specified = field_value(ast, :name)
|
717
|
-
role_name_file = Pathname.new(filename).basename.sub_ext(
|
717
|
+
role_name_file = Pathname.new(filename).basename.sub_ext("").to_s
|
718
718
|
if role_name_specified && role_name_specified != role_name_file
|
719
719
|
field(ast, :name)
|
720
720
|
end
|
721
721
|
end
|
722
722
|
end
|
723
723
|
|
724
|
-
rule
|
725
|
-
tags %w
|
724
|
+
rule "FC050", "Name includes invalid characters" do
|
725
|
+
tags %w{correctness environments roles}
|
726
726
|
def invalid_name(ast)
|
727
727
|
field(ast, :name) unless field_value(ast, :name) =~ /^[a-zA-Z0-9_\-]+$/
|
728
728
|
end
|
@@ -730,8 +730,8 @@ rule 'FC050', 'Name includes invalid characters' do
|
|
730
730
|
role { |ast| invalid_name(ast) }
|
731
731
|
end
|
732
732
|
|
733
|
-
rule
|
734
|
-
tags %w
|
733
|
+
rule "FC051", "Template partials loop indefinitely" do
|
734
|
+
tags %w{correctness}
|
735
735
|
recipe do |_, filename|
|
736
736
|
cbk_templates = template_paths(filename)
|
737
737
|
|
@@ -746,40 +746,40 @@ rule 'FC051', 'Template partials loop indefinitely' do
|
|
746
746
|
end
|
747
747
|
end
|
748
748
|
|
749
|
-
rule
|
750
|
-
tags %w
|
749
|
+
rule "FC052", 'Metadata uses the unimplemented "suggests" keyword' do
|
750
|
+
tags %w{style metadata}
|
751
751
|
metadata do |ast, filename|
|
752
|
-
ast.xpath(%
|
752
|
+
ast.xpath(%q{//command[ident/@value='suggests']})
|
753
753
|
end
|
754
754
|
end
|
755
755
|
|
756
|
-
rule
|
757
|
-
tags %w
|
756
|
+
rule "FC053", 'Metadata uses the unimplemented "recommends" keyword' do
|
757
|
+
tags %w{style metadata}
|
758
758
|
metadata do |ast, filename|
|
759
|
-
ast.xpath(%
|
759
|
+
ast.xpath(%q{//command[ident/@value='recommends']})
|
760
760
|
end
|
761
761
|
end
|
762
762
|
|
763
763
|
# NOTE: FC054 was yanked and should be considered reserved, do not reuse it
|
764
764
|
|
765
|
-
rule
|
766
|
-
tags %w
|
765
|
+
rule "FC055", "Ensure maintainer is set in metadata" do
|
766
|
+
tags %w{correctness metadata}
|
767
767
|
metadata do |ast, filename|
|
768
|
-
[file_match(filename)] unless field(ast,
|
768
|
+
[file_match(filename)] unless field(ast, "maintainer").any?
|
769
769
|
end
|
770
770
|
end
|
771
771
|
|
772
|
-
rule
|
773
|
-
tags %w
|
772
|
+
rule "FC056", "Ensure maintainer_email is set in metadata" do
|
773
|
+
tags %w{correctness metadata}
|
774
774
|
metadata do |ast, filename|
|
775
|
-
[file_match(filename)] unless field(ast,
|
775
|
+
[file_match(filename)] unless field(ast, "maintainer_email").any?
|
776
776
|
end
|
777
777
|
end
|
778
778
|
|
779
|
-
rule
|
780
|
-
tags %w
|
779
|
+
rule "FC057", "Library provider does not declare use_inline_resources" do
|
780
|
+
tags %w{correctness}
|
781
781
|
applies_to do |version|
|
782
|
-
version >= gem_version(
|
782
|
+
version >= gem_version("11.0.0")
|
783
783
|
end
|
784
784
|
library do |ast, filename|
|
785
785
|
ast.xpath('//const_path_ref/const[@value="LWRPBase"]/..//const[@value="Provider"]/../../..').select do |x|
|
@@ -788,23 +788,23 @@ rule 'FC057', 'Library provider does not declare use_inline_resources' do
|
|
788
788
|
end
|
789
789
|
end
|
790
790
|
|
791
|
-
rule
|
792
|
-
tags %w
|
791
|
+
rule "FC058", 'Library provider declares use_inline_resources and declares #action_<name> methods' do
|
792
|
+
tags %w{correctness}
|
793
793
|
applies_to do |version|
|
794
|
-
version >= gem_version(
|
794
|
+
version >= gem_version("11.0.0")
|
795
795
|
end
|
796
796
|
library do |ast, filename|
|
797
797
|
ast.xpath('//const_path_ref/const[@value="LWRPBase"]/..//const[@value="Provider"]/../../..').select do |x|
|
798
798
|
x.xpath('//*[self::vcall or self::var_ref]/ident[@value="use_inline_resources"]').length > 0 &&
|
799
|
-
x.xpath(%
|
799
|
+
x.xpath(%q{//def[ident[contains(@value, 'action_')]]}).length > 0
|
800
800
|
end
|
801
801
|
end
|
802
802
|
end
|
803
803
|
|
804
|
-
rule
|
805
|
-
tags %w
|
804
|
+
rule "FC059", "LWRP provider does not declare use_inline_resources" do
|
805
|
+
tags %w{correctness}
|
806
806
|
applies_to do |version|
|
807
|
-
version >= gem_version(
|
807
|
+
version >= gem_version("11.0.0")
|
808
808
|
end
|
809
809
|
provider do |ast, filename|
|
810
810
|
use_inline_resources = !ast.xpath('//*[self::vcall or self::var_ref]/ident
|
@@ -815,21 +815,21 @@ rule 'FC059', 'LWRP provider does not declare use_inline_resources' do
|
|
815
815
|
end
|
816
816
|
end
|
817
817
|
|
818
|
-
rule
|
819
|
-
tags %w
|
818
|
+
rule "FC060", 'LWRP provider declares use_inline_resources and declares #action_<name> methods' do
|
819
|
+
tags %w{correctness}
|
820
820
|
applies_to do |version|
|
821
|
-
version >= gem_version(
|
821
|
+
version >= gem_version("11.0.0")
|
822
822
|
end
|
823
823
|
provider do |ast, filename|
|
824
824
|
use_inline_resources = !ast.xpath('//*[self::vcall or self::var_ref]/ident
|
825
825
|
[@value="use_inline_resources"]').empty?
|
826
826
|
if use_inline_resources
|
827
|
-
ast.xpath(%
|
827
|
+
ast.xpath(%q{//def[ident[contains(@value, 'action_')]]})
|
828
828
|
end
|
829
829
|
end
|
830
830
|
end
|
831
831
|
|
832
|
-
rule
|
832
|
+
rule "FC061", "Valid cookbook versions are of the form x.y or x.y.z" do
|
833
833
|
tags %w{metadata correctness}
|
834
834
|
metadata do |ast, filename|
|
835
835
|
# matches a version method with a string literal with no interpolation
|
@@ -840,38 +840,38 @@ rule 'FC061', 'Valid cookbook versions are of the form x.y or x.y.z' do
|
|
840
840
|
end
|
841
841
|
end
|
842
842
|
|
843
|
-
rule
|
843
|
+
rule "FC062", "Cookbook should have version metadata" do
|
844
844
|
tags %w{metadata}
|
845
845
|
metadata do |ast, filename|
|
846
|
-
[file_match(filename)] unless field(ast,
|
846
|
+
[file_match(filename)] unless field(ast, "version").any?
|
847
847
|
end
|
848
848
|
end
|
849
849
|
|
850
|
-
rule
|
851
|
-
tags %w
|
850
|
+
rule "FC063", "Cookbook incorrectly depends on itself" do
|
851
|
+
tags %w{metadata correctness}
|
852
852
|
metadata do |ast, filename|
|
853
853
|
name = cookbook_name(filename)
|
854
|
-
ast.xpath(%Q
|
855
|
-
descendant::tstring_content[@value='#{name}'])
|
854
|
+
ast.xpath(%Q{//command[ident/@value='depends']/
|
855
|
+
descendant::tstring_content[@value='#{name}']})
|
856
856
|
end
|
857
857
|
end
|
858
858
|
|
859
|
-
rule
|
860
|
-
tags %w
|
859
|
+
rule "FC064", "Ensure issues_url is set in metadata" do
|
860
|
+
tags %w{metadata supermarket chef12}
|
861
861
|
applies_to do |version|
|
862
|
-
version >= gem_version(
|
862
|
+
version >= gem_version("12.0.0")
|
863
863
|
end
|
864
864
|
metadata do |ast, filename|
|
865
|
-
[file_match(filename)] unless field(ast,
|
865
|
+
[file_match(filename)] unless field(ast, "issues_url").any?
|
866
866
|
end
|
867
867
|
end
|
868
868
|
|
869
|
-
rule
|
870
|
-
tags %w
|
869
|
+
rule "FC065", "Ensure source_url is set in metadata" do
|
870
|
+
tags %w{metadata supermarket chef12}
|
871
871
|
applies_to do |version|
|
872
|
-
version >= gem_version(
|
872
|
+
version >= gem_version("12.0.0")
|
873
873
|
end
|
874
874
|
metadata do |ast, filename|
|
875
|
-
[file_match(filename)] unless field(ast,
|
875
|
+
[file_match(filename)] unless field(ast, "source_url").any?
|
876
876
|
end
|
877
877
|
end
|