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 +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +19 -13
- data/lib/puppet-check/cli.rb +6 -4
- data/lib/puppet-check/data_parser.rb +24 -18
- data/lib/puppet-check/output_results.rb +36 -30
- data/lib/puppet-check/puppet_parser.rb +18 -16
- data/lib/puppet-check/rspec_puppet_support.rb +1 -1
- data/lib/puppet-check/ruby_parser.rb +27 -20
- data/lib/puppet-check/tasks.rb +6 -5
- data/lib/puppet_check.rb +28 -23
- data/spec/puppet-check/cli_spec.rb +1 -1
- data/spec/puppet-check/data_parser_spec.rb +60 -53
- data/spec/puppet-check/output_results_spec.rb +28 -39
- data/spec/puppet-check/puppet_parser_spec.rb +52 -42
- data/spec/puppet-check/rspec_puppet_support_spec.rb +4 -4
- data/spec/puppet-check/ruby_parser_spec.rb +52 -43
- data/spec/puppet-check/tasks_spec.rb +4 -8
- data/spec/puppet_check_spec.rb +63 -25
- data/spec/system/system_spec.rb +20 -20
- metadata +13 -64
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 384ccc25e3b1a78f69e3d017131eaf9583dc24d73f1f9ef1c37c21ac706715b9
|
4
|
+
data.tar.gz: 5094bc3014d74b9a5707fade543d1535d4ef12684d5adaddbb4ceb26c5a4a37e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1bfc1834fc8dc70defbfce8d330f724b834653e5ba48d4a546ff3d93b3bf94d5ac68850d84dcca6d0ac1bad1990d30c07f58c5bfbaaaf2b123fa352c408200f8
|
7
|
+
data.tar.gz: e65f3c64cbc715d2e49c15378165256c17c01e440c979557908bd8a64fa5b77fdf74f8dac5fadbafec6df9b106bf05adb23443b552334759812850e3d89a872f
|
data/CHANGELOG.md
CHANGED
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
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
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').
|
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
|
data/lib/puppet-check/cli.rb
CHANGED
@@ -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.
|
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
|
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.
|
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.
|
21
|
-
PuppetCheck.
|
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.
|
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.
|
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.
|
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.
|
65
|
-
PuppetCheck.
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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
|
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.
|
195
|
-
PuppetCheck.
|
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
|
6
|
-
def self.
|
7
|
-
|
8
|
-
|
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
|
-
|
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
|
-
|
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(
|
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
|
-
|
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$})
|
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
|
-
|
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
|
-
|
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
|
-
|
36
|
-
|
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 =
|
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
|
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
|
-
|
65
|
-
|
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.
|
69
|
-
PuppetCheck.
|
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.
|
84
|
+
PuppetCheck.files[:errors][file] = [err.to_s.gsub("file: #{file}, ", '')]
|
83
85
|
else
|
84
|
-
PuppetCheck.
|
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
|
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.
|
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
|
-
|
20
|
-
|
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
|
24
|
+
# check Reek and parse warnings' JSON output
|
24
25
|
require 'reek'
|
25
26
|
require 'reek/cli/application'
|
26
|
-
|
27
|
-
|
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.
|
35
|
+
next PuppetCheck.files[:warnings][file] = warnings unless warnings.empty?
|
31
36
|
end
|
32
|
-
PuppetCheck.
|
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
|
-
|
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.
|
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.
|
55
|
-
PuppetCheck.
|
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.
|
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.
|
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
|
-
|
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.
|
89
|
+
next PuppetCheck.files[:warnings][file] = offenses unless offenses.empty?
|
83
90
|
end
|
84
|
-
PuppetCheck.
|
91
|
+
PuppetCheck.files[:clean].push(file.to_s)
|
85
92
|
end
|
86
93
|
end
|
87
94
|
|
data/lib/puppet-check/tasks.rb
CHANGED
@@ -6,15 +6,17 @@ end
|
|
6
6
|
require_relative '../puppet_check'
|
7
7
|
|
8
8
|
# the rake interface for PuppetCheck
|
9
|
-
class PuppetCheck::Tasks <
|
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(
|
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').
|
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
|
9
|
-
@
|
10
|
-
|
11
|
-
|
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 :
|
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
|
-
|
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.
|
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
|
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.
|
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
|