puppet-lint 0.1.9 → 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +19 -0
- data/bin/puppet-lint +33 -11
- data/lib/puppet-lint.rb +112 -33
- data/lib/puppet-lint/configuration.rb +57 -0
- data/lib/puppet-lint/plugin.rb +122 -11
- data/lib/puppet-lint/plugins/check_classes.rb +57 -68
- data/lib/puppet-lint/plugins/check_conditionals.rb +19 -22
- data/lib/puppet-lint/plugins/check_resources.rb +42 -32
- data/lib/puppet-lint/plugins/check_strings.rb +34 -18
- data/lib/puppet-lint/plugins/check_variables.rb +11 -6
- data/lib/puppet-lint/plugins/check_whitespace.rb +39 -11
- data/puppet-lint.gemspec +3 -1
- data/spec/puppet-lint/check_classes_spec.rb +59 -53
- data/spec/puppet-lint/check_conditionals_spec.rb +5 -9
- data/spec/puppet-lint/check_resources_spec.rb +36 -35
- data/spec/puppet-lint/check_strings_spec.rb +17 -27
- data/spec/puppet-lint/check_variables_spec.rb +8 -3
- data/spec/puppet-lint/check_whitespace_spec.rb +7 -13
- data/spec/spec_helper.rb +98 -0
- metadata +19 -4
data/Rakefile
CHANGED
@@ -2,5 +2,24 @@ require 'rake'
|
|
2
2
|
require 'rspec/core/rake_task'
|
3
3
|
|
4
4
|
task :default => :test
|
5
|
+
#task :default => [:test, :rdoc]
|
5
6
|
|
6
7
|
RSpec::Core::RakeTask.new(:test)
|
8
|
+
|
9
|
+
### RDOC Tasks ###
|
10
|
+
require 'rdoc'
|
11
|
+
if (RDoc::VERSION.split('.') <=> ['2','4','2']) >= 0
|
12
|
+
require 'rdoc/task'
|
13
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
14
|
+
rdoc.main = "README.md"
|
15
|
+
rdoc.rdoc_files.include("README.md", "lib/**/*.rb")
|
16
|
+
rdoc.options << "--all"
|
17
|
+
end
|
18
|
+
else
|
19
|
+
require 'rake/rdoctask'
|
20
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
21
|
+
rdoc.main = "README.md"
|
22
|
+
rdoc.rdoc_files.include("README.md", "lib/**/*.rb")
|
23
|
+
rdoc.options << "--all"
|
24
|
+
end
|
25
|
+
end
|
data/bin/puppet-lint
CHANGED
@@ -17,10 +17,6 @@ require 'optparse'
|
|
17
17
|
require 'rubygems'
|
18
18
|
require 'puppet-lint'
|
19
19
|
|
20
|
-
options = {
|
21
|
-
:error_level => :all,
|
22
|
-
:with_filename => false,
|
23
|
-
}
|
24
20
|
opts = OptionParser.new do |opts|
|
25
21
|
opts.banner = help
|
26
22
|
|
@@ -30,19 +26,45 @@ opts = OptionParser.new do |opts|
|
|
30
26
|
end
|
31
27
|
|
32
28
|
opts.on("--with-filename", "Display the filename before the warning") do
|
33
|
-
|
29
|
+
PuppetLint.configuration.with_filename = true
|
34
30
|
end
|
35
31
|
|
36
|
-
opts.on("--error-level LEVEL", [:all, :warning, :error], "The level of error to return (warning, error, all)
|
37
|
-
|
32
|
+
opts.on("--error-level LEVEL", [:all, :warning, :error], "The level of error to return.", "(warning, error, all)") do |el|
|
33
|
+
PuppetLint.configuration.error_level = el
|
38
34
|
end
|
39
35
|
|
40
36
|
opts.on("--fail-on-warnings", "Return a non-zero exit status for warnings.") do
|
41
|
-
|
37
|
+
PuppetLint.configuration.fail_on_warnings = true
|
42
38
|
end
|
39
|
+
|
40
|
+
opts.on("--log-format FORMAT",
|
41
|
+
"Change the log format.", "Overrides --with-filename.",
|
42
|
+
"The following placeholders can be used:",
|
43
|
+
"%{filename} - Filename without path.",
|
44
|
+
"%{path} - Path as provided.",
|
45
|
+
"%{fullpath} - Full path.",
|
46
|
+
"%{linenumber} - Line number.",
|
47
|
+
"%{kind} - The kind of message.",
|
48
|
+
" - (warning, error)",
|
49
|
+
"%{KIND} - Uppercase version of %{kind}",
|
50
|
+
"%{check} - Name of the check.",
|
51
|
+
"%{message} - The message."
|
52
|
+
) do |format|
|
53
|
+
PuppetLint.configuration.log_format = format
|
54
|
+
end
|
55
|
+
|
56
|
+
opts.separator ""
|
57
|
+
opts.separator " Disable checks:"
|
58
|
+
|
59
|
+
PuppetLint.configuration.checks.each do |check|
|
60
|
+
opts.on("--no-#{check}-check", "Skip the #{check} check") do
|
61
|
+
PuppetLint.configuration.send("disable_#{check}")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
opts.load(File.expand_path("~/.puppet-lintrc")) unless opts.load(".puppet-lintrc")
|
43
66
|
end
|
44
67
|
|
45
|
-
# Read command line options into `options` hash
|
46
68
|
begin
|
47
69
|
opts.parse!
|
48
70
|
rescue OptionParser::InvalidOption
|
@@ -58,11 +80,11 @@ if ARGV[0].nil?
|
|
58
80
|
end
|
59
81
|
|
60
82
|
begin
|
61
|
-
l = PuppetLint.new
|
83
|
+
l = PuppetLint.new
|
62
84
|
l.file = ARGV[0]
|
63
85
|
l.run
|
64
86
|
|
65
|
-
if l.errors? or (l.warnings? and
|
87
|
+
if l.errors? or (l.warnings? and PuppetLint.configuration.fail_on_warnings)
|
66
88
|
exit 1
|
67
89
|
end
|
68
90
|
rescue PuppetLint::NoCodeError
|
data/lib/puppet-lint.rb
CHANGED
@@ -7,8 +7,8 @@ rescue LoadError
|
|
7
7
|
exit 1
|
8
8
|
end
|
9
9
|
|
10
|
+
require 'puppet-lint/configuration'
|
10
11
|
require 'puppet-lint/plugin'
|
11
|
-
require 'puppet-lint/plugins'
|
12
12
|
|
13
13
|
unless String.respond_to?('prepend')
|
14
14
|
class String
|
@@ -18,25 +18,85 @@ unless String.respond_to?('prepend')
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
+
# If we are using an older ruby version, we back-port the basic functionality
|
22
|
+
# we need for formatting output: 'somestring' % <hash>
|
23
|
+
begin
|
24
|
+
if ('%{test}' % {:test => 'replaced'} == 'replaced')
|
25
|
+
# If this works, we are all good to go.
|
26
|
+
end
|
27
|
+
rescue
|
28
|
+
# If the test failed (threw a error), monkeypatch String.
|
29
|
+
# Most of this code came from http://www.ruby-forum.com/topic/144310 but was
|
30
|
+
# simplified for our use.
|
31
|
+
|
32
|
+
# Basic implementation of 'string' % { } like we need it. needs work.
|
33
|
+
class String
|
34
|
+
Percent = instance_method '%' unless defined? Percent
|
35
|
+
def % *a, &b
|
36
|
+
a.flatten!
|
37
|
+
|
38
|
+
string = case a.last
|
39
|
+
when Hash
|
40
|
+
expand a.pop
|
41
|
+
else
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
if a.empty?
|
46
|
+
string
|
47
|
+
else
|
48
|
+
Percent.bind(string).call(*a, &b)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
def expand! vars = {}
|
53
|
+
loop do
|
54
|
+
changed = false
|
55
|
+
vars.each do |var, value|
|
56
|
+
var = var.to_s
|
57
|
+
var.gsub! %r/[^a-zA-Z0-9_]/, ''
|
58
|
+
[
|
59
|
+
%r/\%\{#{ var }\}/,
|
60
|
+
].each do |pat|
|
61
|
+
changed = gsub! pat, "#{ value }"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
break unless changed
|
65
|
+
end
|
66
|
+
self
|
67
|
+
end
|
68
|
+
def expand opts = {}
|
69
|
+
dup.expand! opts
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
21
74
|
class PuppetLint::NoCodeError < StandardError; end
|
22
75
|
|
23
76
|
class PuppetLint
|
24
|
-
VERSION = '0.1.
|
77
|
+
VERSION = '0.1.10'
|
25
78
|
|
26
79
|
attr_reader :code, :file
|
27
80
|
|
28
|
-
def initialize
|
81
|
+
def initialize
|
29
82
|
@data = nil
|
30
|
-
@
|
31
|
-
@
|
32
|
-
|
33
|
-
|
34
|
-
|
83
|
+
@statistics = {:error => 0, :warning => 0}
|
84
|
+
@fileinfo = {:path => ''}
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.configuration
|
88
|
+
@configuration ||= PuppetLint::Configuration.new
|
89
|
+
end
|
90
|
+
|
91
|
+
def configuration
|
92
|
+
self.class.configuration
|
35
93
|
end
|
36
94
|
|
37
95
|
def file=(path)
|
38
96
|
if File.exist? path
|
39
|
-
@path =
|
97
|
+
@fileinfo[:path] = path
|
98
|
+
@fileinfo[:fullpath] = File.expand_path(path)
|
99
|
+
@fileinfo[:filename] = File.basename(path)
|
40
100
|
@data = File.read(path)
|
41
101
|
end
|
42
102
|
end
|
@@ -45,27 +105,48 @@ class PuppetLint
|
|
45
105
|
@data = value
|
46
106
|
end
|
47
107
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
57
|
-
if @with_filename
|
58
|
-
message.prepend("#{@path} - ")
|
108
|
+
def log_format
|
109
|
+
if configuration.log_format == ''
|
110
|
+
## recreate previous old log format as far as thats possible.
|
111
|
+
format = '%{KIND}: %{message} on line %{linenumber}'
|
112
|
+
if configuration.with_filename
|
113
|
+
format.prepend '%{path} - '
|
114
|
+
end
|
115
|
+
configuration.log_format = format
|
59
116
|
end
|
60
|
-
|
117
|
+
return configuration.log_format
|
118
|
+
end
|
119
|
+
|
120
|
+
def format_message(message)
|
121
|
+
format = log_format
|
122
|
+
puts format % message
|
61
123
|
end
|
62
124
|
|
125
|
+
def report(problems)
|
126
|
+
problems.each do |message|
|
127
|
+
@statistics[message[:kind]] += 1
|
128
|
+
## Add some default attributes.
|
129
|
+
message.merge!(@fileinfo) {|key, v1, v2| v1 }
|
130
|
+
message[:KIND] = message[:kind].to_s.upcase
|
131
|
+
|
132
|
+
if configuration.error_level == message[:kind] or configuration.error_level == :all
|
133
|
+
format_message message
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
63
138
|
def errors?
|
64
|
-
@
|
139
|
+
@statistics[:error] != 0
|
65
140
|
end
|
66
141
|
|
67
142
|
def warnings?
|
68
|
-
@
|
143
|
+
@statistics[:warning] != 0
|
144
|
+
end
|
145
|
+
|
146
|
+
def checks
|
147
|
+
PuppetLint::CheckPlugin.repository.map do |plugin|
|
148
|
+
plugin.new.checks
|
149
|
+
end.flatten
|
69
150
|
end
|
70
151
|
|
71
152
|
def run
|
@@ -74,17 +155,15 @@ class PuppetLint
|
|
74
155
|
end
|
75
156
|
|
76
157
|
PuppetLint::CheckPlugin.repository.each do |plugin|
|
77
|
-
|
78
|
-
case @error_level
|
79
|
-
when :warning
|
80
|
-
problems[:warnings].each { |warning| report :warnings, warning }
|
81
|
-
when :error
|
82
|
-
problems[:errors].each { |error| report :errors, error }
|
83
|
-
else
|
84
|
-
problems[:warnings].each { |warning| report :warnings, warning }
|
85
|
-
problems[:errors].each { |error| report :errors, error }
|
86
|
-
end
|
158
|
+
report plugin.new.run(@fileinfo[:path], @data)
|
87
159
|
end
|
88
160
|
end
|
89
161
|
end
|
90
162
|
|
163
|
+
# Default configuration options
|
164
|
+
PuppetLint.configuration.fail_on_warnings = false
|
165
|
+
PuppetLint.configuration.error_level = :all
|
166
|
+
PuppetLint.configuration.with_filename = false
|
167
|
+
PuppetLint.configuration.log_format = ''
|
168
|
+
|
169
|
+
require 'puppet-lint/plugins'
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class PuppetLint
|
2
|
+
class Configuration
|
3
|
+
def self.add_check(check)
|
4
|
+
define_method("#{check}_enabled?") do
|
5
|
+
settings["#{check}_disabled"] == true ? false : true
|
6
|
+
end
|
7
|
+
|
8
|
+
define_method("disable_#{check}") do
|
9
|
+
settings["#{check}_disabled"] = true
|
10
|
+
end
|
11
|
+
|
12
|
+
define_method("enable_#{check}") do
|
13
|
+
settings["#{check}_disabled"] = false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(method, *args, &block)
|
18
|
+
if method.to_s =~ /^(\w+)=$/
|
19
|
+
option = $1
|
20
|
+
add_option(option.to_s) if settings[option].nil?
|
21
|
+
settings[option] = args[0]
|
22
|
+
else
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_option(option)
|
28
|
+
self.class.add_option(option)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.add_option(option)
|
32
|
+
define_method("#{option}=") do |value|
|
33
|
+
settings[option] = value
|
34
|
+
end
|
35
|
+
|
36
|
+
define_method(option) do
|
37
|
+
settings[option]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_check(check)
|
42
|
+
self.class.add_check(check)
|
43
|
+
end
|
44
|
+
|
45
|
+
def settings
|
46
|
+
@settings ||= {}
|
47
|
+
end
|
48
|
+
|
49
|
+
def checks
|
50
|
+
self.public_methods.select { |method|
|
51
|
+
method =~ /^.+_enabled\?$/
|
52
|
+
}.map { |method|
|
53
|
+
method[0..-10]
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/puppet-lint/plugin.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
class PuppetLint
|
2
|
+
|
2
3
|
module Plugin
|
3
4
|
module ClassMethods
|
4
5
|
def repository
|
@@ -18,29 +19,139 @@ end
|
|
18
19
|
|
19
20
|
class PuppetLint::CheckPlugin
|
20
21
|
include PuppetLint::Plugin
|
21
|
-
attr_reader :
|
22
|
+
attr_reader :problems, :checks
|
22
23
|
|
23
24
|
def initialize
|
24
|
-
@
|
25
|
-
@
|
25
|
+
@problems = []
|
26
|
+
@checks = []
|
27
|
+
@default_info = {:check => 'unknown', :linenumber => 0}
|
26
28
|
end
|
27
29
|
|
28
|
-
def
|
29
|
-
@
|
30
|
+
def register_check(check)
|
31
|
+
@checks << check
|
30
32
|
end
|
31
33
|
|
32
|
-
|
33
|
-
|
34
|
+
# notify(kind, message_hash) #=> nil
|
35
|
+
#
|
36
|
+
# Adds the message to the problems array.
|
37
|
+
# The _kind_ gets added to the _message_hash_ by setting the key :_kind_.
|
38
|
+
# Typically, the _message_hash_ should contain following keys:
|
39
|
+
# <i>message</i>:: which contains a string value describing the problem
|
40
|
+
# <i>linenumber</i>:: which contains the line number on which the problem occurs.
|
41
|
+
# Besides the :_kind_ value that is being set, some other key/values are also
|
42
|
+
# added. Typically, this is
|
43
|
+
# <i>check</i>:: which contains the name of the check that is being executed.
|
44
|
+
# <i>linenumber</i>:: which defaults to 0 if the message does not already contain one.
|
45
|
+
#
|
46
|
+
# notify :warning, :message => "Something happened", :linenumber => 4
|
47
|
+
# => {:kind=>:warning, :message=>"Something happened", :linenumber=>4, :check=>'unknown'}
|
48
|
+
#
|
49
|
+
def notify(kind, message_hash)
|
50
|
+
message_hash[:kind] = kind
|
51
|
+
message_hash.merge!(@default_info) {|key, v1, v2| v1 }
|
52
|
+
@problems << message_hash
|
53
|
+
message_hash
|
34
54
|
end
|
35
55
|
|
36
56
|
def run(path, data)
|
37
|
-
|
57
|
+
lexer = Puppet::Parser::Lexer.new
|
58
|
+
lexer.string = data
|
59
|
+
@tokens = lexer.fullscan
|
60
|
+
@path = path
|
61
|
+
@data = data
|
62
|
+
|
63
|
+
test(path, data) if self.respond_to? :test
|
64
|
+
self.public_methods.select { |method|
|
65
|
+
method.start_with? 'lint_check_'
|
66
|
+
}.each { |method|
|
67
|
+
name = method[11..-1]
|
68
|
+
@default_info[:check] = name
|
69
|
+
self.send(method) if PuppetLint.configuration.send("#{name}_enabled?")
|
70
|
+
}
|
71
|
+
|
72
|
+
@problems
|
73
|
+
end
|
74
|
+
|
75
|
+
def filter_tokens
|
76
|
+
@title_tokens = []
|
77
|
+
@resource_indexes = []
|
78
|
+
@class_indexes = []
|
79
|
+
@defined_type_indexes = []
|
80
|
+
|
81
|
+
@tokens.each_index do |token_idx|
|
82
|
+
if @tokens[token_idx].first == :COLON
|
83
|
+
# gather a list of tokens that are resource titles
|
84
|
+
if @tokens[token_idx-1].first == :RBRACK
|
85
|
+
title_array_tokens = @tokens[@tokens.rindex { |r| r.first == :LBRACK }+1..token_idx-2]
|
86
|
+
@title_tokens += title_array_tokens.select { |token| [:STRING, :NAME].include? token.first }
|
87
|
+
else
|
88
|
+
if @tokens[token_idx + 1].first != :LBRACE
|
89
|
+
@title_tokens << @tokens[token_idx-1]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# gather a list of start and end indexes for resource attribute blocks
|
94
|
+
if @tokens[token_idx+1].first != :LBRACE
|
95
|
+
@resource_indexes << {:start => token_idx+1, :end => @tokens[token_idx+1..-1].index { |r| [:SEMIC, :RBRACE].include? r.first }+token_idx}
|
96
|
+
end
|
97
|
+
elsif [:CLASS, :DEFINE].include? @tokens[token_idx].first
|
98
|
+
lbrace_count = 0
|
99
|
+
@tokens[token_idx+1..-1].each_index do |class_token_idx|
|
100
|
+
idx = class_token_idx + token_idx
|
101
|
+
if @tokens[idx].first == :LBRACE
|
102
|
+
lbrace_count += 1
|
103
|
+
elsif @tokens[idx].first == :RBRACE
|
104
|
+
lbrace_count -= 1
|
105
|
+
if lbrace_count == 0
|
106
|
+
class_indexes << {:start => token_idx, :end => idx} if @tokens[token_idx].first == :CLASS
|
107
|
+
defined_type_indexes << {:start => token_idx, :end => idx} if @tokens[token_idx].first == :DEFINE
|
108
|
+
break
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def tokens
|
117
|
+
@tokens
|
118
|
+
end
|
119
|
+
|
120
|
+
def path
|
121
|
+
@path
|
122
|
+
end
|
123
|
+
|
124
|
+
def data
|
125
|
+
@data
|
126
|
+
end
|
127
|
+
|
128
|
+
def title_tokens
|
129
|
+
filter_tokens if @title_tokens.nil?
|
130
|
+
@title_tokens
|
131
|
+
end
|
132
|
+
|
133
|
+
def resource_indexes
|
134
|
+
filter_tokens if @resource_indexes.nil?
|
135
|
+
@resource_indexes
|
136
|
+
end
|
137
|
+
|
138
|
+
def class_indexes
|
139
|
+
filter_tokens if @class_indexes.nil?
|
140
|
+
@class_indexes
|
141
|
+
end
|
142
|
+
|
143
|
+
def defined_type_indexes
|
144
|
+
filter_tokens if @defined_type_indexes.nil?
|
145
|
+
@defined_type_indexes
|
146
|
+
end
|
38
147
|
|
39
|
-
|
148
|
+
def manifest_lines
|
149
|
+
@manifest_lines ||= @data.split("\n")
|
40
150
|
end
|
41
151
|
|
42
|
-
def
|
43
|
-
|
152
|
+
def self.check(name, &b)
|
153
|
+
PuppetLint.configuration.add_check name
|
154
|
+
define_method("lint_check_#{name}", b)
|
44
155
|
end
|
45
156
|
end
|
46
157
|
|