foodcritic 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|