puppet-check 2.1.0 → 2.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7f43468b01f25dba9ea6e997c982c632fb884d6cb8ec8faa31a670ac523a8ee8
4
- data.tar.gz: 5baeb571e293b32c6120448d45c036523b96ffc0a325ba02c6f47e70323c1f3f
3
+ metadata.gz: 384ccc25e3b1a78f69e3d017131eaf9583dc24d73f1f9ef1c37c21ac706715b9
4
+ data.tar.gz: 5094bc3014d74b9a5707fade543d1535d4ef12684d5adaddbb4ceb26c5a4a37e
5
5
  SHA512:
6
- metadata.gz: f0ff327a57b78229800e56dae894b863ff0217d9ae8f368d7ff9db048160b29c0b1365144fb4db596c3951fafa4b5569d0c5ad741e7d0b3fa1a3491ed64fed50
7
- data.tar.gz: 7b15f95dc0876eb1d8a6b9fa5295c45858fc7eef02ba6a2924c68ad29dadf6fa179c62ed32eae167923f39172eb9b1464910f48e7072dfaf8e9d98328dd6739e
6
+ metadata.gz: 1bfc1834fc8dc70defbfce8d330f724b834653e5ba48d4a546ff3d93b3bf94d5ac68850d84dcca6d0ac1bad1990d30c07f58c5bfbaaaf2b123fa352c408200f8
7
+ data.tar.gz: e65f3c64cbc715d2e49c15378165256c17c01e440c979557908bd8a64fa5b77fdf74f8dac5fadbafec6df9b106bf05adb23443b552334759812850e3d89a872f
data/CHANGELOG.md CHANGED
@@ -1,4 +1,15 @@
1
- ### 2.1.0 (Roadmap)
1
+ ### 2.2.1
2
+ - Improved output formatting for all formats.
3
+ - Update and improve Rake task interfacing.
4
+
5
+ ### 2.2.0
6
+ - Add Enable Pending Cops to base RuboCop configuration.
7
+ - Support checking plans.
8
+ - Fix Puppet >= 6.5 error message capture when line/col info.
9
+ - Minimum Ruby version increased to 2.6.
10
+ - Minimum Puppet version increased to 5.4.
11
+
12
+ ### 2.1.0
2
13
  - Minimum supported version of Puppet bumped to 5.0.
3
14
  - Minimum Ruby version bumped to 2.4.
4
15
  - Official support for Puppet 7, Rubocop 1, and Reek 6.
data/README.md CHANGED
@@ -173,18 +173,24 @@ rake puppetcheck:kitchen:* # Execute Test Kitchen acceptance tests
173
173
  You can add style, smoke, and regression checks to the `rake puppetcheck:file`, or change the output format, by adding the following after the require:
174
174
 
175
175
  ```ruby
176
- # example of modifying Puppet Check behavior
177
- PuppetCheck.settings[:style] = true
178
- PuppetCheck.settings[:fail_on_warnings] = true
179
- PuppetCheck.settings[:smoke] = true
180
- PuppetCheck.settings[:regression] = true # in progress, do not use
181
- PuppetCheck.settings[:public] = 'public.pem'
182
- PuppetCheck.settings[:private] = 'private.pem'
183
- PuppetCheck.settings[:output_format] = 'yaml'
184
- PuppetCheck.settings[:octoconfig] = '.octocatalog-diff.cfg.rb'
185
- PuppetCheck.settings[:octonodes] = %w(localhost.localdomain)
186
- PuppetCheck.settings[:puppetlint_args] = ['--puppetlint-arg-one', '--puppetlint-arg-two']
187
- PuppetCheck.settings[:rubocop_args] = ['--except', 'rubocop-arg-one,rubocop-arg-two']
176
+ # example of modifying Puppet Check behavior and creating a custom task
177
+ settings = {}
178
+ settings[:fail_on_warnings] = true # default false
179
+ settings[:style] = true # default false
180
+ settings[:smoke] = true # default false
181
+ settings[:regression] = true # in progress, do not use; default false
182
+ settings[:public] = 'public.pem' # default nil
183
+ settings[:private] = 'private.pem' # default nil
184
+ settings[:output_format] = 'yaml' # also 'json'; default 'text'
185
+ settings[:octoconfig] = '$HOME/octocatalog-diff.cfg.rb' # default '.octocatalog-diff.cfg.rb'
186
+ settings[:octonodes] = %w(server.example.com) # default: %w(localhost.localdomain)
187
+ settings[:puppetlint_args] = ['--puppetlint-arg-one', '--puppetlint-arg-two'] # default []
188
+ settings[:rubocop_args] = ['--except', 'rubocop-arg-one,rubocop-arg-two'] # default []
189
+
190
+ desc 'Execute custom Puppet-Check file checks'
191
+ task :file_custom do
192
+ Rake::Task[:'puppetcheck:file'].invoke(settings)
193
+ end
188
194
  ```
189
195
 
190
196
  Please note that `rspec` does not support yaml output and therefore would still use the default 'progress' formatter even if `yaml` is specified as the format option to Puppet Check.
@@ -276,33 +282,12 @@ PuppetCheck.new.run(settings, [dirs, files])
276
282
  require 'puppet-check/rspec_puppet_support'
277
283
 
278
284
  RSpecPuppetSupport.run
279
- task.pattern = Dir.glob('**/{classes,defines,facter,functions,hosts,puppet,unit,types}/**/*_spec.rb').reject { |dir| dir =~ /fixtures/ }
285
+ task.pattern = Dir.glob('**/{classes,defines,facter,functions,hosts,puppet,unit,types}/**/*_spec.rb').grep_v(/fixtures/)
280
286
  ```
281
287
 
282
288
  ### Docker
283
289
 
284
- You can also use Puppet Check inside of Docker for quick, portable, and disposable testing. Below is an example `Dockerfile` for this purpose:
285
-
286
- ```dockerfile
287
- # a reliable and small container; today should maybe use ruby:slim instead
288
- FROM ubuntu:20.04
289
- # you need ruby and any other extra dependencies that come from packages; in this example we install git to use it for downloading external module dependencies
290
- RUN apt-get update && apt-get install ruby git -y
291
- # you need puppet-check and any other extra dependencies that come from gems; in this example we install rspec-puppet and rake for extra testing
292
- RUN gem install --no-document puppet-check rspec-puppet rake
293
- # this is needed for the ruby json parser to not flip out on fresh os installs for some reason (change encoding value as necessary)
294
- ENV LANG en_US.UTF-8
295
- # create the directory for your module, directory environment, etc. and change directory into it
296
- WORKDIR /module_name_or_directory_environment_name
297
- # copy the module, directory environment, etc. contents into the corresponding directory inside the container; alternative, bind a volume mount for your module(s) into the container at runtime
298
- COPY / .
299
- # execute your tests; in this example we are executing the full suite of tests
300
- ENTRYPOINT ["rake", "puppetcheck"]
301
- ```
302
-
303
- You can also build your own general container image for testing various Puppet situations by removing the last three lines. You can then test each module, directory environment, etc. on top of that container by merely adding and modifying the final three lines to a `Dockerfile` that uses the container you built from the first four lines. This is recommended usage due to being very efficient and stable.
304
-
305
- As an alternative to copying Puppet code and data into the image for testing, it is also recommended to bind volume mount the container to the directory with your Puppet code and data.
290
+ A supported [Docker image](https://hub.docker.com/r/matthewschuchard/puppet-check) of Puppet-Check is now available from the public Docker Hub registry. Please consult the repository documentation for further usage information.
306
291
 
307
292
  ### Vagrant
308
293
 
@@ -1,4 +1,3 @@
1
- require 'optparse'
2
1
  require_relative '../puppet_check'
3
2
 
4
3
  # the command line interface for PuppetCheck
@@ -7,7 +6,7 @@ class PuppetCheck::CLI
7
6
  def self.run(args)
8
7
  # gather the user arguments
9
8
  settings = parse(args)
10
- raise 'puppet-check: no paths specified; try using --help' if args.empty?
9
+ raise 'puppet-check: no file paths specified; try using --help' if args.empty?
11
10
 
12
11
  # run PuppetCheck with specified paths
13
12
  PuppetCheck.new.run(settings, args)
@@ -15,6 +14,9 @@ class PuppetCheck::CLI
15
14
 
16
15
  # parse the user arguments
17
16
  def self.parse(args)
17
+ private_class_method :method
18
+ require 'optparse'
19
+
18
20
  # show help message if no args specified
19
21
  args = %w[-h] if args.empty?
20
22
 
@@ -27,7 +29,7 @@ class PuppetCheck::CLI
27
29
 
28
30
  # base options
29
31
  opts.on('--version', 'Display the current version.') do
30
- puts 'puppet-check 2.1.0'
32
+ puts 'puppet-check 2.2.1'
31
33
  exit 0
32
34
  end
33
35
 
@@ -58,7 +60,7 @@ class PuppetCheck::CLI
58
60
  opts.on('--rubocop arg_one,arg_two', String, 'Arguments for Rubocop disabled cops') { |arg| settings[:rubocop_args] = ['--except', arg] }
59
61
  end
60
62
 
61
- # remove atched args and return settings
63
+ # remove matched args and return settings
62
64
  opt_parser.parse!(args)
63
65
  settings
64
66
  end
@@ -2,25 +2,23 @@ require_relative '../puppet_check'
2
2
 
3
3
  # executes diagnostics on data files
4
4
  class DataParser
5
+ require 'yaml'
6
+
5
7
  # checks yaml (.yaml/.yml)
6
8
  def self.yaml(files)
7
- require 'yaml'
8
-
9
9
  files.each do |file|
10
10
  # check yaml syntax
11
- begin
12
- parsed = YAML.load_file(file)
13
- rescue StandardError => err
14
- PuppetCheck.settings[:error_files].push("#{file}:\n#{err.to_s.gsub("(#{file}): ", '')}")
15
- else
16
- warnings = []
11
+ parsed = YAML.load_file(file)
12
+ rescue StandardError => err
13
+ PuppetCheck.files[:errors][file] = err.to_s.gsub("(#{file}): ", '').split("\n")
14
+ else
15
+ warnings = []
17
16
 
18
- # perform some rudimentary hiera checks if data exists and is hieradata
19
- warnings = hiera(parsed, file) if parsed && (File.basename(file) != 'hiera.yaml')
17
+ # perform some rudimentary hiera checks if data exists and is hieradata
18
+ warnings = hiera(parsed, file) if parsed && (File.basename(file) != 'hiera.yaml')
20
19
 
21
- next PuppetCheck.settings[:warning_files].push("#{file}:\n#{warnings.join("\n")}") unless warnings.empty?
22
- PuppetCheck.settings[:clean_files].push(file.to_s)
23
- end
20
+ next PuppetCheck.files[:warnings][file] = warnings unless warnings.empty?
21
+ PuppetCheck.files[:clean].push(file.to_s)
24
22
  end
25
23
  end
26
24
 
@@ -30,13 +28,13 @@ class DataParser
30
28
 
31
29
  # keys specified?
32
30
  if public.nil? || private.nil?
33
- PuppetCheck.settings[:ignored_files].concat(files)
31
+ PuppetCheck.files[:ignored].concat(files)
34
32
  return warn 'Public X509 and/or Private RSA PKCS7 certs were not specified. EYAML checks will not be executed.'
35
33
  end
36
34
 
37
35
  # keys exist?
38
36
  unless File.file?(public) && File.file?(private)
39
- PuppetCheck.settings[:ignored_files].concat(files)
37
+ PuppetCheck.files[:ignored].concat(files)
40
38
  return warn 'Specified Public X509 and/or Private RSA PKCS7 certs do not exist. EYAML checks will not be executed.'
41
39
  end
42
40
 
@@ -56,19 +54,25 @@ class DataParser
56
54
  begin
57
55
  parsed = YAML.load_file(decrypted)
58
56
  rescue StandardError => err
59
- PuppetCheck.settings[:error_files].push("#{file}:\n#{err.to_s.gsub("(#{file}): ", '')}")
57
+ PuppetCheck.files[:errors][file] = err.to_s.gsub("(#{file}): ", '').split("\n")
60
58
  else
61
59
  warnings = []
62
60
 
63
61
  # perform some rudimentary hiera checks if data exists and is hieradata
64
62
  warnings = hiera(parsed, file) if parsed
65
63
 
66
- next PuppetCheck.settings[:warning_files].push("#{file}:\n#{warnings.join("\n")}") unless warnings.empty?
67
- PuppetCheck.settings[:clean_files].push(file.to_s)
64
+ next PuppetCheck.files[:warnings][file] = warnings unless warnings.empty?
65
+ PuppetCheck.files[:clean].push(file.to_s)
68
66
  end
69
67
  end
70
68
  end
71
69
 
70
+ # metadata consts
71
+ REQUIRED_KEYS = %w[name version author license summary source dependencies].freeze
72
+ REQ_DEP_KEYS = %w[requirements dependencies].freeze
73
+ DEPRECATED_KEYS = %w[types checksum].freeze
74
+ TASK_INPUTS = %w[environment stdin powershell].freeze
75
+
72
76
  # checks json (.json)
73
77
  def self.json(files)
74
78
  require 'json'
@@ -78,7 +82,7 @@ class DataParser
78
82
  begin
79
83
  parsed = JSON.parse(File.read(file))
80
84
  rescue JSON::ParserError => err
81
- PuppetCheck.settings[:error_files].push("#{file}:\n#{err.to_s.lines.first.strip}")
85
+ PuppetCheck.files[:errors][file] = err.to_s.lines.first.strip.split("\n")
82
86
  else
83
87
  warnings = []
84
88
 
@@ -91,12 +95,12 @@ class DataParser
91
95
  errors = []
92
96
 
93
97
  # check for required keys
94
- %w[name version author license summary source dependencies].each do |key|
98
+ REQUIRED_KEYS.each do |key|
95
99
  errors.push("Required field '#{key}' not found.") unless parsed.key?(key)
96
100
  end
97
101
 
98
102
  # check requirements and dependencies keys
99
- %w[requirements dependencies].each do |key|
103
+ REQ_DEP_KEYS.each do |key|
100
104
  # skip if key is missing or value is an empty string, array, or hash
101
105
  next if !parsed.key?(key) || parsed[key].empty?
102
106
 
@@ -125,14 +129,14 @@ class DataParser
125
129
  end
126
130
 
127
131
  # check for deprecated fields
128
- %w[types checksum].each do |key|
132
+ DEPRECATED_KEYS.each do |key|
129
133
  errors.push("Deprecated field '#{key}' found.") if parsed.key?(key)
130
134
  end
131
135
 
132
136
  # check for summary under 144 character
133
137
  errors.push('Summary exceeds 144 characters.') if parsed.key?('summary') && parsed['summary'].size > 144
134
138
 
135
- next PuppetCheck.settings[:error_files].push("#{file}:\n#{errors.join("\n")}") unless errors.empty?
139
+ next PuppetCheck.files[:errors][file] = errors unless errors.empty?
136
140
 
137
141
  # check for warnings
138
142
  # check for operatingsystem_support hash array
@@ -171,7 +175,7 @@ class DataParser
171
175
  # check that input_method is one of three possible values
172
176
  if parsed.key?('input_method')
173
177
  if parsed['input_method'].is_a?(String)
174
- warnings.push('input_method value is not one of environment, stdin, or powershell') unless %w[environment stdin powershell].include?(parsed['input_method'])
178
+ warnings.push('input_method value is not one of environment, stdin, or powershell') unless TASK_INPUTS.include?(parsed['input_method'])
175
179
  else
176
180
  warnings.push('input_method value is not a String')
177
181
  end
@@ -193,8 +197,8 @@ class DataParser
193
197
  # perform some rudimentary hiera checks if data exists
194
198
  warnings = hiera(parsed, file)
195
199
  end
196
- next PuppetCheck.settings[:warning_files].push("#{file}:\n#{warnings.join("\n")}") unless warnings.empty?
197
- PuppetCheck.settings[:clean_files].push(file.to_s)
200
+ next PuppetCheck.files[:warnings][file] = warnings unless warnings.empty?
201
+ PuppetCheck.files[:clean].push(file.to_s)
198
202
  end
199
203
  end
200
204
  end
@@ -2,44 +2,50 @@ require_relative '../puppet_check'
2
2
 
3
3
  # class to handle outputting diagnostic results in desired format
4
4
  class OutputResults
5
- # output the results as text
6
- def self.text
7
- unless PuppetCheck.settings[:error_files].empty?
8
- print "\033[31mThe following files have errors:\033[0m\n-- "
9
- puts PuppetCheck.settings[:error_files].join("\n\n-- ")
10
- end
11
- unless PuppetCheck.settings[:warning_files].empty?
12
- print "\n\033[33mThe following files have warnings:\033[0m\n-- "
13
- puts PuppetCheck.settings[:warning_files].join("\n\n-- ")
14
- end
15
- unless PuppetCheck.settings[:clean_files].empty?
16
- print "\n\033[32mThe following files have no errors or warnings:\033[0m\n-- "
17
- puts PuppetCheck.settings[:clean_files].join("\n-- ")
18
- end
19
- return if PuppetCheck.settings[:ignored_files].empty?
20
- print "\n\033[36mThe following files have unrecognized formats and therefore were not processed:\033[0m\n-- "
21
- puts PuppetCheck.settings[:ignored_files].join("\n-- ")
22
- end
5
+ # output the results in various formats
6
+ def self.run(files, format)
7
+ # remove empty entries
8
+ files.delete_if { |_, sorted_files| sorted_files.empty? }
23
9
 
24
- # output the results as yaml or json
25
- def self.markup(format)
26
- # generate output hash
27
- hash = {}
28
- hash['errors'] = PuppetCheck.settings[:error_files] unless PuppetCheck.settings[:error_files].empty?
29
- hash['warnings'] = PuppetCheck.settings[:warning_files] unless PuppetCheck.settings[:warning_files].empty?
30
- hash['clean'] = PuppetCheck.settings[:clean_files] unless PuppetCheck.settings[:clean_files].empty?
31
- hash['ignored'] = PuppetCheck.settings[:ignored_files] unless PuppetCheck.settings[:ignored_files].empty?
32
-
33
- # convert hash to markup language
10
+ # output hash according to specified format
34
11
  case format
12
+ when 'text'
13
+ text(files)
35
14
  when 'yaml'
36
15
  require 'yaml'
37
- puts Psych.dump(hash, indentation: 2)
16
+ # maintain filename format consistency among output formats
17
+ files.transform_keys!(&:to_s)
18
+ puts Psych.dump(files, indentation: 2)
38
19
  when 'json'
39
20
  require 'json'
40
- puts JSON.pretty_generate(hash)
21
+ puts JSON.pretty_generate(files)
41
22
  else
42
23
  raise "puppet-check: Unsupported output format '#{format}' was specified."
43
24
  end
44
25
  end
26
+
27
+ # output the results as text
28
+ def self.text(files)
29
+ private_class_method :method
30
+
31
+ # errors
32
+ if files.key?(:errors)
33
+ puts "\033[31mThe following files have errors:\033[0m"
34
+ files[:errors].each { |file, errors| puts "-- #{file}:\n#{errors.join("\n")}" }
35
+ end
36
+ # warnings
37
+ if files.key?(:warnings)
38
+ puts "\n\033[33mThe following files have warnings:\033[0m"
39
+ files[:warnings].each { |file, warnings| puts "-- #{file}:\n#{warnings.join("\n")}" }
40
+ end
41
+ # cleans
42
+ if files.key?(:clean)
43
+ print "\n\033[32mThe following files have no errors or warnings:\033[0m\n-- "
44
+ puts files[:clean].join("\n-- ")
45
+ end
46
+ # ignores
47
+ return unless files.key?(:ignored)
48
+ print "\n\033[36mThe following files have unrecognized formats and therefore were not processed:\033[0m\n-- "
49
+ puts files[:ignored].join("\n-- ")
50
+ end
45
51
  end
@@ -18,41 +18,33 @@ class PuppetParser
18
18
  # check puppet syntax
19
19
  begin
20
20
  # initialize message
21
- message = ''
21
+ messages = []
22
+ # specify tasks attribute for parser validation if this looks like a plan or not
23
+ Puppet[:tasks] = file.match?(%r{plans/\w+\.pp$})
22
24
  # in puppet >= 6.5 the return of this method is a hash with the error
23
25
  new_error = Puppet::Face[:parser, :current].validate(file)
24
26
  # puppet 6.5 output format is now a hash from the face api
25
27
  if Gem::Version.new(Puppet::PUPPETVERSION) >= Gem::Version.new('6.5.0') && new_error != {}
26
- message = new_error.values.map(&:to_s).join("\n").gsub(/ \(file: #{File.absolute_path(file)}(, |\))/, '').gsub(/Could not parse.*: /, '')
28
+ messages.concat(new_error.values.map(&:to_s).map { |error| error.gsub(/ \(file: #{File.absolute_path(file)}(, |\))/, '') }.map { |error| error.gsub('Could not parse for environment *root*: ', '') })
27
29
  end
28
30
  # this is the actual error that we need to rescue Puppet::Face from
29
31
  rescue SystemExit
30
32
  # puppet 5.4-6.4 has a new validator output format and eof errors have fake dir env info
31
- message = if Gem::Version.new(Puppet::PUPPETVERSION) >= Gem::Version.new('5.4') && Gem::Version.new(Puppet::PUPPETVERSION) < Gem::Version.new('6.5')
32
- errors.map(&:to_s).join("\n").gsub(/file: #{File.absolute_path(file)}(, |\))/, '').gsub(/Could not parse.*: /, '')
33
- # puppet 5.0-5.2 can only do one error per line and outputs fake dir env info
34
- elsif Gem::Version.new(Puppet::PUPPETVERSION) >= Gem::Version.new('5.0') && Gem::Version.new(Puppet::PUPPETVERSION) < Gem::Version.new('5.3')
35
- errors.map(&:to_s).join("\n").gsub("#{File.absolute_path(file)}:", '').gsub(/Could not parse.*: /, '')
36
- # puppet < 5 and 5.3 parser output style
37
- else
38
- errors.map(&:to_s).join("\n").gsub("#{File.absolute_path(file)}:", '')
39
- end
33
+ messages.concat(errors.map(&:to_s).join("\n").map { |error| error.gsub(/file: #{File.absolute_path(file)}(, |\))/, '') }.map { |error| error.gsub(/Could not parse.*: /, '') })
40
34
  end
41
- # output message
42
- next PuppetCheck.settings[:error_files].push("#{file}:\n#{message}") unless message.empty?
35
+
36
+ Puppet::Util::Log.close_all
37
+
38
+ # store info and continue validating files
39
+ next PuppetCheck.files[:errors][file] = messages unless messages.empty?
43
40
 
44
41
  # initialize warnings with output from the parser if it exists, since the output is warnings if Puppet::Face did not trigger a SystemExit
45
- warnings = "#{file}:"
42
+ warnings = []
43
+ # weirdly puppet >= 6.5 still does not return warnings and logs them instead unlike errors
46
44
  unless errors.empty?
47
- # puppet 5.4-5.x has a new validator output format
48
- warnings << if Gem::Version.new(Puppet::PUPPETVERSION) >= Gem::Version.new('5.4')
49
- "\n#{errors.map(&:to_s).join("\n").gsub("file: #{File.absolute_path(file)}, ", '')}"
50
- # puppet <= 5.3 validator output format
51
- else
52
- "\n#{errors.map(&:to_s).join("\n").gsub("#{File.absolute_path(file)}:", '')}"
53
- end
45
+ # puppet >= 5.4 has a new validator output format
46
+ warnings.concat(errors.map(&:to_s).join("\n").gsub("file: #{File.absolute_path(file)}, ", '').split("\n"))
54
47
  end
55
- Puppet::Util::Log.close_all
56
48
 
57
49
  # check puppet style
58
50
  if style
@@ -72,12 +64,11 @@ class PuppetParser
72
64
  puppet_lint.run
73
65
 
74
66
  # collect the warnings
75
- if puppet_lint.warnings?
76
- puppet_lint.problems.each { |values| warnings << "\n#{values[:line]}:#{values[:column]}: #{values[:message]}" }
77
- end
67
+ offenses = puppet_lint.problems.map { |problem| "#{problem[:line]}:#{problem[:column]} #{problem[:message]}" }
68
+ warnings.concat(offenses)
78
69
  end
79
- next PuppetCheck.settings[:warning_files].push(warnings) unless warnings == "#{file}:"
80
- PuppetCheck.settings[:clean_files].push(file.to_s)
70
+ next PuppetCheck.files[:warnings][file] = warnings unless warnings.empty?
71
+ PuppetCheck.files[:clean].push(file.to_s)
81
72
  end
82
73
  end
83
74
 
@@ -87,14 +78,12 @@ class PuppetParser
87
78
 
88
79
  files.each do |file|
89
80
  # check puppet template syntax
90
- begin
91
- # credits to gds-operations/puppet-syntax for the parser function call
92
- Puppet::Pops::Parser::EvaluatingParser::EvaluatingEppParser.new.parse_file(file)
93
- rescue StandardError => err
94
- PuppetCheck.settings[:error_files].push("#{file}:\n#{err.to_s.gsub("#{file}:", '')}")
95
- else
96
- PuppetCheck.settings[:clean_files].push(file.to_s)
97
- end
81
+ # credits to gds-operations/puppet-syntax for the parser function call
82
+ Puppet::Pops::Parser::EvaluatingParser::EvaluatingEppParser.new.parse_file(file)
83
+ rescue StandardError => err
84
+ PuppetCheck.files[:errors][file] = [err.to_s.gsub("file: #{file}, ", '')]
85
+ else
86
+ PuppetCheck.files[:clean].push(file.to_s)
98
87
  end
99
88
  end
100
89
  end
@@ -9,7 +9,7 @@ class RSpecPuppetSupport
9
9
  # prepare the spec fixtures directory for rspec-puppet testing
10
10
  def self.run
11
11
  # ensure this method does not do anything inside module dependencies
12
- specdirs = Dir.glob('**/spec').reject { |dir| dir =~ /fixtures/ }
12
+ specdirs = Dir.glob('**/spec').reject { |dir| dir.include?('fixtures') }
13
13
  return if specdirs.empty?
14
14
 
15
15
  # setup fixtures for rspec-puppet testing
@@ -7,31 +7,34 @@ class RubyParser
7
7
  def self.ruby(files, style, rc_args)
8
8
  files.each do |file|
9
9
  # check ruby syntax
10
- begin
11
- # prevents ruby code from actually executing
12
- catch(:good) { instance_eval("BEGIN {throw :good}; #{File.read(file)}", file) }
13
- rescue ScriptError, StandardError => err
14
- PuppetCheck.settings[:error_files].push("#{file}:\n#{err}")
15
- else
16
- # check ruby style
17
- if style
18
- require 'rubocop'
10
+ # prevents ruby code from actually executing
11
+ catch(:good) { instance_eval("BEGIN {throw :good}; #{File.read(file)}", file) }
12
+ rescue ScriptError, StandardError => err
13
+ PuppetCheck.files[:errors][file] = err.to_s.gsub("#{file}:", '').split("\n")
14
+ else
15
+ # check ruby style
16
+ if style
17
+ # check RuboCop and parse warnings' JSON output
18
+ require 'json'
19
+ require 'rubocop'
20
+
21
+ rubocop_warnings = Utils.capture_stdout { RuboCop::CLI.new.run(rc_args + ['--enable-pending-cops', '--require', 'rubocop-performance', '--format', 'json', file]) }
22
+ rubocop_offenses = JSON.parse(rubocop_warnings)['files'][0]['offenses'].map { |warning| "#{warning['location']['line']}:#{warning['location']['column']} #{warning['message']}" }
23
+
24
+ # check Reek and parse warnings' JSON output
25
+ require 'reek'
26
+ require 'reek/cli/application'
19
27
 
20
- # check RuboCop and collect warnings
21
- rubocop_warnings = Utils.capture_stdout { RuboCop::CLI.new.run(rc_args + ['--require', 'rubocop-performance', '--format', 'emacs', file]) }
22
- warnings = rubocop_warnings == '' ? '' : rubocop_warnings.split("#{File.absolute_path(file)}:").join('')
28
+ reek_warnings = Utils.capture_stdout { Reek::CLI::Application.new(['-f', 'json', file]).execute }
29
+ reek_offenses = JSON.parse(reek_warnings).map { |warning| "#{warning['lines'].join(',')}: #{warning['context']} #{warning['message']}" }
23
30
 
24
- # check Reek and collect warnings
25
- require 'reek'
26
- require 'reek/cli/application'
27
- reek_warnings = Utils.capture_stdout { Reek::CLI::Application.new([file]).execute }
28
- warnings << reek_warnings.split("\n")[1..-1].map(&:strip).join("\n") unless reek_warnings == ''
31
+ # assign warnings from combined offenses
32
+ warnings = rubocop_offenses + reek_offenses
29
33
 
30
- # return warnings
31
- next PuppetCheck.settings[:warning_files].push("#{file}:\n#{warnings.strip}") unless warnings == ''
32
- end
33
- PuppetCheck.settings[:clean_files].push(file.to_s)
34
+ # return warnings
35
+ next PuppetCheck.files[:warnings][file] = warnings unless warnings.empty?
34
36
  end
37
+ PuppetCheck.files[:clean].push(file.to_s)
35
38
  end
36
39
  end
37
40
 
@@ -44,22 +47,17 @@ class RubyParser
44
47
  begin
45
48
  # need to eventually have this associated with a different binding during each iteration
46
49
  # older usage throws extra warning and mixes with valid warnings confusingly
47
- warnings = if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.6')
48
- Utils.capture_stderr { ERB.new(File.read(file), trim_mode: '-').result }
49
- # ERB.new(File.read(file), trim_mode: '-').result(RubyParser.new.bind)
50
- # and for extra fun it is incompatible with non-new ruby
51
- else
52
- Utils.capture_stderr { ERB.new(File.read(file), nil, '-').result }
53
- end
50
+ warnings = Utils.capture_stderr { ERB.new(File.read(file), trim_mode: '-').result }
51
+ # warnings = ERB.new(File.read(file), trim_mode: '-').result(RubyParser.new.bind)
54
52
  rescue NameError, TypeError
55
53
  # empty out warnings since it would contain an error if this pass triggers
56
54
  warnings = ''
57
55
  rescue ScriptError => err
58
- next PuppetCheck.settings[:error_files].push("#{file}:\n#{err}")
56
+ next PuppetCheck.files[:errors][file] = err.to_s.gsub('(erb):', '').split("\n")
59
57
  end
60
58
  # return warnings from the check if there were any
61
- next PuppetCheck.settings[:warning_files].push("#{file}:\n#{warnings.gsub('warning: ', '').split('(erb):').join('').strip}") unless warnings == ''
62
- PuppetCheck.settings[:clean_files].push(file.to_s)
59
+ next PuppetCheck.files[:warnings][file] = warnings.to_s.gsub('warning: ', '').delete("\n").split('(erb):').compact unless warnings == ''
60
+ PuppetCheck.files[:clean].push(file.to_s)
63
61
  end
64
62
  end
65
63
 
@@ -67,30 +65,30 @@ class RubyParser
67
65
  def self.librarian(files, style, rc_args)
68
66
  # efficient var assignment prior to iterator
69
67
  if style
70
- require 'rubocop'
71
-
72
68
  # RuboCop is grumpy about non-snake_case filenames so disable the FileName check
73
- rc_args.include?('--except') ? rc_args[rc_args.index('--except') + 1] = "#{rc_args[rc_args.index('--except') + 1]},Naming/FileName" : rc_args.concat(['--except', 'Naming/FileName'])
69
+ rc_args.include?('--except') ? rc_args[rc_args.index('--except') + 1] = "#{rc_args[rc_args.index('--except') + 1]},Naming/FileName" : rc_args.push('--except', 'Naming/FileName')
74
70
  end
75
71
 
76
72
  files.each do |file|
77
73
  # check librarian puppet syntax
78
- begin
79
- # prevents ruby code from actually executing
80
- catch(:good) { instance_eval("BEGIN {throw :good}; #{File.read(file)}", file) }
81
- rescue SyntaxError, LoadError, ArgumentError => err
82
- PuppetCheck.settings[:error_files].push("#{file}:\n#{err}")
83
- # check librarian puppet style
84
- else
85
- if style
86
- # check Rubocop
87
- warnings = Utils.capture_stdout { RuboCop::CLI.new.run(rc_args + ['--require', 'rubocop-performance', '--format', 'emacs', file]) }
74
+ # prevents ruby code from actually executing
75
+ catch(:good) { instance_eval("BEGIN {throw :good}; #{File.read(file)}", file) }
76
+ rescue SyntaxError, LoadError, ArgumentError => err
77
+ PuppetCheck.files[:errors][file] = err.to_s.gsub("#{file}:", '').split("\n")
78
+ # check librarian puppet style
79
+ else
80
+ if style
81
+ # check Rubocop
82
+ require 'json'
83
+ require 'rubocop'
84
+
85
+ warnings = Utils.capture_stdout { RuboCop::CLI.new.run(rc_args + ['--enable-pending-cops', '--require', 'rubocop-performance', '--format', 'json', file]) }
86
+ offenses = JSON.parse(warnings)['files'][0]['offenses'].map { |warning| "#{warning['location']['line']}:#{warning['location']['column']} #{warning['message']}" }
88
87
 
89
- # collect style warnings
90
- next PuppetCheck.settings[:warning_files].push("#{file}:\n#{warnings.split("#{File.absolute_path(file)}:").join('')}") unless warnings.empty?
91
- end
92
- PuppetCheck.settings[:clean_files].push(file.to_s)
88
+ # collect style warnings
89
+ next PuppetCheck.files[:warnings][file] = offenses unless offenses.empty?
93
90
  end
91
+ PuppetCheck.files[:clean].push(file.to_s)
94
92
  end
95
93
  end
96
94
 
@@ -6,15 +6,17 @@ end
6
6
  require_relative '../puppet_check'
7
7
 
8
8
  # the rake interface for PuppetCheck
9
- class PuppetCheck::Tasks < ::Rake::TaskLib
9
+ class PuppetCheck::Tasks < Rake::TaskLib
10
10
  def initialize
11
+ super
12
+
11
13
  desc 'Execute all Puppet-Check checks'
12
14
  task puppetcheck: %w[puppetcheck:file puppetcheck:spec puppetcheck:beaker puppetcheck:kitchen]
13
15
 
14
16
  namespace :puppetcheck do
15
17
  desc 'Execute Puppet-Check file checks'
16
- task :file do
17
- PuppetCheck.new.run(PuppetCheck.settings, Dir.glob('*'))
18
+ task :file, [:settings] do |_, wrapped_settings|
19
+ PuppetCheck.new.run(wrapped_settings[:settings], Dir.glob('*'))
18
20
  end
19
21
 
20
22
  # rspec and rspec-puppet tasks
@@ -26,9 +28,8 @@ class PuppetCheck::Tasks < ::Rake::TaskLib
26
28
  RSpec::Core::RakeTask.new(:spec) do |task|
27
29
  RSpecPuppetSupport.run
28
30
  # generate tasks for all recognized directories and ensure spec tests inside module dependencies are ignored
29
- spec_dirs = Dir.glob('**/{classes,defines,facter,functions,hosts,puppet,unit,types}/**/*_spec.rb').reject { |dir| dir =~ /fixtures/ }
31
+ spec_dirs = Dir.glob('**/{classes,defines,facter,functions,hosts,puppet,unit,types}/**/*_spec.rb').grep_v(/fixtures/)
30
32
  task.pattern = spec_dirs.empty? ? 'skip_rspec' : spec_dirs
31
- task.rspec_opts = '-f json' if PuppetCheck.settings[:output_format] == 'json'
32
33
  end
33
34
  rescue LoadError
34
35
  desc 'RSpec is not installed.'