puppet-lint 4.2.4 → 5.0.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +21 -1
  3. data/lib/puppet-lint/bin.rb +4 -1
  4. data/lib/puppet-lint/checkplugin.rb +3 -3
  5. data/lib/puppet-lint/checks.rb +24 -11
  6. data/lib/puppet-lint/configuration.rb +1 -1
  7. data/lib/puppet-lint/data.rb +2 -2
  8. data/lib/puppet-lint/lexer/string_slurper.rb +6 -6
  9. data/lib/puppet-lint/lexer/token.rb +9 -9
  10. data/lib/puppet-lint/lexer.rb +40 -3
  11. data/lib/puppet-lint/optparser.rb +4 -0
  12. data/lib/puppet-lint/plugins/check_classes/arrow_on_right_operand_line.rb +1 -1
  13. data/lib/puppet-lint/plugins/check_comments/slash_comments.rb +1 -1
  14. data/lib/puppet-lint/plugins/check_comments/star_comments.rb +1 -1
  15. data/lib/puppet-lint/plugins/check_nodes/unquoted_node_name.rb +1 -1
  16. data/lib/puppet-lint/plugins/check_resources/ensure_first_param.rb +1 -1
  17. data/lib/puppet-lint/plugins/check_resources/ensure_not_symlink_target.rb +1 -1
  18. data/lib/puppet-lint/plugins/check_resources/file_mode.rb +1 -1
  19. data/lib/puppet-lint/plugins/check_resources/unquoted_resource_title.rb +1 -1
  20. data/lib/puppet-lint/plugins/check_strings/double_quoted_strings.rb +2 -2
  21. data/lib/puppet-lint/plugins/check_strings/only_variable_string.rb +2 -2
  22. data/lib/puppet-lint/plugins/check_strings/puppet_url_without_modules.rb +1 -1
  23. data/lib/puppet-lint/plugins/check_strings/quoted_booleans.rb +1 -1
  24. data/lib/puppet-lint/plugins/check_strings/variables_not_enclosed.rb +1 -1
  25. data/lib/puppet-lint/plugins/check_whitespace/hard_tabs.rb +1 -1
  26. data/lib/puppet-lint/plugins/check_whitespace/space_before_arrow.rb +72 -0
  27. data/lib/puppet-lint/plugins/check_whitespace/trailing_whitespace.rb +1 -1
  28. data/lib/puppet-lint/plugins/legacy_facts/legacy_facts.rb +49 -2
  29. data/lib/puppet-lint/plugins/top_scope_facts/top_scope_facts.rb +1 -1
  30. data/lib/puppet-lint/plugins.rb +1 -1
  31. data/lib/puppet-lint/report/codeclimate.rb +1 -1
  32. data/lib/puppet-lint/tasks/puppet-lint.rb +3 -3
  33. data/lib/puppet-lint/tasks/release_test.rb +2 -2
  34. data/lib/puppet-lint/version.rb +1 -1
  35. data/lib/puppet-lint.rb +2 -3
  36. data/rubocop_baseline.yml +1 -8
  37. data/spec/acceptance/puppet_lint_spec.rb +7 -0
  38. data/spec/fixtures/test/manifests/parseable.yaml +3 -0
  39. data/spec/fixtures/test/manifests/top_scope_variables.pp +4 -0
  40. data/spec/spec_helper.rb +3 -2
  41. data/spec/unit/puppet-lint/bin_spec.rb +12 -0
  42. data/spec/unit/puppet-lint/checks_spec.rb +49 -0
  43. data/spec/unit/puppet-lint/lexer_spec.rb +21 -13
  44. data/spec/unit/puppet-lint/plugins/check_whitespace/space_before_arrow_spec.rb +126 -0
  45. data/spec/unit/puppet-lint/plugins/legacy_facts/legacy_facts_spec.rb +59 -0
  46. metadata +11 -8
  47. data/lib/puppet-lint/monkeypatches.rb +0 -51
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9b11b8ac88b45741f4cc6ec8a5eca05da09c52c1064a0bbd89394459981845b6
4
- data.tar.gz: ed5b7f181a30f706ca4224a526d29212ed2a7e9fb4d2fea0927e2e2a3e02ae8c
3
+ metadata.gz: 4f8119856eb91c58047532aa02757d8b4904120ad6401a3af2b385a1a40aa321
4
+ data.tar.gz: 870ad4f57b3c1dfcf40207af7a84d5afc18ec388bb520e362e6d8bafbd647205
5
5
  SHA512:
6
- metadata.gz: b79ad1ee950cc2dde1c61dd3adaf0b1900b80c63916d28f6ed5fba99f84ae76608ccfba01e533c6f7014f05b659904dc80ea9631b7ebfef573166a4a930b69ac
7
- data.tar.gz: 927256f4add5e6c2fd2e0336ba6244b730223429de681fd73644e0c45df36b8d4237899af8bc2c92b7f3e4b159beedbe8c1071c94e5504178ea7dbfcaeb86d93
6
+ metadata.gz: 76294db75e44acc05da97c12f26f5dae32d358e4b0e254c92bda9a0d3f9948d0e07b2d6a6e615e238457214ea85f22e2d499e15184ad1b747665d63d667bdc80
7
+ data.tar.gz: 5d2402a687836436ceb7a63d7510d28acb6e594a399d2904cc2a2215d14971b006642cfb8a89365833af5a7307c08e44759e980870bd75e828944920e5149bc3
data/README.md CHANGED
@@ -41,6 +41,8 @@ To instruct Lint to automatically fix any issues that it detects, use the `--fix
41
41
  puppet-lint --fix /modules
42
42
  ```
43
43
 
44
+ Note: The auto-fix functionality is available for Puppet manifest files only.
45
+
44
46
  ### Modify which checks to run
45
47
 
46
48
  Puppet Lint options allow you to modify which checks to run. You can disable any of the checks temporarily or permanently, or you can limit testing to specific checks.
@@ -117,7 +119,7 @@ Any flag that can be specified on the command line can also be specified in the
117
119
  Or to specify an allowlist of allowed checks, include a line like:
118
120
 
119
121
  ```
120
- --only-checks=trailing_whitespace,hard_tabs,duplicate_params,double_quoted_strings,unquoted_file_mode,only_variable_string,variables_not_enclosed,single_quote_string_with_variables,variable_contains_dash,ensure_not_symlink_target,unquoted_resource_title,relative_classname_inclusion,file_mode,resource_reference_without_title_capital,leading_zero,arrow_alignment,variable_is_lowercase,ensure_first_param,resource_reference_without_whitespace,file_ensure,trailing_comma,leading_zero
122
+ --only-checks=trailing_whitespace,hard_tabs,duplicate_params,double_quoted_strings,unquoted_file_mode,only_variable_string,variables_not_enclosed,single_quote_string_with_variables,variable_contains_dash,ensure_not_symlink_target,unquoted_resource_title,relative_classname_inclusion,file_mode,resource_reference_without_title_capital,leading_zero,arrow_alignment,space_before_arrow,variable_is_lowercase,ensure_first_param,resource_reference_without_whitespace,file_ensure,trailing_comma,leading_zero
121
123
  ```
122
124
 
123
125
  Please note that there is an important difference between reading options from the command line and reading options from a configuration file: In the former case the shell interprets one level of quotes. That does not happen in the latter case. So, it would make sense to quote some configuration values on the command line, like so:
@@ -245,6 +247,23 @@ See `puppet-lint --help` for a full list of command line options and checks.
245
247
 
246
248
  For a complete list of checks, and how to resolve errors on each check, see the Puppet Lint [checks](http://puppet-lint.com/checks/) page.
247
249
 
250
+ ### YAML Checks
251
+
252
+ Puppet Lint can check Hiera YAML files for legacy facts.
253
+
254
+ For example, the following YAML would trigger warnings:
255
+
256
+ ```yaml
257
+ hierarchy:
258
+ - name: "Legacy Facts Example"
259
+ paths:
260
+ - "os/%{facts.hostname}.yaml"
261
+ - "/var/www/%{::hostname}.yaml"
262
+ - "%{::bios_vendor}.yaml"
263
+ ```
264
+
265
+ Refer to the [Puppet Core Facts documentation](https://www.puppet.com/docs/puppet/8/core_facts.html) for a complete list of available core facts.
266
+
248
267
  ### Spacing, Indentation, and Whitespace
249
268
 
250
269
  * Must use two-space soft tabs.
@@ -253,6 +272,7 @@ For a complete list of checks, and how to resolve errors on each check, see the
253
272
  * Should not exceed an 140-character line width.
254
273
  * An exception has been made for `source => 'puppet://...'` lines as splitting these over multiple lines decreases the readability of the manifests.
255
274
  * Should align arrows (`=>`) within blocks of attributes.
275
+ * Should contain at most a single space before an arrows(`=>`) where the parameter block contains exactly one parameter.
256
276
 
257
277
  ### Quoting
258
278
 
@@ -66,7 +66,10 @@ class PuppetLint::Bin
66
66
 
67
67
  path = path.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
68
68
  path = if File.directory?(path)
69
- Dir.glob("#{path}/**/*.pp")
69
+ Dir.glob([
70
+ "#{path}/**/*.pp",
71
+ "#{path}/**/*.{yaml,yml}",
72
+ ])
70
73
  else
71
74
  @args
72
75
  end
@@ -180,9 +180,9 @@ class PuppetLint::CheckPlugin
180
180
  def default_info
181
181
  @default_info ||= {
182
182
  check: self.class.const_get(:NAME),
183
- fullpath: fullpath,
184
- path: path,
185
- filename: filename
183
+ fullpath:,
184
+ path:,
185
+ filename:
186
186
  }
187
187
  end
188
188
 
@@ -6,6 +6,8 @@ class PuppetLint::Checks
6
6
  # Public: Get an Array of problem Hashes.
7
7
  attr_accessor :problems
8
8
 
9
+ YAML_COMPATIBLE_CHECKS = [:legacy_facts].freeze
10
+
9
11
  # Public: Initialise a new PuppetLint::Checks object.
10
12
  def initialize
11
13
  @problems = []
@@ -34,7 +36,7 @@ class PuppetLint::Checks
34
36
  problems << {
35
37
  kind: :error,
36
38
  check: :syntax,
37
- message: message,
39
+ message:,
38
40
  line: e.line_no,
39
41
  column: e.column,
40
42
  fullpath: PuppetLint::Data.fullpath,
@@ -45,23 +47,34 @@ class PuppetLint::Checks
45
47
  end
46
48
  end
47
49
 
48
- # Internal: Run the lint checks over the manifest code.
50
+ # Internal: Run the lint checks over the manifest or YAML code.
49
51
  #
50
52
  # fileinfo - The path to the file as passed to puppet-lint as a String.
51
- # data - The String manifest code to be checked.
53
+ # data - The String manifest or YAML code to be checked.
52
54
  #
53
55
  # Returns an Array of problem Hashes.
54
56
  def run(fileinfo, data)
55
- load_data(fileinfo, data)
56
-
57
57
  checks_run = []
58
- enabled_checks.each do |check|
59
- klass = PuppetLint.configuration.check_object[check].new
60
- # FIXME: shadowing #problems
61
- problems = klass.run
62
- checks_run << [klass, problems]
63
- end
58
+ if File.extname(fileinfo).downcase.match?(%r{\.ya?ml$})
59
+ PuppetLint::Data.path = fileinfo
60
+ PuppetLint::Data.manifest_lines = data.split("\n", -1)
64
61
 
62
+ enabled_checks.select { |check| YAML_COMPATIBLE_CHECKS.include?(check) }.each do |check|
63
+ klass = PuppetLint.configuration.check_object[check].new
64
+ # FIXME: shadowing #problems
65
+ problems = klass.run
66
+ checks_run << [klass, problems]
67
+ end
68
+ else
69
+ load_data(fileinfo, data)
70
+
71
+ enabled_checks.each do |check|
72
+ klass = PuppetLint.configuration.check_object[check].new
73
+ # FIXME: shadowing #problems
74
+ problems = klass.run
75
+ checks_run << [klass, problems]
76
+ end
77
+ end
65
78
  checks_run.each do |klass, problems|
66
79
  if PuppetLint.configuration.fix
67
80
  @problems.concat(klass.fix_problems)
@@ -48,7 +48,7 @@ class PuppetLint::Configuration
48
48
  # Signature
49
49
  #
50
50
  # <option>=(value)
51
- def method_missing(method, *args, &_block)
51
+ def method_missing(method, *args, &)
52
52
  super unless method.to_s =~ %r{^(\w+)=?$}
53
53
 
54
54
  option = Regexp.last_match(1)
@@ -359,7 +359,7 @@ class PuppetLint::Data
359
359
  end: i + j + 1,
360
360
  tokens: tokens[i..(i + j + 1)],
361
361
  param_tokens: param_tokens(tokens[i..(i + j + 1)]),
362
- type: type,
362
+ type:,
363
363
  name_token: token.next_code_token,
364
364
  inherited_token: inherited_class
365
365
  }
@@ -386,7 +386,7 @@ class PuppetLint::Data
386
386
  @function_indexes ||= begin
387
387
  functions = []
388
388
  tokens.each_with_index do |token, token_idx|
389
- next unless token.type == :NAME
389
+ next unless token.type == :FUNCTION_NAME
390
390
  next unless token_idx.zero? ||
391
391
  (token_idx == 1 && tokens[0].type == :WHITESPACE) ||
392
392
  [:NEWLINE, :INDENT].include?(token.prev_token.type) ||
@@ -6,12 +6,12 @@ class PuppetLint::Lexer
6
6
  class StringSlurper
7
7
  attr_accessor :scanner, :results, :interp_stack
8
8
 
9
- START_INTERP_PATTERN = %r{\$\{}.freeze
10
- END_INTERP_PATTERN = %r{\}}.freeze
11
- END_STRING_PATTERN = %r{(\A|[^\\])(\\\\)*"}.freeze
12
- UNENC_VAR_PATTERN = %r{(\A|[^\\])\$(::)?(\w+(-\w+)*::)*\w+(-\w+)*}.freeze
13
- ESC_DQUOTE_PATTERN = %r{\\+"}.freeze
14
- LBRACE_PATTERN = %r{\{}.freeze
9
+ START_INTERP_PATTERN = %r{\$\{}
10
+ END_INTERP_PATTERN = %r{\}}
11
+ END_STRING_PATTERN = %r{(\A|[^\\])(\\\\)*"}
12
+ UNENC_VAR_PATTERN = %r{(\A|[^\\])\$(::)?(\w+(-\w+)*::)*\w+(-\w+)*}
13
+ ESC_DQUOTE_PATTERN = %r{\\+"}
14
+ LBRACE_PATTERN = %r{\{}
15
15
 
16
16
  def initialize(string)
17
17
  @scanner = StringScanner.new(string)
@@ -172,9 +172,9 @@ class PuppetLint::Lexer
172
172
  return nil unless [:next, :prev].include?(direction)
173
173
 
174
174
  opts[:skip_blocks] ||= true
175
- to_find = Array[*type]
175
+ to_find = [*type]
176
176
 
177
- token_iter = send("#{direction}_token".to_sym)
177
+ token_iter = send(:"#{direction}_token")
178
178
  until token_iter.nil?
179
179
  return token_iter if to_find.include?(token_iter.type) && (opts[:value].nil? || token_iter.value == opts[:value])
180
180
 
@@ -183,18 +183,18 @@ class PuppetLint::Lexer
183
183
 
184
184
  if opts[:skip_blocks]
185
185
  case token_iter.type
186
- when "#{opening_token}BRACE".to_sym
187
- token_iter = token_iter.send("#{direction}_token_of".to_sym, ["#{closing_token}BRACE".to_sym, opts])
188
- when "#{opening_token}BRACK".to_sym
189
- token_iter = token_iter.send("#{direction}_token_of".to_sym, ["#{closing_token}BRACK".to_sym, opts])
190
- when "#{opening_token}PAREN".to_sym
191
- token_iter = token_iter.send("#{direction}_token_of".to_sym, ["#{closing_token}PAREN".to_sym, opts])
186
+ when :"#{opening_token}BRACE"
187
+ token_iter = token_iter.send(:"#{direction}_token_of", [:"#{closing_token}BRACE", opts])
188
+ when :"#{opening_token}BRACK"
189
+ token_iter = token_iter.send(:"#{direction}_token_of", [:"#{closing_token}BRACK", opts])
190
+ when :"#{opening_token}PAREN"
191
+ token_iter = token_iter.send(:"#{direction}_token_of", [:"#{closing_token}PAREN", opts])
192
192
  end
193
193
  end
194
194
 
195
195
  return nil if token_iter.nil?
196
196
 
197
- token_iter = token_iter.send("#{direction}_token".to_sym)
197
+ token_iter = token_iter.send(:"#{direction}_token")
198
198
  end
199
199
  nil
200
200
  end
@@ -111,9 +111,9 @@ class PuppetLint::Lexer
111
111
  # \p{Zs} == ASCII + Unicode non-linebreaking whitespace
112
112
  WHITESPACE_RE = (RUBY_VERSION == '1.8.7') ? %r{[\t\v\f ]} : %r{[\t\v\f\p{Zs}]}
113
113
 
114
- LINE_END_RE = %r{(?:\r\n|\r|\n)}.freeze
114
+ LINE_END_RE = %r{(?:\r\n|\r|\n)}
115
115
 
116
- NAME_RE = %r{\A((?:(?:::)?[_a-z0-9][-\w]*)(?:::[a-z0-9][-\w]*)*)}.freeze
116
+ NAME_RE = %r{\A((?:(?:::)?[_a-z0-9][-\w]*)(?:::[a-z0-9][-\w]*)*)}
117
117
 
118
118
  # Internal: An Array of Arrays containing tokens that can be described by
119
119
  # a single regular expression. Each sub-Array contains 2 elements, the
@@ -123,7 +123,44 @@ class PuppetLint::Lexer
123
123
  [:WHITESPACE, %r{\A(#{WHITESPACE_RE}+)}],
124
124
  # FIXME: Future breaking change, the following :TYPE tokens conflict with
125
125
  # the :TYPE keyword token.
126
- [: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 Layout/LineLength
126
+ [:TYPE, %r{\A(
127
+ Any|
128
+ Array|
129
+ Binary|
130
+ Boolean|
131
+ Callable|
132
+ CatalogEntry|
133
+ Class|
134
+ Collection|
135
+ Data|
136
+ Default|
137
+ Enum|
138
+ Error|
139
+ Float|
140
+ Hash|
141
+ Integer|
142
+ Iterable|
143
+ Iterator|
144
+ NotUndef|
145
+ Numeric|
146
+ Optional|
147
+ Pattern|
148
+ Regexp|
149
+ Resource|
150
+ RichData|
151
+ Runtime|
152
+ Scalar|
153
+ ScalarData|
154
+ SemVer|
155
+ SemVerRange|
156
+ Sensitive|
157
+ String|
158
+ Struct|
159
+ Tuple|
160
+ Type|
161
+ Undef|
162
+ Variant
163
+ )\b}x],
127
164
  [:CLASSREF, %r{\A(((::){0,1}[A-Z][-\w]*)+)}],
128
165
  [:NUMBER, %r{\A\b((?:0[xX][0-9A-Fa-f]+|0?\d+(?:\.\d+)?(?:[eE]-?\d+)?))\b}],
129
166
  [:FUNCTION_NAME, %r{#{NAME_RE}(?=\()}],
@@ -128,6 +128,10 @@ class PuppetLint::OptParser
128
128
  PuppetLint.configuration.ignore_paths = paths.split(',')
129
129
  end
130
130
 
131
+ opts.on('--top-scope-variables VARS', 'A comma separated list of allowed top scope variables') do |vars|
132
+ PuppetLint.configuration.top_scope_variables = vars.split(',')
133
+ end
134
+
131
135
  PuppetLint.configuration.checks.each do |check|
132
136
  opts.on("--no-#{check}-check", "Skip the #{check} check.") do
133
137
  PuppetLint.configuration.send(:"disable_#{check}")
@@ -12,7 +12,7 @@ PuppetLint.new_check(:arrow_on_right_operand_line) do
12
12
  message: "arrow should be on the right operand's line",
13
13
  line: token.line,
14
14
  column: token.column,
15
- token: token,
15
+ token:,
16
16
  description: 'Test the manifest tokens for chaining arrow that is on the line of the left operand when the right operand is on another line.',
17
17
  help_uri: 'https://puppet.com/docs/puppet/latest/style_guide.html#chaining-arrow-syntax',
18
18
  )
@@ -12,7 +12,7 @@ PuppetLint.new_check(:slash_comments) do
12
12
  message: '// comment found',
13
13
  line: token.line,
14
14
  column: token.column,
15
- token: token,
15
+ token:,
16
16
  description: 'Check the manifest tokens for any comments started with slashes (//) and record a warning for each instance found.',
17
17
  help_uri: 'https://puppet.com/docs/puppet/latest/style_guide.html#comments',
18
18
  )
@@ -12,7 +12,7 @@ PuppetLint.new_check(:star_comments) do
12
12
  message: '/* */ comment found',
13
13
  line: token.line,
14
14
  column: token.column,
15
- token: token,
15
+ token:,
16
16
  description: 'Check the manifest tokens for any comments encapsulated with slash-asterisks (/* */) and record a warning for each instance found.',
17
17
  help_uri: 'https://puppet.com/docs/puppet/latest/style_guide.html#comments',
18
18
  )
@@ -31,7 +31,7 @@ PuppetLint.new_check(:unquoted_node_name) do
31
31
  message: 'unquoted node name found',
32
32
  line: token.line,
33
33
  column: token.column,
34
- token: token,
34
+ token:,
35
35
  description: 'Check the manifest for unquoted node names and record a warning for each instance found.',
36
36
  help_uri: nil,
37
37
  )
@@ -20,7 +20,7 @@ PuppetLint.new_check(:ensure_first_param) do
20
20
  message: "ensure found on line but it's not the first attribute",
21
21
  line: ensure_token.line,
22
22
  column: ensure_token.column,
23
- resource: resource,
23
+ resource:,
24
24
  description: 'Check the tokens of each resource instance for an ensure parameter and if ' \
25
25
  'found, check that it is the first parameter listed. If it is not the first parameter, record a warning.',
26
26
  help_uri: 'https://puppet.com/docs/puppet/latest/style_guide.html#attribute-ordering',
@@ -20,7 +20,7 @@ PuppetLint.new_check(:ensure_not_symlink_target) do
20
20
  line: value_token.line,
21
21
  column: value_token.column,
22
22
  param_token: ensure_token,
23
- value_token: value_token,
23
+ value_token:,
24
24
  description: 'Check the tokens of each File resource instance for an ensure parameter and ' \
25
25
  'record a warning if the value of that parameter looks like a symlink target (starts with a \'/\').',
26
26
  help_uri: 'https://puppet.com/docs/puppet/latest/style_guide.html#symbolic-links',
@@ -6,7 +6,7 @@
6
6
  MSG = 'mode should be represented as a 4 digit octal value or symbolic mode'.freeze
7
7
  SYM_RE = '([ugoa]*[-=+][-=+rstwxXugo]*)(,[ugoa]*[-=+][-=+rstwxXugo]*)*'.freeze
8
8
  IGNORE_TYPES = Set[:VARIABLE, :UNDEF, :FUNCTION_NAME]
9
- MODE_RE = %r{\A([0-7]{4}|#{SYM_RE})\Z}.freeze
9
+ MODE_RE = %r{\A([0-7]{4}|#{SYM_RE})\Z}
10
10
 
11
11
  PuppetLint.new_check(:file_mode) do
12
12
  def check
@@ -12,7 +12,7 @@ PuppetLint.new_check(:unquoted_resource_title) do
12
12
  message: 'unquoted resource title',
13
13
  line: token.line,
14
14
  column: token.column,
15
- token: token,
15
+ token:,
16
16
  description: 'Check the manifest tokens for any resource titles / namevars that are not quoted and record a warning for each instance found.',
17
17
  help_uri: 'https://puppet.com/docs/puppet/latest/style_guide.html#resource-names',
18
18
  )
@@ -3,7 +3,7 @@
3
3
  # each instance found.
4
4
  #
5
5
  # https://puppet.com/docs/puppet/latest/style_guide.html#quoting
6
- ESCAPE_CHAR_RE = %r{(\\\$|\\"|\\'|'|\r|\t|\\t|\\s|\n|\\n|\\\\)}.freeze
6
+ ESCAPE_CHAR_RE = %r{(\\\$|\\"|\\'|'|\r|\t|\\t|\\s|\n|\\n|\\\\)}
7
7
 
8
8
  PuppetLint.new_check(:double_quoted_strings) do
9
9
  def check
@@ -18,7 +18,7 @@ PuppetLint.new_check(:double_quoted_strings) do
18
18
  message: 'double quoted string containing no variables',
19
19
  line: token.line,
20
20
  column: token.column,
21
- token: token,
21
+ token:,
22
22
  description: 'Check the manifest tokens for any double quoted strings that don\'t ' \
23
23
  'contain any variables or common escape characters and record a warning for each instance found.',
24
24
  help_uri: 'https://puppet.com/docs/puppet/latest/style_guide.html#quoting',
@@ -27,8 +27,8 @@ PuppetLint.new_check(:only_variable_string) do
27
27
  message: 'string containing only a variable',
28
28
  line: var_token.line,
29
29
  column: var_token.column,
30
- start_token: start_token,
31
- var_token: var_token,
30
+ start_token:,
31
+ var_token:,
32
32
  end_token: eos_token,
33
33
  description: 'Check the manifest tokens for double quoted strings that contain a single variable only and record a warning for each instance found.',
34
34
  help_uri: 'https://puppet.com/docs/puppet/latest/style_guide.html#quoting',
@@ -19,7 +19,7 @@ PuppetLint.new_check(:puppet_url_without_modules) do
19
19
  message: 'puppet:// URL without modules/ found',
20
20
  line: token.line,
21
21
  column: token.column,
22
- token: token,
22
+ token:,
23
23
  description: 'Check the manifest tokens for any puppet:// URL strings where the path section doesn\'t start with modules/ and record a warning for each instance found.',
24
24
  help_uri: nil,
25
25
  )
@@ -18,7 +18,7 @@ PuppetLint.new_check(:quoted_booleans) do
18
18
  message: 'quoted boolean value found',
19
19
  line: token.line,
20
20
  column: token.column,
21
- token: token,
21
+ token:,
22
22
  description: 'Check the manifest tokens for any double or single quoted strings containing only a boolean value and record a warning for each instance found.',
23
23
  help_uri: nil,
24
24
  )
@@ -25,7 +25,7 @@ PuppetLint.new_check(:variables_not_enclosed) do
25
25
  message: 'variable not enclosed in {}',
26
26
  line: token.line,
27
27
  column: token.column,
28
- token: token,
28
+ token:,
29
29
  description: 'Check the manifest tokens for any variables in a string that have not been enclosed by braces ({}) and record a warning for each instance found.',
30
30
  help_uri: 'https://puppet.com/docs/puppet/latest/style_guide.html#quoting',
31
31
  )
@@ -16,7 +16,7 @@ PuppetLint.new_check(:hard_tabs) do
16
16
  message: 'tab character found',
17
17
  line: token.line,
18
18
  column: token.column,
19
- token: token,
19
+ token:,
20
20
  description: 'Check the raw manifest string for lines containing hard tab characters and record an error for each instance found.',
21
21
  help_uri: 'https://puppet.com/docs/puppet/latest/style_guide.html#spacing-indentation-and-whitespace',
22
22
  )
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Check the manifest tokens for any arrows (=>) that have too much space
4
+ # before them in situations when a given resource has at most one line with
5
+ # such arrows. For example:
6
+
7
+ # file {
8
+ # # too much space after "foo"
9
+ # foo⎵⎵=>⎵'bar'
10
+ # }
11
+ #
12
+ # file {
13
+ # # too much space after 'bar'
14
+ # foo⎵=>⎵{ bar⎵⎵=>⎵'baz' }
15
+ # }
16
+ #
17
+
18
+ # Linting multi-parameter resources like this:
19
+ #
20
+ # package { 'xxx':
21
+ # foo => 'bar',
22
+ # bar => 'baz',
23
+ # }
24
+ #
25
+ # is handled by the "arrow_alignment" plugin.
26
+
27
+ PuppetLint.new_check(:space_before_arrow) do
28
+ def check
29
+ resource_indexes.each do |res_idx|
30
+ resource_tokens = res_idx[:tokens]
31
+ resource_tokens.reject! do |token|
32
+ Set[:COMMENT, :SLASH_COMMENT, :MLCOMMENT].include?(token.type)
33
+ end
34
+
35
+ first_arrow = resource_tokens.index { |r| r.type == :FARROW }
36
+ last_arrow = resource_tokens.rindex { |r| r.type == :FARROW }
37
+ next if first_arrow.nil?
38
+ next if last_arrow.nil?
39
+
40
+ # If this is a single line resource, skip it
41
+ next unless resource_tokens[first_arrow].line == resource_tokens[last_arrow].line
42
+
43
+ resource_tokens.select { |token| token.type == :FARROW }.each do |token|
44
+ prev_token = token.prev_token
45
+ next unless prev_token
46
+ next if prev_token.value == ' '
47
+
48
+ line = prev_token.line
49
+ column = prev_token.column
50
+
51
+ notify(
52
+ :warning,
53
+ message: "there should be a single space before '=>' on line #{line}, column #{column}",
54
+ line:,
55
+ column:,
56
+ token: prev_token,
57
+ )
58
+ end
59
+ end
60
+ end
61
+
62
+ def fix(problem)
63
+ token = problem[:token]
64
+
65
+ if token.type == :WHITESPACE
66
+ token.value = ' '
67
+ return
68
+ end
69
+
70
+ add_token(tokens.index(token), PuppetLint::Lexer::Token.new(:WHITESPACE, ' ', 0, 0))
71
+ end
72
+ end
@@ -18,7 +18,7 @@ PuppetLint.new_check(:trailing_whitespace) do
18
18
  message: 'trailing whitespace found',
19
19
  line: token.line,
20
20
  column: token.column,
21
- token: token,
21
+ token:,
22
22
  description: 'Check the manifest tokens for lines ending with whitespace and record an error for each instance found.',
23
23
  help_uri: 'https://puppet.com/docs/puppet/latest/style_guide.html#spacing-indentation-and-whitespace',
24
24
  )
@@ -1,3 +1,5 @@
1
+ require 'yaml'
2
+
1
3
  # Public: A puppet-lint custom check to detect legacy facts.
2
4
  #
3
5
  # This check will optionally convert from legacy facts like $::operatingsystem
@@ -112,6 +114,51 @@ HASH_KEY_TYPES = Set[
112
114
 
113
115
  PuppetLint.new_check(:legacy_facts) do
114
116
  def check
117
+ if File.extname(PuppetLint::Data.path).downcase.match?(%r{\.ya?ml$})
118
+ content = PuppetLint::Data.manifest_lines
119
+ yaml_content = content.join("\n")
120
+ data = YAML.safe_load(yaml_content, aliases: true, permitted_classes: [Symbol])
121
+ search_yaml(data)
122
+ else
123
+ check_puppet
124
+ end
125
+ end
126
+
127
+ def search_yaml(data, path = [])
128
+ case data
129
+ when Hash
130
+ data.each do |k, v|
131
+ search_value(k.to_s, path)
132
+ search_yaml(v, path + [k.to_s])
133
+ end
134
+ when Array
135
+ data.each_with_index { |v, i| search_yaml(v, path + [i]) }
136
+ when String
137
+ search_value(data, path)
138
+ end
139
+ end
140
+
141
+ def search_value(value, _path)
142
+ value.scan(%r{%{(?:(?:::?)?|facts\.)([a-zA-Z0-9_]+)(?!\.[a-zA-Z])}}) do |match|
143
+ base_fact = match[0].split('.').first
144
+ next unless EASY_FACTS.include?(base_fact) || UNCONVERTIBLE_FACTS.include?(base_fact) || base_fact.match(Regexp.union(REGEX_FACTS))
145
+
146
+ notify :warning, {
147
+ message: "legacy fact '#{base_fact}'",
148
+ line: find_line_for_content(value),
149
+ column: 1
150
+ }
151
+ end
152
+ end
153
+
154
+ def find_line_for_content(content)
155
+ PuppetLint::Data.manifest_lines.each_with_index do |line, index|
156
+ return index + 1 if line.include?(content)
157
+ end
158
+ 1
159
+ end
160
+
161
+ def check_puppet
115
162
  tokens.select { |x| LEGACY_FACTS_VAR_TYPES.include?(x.type) }.each do |token|
116
163
  fact_name = ''
117
164
 
@@ -137,8 +184,8 @@ PuppetLint.new_check(:legacy_facts) do
137
184
  message: "legacy fact '#{fact_name}'",
138
185
  line: token.line,
139
186
  column: token.column,
140
- token: token,
141
- fact_name: fact_name
187
+ token:,
188
+ fact_name:
142
189
  }
143
190
  end
144
191
  end
@@ -38,7 +38,7 @@ PuppetLint.new_check(:top_scope_facts) do
38
38
  message: 'top scope fact instead of facts hash',
39
39
  line: token.line,
40
40
  column: token.column,
41
- token: token
41
+ token:
42
42
  }
43
43
  end
44
44
  end
@@ -75,7 +75,7 @@ class PuppetLint::Plugins
75
75
  end
76
76
  end
77
77
 
78
- Dir[File.expand_path('plugins/**/*.rb', File.dirname(__FILE__))].sort.each do |file|
78
+ Dir[File.expand_path('plugins/**/*.rb', File.dirname(__FILE__))].each do |file|
79
79
  require file
80
80
  end
81
81
 
@@ -25,7 +25,7 @@ class PuppetLint::Report
25
25
  check_name: message[:check],
26
26
  description: message[:message],
27
27
  categories: [:Style],
28
- severity: severity,
28
+ severity:,
29
29
  location: {
30
30
  path: message[:path],
31
31
  lines: {
@@ -27,7 +27,7 @@ class PuppetLint::RakeTask < Rake::TaskLib
27
27
  #
28
28
  # PuppetLint::RakeTask.new
29
29
  # rubocop:disable Lint/MissingSuper
30
- def initialize(*args, &task_block)
30
+ def initialize(*args, &)
31
31
  @name = args.shift || :lint
32
32
  @pattern = DEFAULT_PATTERN
33
33
  @with_filename = true
@@ -35,7 +35,7 @@ class PuppetLint::RakeTask < Rake::TaskLib
35
35
  @only_checks = []
36
36
  @ignore_paths = []
37
37
 
38
- define(args, &task_block)
38
+ define(args, &)
39
39
  end
40
40
 
41
41
  def define(args, &task_block)
@@ -65,7 +65,7 @@ class PuppetLint::RakeTask < Rake::TaskLib
65
65
 
66
66
  ['with_filename', 'fail_on_warnings', 'error_level', 'log_format', 'with_context', 'fix', 'show_ignored', 'relative'].each do |config|
67
67
  value = instance_variable_get(:"@#{config}")
68
- PuppetLint.configuration.send("#{config}=".to_sym, value) unless value.nil?
68
+ PuppetLint.configuration.send(:"#{config}=", value) unless value.nil?
69
69
  end
70
70
 
71
71
  @ignore_paths = PuppetLint.configuration.ignore_paths if PuppetLint.configuration.ignore_paths && @ignore_paths.empty?
@@ -27,7 +27,7 @@ def run_cmd(message, *cmd)
27
27
  [output.strip, status.success?]
28
28
  end
29
29
 
30
- def with_puppet_lint_head(&block)
30
+ def with_puppet_lint_head(&)
31
31
  print(' Updating Gemfile to use puppet-lint HEAD... ')
32
32
 
33
33
  buffer = Parser::Source::Buffer.new('Gemfile')
@@ -49,7 +49,7 @@ def with_puppet_lint_head(&block)
49
49
 
50
50
  puts 'Done'
51
51
 
52
- Bundler.with_clean_env(&block)
52
+ Bundler.with_clean_env(&)
53
53
 
54
54
  run_cmd('Restoring Gemfile', 'git', 'checkout', '--', 'Gemfile')
55
55
  end
@@ -1,3 +1,3 @@
1
1
  class PuppetLint
2
- VERSION = '4.2.4'.freeze
2
+ VERSION = '5.0.0'.freeze
3
3
  end
data/lib/puppet-lint.rb CHANGED
@@ -11,7 +11,6 @@ require 'puppet-lint/data'
11
11
  require 'puppet-lint/checks'
12
12
  require 'puppet-lint/report/github'
13
13
  require 'puppet-lint/bin'
14
- require 'puppet-lint/monkeypatches'
15
14
 
16
15
  class PuppetLint::NoCodeError < StandardError; end
17
16
 
@@ -250,11 +249,11 @@ class PuppetLint
250
249
  # def check
251
250
  # end
252
251
  # end
253
- def self.new_check(name, &block)
252
+ def self.new_check(name, &)
254
253
  class_name = name.to_s.split('_').map(&:capitalize).join
255
254
  klass = PuppetLint.const_set(:"Check#{class_name}", Class.new(PuppetLint::CheckPlugin))
256
255
  klass.const_set(:NAME, name)
257
- klass.class_exec(&block)
256
+ klass.class_exec(&)
258
257
  PuppetLint.configuration.add_check(name, klass)
259
258
  PuppetLint::Data.ignore_overrides[name] ||= {}
260
259
  end
data/rubocop_baseline.yml CHANGED
@@ -6,7 +6,7 @@ AllCops:
6
6
  NewCops: enable
7
7
  ExtraDetails: true
8
8
  DisplayStyleGuide: true
9
- TargetRubyVersion: '2.7'
9
+ TargetRubyVersion: '3.1'
10
10
  DisplayCopNames: true
11
11
  SuggestExtensions: false
12
12
  Exclude:
@@ -81,10 +81,3 @@ Style/Documentation:
81
81
  - spec/**/*
82
82
  Style/WordArray:
83
83
  EnforcedStyle: brackets
84
- ####################################################
85
- # Cops below here due for deprecation
86
- ####################################################
87
- # ``Rspec/FilePath`` is going to be deprecated in the next major release of rubocop >=3.0.0: see <https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FilePath>
88
- # As the new cops are already present, e.g., Rspec/SpecFilePathPathFormat, then disabling this in preparation
89
- RSpec/FilePath:
90
- Enabled: false
@@ -43,4 +43,11 @@ describe 'When executing puppet-lint' do
43
43
  expect(result[:stdout]).to have_warnings(2)
44
44
  end
45
45
  end
46
+
47
+ context 'with a YAML file provided' do
48
+ it 'returns zero errors' do
49
+ result = puppet_lint([File.join(manifest_root, 'parseable.yaml')])
50
+ expect(result[:stdout]).to have_errors(0)
51
+ end
52
+ end
46
53
  end
@@ -0,0 +1,3 @@
1
+ ---
2
+ heredoc: |
3
+ contains $
@@ -0,0 +1,4 @@
1
+ # dummy resource to test --top-scope-variables
2
+ define test::top_scope_variables() {
3
+ notice($::role)
4
+ }
data/spec/spec_helper.rb CHANGED
@@ -22,6 +22,7 @@ end
22
22
 
23
23
  require 'puppet-lint'
24
24
  require 'rspec/its'
25
+ require 'pp'
25
26
  begin
26
27
  require 'rspec/json_expectations'
27
28
  rescue LoadError, SyntaxError
@@ -33,7 +34,7 @@ module RSpec::LintExampleGroup
33
34
  def initialize(method, message)
34
35
  @expected_problem = {
35
36
  kind: method.to_s.delete_prefix('contain_').to_sym,
36
- message: message
37
+ message:
37
38
  }
38
39
  @description = ["contain a #{@expected_problem[:kind]}"]
39
40
  end
@@ -107,7 +108,7 @@ module RSpec::LintExampleGroup
107
108
  end
108
109
  end
109
110
 
110
- def method_missing(method, *args, &block)
111
+ def method_missing(method, *args, &)
111
112
  return HaveProblem.new(method, args.first) if method.to_s.start_with?('contain_')
112
113
 
113
114
  super
@@ -121,6 +121,18 @@ describe PuppetLint::Bin do
121
121
  its(:stdout) { is_expected.to eq('') }
122
122
  end
123
123
 
124
+ context 'when passed top scope variables option' do
125
+ let(:args) do
126
+ [
127
+ '--top-scope-variables=role',
128
+ 'spec/fixtures/test/manifests/top_scope_variables.pp',
129
+ ]
130
+ end
131
+
132
+ its(:exitstatus) { is_expected.to eq(0) }
133
+ its(:stdout) { is_expected.to eq('') }
134
+ end
135
+
124
136
  context 'when limited to errors only' do
125
137
  let(:args) do
126
138
  [
@@ -196,6 +196,55 @@ describe PuppetLint::Checks do
196
196
  end
197
197
  end
198
198
 
199
+ describe '#run_yaml' do
200
+ let(:fileinfo) { File.join('path', 'to', 'test.yaml') }
201
+ let(:data) do
202
+ <<~EOS
203
+ ---
204
+ version: 5
205
+ defaults:
206
+ datadir: data
207
+ data_hash: yaml_data
208
+ hierarchy:
209
+ - name: "LEGACY FACT PATH"
210
+ paths:
211
+ - "os/%{facts.hostname}.yaml"
212
+ - name: "osfamily/major release"
213
+ paths:
214
+ - "os/%{facts.os.name}/%{facts.os.release.major}.yaml"
215
+ - name: 'common'
216
+ path: 'common.yaml'
217
+ EOS
218
+ end
219
+ let(:enabled_checks) { [:legacy_facts] }
220
+
221
+ before(:each) do
222
+ allow(instance).to receive(:enabled_checks).and_return(enabled_checks) # rubocop: disable RSpec/SubjectStub
223
+ allow(File).to receive(:extname).with(fileinfo).and_return('.yaml')
224
+ end
225
+
226
+ context 'when there are checks enabled' do
227
+ let(:enabled_checks) { [:legacy_facts] }
228
+ let(:enabled_check_classes) { enabled_checks.map { |r| PuppetLint.configuration.check_object[r] } }
229
+ let(:disabled_checks) { PuppetLint.configuration.checks - enabled_checks }
230
+ let(:disabled_check_classes) { disabled_checks.map { |r| PuppetLint.configuration.check_object[r] } }
231
+
232
+ it 'runs the enabled checks' do
233
+ expect(enabled_check_classes).to all(receive(:new).and_call_original)
234
+
235
+ instance.run(fileinfo, data)
236
+ end
237
+
238
+ it 'does not run the disabled checks' do
239
+ disabled_check_classes.each do |check_class|
240
+ expect(check_class).not_to receive(:new)
241
+ end
242
+
243
+ instance.run(fileinfo, data)
244
+ end
245
+ end
246
+ end
247
+
199
248
  describe '#enabled_checks' do
200
249
  subject(:enabled_checks) { instance.enabled_checks }
201
250
 
@@ -256,7 +256,7 @@ describe PuppetLint::Lexer do
256
256
  end
257
257
 
258
258
  context 'treats a variable named the same as the keyword as a variable' do
259
- PuppetLint::Lexer::KEYWORDS.each_key do |keyword|
259
+ described_class::KEYWORDS.each_key do |keyword|
260
260
  context "for '#{keyword}'" do
261
261
  let(:segments) do
262
262
  [
@@ -1359,6 +1359,14 @@ END
1359
1359
  end
1360
1360
 
1361
1361
  context ':TYPE' do
1362
+ shared_examples 'a type matcher' do |type|
1363
+ it "matches #{type}" do
1364
+ token = lexer.tokenise(type).first
1365
+ expect(token.type).to eq(:TYPE)
1366
+ expect(token.value).to eq(type)
1367
+ end
1368
+ end
1369
+
1362
1370
  it 'matches Data Types' do
1363
1371
  token = lexer.tokenise('Integer').first
1364
1372
  expect(token.type).to eq(:TYPE)
@@ -1378,18 +1386,18 @@ END
1378
1386
  end
1379
1387
 
1380
1388
  describe 'Platform Types' do
1381
- it 'matches Callable' do
1382
- token = lexer.tokenise('Callable').first
1383
- expect(token.type).to eq(:TYPE)
1384
- expect(token.value).to eq('Callable')
1385
- end
1386
-
1387
- it 'matches Sensitive' do
1388
- token = lexer.tokenise('Sensitive').first
1389
- expect(token.type).to eq(:TYPE)
1390
- expect(token.value).to eq('Sensitive')
1391
- end
1392
- end
1389
+ it_behaves_like 'a type matcher', 'Callable'
1390
+ it_behaves_like 'a type matcher', 'Sensitive'
1391
+ end
1392
+
1393
+ it_behaves_like 'a type matcher', 'Error'
1394
+ it_behaves_like 'a type matcher', 'Binary'
1395
+ it_behaves_like 'a type matcher', 'Iterable'
1396
+ it_behaves_like 'a type matcher', 'Iterator'
1397
+ it_behaves_like 'a type matcher', 'RichData'
1398
+ it_behaves_like 'a type matcher', 'ScalarData'
1399
+ it_behaves_like 'a type matcher', 'SemVer'
1400
+ it_behaves_like 'a type matcher', 'SemVerRange'
1393
1401
  end
1394
1402
 
1395
1403
  context ':HEREDOC without interpolation' do
@@ -0,0 +1,126 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'space_before_arrow' do
4
+ let(:msg) { "there should be a single space before '=>' on line %d, column %d" }
5
+
6
+ context 'with code that should not trigger any warnings' do
7
+ context 'resource with multiple parameters on different lines' do
8
+ let(:code) do
9
+ <<-END
10
+ file { 'foo':
11
+ foo => bar,
12
+ bar => buzz,
13
+ }
14
+ END
15
+ end
16
+
17
+ it 'does not detect any problems' do
18
+ expect(problems).to be_empty
19
+ end
20
+ end
21
+
22
+ context 'resource with single param and normal spacing' do
23
+ let(:code) do
24
+ <<-END
25
+ file { 'foo':
26
+ foo => bar,
27
+ }
28
+ END
29
+ end
30
+
31
+ it 'does not detect any problems' do
32
+ expect(problems).to be_empty
33
+ end
34
+ end
35
+
36
+ context 'resource with multiple params and normal spacing' do
37
+ let(:code) do
38
+ <<-END
39
+ file { 'foo':
40
+ foo => { "bar" => "baz" },
41
+ }
42
+ END
43
+ end
44
+
45
+ it 'does not detect any problems' do
46
+ expect(problems).to be_empty
47
+ end
48
+ end
49
+ end
50
+
51
+ context 'resource with a single param and simple value with too much space before arrow' do
52
+ let(:code) do
53
+ <<-END
54
+ file { 'foo':
55
+ foo => bar,
56
+ }
57
+ END
58
+ end
59
+
60
+ context 'with fix disabled' do
61
+ it 'detects extra space before arrow' do
62
+ expect(problems.size).to eq(1)
63
+ end
64
+
65
+ it 'produces 1 warning' do
66
+ expect(problems).to contain_warning(msg % [2, 14]).on_line(2).in_column(14)
67
+ end
68
+ end
69
+
70
+ context 'with fix enabled' do
71
+ before(:each) do
72
+ PuppetLint.configuration.fix = true
73
+ end
74
+
75
+ after(:each) do
76
+ PuppetLint.configuration.fix = false
77
+ end
78
+
79
+ it 'detects the problem' do
80
+ expect(problems.size).to eq(1)
81
+ end
82
+
83
+ it 'fixes the manifest' do
84
+ expect(problems).to contain_fixed(msg % [2, 14])
85
+ end
86
+ end
87
+ end
88
+
89
+ context 'resource with a single param, a hash as value and bad spacing within the hash' do
90
+ let(:code) do
91
+ <<-END
92
+ file { 'foo':
93
+ foo => { "bar" => "baz" },
94
+ }
95
+ END
96
+ end
97
+
98
+ context 'with fix disabled' do
99
+ it 'detects extra space before arrow' do
100
+ expect(problems.size).to eq(1)
101
+ end
102
+
103
+ it 'produces a warning' do
104
+ expect(problems).to contain_warning(msg % [2, 25]).on_line(2).in_column(25)
105
+ end
106
+ end
107
+
108
+ context 'with fix enabled' do
109
+ before(:each) do
110
+ PuppetLint.configuration.fix = true
111
+ end
112
+
113
+ after(:each) do
114
+ PuppetLint.configuration.fix = false
115
+ end
116
+
117
+ it 'detects the problem' do
118
+ expect(problems.size).to eq(1)
119
+ end
120
+
121
+ it 'fixes the manifest' do
122
+ expect(problems).to contain_fixed(msg % [2, 25])
123
+ end
124
+ end
125
+ end
126
+ end
@@ -129,6 +129,65 @@ describe 'legacy_facts' do
129
129
  expect(problems.size).to eq(1)
130
130
  end
131
131
  end
132
+
133
+ context 'YAML file processing' do
134
+ before(:each) do
135
+ allow(File).to receive(:extname).and_return('.yaml')
136
+ end
137
+
138
+ context 'with YAML string containing legacy fact' do
139
+ let(:code) { 'some_key: "%{::osfamily}"' }
140
+
141
+ it 'detects a single problem' do
142
+ expect(problems.size).to eq(1)
143
+ end
144
+ end
145
+
146
+ context 'with YAML string not containing legacy fact' do
147
+ let(:code) { 'some_key: "%{facts.os.name}"' }
148
+
149
+ it 'does not detect any problems' do
150
+ expect(problems).to be_empty
151
+ end
152
+ end
153
+
154
+ context 'with YAML nested structure containing legacy fact' do
155
+ let(:code) { "nested:\n value: \"%{::architecture}\"" }
156
+
157
+ it 'detects a single problem' do
158
+ expect(problems.size).to eq(1)
159
+ end
160
+ end
161
+
162
+ context 'with YAML array containing legacy facts' do
163
+ let(:code) do
164
+ [
165
+ 'array:',
166
+ ' - "%{::processor0}"',
167
+ ' - "%{::ipaddress_eth0}"',
168
+ ].join("\n")
169
+ end
170
+
171
+ it 'detects multiple problems' do
172
+ expect(problems.size).to eq(2)
173
+ end
174
+ end
175
+
176
+ context 'with YAML alias containing legacy fact' do
177
+ let(:code) do
178
+ [
179
+ 'template: &template',
180
+ ' fact: "%{::osfamily}"',
181
+ 'instance:',
182
+ ' <<: *template',
183
+ ].join("\n")
184
+ end
185
+
186
+ it 'detects multiple instances' do
187
+ expect(problems.size).to eq(2)
188
+ end
189
+ end
190
+ end
132
191
  end
133
192
 
134
193
  context 'with fix enabled' do
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppet-lint
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.4
4
+ version: 5.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Sharpe
8
8
  - Puppet, Inc.
9
9
  - Community Contributors
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2024-02-09 00:00:00.000000000 Z
13
+ date: 2025-09-23 00:00:00.000000000 Z
14
14
  dependencies: []
15
15
  description: " Checks your Puppet manifests against the Puppetlabs style guide
16
16
  and alerts you to any discrepancies.\n"
@@ -34,7 +34,6 @@ files:
34
34
  - lib/puppet-lint/lexer.rb
35
35
  - lib/puppet-lint/lexer/string_slurper.rb
36
36
  - lib/puppet-lint/lexer/token.rb
37
- - lib/puppet-lint/monkeypatches.rb
38
37
  - lib/puppet-lint/optparser.rb
39
38
  - lib/puppet-lint/plugins.rb
40
39
  - lib/puppet-lint/plugins/check_classes/arrow_on_right_operand_line.rb
@@ -74,6 +73,7 @@ files:
74
73
  - lib/puppet-lint/plugins/check_whitespace/arrow_alignment.rb
75
74
  - lib/puppet-lint/plugins/check_whitespace/hard_tabs.rb
76
75
  - lib/puppet-lint/plugins/check_whitespace/line_length.rb
76
+ - lib/puppet-lint/plugins/check_whitespace/space_before_arrow.rb
77
77
  - lib/puppet-lint/plugins/check_whitespace/trailing_whitespace.rb
78
78
  - lib/puppet-lint/plugins/legacy_facts/legacy_facts.rb
79
79
  - lib/puppet-lint/plugins/top_scope_facts/top_scope_facts.rb
@@ -94,6 +94,8 @@ files:
94
94
  - spec/fixtures/test/manifests/init.pp
95
95
  - spec/fixtures/test/manifests/malformed.pp
96
96
  - spec/fixtures/test/manifests/mismatched_control_comment.pp
97
+ - spec/fixtures/test/manifests/parseable.yaml
98
+ - spec/fixtures/test/manifests/top_scope_variables.pp
97
99
  - spec/fixtures/test/manifests/two_warnings.pp
98
100
  - spec/fixtures/test/manifests/unterminated_control_comment.pp
99
101
  - spec/fixtures/test/manifests/url_interpolation.pp
@@ -146,6 +148,7 @@ files:
146
148
  - spec/unit/puppet-lint/plugins/check_whitespace/80chars_spec.rb
147
149
  - spec/unit/puppet-lint/plugins/check_whitespace/arrow_alignment_spec.rb
148
150
  - spec/unit/puppet-lint/plugins/check_whitespace/hard_tabs_spec.rb
151
+ - spec/unit/puppet-lint/plugins/check_whitespace/space_before_arrow_spec.rb
149
152
  - spec/unit/puppet-lint/plugins/check_whitespace/trailing_whitespace_spec.rb
150
153
  - spec/unit/puppet-lint/plugins/legacy_facts/legacy_facts_spec.rb
151
154
  - spec/unit/puppet-lint/plugins/top_scope_facts/top_scope_facts_spec.rb
@@ -154,7 +157,7 @@ homepage: https://github.com/puppetlabs/puppet-lint/
154
157
  licenses:
155
158
  - MIT
156
159
  metadata: {}
157
- post_install_message:
160
+ post_install_message:
158
161
  rdoc_options: []
159
162
  require_paths:
160
163
  - lib
@@ -162,15 +165,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
162
165
  requirements:
163
166
  - - ">="
164
167
  - !ruby/object:Gem::Version
165
- version: '2.7'
168
+ version: '3.1'
166
169
  required_rubygems_version: !ruby/object:Gem::Requirement
167
170
  requirements:
168
171
  - - ">="
169
172
  - !ruby/object:Gem::Version
170
173
  version: '0'
171
174
  requirements: []
172
- rubygems_version: 3.1.6
173
- signing_key:
175
+ rubygems_version: 3.3.27
176
+ signing_key:
174
177
  specification_version: 4
175
178
  summary: Ensure your Puppet manifests conform with the Puppetlabs style guide
176
179
  test_files: []
@@ -1,51 +0,0 @@
1
- begin
2
- '%{test}' % { test: 'replaced' } == 'replaced'
3
- rescue StandardError
4
- # monkeypatch String#% into Ruby 1.8.7
5
- class String
6
- Percent = instance_method(:%) unless defined?(Percent)
7
-
8
- def %(*a, &b)
9
- a.flatten!
10
-
11
- string = case a.last
12
- when Hash
13
- expand(a.pop)
14
- else
15
- self
16
- end
17
-
18
- if a.empty?
19
- string
20
- else
21
- Percent.bind_call(string, a, &b)
22
- end
23
- end
24
-
25
- def expand!(vars = {})
26
- loop do
27
- changed = false
28
- vars.each do |var, value|
29
- var = var.to_s
30
- var.gsub!(%r{[^a-zA-Z0-9_]}, '')
31
- changed = gsub!(%r{%\{#{var}\}}, value.to_s)
32
- end
33
- break unless changed
34
- end
35
- self
36
- end
37
-
38
- def expand(opts = {})
39
- dup.expand!(opts)
40
- end
41
- end
42
- end
43
-
44
- unless String.respond_to?(:prepend)
45
- # monkeypatch String#prepend into Ruby 1.8.7
46
- class String
47
- def prepend(lead)
48
- replace("#{lead}#{self}")
49
- end
50
- end
51
- end