puppet-lint 1.0.1 → 1.1.0

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/README.md CHANGED
@@ -27,6 +27,40 @@ If you want to test your entire Puppet manifest directory, you can add
27
27
 
28
28
  rake lint
29
29
 
30
+ If you want to modify the default behaviour of the rake task, you can modify
31
+ the PuppetLint configuration by defining the task yourself.
32
+
33
+ PuppetLint::RakeTask.new :lint do |config|
34
+ # Pattern of files to check, defaults to `**/*.pp`
35
+ config.pattern = 'modules'
36
+
37
+ # Pattern of files to ignore
38
+ config.ignore_paths = ['modules/apt', 'modules/stdlib']
39
+
40
+ # List of checks to disable
41
+ config.disable_checks = ['documentation', '80chars']
42
+
43
+ # Should puppet-lint prefix it's output with the file being checked,
44
+ # defaults to true
45
+ config.with_filename = false
46
+
47
+ # Should the task fail if there were any warnings, defaults to false
48
+ config.fail_on_warnings = true
49
+
50
+ # Format string for puppet-lint's output (see the puppet-lint help output
51
+ # for details
52
+ config.log_format = '%{filename} - %{message}'
53
+
54
+ # Print out the context for the problem, defaults to false
55
+ config.with_context = true
56
+
57
+ # Enable automatic fixing of problems, defaults to false
58
+ config.fix = true
59
+
60
+ # Show ignored problems in the output, defaults to false
61
+ config.show_ignored = true
62
+ end
63
+
30
64
  ## Implemented tests
31
65
 
32
66
  At the moment, the following tests have been implemented:
@@ -255,6 +255,9 @@ class PuppetLint::Data
255
255
  rparen_idx = i
256
256
  break
257
257
  end
258
+ elsif token.type == :LBRACE && depth == 0
259
+ # no parameters
260
+ break
258
261
  end
259
262
  end
260
263
 
@@ -289,6 +292,7 @@ class PuppetLint::Data
289
292
  # Returns nothing.
290
293
  def parse_control_comments
291
294
  @ignore_overrides.each_key { |check| @ignore_overrides[check].clear }
295
+ control_re = /\A(lint:\S+)(\s+lint:\S+)*(.*)/
292
296
 
293
297
  comment_token_types = Set[:COMMENT, :MLCOMMENT, :SLASH_COMMENT]
294
298
 
@@ -301,28 +305,37 @@ class PuppetLint::Data
301
305
 
302
306
  stack = []
303
307
  control_comment_tokens.each do |token|
304
- control, reason = token.value.strip.split(' ', 2)
305
- split_control = control.split(':')
306
- command = split_control[1]
308
+ comment_data = control_re.match(token.value.strip).to_a[1..-1].compact.map(&:strip)
309
+ if comment_data.last =~ /\Alint:(ignore|endignore)/
310
+ comment_data << ''
311
+ end
312
+ reason = comment_data.pop
313
+ stack_add = []
314
+ comment_data.each do |control|
315
+ split_control = control.split(':')
316
+ command = split_control[1]
307
317
 
308
- if command == 'ignore'
309
- check = split_control[2].to_sym
318
+ if command == 'ignore'
319
+ check = split_control[2].to_sym
310
320
 
311
- if token.prev_token && !Set[:NEWLINE, :INDENT].include?(token.prev_token.type)
312
- # control comment at the end of the line, override applies to
313
- # a single line only
314
- (ignore_overrides[check] ||= {})[token.line] = reason
321
+ if token.prev_token && !Set[:NEWLINE, :INDENT].include?(token.prev_token.type)
322
+ # control comment at the end of the line, override applies to
323
+ # a single line only
324
+ (ignore_overrides[check] ||= {})[token.line] = reason
325
+ else
326
+ stack_add << [token.line, reason, check]
327
+ end
315
328
  else
316
- stack << [token.line, reason, check]
317
- end
318
- else
319
- start = stack.pop
320
- unless start.nil?
321
- (start[0]..token.line).each do |i|
322
- (ignore_overrides[start[2]] ||= {})[i] = start[1]
329
+ stack.pop.each do |start|
330
+ unless start.nil?
331
+ (start[0]..token.line).each do |i|
332
+ (ignore_overrides[start[2]] ||= {})[i] = start[1]
333
+ end
334
+ end
323
335
  end
324
336
  end
325
337
  end
338
+ stack << stack_add unless stack_add.empty?
326
339
  end
327
340
  end
328
341
  end
@@ -15,24 +15,22 @@ class PuppetLint
15
15
 
16
16
  # Internal: Initialise a new PuppetLint::LexerError object.
17
17
  #
18
- # code - The String manifest code being tokenised.
19
- # offset - The Integer position in the code string that the tokeniser was
20
- # at when it encountered the error.
21
- def initialize(code, offset)
22
- chunk = code[0..offset]
23
- @line_no = chunk.scan(/(\r\n|\r|\n)/).size + 1
24
- if @line_no == 1
25
- @column = chunk.length
26
- else
27
- @column = chunk.length - chunk.rindex(/(\r\n|\r|\n)/) - 1
28
- end
29
- @column = 1 if @column == 0
18
+ # line_no - The Integer line number of the location of the error.
19
+ # column - The Integer column number of the location of the error.
20
+ def initialize(line_no, column)
21
+ @line_no = line_no
22
+ @column = column
30
23
  end
31
24
  end
32
25
 
33
26
  # Internal: The puppet-lint lexer. Converts your manifest into its tokenised
34
27
  # form.
35
28
  class Lexer
29
+ def initialize
30
+ @line_no = 1
31
+ @column = 1
32
+ end
33
+
36
34
  # Internal: A Hash whose keys are Strings representing reserved keywords in
37
35
  # the Puppet DSL.
38
36
  KEYWORDS = {
@@ -153,16 +151,17 @@ class PuppetLint
153
151
 
154
152
  KNOWN_TOKENS.each do |type, regex|
155
153
  if value = chunk[regex, 1]
154
+ length = value.size
156
155
  if type == :NAME
157
156
  if KEYWORDS.include? value
158
- tokens << new_token(value.upcase.to_sym, value, :chunk => code[0..i])
157
+ tokens << new_token(value.upcase.to_sym, value, length)
159
158
  else
160
- tokens << new_token(type, value, :chunk => code[0..i])
159
+ tokens << new_token(type, value, length)
161
160
  end
162
161
  else
163
- tokens << new_token(type, value, :chunk => code[0..i])
162
+ tokens << new_token(type, value, length)
164
163
  end
165
- i += value.size
164
+ i += length
166
165
  found = true
167
166
  break
168
167
  end
@@ -170,68 +169,67 @@ class PuppetLint
170
169
 
171
170
  unless found
172
171
  if var_name = chunk[/\A\$((::)?([\w-]+::)*[\w-]+(\[.+?\])*)/, 1]
173
- tokens << new_token(:VARIABLE, var_name, :chunk => code[0..i])
174
- i += var_name.size + 1
172
+ length = var_name.size + 1
173
+ tokens << new_token(:VARIABLE, var_name, length)
175
174
 
176
175
  elsif chunk.match(/\A'(.*?)'/m)
177
176
  str_content = StringScanner.new(code[i+1..-1]).scan_until(/(\A|[^\\])(\\\\)*'/m)
178
- tokens << new_token(:SSTRING, str_content[0..-2], :chunk => code[0..i])
179
- i += str_content.size + 1
177
+ length = str_content.size + 1
178
+ tokens << new_token(:SSTRING, str_content[0..-2], length)
180
179
 
181
180
  elsif chunk.match(/\A"/)
182
181
  str_contents = StringScanner.new(code[i+1..-1]).scan_until(/(\A|[^\\])(\\\\)*"/m)
183
182
  _ = code[0..i].split("\n")
184
183
  interpolate_string(str_contents, _.count, _.last.length)
185
- i += str_contents.size + 1
184
+ length = str_contents.size + 1
186
185
 
187
186
  elsif comment = chunk[/\A(#.*)/, 1]
188
- comment_size = comment.size
187
+ length = comment.size
189
188
  comment.sub!(/#/, '')
190
- tokens << new_token(:COMMENT, comment, :chunk => code[0..i])
191
- i += comment_size
189
+ tokens << new_token(:COMMENT, comment, length)
192
190
 
193
191
  elsif slash_comment = chunk[/\A(\/\/.*)/, 1]
194
- slash_comment_size = slash_comment.size
192
+ length = slash_comment.size
195
193
  slash_comment.sub!(/\/\//, '')
196
- tokens << new_token(:SLASH_COMMENT, slash_comment, :chunk => code[0..i])
197
- i += slash_comment_size
194
+ tokens << new_token(:SLASH_COMMENT, slash_comment, length)
198
195
 
199
196
  elsif mlcomment = chunk[/\A(\/\*.*?\*\/)/m, 1]
200
- mlcomment_size = mlcomment.size
197
+ length = mlcomment.size
201
198
  mlcomment.sub!(/\A\/\* ?/, '')
202
199
  mlcomment.sub!(/ ?\*\/\Z/, '')
203
200
  mlcomment.gsub!(/ *\* ?/, '')
204
201
  mlcomment.strip!
205
- tokens << new_token(:MLCOMMENT, mlcomment, :chunk => code[0..i])
206
- i += mlcomment_size
202
+ tokens << new_token(:MLCOMMENT, mlcomment, length)
207
203
 
208
204
  elsif chunk.match(/\A\/.*?\//) && possible_regex?
209
205
  str_content = StringScanner.new(code[i+1..-1]).scan_until(/(\A|[^\\])(\\\\)*\//m)
210
- tokens << new_token(:REGEX, str_content[0..-2], :chunk => code[0..i])
211
- i += str_content.size + 1
206
+ length = str_content.size + 1
207
+ tokens << new_token(:REGEX, str_content[0..-2], length)
212
208
 
213
209
  elsif eolindent = chunk[/\A((\r\n|\r|\n)[ \t]+)/m, 1]
214
210
  eol = eolindent[/\A([\r\n]+)/m, 1]
215
211
  indent = eolindent[/\A[\r\n]+([ \t]+)/m, 1]
216
- tokens << new_token(:NEWLINE, eol, :chunk => code[0..i])
217
- tokens << new_token(:INDENT, indent, :chunk => code[0..i+eol.size])
218
- i += indent.size + eol.size
212
+ tokens << new_token(:NEWLINE, eol, eol.size)
213
+ tokens << new_token(:INDENT, indent, indent.size)
214
+ length = indent.size + eol.size
219
215
 
220
216
  elsif whitespace = chunk[/\A([ \t]+)/, 1]
221
- tokens << new_token(:WHITESPACE, whitespace, :chunk => code[0..i])
222
- i += whitespace.size
217
+ length = whitespace.size
218
+ tokens << new_token(:WHITESPACE, whitespace, length)
223
219
 
224
220
  elsif eol = chunk[/\A(\r\n|\r|\n)/, 1]
225
- tokens << new_token(:NEWLINE, eol, :chunk => code[0..i])
226
- i += eol.size
221
+ length = eol.size
222
+ tokens << new_token(:NEWLINE, eol, length)
227
223
 
228
224
  elsif chunk.match(/\A\//)
229
- tokens << new_token(:DIV, '/', :chunk => code[0..i])
230
- i += 1
225
+ length = 1
226
+ tokens << new_token(:DIV, '/', length)
231
227
 
232
228
  else
233
- raise PuppetLint::LexerError.new(code, i)
229
+ raise PuppetLint::LexerError.new(@line_no, @column)
234
230
  end
231
+
232
+ i += length
235
233
  end
236
234
  end
237
235
 
@@ -259,29 +257,18 @@ class PuppetLint
259
257
  # Internal: Create a new PuppetLint::Lexer::Token object, calculate its
260
258
  # line number and column and then add it to the Linked List of tokens.
261
259
  #
262
- # type - The Symbol token type.
263
- # value - The token value.
264
- # opts - A Hash of additional values required to determine line number and
260
+ # type - The Symbol token type.
261
+ # value - The token value.
262
+ # length - The Integer length of the token's value.
263
+ # opts - A Hash of additional values required to determine line number and
265
264
  # column:
266
- # :chunk - The String chunk of the manifest that has been tokenised so
267
- # far.
268
265
  # :line - The Integer line number if calculated externally.
269
266
  # :column - The Integer column number if calculated externally.
270
267
  #
271
268
  # Returns the instantiated PuppetLint::Lexer::Token object.
272
- def new_token(type, value, opts = {})
273
- if opts[:chunk]
274
- line_no = opts[:chunk].scan(/(\r\n|\r|\n)/).size + 1
275
- if line_no == 1
276
- column = opts[:chunk].length
277
- else
278
- column = opts[:chunk].length - opts[:chunk].rindex(/(\r\n|\r|\n)/) - 1
279
- end
280
- column += 1 if column == 0
281
- else
282
- column = opts[:column]
283
- line_no = opts[:line]
284
- end
269
+ def new_token(type, value, length, opts = {})
270
+ column = opts[:column] || @column
271
+ line_no = opts[:line] || @line_no
285
272
 
286
273
  token = Token.new(type, value, line_no, column)
287
274
  unless tokens.last.nil?
@@ -298,6 +285,12 @@ class PuppetLint
298
285
  end
299
286
  end
300
287
 
288
+ @column += length
289
+ if type == :NEWLINE
290
+ @line_no += 1
291
+ @column = 1
292
+ end
293
+
301
294
  token
302
295
  end
303
296
 
@@ -333,30 +326,30 @@ class PuppetLint
333
326
  until value.nil?
334
327
  if terminator == "\""
335
328
  if first
336
- tokens << new_token(:STRING, value, :line => line, :column => column)
329
+ tokens << new_token(:STRING, value, value.size + 2, :line => line, :column => column)
337
330
  first = false
338
331
  else
339
332
  line += value.scan(/(\r\n|\r|\n)/).size
340
333
  token_column = column + (ss.pos - value.size)
341
- tokens << new_token(:DQPOST, value, :line => line, :column => token_column)
334
+ tokens << new_token(:DQPOST, value, value.size + 1, :line => line, :column => token_column)
342
335
  end
343
336
  else
344
337
  if first
345
- tokens << new_token(:DQPRE, value, :line => line, :column => column)
338
+ tokens << new_token(:DQPRE, value, value.size + 1, :line => line, :column => column)
346
339
  first = false
347
340
  else
348
341
  line += value.scan(/(\r\n|\r|\n)/).size
349
342
  token_column = column + (ss.pos - value.size)
350
- tokens << new_token(:DQMID, value, :line => line, :column => token_column)
343
+ tokens << new_token(:DQMID, value, value.size, :line => line, :column => token_column)
351
344
  end
352
345
  if ss.scan(/\{/).nil?
353
346
  var_name = ss.scan(/(::)?([\w-]+::)*[\w-]+/)
354
347
  if var_name.nil?
355
348
  token_column = column + ss.pos - 1
356
- tokens << new_token(:DQMID, "$", :line => line, :column => token_column)
349
+ tokens << new_token(:DQMID, "$", 1, :line => line, :column => token_column)
357
350
  else
358
351
  token_column = column + (ss.pos - var_name.size)
359
- tokens << new_token(:UNENC_VARIABLE, var_name, :line => line, :column => token_column)
352
+ tokens << new_token(:UNENC_VARIABLE, var_name, var_name.size, :line => line, :column => token_column)
360
353
  end
361
354
  else
362
355
  contents = ss.scan_until(/\}/)[0..-2]
@@ -368,7 +361,7 @@ class PuppetLint
368
361
  lexer.tokens.each do |token|
369
362
  tok_col = column + token.column + (ss.pos - contents.size - 1)
370
363
  tok_line = token.line + line - 1
371
- tokens << new_token(token.type, token.value, :line => tok_line, :column => tok_col)
364
+ tokens << new_token(token.type, token.value, token.value.size, :line => tok_line, :column => tok_col)
372
365
  end
373
366
  end
374
367
  end
@@ -186,6 +186,17 @@ PuppetLint.new_check(:variable_scope) do
186
186
  'serverip',
187
187
  'serverversion',
188
188
  'caller_module_name',
189
+ 'alias',
190
+ 'audit',
191
+ 'before',
192
+ 'loglevel',
193
+ 'noop',
194
+ 'notify',
195
+ 'require',
196
+ 'schedule',
197
+ 'stage',
198
+ 'subscribe',
199
+ 'tag',
189
200
  ]
190
201
  POST_VAR_TOKENS = Set[:COMMA, :EQUALS, :RPAREN]
191
202
 
@@ -2,17 +2,22 @@
2
2
  # each instance found.
3
3
  PuppetLint.new_check(:unquoted_node_name) do
4
4
  def check
5
- tokens.select { |r|
6
- r.type == :NODE && r.next_code_token.type == :NAME
7
- }.each do |token|
8
- value_token = token.next_code_token
9
- unless value_token.value == 'default'
10
- notify :warning, {
11
- :message => 'unquoted node name found',
12
- :line => value_token.line,
13
- :column => value_token.column,
14
- :token => value_token,
15
- }
5
+ node_tokens = tokens.select { |token| token.type == :NODE }
6
+ node_tokens.each do |node|
7
+ node_token_idx = tokens.index(node)
8
+ node_lbrace_idx = tokens.index(tokens.find { |token| token.type == :LBRACE })
9
+
10
+ tokens[node_token_idx..node_lbrace_idx].select { |token|
11
+ token.type == :NAME
12
+ }.each do |token|
13
+ unless token.value == 'default'
14
+ notify :warning, {
15
+ :message => 'unquoted node name found',
16
+ :line => token.line,
17
+ :column => token.column,
18
+ :token => token,
19
+ }
20
+ end
16
21
  end
17
22
  end
18
23
  end
@@ -111,7 +111,8 @@ PuppetLint.new_check(:arrow_alignment) do
111
111
  resource_tokens.each_with_index do |token, idx|
112
112
  if token.type == :FARROW
113
113
  (level_tokens[indent_depth_idx] ||= []) << token
114
- indent_length = token.prev_token.column + 1
114
+ prev_indent_token = resource_tokens[0..idx].rindex { |t| t.type == :INDENT }
115
+ indent_length = resource_tokens[prev_indent_token].to_manifest.length + token.prev_code_token.to_manifest.length + 2
115
116
 
116
117
  if indent_depth[indent_depth_idx] < indent_length
117
118
  indent_depth[indent_depth_idx] = indent_length
@@ -124,12 +125,15 @@ PuppetLint.new_check(:arrow_alignment) do
124
125
  elsif token.type == :RBRACE
125
126
  level_tokens[indent_depth_idx].each do |arrow_tok|
126
127
  unless arrow_tok.column == indent_depth[indent_depth_idx]
128
+ arrows_on_line = level_tokens[indent_depth_idx].select { |t| t.line == arrow_tok.line }
127
129
  notify :warning, {
128
- :message => 'indentation of => is not properly aligned',
129
- :line => arrow_tok.line,
130
- :column => arrow_tok.column,
131
- :token => arrow_tok,
132
- :indent_depth => indent_depth[indent_depth_idx],
130
+ :message => 'indentation of => is not properly aligned',
131
+ :line => arrow_tok.line,
132
+ :column => arrow_tok.column,
133
+ :token => arrow_tok,
134
+ :indent_depth => indent_depth[indent_depth_idx],
135
+ :newline => !(arrows_on_line.index(arrow_tok) == 0),
136
+ :newline_indent => arrows_on_line.first.prev_code_token.prev_token.value,
133
137
  }
134
138
  end
135
139
  end
@@ -142,7 +146,24 @@ PuppetLint.new_check(:arrow_alignment) do
142
146
  end
143
147
 
144
148
  def fix(problem)
145
- new_indent = ' ' * (problem[:indent_depth] - problem[:token].prev_token.column)
146
- problem[:token].prev_token.value = new_indent
149
+ new_ws_len = (problem[:indent_depth] - (problem[:newline_indent].length + problem[:token].prev_code_token.to_manifest.length + 1))
150
+ new_ws = ' ' * new_ws_len
151
+ if problem[:newline]
152
+ index = tokens.index(problem[:token].prev_code_token.prev_token)
153
+
154
+ #insert newline
155
+ tokens.insert(index, PuppetLint::Lexer::Token.new(:NEWLINE, "\n", 0, 0))
156
+
157
+ # indent the parameter to the correct depth
158
+ problem[:token].prev_code_token.prev_token.type = :INDENT
159
+ problem[:token].prev_code_token.prev_token.value = problem[:newline_indent].dup
160
+ end
161
+
162
+ if problem[:token].prev_token.type == :WHITESPACE
163
+ problem[:token].prev_token.value = new_ws
164
+ else
165
+ index = tokens.index(problem[:token].prev_token)
166
+ tokens.insert(index + 1, PuppetLint::Lexer::Token.new(:WHITESPACE, new_ws, 0, 0))
167
+ end
147
168
  end
148
169
  end
@@ -11,6 +11,22 @@ class PuppetLint
11
11
  # require 'puppet-lint'
12
12
  # PuppetLint::RakeTask.new
13
13
  class RakeTask < ::Rake::TaskLib
14
+ include ::Rake::DSL if defined?(::Rake::DSL)
15
+
16
+ DEFAULT_PATTERN = '**/*.pp'
17
+
18
+ attr_accessor :name
19
+ attr_accessor :pattern
20
+ attr_accessor :ignore_paths
21
+ attr_accessor :with_filename
22
+ attr_accessor :disable_checks
23
+ attr_accessor :fail_on_warnings
24
+ attr_accessor :error_level
25
+ attr_accessor :log_format
26
+ attr_accessor :with_context
27
+ attr_accessor :fix
28
+ attr_accessor :show_ignored
29
+
14
30
  # Public: Initialise a new PuppetLint::RakeTask.
15
31
  #
16
32
  # args - Not used.
@@ -18,20 +34,38 @@ class PuppetLint
18
34
  # Example
19
35
  #
20
36
  # PuppetLint::RakeTask.new
21
- def initialize(*args)
37
+ def initialize(*args, &task_block)
38
+ @name = args.shift || :lint
39
+ @pattern = DEFAULT_PATTERN
40
+ @with_filename = true
41
+ @disable_checks = []
42
+ @ignore_paths = []
43
+
44
+ define(args, &task_block)
45
+ end
46
+
47
+ def define(args, &task_block)
22
48
  desc 'Run puppet-lint'
23
49
 
24
- task :lint do
25
- PuppetLint.configuration.with_filename = true
50
+ task_block.call(*[self, args].slice(0, task_block.arity)) if task_block
51
+
52
+ task @name do
26
53
  PuppetLint::OptParser.build
27
54
 
55
+ Array(@disable_checks).each do |check|
56
+ PuppetLint.configuration.send("disable_#{check}")
57
+ end
58
+
59
+ %w{with_filename fail_on_warnings error_level log_format with_context fix show_ignored}.each do |config|
60
+ value = instance_variable_get("@#{config}")
61
+ PuppetLint.configuration.send("#{config}=".to_sym, value) unless value.nil?
62
+ end
63
+
28
64
  RakeFileUtils.send(:verbose, true) do
29
65
  linter = PuppetLint.new
30
- matched_files = FileList['**/*.pp']
66
+ matched_files = FileList[@pattern]
31
67
 
32
- if ignore_paths = PuppetLint.configuration.ignore_paths
33
- matched_files = matched_files.exclude(*ignore_paths)
34
- end
68
+ matched_files = matched_files.exclude(*@ignore_paths)
35
69
 
36
70
  matched_files.to_a.each do |puppet_file|
37
71
  linter.file = puppet_file
@@ -1,3 +1,3 @@
1
1
  class PuppetLint
2
- VERSION = '1.0.1'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -0,0 +1,6 @@
1
+ # lint:ignore:double_quoted_strings lint:ignore:quoted_booleans
2
+ "true"
3
+ "false"
4
+ # lint:endignore
5
+
6
+ "true"
@@ -0,0 +1,2 @@
1
+ "true" # lint:ignore:double_quoted_strings lint:ignore:quoted_booleans
2
+ "false" # lint:ignore:quoted_booleans lint:ignore:double_quoted_strings reason
@@ -306,4 +306,21 @@ describe PuppetLint::Bin do
306
306
  " for a good reason",
307
307
  ].join("\n")) }
308
308
  end
309
+
310
+ context 'ignoring multiple checks on a line' do
311
+ let(:args) { [
312
+ 'spec/fixtures/test/manifests/ignore_multiple_line.pp',
313
+ ] }
314
+
315
+ its(:exitstatus) { is_expected.to eq(0) }
316
+ end
317
+
318
+ context 'ignoring multiple checks in a block' do
319
+ let(:args) { [
320
+ 'spec/fixtures/test/manifests/ignore_multiple_block.pp',
321
+ ] }
322
+
323
+ its(:exitstatus) { is_expected.to eq(0) }
324
+ its(:stdout) { is_expected.to match(/^.*line 6$/) }
325
+ end
309
326
  end
@@ -94,4 +94,16 @@ describe 'quoted_booleans', :type => :lint do
94
94
  expect(problems).to contain_ignored(msg).on_line(5).in_column(7).with_reason('another reason')
95
95
  end
96
96
  end
97
+
98
+ context 'disable multiple checks on a line with a reason' do
99
+ let(:code) { '"true" # lint:ignore:quoted_booleans lint:ignore:double_quoted_string a reason' }
100
+
101
+ it 'should detect 1 problems' do
102
+ expect(problems).to have(1).problems
103
+ end
104
+
105
+ it 'should have one ignored problems' do
106
+ expect(problems).to contain_ignored(msg).on_line(1).in_column(1).with_reason('a reason')
107
+ end
108
+ end
97
109
  end
@@ -13,27 +13,31 @@ describe PuppetLint::Lexer do
13
13
 
14
14
  context '#new_token' do
15
15
  it 'should calculate the line number for an empty string' do
16
- token = @lexer.new_token(:TEST, 'test', :chunk => '')
16
+ token = @lexer.new_token(:TEST, 'test', 4)
17
17
  expect(token.line).to eq(1)
18
18
  end
19
19
 
20
20
  it 'should calculate the line number for a multi line string' do
21
- token = @lexer.new_token(:TEST, 'test', :chunk => "foo\nbar")
21
+ @lexer.instance_variable_set('@line_no', 2)
22
+ token = @lexer.new_token(:TEST, 'test', 4)
22
23
  expect(token.line).to eq(2)
23
24
  end
24
25
 
25
26
  it 'should calculate the column number for an empty string' do
26
- token = @lexer.new_token(:TEST, 'test', :chunk => '')
27
+ token = @lexer.new_token(:TEST, 'test', 4)
27
28
  expect(token.column).to eq(1)
28
29
  end
29
30
 
30
31
  it 'should calculate the column number for a single line string' do
31
- token = @lexer.new_token(:TEST, 'test', :chunk => 'this is a test')
32
+ @lexer.instance_variable_set('@column', 'this is a test'.size)
33
+ token = @lexer.new_token(:TEST, 'test', 4)
32
34
  expect(token.column).to eq(14)
33
35
  end
34
36
 
35
37
  it 'should calculate the column number for a multi line string' do
36
- token = @lexer.new_token(:TEST, 'test', :chunk => "foo\nbar\nbaz\ngronk")
38
+ @lexer.instance_variable_set('@line_no', 4)
39
+ @lexer.instance_variable_set('@column', "gronk".size)
40
+ token = @lexer.new_token(:TEST, 'test', 4)
37
41
  expect(token.column).to eq(5)
38
42
  end
39
43
  end
@@ -165,4 +165,18 @@ describe 'variable_scope' do
165
165
  expect(problems).to contain_warning(msg).on_line(6).in_column(11)
166
166
  end
167
167
  end
168
+
169
+ %w{alias audit before loglevel noop notify require schedule stage subscribe tag}.each do |metaparam|
170
+ context "referencing #{metaparam} metaparam value as a variable" do
171
+ let(:code) { "
172
+ class foo() {
173
+ $#{metaparam}
174
+ }
175
+ " }
176
+
177
+ it 'should not detect any problems' do
178
+ expect(problems).to have(0).problems
179
+ end
180
+ end
181
+ end
168
182
  end
@@ -39,6 +39,33 @@ describe 'unquoted_node_name' do
39
39
  expect(problems).to have(0).problems
40
40
  end
41
41
  end
42
+
43
+ context 'multiple bare node names' do
44
+ let(:code) { "node foo, bar, baz { }" }
45
+
46
+ it 'should detect 3 problems' do
47
+ expect(problems).to have(3).problems
48
+ end
49
+
50
+ it 'should create 3 warnings' do
51
+ expect(problems).to contain_warning(msg).on_line(1).in_column(6)
52
+ expect(problems).to contain_warning(msg).on_line(1).in_column(11)
53
+ expect(problems).to contain_warning(msg).on_line(1).in_column(16)
54
+ end
55
+ end
56
+
57
+ context 'mixed node name types' do
58
+ let(:code) { "node foo, 'bar', baz { }" }
59
+
60
+ it 'should detect 2 problems' do
61
+ expect(problems).to have(2).problems
62
+ end
63
+
64
+ it 'should create 2 warnings' do
65
+ expect(problems).to contain_warning(msg).on_line(1).in_column(6)
66
+ expect(problems).to contain_warning(msg).on_line(1).in_column(18)
67
+ end
68
+ end
42
69
  end
43
70
 
44
71
  context 'with fix enabled' do
@@ -65,5 +92,42 @@ describe 'unquoted_node_name' do
65
92
  expect(manifest).to eq("node 'foo' { }")
66
93
  end
67
94
  end
95
+
96
+ context 'multiple bare node names' do
97
+ let(:code) { "node foo, bar, baz { }" }
98
+ let(:fixed) { "node 'foo', 'bar', 'baz' { }" }
99
+
100
+ it 'should detect 3 problems' do
101
+ expect(problems).to have(3).problems
102
+ end
103
+
104
+ it 'should fix the 3 problems' do
105
+ expect(problems).to contain_fixed(msg).on_line(1).in_column(6)
106
+ expect(problems).to contain_fixed(msg).on_line(1).in_column(11)
107
+ expect(problems).to contain_fixed(msg).on_line(1).in_column(16)
108
+ end
109
+
110
+ it 'should quote all three node names' do
111
+ expect(manifest).to eq(fixed)
112
+ end
113
+ end
114
+
115
+ context 'mixed node name types' do
116
+ let(:code) { "node foo, 'bar', baz { }" }
117
+ let(:fixed) { "node 'foo', 'bar', 'baz' { }" }
118
+
119
+ it 'should detect 2 problems' do
120
+ expect(problems).to have(2).problems
121
+ end
122
+
123
+ it 'should fix the 2 problems' do
124
+ expect(problems).to contain_fixed(msg).on_line(1).in_column(6)
125
+ expect(problems).to contain_fixed(msg).on_line(1).in_column(18)
126
+ end
127
+
128
+ it 'should quote the 2 unquoted node names' do
129
+ expect(manifest).to eq(fixed)
130
+ end
131
+ end
68
132
  end
69
133
  end
@@ -250,6 +250,24 @@ describe 'arrow_alignment' do
250
250
  expect(problems).to have(0).problems
251
251
  end
252
252
  end
253
+
254
+ context 'multiline resource with multiple params on a line' do
255
+ let(:code) { "
256
+ user { 'test':
257
+ a => 'foo', bb => 'bar',
258
+ ccc => 'baz',
259
+ }
260
+ " }
261
+
262
+ it 'should detect 2 problems' do
263
+ expect(problems).to have(2).problems
264
+ end
265
+
266
+ it 'should create 2 warnings' do
267
+ expect(problems).to contain_warning(msg).on_line(3).in_column(13)
268
+ expect(problems).to contain_warning(msg).on_line(3).in_column(26)
269
+ end
270
+ end
253
271
  end
254
272
 
255
273
  context 'with fix enabled' do
@@ -399,5 +417,94 @@ describe 'arrow_alignment' do
399
417
  expect(manifest).to eq(fixed)
400
418
  end
401
419
  end
420
+
421
+ context 'resource with unaligned => and no whitespace between param and =>' do
422
+ let(:code) { "
423
+ user { 'test':
424
+ param1 => 'foo',
425
+ param2=> 'bar',
426
+ }
427
+ " }
428
+
429
+ let(:fixed) { "
430
+ user { 'test':
431
+ param1 => 'foo',
432
+ param2 => 'bar',
433
+ }
434
+ " }
435
+
436
+ it 'should detect 1 problem' do
437
+ expect(problems).to have(1).problem
438
+ end
439
+
440
+ it 'should fix the problem' do
441
+ expect(problems).to contain_fixed(msg).on_line(4).in_column(17)
442
+ end
443
+
444
+ it 'should add whitespace between the param and the arrow' do
445
+ expect(manifest).to eq(fixed)
446
+ end
447
+ end
448
+
449
+ context 'multiline resource with multiple params on a line' do
450
+ let(:code) { "
451
+ user { 'test':
452
+ a => 'foo', bb => 'bar',
453
+ ccc => 'baz',
454
+ }
455
+ " }
456
+
457
+ let(:fixed) { "
458
+ user { 'test':
459
+ a => 'foo',
460
+ bb => 'bar',
461
+ ccc => 'baz',
462
+ }
463
+ " }
464
+
465
+ it 'should detect 2 problems' do
466
+ expect(problems).to have(2).problems
467
+ end
468
+
469
+ it 'should fix 2 problems' do
470
+ expect(problems).to contain_fixed(msg).on_line(3).in_column(13)
471
+ expect(problems).to contain_fixed(msg).on_line(3).in_column(26)
472
+ end
473
+
474
+ it 'should move the extra param onto its own line and realign' do
475
+ expect(manifest).to eq(fixed)
476
+ end
477
+ end
478
+
479
+ context 'multiline resource with multiple params on a line, extra one longer' do
480
+ let(:code) { "
481
+ user { 'test':
482
+ a => 'foo', bbccc => 'bar',
483
+ ccc => 'baz',
484
+ }
485
+ " }
486
+
487
+ let(:fixed) { "
488
+ user { 'test':
489
+ a => 'foo',
490
+ bbccc => 'bar',
491
+ ccc => 'baz',
492
+ }
493
+ " }
494
+
495
+ it 'should detect 2 problems' do
496
+ expect(problems).to have(3).problems
497
+ end
498
+
499
+ it 'should fix 2 problems' do
500
+ expect(problems).to contain_fixed(msg).on_line(3).in_column(13)
501
+ expect(problems).to contain_fixed(msg).on_line(3).in_column(29)
502
+ expect(problems).to contain_fixed(msg).on_line(4).in_column(15)
503
+ end
504
+
505
+ it 'should move the extra param onto its own line and realign' do
506
+ expect(manifest).to eq(fixed)
507
+ end
508
+ end
402
509
  end
403
510
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppet-lint
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
- - 0
9
8
  - 1
10
- version: 1.0.1
9
+ - 0
10
+ version: 1.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Tim Sharpe
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2014-08-20 00:00:00 +10:00
18
+ date: 2014-09-23 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -123,6 +123,8 @@ files:
123
123
  - puppet-lint.gemspec
124
124
  - spec/fixtures/test/manifests/fail.pp
125
125
  - spec/fixtures/test/manifests/ignore.pp
126
+ - spec/fixtures/test/manifests/ignore_multiple_block.pp
127
+ - spec/fixtures/test/manifests/ignore_multiple_line.pp
126
128
  - spec/fixtures/test/manifests/ignore_reason.pp
127
129
  - spec/fixtures/test/manifests/init.pp
128
130
  - spec/fixtures/test/manifests/malformed.pp
@@ -203,6 +205,8 @@ summary: Ensure your Puppet manifests conform with the Puppetlabs style guide
203
205
  test_files:
204
206
  - spec/fixtures/test/manifests/fail.pp
205
207
  - spec/fixtures/test/manifests/ignore.pp
208
+ - spec/fixtures/test/manifests/ignore_multiple_block.pp
209
+ - spec/fixtures/test/manifests/ignore_multiple_line.pp
206
210
  - spec/fixtures/test/manifests/ignore_reason.pp
207
211
  - spec/fixtures/test/manifests/init.pp
208
212
  - spec/fixtures/test/manifests/malformed.pp