puppet-check 2.2.0 → 2.2.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b1e591abda5a49b0731073d14831000016b08d72b505ab3d22a287029fa1243f
4
- data.tar.gz: 96bceb6ef51e82582984ab0c88f96591d30c26650e174a0215133af0723c9526
3
+ metadata.gz: 384ccc25e3b1a78f69e3d017131eaf9583dc24d73f1f9ef1c37c21ac706715b9
4
+ data.tar.gz: 5094bc3014d74b9a5707fade543d1535d4ef12684d5adaddbb4ceb26c5a4a37e
5
5
  SHA512:
6
- metadata.gz: 805ef299109715901160e9f4c4894ff05f31140c8234ed867273434e85b525e4c91e0e5fd7e6f96fb23352a3b801afa006b31cb4a93515c76182725b510a57f8
7
- data.tar.gz: 2fc8340befd7b6c5f9662038aeea6614858ed1a2bd92f0d76c797fe42b316fed9d080e38cd8f4203d16240c365086bc1165c89102f3ee4e496dae6f1cf75b9bd
6
+ metadata.gz: 1bfc1834fc8dc70defbfce8d330f724b834653e5ba48d4a546ff3d93b3bf94d5ac68850d84dcca6d0ac1bad1990d30c07f58c5bfbaaaf2b123fa352c408200f8
7
+ data.tar.gz: e65f3c64cbc715d2e49c15378165256c17c01e440c979557908bd8a64fa5b77fdf74f8dac5fadbafec6df9b106bf05adb23443b552334759812850e3d89a872f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ### 2.2.1
2
+ - Improved output formatting for all formats.
3
+ - Update and improve Rake task interfacing.
4
+
1
5
  ### 2.2.0
2
6
  - Add Enable Pending Cops to base RuboCop configuration.
3
7
  - Support checking plans.
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,7 +282,7 @@ 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
@@ -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.2.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,23 +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
11
  parsed = YAML.load_file(file)
12
12
  rescue StandardError => err
13
- PuppetCheck.settings[:error_files].push("#{file}:\n#{err.to_s.gsub("(#{file}): ", '')}")
13
+ PuppetCheck.files[:errors][file] = err.to_s.gsub("(#{file}): ", '').split("\n")
14
14
  else
15
15
  warnings = []
16
16
 
17
17
  # perform some rudimentary hiera checks if data exists and is hieradata
18
18
  warnings = hiera(parsed, file) if parsed && (File.basename(file) != 'hiera.yaml')
19
19
 
20
- next PuppetCheck.settings[:warning_files].push("#{file}:\n#{warnings.join("\n")}") unless warnings.empty?
21
- PuppetCheck.settings[:clean_files].push(file.to_s)
20
+ next PuppetCheck.files[:warnings][file] = warnings unless warnings.empty?
21
+ PuppetCheck.files[:clean].push(file.to_s)
22
22
  end
23
23
  end
24
24
 
@@ -28,13 +28,13 @@ class DataParser
28
28
 
29
29
  # keys specified?
30
30
  if public.nil? || private.nil?
31
- PuppetCheck.settings[:ignored_files].concat(files)
31
+ PuppetCheck.files[:ignored].concat(files)
32
32
  return warn 'Public X509 and/or Private RSA PKCS7 certs were not specified. EYAML checks will not be executed.'
33
33
  end
34
34
 
35
35
  # keys exist?
36
36
  unless File.file?(public) && File.file?(private)
37
- PuppetCheck.settings[:ignored_files].concat(files)
37
+ PuppetCheck.files[:ignored].concat(files)
38
38
  return warn 'Specified Public X509 and/or Private RSA PKCS7 certs do not exist. EYAML checks will not be executed.'
39
39
  end
40
40
 
@@ -54,19 +54,25 @@ class DataParser
54
54
  begin
55
55
  parsed = YAML.load_file(decrypted)
56
56
  rescue StandardError => err
57
- PuppetCheck.settings[:error_files].push("#{file}:\n#{err.to_s.gsub("(#{file}): ", '')}")
57
+ PuppetCheck.files[:errors][file] = err.to_s.gsub("(#{file}): ", '').split("\n")
58
58
  else
59
59
  warnings = []
60
60
 
61
61
  # perform some rudimentary hiera checks if data exists and is hieradata
62
62
  warnings = hiera(parsed, file) if parsed
63
63
 
64
- next PuppetCheck.settings[:warning_files].push("#{file}:\n#{warnings.join("\n")}") unless warnings.empty?
65
- 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)
66
66
  end
67
67
  end
68
68
  end
69
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
+
70
76
  # checks json (.json)
71
77
  def self.json(files)
72
78
  require 'json'
@@ -76,7 +82,7 @@ class DataParser
76
82
  begin
77
83
  parsed = JSON.parse(File.read(file))
78
84
  rescue JSON::ParserError => err
79
- 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")
80
86
  else
81
87
  warnings = []
82
88
 
@@ -89,12 +95,12 @@ class DataParser
89
95
  errors = []
90
96
 
91
97
  # check for required keys
92
- %w[name version author license summary source dependencies].each do |key|
98
+ REQUIRED_KEYS.each do |key|
93
99
  errors.push("Required field '#{key}' not found.") unless parsed.key?(key)
94
100
  end
95
101
 
96
102
  # check requirements and dependencies keys
97
- %w[requirements dependencies].each do |key|
103
+ REQ_DEP_KEYS.each do |key|
98
104
  # skip if key is missing or value is an empty string, array, or hash
99
105
  next if !parsed.key?(key) || parsed[key].empty?
100
106
 
@@ -123,14 +129,14 @@ class DataParser
123
129
  end
124
130
 
125
131
  # check for deprecated fields
126
- %w[types checksum].each do |key|
132
+ DEPRECATED_KEYS.each do |key|
127
133
  errors.push("Deprecated field '#{key}' found.") if parsed.key?(key)
128
134
  end
129
135
 
130
136
  # check for summary under 144 character
131
137
  errors.push('Summary exceeds 144 characters.') if parsed.key?('summary') && parsed['summary'].size > 144
132
138
 
133
- next PuppetCheck.settings[:error_files].push("#{file}:\n#{errors.join("\n")}") unless errors.empty?
139
+ next PuppetCheck.files[:errors][file] = errors unless errors.empty?
134
140
 
135
141
  # check for warnings
136
142
  # check for operatingsystem_support hash array
@@ -169,7 +175,7 @@ class DataParser
169
175
  # check that input_method is one of three possible values
170
176
  if parsed.key?('input_method')
171
177
  if parsed['input_method'].is_a?(String)
172
- 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'])
173
179
  else
174
180
  warnings.push('input_method value is not a String')
175
181
  end
@@ -191,8 +197,8 @@ class DataParser
191
197
  # perform some rudimentary hiera checks if data exists
192
198
  warnings = hiera(parsed, file)
193
199
  end
194
- next PuppetCheck.settings[:warning_files].push("#{file}:\n#{warnings.join("\n")}") unless warnings.empty?
195
- 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)
196
202
  end
197
203
  end
198
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,30 +18,33 @@ class PuppetParser
18
18
  # check puppet syntax
19
19
  begin
20
20
  # initialize message
21
- message = ''
21
+ messages = []
22
22
  # specify tasks attribute for parser validation if this looks like a plan or not
23
- Puppet[:tasks] = file.match?(%r{plans/\w+\.pp$}) ? true : false
23
+ Puppet[:tasks] = file.match?(%r{plans/\w+\.pp$})
24
24
  # in puppet >= 6.5 the return of this method is a hash with the error
25
25
  new_error = Puppet::Face[:parser, :current].validate(file)
26
26
  # puppet 6.5 output format is now a hash from the face api
27
27
  if Gem::Version.new(Puppet::PUPPETVERSION) >= Gem::Version.new('6.5.0') && new_error != {}
28
- message = new_error.values.map(&:to_s).join("\n").gsub(/ \(file: #{File.absolute_path(file)}(, |\))/, '').gsub('Could not parse for environment *root*: ', '')
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*: ', '') })
29
29
  end
30
30
  # this is the actual error that we need to rescue Puppet::Face from
31
31
  rescue SystemExit
32
32
  # puppet 5.4-6.4 has a new validator output format and eof errors have fake dir env info
33
- message = errors.map(&:to_s).join("\n").gsub(/file: #{File.absolute_path(file)}(, |\))/, '').gsub(/Could not parse.*: /, '')
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.*: /, '') })
34
34
  end
35
- # output message
36
- 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?
37
40
 
38
41
  # initialize warnings with output from the parser if it exists, since the output is warnings if Puppet::Face did not trigger a SystemExit
39
- warnings = "#{file}:"
42
+ warnings = []
43
+ # weirdly puppet >= 6.5 still does not return warnings and logs them instead unlike errors
40
44
  unless errors.empty?
41
45
  # puppet >= 5.4 has a new validator output format
42
- warnings << "\n#{errors.map(&:to_s).join("\n").gsub("file: #{File.absolute_path(file)}, ", '')}"
46
+ warnings.concat(errors.map(&:to_s).join("\n").gsub("file: #{File.absolute_path(file)}, ", '').split("\n"))
43
47
  end
44
- Puppet::Util::Log.close_all
45
48
 
46
49
  # check puppet style
47
50
  if style
@@ -61,12 +64,11 @@ class PuppetParser
61
64
  puppet_lint.run
62
65
 
63
66
  # collect the warnings
64
- if puppet_lint.warnings?
65
- puppet_lint.problems.each { |values| warnings << "\n#{values[:line]}:#{values[:column]}: #{values[:message]}" }
66
- end
67
+ offenses = puppet_lint.problems.map { |problem| "#{problem[:line]}:#{problem[:column]} #{problem[:message]}" }
68
+ warnings.concat(offenses)
67
69
  end
68
- next PuppetCheck.settings[:warning_files].push(warnings) unless warnings == "#{file}:"
69
- 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)
70
72
  end
71
73
  end
72
74
 
@@ -79,9 +81,9 @@ class PuppetParser
79
81
  # credits to gds-operations/puppet-syntax for the parser function call
80
82
  Puppet::Pops::Parser::EvaluatingParser::EvaluatingEppParser.new.parse_file(file)
81
83
  rescue StandardError => err
82
- PuppetCheck.settings[:error_files].push("#{file}:\n#{err.to_s.gsub("#{file}:", '')}")
84
+ PuppetCheck.files[:errors][file] = [err.to_s.gsub("file: #{file}, ", '')]
83
85
  else
84
- PuppetCheck.settings[:clean_files].push(file.to_s)
86
+ PuppetCheck.files[:clean].push(file.to_s)
85
87
  end
86
88
  end
87
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
@@ -10,26 +10,31 @@ class RubyParser
10
10
  # prevents ruby code from actually executing
11
11
  catch(:good) { instance_eval("BEGIN {throw :good}; #{File.read(file)}", file) }
12
12
  rescue ScriptError, StandardError => err
13
- PuppetCheck.settings[:error_files].push("#{file}:\n#{err}")
13
+ PuppetCheck.files[:errors][file] = err.to_s.gsub("#{file}:", '').split("\n")
14
14
  else
15
15
  # check ruby style
16
16
  if style
17
+ # check RuboCop and parse warnings' JSON output
18
+ require 'json'
17
19
  require 'rubocop'
18
20
 
19
- # check RuboCop and collect warnings
20
- rubocop_warnings = Utils.capture_stdout { RuboCop::CLI.new.run(rc_args + ['--enable-pending-cops', '--require', 'rubocop-performance', '--format', 'emacs', file]) }
21
- warnings = rubocop_warnings == '' ? '' : rubocop_warnings.split("#{File.absolute_path(file)}:").join('')
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']}" }
22
23
 
23
- # check Reek and collect warnings
24
+ # check Reek and parse warnings' JSON output
24
25
  require 'reek'
25
26
  require 'reek/cli/application'
26
- reek_warnings = Utils.capture_stdout { Reek::CLI::Application.new([file]).execute }
27
- warnings << reek_warnings.split("\n")[1..].map(&:strip).join("\n") unless reek_warnings == ''
27
+
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']}" }
30
+
31
+ # assign warnings from combined offenses
32
+ warnings = rubocop_offenses + reek_offenses
28
33
 
29
34
  # return warnings
30
- next PuppetCheck.settings[:warning_files].push("#{file}:\n#{warnings.strip}") unless warnings == ''
35
+ next PuppetCheck.files[:warnings][file] = warnings unless warnings.empty?
31
36
  end
32
- PuppetCheck.settings[:clean_files].push(file.to_s)
37
+ PuppetCheck.files[:clean].push(file.to_s)
33
38
  end
34
39
  end
35
40
 
@@ -43,16 +48,16 @@ class RubyParser
43
48
  # need to eventually have this associated with a different binding during each iteration
44
49
  # older usage throws extra warning and mixes with valid warnings confusingly
45
50
  warnings = Utils.capture_stderr { ERB.new(File.read(file), trim_mode: '-').result }
46
- # ERB.new(File.read(file), trim_mode: '-').result(RubyParser.new.bind)
51
+ # warnings = ERB.new(File.read(file), trim_mode: '-').result(RubyParser.new.bind)
47
52
  rescue NameError, TypeError
48
53
  # empty out warnings since it would contain an error if this pass triggers
49
54
  warnings = ''
50
55
  rescue ScriptError => err
51
- next PuppetCheck.settings[:error_files].push("#{file}:\n#{err}")
56
+ next PuppetCheck.files[:errors][file] = err.to_s.gsub('(erb):', '').split("\n")
52
57
  end
53
58
  # return warnings from the check if there were any
54
- next PuppetCheck.settings[:warning_files].push("#{file}:\n#{warnings.gsub('warning: ', '').split('(erb):').join('').strip}") unless warnings == ''
55
- 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)
56
61
  end
57
62
  end
58
63
 
@@ -60,10 +65,8 @@ class RubyParser
60
65
  def self.librarian(files, style, rc_args)
61
66
  # efficient var assignment prior to iterator
62
67
  if style
63
- require 'rubocop'
64
-
65
68
  # RuboCop is grumpy about non-snake_case filenames so disable the FileName check
66
- 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')
67
70
  end
68
71
 
69
72
  files.each do |file|
@@ -71,17 +74,21 @@ class RubyParser
71
74
  # prevents ruby code from actually executing
72
75
  catch(:good) { instance_eval("BEGIN {throw :good}; #{File.read(file)}", file) }
73
76
  rescue SyntaxError, LoadError, ArgumentError => err
74
- PuppetCheck.settings[:error_files].push("#{file}:\n#{err}")
77
+ PuppetCheck.files[:errors][file] = err.to_s.gsub("#{file}:", '').split("\n")
75
78
  # check librarian puppet style
76
79
  else
77
80
  if style
78
81
  # check Rubocop
79
- warnings = Utils.capture_stdout { RuboCop::CLI.new.run(rc_args + ['--enable-pending-cops', '--require', 'rubocop-performance', '--format', 'emacs', file]) }
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']}" }
80
87
 
81
88
  # collect style warnings
82
- next PuppetCheck.settings[:warning_files].push("#{file}:\n#{warnings.split("#{File.absolute_path(file)}:").join('')}") unless warnings.empty?
89
+ next PuppetCheck.files[:warnings][file] = offenses unless offenses.empty?
83
90
  end
84
- PuppetCheck.settings[:clean_files].push(file.to_s)
91
+ PuppetCheck.files[:clean].push(file.to_s)
85
92
  end
86
93
  end
87
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.'
data/lib/puppet_check.rb CHANGED
@@ -5,33 +5,35 @@ require_relative 'puppet-check/output_results'
5
5
 
6
6
  # interfaces from CLI/tasks and to individual parsers
7
7
  class PuppetCheck
8
- # initialize settings hash
9
- @settings = {}
10
-
11
- # allow the parser methods read user options and append to the file arrays; allow CLI and tasks write to user options
8
+ # initialize files hash
9
+ @files = {
10
+ errors: {},
11
+ warnings: {},
12
+ clean: [],
13
+ ignored: []
14
+ }
15
+
16
+ # allow the parser methods write to the files
12
17
  class << self
13
- attr_accessor :settings
18
+ attr_accessor :files
14
19
  end
15
20
 
16
21
  # main runner for PuppetCheck
17
- def run(settings, paths)
18
- # establish settings
19
- self.class.settings = settings
20
-
22
+ def run(settings = {}, paths = [])
21
23
  # settings defaults
22
- self.class.defaults(settings)
24
+ settings = self.class.defaults(settings)
23
25
 
24
26
  # grab all of the files to be processed
25
27
  files = self.class.parse_paths(paths)
26
28
 
27
29
  # parse the files
28
- execute_parsers(files, settings)
30
+ parsed_files = execute_parsers(files, settings)
29
31
 
30
32
  # output the diagnostic results
31
- settings[:output_format] == 'text' ? OutputResults.text : OutputResults.markup(settings[:output_format])
33
+ OutputResults.run(parsed_files, settings[:output_format])
32
34
 
33
35
  # progress to regression checks if no errors in file checks
34
- if self.class.settings[:error_files].empty? && (!settings[:fail_on_warning] || self.class.settings[:warning_files].empty?)
36
+ if self.class.files[:errors].empty? && (!settings[:fail_on_warning] || self.class.files[:warnings].empty?)
35
37
  begin
36
38
  require_relative 'puppet-check/regression_check'
37
39
  # if octocatalog-diff is not installed then return immediately
@@ -68,7 +70,8 @@ class PuppetCheck
68
70
  end
69
71
 
70
72
  # establish default settings
71
- def self.defaults(settings)
73
+ def self.defaults(settings = {})
74
+ private_class_method :method
72
75
  # initialize fail on warning, style check, and regression check bools
73
76
  settings[:fail_on_warning] ||= false
74
77
  settings[:style] ||= false
@@ -82,12 +85,6 @@ class PuppetCheck
82
85
  # initialize output format option
83
86
  settings[:output_format] ||= 'text'
84
87
 
85
- # initialize diagnostic output arrays
86
- @settings[:error_files] = []
87
- @settings[:warning_files] = []
88
- @settings[:clean_files] = []
89
- @settings[:ignored_files] = []
90
-
91
88
  # initialize octocatalog-diff options
92
89
  settings[:octoconfig] ||= '.octocatalog-diff.cfg.rb'
93
90
  settings[:octonodes] ||= %w[localhost.localdomain]
@@ -95,10 +92,14 @@ class PuppetCheck
95
92
  # initialize style arg arrays
96
93
  settings[:puppetlint_args] ||= []
97
94
  settings[:rubocop_args] ||= []
95
+
96
+ # return update settings
97
+ settings
98
98
  end
99
99
 
100
100
  # parse the paths and return the array of files
101
- def self.parse_paths(paths)
101
+ def self.parse_paths(paths = [])
102
+ private_class_method :method
102
103
  files = []
103
104
 
104
105
  # traverse the unique paths and return all files
@@ -111,13 +112,15 @@ class PuppetCheck
111
112
  end
112
113
 
113
114
  # do not process fixtures, check that at least one file was found, and remove double slashes
114
- files.reject! { |file| file =~ /fixtures/ }
115
+ files.reject! { |file| file.include?('fixtures') }
115
116
  raise "puppet-check: no files found in supplied paths '#{paths.join(', ')}'." if files.empty?
116
117
  files.map! { |file| file.gsub('//', '/') }
117
118
 
118
119
  files.uniq
119
120
  end
120
121
 
122
+ private
123
+
121
124
  # categorize and pass the files out to the parsers to determine their status
122
125
  def execute_parsers(files, settings)
123
126
  # check manifests
@@ -145,6 +148,8 @@ class PuppetCheck
145
148
  librarians, files = files.partition { |file| File.basename(file) =~ /(?:Puppet|Module|Rake|Gem)file$/ }
146
149
  RubyParser.librarian(librarians, settings[:style], settings[:rubocop_args]) unless librarians.empty?
147
150
  # ignore everything else
148
- self.class.settings[:ignored_files].concat(files)
151
+ files.each { |file| self.class.files[:ignored].push(file.to_s) }
152
+ # return PuppetCheck.files to mitigate singleton write accessor side effects
153
+ PuppetCheck.files
149
154
  end
150
155
  end