puppet-lint 2.4.2 → 2.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +112 -96
- data/lib/puppet-lint.rb +1 -1
- data/lib/puppet-lint/lexer.rb +7 -5
- data/lib/puppet-lint/lexer/string_slurper.rb +14 -3
- data/lib/puppet-lint/lexer/token.rb +6 -0
- data/lib/puppet-lint/plugins/check_classes/parameter_order.rb +12 -1
- data/lib/puppet-lint/plugins/check_documentation/documentation.rb +4 -0
- data/lib/puppet-lint/plugins/check_resources/ensure_first_param.rb +1 -1
- data/lib/puppet-lint/tasks/puppet-lint.rb +14 -0
- data/lib/puppet-lint/tasks/release_test.rb +3 -1
- data/lib/puppet-lint/version.rb +1 -1
- data/spec/puppet-lint/bin_spec.rb +8 -8
- data/spec/puppet-lint/lexer/string_slurper_spec.rb +34 -0
- data/spec/puppet-lint/plugins/check_classes/parameter_order_spec.rb +18 -0
- data/spec/puppet-lint/plugins/check_documentation/documentation_spec.rb +18 -0
- data/spec/puppet-lint/plugins/check_strings/double_quoted_strings_spec.rb +5 -5
- data/spec/puppet-lint/plugins/check_variables/variable_is_lowercase_spec.rb +28 -0
- data/spec/spec_helper.rb +7 -5
- metadata +9 -16
- data/.gitignore +0 -12
- data/.rspec +0 -2
- data/.rubocop.yml +0 -74
- data/.rubocop_todo.yml +0 -89
- data/.travis.yml +0 -26
- data/Gemfile +0 -40
- data/Rakefile +0 -44
- data/appveyor.yml +0 -35
- data/puppet-lint.gemspec +0 -19
data/lib/puppet-lint.rb
CHANGED
@@ -100,9 +100,9 @@ class PuppetLint
|
|
100
100
|
# Returns a format String to be used with String#%.
|
101
101
|
def log_format
|
102
102
|
if configuration.log_format.nil? || configuration.log_format.empty?
|
103
|
-
## recreate previous old log format as far as thats possible.
|
104
103
|
format = '%{KIND}: %{message} on line %{line}'
|
105
104
|
format.prepend('%{path} - ') if configuration.with_filename
|
105
|
+
format.concat(' (check: %{check})')
|
106
106
|
configuration.log_format = format
|
107
107
|
end
|
108
108
|
|
data/lib/puppet-lint/lexer.rb
CHANGED
@@ -124,6 +124,8 @@ class PuppetLint
|
|
124
124
|
# value of the token.
|
125
125
|
KNOWN_TOKENS = [
|
126
126
|
[:WHITESPACE, %r{\A(#{WHITESPACE_RE}+)}],
|
127
|
+
# FIXME: Future breaking change, the following :TYPE tokens conflict with
|
128
|
+
# the :TYPE keyword token.
|
127
129
|
[:TYPE, %r{\A(Integer|Float|Boolean|Regexp|String|Array|Hash|Resource|Class|Collection|Scalar|Numeric|CatalogEntry|Data|Tuple|Struct|Optional|NotUndef|Variant|Enum|Pattern|Any|Callable|Type|Runtime|Undef|Default|Sensitive)\b}], # rubocop:disable Metrics/LineLength
|
128
130
|
[:CLASSREF, %r{\A(((::){0,1}[A-Z][-\w]*)+)}],
|
129
131
|
[:NUMBER, %r{\A\b((?:0[xX][0-9A-Fa-f]+|0?\d+(?:\.\d+)?(?:[eE]-?\d+)?))\b}],
|
@@ -240,7 +242,7 @@ class PuppetLint
|
|
240
242
|
begin
|
241
243
|
string_segments = slurper.parse
|
242
244
|
process_string_segments(string_segments)
|
243
|
-
length = slurper.
|
245
|
+
length = slurper.consumed_chars + 1
|
244
246
|
rescue PuppetLint::Lexer::StringSlurper::UnterminatedStringError
|
245
247
|
raise PuppetLint::LexerError.new(@line_no, @column, 'unterminated string')
|
246
248
|
end
|
@@ -287,7 +289,7 @@ class PuppetLint
|
|
287
289
|
slurper = PuppetLint::Lexer::StringSlurper.new(code[i + length..-1])
|
288
290
|
heredoc_segments = slurper.parse_heredoc(heredoc_tag)
|
289
291
|
process_heredoc_segments(heredoc_segments)
|
290
|
-
length += slurper.
|
292
|
+
length += slurper.consumed_chars
|
291
293
|
end
|
292
294
|
|
293
295
|
elsif eol = chunk[%r{\A(#{LINE_END_RE})}, 1]
|
@@ -299,7 +301,7 @@ class PuppetLint
|
|
299
301
|
slurper = PuppetLint::Lexer::StringSlurper.new(code[i + length..-1])
|
300
302
|
heredoc_segments = slurper.parse_heredoc(heredoc_tag)
|
301
303
|
process_heredoc_segments(heredoc_segments)
|
302
|
-
length += slurper.
|
304
|
+
length += slurper.consumed_chars
|
303
305
|
end
|
304
306
|
|
305
307
|
elsif chunk.start_with?('/')
|
@@ -417,7 +419,7 @@ class PuppetLint
|
|
417
419
|
lexer = PuppetLint::Lexer.new
|
418
420
|
lexer.tokenise(segment[1])
|
419
421
|
lexer.tokens.each_with_index do |t, i|
|
420
|
-
type = i.zero? &&
|
422
|
+
type = i.zero? && t.interpolated_variable? ? :VARIABLE : t.type
|
421
423
|
tokens << new_token(type, t.value, :raw => t.raw)
|
422
424
|
end
|
423
425
|
when :UNENC_VAR
|
@@ -449,7 +451,7 @@ class PuppetLint
|
|
449
451
|
lexer = PuppetLint::Lexer.new
|
450
452
|
lexer.tokenise(segment[1])
|
451
453
|
lexer.tokens.each_with_index do |t, i|
|
452
|
-
type = i.zero? && t.
|
454
|
+
type = i.zero? && t.interpolated_variable? ? :VARIABLE : t.type
|
453
455
|
tokens << new_token(type, t.value, :raw => t.raw)
|
454
456
|
end
|
455
457
|
when :UNENC_VAR
|
@@ -60,7 +60,7 @@ class PuppetLint
|
|
60
60
|
|
61
61
|
def parse_heredoc(heredoc_tag)
|
62
62
|
heredoc_name = heredoc_tag[%r{\A"?(.+?)"?(:.+?)?#{PuppetLint::Lexer::WHITESPACE_RE}*(/.*)?\Z}, 1]
|
63
|
-
end_heredoc_pattern = %r{
|
63
|
+
end_heredoc_pattern = %r{^\|?\s*-?\s*#{Regexp.escape(heredoc_name)}$}
|
64
64
|
interpolation = heredoc_tag.start_with?('"')
|
65
65
|
|
66
66
|
@segment_type = :HEREDOC
|
@@ -98,8 +98,19 @@ class PuppetLint
|
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
101
|
-
|
102
|
-
|
101
|
+
# Get the number of characters consumed by the StringSlurper.
|
102
|
+
#
|
103
|
+
# StringScanner from Ruby 2.0 onwards supports #charpos which returns
|
104
|
+
# the number of characters and is multibyte character aware.
|
105
|
+
#
|
106
|
+
# Prior to this, Ruby's multibyte character support in Strings was a
|
107
|
+
# bit unusual and neither String#length nor String#split behave as
|
108
|
+
# expected, so we use String#scan to split all the consumed text using
|
109
|
+
# a UTF-8 aware regex and use the length of the result
|
110
|
+
def consumed_chars
|
111
|
+
return scanner.charpos if scanner.respond_to?(:charpos)
|
112
|
+
|
113
|
+
(scanner.pre_match + scanner.matched).scan(%r{.}mu).length
|
103
114
|
end
|
104
115
|
|
105
116
|
def start_interp
|
@@ -199,6 +199,12 @@ class PuppetLint
|
|
199
199
|
end
|
200
200
|
nil
|
201
201
|
end
|
202
|
+
|
203
|
+
def interpolated_variable?
|
204
|
+
return false if type == :TYPE && value != 'type'
|
205
|
+
return true if type == :NAME
|
206
|
+
PuppetLint::Lexer::KEYWORDS.include?(type.to_s.downcase)
|
207
|
+
end
|
202
208
|
end
|
203
209
|
end
|
204
210
|
end
|
@@ -22,6 +22,7 @@ PuppetLint.new_check(:parameter_order) do
|
|
22
22
|
end
|
23
23
|
|
24
24
|
next unless hash_or_array_stack.empty? && paren_stack.empty?
|
25
|
+
next unless parameter?(token)
|
25
26
|
next unless required_parameter?(token)
|
26
27
|
|
27
28
|
prev_tokens = class_idx[:param_tokens][0..i]
|
@@ -38,9 +39,19 @@ PuppetLint.new_check(:parameter_order) do
|
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
|
-
def
|
42
|
+
def parameter?(token)
|
42
43
|
return false unless token.type == :VARIABLE
|
44
|
+
return false unless token.prev_code_token
|
45
|
+
|
46
|
+
[
|
47
|
+
:LPAREN, # First parameter, no type specification
|
48
|
+
:COMMA, # Subsequent parameter, no type specification
|
49
|
+
:TYPE, # Parameter with simple type specification
|
50
|
+
:RBRACK, # Parameter with complex type specification
|
51
|
+
].include?(token.prev_code_token.type)
|
52
|
+
end
|
43
53
|
|
54
|
+
def required_parameter?(token)
|
44
55
|
data_type = token.prev_token_of(:TYPE, :skip_blocks => true)
|
45
56
|
return false if data_type && data_type.value == 'Optional'
|
46
57
|
|
@@ -30,8 +30,12 @@ PuppetLint.new_check(:documentation) do
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def find_comment_token(start_token)
|
33
|
+
newlines = 0
|
34
|
+
|
33
35
|
prev_token = start_token.prev_token
|
34
36
|
while !prev_token.nil? && WHITESPACE_TOKENS.include?(prev_token.type)
|
37
|
+
newlines += 1 if prev_token.type == :NEWLINE
|
38
|
+
break if newlines > 1
|
35
39
|
prev_token = prev_token.prev_token
|
36
40
|
end
|
37
41
|
|
@@ -26,7 +26,7 @@ PuppetLint.new_check(:ensure_first_param) do
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def fix(problem)
|
29
|
-
first_param_name_token =
|
29
|
+
first_param_name_token = problem[:resource][:param_tokens].first
|
30
30
|
first_param_comma_token = first_param_name_token.next_token_of(:COMMA)
|
31
31
|
ensure_param_name_token = first_param_comma_token.next_token_of(:NAME, :value => 'ensure')
|
32
32
|
|
@@ -20,6 +20,7 @@ class PuppetLint
|
|
20
20
|
attr_accessor :ignore_paths
|
21
21
|
attr_accessor :with_filename
|
22
22
|
attr_accessor :disable_checks
|
23
|
+
attr_accessor :only_checks
|
23
24
|
attr_accessor :fail_on_warnings
|
24
25
|
attr_accessor :error_level
|
25
26
|
attr_accessor :log_format
|
@@ -40,6 +41,7 @@ class PuppetLint
|
|
40
41
|
@pattern = DEFAULT_PATTERN
|
41
42
|
@with_filename = true
|
42
43
|
@disable_checks = []
|
44
|
+
@only_checks = []
|
43
45
|
@ignore_paths = []
|
44
46
|
|
45
47
|
define(args, &task_block)
|
@@ -55,6 +57,17 @@ class PuppetLint
|
|
55
57
|
task @name do
|
56
58
|
PuppetLint::OptParser.build
|
57
59
|
|
60
|
+
if Array(@only_checks).any?
|
61
|
+
enable_checks = Array(@only_checks).map(&:to_sym)
|
62
|
+
PuppetLint.configuration.checks.each do |check|
|
63
|
+
if enable_checks.include?(check)
|
64
|
+
PuppetLint.configuration.send("enable_#{check}")
|
65
|
+
else
|
66
|
+
PuppetLint.configuration.send("disable_#{check}")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
58
71
|
Array(@disable_checks).each do |check|
|
59
72
|
PuppetLint.configuration.send("disable_#{check}")
|
60
73
|
end
|
@@ -79,6 +92,7 @@ class PuppetLint
|
|
79
92
|
matched_files = matched_files.exclude(*@ignore_paths)
|
80
93
|
|
81
94
|
matched_files.to_a.each do |puppet_file|
|
95
|
+
next unless File.file?(puppet_file)
|
82
96
|
linter.file = puppet_file
|
83
97
|
linter.run
|
84
98
|
linter.print_problems
|
@@ -55,7 +55,9 @@ def with_puppet_lint_head
|
|
55
55
|
end
|
56
56
|
|
57
57
|
task :release_test do
|
58
|
-
branch = if ENV['
|
58
|
+
branch = if ENV['GITHUB_REF']
|
59
|
+
ENV['GITHUB_REF']
|
60
|
+
elsif ENV['APPVEYOR']
|
59
61
|
ENV['APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH']
|
60
62
|
elsif ENV['TRAVIS']
|
61
63
|
ENV['TRAVIS_PULL_REQUEST_BRANCH']
|
data/lib/puppet-lint/version.rb
CHANGED
@@ -91,8 +91,8 @@ describe PuppetLint::Bin do
|
|
91
91
|
its(:stdout) do
|
92
92
|
is_expected.to eq(
|
93
93
|
[
|
94
|
-
"#{args[0]} - WARNING: optional parameter listed before required parameter on line 2",
|
95
|
-
"#{args[1]} - ERROR: test::foo not in autoload module layout on line 2",
|
94
|
+
"#{args[0]} - WARNING: optional parameter listed before required parameter on line 2 (check: parameter_order)",
|
95
|
+
"#{args[1]} - ERROR: test::foo not in autoload module layout on line 2 (check: autoloader_layout)",
|
96
96
|
].join("\n")
|
97
97
|
)
|
98
98
|
end
|
@@ -102,7 +102,7 @@ describe PuppetLint::Bin do
|
|
102
102
|
let(:args) { 'spec/fixtures/test/manifests/malformed.pp' }
|
103
103
|
|
104
104
|
its(:exitstatus) { is_expected.to eq(1) }
|
105
|
-
its(:stdout) { is_expected.to eq('ERROR: Syntax error on line 1') }
|
105
|
+
its(:stdout) { is_expected.to eq('ERROR: Syntax error on line 1 (check: syntax)') }
|
106
106
|
its(:stderr) { is_expected.to eq('Try running `puppet parser validate <file>`') }
|
107
107
|
end
|
108
108
|
|
@@ -198,7 +198,7 @@ describe PuppetLint::Bin do
|
|
198
198
|
its(:stdout) do
|
199
199
|
is_expected.to eq(
|
200
200
|
[
|
201
|
-
'WARNING: optional parameter listed before required parameter on line 2',
|
201
|
+
'WARNING: optional parameter listed before required parameter on line 2 (check: parameter_order)',
|
202
202
|
'',
|
203
203
|
" define test::warning($foo='bar', $baz) { }",
|
204
204
|
' ^',
|
@@ -432,7 +432,7 @@ describe PuppetLint::Bin do
|
|
432
432
|
its(:stdout) do
|
433
433
|
is_expected.to eq(
|
434
434
|
[
|
435
|
-
'IGNORED: double quoted string containing no variables on line 3',
|
435
|
+
'IGNORED: double quoted string containing no variables on line 3 (check: double_quoted_strings)',
|
436
436
|
' for a good reason',
|
437
437
|
].join("\n")
|
438
438
|
)
|
@@ -457,7 +457,7 @@ describe PuppetLint::Bin do
|
|
457
457
|
end
|
458
458
|
|
459
459
|
its(:exitstatus) { is_expected.to eq(0) }
|
460
|
-
its(:stdout) { is_expected.to match(%r{^.*line 6
|
460
|
+
its(:stdout) { is_expected.to match(%r{^.*line 6(?!\d)}) }
|
461
461
|
end
|
462
462
|
|
463
463
|
context 'when an lint:endignore control comment exists with no opening lint:ignore comment' do
|
@@ -548,7 +548,7 @@ describe PuppetLint::Bin do
|
|
548
548
|
|
549
549
|
its(:exitstatus) { is_expected.to eq(0) }
|
550
550
|
its(:stdout) do
|
551
|
-
is_expected.to eq('WARNING: variable contains a dash on line 3')
|
551
|
+
is_expected.to eq('WARNING: variable contains a dash on line 3 (check: variable_contains_dash)')
|
552
552
|
end
|
553
553
|
end
|
554
554
|
|
@@ -562,7 +562,7 @@ describe PuppetLint::Bin do
|
|
562
562
|
|
563
563
|
its(:exitstatus) { is_expected.to eq(0) }
|
564
564
|
its(:stdout) do
|
565
|
-
is_expected.to eq('WARNING: variable contains an uppercase letter on line 4')
|
565
|
+
is_expected.to eq('WARNING: variable contains an uppercase letter on line 4 (check: variable_is_lowercase)')
|
566
566
|
end
|
567
567
|
end
|
568
568
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe PuppetLint::Lexer::StringSlurper do
|
@@ -316,6 +318,18 @@ describe PuppetLint::Lexer::StringSlurper do
|
|
316
318
|
describe '#parse_heredoc' do
|
317
319
|
subject(:segments) { described_class.new(heredoc).parse_heredoc(heredoc_tag) }
|
318
320
|
|
321
|
+
context 'when the heredoc text contains the tag' do
|
322
|
+
let(:heredoc) { %( SOMETHING else\n |-THING) }
|
323
|
+
let(:heredoc_tag) { 'THING' }
|
324
|
+
|
325
|
+
it 'terminates the heredoc at the closing tag' do
|
326
|
+
expect(segments).to eq([
|
327
|
+
[:HEREDOC, " SOMETHING else\n "],
|
328
|
+
[:HEREDOC_TERM, '|-THING'],
|
329
|
+
])
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
319
333
|
context 'when parsing a heredoc with interpolation disabled' do
|
320
334
|
context 'that is a plain heredoc' do
|
321
335
|
let(:heredoc) { %( SOMETHING\n ELSE\n :\n |-myheredoc) }
|
@@ -436,4 +450,24 @@ describe PuppetLint::Lexer::StringSlurper do
|
|
436
450
|
end
|
437
451
|
end
|
438
452
|
end
|
453
|
+
|
454
|
+
describe '#consumed_chars' do
|
455
|
+
subject { described_class.new(string).tap(&:parse).consumed_chars }
|
456
|
+
|
457
|
+
context 'when slurping a string containing multibyte characters' do
|
458
|
+
let(:string) { 'accentués"' }
|
459
|
+
|
460
|
+
it 'counts the multibyte character as a single consumed character' do
|
461
|
+
is_expected.to eq(10)
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
context 'when slurping an empty string' do
|
466
|
+
let(:string) { '"' }
|
467
|
+
|
468
|
+
it 'consumes only the closing quote' do
|
469
|
+
is_expected.to eq(1)
|
470
|
+
end
|
471
|
+
end
|
472
|
+
end
|
439
473
|
end
|
@@ -147,5 +147,23 @@ describe 'parameter_order' do
|
|
147
147
|
|
148
148
|
it { expect(problems).to have(0).problems }
|
149
149
|
end
|
150
|
+
|
151
|
+
context "#{type} parameter with array operation" do
|
152
|
+
let(:code) do
|
153
|
+
<<-END
|
154
|
+
#{type} ntp (
|
155
|
+
# XXX: remove self from list
|
156
|
+
Array[String] $ntp_servers = [
|
157
|
+
'foo',
|
158
|
+
'bar',
|
159
|
+
'baz',
|
160
|
+
] - $::fqdn,
|
161
|
+
Array[String] $pools = [],
|
162
|
+
) { }
|
163
|
+
END
|
164
|
+
end
|
165
|
+
|
166
|
+
it { expect(problems).to have(0).problems }
|
167
|
+
end
|
150
168
|
end
|
151
169
|
end
|
@@ -29,6 +29,24 @@ describe 'documentation' do
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
+
describe 'incorrectly documented class' do
|
33
|
+
let(:code) do
|
34
|
+
<<-END
|
35
|
+
# foo
|
36
|
+
|
37
|
+
class test {}
|
38
|
+
END
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should only detect a single problem' do
|
42
|
+
expect(problems).to have(1).problem
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should create a warning' do
|
46
|
+
expect(problems).to contain_warning(class_msg).on_line(3).in_column(9)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
32
50
|
describe 'undocumented defined type' do
|
33
51
|
let(:code) { 'define test {}' }
|
34
52
|
|
@@ -93,15 +93,15 @@ describe 'double_quoted_strings' do
|
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
|
-
context 'double quoted
|
96
|
+
context 'double quoted strings containing supported escape patterns' do
|
97
97
|
let(:code) do
|
98
98
|
<<-END
|
99
|
-
$string1 = "this string
|
100
|
-
$string2 = "this string contains \
|
99
|
+
$string1 = "this string contains \n newline"
|
100
|
+
$string2 = "this string contains \t tab"
|
101
101
|
$string3 = "this string contains \${escaped} var"
|
102
102
|
$string4 = "this string contains \\"escaped \\" double quotes"
|
103
103
|
$string5 = "this string contains \\'escaped \\' single quotes"
|
104
|
-
$string6 = "this string contains \r
|
104
|
+
$string6 = "this string contains \r carriage return"
|
105
105
|
$string7 = "this string contains \\\\ an escaped backslash"
|
106
106
|
END
|
107
107
|
end
|
@@ -112,7 +112,7 @@ describe 'double_quoted_strings' do
|
|
112
112
|
end
|
113
113
|
|
114
114
|
context 'double quoted string with random escape should be rejected' do
|
115
|
-
let(:code) { %( $ztring = "this string contains \l random
|
115
|
+
let(:code) { %( $ztring = "this string contains \l random escape" ) }
|
116
116
|
|
117
117
|
it 'should only detect a single problem' do
|
118
118
|
expect(problems).to have(1).problem
|
@@ -22,4 +22,32 @@ describe 'variable_is_lowercase' do
|
|
22
22
|
expect(problems).to have(0).problems
|
23
23
|
end
|
24
24
|
end
|
25
|
+
|
26
|
+
context 'when typecasting inside an interpolation' do
|
27
|
+
let(:code) { %("${Integer(fact('memory.system.total_bytes'))}") }
|
28
|
+
|
29
|
+
it 'should not detect any problems' do
|
30
|
+
expect(problems).to have(0).problems
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when an interpolated variable contains an uppercase letter' do
|
35
|
+
let(:code) { '"${fooBar}"' }
|
36
|
+
|
37
|
+
it 'should only detect a single problem' do
|
38
|
+
expect(problems).to have(1).problem
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should create a warning' do
|
42
|
+
expect(problems).to contain_warning(msg).on_line(1).in_column(4)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when an interpolated variable only contains lowercase letters' do
|
47
|
+
let(:code) { '"${foobar}"' }
|
48
|
+
|
49
|
+
it 'should not detect any problems' do
|
50
|
+
expect(problems).to have(0).problems
|
51
|
+
end
|
52
|
+
end
|
25
53
|
end
|