puppet-lint 2.2.1 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
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