puppet-lint 0.3.2 → 0.4.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/.gitignore +2 -0
  2. data/Gemfile +0 -1
  3. data/README.md +3 -1
  4. data/Rakefile +0 -19
  5. data/lib/puppet-lint.rb +65 -74
  6. data/lib/puppet-lint/bin.rb +21 -0
  7. data/lib/puppet-lint/checkplugin.rb +22 -0
  8. data/lib/puppet-lint/{plugin.rb → checks.rb} +66 -31
  9. data/lib/puppet-lint/configuration.rb +105 -0
  10. data/lib/puppet-lint/lexer.rb +94 -31
  11. data/lib/puppet-lint/lexer/token.rb +38 -2
  12. data/lib/puppet-lint/monkeypatches.rb +2 -0
  13. data/lib/puppet-lint/monkeypatches/string_percent.rb +52 -0
  14. data/lib/puppet-lint/monkeypatches/string_prepend.rb +7 -0
  15. data/lib/puppet-lint/plugins.rb +42 -0
  16. data/lib/puppet-lint/plugins/check_comments.rb +8 -1
  17. data/lib/puppet-lint/plugins/check_resources.rb +25 -3
  18. data/lib/puppet-lint/plugins/check_strings.rb +53 -6
  19. data/lib/puppet-lint/plugins/check_whitespace.rb +69 -26
  20. data/lib/puppet-lint/version.rb +1 -1
  21. data/puppet-lint.gemspec +0 -1
  22. data/spec/puppet-lint/configuration_spec.rb +1 -0
  23. data/spec/puppet-lint/lexer_spec.rb +2 -2
  24. data/spec/puppet-lint/plugins/check_comments/slash_comments_spec.rb +22 -0
  25. data/spec/puppet-lint/plugins/check_resources/file_mode_spec.rb +119 -6
  26. data/spec/puppet-lint/plugins/check_resources/unquoted_file_mode_spec.rb +30 -3
  27. data/spec/puppet-lint/plugins/check_resources/unquoted_resource_title_spec.rb +103 -3
  28. data/spec/puppet-lint/plugins/check_strings/double_quoted_strings_spec.rb +39 -0
  29. data/spec/puppet-lint/plugins/check_strings/only_variable_string_spec.rb +23 -0
  30. data/spec/puppet-lint/plugins/check_strings/quoted_booleans_spec.rb +88 -0
  31. data/spec/puppet-lint/plugins/check_strings/variables_not_enclosed_spec.rb +44 -0
  32. data/spec/puppet-lint/plugins/check_whitespace/arrow_alignment_spec.rb +171 -6
  33. data/spec/puppet-lint/plugins/check_whitespace/hard_tabs_spec.rb +22 -0
  34. data/spec/puppet-lint/plugins/check_whitespace/trailing_whitespace_spec.rb +44 -0
  35. data/spec/puppet-lint_spec.rb +1 -1
  36. metadata +10 -22
data/.gitignore CHANGED
@@ -1,6 +1,8 @@
1
1
  *.gem
2
2
  .bundle/
3
3
  .rbenv-version
4
+ .ruby-version
4
5
  Gemfile.lock
5
6
  vendor/gems
6
7
  coverage/
8
+ *.swp
data/Gemfile CHANGED
@@ -2,6 +2,5 @@ source :rubygems
2
2
 
3
3
  gem 'rake'
4
4
  gem 'rspec'
5
- gem 'rdoc'
6
5
  gem 'ruby-prof'
7
6
  gem 'rcov', :platform => :ruby_18
data/README.md CHANGED
@@ -55,6 +55,8 @@ At the moment, the following tests have been implemented:
55
55
  ### Resources
56
56
 
57
57
  * All resource titles should be quoted.
58
+ * An exception has been made for resource titles that consist of only
59
+ a variable standing by itself.
58
60
  * If a resource declaration includes an `ensure` attribute, it should be the
59
61
  first attribute specified.
60
62
  * Symbolic links should be declared by using an ensure value of `link` and
@@ -240,7 +242,7 @@ puppet-lint --no-80chars-check /path/to/my/manifest.pp
240
242
  puppet-lint will also check for a `.puppet-lint.rc` file in the current
241
243
  directory and your home directory and read in flags from there, so if you
242
244
  wanted to always skip the hard tab character check, you could create
243
- `~./puppet-lint.rc` containing
245
+ `~/.puppet-lint.rc` containing
244
246
 
245
247
  ```
246
248
  --no-hard_tabs-check
data/Rakefile CHANGED
@@ -2,7 +2,6 @@ require 'rake'
2
2
  require 'rspec/core/rake_task'
3
3
 
4
4
  task :default => :test
5
- #task :default => [:test, :rdoc]
6
5
 
7
6
  RSpec::Core::RakeTask.new(:test)
8
7
 
@@ -10,21 +9,3 @@ RSpec::Core::RakeTask.new(:cov) do |t|
10
9
  t.rcov = true
11
10
  t.rcov_opts = '--exclude "spec" --xrefs'
12
11
  end
13
-
14
- ### RDOC Tasks ###
15
- require 'rdoc'
16
- if (RDoc::VERSION.split('.') <=> ['2','4','2']) >= 0
17
- require 'rdoc/task'
18
- RDoc::Task.new(:rdoc) do |rdoc|
19
- rdoc.main = "README.md"
20
- rdoc.rdoc_files.include("README.md", "lib/**/*.rb")
21
- rdoc.options << "--all"
22
- end
23
- else
24
- require 'rake/rdoctask'
25
- Rake::RDocTask.new(:rdoc) do |rdoc|
26
- rdoc.main = "README.md"
27
- rdoc.rdoc_files.include("README.md", "lib/**/*.rb")
28
- rdoc.options << "--all"
29
- end
30
- end
data/lib/puppet-lint.rb CHANGED
@@ -1,102 +1,58 @@
1
1
  require 'puppet-lint/version'
2
2
  require 'puppet-lint/lexer'
3
3
  require 'puppet-lint/configuration'
4
- require 'puppet-lint/plugin'
4
+ require 'puppet-lint/checks'
5
5
  require 'puppet-lint/bin'
6
-
7
- unless String.respond_to?('prepend')
8
- class String
9
- def prepend(lead)
10
- self.replace "#{lead}#{self}"
11
- end
12
- end
13
- end
14
-
15
- # If we are using an older ruby version, we back-port the basic functionality
16
- # we need for formatting output: 'somestring' % <hash>
17
- begin
18
- if ('%{test}' % {:test => 'replaced'} == 'replaced')
19
- # If this works, we are all good to go.
20
- end
21
- rescue
22
- # If the test failed (threw a error), monkeypatch String.
23
- # Most of this code came from http://www.ruby-forum.com/topic/144310 but was
24
- # simplified for our use.
25
-
26
- # Basic implementation of 'string' % { } like we need it. needs work.
27
- class String
28
- Percent = instance_method '%' unless defined? Percent
29
- def % *a, &b
30
- a.flatten!
31
-
32
- string = case a.last
33
- when Hash
34
- expand a.pop
35
- else
36
- self
37
- end
38
-
39
- if a.empty?
40
- string
41
- else
42
- Percent.bind(string).call(a, &b)
43
- end
44
-
45
- end
46
- def expand! vars = {}
47
- loop do
48
- changed = false
49
- vars.each do |var, value|
50
- var = var.to_s
51
- var.gsub! %r/[^a-zA-Z0-9_]/, ''
52
- [
53
- %r/\%\{#{ var }\}/,
54
- ].each do |pat|
55
- changed = gsub! pat, "#{ value }"
56
- end
57
- end
58
- break unless changed
59
- end
60
- self
61
- end
62
- def expand opts = {}
63
- dup.expand! opts
64
- end
65
- end
66
- end
6
+ require 'puppet-lint/monkeypatches'
67
7
 
68
8
  class PuppetLint::NoCodeError < StandardError; end
69
9
 
70
10
  class PuppetLint
71
- attr_reader :data
11
+ # Public: Gets/Sets the String manifest code to be checked.
12
+ attr_accessor :code
13
+
14
+ attr_reader :manifest
72
15
 
16
+ # Public: Initialise a new PuppetLint object.
73
17
  def initialize
74
- @data = nil
75
- @statistics = {:error => 0, :warning => 0}
18
+ @code = nil
19
+ @statistics = {:error => 0, :warning => 0, :fixed => 0}
76
20
  @fileinfo = {:path => ''}
21
+ @manifest = ''
77
22
  end
78
23
 
24
+ # Public: Access PuppetLint's configuration from outside the class.
25
+ #
26
+ # Returns a PuppetLint::Configuration object.
79
27
  def self.configuration
80
28
  @configuration ||= PuppetLint::Configuration.new
81
29
  end
82
30
 
31
+ # Public: Access PuppetLint's configuration from inside the class.
32
+ #
33
+ # Returns a PuppetLint::Configuration object.
83
34
  def configuration
84
35
  self.class.configuration
85
36
  end
86
37
 
38
+ # Public: Set the path of the manifest file to be tested and read the
39
+ # contents of the file.
40
+ #
41
+ # Returns nothing.
87
42
  def file=(path)
88
43
  if File.exist? path
89
44
  @fileinfo[:path] = path
90
45
  @fileinfo[:fullpath] = File.expand_path(path)
91
46
  @fileinfo[:filename] = File.basename(path)
92
- @data = File.read(path)
47
+ @code = File.read(path)
93
48
  end
94
49
  end
95
50
 
96
- def code=(value)
97
- @data = value
98
- end
99
-
51
+ # Internal: Retrieve the format string to be used when writing problems to
52
+ # STDOUT. If the user has not specified a custom log format, build one for
53
+ # them.
54
+ #
55
+ # Returns a format String to be used with String#%.
100
56
  def log_format
101
57
  if configuration.log_format == ''
102
58
  ## recreate previous old log format as far as thats possible.
@@ -109,50 +65,85 @@ class PuppetLint
109
65
  return configuration.log_format
110
66
  end
111
67
 
68
+ # Internal: Format a problem message and print it to STDOUT.
69
+ #
70
+ # message - A Hash containing all the information about a problem.
71
+ #
72
+ # Returns nothing.
112
73
  def format_message(message)
113
74
  format = log_format
114
75
  puts format % message
115
76
  end
116
77
 
78
+ # Internal: Print out the line of the manifest on which the problem was found
79
+ # as well as a marker pointing to the location on the line.
80
+ #
81
+ # message - A Hash containing all the information about a problem.
82
+ # linter - The PuppetLint::Checks object that was used to test the manifest.
83
+ #
84
+ # Returns nothing.
117
85
  def print_context(message, linter)
118
86
  # XXX: I don't really like the way this has been implemented (passing the
119
87
  # linter object down through layers of functions. Refactor me!
120
88
  return if message[:check] == 'documentation'
89
+ return if message[:kind] == :fixed
121
90
  line = linter.manifest_lines[message[:linenumber] - 1]
122
91
  offset = line.index(/\S/)
123
92
  puts "\n #{line.strip}"
124
93
  printf "%#{message[:column] + 2 - offset}s\n\n", '^'
125
94
  end
126
95
 
96
+ # Internal: Print the reported problems with a manifest to stdout.
97
+ #
98
+ # problems - An Array of problem Hashes as returned by
99
+ # PuppetLint::Checks#run.
100
+ # linter - The PuppetLint::Checks object that was used to test the
101
+ # manifest.
102
+ #
103
+ # Returns nothing.
127
104
  def report(problems, linter)
128
105
  problems.each do |message|
129
106
  @statistics[message[:kind]] += 1
130
- ## Add some default attributes.
107
+
131
108
  message.merge!(@fileinfo) {|key, v1, v2| v1 }
132
109
  message[:KIND] = message[:kind].to_s.upcase
133
110
 
134
- if configuration.error_level == message[:kind] or configuration.error_level == :all
111
+ if message[:kind] == :fixed || [message[:kind], :all].include?(configuration.error_level)
135
112
  format_message message
136
113
  print_context(message, linter) if configuration.with_context
137
114
  end
138
115
  end
139
116
  end
140
117
 
118
+ # Public: Determine if PuppetLint found any errors in the manifest.
119
+ #
120
+ # Returns true if errors were found, otherwise returns false.
141
121
  def errors?
142
122
  @statistics[:error] != 0
143
123
  end
144
124
 
125
+ # Public: Determine if PuppetLint found any warnings in the manifest.
126
+ #
127
+ # Returns true if warnings were found, otherwise returns false.
145
128
  def warnings?
146
129
  @statistics[:warning] != 0
147
130
  end
148
131
 
132
+ # Public: Run the loaded manifest code through the lint checks and print the
133
+ # results of the checks to stdout.
134
+ #
135
+ # Returns nothing.
136
+ # Raises PuppetLint::NoCodeError if no manifest code has been loaded.
149
137
  def run
150
- if @data.nil?
138
+ if @code.nil?
151
139
  raise PuppetLint::NoCodeError
152
140
  end
153
141
 
154
142
  linter = PuppetLint::Checks.new
155
- problems = linter.run(@fileinfo, @data)
143
+ problems = linter.run(@fileinfo, @code)
144
+
145
+ @manifest = linter.manifest if PuppetLint.configuration.fix
146
+
156
147
  report problems, linter
157
148
  end
158
149
  end
@@ -1,10 +1,17 @@
1
1
  require 'optparse'
2
2
 
3
3
  class PuppetLint::Bin
4
+ # Public: Initialise a new PuppetLint::Bin.
5
+ #
6
+ # args - An Array of command line argument Strings to be passed to the option
7
+ # parser.
4
8
  def initialize(args)
5
9
  @args = args
6
10
  end
7
11
 
12
+ # Public: Run puppet-lint as a command line tool.
13
+ #
14
+ # Returns an Integer exit code to be passed back to the shell.
8
15
  def run
9
16
  help = <<-EOHELP
10
17
  Puppet-lint
@@ -41,6 +48,14 @@ class PuppetLint::Bin
41
48
  PuppetLint.configuration.error_level = el
42
49
  end
43
50
 
51
+ opts.on("-l", '--load FILE', 'Load a file containing custom puppet-lint checks.') do |f|
52
+ load f
53
+ end
54
+
55
+ opts.on('-f', '--fix', 'Attempt to automatically fix errors') do
56
+ PuppetLint.configuration.fix = true
57
+ end
58
+
44
59
  opts.on("--log-format FORMAT",
45
60
  "Change the log format.", "Overrides --with-filename.",
46
61
  "The following placeholders can be used:",
@@ -111,6 +126,12 @@ class PuppetLint::Bin
111
126
  if l.errors? or (l.warnings? and PuppetLint.configuration.fail_on_warnings)
112
127
  return_val = 1
113
128
  end
129
+
130
+ if PuppetLint.configuration.fix
131
+ File.open(f, 'w') do |fd|
132
+ fd.puts l.manifest
133
+ end
134
+ end
114
135
  end
115
136
  return return_val
116
137
 
@@ -0,0 +1,22 @@
1
+ class PuppetLint::CheckPlugin
2
+ # Public: Define a new lint check.
3
+ #
4
+ # name - The String name of the check.
5
+ # b - The Block implementation of the check.
6
+ #
7
+ # Returns nothing.
8
+ def self.check(name, &b)
9
+ PuppetLint.configuration.add_check(name, &b)
10
+ end
11
+
12
+ # Public: Define a new check helper method.
13
+ #
14
+ # name - The String name of the helper.
15
+ # b - The Block implementation of the helper.
16
+ #
17
+ # Returns nothing.
18
+ def self.helper(name, &b)
19
+ PuppetLint.configuration.add_helper(name, &b)
20
+ end
21
+ end
22
+
@@ -1,7 +1,14 @@
1
+ require 'puppet-lint/checkplugin'
2
+
1
3
  class PuppetLint::Checks
4
+ # Public: Get an Array of problem Hashes.
2
5
  attr_reader :problems
3
- attr_reader :manifest_lines
4
6
 
7
+ # Public: Get an Array of PuppetLint::Lexer::Token objects.
8
+ attr_reader :tokens
9
+
10
+ # Public: Initialise a new PuppetLint::Checks object and prepare the check
11
+ # methods.
5
12
  def initialize
6
13
  @problems = []
7
14
  @default_info = {:check => 'unknown', :linenumber => 0, :column => 0}
@@ -10,30 +17,39 @@ class PuppetLint::Checks
10
17
  method = PuppetLint.configuration.check_method[check]
11
18
  self.class.send(:define_method, "lint_check_#{check}", &method)
12
19
  end
20
+
21
+ PuppetLint.configuration.helpers.each do |helper|
22
+ method = PuppetLint.configuration.helper_method[helper]
23
+ self.class.send(:define_method, helper, &method)
24
+ end
13
25
  end
14
26
 
15
- # notify(kind, message_hash) #=> nil
16
- #
17
- # Adds the message to the problems array.
18
- # The _kind_ gets added to the _message_hash_ by setting the key :_kind_.
19
- # Typically, the _message_hash_ should contain following keys:
20
- # <i>message</i>:: which contains a string value describing the problem
21
- # <i>linenumber</i>:: which contains the line number on which the problem occurs.
22
- # Besides the :_kind_ value that is being set, some other key/values are also
23
- # added. Typically, this is
24
- # <i>check</i>:: which contains the name of the check that is being executed.
25
- # <i>linenumber</i>:: which defaults to 0 if the message does not already contain one.
27
+ # Public: Add a message to the problems array.
26
28
  #
27
- # notify :warning, :message => "Something happened", :linenumber => 4
28
- # => {:kind=>:warning, :message=>"Something happened", :linenumber=>4, :check=>'unknown'}
29
+ # kind - The kind of problem as a Symbol (:warning, :error).
30
+ # problem - A Hash containing the attributes of the problem.
31
+ # :message - The String message describing the problem.
32
+ # :linenumber - The Integer line number of the location of the problem.
33
+ # :check - The String name of the check that the problem came from.
34
+ # :column - The Integer column number of the location of the problem.
29
35
  #
30
- def notify(kind, message_hash)
31
- message_hash[:kind] = kind
32
- message_hash.merge!(@default_info) {|key, v1, v2| v1 }
33
- @problems << message_hash
34
- message_hash
36
+ # Returns nothing.
37
+ def notify(kind, problem)
38
+ problem[:kind] = kind
39
+ problem.merge!(@default_info) {|key, v1, v2| v1 }
40
+ @problems << problem
35
41
  end
36
42
 
43
+ # Internal: Tokenise the manifest code and prepare it for checking.
44
+ #
45
+ # fileinfo - A Hash containing the following:
46
+ # :fullpath - The expanded path to the file as a String.
47
+ # :filename - The name of the file as a String.
48
+ # :path - The original path to the file as passed to puppet-lint as
49
+ # a String.
50
+ # data - The String manifest code to be checked.
51
+ #
52
+ # Returns nothing.
37
53
  def load_data(fileinfo, data)
38
54
  lexer = PuppetLint::Lexer.new
39
55
  begin
@@ -50,6 +66,16 @@ class PuppetLint::Checks
50
66
  @data = data
51
67
  end
52
68
 
69
+ # Internal: Run the lint checks over the manifest code.
70
+ #
71
+ # fileinfo - A Hash containing the following:
72
+ # :fullpath - The expanded path to the file as a String.
73
+ # :filename - The name of the file as a String.
74
+ # :path - The original path to the file as passed to puppet-lint as
75
+ # a String.
76
+ # data - The String manifest code to be checked.
77
+ #
78
+ # Returns an Array of problem Hashes.
53
79
  def run(fileinfo, data)
54
80
  load_data(fileinfo, data)
55
81
 
@@ -61,6 +87,9 @@ class PuppetLint::Checks
61
87
  @problems
62
88
  end
63
89
 
90
+ # Internal: Get a list of checks that have not been disabled.
91
+ #
92
+ # Returns an Array of String check names.
64
93
  def enabled_checks
65
94
  @enabled_checks ||= Proc.new do
66
95
  self.public_methods.select { |method|
@@ -73,14 +102,16 @@ class PuppetLint::Checks
73
102
  end.call
74
103
  end
75
104
 
76
- def tokens
77
- @tokens
78
- end
79
-
105
+ # Public: Get the full expanded path to the file being checked.
106
+ #
107
+ # Returns a String path.
80
108
  def fullpath
81
109
  @fileinfo[:fullpath]
82
110
  end
83
111
 
112
+ # Public: Retrieve a list of tokens that represent resource titles.
113
+ #
114
+ # Returns an Array of PuppetLint::Lexer::Token objects.
84
115
  def title_tokens
85
116
  @title_tokens ||= Proc.new do
86
117
  result = []
@@ -107,7 +138,7 @@ class PuppetLint::Checks
107
138
  end.call
108
139
  end
109
140
 
110
- # Internal: Calculate the positions of all resource declarations within the
141
+ # Public: Calculate the positions of all resource declarations within the
111
142
  # tokenised manifest. These positions only point to the content of the
112
143
  # resource declaration, they do not include resource types or
113
144
  # titles/namevars.
@@ -146,7 +177,7 @@ class PuppetLint::Checks
146
177
  end.call
147
178
  end
148
179
 
149
- # Internal: Calculate the positions of all class definitions within the
180
+ # Public: Calculate the positions of all class definitions within the
150
181
  # tokenised manifest.
151
182
  #
152
183
  # Returns an Array of Hashes, each containing:
@@ -185,7 +216,7 @@ class PuppetLint::Checks
185
216
  end.call
186
217
  end
187
218
 
188
- # Internal: Calculate the positions of all defined type definitions within
219
+ # Public: Calculate the positions of all defined type definitions within
189
220
  # the tokenised manifest.
190
221
  #
191
222
  # Returns an Array of Hashes, each containing:
@@ -222,18 +253,22 @@ class PuppetLint::Checks
222
253
  end.call
223
254
  end
224
255
 
256
+ # Public: Retrieves a list of token types that are considered to be
257
+ # formatting tokens (ie whitespace, newlines, etc).
258
+ #
259
+ # Returns an Array of Symbols.
225
260
  def formatting_tokens
226
261
  @formatting_tokens ||= PuppetLint::Lexer::FORMATTING_TOKENS
227
262
  end
228
263
 
264
+ # Public: Access the lines of the manifest that is being checked.
265
+ #
266
+ # Returns an Array of Strings.
229
267
  def manifest_lines
230
268
  @manifest_lines ||= @data.split("\n")
231
269
  end
232
- end
233
270
 
234
- class PuppetLint::CheckPlugin
235
- def self.check(name, &b)
236
- PuppetLint.configuration.add_check(name, &b)
271
+ def manifest
272
+ tokens.map { |t| t.to_manifest }.join('')
237
273
  end
238
274
  end
239
-