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
@@ -0,0 +1,7 @@
1
+ unless String.respond_to?('prepend')
2
+ class String
3
+ def prepend(lead)
4
+ self.replace "#{lead}#{self}"
5
+ end
6
+ end
7
+ end
@@ -1,5 +1,45 @@
1
+ require 'pathname'
2
+
1
3
  class PuppetLint
2
4
  class Plugins
5
+ # Public: Find any gems containing puppet-lint plugins and load them.
6
+ #
7
+ # Returns nothing.
8
+ def self.load_from_gems
9
+ gem_directories.select { |path|
10
+ (path + 'puppet-lint/plugins').directory?
11
+ }.each do |gem_path|
12
+ Dir["#{(gem_path + 'puppet-lint/plugins').to_s}/*.rb"].each do |file|
13
+ load file
14
+ end
15
+ end
16
+ end
17
+ private
18
+ # Internal: Check if RubyGems is loaded and available.
19
+ #
20
+ # Returns true if RubyGems is available, false if not.
21
+ def self.has_rubygems?
22
+ defined? ::Gem
23
+ end
24
+
25
+ # Internal: Retrieve a list of available gem paths from RubyGems.
26
+ #
27
+ # Returns an Array of Pathname objects.
28
+ def self.gem_directories
29
+ if has_rubygems?
30
+ if Gem::Specification.respond_to? :latest_specs
31
+ Gem::Specification.latest_specs.map do |spec|
32
+ Pathname.new(spec.full_gem_path) + 'lib'
33
+ end
34
+ else
35
+ Gem.searcher.init_gemspecs.map do |spec|
36
+ Pathname.new(spec.full_gem_path) + 'lib'
37
+ end
38
+ end
39
+ else
40
+ []
41
+ end
42
+ end
3
43
  end
4
44
  end
5
45
 
@@ -11,3 +51,5 @@ require 'puppet-lint/plugins/check_strings'
11
51
  require 'puppet-lint/plugins/check_variables'
12
52
  require 'puppet-lint/plugins/check_whitespace'
13
53
  require 'puppet-lint/plugins/check_resources'
54
+
55
+ PuppetLint::Plugins.load_from_gems
@@ -7,7 +7,14 @@ class PuppetLint::Plugins::CheckComments < PuppetLint::CheckPlugin
7
7
  tokens.select { |token|
8
8
  token.type == :SLASH_COMMENT
9
9
  }.each do |token|
10
- notify :warning, {
10
+ if PuppetLint.configuration.fix
11
+ token.type = :COMMENT
12
+ notify_type = :fixed
13
+ else
14
+ notify_type = :warning
15
+ end
16
+
17
+ notify notify_type, {
11
18
  :message => '// comment found',
12
19
  :linenumber => token.line,
13
20
  :column => token.column,
@@ -6,7 +6,14 @@ class PuppetLint::Plugins::CheckResources < PuppetLint::CheckPlugin
6
6
  check 'unquoted_resource_title' do
7
7
  title_tokens.each do |token|
8
8
  if token.type == :NAME
9
- notify :warning, {
9
+ if PuppetLint.configuration.fix
10
+ token.type = :SSTRING
11
+ notify_type = :fixed
12
+ else
13
+ notify_type = :warning
14
+ end
15
+
16
+ notify notify_type, {
10
17
  :message => 'unquoted resource title',
11
18
  :linenumber => token.line,
12
19
  :column => token.column,
@@ -102,7 +109,14 @@ class PuppetLint::Plugins::CheckResources < PuppetLint::CheckPlugin
102
109
  }.each do |resource_token|
103
110
  value_token = resource_token.next_code_token.next_code_token
104
111
  if {:NAME => true, :NUMBER => true}.include? value_token.type
105
- notify :warning, {
112
+ if PuppetLint.configuration.fix
113
+ value_token.type = :SSTRING
114
+ notify_type = :fixed
115
+ else
116
+ notify_type = :warning
117
+ end
118
+
119
+ notify notify_type, {
106
120
  :message => 'unquoted file mode',
107
121
  :linenumber => value_token.line,
108
122
  :column => value_token.column,
@@ -142,7 +156,15 @@ class PuppetLint::Plugins::CheckResources < PuppetLint::CheckPlugin
142
156
  break if value_token.value =~ sym_mode
143
157
  break if value_token.type == :UNDEF
144
158
 
145
- notify :warning, {
159
+ notify_type = :warning
160
+
161
+ if PuppetLint.configuration.fix && value_token.value =~ /\A[0-7]{3}\Z/
162
+ value_token.value = "0#{value_token.value.to_s}"
163
+ value_token.type = :SSTRING
164
+ notify_type = :fixed
165
+ end
166
+
167
+ notify notify_type, {
146
168
  :message => msg,
147
169
  :linenumber => value_token.line,
148
170
  :column => value_token.column,
@@ -12,7 +12,14 @@ class PuppetLint::Plugins::CheckStrings < PuppetLint::CheckPlugin
12
12
  }.select { |r|
13
13
  r.value[/(\t|\\t|\n|\\n)/].nil?
14
14
  }.each do |token|
15
- notify :warning, {
15
+ if PuppetLint.configuration.fix
16
+ token.type = :SSTRING
17
+ notify_type = :fixed
18
+ else
19
+ notify_type = :warning
20
+ end
21
+
22
+ notify notify_type, {
16
23
  :message => 'double quoted string containing no variables',
17
24
  :linenumber => token.line,
18
25
  :column => token.column,
@@ -32,10 +39,36 @@ class PuppetLint::Plugins::CheckStrings < PuppetLint::CheckPlugin
32
39
  if {:VARIABLE => true, :UNENC_VARIABLE => true}.include? tokens[token_idx + 1].type
33
40
  if tokens[token_idx + 2].type == :DQPOST
34
41
  if tokens[token_idx + 2].value == ''
35
- notify :warning, {
42
+ if PuppetLint.configuration.fix
43
+ prev_token = token.prev_token
44
+ prev_code_token = token.prev_code_token
45
+ next_token = token.next_token.next_token.next_token
46
+ next_code_token = token.next_token.next_token.next_code_token
47
+ var_token = token.next_token
48
+
49
+ tokens.delete_at(token_idx + 2)
50
+ tokens.delete_at(token_idx)
51
+
52
+ prev_token.next_token = var_token unless prev_token.nil?
53
+ prev_code_token.next_code_token = var_token unless prev_code_token.nil?
54
+ next_code_token.prev_code_token = var_token unless next_code_token.nil?
55
+ next_token.prev_token = var_token unless next_token.nil?
56
+ var_token.type = :VARIABLE
57
+ var_token.next_token = next_token
58
+ var_token.next_code_token = next_code_token
59
+ var_token.prev_code_token = prev_code_token
60
+ var_token.prev_token = prev_token
61
+ notify_type = :fixed
62
+ notify_token = var_token
63
+ else
64
+ notify_type = :warning
65
+ notify_token = tokens[token_idx + 1]
66
+ end
67
+
68
+ notify notify_type, {
36
69
  :message => 'string containing only a variable',
37
- :linenumber => tokens[token_idx + 1].line,
38
- :column => tokens[token_idx + 1].column,
70
+ :linenumber => notify_token.line,
71
+ :column => notify_token.column,
39
72
  }
40
73
  end
41
74
  end
@@ -53,7 +86,14 @@ class PuppetLint::Plugins::CheckStrings < PuppetLint::CheckPlugin
53
86
  tokens.select { |r|
54
87
  r.type == :UNENC_VARIABLE
55
88
  }.each do |token|
56
- notify :warning, {
89
+ if PuppetLint.configuration.fix
90
+ token.type = :VARIABLE
91
+ notify_type = :fixed
92
+ else
93
+ notify_type = :warning
94
+ end
95
+
96
+ notify notify_type, {
57
97
  :message => 'variable not enclosed in {}',
58
98
  :linenumber => token.line,
59
99
  :column => token.column,
@@ -86,7 +126,14 @@ class PuppetLint::Plugins::CheckStrings < PuppetLint::CheckPlugin
86
126
  tokens.select { |r|
87
127
  {:STRING => true, :SSTRING => true}.include?(r.type) && %w{true false}.include?(r.value)
88
128
  }.each do |token|
89
- notify :warning, {
129
+ if PuppetLint.configuration.fix
130
+ token.type = token.value.upcase.to_sym
131
+ notify_type = :fixed
132
+ else
133
+ notify_type = :warning
134
+ end
135
+
136
+ notify notify_type, {
90
137
  :message => 'quoted boolean value found',
91
138
  :linenumber => token.line,
92
139
  :column => token.column,
@@ -4,42 +4,65 @@ class PuppetLint::Plugins::CheckWhitespace < PuppetLint::CheckPlugin
4
4
  #
5
5
  # Returns nothing.
6
6
  check 'hard_tabs' do
7
- manifest_lines.each_with_index do |line, idx|
8
- if line.include? "\t"
9
- notify :error, {
10
- :message => 'tab character found',
11
- :linenumber => idx + 1,
12
- :column => line.index("\t") + 1,
13
- }
7
+ tokens.select { |r|
8
+ [:INDENT, :WHITESPACE].include?(r.type) && r.value.include?("\t")
9
+ }.each do |token|
10
+ if PuppetLint.configuration.fix
11
+ token.value.gsub!("\t", ' ')
12
+ notify_type = :fixed
13
+ else
14
+ notify_type = :error
14
15
  end
16
+
17
+ notify notify_type, {
18
+ :message => 'tab character found',
19
+ :linenumber => token.line,
20
+ :column => token.column,
21
+ }
15
22
  end
16
23
  end
17
24
 
18
- # Check the raw manifest string for lines ending with whitespace and record
19
- # an error for each instance found.
25
+ # Check the manifest tokens for lines ending with whitespace and record an
26
+ # error for each instance found.
20
27
  #
21
28
  # Returns nothing.
22
29
  check 'trailing_whitespace' do
23
- manifest_lines.each_with_index do |line, idx|
24
- if line.end_with? ' '
25
- notify :error, {
26
- :message => 'trailing whitespace found',
27
- :linenumber => idx + 1,
28
- :column => line.rindex(' ') + 1,
29
- }
30
+ tokens.select { |token|
31
+ token.type == :WHITESPACE
32
+ }.select { |token|
33
+ token.next_token.nil? || token.next_token.type == :NEWLINE
34
+ }.each do |token|
35
+ if PuppetLint.configuration.fix
36
+ notify_type = :fixed
37
+ prev_token = token.prev_token
38
+ next_token = token.next_token
39
+
40
+ tokens.delete(token)
41
+ prev_token.next_token = next_token
42
+
43
+ unless next_token.nil?
44
+ next_token.prev_token = prev_token
45
+ end
46
+ else
47
+ notify_type = :error
30
48
  end
49
+
50
+ notify notify_type, {
51
+ :message => 'trailing whitespace found',
52
+ :linenumber => token.line,
53
+ :column => token.column,
54
+ }
31
55
  end
32
56
  end
33
57
 
34
58
  # Test the raw manifest string for lines containing more than 80 characters
35
59
  # and record a warning for each instance found. The only exception to this
36
- # rule is lines containing puppet:// URLs which would hurt readability if
37
- # split.
60
+ # rule is lines containing URLs which would hurt readability if split.
38
61
  #
39
62
  # Returns nothing.
40
63
  check '80chars' do
41
64
  manifest_lines.each_with_index do |line, idx|
42
- unless line =~ /puppet:\/\//
65
+ unless line =~ /:\/\//
43
66
  if line.scan(/./mu).size > 80
44
67
  notify :warning, {
45
68
  :message => 'line has more than 80 characters',
@@ -75,7 +98,8 @@ class PuppetLint::Plugins::CheckWhitespace < PuppetLint::CheckPlugin
75
98
  # Returns nothing.
76
99
  check 'arrow_alignment' do
77
100
  resource_indexes.each do |res_idx|
78
- indent_depth = [nil]
101
+ indent_depth = [0]
102
+ indent_depth_idx = 0
79
103
  resource_tokens = tokens[res_idx[:start]..res_idx[:end]]
80
104
  resource_tokens.reject! do |token|
81
105
  {:COMMENT => true, :SLASH_COMMENT => true, :MLCOMMENT => true}.include? token.type
@@ -88,21 +112,40 @@ class PuppetLint::Plugins::CheckWhitespace < PuppetLint::CheckPlugin
88
112
  if token.type == :FARROW
89
113
  indent_length = token.column
90
114
 
91
- if indent_depth.last.nil?
92
- indent_depth[-1] = indent_length
115
+ if indent_depth[indent_depth_idx] < indent_length
116
+ indent_depth[indent_depth_idx] = indent_length
93
117
  end
94
118
 
95
- unless indent_depth.last == indent_length
96
- notify :warning, {
119
+ elsif token.type == :LBRACE
120
+ indent_depth_idx += 1
121
+ indent_depth << 0
122
+ elsif token.type == :RBRACE
123
+ indent_depth_idx -= 1
124
+ end
125
+ end
126
+
127
+ indent_depth_idx = 0
128
+ resource_tokens.each_with_index do |token, idx|
129
+ if token.type == :FARROW
130
+ indent_length = token.column
131
+ unless indent_depth[indent_depth_idx] == indent_length
132
+ if PuppetLint.configuration.fix
133
+ offset = indent_depth[indent_depth_idx] - indent_length
134
+ token.prev_token.value = token.prev_token.value + (' ' * offset)
135
+ notify_type = :fixed
136
+ else
137
+ notify_type = :warning
138
+ end
139
+ notify notify_type, {
97
140
  :message => 'indentation of => is not properly aligned',
98
141
  :linenumber => token.line,
99
142
  :column => token.column,
100
143
  }
101
144
  end
102
145
  elsif token.type == :LBRACE
103
- indent_depth.push(nil)
146
+ indent_depth_idx += 1
104
147
  elsif token.type == :RBRACE
105
- indent_depth.pop
148
+ indent_depth_idx -= 1
106
149
  end
107
150
  end
108
151
  end
@@ -1,3 +1,3 @@
1
1
  class PuppetLint
2
- VERSION = '0.3.2'
2
+ VERSION = '0.4.0.pre1'
3
3
  end
data/puppet-lint.gemspec CHANGED
@@ -15,7 +15,6 @@ Gem::Specification.new do |s|
15
15
  s.require_paths = ["lib"]
16
16
 
17
17
  s.add_development_dependency 'rspec'
18
- s.add_development_dependency 'rdoc'
19
18
  s.add_development_dependency 'rcov'
20
19
 
21
20
  s.authors = ['Tim Sharpe']
@@ -49,6 +49,7 @@ describe PuppetLint::Configuration do
49
49
  'error_level' => :all,
50
50
  'log_format' => '',
51
51
  'with_context' => false,
52
+ 'fix' => false,
52
53
  }
53
54
  end
54
55
  end
@@ -654,9 +654,9 @@ describe PuppetLint::Lexer do
654
654
  end
655
655
 
656
656
  it 'should match comments on multiple lines' do
657
- token = @lexer.tokenise("/*\n * foo bar\n*/").first
657
+ token = @lexer.tokenise("/* foo\n * bar\n*/").first
658
658
  token.type.should == :MLCOMMENT
659
- token.value.should == 'foo bar'
659
+ token.value.should == "foo\nbar"
660
660
  end
661
661
  end
662
662
 
@@ -13,4 +13,26 @@ describe 'slash_comments' do
13
13
  })
14
14
  end
15
15
  end
16
+
17
+ describe 'slash comments w/fix' do
18
+ before do
19
+ PuppetLint.configuration.fix = true
20
+ end
21
+
22
+ after do
23
+ PuppetLint.configuration.fix = false
24
+ end
25
+
26
+ let(:code) { '// foo' }
27
+
28
+ its(:problems) do
29
+ should only_have_problem({
30
+ :kind => :fixed,
31
+ :message => '// comment found',
32
+ :linenumber => 1,
33
+ :column => 1,
34
+ })
35
+ end
36
+ its(:manifest) { should == '# foo' }
37
+ end
16
38
  end
@@ -4,9 +4,35 @@ describe 'file_mode' do
4
4
  describe '3 digit file mode' do
5
5
  let(:code) { "file { 'foo': mode => '777' }" }
6
6
 
7
- its(:problems) {
8
- should only_have_problem :kind => :warning, :message => "mode should be represented as a 4 digit octal value or symbolic mode", :linenumber => 1
9
- }
7
+ its(:problems) do
8
+ should only_have_problem({
9
+ :kind => :warning,
10
+ :message => "mode should be represented as a 4 digit octal value or symbolic mode",
11
+ :linenumber => 1,
12
+ :column => 23,
13
+ })
14
+ end
15
+ end
16
+
17
+ describe '3 digit file mode w/fix' do
18
+ before do
19
+ PuppetLint.configuration.fix = true
20
+ end
21
+
22
+ after do
23
+ PuppetLint.configuration.fix = false
24
+ end
25
+
26
+ let(:code) { "file { 'foo': mode => '777' }" }
27
+
28
+ its(:manifest) { should == "file { 'foo': mode => '0777' }" }
29
+ its(:problems) do
30
+ should only_have_problem({
31
+ :kind => :fixed,
32
+ :message => 'mode should be represented as a 4 digit octal value or symbolic mode',
33
+ :linenumber => 1,
34
+ })
35
+ end
10
36
  end
11
37
 
12
38
  describe '4 digit file mode' do
@@ -15,29 +41,116 @@ describe 'file_mode' do
15
41
  its(:problems) { should be_empty }
16
42
  end
17
43
 
44
+ describe '4 digit file mode w/fix' do
45
+ before do
46
+ PuppetLint.configuration.fix = true
47
+ end
48
+
49
+ after do
50
+ PuppetLint.configuration.fix = false
51
+ end
52
+
53
+ let(:code) { "file { 'foo': mode => '0777' }" }
54
+
55
+ its(:problems) { should be_empty }
56
+ its(:manifest) { should == "file { 'foo': mode => '0777' }" }
57
+ end
58
+
18
59
  describe 'file mode as a variable' do
19
60
  let(:code) { "file { 'foo': mode => $file_mode }" }
20
61
 
21
62
  its(:problems) { should be_empty }
22
63
  end
23
64
 
65
+ describe 'file mode as a variable w/fix' do
66
+ before do
67
+ PuppetLint.configuration.fix = true
68
+ end
69
+
70
+ after do
71
+ PuppetLint.configuration.fix = false
72
+ end
73
+
74
+ let(:code) { "file { 'foo': mode => $file_mode }" }
75
+
76
+ its(:problems) { should be_empty }
77
+ its(:manifest) { should == "file { 'foo': mode => $file_mode }" }
78
+ end
79
+
24
80
  describe 'symbolic file mode' do
25
81
  let(:code) { "file { 'foo': mode => 'u=rw,og=r' }" }
26
82
 
27
83
  its(:problems) { should be_empty }
28
84
  end
29
85
 
86
+ describe 'symbolic file mode w/fix' do
87
+ before do
88
+ PuppetLint.configuration.fix = true
89
+ end
90
+
91
+ after do
92
+ PuppetLint.configuration.fix = false
93
+ end
94
+
95
+ let(:code) { "file { 'foo': mode => 'u=rw,og=r' }" }
96
+
97
+ its(:problems) { should be_empty }
98
+ its(:manifest) { should == "file { 'foo': mode => 'u=rw,og=r' }" }
99
+ end
100
+
30
101
  describe 'file mode undef unquoted' do
31
102
  let(:code) { "file { 'foo': mode => undef }" }
32
103
 
33
104
  its(:problems) { should be_empty }
34
105
  end
35
106
 
107
+ describe 'file mode undef unquoted w/fix' do
108
+ before do
109
+ PuppetLint.configuration.fix = true
110
+ end
111
+
112
+ after do
113
+ PuppetLint.configuration.fix = false
114
+ end
115
+
116
+ let(:code) { "file { 'foo': mode => undef }" }
117
+
118
+ its(:problems) { should be_empty }
119
+ its(:manifest) { should == "file { 'foo': mode => undef }" }
120
+ end
121
+
122
+ describe 'file mode undef quoted' do
123
+ let(:code) { "file { 'foo': mode => 'undef' }" }
124
+
125
+ its(:problems) do
126
+ should only_have_problem({
127
+ :kind => :warning,
128
+ :message => "mode should be represented as a 4 digit octal value or symbolic mode",
129
+ :linenumber => 1,
130
+ :column => 23,
131
+ })
132
+ end
133
+ end
134
+
36
135
  describe 'file mode undef quoted' do
136
+ before do
137
+ PuppetLint.configuration.fix = true
138
+ end
139
+
140
+ after do
141
+ PuppetLint.configuration.fix = false
142
+ end
143
+
37
144
  let(:code) { "file { 'foo': mode => 'undef' }" }
38
145
 
39
- its(:problems) {
40
- should only_have_problem :kind => :warning, :message => "mode should be represented as a 4 digit octal value or symbolic mode", :linenumber => 1
41
- }
146
+ its(:problems) do
147
+ should only_have_problem({
148
+ :kind => :warning,
149
+ :message => "mode should be represented as a 4 digit octal value or symbolic mode",
150
+ :linenumber => 1,
151
+ :column => 23,
152
+ })
153
+ end
154
+ its(:manifest) { should == "file { 'foo': mode => 'undef' }" }
42
155
  end
43
156
  end