puppet-lint 2.2.1 → 2.3.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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -19
  3. data/README.md +181 -174
  4. data/Rakefile +1 -1
  5. data/lib/puppet-lint/bin.rb +6 -0
  6. data/lib/puppet-lint/lexer.rb +1 -1
  7. data/lib/puppet-lint/plugins.rb +16 -10
  8. data/lib/puppet-lint/plugins/check_classes/arrow_on_right_operand_line.rb +46 -0
  9. data/lib/puppet-lint/plugins/check_classes/autoloader_layout.rb +33 -0
  10. data/lib/puppet-lint/plugins/check_classes/class_inherits_from_params_class.rb +20 -0
  11. data/lib/puppet-lint/plugins/check_classes/code_on_top_scope.rb +20 -0
  12. data/lib/puppet-lint/plugins/check_classes/inherits_across_namespaces.rb +22 -0
  13. data/lib/puppet-lint/plugins/check_classes/names_containing_dash.rb +23 -0
  14. data/lib/puppet-lint/plugins/check_classes/names_containing_uppercase.rb +28 -0
  15. data/lib/puppet-lint/plugins/check_classes/nested_classes_or_defines.rb +28 -0
  16. data/lib/puppet-lint/plugins/check_classes/parameter_order.rb +44 -0
  17. data/lib/puppet-lint/plugins/check_classes/right_to_left_relationship.rb +15 -0
  18. data/lib/puppet-lint/plugins/check_classes/variable_scope.rb +143 -0
  19. data/lib/puppet-lint/plugins/check_comments/slash_comments.rb +22 -0
  20. data/lib/puppet-lint/plugins/{check_comments.rb → check_comments/star_comments.rb} +1 -24
  21. data/lib/puppet-lint/plugins/{check_conditionals.rb → check_conditionals/case_without_default.rb} +1 -27
  22. data/lib/puppet-lint/plugins/check_conditionals/selector_inside_resource.rb +25 -0
  23. data/lib/puppet-lint/plugins/{check_documentation.rb → check_documentation/documentation.rb} +0 -0
  24. data/lib/puppet-lint/plugins/{check_nodes.rb → check_nodes/unquoted_node_name.rb} +0 -0
  25. data/lib/puppet-lint/plugins/check_resources/duplicate_params.rb +37 -0
  26. data/lib/puppet-lint/plugins/check_resources/ensure_first_param.rb +70 -0
  27. data/lib/puppet-lint/plugins/check_resources/ensure_not_symlink_target.rb +44 -0
  28. data/lib/puppet-lint/plugins/check_resources/file_mode.rb +42 -0
  29. data/lib/puppet-lint/plugins/check_resources/unquoted_file_mode.rb +31 -0
  30. data/lib/puppet-lint/plugins/check_resources/unquoted_resource_title.rb +22 -0
  31. data/lib/puppet-lint/plugins/check_strings/double_quoted_strings.rb +27 -0
  32. data/lib/puppet-lint/plugins/check_strings/only_variable_string.rb +63 -0
  33. data/lib/puppet-lint/plugins/check_strings/puppet_url_without_modules.rb +25 -0
  34. data/lib/puppet-lint/plugins/check_strings/quoted_booleans.rb +26 -0
  35. data/lib/puppet-lint/plugins/check_strings/single_quote_string_with_variables.rb +17 -0
  36. data/lib/puppet-lint/plugins/check_strings/variables_not_enclosed.rb +23 -0
  37. data/lib/puppet-lint/plugins/check_variables/variable_contains_dash.rb +21 -0
  38. data/lib/puppet-lint/plugins/{check_variables.rb → check_variables/variable_is_lowercase.rb} +0 -22
  39. data/lib/puppet-lint/plugins/check_whitespace/140chars.rb +21 -0
  40. data/lib/puppet-lint/plugins/check_whitespace/2sp_soft_tabs.rb +19 -0
  41. data/lib/puppet-lint/plugins/check_whitespace/80chars.rb +21 -0
  42. data/lib/puppet-lint/plugins/{check_whitespace.rb → check_whitespace/arrow_alignment.rb} +0 -118
  43. data/lib/puppet-lint/plugins/check_whitespace/hard_tabs.rb +24 -0
  44. data/lib/puppet-lint/plugins/check_whitespace/trailing_whitespace.rb +28 -0
  45. data/lib/puppet-lint/tasks/puppet-lint.rb +4 -0
  46. data/lib/puppet-lint/version.rb +1 -1
  47. data/spec/puppet-lint/bin_spec.rb +18 -1
  48. data/spec/puppet-lint/lexer_spec.rb +19 -0
  49. metadata +41 -68
  50. data/lib/puppet-lint/plugins/check_classes.rb +0 -433
  51. data/lib/puppet-lint/plugins/check_resources.rb +0 -251
  52. data/lib/puppet-lint/plugins/check_strings.rb +0 -186
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ begin
11
11
  GitHubChangelogGenerator::RakeTask.new :changelog do |config|
12
12
  version = PuppetLint::VERSION
13
13
  config.future_release = "#{version}"
14
- config.exclude_labels = %w{duplicate question invalid wontfix}
14
+ config.exclude_labels = %w{duplicate question invalid wontfix release-pr}
15
15
  end
16
16
  rescue LoadError
17
17
  end
@@ -53,11 +53,15 @@ class PuppetLint::Bin
53
53
  end
54
54
 
55
55
  return_val = 0
56
+
57
+ puts '[' if PuppetLint.configuration.json
56
58
  path.each do |f|
57
59
  l = PuppetLint.new
58
60
  l.file = f
59
61
  l.run
60
62
  l.print_problems
63
+ puts ',' if f != path.last and PuppetLint.configuration.json
64
+
61
65
  if l.errors? or (l.warnings? and PuppetLint.configuration.fail_on_warnings)
62
66
  return_val = 1
63
67
  end
@@ -68,6 +72,8 @@ class PuppetLint::Bin
68
72
  end
69
73
  end
70
74
  end
75
+ puts ']' if PuppetLint.configuration.json
76
+
71
77
  return return_val
72
78
 
73
79
  rescue PuppetLint::NoCodeError
@@ -416,7 +416,7 @@ class PuppetLint
416
416
  line += value.scan(/(\r\n|\r|\n)/).size
417
417
  token_column = column + (ss.pos - value.size)
418
418
  tokens << new_token(:DQPOST, value, :line => line, :column => token_column)
419
- @column = token_column + 1
419
+ @column = column + ss.pos + 1
420
420
  @line_no = line
421
421
  end
422
422
  else
@@ -40,12 +40,24 @@ class PuppetLint
40
40
  # Returns an Array of Gem::Specification objects.
41
41
  def self.gemspecs
42
42
  @gemspecs ||= if Gem::Specification.respond_to?(:latest_specs)
43
- Gem::Specification.latest_specs
43
+ Gem::Specification.latest_specs(load_prerelease_plugins?)
44
44
  else
45
45
  Gem.searcher.init_gemspecs
46
46
  end
47
47
  end
48
48
 
49
+ # Internal: Determine whether to load plugins that contain a letter in their version number.
50
+ #
51
+ # Returns true if the configuration is set to load "prerelease" gems, false otherwise.
52
+ def self.load_prerelease_plugins?
53
+ # Load prerelease plugins (which ruby defines as any gem which has a letter in its version number).
54
+ # Can't use puppet-lint configuration object here because this code executes before the command line is parsed.
55
+ if ENV['PUPPET_LINT_LOAD_PRERELEASE_PLUGINS']
56
+ return %w(true yes).include?(ENV['PUPPET_LINT_LOAD_PRERELEASE_PLUGINS'].downcase)
57
+ end
58
+ false
59
+ end
60
+
49
61
  # Internal: Retrieve a list of available gem paths from RubyGems.
50
62
  #
51
63
  # Returns an Array of Pathname objects.
@@ -61,14 +73,8 @@ class PuppetLint
61
73
  end
62
74
  end
63
75
 
64
- require 'puppet-lint/plugins/check_classes'
65
- require 'puppet-lint/plugins/check_comments'
66
- require 'puppet-lint/plugins/check_conditionals'
67
- require 'puppet-lint/plugins/check_documentation'
68
- require 'puppet-lint/plugins/check_strings'
69
- require 'puppet-lint/plugins/check_variables'
70
- require 'puppet-lint/plugins/check_whitespace'
71
- require 'puppet-lint/plugins/check_resources'
72
- require 'puppet-lint/plugins/check_nodes'
76
+ Dir[File.expand_path('plugins/**/*.rb', File.dirname(__FILE__))].each do |file|
77
+ require file
78
+ end
73
79
 
74
80
  PuppetLint::Plugins.load_from_gems
@@ -0,0 +1,46 @@
1
+ # Public: Test the manifest tokens for chaining arrow that is
2
+ # on the line of the left operand when the right operand is on another line.
3
+ #
4
+ # https://docs.puppet.com/guides/style_guide.html#chaining-arrow-syntax
5
+ PuppetLint.new_check(:arrow_on_right_operand_line) do
6
+ def check
7
+ tokens.select { |r| Set[:IN_EDGE, :IN_EDGE_SUB].include?(r.type) }.each do |token|
8
+ if token.next_code_token.line != token.line
9
+ notify :warning, {
10
+ :message => 'arrow should be on the right operand\'s line',
11
+ :line => token.line,
12
+ :column => token.column,
13
+ :token => token,
14
+ }
15
+ end
16
+ end
17
+ end
18
+
19
+ def fix(problem)
20
+ token = problem[:token]
21
+ tokens.delete(token)
22
+
23
+ # remove any excessive whitespace on the line
24
+ temp_token = token.prev_code_token
25
+ while (temp_token = temp_token.next_token)
26
+ tokens.delete(temp_token) if whitespace?(temp_token)
27
+ break if temp_token.type == :NEWLINE
28
+ end
29
+
30
+ temp_token.next_token = token
31
+ token.prev_token = temp_token
32
+ index = tokens.index(token.next_code_token)
33
+ tokens.insert(index, token)
34
+
35
+ whitespace_token = PuppetLint::Lexer::Token.new(:WHITESPACE, ' ', temp_token.line + 1, 3)
36
+ whitespace_token.prev_token = token
37
+ token.next_token = whitespace_token
38
+ whitespace_token.next_token = tokens[index + 1]
39
+ tokens[index + 1].prev_token = whitespace_token
40
+ tokens.insert(index + 1, whitespace_token)
41
+ end
42
+
43
+ def whitespace?(token)
44
+ Set[:INDENT, :WHITESPACE].include?(token.type)
45
+ end
46
+ end
@@ -0,0 +1,33 @@
1
+ # Public: Test the manifest tokens for any classes or defined types that are
2
+ # not in an appropriately named file for the autoloader to detect and record
3
+ # an error of each instance found.
4
+ #
5
+ # https://docs.puppet.com/guides/style_guide.html#separate-files
6
+ PuppetLint.new_check(:autoloader_layout) do
7
+ def check
8
+ unless fullpath.nil? || fullpath == ''
9
+ (class_indexes + defined_type_indexes).each do |class_idx|
10
+ title_token = class_idx[:name_token]
11
+ split_title = title_token.value.split('::')
12
+ mod = split_title.first
13
+ if split_title.length > 1
14
+ expected_path = "/#{mod}/manifests/#{split_title[1..-1].join('/')}.pp"
15
+ else
16
+ expected_path = "/#{title_token.value}/manifests/init.pp"
17
+ end
18
+
19
+ if PuppetLint.configuration.relative
20
+ expected_path = expected_path.gsub(/^\//,'').split('/')[1..-1].join('/')
21
+ end
22
+
23
+ unless fullpath.end_with? expected_path
24
+ notify :error, {
25
+ :message => "#{title_token.value} not in autoload module layout",
26
+ :line => title_token.line,
27
+ :column => title_token.column,
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,20 @@
1
+ # Public: Check the manifest tokens for any classes that inherit a params
2
+ # subclass and record a warning for each instance found.
3
+ #
4
+ # No style guide reference
5
+ PuppetLint.new_check(:class_inherits_from_params_class) do
6
+ def check
7
+ class_indexes.each do |class_idx|
8
+ unless class_idx[:inherited_token].nil?
9
+ if class_idx[:inherited_token].value.end_with? '::params'
10
+ notify :warning, {
11
+ :message => 'class inheriting from params class',
12
+ :line => class_idx[:inherited_token].line,
13
+ :column => class_idx[:inherited_token].column,
14
+ }
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ PuppetLint.configuration.send('disable_class_inherits_from_params_class')
@@ -0,0 +1,20 @@
1
+ # Public: Test that no code is outside of a class or define scope.
2
+ #
3
+ # No style guide reference
4
+ PuppetLint.new_check(:code_on_top_scope) do
5
+ def check
6
+ class_scope = (class_indexes + defined_type_indexes).map { |e| tokens[e[:start]..e[:end]] }.flatten
7
+ top_scope = tokens - class_scope
8
+
9
+ top_scope.each do |token|
10
+ unless formatting_tokens.include? token.type
11
+ notify :warning, {
12
+ :message => "code outside of class or define block - #{token.value}",
13
+ :line => token.line,
14
+ :column => token.column
15
+ }
16
+ end
17
+ end
18
+ end
19
+ end
20
+ PuppetLint.configuration.send("disable_code_on_top_scope")
@@ -0,0 +1,22 @@
1
+ # Public: Test the manifest tokens for any classes that inherit across
2
+ # namespaces and record a warning for each instance found.
3
+ #
4
+ # https://docs.puppet.com/guides/style_guide.html#class-inheritance
5
+ PuppetLint.new_check(:inherits_across_namespaces) do
6
+ def check
7
+ class_indexes.each do |class_idx|
8
+ unless class_idx[:inherited_token].nil?
9
+ inherited_module_name = class_idx[:inherited_token].value.split('::').reject { |r| r.empty? }.first
10
+ class_module_name = class_idx[:name_token].value.split('::').reject { |r| r.empty? }.first
11
+
12
+ unless class_module_name == inherited_module_name
13
+ notify :warning, {
14
+ :message => "class inherits across module namespaces",
15
+ :line => class_idx[:inherited_token].line,
16
+ :column => class_idx[:inherited_token].column,
17
+ }
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ # Public: Check the manifest tokens for any classes or defined types that
2
+ # have a dash in their name and record an error for each instance found.
3
+ #
4
+ # No style guide reference
5
+ PuppetLint.new_check(:names_containing_dash) do
6
+ def check
7
+ (class_indexes + defined_type_indexes).each do |class_idx|
8
+ if class_idx[:name_token].value.include? '-'
9
+ if class_idx[:type] == :CLASS
10
+ obj_type = 'class'
11
+ else
12
+ obj_type = 'defined type'
13
+ end
14
+
15
+ notify :error, {
16
+ :message => "#{obj_type} name containing a dash",
17
+ :line => class_idx[:name_token].line,
18
+ :column => class_idx[:name_token].column,
19
+ }
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ # Public: Find and warn about module names with illegal uppercase characters.
2
+ #
3
+ # https://docs.puppet.com/puppet/latest/reference/modules_fundamentals.html#allowed-module-names
4
+ # Provides a fix. [puppet-lint #554]
5
+ PuppetLint.new_check(:names_containing_uppercase) do
6
+ def check
7
+ (class_indexes + defined_type_indexes).each do |class_idx|
8
+ if class_idx[:name_token].value =~ /[A-Z]/
9
+ if class_idx[:type] == :CLASS
10
+ obj_type = 'class'
11
+ else
12
+ obj_type = 'defined type'
13
+ end
14
+
15
+ notify :error, {
16
+ :message => "#{obj_type} '#{class_idx[:name_token].value}' contains illegal uppercase",
17
+ :line => class_idx[:name_token].line,
18
+ :column => class_idx[:name_token].column,
19
+ :token => class_idx[:name_token],
20
+ }
21
+ end
22
+ end
23
+ end
24
+
25
+ def fix(problem)
26
+ problem[:token].value.downcase!
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # Public: Test the manifest tokens for any classes or defined types that are
2
+ # defined inside another class.
3
+ #
4
+ # https://docs.puppet.com/guides/style_guide.html#nested-classes-or-defined-types
5
+ PuppetLint.new_check(:nested_classes_or_defines) do
6
+ TOKENS = Set[:CLASS, :DEFINE]
7
+
8
+ def check
9
+ class_indexes.each do |class_idx|
10
+ # Skip the first token so that we don't pick up the first :CLASS
11
+ class_tokens = class_idx[:tokens][1..-1]
12
+
13
+ class_tokens.each do |token|
14
+ if TOKENS.include?(token.type)
15
+ if token.next_code_token.type != :LBRACE
16
+ type = token.type == :CLASS ? 'class' : 'defined type'
17
+
18
+ notify :warning, {
19
+ :message => "#{type} defined inside a class",
20
+ :line => token.line,
21
+ :column => token.column,
22
+ }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,44 @@
1
+ # Public: Test the manifest tokens for any parameterised classes or defined
2
+ # types that take parameters and record a warning if there are any optional
3
+ # parameters listed before required parameters.
4
+ #
5
+ # https://docs.puppet.com/guides/style_guide.html#display-order-of-parameters
6
+ PuppetLint.new_check(:parameter_order) do
7
+ def check
8
+ (class_indexes + defined_type_indexes).each do |class_idx|
9
+ unless class_idx[:param_tokens].nil?
10
+ paren_stack = []
11
+ hash_or_array_stack = []
12
+ class_idx[:param_tokens].each_with_index do |token, i|
13
+ if token.type == :LPAREN
14
+ paren_stack.push(true)
15
+ elsif token.type == :RPAREN
16
+ paren_stack.pop
17
+ elsif token.type == :LBRACE || token.type == :LBRACK
18
+ hash_or_array_stack.push(true)
19
+ elsif token.type == :RBRACE || token.type == :RBRACK
20
+ hash_or_array_stack.pop
21
+ end
22
+ next if (! hash_or_array_stack.empty?)
23
+ next unless paren_stack.empty?
24
+
25
+ if token.type == :VARIABLE
26
+ if token.next_code_token.nil? || [:COMMA, :RPAREN].include?(token.next_code_token.type)
27
+ prev_tokens = class_idx[:param_tokens][0..i]
28
+ unless prev_tokens.rindex { |r| r.type == :EQUALS }.nil?
29
+ unless token.prev_code_token.nil? or token.prev_code_token.type == :EQUALS
30
+ msg = 'optional parameter listed before required parameter'
31
+ notify :warning, {
32
+ :message => msg,
33
+ :line => token.line,
34
+ :column => token.column,
35
+ }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,15 @@
1
+ # Public: Test the manifest tokens for any right-to-left (<-) chaining
2
+ # operators and record a warning for each instance found.
3
+ #
4
+ # https://docs.puppet.com/guides/style_guide.html#chaining-arrow-syntax
5
+ PuppetLint.new_check(:right_to_left_relationship) do
6
+ def check
7
+ tokens.select { |r| r.type == :OUT_EDGE }.each do |token|
8
+ notify :warning, {
9
+ :message => 'right-to-left (<-) relationship',
10
+ :line => token.line,
11
+ :column => token.column,
12
+ }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,143 @@
1
+ # Public: Test the manifest tokens for any variables that are referenced in
2
+ # the manifest. If the variables are not fully qualified or one of the
3
+ # variables automatically created in the scope, check that they have been
4
+ # defined in the local scope and record a warning for each variable that has
5
+ # not.
6
+ #
7
+ # https://docs.puppet.com/guides/style_guide.html#namespacing-variables
8
+ PuppetLint.new_check(:variable_scope) do
9
+ DEFAULT_SCOPE_VARS = Set[
10
+ 'name',
11
+ 'title',
12
+ 'module_name',
13
+ 'environment',
14
+ 'clientcert',
15
+ 'clientversion',
16
+ 'servername',
17
+ 'serverip',
18
+ 'serverversion',
19
+ 'caller_module_name',
20
+ 'alias',
21
+ 'audit',
22
+ 'before',
23
+ 'loglevel',
24
+ 'noop',
25
+ 'notify',
26
+ 'require',
27
+ 'schedule',
28
+ 'stage',
29
+ 'subscribe',
30
+ 'tag',
31
+ 'facts',
32
+ 'trusted',
33
+ 'server_facts',
34
+ ]
35
+ POST_VAR_TOKENS = Set[:COMMA, :EQUALS, :RPAREN]
36
+
37
+ def check
38
+ variables_in_scope = DEFAULT_SCOPE_VARS.clone
39
+
40
+ (class_indexes + defined_type_indexes).each do |idx|
41
+ referenced_variables = Set[]
42
+ object_tokens = idx[:tokens]
43
+
44
+ unless idx[:param_tokens].nil?
45
+ idx[:param_tokens].each do |token|
46
+ if token.type == :VARIABLE
47
+ if POST_VAR_TOKENS.include? token.next_code_token.type
48
+ variables_in_scope << token.value
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ future_parser_scopes = {}
55
+ in_pipe = false
56
+ block_params_stack = []
57
+
58
+ object_tokens.each do |token|
59
+ case token.type
60
+ when :EQUALS
61
+ if token.prev_code_token.type == :VARIABLE
62
+ variables_in_scope << token.prev_code_token.value
63
+ elsif token.prev_code_token.type == :RBRACK
64
+ temp_token = token
65
+
66
+ brack_depth = 0
67
+ while temp_token = temp_token.prev_code_token
68
+ case temp_token.type
69
+ when :VARIABLE
70
+ variables_in_scope << temp_token.value
71
+ when :RBRACK
72
+ brack_depth += 1
73
+ when :LBRACK
74
+ brack_depth -= 1
75
+ break if brack_depth == 0
76
+ when :COMMA
77
+ # ignore
78
+ else # unexpected
79
+ break
80
+ end
81
+ end
82
+ end
83
+ when :VARIABLE
84
+ if in_pipe
85
+ block_params_stack[-1] << token.value
86
+ else
87
+ referenced_variables << token
88
+ end
89
+ when :PIPE
90
+ in_pipe = !in_pipe
91
+
92
+ if in_pipe
93
+ block_params_stack << []
94
+ else
95
+ start_idx = tokens.find_index(token)
96
+ end_token = nil
97
+ brace_depth = 0
98
+
99
+ tokens[start_idx..-1].each do |sub_token|
100
+ case sub_token.type
101
+ when :LBRACE
102
+ brace_depth += 1
103
+ when :RBRACE
104
+ brace_depth -= 1
105
+ if brace_depth == 0
106
+ end_token = sub_token
107
+ break
108
+ end
109
+ end
110
+ end
111
+
112
+ params = block_params_stack.pop
113
+ (token.line..end_token.line).each do |line|
114
+ future_parser_scopes[line] ||= []
115
+ future_parser_scopes[line].concat(params)
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ msg = "top-scope variable being used without an explicit namespace"
122
+ referenced_variables.each do |token|
123
+ unless future_parser_scopes[token.line].nil?
124
+ next if future_parser_scopes[token.line].include?(token.value.gsub(/\[.+\]\Z/, ''))
125
+ end
126
+
127
+ unless token.value.include? '::'
128
+ unless token.value =~ /^(facts|trusted)\[.+\]/
129
+ unless variables_in_scope.include? token.value.gsub(/\[.+\]\Z/, '')
130
+ unless token.value =~ /\A\d+\Z/
131
+ notify :warning, {
132
+ :message => msg,
133
+ :line => token.line,
134
+ :column => token.column,
135
+ }
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end