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