foodcritic 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/foodcritic CHANGED
@@ -1,8 +1,19 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'foodcritic'
3
+ require 'optparse'
4
+
5
+ options = {}
6
+ options[:tags] = []
7
+ parser = OptionParser.new do |opts|
8
+ opts.banner = 'foodcritic [cookbook_path]'
9
+ opts.on("-t", "--tags TAGS", "Only check against rules with the specified tags.") {|t|options[:tags] << t}
10
+ end
11
+ parser.parse!
12
+
3
13
  unless ARGV.length == 1 and Dir.exists?(ARGV[0])
4
- STDERR.puts 'foodcritic [cookbook_path]'
14
+ puts parser.help
5
15
  exit 1
6
16
  end
7
- review = FoodCritic::Linter.new.check(ARGV[0])
8
- puts review unless review.warnings.empty?
17
+
18
+ review = FoodCritic::Linter.new.check(ARGV[0], options)
19
+ puts review unless review.warnings.empty?
@@ -38,7 +38,7 @@ module FoodCritic
38
38
 
39
39
  # A rule to be matched against.
40
40
  class Rule
41
- attr_accessor :code, :name, :description, :recipe
41
+ attr_accessor :code, :name, :description, :recipe, :tags
42
42
 
43
43
  # Create a new rule
44
44
  #
@@ -46,6 +46,7 @@ module FoodCritic
46
46
  # @param [String] name The short descriptive name of this rule presented to the end user.
47
47
  def initialize(code, name)
48
48
  @code, @name = code, name
49
+ @tags = [code]
49
50
  end
50
51
  end
51
52
 
@@ -19,6 +19,13 @@ module FoodCritic
19
19
  yield self
20
20
  end
21
21
 
22
+ # Add tags to the rule which can be used to filter the rules to be applied.
23
+ #
24
+ # @param [Array] tags The tags associated with this rule.
25
+ def tags(tags)
26
+ rules.last.tags += tags
27
+ end
28
+
22
29
  # Set the rule description
23
30
  #
24
31
  # @param [String] description Set the rule description.
@@ -1,4 +1,5 @@
1
1
  require 'nokogiri'
2
+ require 'chef/solr_query/query_transform'
2
3
 
3
4
  module FoodCritic
4
5
 
@@ -31,6 +32,29 @@ module FoodCritic
31
32
  ast.xpath("//fcall/ident[@value = 'search']")
32
33
  end
33
34
 
35
+ # Searches performed by the specified recipe that are literal strings. Searches with a query formed from a
36
+ # subexpression will be ignored.
37
+ #
38
+ # @param [Nokogiri::XML::Node] ast The AST of the cookbook recipe to check.
39
+ # @return [Nokogiri::XML::Node] The matching nodes
40
+ def literal_searches(ast)
41
+ ast.xpath("//method_add_arg[fcall/ident/@value = 'search' and count(descendant::string_embexpr) = 0]/descendant::tstring_content")
42
+ end
43
+
44
+ # Is this a valid Lucene query?
45
+ #
46
+ # @param [String] query The query to check for syntax errors
47
+ # @return [Boolean] True if the query is well-formed
48
+ def valid_query?(query)
49
+ # Exceptions for flow control. Alternatively we could re-implement the parse method.
50
+ begin
51
+ Chef::SolrQuery::QueryTransform.parse(query)
52
+ true
53
+ rescue Chef::Exceptions::QueryParseError
54
+ false
55
+ end
56
+ end
57
+
34
58
  # Find Chef resources of the specified type.
35
59
  # TODO: Include blockless resources
36
60
  #
@@ -1,4 +1,5 @@
1
1
  require 'ripper'
2
+ require 'gherkin/tag_expression'
2
3
 
3
4
  module FoodCritic
4
5
 
@@ -15,12 +16,15 @@ module FoodCritic
15
16
  # Review the cookbooks at the provided path, identifying potential improvements.
16
17
  #
17
18
  # @param [String] cookbook_path The file path to an individual cookbook directory
19
+ # @param [Hash] options Options to apply to the linting
20
+ # @option options [Array] tags The tags to filter rules based on
18
21
  # @return [FoodCritic::Review] A review of your cookbooks, with any warnings issued.
19
- def check(cookbook_path)
22
+ def check(cookbook_path, options)
20
23
  warnings = []
24
+ tag_expr = Gherkin::TagExpression.new(options[:tags])
21
25
  files_to_process(cookbook_path).each do |file|
22
26
  ast = read_file(file)
23
- @rules.each do |rule|
27
+ @rules.select{|rule| tag_expr.eval(rule.tags)}.each do |rule|
24
28
  matches = rule.recipe.yield(ast, file)
25
29
  matches.each{|match| warnings << Warning.new(rule, {:filename => file}.merge(match))} unless matches.nil?
26
30
  end
@@ -1,4 +1,5 @@
1
1
  rule "FC002", "Avoid string interpolation where not required" do
2
+ tags %w{style strings}
2
3
  description "When setting a resource value avoid string interpolation where not required."
3
4
  recipe do |ast|
4
5
  ast.xpath(%q{//string_literal[count(descendant::string_embexpr) = 1 and
@@ -7,6 +8,7 @@ rule "FC002", "Avoid string interpolation where not required" do
7
8
  end
8
9
 
9
10
  rule "FC003", "Check whether you are running with chef server before using server-specific features" do
11
+ tags %w{portability solo}
10
12
  description "Ideally your cookbooks should be usable without requiring chef server."
11
13
  recipe do |ast|
12
14
  checks_for_chef_solo?(ast) ? [] : searches(ast).map{|s| match(s)}
@@ -14,6 +16,7 @@ rule "FC003", "Check whether you are running with chef server before using serve
14
16
  end
15
17
 
16
18
  rule "FC004", "Use a service resource to start and stop services" do
19
+ tags %w{style services}
17
20
  description "Avoid use of execute to control services - use the service resource instead."
18
21
  recipe do |ast|
19
22
  find_resources(ast, 'execute').find_all do |cmd|
@@ -24,6 +27,7 @@ rule "FC004", "Use a service resource to start and stop services" do
24
27
  end
25
28
 
26
29
  rule "FC005", "Avoid repetition of resource declarations" do
30
+ tags %w{style}
27
31
  description "Where you have a lot of resources that vary in only a single attribute wrap them in a loop for brevity."
28
32
  recipe do |ast|
29
33
  matches = []
@@ -39,6 +43,7 @@ rule "FC005", "Avoid repetition of resource declarations" do
39
43
  end
40
44
 
41
45
  rule "FC006", "Mode should be quoted or fully specified when setting file permissions" do
46
+ tags %w{correctness files}
42
47
  description "Not quoting mode when setting permissions can lead to incorrect permissions being set."
43
48
  recipe do |ast|
44
49
  ast.xpath(%q{//ident[@value='mode']/parent::command/descendant::int[string-length(@value) < 4]/
@@ -47,6 +52,7 @@ rule "FC006", "Mode should be quoted or fully specified when setting file permis
47
52
  end
48
53
 
49
54
  rule "FC007", "Ensure recipe dependencies are reflected in cookbook metadata" do
55
+ tags %w{correctness metadata}
50
56
  description "You are including a recipe that is not in the current cookbook and not defined as a dependency in your cookbook metadata."
51
57
  recipe do |ast,filename|
52
58
  metadata_path = Pathname.new(File.join(File.dirname(filename), '..', 'metadata.rb')).cleanpath
@@ -60,6 +66,7 @@ rule "FC007", "Ensure recipe dependencies are reflected in cookbook metadata" do
60
66
  end
61
67
 
62
68
  rule "FC008", "Generated cookbook metadata needs updating" do
69
+ tags %w{style metadata}
63
70
  description "The cookbook metadata for this cookbook is boilerplate output from knife generate cookbook and needs updating with the real details of your cookbook."
64
71
  recipe do |ast,filename|
65
72
  metadata_path = Pathname.new(File.join(File.dirname(filename), '..', 'metadata.rb')).cleanpath
@@ -74,6 +81,7 @@ rule "FC008", "Generated cookbook metadata needs updating" do
74
81
  end
75
82
 
76
83
  rule "FC009", "Resource attribute not recognised" do
84
+ tags %w{correctness}
77
85
  description "You appear to be using an unrecognised attribute on a standard Chef resource. Please check for typos."
78
86
  recipe do |ast|
79
87
  matches = []
@@ -90,4 +98,13 @@ rule "FC009", "Resource attribute not recognised" do
90
98
  end
91
99
  matches
92
100
  end
101
+ end
102
+
103
+ rule "FC010", "Invalid search syntax" do
104
+ tags %w{correctness search}
105
+ description "The search expression in the recipe could not be parsed. Please check your syntax."
106
+ recipe do |ast|
107
+ # This only works for literal search strings
108
+ literal_searches(ast).reject{|search| valid_query?(search['value'])}.map{|search| match(search)}
109
+ end
93
110
  end
@@ -1,3 +1,3 @@
1
1
  module FoodCritic
2
- VERSION = '0.4.0'
2
+ VERSION = '0.5.0'
3
3
  end
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: 0.4.0
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-10 00:00:00.000000000 Z
12
+ date: 2011-12-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: chef
16
- requirement: &2153103960 !ruby/object:Gem::Requirement
16
+ requirement: &2152896120 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,21 @@ dependencies:
21
21
  version: 0.10.4
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2153103960
24
+ version_requirements: *2152896120
25
+ - !ruby/object:Gem::Dependency
26
+ name: gherkin
27
+ requirement: &2152893520 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 2.7.1
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2152893520
25
36
  - !ruby/object:Gem::Dependency
26
37
  name: nokogiri
27
- requirement: &2153103180 !ruby/object:Gem::Requirement
38
+ requirement: &2152891580 !ruby/object:Gem::Requirement
28
39
  none: false
29
40
  requirements:
30
41
  - - ~>
@@ -32,7 +43,7 @@ dependencies:
32
43
  version: 1.5.0
33
44
  type: :runtime
34
45
  prerelease: false
35
- version_requirements: *2153103180
46
+ version_requirements: *2152891580
36
47
  description: Lint tool for Opscode Chef cookbooks.
37
48
  email:
38
49
  executables:
@@ -69,12 +80,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
69
80
  version: '0'
70
81
  segments:
71
82
  - 0
72
- hash: 1643437083715464422
83
+ hash: -2449883706857306958
73
84
  requirements: []
74
85
  rubyforge_project:
75
86
  rubygems_version: 1.8.10
76
87
  signing_key:
77
88
  specification_version: 3
78
- summary: foodcritic-0.4.0
89
+ summary: foodcritic-0.5.0
79
90
  test_files: []
80
91
  has_rdoc: