foodcritic 3.0.3 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +6 -14
- data/CHANGELOG.md +65 -0
- data/README.md +1 -1
- data/chef_dsl_metadata/chef_11.10.0.json +10272 -0
- data/chef_dsl_metadata/chef_11.10.2.json +10272 -0
- data/chef_dsl_metadata/chef_11.10.4.json +10272 -0
- data/chef_dsl_metadata/chef_11.6.2.json +9566 -0
- data/chef_dsl_metadata/chef_11.8.0.json +10074 -0
- data/chef_dsl_metadata/chef_11.8.2.json +10074 -0
- data/features/001_check_node_access.feature +60 -0
- data/features/003_check_for_chef_server.feature +5 -0
- data/features/006_check_file_mode.feature +1 -0
- data/features/022_check_for_dodgy_conditions_within_loop.feature +10 -0
- data/features/040_check_raw_git_usage.feature +6 -0
- data/features/047_check_for_attribute_assignment_without_precedence.feature +2 -0
- data/features/build_framework_support.feature +10 -0
- data/features/exclude_paths_to_lint.feature +19 -0
- data/features/show_lines_matched.feature +4 -4
- data/features/sort_warnings.feature +1 -1
- data/features/step_definitions/cookbook_steps.rb +117 -24
- data/features/support/command_helpers.rb +45 -12
- data/features/support/env.rb +5 -0
- data/lib/foodcritic/api.rb +122 -99
- data/lib/foodcritic/ast.rb +6 -7
- data/lib/foodcritic/chef.rb +24 -23
- data/lib/foodcritic/command_line.rb +49 -41
- data/lib/foodcritic/domain.rb +12 -10
- data/lib/foodcritic/dsl.rb +4 -7
- data/lib/foodcritic/error_checker.rb +0 -3
- data/lib/foodcritic/linter.rb +45 -43
- data/lib/foodcritic/notifications.rb +32 -32
- data/lib/foodcritic/output.rb +3 -6
- data/lib/foodcritic/rake_task.rb +9 -10
- data/lib/foodcritic/rules.rb +278 -240
- data/lib/foodcritic/template.rb +6 -13
- data/lib/foodcritic/version.rb +1 -1
- data/lib/foodcritic/xml.rb +1 -3
- data/man/foodcritic.1 +6 -2
- data/man/foodcritic.1.ronn +4 -1
- data/spec/foodcritic/linter_spec.rb +2 -2
- data/spec/regression/expected-output.txt +296 -1
- metadata +160 -138
data/lib/foodcritic/ast.rb
CHANGED
@@ -1,22 +1,21 @@
|
|
1
1
|
module FoodCritic
|
2
2
|
module AST
|
3
|
-
|
4
3
|
private
|
5
4
|
|
6
5
|
def ast_hash_node?(node)
|
7
|
-
node.first.respond_to?(:first)
|
6
|
+
node.first.respond_to?(:first) && node.first.first == :assoc_new
|
8
7
|
end
|
9
8
|
|
10
9
|
def ast_node_has_children?(node)
|
11
|
-
node.respond_to?(:first)
|
10
|
+
node.respond_to?(:first) && !node.respond_to?(:match)
|
12
11
|
end
|
13
12
|
|
14
13
|
# If the provided node is the line / column information.
|
15
14
|
def position_node?(node)
|
16
|
-
node.respond_to?(:length)
|
17
|
-
node.
|
15
|
+
node.respond_to?(:length) &&
|
16
|
+
node.length == 2 &&
|
17
|
+
node.respond_to?(:all?) &&
|
18
|
+
node.all? { |child| child.respond_to?(:to_i) }
|
18
19
|
end
|
19
|
-
|
20
20
|
end
|
21
|
-
|
22
21
|
end
|
data/lib/foodcritic/chef.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
module FoodCritic
|
2
|
-
|
3
2
|
# Encapsulates functions that previously were calls to the Chef gem.
|
4
3
|
module Chef
|
5
|
-
|
6
4
|
def chef_dsl_methods
|
7
5
|
load_metadata
|
8
6
|
@dsl_metadata[:dsl_methods].map(&:to_sym)
|
@@ -25,7 +23,7 @@ module FoodCritic
|
|
25
23
|
|
26
24
|
# Is this a valid Lucene query?
|
27
25
|
def valid_query?(query)
|
28
|
-
|
26
|
+
fail ArgumentError, 'Query cannot be nil or empty' if query.to_s.empty?
|
29
27
|
|
30
28
|
# Attempt to create a search query parser
|
31
29
|
search = FoodCritic::Chef::Search.new
|
@@ -48,23 +46,27 @@ module FoodCritic
|
|
48
46
|
# The DSL metadata doesn't necessarily reflect the version of Chef in the
|
49
47
|
# local user gemset.
|
50
48
|
def load_metadata
|
51
|
-
version = self.respond_to?(:chef_version)
|
49
|
+
version = if self.respond_to?(:chef_version)
|
50
|
+
chef_version
|
51
|
+
else
|
52
|
+
Linter::DEFAULT_CHEF_VERSION
|
53
|
+
end
|
52
54
|
metadata_path = [version, version.sub(/\.[a-z].*/, ''),
|
53
|
-
|
54
|
-
|
55
|
-
|
55
|
+
Linter::DEFAULT_CHEF_VERSION].map do |version|
|
56
|
+
metadata_path(version)
|
57
|
+
end.find { |m| File.exist?(m) }
|
56
58
|
@dsl_metadata ||= Yajl::Parser.parse(IO.read(metadata_path),
|
57
|
-
|
59
|
+
symbolize_keys: true)
|
58
60
|
end
|
59
61
|
|
60
62
|
def metadata_path(chef_version)
|
61
63
|
File.join(File.dirname(__FILE__), '..', '..',
|
62
|
-
|
64
|
+
"chef_dsl_metadata/chef_#{chef_version}.json")
|
63
65
|
end
|
64
66
|
|
65
67
|
def resource_check?(key, resource_type, field)
|
66
68
|
if resource_type.to_s.empty? || field.to_s.empty?
|
67
|
-
|
69
|
+
fail ArgumentError, 'Arguments cannot be nil or empty.'
|
68
70
|
end
|
69
71
|
|
70
72
|
load_metadata
|
@@ -80,10 +82,10 @@ module FoodCritic
|
|
80
82
|
end
|
81
83
|
|
82
84
|
class Search
|
83
|
-
|
84
85
|
# The search grammars that ship with any Chef gems installed locally.
|
85
86
|
# These are returned in descending version order (a newer Chef version
|
86
87
|
# could break our ability to load the grammar).
|
88
|
+
# Grammars are not available from Chef 11+.
|
87
89
|
def chef_search_grammars
|
88
90
|
Gem.path.map do |gem_path|
|
89
91
|
Dir["#{gem_path}/gems/chef-*/**/lucene.treetop"]
|
@@ -92,16 +94,17 @@ module FoodCritic
|
|
92
94
|
|
93
95
|
# Create the search parser from the first loadable grammar.
|
94
96
|
def create_parser(grammar_paths)
|
95
|
-
@search_parser ||=
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
97
|
+
@search_parser ||=
|
98
|
+
grammar_paths.inject(nil) do |parser, lucene_grammar|
|
99
|
+
begin
|
100
|
+
break parser unless parser.nil?
|
101
|
+
# Don't instantiate custom nodes
|
102
|
+
Treetop.load_from_string(
|
103
|
+
IO.read(lucene_grammar).gsub(/<[^>]+>/, ''))
|
104
|
+
LuceneParser.new
|
105
|
+
rescue
|
106
|
+
# Silently swallow and try the next grammar
|
107
|
+
end
|
105
108
|
end
|
106
109
|
end
|
107
110
|
|
@@ -114,8 +117,6 @@ module FoodCritic
|
|
114
117
|
def parser
|
115
118
|
@search_parser
|
116
119
|
end
|
117
|
-
|
118
120
|
end
|
119
121
|
end
|
120
|
-
|
121
122
|
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
module FoodCritic
|
2
|
-
|
3
2
|
# Command line parsing.
|
4
3
|
class CommandLine
|
5
|
-
|
6
4
|
# Create a new instance of CommandLine
|
7
5
|
#
|
8
6
|
# @param [Array] args The command line arguments
|
@@ -10,59 +8,69 @@ module FoodCritic
|
|
10
8
|
@args = args
|
11
9
|
@original_args = args.dup
|
12
10
|
@options = {
|
13
|
-
:
|
14
|
-
:
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:
|
18
|
-
:
|
11
|
+
fail_tags: [],
|
12
|
+
tags: [],
|
13
|
+
include_rules: [],
|
14
|
+
cookbook_paths: [],
|
15
|
+
role_paths: [],
|
16
|
+
environment_paths: [],
|
17
|
+
exclude_paths: []
|
19
18
|
}
|
20
19
|
@parser = OptionParser.new do |opts|
|
21
20
|
opts.banner = 'foodcritic [cookbook_paths]'
|
22
|
-
opts.on(
|
23
|
-
|
21
|
+
opts.on('-t', '--tags TAGS',
|
22
|
+
'Check against (or exclude ~) rules with the '\
|
23
|
+
'specified tags.') do |t|
|
24
24
|
@options[:tags] << t
|
25
25
|
end
|
26
|
-
opts.on(
|
27
|
-
|
26
|
+
opts.on('-f', '--epic-fail TAGS',
|
27
|
+
"Fail the build based on tags. Use 'any' to fail "\
|
28
|
+
'on all warnings.') do |t|
|
28
29
|
@options[:fail_tags] << t
|
29
30
|
end
|
30
|
-
opts.on(
|
31
|
-
|
31
|
+
opts.on('-c', '--chef-version VERSION',
|
32
|
+
'Only check against rules valid for this version '\
|
33
|
+
'of Chef.') do |c|
|
32
34
|
@options[:chef_version] = c
|
33
35
|
end
|
34
|
-
opts.on(
|
35
|
-
|
36
|
+
opts.on('-B', '--cookbook-path PATH',
|
37
|
+
'Cookbook path(s) to check.') do |b|
|
36
38
|
@options[:cookbook_paths] << b
|
37
39
|
end
|
38
|
-
opts.on(
|
39
|
-
|
40
|
+
opts.on('-C', '--[no-]context',
|
41
|
+
'Show lines matched against rather than the '\
|
42
|
+
'default summary.') do |c|
|
40
43
|
@options[:context] = c
|
41
44
|
end
|
42
|
-
opts.on(
|
43
|
-
|
45
|
+
opts.on('-E', '--environment-path PATH',
|
46
|
+
'Environment path(s) to check.') do |e|
|
44
47
|
@options[:environment_paths] << e
|
45
48
|
end
|
46
|
-
opts.on(
|
47
|
-
|
49
|
+
opts.on('-I', '--include PATH',
|
50
|
+
'Additional rule file path(s) to load.') do |i|
|
48
51
|
@options[:include_rules] << i
|
49
52
|
end
|
50
|
-
opts.on(
|
51
|
-
|
53
|
+
opts.on('-G', '--search-gems',
|
54
|
+
'Search rubygems for rule files with the path '\
|
55
|
+
'foodcritic/rules/**/*.rb') do |g|
|
52
56
|
@options[:search_gems] = true
|
53
57
|
end
|
54
|
-
opts.on(
|
55
|
-
|
58
|
+
opts.on('-R', '--role-path PATH',
|
59
|
+
'Role path(s) to check.') do |r|
|
56
60
|
@options[:role_paths] << r
|
57
61
|
end
|
58
|
-
opts.on(
|
59
|
-
|
62
|
+
opts.on('-S', '--search-grammar PATH',
|
63
|
+
'Specify grammar to use when validating search syntax.') do |s|
|
60
64
|
@options[:search_grammar] = s
|
61
65
|
end
|
62
|
-
opts.on(
|
63
|
-
|
66
|
+
opts.on('-V', '--version',
|
67
|
+
'Display the foodcritic version.') do |v|
|
64
68
|
@options[:version] = true
|
65
69
|
end
|
70
|
+
opts.on('-X', '--exclude PATH',
|
71
|
+
'Exclude path(s) from being linted.') do |e|
|
72
|
+
options[:exclude_paths] << e
|
73
|
+
end
|
66
74
|
end
|
67
75
|
# -v is not implemented but OptionParser gives the Foodcritic's version
|
68
76
|
# if that flag is passed
|
@@ -81,7 +89,7 @@ module FoodCritic
|
|
81
89
|
#
|
82
90
|
# @return [Boolean] True if help should be shown.
|
83
91
|
def show_help?
|
84
|
-
@args.length == 1
|
92
|
+
@args.length == 1 && @args.first == '--help'
|
85
93
|
end
|
86
94
|
|
87
95
|
# The help text.
|
@@ -111,7 +119,7 @@ module FoodCritic
|
|
111
119
|
def valid_paths?
|
112
120
|
paths = options[:cookbook_paths] + options[:role_paths] +
|
113
121
|
options[:environment_paths]
|
114
|
-
paths.any? && paths.all?{|path| File.
|
122
|
+
paths.any? && paths.all? { |path| File.exist?(path) }
|
115
123
|
end
|
116
124
|
|
117
125
|
# Is the search grammar specified valid?
|
@@ -120,7 +128,7 @@ module FoodCritic
|
|
120
128
|
# loaded.
|
121
129
|
def valid_grammar?
|
122
130
|
return true unless options.key?(:search_grammar)
|
123
|
-
return false unless File.
|
131
|
+
return false unless File.exist?(options[:search_grammar])
|
124
132
|
search = FoodCritic::Chef::Search.new
|
125
133
|
search.create_parser([options[:search_grammar]])
|
126
134
|
search.parser?
|
@@ -142,7 +150,7 @@ module FoodCritic
|
|
142
150
|
|
143
151
|
# The environment paths to check
|
144
152
|
#
|
145
|
-
# @return [Array<String>] Path(s) to the environment directories
|
153
|
+
# @return [Array<String>] Path(s) to the environment directories checked.
|
146
154
|
def environment_paths
|
147
155
|
Array(@options[:environment_paths])
|
148
156
|
end
|
@@ -159,11 +167,13 @@ module FoodCritic
|
|
159
167
|
#
|
160
168
|
# @return [Hash] The parsed command-line options.
|
161
169
|
def options
|
162
|
-
original_options.merge(
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
170
|
+
original_options.merge(
|
171
|
+
{
|
172
|
+
cookbook_paths: cookbook_paths,
|
173
|
+
role_paths: role_paths,
|
174
|
+
environment_paths: environment_paths,
|
175
|
+
}
|
176
|
+
)
|
167
177
|
end
|
168
178
|
|
169
179
|
# The original command-line options
|
@@ -172,7 +182,5 @@ module FoodCritic
|
|
172
182
|
def original_options
|
173
183
|
@options
|
174
184
|
end
|
175
|
-
|
176
185
|
end
|
177
|
-
|
178
186
|
end
|
data/lib/foodcritic/domain.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'gherkin/tag_expression'
|
2
2
|
|
3
3
|
module FoodCritic
|
4
|
-
|
5
4
|
# A warning of a possible issue
|
6
5
|
class Warning
|
7
6
|
attr_reader :rule, :match, :is_failed
|
@@ -11,9 +10,13 @@ module FoodCritic
|
|
11
10
|
# Warning.new(rule, :filename => 'foo/recipes.default.rb',
|
12
11
|
# :line => 5, :column=> 40)
|
13
12
|
#
|
14
|
-
def initialize(rule, match={}, options={})
|
13
|
+
def initialize(rule, match = {}, options = {})
|
15
14
|
@rule, @match = rule, match
|
16
|
-
@is_failed = options[:fail_tags].empty?
|
15
|
+
@is_failed = if options[:fail_tags].empty?
|
16
|
+
false
|
17
|
+
else
|
18
|
+
rule.matches_tags?(options[:fail_tags])
|
19
|
+
end
|
17
20
|
end
|
18
21
|
|
19
22
|
# If this warning has failed or not.
|
@@ -24,7 +27,6 @@ module FoodCritic
|
|
24
27
|
|
25
28
|
# The collected warnings (if any) raised against a cookbook tree.
|
26
29
|
class Review
|
27
|
-
|
28
30
|
attr_reader :cookbook_paths, :warnings
|
29
31
|
|
30
32
|
def initialize(cookbook_paths, warnings)
|
@@ -51,23 +53,24 @@ module FoodCritic
|
|
51
53
|
@warnings.map do |w|
|
52
54
|
["#{w.rule.code}: #{w.rule.name}: #{w.match[:filename]}",
|
53
55
|
w.match[:line].to_i]
|
54
|
-
end.sort do |x,y|
|
56
|
+
end.sort do |x, y|
|
55
57
|
x.first == y.first ? x[1] <=> y[1] : x.first <=> y.first
|
56
|
-
end.map{|w|"#{w.first}:#{w[1]}"}.uniq.join("\n")
|
58
|
+
end.map { |w|"#{w.first}:#{w[1]}" }.uniq.join("\n")
|
57
59
|
end
|
58
60
|
end
|
59
61
|
|
60
62
|
# A rule to be matched against.
|
61
63
|
class Rule
|
62
64
|
attr_accessor :code, :name, :applies_to, :cookbook, :attributes, :recipe,
|
63
|
-
|
65
|
+
:provider, :resource, :metadata, :library, :template, :role,
|
66
|
+
:environment
|
64
67
|
|
65
68
|
attr_writer :tags
|
66
69
|
|
67
70
|
def initialize(code, name)
|
68
71
|
@code, @name = code, name
|
69
72
|
@tags = [code]
|
70
|
-
@applies_to = Proc.new {|version| true}
|
73
|
+
@applies_to = Proc.new { |version| true }
|
71
74
|
end
|
72
75
|
|
73
76
|
# The tags associated with this rule. Rule is always tagged with the tag
|
@@ -76,7 +79,7 @@ module FoodCritic
|
|
76
79
|
['any'] + @tags
|
77
80
|
end
|
78
81
|
|
79
|
-
# Checks the rule
|
82
|
+
# Checks the rule tags to see if they match a Gherkin (Cucumber) expression
|
80
83
|
def matches_tags?(tag_expr)
|
81
84
|
Gherkin::TagExpression.new(tag_expr).evaluate(tags.map do |t|
|
82
85
|
Gherkin::Formatter::Model::Tag.new(t, 1)
|
@@ -88,5 +91,4 @@ module FoodCritic
|
|
88
91
|
"#{@code}: #{@name}"
|
89
92
|
end
|
90
93
|
end
|
91
|
-
|
92
94
|
end
|
data/lib/foodcritic/dsl.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'pathname'
|
2
2
|
|
3
3
|
module FoodCritic
|
4
|
-
|
5
4
|
# The DSL methods exposed for defining rules. A minimal example rule:
|
6
5
|
#
|
7
6
|
# rule "FC123", "My rule name" do
|
@@ -27,7 +26,7 @@ module FoodCritic
|
|
27
26
|
|
28
27
|
include Api
|
29
28
|
|
30
|
-
def initialize(chef_version=Linter::DEFAULT_CHEF_VERSION)
|
29
|
+
def initialize(chef_version = Linter::DEFAULT_CHEF_VERSION)
|
31
30
|
@chef_version = chef_version
|
32
31
|
end
|
33
32
|
|
@@ -38,8 +37,8 @@ module FoodCritic
|
|
38
37
|
yield self
|
39
38
|
end
|
40
39
|
|
41
|
-
# Add tags to the rule which can be used by the end user to filter the
|
42
|
-
# to be applied.
|
40
|
+
# Add tags to the rule which can be used by the end user to filter the
|
41
|
+
# rules to be applied.
|
43
42
|
def tags(tags)
|
44
43
|
rules.last.tags += tags
|
45
44
|
end
|
@@ -73,7 +72,7 @@ module FoodCritic
|
|
73
72
|
rule_block :role
|
74
73
|
|
75
74
|
# Load the ruleset(s).
|
76
|
-
def self.load(paths, chef_version=Linter::DEFAULT_CHEF_VERSION)
|
75
|
+
def self.load(paths, chef_version = Linter::DEFAULT_CHEF_VERSION)
|
77
76
|
dsl = RuleDsl.new(chef_version)
|
78
77
|
paths.map do |path|
|
79
78
|
File.directory?(path) ? Dir["#{path}/**/*.rb"].sort : path
|
@@ -82,7 +81,5 @@ module FoodCritic
|
|
82
81
|
end
|
83
82
|
dsl.rules
|
84
83
|
end
|
85
|
-
|
86
84
|
end
|
87
|
-
|
88
85
|
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
module FoodCritic
|
2
|
-
|
3
2
|
# Expose if any errors are found in parsing
|
4
3
|
class ErrorChecker < Ripper::SexpBuilder
|
5
|
-
|
6
4
|
# Create a new instance of ErrorChecker
|
7
5
|
#
|
8
6
|
# @see Ripper::SexpBuilder#initialize
|
@@ -25,6 +23,5 @@ module FoodCritic
|
|
25
23
|
end
|
26
24
|
|
27
25
|
self.register_error_handlers
|
28
|
-
|
29
26
|
end
|
30
27
|
end
|
data/lib/foodcritic/linter.rb
CHANGED
@@ -6,12 +6,11 @@ require 'set'
|
|
6
6
|
module FoodCritic
|
7
7
|
# The main entry point for linting your Chef cookbooks.
|
8
8
|
class Linter
|
9
|
-
|
10
9
|
include FoodCritic::Api
|
11
10
|
|
12
11
|
# The default version that will be used to determine relevant rules. This
|
13
12
|
# can be over-ridden at the command line with the `--chef-version` option.
|
14
|
-
DEFAULT_CHEF_VERSION =
|
13
|
+
DEFAULT_CHEF_VERSION = '11.10.4'
|
15
14
|
attr_reader :chef_version
|
16
15
|
|
17
16
|
# Perform a lint check. This method is intended for use by the command-line
|
@@ -21,7 +20,7 @@ module FoodCritic
|
|
21
20
|
# The first item is the string output, the second is exit code.
|
22
21
|
return [cmd_line.help, 0] if cmd_line.show_help?
|
23
22
|
return [cmd_line.version, 0] if cmd_line.show_version?
|
24
|
-
if !
|
23
|
+
if !cmd_line.valid_grammar?
|
25
24
|
[cmd_line.help, 4]
|
26
25
|
elsif cmd_line.valid_paths?
|
27
26
|
review = FoodCritic::Linter.new.check(cmd_line.options)
|
@@ -45,7 +44,6 @@ module FoodCritic
|
|
45
44
|
# * `:exclude_paths` - Paths to exclude from linting
|
46
45
|
#
|
47
46
|
def check(options = {})
|
48
|
-
|
49
47
|
options = setup_defaults(options)
|
50
48
|
@options = options
|
51
49
|
@chef_version = options[:chef_version] || DEFAULT_CHEF_VERSION
|
@@ -58,33 +56,34 @@ module FoodCritic
|
|
58
56
|
files_to_process(paths).each do |p|
|
59
57
|
|
60
58
|
relevant_tags = if options[:tags].any?
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
59
|
+
options[:tags]
|
60
|
+
else
|
61
|
+
cookbook_tags(p[:filename])
|
62
|
+
end
|
65
63
|
|
66
64
|
active_rules(relevant_tags).each do |rule|
|
67
65
|
|
68
66
|
state = {
|
69
|
-
:
|
70
|
-
:
|
71
|
-
:
|
72
|
-
:
|
73
|
-
:
|
67
|
+
path_type: p[:path_type],
|
68
|
+
file: p[:filename],
|
69
|
+
ast: read_ast(p[:filename]),
|
70
|
+
rule: rule,
|
71
|
+
last_dir: last_dir
|
74
72
|
}
|
75
73
|
|
76
74
|
matches = if p[:path_type] == :cookbook
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
75
|
+
cookbook_matches(state)
|
76
|
+
else
|
77
|
+
other_matches(state)
|
78
|
+
end
|
81
79
|
|
82
80
|
matches = remove_ignored(matches, state[:rule], state[:file])
|
83
81
|
|
84
82
|
# Convert the matches into warnings
|
85
83
|
matches.each do |match|
|
86
84
|
warnings << Warning.new(state[:rule],
|
87
|
-
|
85
|
+
{ filename: state[:file] }.merge(match),
|
86
|
+
options)
|
88
87
|
matched_rule_tags << state[:rule].tags
|
89
88
|
end
|
90
89
|
end
|
@@ -133,8 +132,8 @@ module FoodCritic
|
|
133
132
|
private
|
134
133
|
|
135
134
|
def rule_files_in_gems
|
136
|
-
Gem::Specification.latest_specs(true).map do |spec|
|
137
|
-
spec.matches_for_glob('foodcritic/rules/**/*.rb')
|
135
|
+
Gem::Specification.latest_specs(true).map do |spec|
|
136
|
+
spec.matches_for_glob('foodcritic/rules/**/*.rb')
|
138
137
|
end.flatten
|
139
138
|
end
|
140
139
|
|
@@ -142,14 +141,14 @@ module FoodCritic
|
|
142
141
|
matches.reject do |m|
|
143
142
|
matched_file = m[:filename] || file
|
144
143
|
(line = m[:line]) && File.exist?(matched_file) &&
|
145
|
-
ignore_line_match?(File.readlines(matched_file)[line-1], rule)
|
144
|
+
ignore_line_match?(File.readlines(matched_file)[line - 1], rule)
|
146
145
|
end
|
147
146
|
end
|
148
147
|
|
149
148
|
def ignore_line_match?(line, rule)
|
150
149
|
ignores = line.to_s[/\s+#\s*(.*)/, 1]
|
151
|
-
if ignores
|
152
|
-
!
|
150
|
+
if ignores && ignores.include?('~')
|
151
|
+
!rule.matches_tags?(ignores.split(/[ ,]/))
|
153
152
|
else
|
154
153
|
false
|
155
154
|
end
|
@@ -176,18 +175,17 @@ module FoodCritic
|
|
176
175
|
|
177
176
|
def active_rules(tags)
|
178
177
|
@rules.select do |rule|
|
179
|
-
rule.matches_tags?(tags)
|
180
|
-
applies_to_version?(rule, chef_version)
|
178
|
+
rule.matches_tags?(tags) && applies_to_version?(rule, chef_version)
|
181
179
|
end
|
182
180
|
end
|
183
181
|
|
184
182
|
def cookbook_dir(file)
|
185
183
|
Pathname.new(File.join(File.dirname(file),
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
184
|
+
case File.basename(file)
|
185
|
+
when 'metadata.rb' then ''
|
186
|
+
when /\.erb$/ then '../..'
|
187
|
+
else '..'
|
188
|
+
end)).cleanpath
|
191
189
|
end
|
192
190
|
|
193
191
|
def dsl_method_for_file(file)
|
@@ -208,26 +206,31 @@ module FoodCritic
|
|
208
206
|
# Return the files within a cookbook tree that we are interested in trying
|
209
207
|
# to match rules against.
|
210
208
|
def files_to_process(paths)
|
211
|
-
paths.reject{|type, _| type == :exclude}.map do |path_type, dirs|
|
209
|
+
paths.reject { |type, _| type == :exclude }.map do |path_type, dirs|
|
212
210
|
dirs.map do |dir|
|
213
211
|
exclusions = []
|
212
|
+
|
214
213
|
unless paths[:exclude].empty?
|
215
|
-
exclusions = Dir.glob(paths[:exclude].map
|
214
|
+
exclusions = Dir.glob(paths[:exclude].map do |p|
|
215
|
+
File.join(dir, p, '**/**')
|
216
|
+
end)
|
216
217
|
end
|
217
218
|
|
218
219
|
if File.directory?(dir)
|
219
220
|
glob = if path_type == :cookbook
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
221
|
+
'{metadata.rb,{attributes,definitions,libraries,'\
|
222
|
+
'providers,recipes,resources}/*.rb,templates/*/*.erb}'
|
223
|
+
else
|
224
|
+
'*.rb'
|
225
|
+
end
|
226
|
+
|
224
227
|
(Dir.glob(File.join(dir, glob)) +
|
225
228
|
Dir.glob(File.join(dir, "*/#{glob}")) - exclusions)
|
226
229
|
else
|
227
230
|
dir unless exclusions.include?(dir)
|
228
231
|
end
|
229
232
|
end.compact.flatten.map do |filename|
|
230
|
-
{:
|
233
|
+
{ filename: filename, path_type: path_type }
|
231
234
|
end
|
232
235
|
end.flatten
|
233
236
|
end
|
@@ -243,7 +246,7 @@ module FoodCritic
|
|
243
246
|
if m.respond_to?(:node_name)
|
244
247
|
match(m)
|
245
248
|
elsif m.respond_to?(:xpath)
|
246
|
-
m.to_a.map{|m| match(m)}
|
249
|
+
m.to_a.map { |m| match(m) }
|
247
250
|
else
|
248
251
|
m
|
249
252
|
end
|
@@ -259,8 +262,8 @@ module FoodCritic
|
|
259
262
|
[key, Array(value)] if key.to_s.end_with?('paths')
|
260
263
|
end.compact]
|
261
264
|
|
262
|
-
unless paths.find{|k, v| k != :exclude_paths
|
263
|
-
|
265
|
+
unless paths.find { |k, v| k != :exclude_paths && !v.empty? }
|
266
|
+
fail ArgumentError, 'A cookbook path or role path must be specified'
|
264
267
|
end
|
265
268
|
|
266
269
|
Hash[paths.map do |key, value|
|
@@ -269,9 +272,8 @@ module FoodCritic
|
|
269
272
|
end
|
270
273
|
|
271
274
|
def setup_defaults(options)
|
272
|
-
{:
|
273
|
-
:
|
275
|
+
{ tags: [], fail_tags: [], include_rules: [], exclude_paths: [],
|
276
|
+
cookbook_paths: [], role_paths: [] }.merge(options)
|
274
277
|
end
|
275
|
-
|
276
278
|
end
|
277
279
|
end
|