puppet-lint 0.3.2 → 0.4.0.pre1

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.
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
@@ -1,19 +1,50 @@
1
1
  class PuppetLint
2
2
  class Configuration
3
+ # Internal: Add helper methods for a new check to the
4
+ # PuppetLint::Configuration object.
5
+ #
6
+ # check - The String name of the check.
7
+ #
8
+ # Returns nothing.
9
+ #
10
+ # Signature
11
+ #
12
+ # <check>_enabled?
13
+ # disable_<check>
14
+ # enable_<check>
3
15
  def self.add_check(check)
16
+ # Public: Determine if the named check is enabled.
17
+ #
18
+ # Returns true if the check is enabled, otherwise return false.
4
19
  define_method("#{check}_enabled?") do
5
20
  settings["#{check}_disabled"] == true ? false : true
6
21
  end
7
22
 
23
+ # Public: Disable the named check.
24
+ #
25
+ # Returns nothing.
8
26
  define_method("disable_#{check}") do
9
27
  settings["#{check}_disabled"] = true
10
28
  end
11
29
 
30
+ # Public: Enable the named check.
31
+ #
32
+ # Returns nothing.
12
33
  define_method("enable_#{check}") do
13
34
  settings["#{check}_disabled"] = false
14
35
  end
15
36
  end
16
37
 
38
+ # Public: Catch situations where options are being set for the first time
39
+ # and create the necessary methods to get & set the option in the future.
40
+ #
41
+ # args[0] - The value to set the option to.
42
+ #
43
+ # Returns nothing.
44
+ #
45
+ # Signature
46
+ #
47
+ # <option>=(value)
17
48
  def method_missing(method, *args, &block)
18
49
  if method.to_s =~ /^(\w+)=$/
19
50
  option = $1
@@ -24,37 +55,110 @@ class PuppetLint
24
55
  end
25
56
  end
26
57
 
58
+ # Internal: Add options to the PuppetLint::Configuration object from inside
59
+ # the class.
60
+ #
61
+ # option - The String name of the option.
62
+ #
63
+ # Returns nothing.
64
+ #
65
+ # Signature
66
+ #
67
+ # <option>
68
+ # <option>=(value)
27
69
  def add_option(option)
28
70
  self.class.add_option(option)
29
71
  end
30
72
 
73
+ # Public: Add an option to the PuppetLint::Configuration object from
74
+ # outside the class.
75
+ #
76
+ # option - The String name of the option.
77
+ #
78
+ # Returns nothing.
79
+ #
80
+ # Signature
81
+ #
82
+ # <option>
83
+ # <option>=(value)
31
84
  def self.add_option(option)
85
+ # Public: Set the value of the named option.
86
+ #
87
+ # value - The value to set the option to.
88
+ #
89
+ # Returns nothing.
32
90
  define_method("#{option}=") do |value|
33
91
  settings[option] = value
34
92
  end
35
93
 
94
+ # Public: Get the value of the named option.
95
+ #
96
+ # Returns the value of the option.
36
97
  define_method(option) do
37
98
  settings[option]
38
99
  end
39
100
  end
40
101
 
102
+ # Internal: Register a new check.
103
+ #
104
+ # check - The String name of the check
105
+ # b - The Block containing the logic of the check
106
+ #
107
+ # Returns nothing.
41
108
  def add_check(check, &b)
42
109
  self.class.add_check(check)
43
110
  check_method[check] = b
44
111
  end
45
112
 
113
+ # Internal: Register a new check helper method.
114
+ #
115
+ # name - The String name of the method.
116
+ # b - The Block containing the logic of the helper.
117
+ #
118
+ # Returns nothing.
119
+ def add_helper(name, &b)
120
+ helper_method[name] = b
121
+ end
122
+
123
+ # Internal: Access the internal storage for settings.
124
+ #
125
+ # Returns a Hash containing all the settings.
46
126
  def settings
47
127
  @settings ||= {}
48
128
  end
49
129
 
130
+ # Internal: Access the internal storage for check method blocks.
131
+ #
132
+ # Returns a Hash containing all the check blocks.
50
133
  def check_method
51
134
  @check_method ||= {}
52
135
  end
53
136
 
137
+ # Public: Get a list of all the defined checks.
138
+ #
139
+ # Returns an Array of String check names.
54
140
  def checks
55
141
  check_method.keys
56
142
  end
57
143
 
144
+ # Internal: Access the internal storage for helper method blocks.
145
+ #
146
+ # Returns a Hash containing all the helper blocks.
147
+ def helper_method
148
+ @helper_method ||= {}
149
+ end
150
+
151
+ # Public: Get a list of all the helper methods.
152
+ #
153
+ # Returns an Array of String method names.
154
+ def helpers
155
+ helper_method.keys
156
+ end
157
+
158
+ # Public: Clear the PuppetLint::Configuration storage and set some sane
159
+ # default values.
160
+ #
161
+ # Returns nothing.
58
162
  def defaults
59
163
  settings.clear
60
164
  self.with_filename = false
@@ -62,6 +166,7 @@ class PuppetLint
62
166
  self.error_level = :all
63
167
  self.log_format = ''
64
168
  self.with_context = false
169
+ self.fix = false
65
170
  end
66
171
  end
67
172
  end
@@ -5,7 +5,17 @@ require 'set'
5
5
 
6
6
  class PuppetLint
7
7
  class LexerError < StandardError
8
- attr_reader :line_no, :column
8
+ # Internal: Get the Integer line number of the location of the error.
9
+ attr_reader :line_no
10
+
11
+ # Internal: Get the Integer column number of the location of the error.
12
+ attr_reader :column
13
+
14
+ # Internal: Initialise a new PuppetLint::LexerError object.
15
+ #
16
+ # code - The String manifest code being tokenised.
17
+ # offset - The Integer position in the code string that the tokeniser was
18
+ # at when it encountered the error.
9
19
  def initialize(code, offset)
10
20
  chunk = code[0..offset]
11
21
  @line_no = chunk.count("\n") + 1
@@ -19,35 +29,43 @@ class PuppetLint
19
29
  end
20
30
 
21
31
  class Lexer
32
+ # Internal: A Hash whose keys are Strings representing reserved keywords in
33
+ # the Puppet DSL.
22
34
  KEYWORDS = {
23
- 'class' => true,
24
- 'case' => true,
25
- 'default' => true,
26
- 'define' => true,
27
- 'import' => true,
28
- 'if' => true,
29
- 'else' => true,
30
- 'elsif' => true,
35
+ 'class' => true,
36
+ 'case' => true,
37
+ 'default' => true,
38
+ 'define' => true,
39
+ 'import' => true,
40
+ 'if' => true,
41
+ 'else' => true,
42
+ 'elsif' => true,
31
43
  'inherits' => true,
32
- 'node' => true,
33
- 'and' => true,
34
- 'or' => true,
35
- 'undef' => true,
36
- 'true' => true,
37
- 'false' => true,
38
- 'in' => true,
39
- 'unless' => true,
44
+ 'node' => true,
45
+ 'and' => true,
46
+ 'or' => true,
47
+ 'undef' => true,
48
+ 'true' => true,
49
+ 'false' => true,
50
+ 'in' => true,
51
+ 'unless' => true,
40
52
  }
41
53
 
54
+ # Internal: A Hash whose keys are Symbols representing token types which
55
+ # a regular expression can follow.
42
56
  REGEX_PREV_TOKENS = {
43
- :NODE => true,
44
- :LBRACE => true,
45
- :RBRACE => true,
46
- :MATCH => true,
57
+ :NODE => true,
58
+ :LBRACE => true,
59
+ :RBRACE => true,
60
+ :MATCH => true,
47
61
  :NOMATCH => true,
48
- :COMMA => true,
62
+ :COMMA => true,
49
63
  }
50
64
 
65
+ # Internal: An Array of Arrays containing tokens that can be described by
66
+ # a single regular expression. Each sub-Array contains 2 elements, the
67
+ # name of the token as a Symbol and a regular expression describing the
68
+ # value of the token.
51
69
  KNOWN_TOKENS = [
52
70
  [:CLASSREF, /\A(((::){0,1}[A-Z][-\w]*)+)/],
53
71
  [:NUMBER, /\A\b((?:0[xX][0-9A-Fa-f]+|0?\d+(?:\.\d+)?(?:[eE]-?\d+)?))\b/],
@@ -93,19 +111,32 @@ class PuppetLint
93
111
  [:TIMES, /\A(\*)/],
94
112
  ]
95
113
 
114
+ # Internal: A Hash whose keys are Symbols representing token types which
115
+ # are considered to be formatting tokens (i.e. tokens that don't contain
116
+ # code).
96
117
  FORMATTING_TOKENS = {
97
- :WHITESPACE => true,
98
- :NEWLINE => true,
99
- :COMMENT => true,
100
- :MLCOMMENT => true,
118
+ :WHITESPACE => true,
119
+ :NEWLINE => true,
120
+ :COMMENT => true,
121
+ :MLCOMMENT => true,
101
122
  :SLASH_COMMENT => true,
102
- :INDENT => true,
123
+ :INDENT => true,
103
124
  }
104
125
 
126
+ # Internal: Access the internal token storage.
127
+ #
128
+ # Returns an Array of PuppetLint::Lexer::Toxen objects.
105
129
  def tokens
106
130
  @tokens ||= []
107
131
  end
108
132
 
133
+ # Internal: Convert a Puppet manifest into tokens.
134
+ #
135
+ # code - The Puppet manifest to be tokenised as a String.
136
+ #
137
+ # Returns an Array of PuppetLint::Lexer::Token objects.
138
+ # Raises PuppetLint::LexerError if it encounters unexpected characters
139
+ # (usually the result of syntax errors).
109
140
  def tokenise(code)
110
141
  code.chomp!
111
142
 
@@ -165,8 +196,7 @@ class PuppetLint
165
196
  mlcomment_size = mlcomment.size
166
197
  mlcomment.sub!(/\A\/\* ?/, '')
167
198
  mlcomment.sub!(/ ?\*\/\Z/, '')
168
- mlcomment.gsub!(/^ ?\* ?/, '')
169
- mlcomment.gsub!(/\n/, ' ')
199
+ mlcomment.gsub!(/ *\* ?/, '')
170
200
  mlcomment.strip!
171
201
  tokens << new_token(:MLCOMMENT, mlcomment, :chunk => code[0..i])
172
202
  i += mlcomment_size
@@ -176,7 +206,7 @@ class PuppetLint
176
206
  tokens << new_token(:REGEX, str_content[0..-2], :chunk => code[0..i])
177
207
  i += str_content.size + 1
178
208
 
179
- elsif indent = chunk[/\A\n([ \t]+)/m, 1]
209
+ elsif indent = chunk[/\A\r?\n([ \t]+)/m, 1]
180
210
  tokens << new_token(:NEWLINE, '\n', :chunk => code[0..i])
181
211
  tokens << new_token(:INDENT, indent, :chunk => code[0..i+1])
182
212
  i += indent.size + 1
@@ -185,7 +215,7 @@ class PuppetLint
185
215
  tokens << new_token(:WHITESPACE, whitespace, :chunk => code[0..i])
186
216
  i += whitespace.size
187
217
 
188
- elsif chunk.match(/\A\n/)
218
+ elsif chunk.match(/\A\r?\n/)
189
219
  tokens << new_token(:NEWLINE, '\n', :chunk => code[0..i])
190
220
  i += 1
191
221
 
@@ -202,6 +232,10 @@ class PuppetLint
202
232
  tokens
203
233
  end
204
234
 
235
+ # Internal: Given the tokens already processed, determine if the next token
236
+ # could be a regular expression.
237
+ #
238
+ # Returns true if the next token could be a regex, otherwise return false.
205
239
  def possible_regex?
206
240
  prev_token = tokens.reject { |r|
207
241
  FORMATTING_TOKENS.include? r.type
@@ -216,6 +250,19 @@ class PuppetLint
216
250
  end
217
251
  end
218
252
 
253
+ # Internal: Create a new PuppetLint::Lexer::Token object, calculate its
254
+ # line number and column and then add it to the Linked List of tokens.
255
+ #
256
+ # type - The Symbol token type.
257
+ # value - The token value.
258
+ # opts - A Hash of additional values required to determine line number and
259
+ # column:
260
+ # :chunk - The String chunk of the manifest that has been tokenised so
261
+ # far.
262
+ # :line - The Integer line number if calculated externally.
263
+ # :column - The Integer column number if calculated externally.
264
+ #
265
+ # Returns the instantiated PuppetLint::Lexer::Token object.
219
266
  def new_token(type, value, opts = {})
220
267
  if opts[:chunk]
221
268
  line_no = opts[:chunk].count("\n") + 1
@@ -248,6 +295,15 @@ class PuppetLint
248
295
  token
249
296
  end
250
297
 
298
+ # Internal: Split a string on multiple terminators, excluding escaped
299
+ # terminators.
300
+ #
301
+ # string - The String to be split.
302
+ # terminators - The String of terminators that the String should be split
303
+ # on.
304
+ #
305
+ # Returns an Array consisting of two Strings, the String up to the first
306
+ # terminator and the terminator that was found.
251
307
  def get_string_segment(string, terminators)
252
308
  str = string.scan_until(/([^\\]|^|[^\\])([\\]{2})*[#{terminators}]+/)
253
309
  begin
@@ -257,6 +313,13 @@ class PuppetLint
257
313
  end
258
314
  end
259
315
 
316
+ # Internal: Tokenise the contents of a double quoted string.
317
+ #
318
+ # string - The String to be tokenised.
319
+ # line - The Integer line number of the start of the passed string.
320
+ # column - The Integer column number of the start of the passed string.
321
+ #
322
+ # Returns nothing.
260
323
  def interpolate_string(string, line, column)
261
324
  ss = StringScanner.new(string)
262
325
  first = true
@@ -2,10 +2,10 @@ class PuppetLint
2
2
  class Lexer
3
3
  class Token
4
4
  # Internal: Returns the Symbol type of the Token.
5
- attr_reader :type
5
+ attr_accessor :type
6
6
 
7
7
  # Internal: Returns the String value of the Token.
8
- attr_reader :value
8
+ attr_accessor :value
9
9
 
10
10
  # Internal: Returns the Integer line number of the manifest text where
11
11
  # the Token can be found.
@@ -57,6 +57,42 @@ class PuppetLint
57
57
  def inspect
58
58
  "<Token #{@type.inspect} (#{@value}) @#{@line}:#{@column}>"
59
59
  end
60
+
61
+ # Internal: Produce a Puppet DSL representation of a Token.
62
+ #
63
+ # Returns a Puppet DSL String.
64
+ def to_manifest
65
+ case @type
66
+ when :STRING
67
+ "\"#{@value}\""
68
+ when :SSTRING
69
+ "'#{@value}'"
70
+ when :DQPRE
71
+ "\"#{@value}"
72
+ when :DQPOST
73
+ "#{@value}\""
74
+ when :VARIABLE
75
+ if !@prev_code_token.nil? && [:DQPRE, :DQMID].include?(@prev_code_token.type)
76
+ "${#{@value}}"
77
+ else
78
+ "$#{@value}"
79
+ end
80
+ when :UNENC_VARIABLE
81
+ "$#{@value}"
82
+ when :NEWLINE
83
+ "\n"
84
+ when :COMMENT
85
+ if @value.start_with?('#') || @value.empty?
86
+ "##{@value}"
87
+ else
88
+ "# #{@value}"
89
+ end
90
+ when :REGEX
91
+ "/#{@value}/"
92
+ else
93
+ @value
94
+ end
95
+ end
60
96
  end
61
97
  end
62
98
  end
@@ -0,0 +1,2 @@
1
+ require 'puppet-lint/monkeypatches/string_prepend'
2
+ require 'puppet-lint/monkeypatches/string_percent'
@@ -0,0 +1,52 @@
1
+ # If we are using an older ruby version, we back-port the basic functionality
2
+ # we need for formatting output: 'somestring' % <hash>
3
+ begin
4
+ if ('%{test}' % {:test => 'replaced'} == 'replaced')
5
+ # If this works, we are all good to go.
6
+ end
7
+ rescue
8
+ # If the test failed (threw a error), monkeypatch String.
9
+ # Most of this code came from http://www.ruby-forum.com/topic/144310 but was
10
+ # simplified for our use.
11
+
12
+ # Basic implementation of 'string' % { } like we need it. needs work.
13
+ class String
14
+ Percent = instance_method '%' unless defined? Percent
15
+ def % *a, &b
16
+ a.flatten!
17
+
18
+ string = case a.last
19
+ when Hash
20
+ expand a.pop
21
+ else
22
+ self
23
+ end
24
+
25
+ if a.empty?
26
+ string
27
+ else
28
+ Percent.bind(string).call(a, &b)
29
+ end
30
+
31
+ end
32
+ def expand! vars = {}
33
+ loop do
34
+ changed = false
35
+ vars.each do |var, value|
36
+ var = var.to_s
37
+ var.gsub! %r/[^a-zA-Z0-9_]/, ''
38
+ [
39
+ %r/\%\{#{ var }\}/,
40
+ ].each do |pat|
41
+ changed = gsub! pat, "#{ value }"
42
+ end
43
+ end
44
+ break unless changed
45
+ end
46
+ self
47
+ end
48
+ def expand opts = {}
49
+ dup.expand! opts
50
+ end
51
+ end
52
+ end