foodcritic 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/foodcritic/api.rb +36 -6
- data/lib/foodcritic/command_line.rb +5 -1
- data/lib/foodcritic/domain.rb +2 -1
- data/lib/foodcritic/dsl.rb +7 -0
- data/lib/foodcritic/linter.rb +18 -2
- data/lib/foodcritic/rules.rb +44 -9
- data/lib/foodcritic/version.rb +1 -1
- metadata +4 -4
data/lib/foodcritic/api.rb
CHANGED
|
@@ -117,7 +117,17 @@ module FoodCritic
|
|
|
117
117
|
return [] unless ast.respond_to?(:xpath)
|
|
118
118
|
scope_type = ''
|
|
119
119
|
scope_type = "[@value='#{options[:type]}']" unless options[:type] == :any
|
|
120
|
-
|
|
120
|
+
# XXX: include nested resources (provider actions)
|
|
121
|
+
no_actions = "[command/ident/@value != 'action']"
|
|
122
|
+
ast.xpath("//method_add_block[command/ident#{scope_type}]#{no_actions}")
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Helper to return a comparable version for a string.
|
|
126
|
+
#
|
|
127
|
+
# @param [String] version The version
|
|
128
|
+
# @return [Gem::Version] The comparable version
|
|
129
|
+
def gem_version(version)
|
|
130
|
+
Gem::Version.create(version)
|
|
121
131
|
end
|
|
122
132
|
|
|
123
133
|
# Retrieve the recipes that are included within the given recipe AST.
|
|
@@ -127,8 +137,9 @@ module FoodCritic
|
|
|
127
137
|
def included_recipes(ast)
|
|
128
138
|
raise_unless_xpath!(ast)
|
|
129
139
|
# we only support literal strings, ignoring sub-expressions
|
|
130
|
-
included = ast.xpath(%q{//command[ident/@value = 'include_recipe'
|
|
131
|
-
|
|
140
|
+
included = ast.xpath(%q{//command[ident/@value = 'include_recipe'][count(
|
|
141
|
+
descendant::args_add) = 1][descendant::args_add/string_literal]/
|
|
142
|
+
descendant::tstring_content})
|
|
132
143
|
included.inject(Hash.new([])){|h, i| h[i['value']] += [i]; h}
|
|
133
144
|
end
|
|
134
145
|
|
|
@@ -213,6 +224,13 @@ module FoodCritic
|
|
|
213
224
|
end
|
|
214
225
|
atts[att.xpath('string(ident/@value)')] = att_value
|
|
215
226
|
end
|
|
227
|
+
resource.xpath("do_block/descendant::method_add_block[
|
|
228
|
+
count(ancestor::do_block) = 1][brace_block | do_block]").each do |batt|
|
|
229
|
+
att_name = batt.xpath('string(method_add_arg/fcall/ident/@value)')
|
|
230
|
+
if att_name and ! att_name.empty? and batt.children.length > 1
|
|
231
|
+
atts[att_name] = batt.children[1]
|
|
232
|
+
end
|
|
233
|
+
end
|
|
216
234
|
atts
|
|
217
235
|
end
|
|
218
236
|
|
|
@@ -316,9 +334,20 @@ module FoodCritic
|
|
|
316
334
|
xml_node.add_child(pos)
|
|
317
335
|
else
|
|
318
336
|
if child.respond_to?(:first)
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
337
|
+
if child.first.respond_to?(:first) and
|
|
338
|
+
child.first.first == :assoc_new
|
|
339
|
+
child.each do |c|
|
|
340
|
+
n = Nokogiri::XML::Node.new(
|
|
341
|
+
c.first.to_s.gsub(/[^a-z_]/, ''), doc)
|
|
342
|
+
c.drop(1).each do |a|
|
|
343
|
+
xml_node.add_child(build_xml(a, doc, n))
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
else
|
|
347
|
+
n = Nokogiri::XML::Node.new(
|
|
348
|
+
child.first.to_s.gsub(/[^a-z_]/, ''), doc)
|
|
349
|
+
xml_node.add_child(build_xml(child, doc, n))
|
|
350
|
+
end
|
|
322
351
|
else
|
|
323
352
|
xml_node['value'] = child.to_s unless child.nil?
|
|
324
353
|
end
|
|
@@ -349,6 +378,7 @@ module FoodCritic
|
|
|
349
378
|
expr += '[is_att_type(descendant::ident'
|
|
350
379
|
expr += '[not(ancestor::aref/call)]' if options[:ignore_calls]
|
|
351
380
|
expr += "/@value)]/descendant::#{type}"
|
|
381
|
+
expr += "[ident/@value != 'node']" if type == :symbol
|
|
352
382
|
ast.xpath(expr, AttFilter.new).sort
|
|
353
383
|
end
|
|
354
384
|
|
|
@@ -24,6 +24,10 @@ module FoodCritic
|
|
|
24
24
|
"Fail the build if any of the specified tags are matched.") do |t|
|
|
25
25
|
options[:fail_tags] << t
|
|
26
26
|
end
|
|
27
|
+
opts.on("-c", "--chef-version VERSION",
|
|
28
|
+
"Only check against rules valid for this version of Chef.") do |c|
|
|
29
|
+
options[:chef_version] = c
|
|
30
|
+
end
|
|
27
31
|
opts.on("-C", "--[no-]context",
|
|
28
32
|
"Show lines matched against rather than the default summary.") do |c|
|
|
29
33
|
options[:context] = c
|
|
@@ -37,7 +41,7 @@ module FoodCritic
|
|
|
37
41
|
options[:search_grammar] = s
|
|
38
42
|
end
|
|
39
43
|
opts.on("-V", "--version",
|
|
40
|
-
"Display version.") do |v|
|
|
44
|
+
"Display the foodcritic version.") do |v|
|
|
41
45
|
options[:version] = true
|
|
42
46
|
end
|
|
43
47
|
end
|
data/lib/foodcritic/domain.rb
CHANGED
|
@@ -57,7 +57,7 @@ module FoodCritic
|
|
|
57
57
|
|
|
58
58
|
# A rule to be matched against.
|
|
59
59
|
class Rule
|
|
60
|
-
attr_accessor :code, :name, :cookbook, :recipe, :provider, :resource
|
|
60
|
+
attr_accessor :code, :name, :applies_to, :cookbook, :recipe, :provider, :resource
|
|
61
61
|
attr_writer :tags
|
|
62
62
|
|
|
63
63
|
# Create a new rule
|
|
@@ -69,6 +69,7 @@ module FoodCritic
|
|
|
69
69
|
def initialize(code, name)
|
|
70
70
|
@code, @name = code, name
|
|
71
71
|
@tags = [code]
|
|
72
|
+
@applies_to = Proc.new {|version| true}
|
|
72
73
|
end
|
|
73
74
|
|
|
74
75
|
# The tags associated with this rule.
|
data/lib/foodcritic/dsl.rb
CHANGED
|
@@ -29,6 +29,13 @@ module FoodCritic
|
|
|
29
29
|
rules.last.tags += tags
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
# Limit the versions that this rule can be seen to apply to.
|
|
33
|
+
#
|
|
34
|
+
# @param [block] block Your version constraint logic.
|
|
35
|
+
def applies_to(&block)
|
|
36
|
+
rules.last.applies_to = block
|
|
37
|
+
end
|
|
38
|
+
|
|
32
39
|
# Define a matcher that will be passed the AST with this method.
|
|
33
40
|
#
|
|
34
41
|
# @param [block] block Your implemented matcher that returns a match Hash.
|
data/lib/foodcritic/linter.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'optparse'
|
|
2
2
|
require 'ripper'
|
|
3
|
+
require 'rubygems'
|
|
3
4
|
require 'gherkin/tag_expression'
|
|
4
5
|
require 'set'
|
|
5
6
|
|
|
@@ -10,6 +11,9 @@ module FoodCritic
|
|
|
10
11
|
|
|
11
12
|
include FoodCritic::Api
|
|
12
13
|
|
|
14
|
+
# The default version that will be used to determine relevant rules
|
|
15
|
+
DEFAULT_CHEF_VERSION = "0.10.8"
|
|
16
|
+
|
|
13
17
|
# Perform option parsing from the provided arguments and do a lint check
|
|
14
18
|
# based on those arguments.
|
|
15
19
|
#
|
|
@@ -51,8 +55,10 @@ module FoodCritic
|
|
|
51
55
|
load_rules unless defined? @rules
|
|
52
56
|
warnings = []; last_dir = nil; matched_rule_tags = Set.new
|
|
53
57
|
|
|
54
|
-
active_rules = @rules.select
|
|
55
|
-
rule.tags)
|
|
58
|
+
active_rules = @rules.select do |rule|
|
|
59
|
+
matching_tags?(options[:tags], rule.tags) and
|
|
60
|
+
applies_to_version?(rule, options[:chef_version] || DEFAULT_CHEF_VERSION)
|
|
61
|
+
end
|
|
56
62
|
files_to_process(cookbook_path).each do |file|
|
|
57
63
|
cookbook_dir = Pathname.new(
|
|
58
64
|
File.join(File.dirname(file), '..')).cleanpath
|
|
@@ -107,6 +113,16 @@ module FoodCritic
|
|
|
107
113
|
|
|
108
114
|
private
|
|
109
115
|
|
|
116
|
+
# Some rules are version specific.
|
|
117
|
+
#
|
|
118
|
+
# @param [FoodCritic::Rule] rule The rule determine applicability for
|
|
119
|
+
# @param [String] version The version of Chef
|
|
120
|
+
# @return [Boolean] True if the rule applies to this version of Chef
|
|
121
|
+
def applies_to_version?(rule, version)
|
|
122
|
+
return true unless version
|
|
123
|
+
rule.applies_to.yield(Gem::Version.create(version))
|
|
124
|
+
end
|
|
125
|
+
|
|
110
126
|
# Invoke the DSL method with the provided parameters.
|
|
111
127
|
#
|
|
112
128
|
# @param [Proc] match_method Proc to invoke
|
data/lib/foodcritic/rules.rb
CHANGED
|
@@ -9,9 +9,9 @@ end
|
|
|
9
9
|
rule "FC002", "Avoid string interpolation where not required" do
|
|
10
10
|
tags %w{style strings}
|
|
11
11
|
recipe do |ast|
|
|
12
|
-
ast.xpath(%q{
|
|
13
|
-
count(
|
|
14
|
-
= 0]})
|
|
12
|
+
ast.xpath(%q{//*[self::string_literal | self::assoc_new]/string_add[
|
|
13
|
+
count(descendant::string_embexpr) = 1 and
|
|
14
|
+
count(tstring_content|string_add/tstring_content) = 0]})
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
|
|
@@ -31,10 +31,10 @@ rule "FC004", "Use a service resource to start and stop services" do
|
|
|
31
31
|
recipe do |ast|
|
|
32
32
|
find_resources(ast, :type => 'execute').find_all do |cmd|
|
|
33
33
|
cmd_str = (resource_attribute(cmd, 'command') || resource_name(cmd)).to_s
|
|
34
|
-
cmd_str.include?('/etc/init.d') ||
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
cmd_str.
|
|
34
|
+
(cmd_str.include?('/etc/init.d') || ['service ', '/sbin/service ',
|
|
35
|
+
'start ', 'stop ', 'invoke-rc.d '].any? do |service_cmd|
|
|
36
|
+
cmd_str.start_with?(service_cmd)
|
|
37
|
+
end) && %w{start stop restart reload}.any?{|a| cmd_str.include?(a)}
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
end
|
|
@@ -45,7 +45,14 @@ rule "FC005", "Avoid repetition of resource declarations" do
|
|
|
45
45
|
resources = find_resources(ast).map do |res|
|
|
46
46
|
resource_attributes(res).merge({:type => resource_type(res),
|
|
47
47
|
:ast => res})
|
|
48
|
-
end.chunk
|
|
48
|
+
end.chunk do |res|
|
|
49
|
+
res[:type] +
|
|
50
|
+
res[:ast].xpath("ancestor::*[self::if | self::unless | self::elsif |
|
|
51
|
+
self::else | self::when | self::method_add_block/call][position() = 1]/
|
|
52
|
+
descendant::pos[position() = 1]").to_s +
|
|
53
|
+
res[:ast].xpath("ancestor::method_add_block/command[
|
|
54
|
+
ident/@value='action']/args_add_block/descendant::ident/@value").to_s
|
|
55
|
+
end.reject{|res| res[1].size < 3}
|
|
49
56
|
resources.map do |cont_res|
|
|
50
57
|
first_resource = cont_res[1][0][:ast]
|
|
51
58
|
# we have contiguous resources of the same type, but do they share the
|
|
@@ -175,6 +182,7 @@ end
|
|
|
175
182
|
|
|
176
183
|
rule "FC015", "Consider converting definition to a LWRP" do
|
|
177
184
|
tags %w{style definitions lwrp}
|
|
185
|
+
applies_to {|version| version >= gem_version("0.7.12")}
|
|
178
186
|
cookbook do |dir|
|
|
179
187
|
Dir[File.join(dir, 'definitions', '*.rb')].reject do |entry|
|
|
180
188
|
['.', '..'].include? entry
|
|
@@ -184,6 +192,7 @@ end
|
|
|
184
192
|
|
|
185
193
|
rule "FC016", "LWRP does not declare a default action" do
|
|
186
194
|
tags %w{correctness lwrp}
|
|
195
|
+
applies_to {|version| version >= gem_version("0.7.12")}
|
|
187
196
|
resource do |ast, filename|
|
|
188
197
|
unless ["//ident/@value='default_action'",
|
|
189
198
|
"//def/bodystmt/descendant::assign/
|
|
@@ -195,6 +204,9 @@ end
|
|
|
195
204
|
|
|
196
205
|
rule "FC017", "LWRP does not notify when updated" do
|
|
197
206
|
tags %w{correctness lwrp}
|
|
207
|
+
applies_to do |version|
|
|
208
|
+
version >= gem_version("0.7.12")
|
|
209
|
+
end
|
|
198
210
|
provider do |ast, filename|
|
|
199
211
|
if ast.xpath(%q{//call/*[self::vcall or self::var_ref/ident/
|
|
200
212
|
@value='new_resource']/../
|
|
@@ -206,6 +218,7 @@ end
|
|
|
206
218
|
|
|
207
219
|
rule "FC018", "LWRP uses deprecated notification syntax" do
|
|
208
220
|
tags %w{style lwrp deprecated}
|
|
221
|
+
applies_to {|version| version >= gem_version("0.9.10")}
|
|
209
222
|
provider do |ast|
|
|
210
223
|
ast.xpath("//assign/var_field/ivar[@value='@updated']").map do |class_var|
|
|
211
224
|
match(class_var)
|
|
@@ -241,6 +254,7 @@ end
|
|
|
241
254
|
|
|
242
255
|
rule "FC020", "Conditional execution string attribute looks like Ruby" do
|
|
243
256
|
tags %w{correctness}
|
|
257
|
+
applies_to {|version| version >= gem_version("0.7.4")}
|
|
244
258
|
recipe do |ast, filename|
|
|
245
259
|
conditions = ast.xpath(%q{//command[(ident/@value='only_if' or ident/
|
|
246
260
|
@value='not_if') and descendant::tstring_content]}).map{|m| match(m)}
|
|
@@ -260,6 +274,7 @@ end
|
|
|
260
274
|
|
|
261
275
|
rule "FC021", "Resource condition in provider may not behave as expected" do
|
|
262
276
|
tags %w{correctness lwrp}
|
|
277
|
+
applies_to {|version| version >= gem_version("0.10.6")}
|
|
263
278
|
provider do |ast|
|
|
264
279
|
find_resources(ast).map do |resource|
|
|
265
280
|
condition = resource.xpath(%q{//method_add_block/
|
|
@@ -274,6 +289,7 @@ end
|
|
|
274
289
|
|
|
275
290
|
rule "FC022", "Resource condition within loop may not behave as expected" do
|
|
276
291
|
tags %w{correctness}
|
|
292
|
+
applies_to {|version| version >= gem_version("0.10.6")}
|
|
277
293
|
recipe do |ast|
|
|
278
294
|
ast.xpath("//call[ident/@value='each']/../do_block").map do |loop|
|
|
279
295
|
block_vars = loop.xpath("block_var/params/child::*").map do |n|
|
|
@@ -301,7 +317,8 @@ rule "FC023", "Prefer conditional attributes" do
|
|
|
301
317
|
[@value='only_if' or @value='not_if']) = 0]/ancestor::*[self::if or
|
|
302
318
|
self::unless][count(descendant::method_add_block[command/ident]) = 1]
|
|
303
319
|
[count(stmts_add/method_add_block/call) = 0]
|
|
304
|
-
[count(stmts_add/stmts_add) = 0]
|
|
320
|
+
[count(stmts_add/stmts_add) = 0]
|
|
321
|
+
[count(descendant::*[self::else or self::elsif]) = 0]})
|
|
305
322
|
end
|
|
306
323
|
end
|
|
307
324
|
|
|
@@ -324,3 +341,21 @@ rule "FC024", "Consider adding platform equivalents" do
|
|
|
324
341
|
end.flatten
|
|
325
342
|
end
|
|
326
343
|
end
|
|
344
|
+
|
|
345
|
+
rule "FC025", "Prefer chef_gem to compile-time gem install" do
|
|
346
|
+
tags %w{style deprecated}
|
|
347
|
+
applies_to {|version| version >= gem_version("0.10.10")}
|
|
348
|
+
recipe do |ast|
|
|
349
|
+
gem_install = ast.xpath("//stmts_add/assign[method_add_block[command/ident/
|
|
350
|
+
@value='gem_package'][do_block/stmts_add/command[ident/@value='action']
|
|
351
|
+
[descendant::ident/@value='nothing']]]")
|
|
352
|
+
gem_install.map do |install|
|
|
353
|
+
gem_var = install.xpath("var_field/ident/@value")
|
|
354
|
+
unless ast.xpath("//method_add_arg[call/var_ref/ident/@value='#{gem_var}']
|
|
355
|
+
[arg_paren/descendant::ident/@value='install' or
|
|
356
|
+
arg_paren/descendant::ident/@value='upgrade']").empty?
|
|
357
|
+
gem_install
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
end
|
data/lib/foodcritic/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: foodcritic
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2012-
|
|
12
|
+
date: 2012-04-21 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: gherkin
|
|
@@ -193,12 +193,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
193
193
|
version: '0'
|
|
194
194
|
segments:
|
|
195
195
|
- 0
|
|
196
|
-
hash:
|
|
196
|
+
hash: 700162912727168001
|
|
197
197
|
requirements: []
|
|
198
198
|
rubyforge_project:
|
|
199
199
|
rubygems_version: 1.8.19
|
|
200
200
|
signing_key:
|
|
201
201
|
specification_version: 3
|
|
202
|
-
summary: foodcritic-1.
|
|
202
|
+
summary: foodcritic-1.2.0
|
|
203
203
|
test_files: []
|
|
204
204
|
has_rdoc:
|