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
@@ -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