puppet-lint 0.1.9 → 0.1.10
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/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
|
|