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 +4 -4
- data/CHANGELOG.md +12 -1
- data/README.md +20 -35
- data/lib/puppet-check/cli.rb +6 -4
- data/lib/puppet-check/data_parser.rb +30 -26
- data/lib/puppet-check/output_results.rb +36 -30
- data/lib/puppet-check/puppet_parser.rb +24 -35
- data/lib/puppet-check/rspec_puppet_support.rb +1 -1
- data/lib/puppet-check/ruby_parser.rb +46 -48
- data/lib/puppet-check/tasks.rb +6 -5
- data/lib/puppet_check.rb +28 -23
- data/spec/fixtures/plans/good.pp +3 -0
- data/spec/fixtures/plans/style.pp +6 -0
- data/spec/fixtures/plans/syntax.pp +5 -0
- 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 +60 -32
- data/spec/puppet-check/regression_check_spec.rb +1 -1
- data/spec/puppet-check/rspec_puppet_support_spec.rb +3 -3
- data/spec/puppet-check/ruby_parser_spec.rb +53 -44
- data/spec/puppet-check/tasks_spec.rb +4 -8
- data/spec/puppet_check_spec.rb +63 -25
- data/spec/system/system_spec.rb +26 -24
- metadata +28 -85
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
@@ -1,4 +1,15 @@
|
|
1
|
-
### 2.1
|
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
|
-
|
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,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').
|
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
|
-
|
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
|
|
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.1
|
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,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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
19
|
-
|
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
|
-
|
22
|
-
|
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.
|
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.
|
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.
|
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.
|
67
|
-
PuppetCheck.
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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
|
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.
|
197
|
-
PuppetCheck.
|
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
|
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,41 +18,33 @@ class PuppetParser
|
|
18
18
|
# check puppet syntax
|
19
19
|
begin
|
20
20
|
# initialize message
|
21
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
42
|
-
|
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 =
|
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
|
48
|
-
warnings
|
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
|
-
|
76
|
-
|
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.
|
80
|
-
PuppetCheck.
|
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
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
21
|
-
|
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
|
-
|
25
|
-
|
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
|
-
|
31
|
-
|
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 =
|
48
|
-
|
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.
|
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.
|
62
|
-
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)
|
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.
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
90
|
-
|
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
|
|
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.'
|