puppet-check 2.2.2 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +1 -4
- data/lib/puppet-check/cli.rb +3 -1
- data/lib/puppet-check/data_parser.rb +120 -119
- data/lib/puppet-check/output_results.rb +21 -14
- data/lib/puppet-check/puppet_parser.rb +8 -5
- data/lib/puppet-check/ruby_parser.rb +17 -12
- data/lib/puppet_check.rb +36 -38
- data/spec/octocatalog-diff/octocatalog_diff.cfg.rb +1 -5
- data/spec/puppet-check/data_parser_spec.rb +8 -7
- data/spec/puppet-check/output_results_spec.rb +4 -4
- data/spec/puppet-check/regression_check_spec.rb +1 -1
- data/spec/puppet-check/rspec_puppet_support_spec.rb +1 -4
- data/spec/puppet_check_spec.rb +5 -1
- data/spec/system/system_spec.rb +12 -12
- metadata +9 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: caea7da11b0b884512bc9e078c71caece3bfb2045f5798c55b41201b948e5d3f
|
4
|
+
data.tar.gz: 6e4eae239a2cbbeda1709741c523161720dd8b28399d9e1e5cae6add54cef59b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18d8d802b337a1b47de63aa25a4f0c2b7e6f6f6678047b2f941a28766ee7d1a18f07244b287faf37747170d170230dfb58073f67a63dce95513421acd125ca5a
|
7
|
+
data.tar.gz: '09031de8c432747e7967241ff0abd4290d34d38892eb4df0443e3f7e88acd5d30c177fbabf811a6958874533fe951ea795965acea0617d21e0f2f28832987e17'
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,4 @@
|
|
1
1
|
# Puppet Check
|
2
|
-
[![Build Status](https://travis-ci.com/mschuchard/puppet-check.svg?branch=master)](https://travis-ci.com/mschuchard/puppet-check)
|
3
|
-
[![CircleCI](https://circleci.com/gh/mschuchard/puppet-check.svg?style=svg)](https://circleci.com/gh/mschuchard/puppet-check)
|
4
|
-
|
5
2
|
- [Description](#description)
|
6
3
|
- [Usage](#usage)
|
7
4
|
- [CLI](#cli)
|
@@ -16,7 +13,7 @@
|
|
16
13
|
## Description
|
17
14
|
Puppet Check is a gem that provides a comprehensive, streamlined, and efficient analysis of the syntax, style, and validity of your entire Puppet code and data.
|
18
15
|
|
19
|
-
**IMPORTANT**: The current support for encrypted yaml validation is experimental
|
16
|
+
**IMPORTANT**: The current support for encrypted yaml validation is experimental and should be considered a beta feature as of 2.3.0.
|
20
17
|
|
21
18
|
### Former Method for Code and Data Checks
|
22
19
|
![Old](https://raw.githubusercontent.com/mschuchard/puppet-check/master/images/puppetcheck_old.png)
|
data/lib/puppet-check/cli.rb
CHANGED
@@ -29,7 +29,9 @@ class PuppetCheck::CLI
|
|
29
29
|
|
30
30
|
# base options
|
31
31
|
opts.on('--version', 'Display the current version.') do
|
32
|
-
|
32
|
+
require 'rubygems'
|
33
|
+
|
34
|
+
puts Gem::Specification.load("#{File.dirname(__FILE__)}/../../puppet-check.gemspec").version
|
33
35
|
exit 0
|
34
36
|
end
|
35
37
|
|
@@ -43,27 +43,30 @@ class DataParser
|
|
43
43
|
x509 = OpenSSL::X509::Certificate.new(File.read(public))
|
44
44
|
|
45
45
|
files.each do |file|
|
46
|
-
#
|
46
|
+
# check encoded yaml syntax
|
47
|
+
parsed = YAML.load_file(file)
|
47
48
|
|
48
|
-
#
|
49
|
-
|
49
|
+
# extract encoded values
|
50
|
+
# ENC[PKCS7]
|
50
51
|
|
51
|
-
#
|
52
|
+
# decrypt the encoded yaml
|
53
|
+
# decrypted = OpenSSL::PKCS7.new(File.read(file)).decrypt(rsa, x509)
|
52
54
|
|
53
|
-
# check
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
55
|
+
# check decoded eyaml syntax
|
56
|
+
# decoded = YAML.safe_load(decrypted)
|
57
|
+
|
58
|
+
# merge data hashes
|
59
|
+
# parsed = merge(parsed, decoded)
|
60
|
+
rescue StandardError => err
|
61
|
+
PuppetCheck.files[:errors][file] = err.to_s.gsub("(#{file}): ", '').split("\n")
|
62
|
+
else
|
63
|
+
warnings = []
|
64
|
+
|
65
|
+
# perform some rudimentary hiera checks if data exists and is hieradata
|
66
|
+
warnings = hiera(parsed, file) if parsed
|
67
|
+
|
68
|
+
next PuppetCheck.files[:warnings][file] = warnings unless warnings.empty?
|
69
|
+
PuppetCheck.files[:clean].push(file.to_s)
|
67
70
|
end
|
68
71
|
end
|
69
72
|
|
@@ -79,127 +82,125 @@ class DataParser
|
|
79
82
|
|
80
83
|
files.each do |file|
|
81
84
|
# check json syntax
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
warnings = []
|
88
|
-
|
89
|
-
# check metadata.json
|
90
|
-
if File.basename(file) == 'metadata.json'
|
91
|
-
# metadata-json-lint has issues and is essentially no longer maintained, so here is an improved and leaner version of it
|
92
|
-
require 'rubygems/util/licenses'
|
93
|
-
|
94
|
-
# check for errors
|
95
|
-
errors = []
|
96
|
-
|
97
|
-
# check for required keys
|
98
|
-
REQUIRED_KEYS.each do |key|
|
99
|
-
errors.push("Required field '#{key}' not found.") unless parsed.key?(key)
|
100
|
-
end
|
85
|
+
parsed = JSON.parse(File.read(file))
|
86
|
+
rescue JSON::ParserError => err
|
87
|
+
PuppetCheck.files[:errors][file] = err.to_s.lines.first.strip.split("\n")
|
88
|
+
else
|
89
|
+
warnings = []
|
101
90
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
91
|
+
# check metadata.json
|
92
|
+
if File.basename(file) == 'metadata.json'
|
93
|
+
# metadata-json-lint has issues and is essentially no longer maintained, so here is an improved and leaner version of it
|
94
|
+
require 'rubygems/util/licenses'
|
106
95
|
|
107
|
-
|
108
|
-
|
96
|
+
# check for errors
|
97
|
+
errors = []
|
109
98
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
99
|
+
# check for required keys
|
100
|
+
REQUIRED_KEYS.each do |key|
|
101
|
+
errors.push("Required field '#{key}' not found.") unless parsed.key?(key)
|
102
|
+
end
|
103
|
+
|
104
|
+
# check requirements and dependencies keys
|
105
|
+
REQ_DEP_KEYS.each do |key|
|
106
|
+
# skip if key is missing or value is an empty string, array, or hash
|
107
|
+
next if !parsed.key?(key) || parsed[key].empty?
|
108
|
+
|
109
|
+
# check that dependencies and requirements are an array of hashes
|
110
|
+
next errors.push("Field '#{key}' is not an array of hashes.") unless (parsed[key].is_a? Array) && (parsed[key][0].is_a? Hash)
|
117
111
|
|
118
|
-
|
119
|
-
|
112
|
+
# check dependencies and requirements values
|
113
|
+
names = []
|
114
|
+
parsed[key].each do |req_dep|
|
115
|
+
# check for duplicate dependencies and requirements
|
116
|
+
name = req_dep['name']
|
117
|
+
next errors.push("Duplicate #{key} on #{name}.") if names.include?(name)
|
118
|
+
names << name
|
120
119
|
|
121
|
-
|
122
|
-
|
120
|
+
# warn and skip if key is missing
|
121
|
+
next warnings.push("'#{req_dep['name']}' is missing a 'version_requirement' key.") if req_dep['version_requirement'].nil?
|
123
122
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
123
|
+
# warn and skip if no upper bound
|
124
|
+
next warnings.push("'#{req_dep['name']}' is missing an upper bound.") unless req_dep['version_requirement'].include?('<')
|
125
|
+
|
126
|
+
# check for semantic versioning
|
127
|
+
if key == 'dependencies' && req_dep['version_requirement'] !~ /\d+\.\d+\.\d+.*\d+\.\d+\.\d+/
|
128
|
+
warnings.push("'#{req_dep['name']}' has non-semantic versioning in its 'version_requirement' key.")
|
128
129
|
end
|
129
130
|
end
|
131
|
+
end
|
130
132
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
133
|
+
# check for deprecated fields
|
134
|
+
DEPRECATED_KEYS.each do |key|
|
135
|
+
errors.push("Deprecated field '#{key}' found.") if parsed.key?(key)
|
136
|
+
end
|
135
137
|
|
136
|
-
|
137
|
-
|
138
|
+
# check for summary under 144 character
|
139
|
+
errors.push('Summary exceeds 144 characters.') if parsed.key?('summary') && parsed['summary'].size > 144
|
138
140
|
|
139
|
-
|
141
|
+
next PuppetCheck.files[:errors][file] = errors unless errors.empty?
|
140
142
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
143
|
+
# check for warnings
|
144
|
+
# check for operatingsystem_support hash array
|
145
|
+
if parsed.key?('operatingsystem_support')
|
146
|
+
# check if operatingsystem_support array is actually empty
|
147
|
+
if !(parsed['operatingsystem_support'].is_a? Array) || parsed['operatingsystem_support'].empty? || (!parsed['operatingsystem_support'].empty? && !(parsed['operatingsystem_support'][0].is_a? Hash))
|
148
|
+
warnings.push('Recommended field \'operatingsystem\' not found.')
|
149
|
+
warnings.push('Recommended field \'operatingsystemrelease\' not found.')
|
150
|
+
else
|
151
|
+
# check for operatingsystem string
|
152
|
+
if parsed['operatingsystem_support'][0].key?('operatingsystem')
|
153
|
+
warnings.push('Field \'operatingsystem\' is not a string.') unless parsed['operatingsystem_support'][0]['operatingsystem'].is_a? String
|
148
154
|
else
|
149
|
-
|
150
|
-
if parsed['operatingsystem_support'][0].key?('operatingsystem')
|
151
|
-
warnings.push('Field \'operatingsystem\' is not a string.') unless parsed['operatingsystem_support'][0]['operatingsystem'].is_a? String
|
152
|
-
else
|
153
|
-
warnings.push('Recommended field \'operatingsystem\' not found.')
|
154
|
-
end
|
155
|
-
|
156
|
-
# check for operatingsystemrelease string array
|
157
|
-
if parsed['operatingsystem_support'][0].key?('operatingsystemrelease')
|
158
|
-
warnings.push('Field \'operatingsystemrelease\' is not a string array.') unless parsed['operatingsystem_support'][0]['operatingsystemrelease'][0].is_a? String
|
159
|
-
else
|
160
|
-
warnings.push('Recommended field \'operatingsystemrelease\' not found.')
|
161
|
-
end
|
155
|
+
warnings.push('Recommended field \'operatingsystem\' not found.')
|
162
156
|
end
|
163
|
-
else
|
164
|
-
warnings.push('Recommended field \'operatingsystem_support\' not found.')
|
165
|
-
end
|
166
157
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
end
|
171
|
-
# assume this is task metadata if it has this key
|
172
|
-
elsif parsed.key?('description')
|
173
|
-
# check that description is a string
|
174
|
-
warnings.push('description value is not a String') unless parsed['description'].is_a?(String)
|
175
|
-
# check that input_method is one of three possible values
|
176
|
-
if parsed.key?('input_method')
|
177
|
-
if parsed['input_method'].is_a?(String)
|
178
|
-
warnings.push('input_method value is not one of environment, stdin, or powershell') unless TASK_INPUTS.include?(parsed['input_method'])
|
158
|
+
# check for operatingsystemrelease string array
|
159
|
+
if parsed['operatingsystem_support'][0].key?('operatingsystemrelease')
|
160
|
+
warnings.push('Field \'operatingsystemrelease\' is not a string array.') unless parsed['operatingsystem_support'][0]['operatingsystemrelease'][0].is_a? String
|
179
161
|
else
|
180
|
-
warnings.push('
|
162
|
+
warnings.push('Recommended field \'operatingsystemrelease\' not found.')
|
181
163
|
end
|
182
164
|
end
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
165
|
+
else
|
166
|
+
warnings.push('Recommended field \'operatingsystem_support\' not found.')
|
167
|
+
end
|
168
|
+
|
169
|
+
# check for spdx license
|
170
|
+
if parsed.key?('license') && !Gem::Licenses.match?(parsed['license']) && parsed['license'] !~ /[pP]roprietary/
|
171
|
+
warnings.push("License identifier '#{parsed['license']}' is not in the SPDX list: http://spdx.org/licenses/")
|
172
|
+
end
|
173
|
+
# assume this is task metadata if it has this key
|
174
|
+
elsif parsed.key?('description')
|
175
|
+
# check that description is a string
|
176
|
+
warnings.push('description value is not a String') unless parsed['description'].is_a?(String)
|
177
|
+
# check that input_method is one of three possible values
|
178
|
+
if parsed.key?('input_method')
|
179
|
+
if parsed['input_method'].is_a?(String)
|
180
|
+
warnings.push('input_method value is not one of environment, stdin, or powershell') unless TASK_INPUTS.include?(parsed['input_method'])
|
181
|
+
else
|
182
|
+
warnings.push('input_method value is not a String')
|
194
183
|
end
|
195
|
-
# assume this is hieradata and ensure it is non-empty
|
196
|
-
elsif parsed
|
197
|
-
# perform some rudimentary hiera checks if data exists
|
198
|
-
warnings = hiera(parsed, file)
|
199
184
|
end
|
200
|
-
|
201
|
-
|
185
|
+
# check that parameters is a hash
|
186
|
+
if parsed.key?('parameters') && !parsed['parameters'].is_a?(Hash)
|
187
|
+
warnings.push('parameters value is not a Hash')
|
188
|
+
end
|
189
|
+
# check that puppet_task_version is an integer
|
190
|
+
if parsed.key?('puppet_task_version') && !parsed['puppet_task_version'].is_a?(Integer)
|
191
|
+
warnings.push('puppet_task_version value is not an Integer')
|
192
|
+
end
|
193
|
+
# check that supports_noop is a boolean
|
194
|
+
if parsed.key?('supports_noop') && !(parsed['supports_noop'].is_a?(TrueClass) || parsed['supports_noop'].is_a?(FalseClass))
|
195
|
+
warnings.push('supports_noop value is not a Boolean')
|
196
|
+
end
|
197
|
+
# assume this is hieradata and ensure it is non-empty
|
198
|
+
elsif parsed
|
199
|
+
# perform some rudimentary hiera checks if data exists
|
200
|
+
warnings = hiera(parsed, file)
|
202
201
|
end
|
202
|
+
next PuppetCheck.files[:warnings][file] = warnings unless warnings.empty?
|
203
|
+
PuppetCheck.files[:clean].push(file.to_s)
|
203
204
|
end
|
204
205
|
end
|
205
206
|
|
@@ -219,7 +220,7 @@ class DataParser
|
|
219
220
|
end
|
220
221
|
|
221
222
|
# check that '---' does not show up more than once in the hieradata
|
222
|
-
warnings.push('The string --- appears more than once in this data and Hiera may fail to parse it correctly.') if File.read(file).scan(
|
223
|
+
warnings.push('The string --- appears more than once in this data and Hiera may fail to parse it correctly.') if File.read(file).scan('---').count >= 2
|
223
224
|
|
224
225
|
warnings
|
225
226
|
end
|
@@ -29,23 +29,30 @@ class OutputResults
|
|
29
29
|
private_class_method :method
|
30
30
|
|
31
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
|
32
|
+
category_info("\033[31mThe following files have errors:\033[0m\n", files[:errors]) if files.key?(:errors)
|
36
33
|
# 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
|
34
|
+
category_info("\033[33mThe following files have warnings:\033[0m\n", files[:warnings]) if files.key?(:warnings)
|
41
35
|
# 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
|
36
|
+
category_info("\033[32mThe following files have no errors or warnings:\033[0m\n-- ", files[:clean]) if files.key?(:clean)
|
46
37
|
# ignores
|
47
38
|
return unless files.key?(:ignored)
|
48
|
-
|
49
|
-
|
39
|
+
category_info("\033[36mThe following files have unrecognized formats and therefore were not processed:\033[0m\n-- ", files[:ignored])
|
40
|
+
end
|
41
|
+
|
42
|
+
# display heading, files, and file messages per category for text formatting
|
43
|
+
def self.category_info(heading, files)
|
44
|
+
private_class_method :method
|
45
|
+
# display category heading
|
46
|
+
print heading
|
47
|
+
|
48
|
+
# display files and optionally messages
|
49
|
+
case files
|
50
|
+
when Hash then files.each { |file, messages| puts "-- #{file}:\n#{messages.join("\n")}" }
|
51
|
+
when Array then puts files.join("\n-- ")
|
52
|
+
else raise "puppet-check: The category files were of unexpected type #{files.class}. Please file an issue with this log message, category heading, and information about the parsed files."
|
53
|
+
end
|
54
|
+
|
55
|
+
# newline between categories for easier visual parsing
|
56
|
+
puts ''
|
50
57
|
end
|
51
58
|
end
|
@@ -10,6 +10,13 @@ class PuppetParser
|
|
10
10
|
# prepare the Puppet settings for the error checking
|
11
11
|
Puppet.initialize_settings unless Puppet.settings.app_defaults_initialized?
|
12
12
|
|
13
|
+
# prepare the PuppetLint object for style checks
|
14
|
+
if style
|
15
|
+
require 'puppet-lint'
|
16
|
+
require 'puppet-lint/optparser'
|
17
|
+
puppet_lint = PuppetLint.new
|
18
|
+
end
|
19
|
+
|
13
20
|
files.each do |file|
|
14
21
|
# setup error logging and collection; warnings logged for all versions, but errors for only puppet < 6.5
|
15
22
|
errors = []
|
@@ -48,9 +55,6 @@ class PuppetParser
|
|
48
55
|
|
49
56
|
# check puppet style
|
50
57
|
if style
|
51
|
-
require 'puppet-lint'
|
52
|
-
require 'puppet-lint/optparser'
|
53
|
-
|
54
58
|
# check for invalid arguments to PuppetLint
|
55
59
|
begin
|
56
60
|
PuppetLint::OptParser.build.parse!(pl_args.clone)
|
@@ -58,8 +62,7 @@ class PuppetParser
|
|
58
62
|
raise "puppet-lint: invalid option supplied among #{pl_args.join(' ')}"
|
59
63
|
end
|
60
64
|
|
61
|
-
#
|
62
|
-
puppet_lint = PuppetLint.new
|
65
|
+
# execute puppet-lint style checks
|
63
66
|
puppet_lint.file = file
|
64
67
|
puppet_lint.run
|
65
68
|
|
@@ -5,6 +5,16 @@ require_relative 'utils'
|
|
5
5
|
class RubyParser
|
6
6
|
# checks ruby (.rb)
|
7
7
|
def self.ruby(files, style, rc_args)
|
8
|
+
# prepare rubocop object for style checks
|
9
|
+
if style
|
10
|
+
require 'json'
|
11
|
+
require 'rubocop'
|
12
|
+
require 'reek'
|
13
|
+
require 'reek/cli/application'
|
14
|
+
|
15
|
+
rubocop_cli = RuboCop::CLI.new
|
16
|
+
end
|
17
|
+
|
8
18
|
files.each do |file|
|
9
19
|
# check ruby syntax
|
10
20
|
# prevents ruby code from actually executing
|
@@ -15,16 +25,10 @@ class RubyParser
|
|
15
25
|
# check ruby style
|
16
26
|
if style
|
17
27
|
# 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]) }
|
28
|
+
rubocop_warnings = Utils.capture_stdout { rubocop_cli.run(rc_args + ['--enable-pending-cops', '--require', 'rubocop-performance', '--format', 'json', file]) }
|
22
29
|
rubocop_offenses = JSON.parse(rubocop_warnings)['files'][0]['offenses'].map { |warning| "#{warning['location']['line']}:#{warning['location']['column']} #{warning['message']}" }
|
23
30
|
|
24
31
|
# check Reek and parse warnings' JSON output
|
25
|
-
require 'reek'
|
26
|
-
require 'reek/cli/application'
|
27
|
-
|
28
32
|
reek_warnings = Utils.capture_stdout { Reek::CLI::Application.new(['-f', 'json', file]).execute }
|
29
33
|
reek_offenses = JSON.parse(reek_warnings).map { |warning| "#{warning['lines'].join(',')}: #{warning['context']} #{warning['message']}" }
|
30
34
|
|
@@ -65,6 +69,11 @@ class RubyParser
|
|
65
69
|
def self.librarian(files, style, rc_args)
|
66
70
|
# efficient var assignment prior to iterator
|
67
71
|
if style
|
72
|
+
require 'json'
|
73
|
+
require 'rubocop'
|
74
|
+
|
75
|
+
rubocop_cli = RuboCop::CLI.new
|
76
|
+
|
68
77
|
# RuboCop is grumpy about non-snake_case filenames so disable the FileName check
|
69
78
|
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')
|
70
79
|
end
|
@@ -78,11 +87,7 @@ class RubyParser
|
|
78
87
|
# check librarian puppet style
|
79
88
|
else
|
80
89
|
if style
|
81
|
-
|
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]) }
|
90
|
+
warnings = Utils.capture_stdout { rubocop_cli.run(rc_args + ['--enable-pending-cops', '--require', 'rubocop-performance', '--format', 'json', file]) }
|
86
91
|
offenses = JSON.parse(warnings)['files'][0]['offenses'].map { |warning| "#{warning['location']['line']}:#{warning['location']['column']} #{warning['message']}" }
|
87
92
|
|
88
93
|
# collect style warnings
|
data/lib/puppet_check.rb
CHANGED
@@ -13,7 +13,7 @@ class PuppetCheck
|
|
13
13
|
ignored: []
|
14
14
|
}
|
15
15
|
|
16
|
-
# allow the parser methods write to the files
|
16
|
+
# allow the parser methods to write to the files
|
17
17
|
class << self
|
18
18
|
attr_accessor :files
|
19
19
|
end
|
@@ -27,7 +27,7 @@ class PuppetCheck
|
|
27
27
|
files = self.class.parse_paths(paths)
|
28
28
|
|
29
29
|
# parse the files
|
30
|
-
parsed_files = execute_parsers(files, settings)
|
30
|
+
parsed_files = execute_parsers(files, settings[:style], settings[:puppetlint_args], settings[:rubocop_args], settings[:public], settings[:private])
|
31
31
|
|
32
32
|
# output the diagnostic results
|
33
33
|
OutputResults.run(parsed_files.clone, settings[:output_format])
|
@@ -38,6 +38,7 @@ class PuppetCheck
|
|
38
38
|
require_relative 'puppet-check/regression_check'
|
39
39
|
# if octocatalog-diff is not installed then return immediately
|
40
40
|
rescue LoadError
|
41
|
+
warn 'octocatalog-diff is not installed, and therefore the regressions check will be skipped'
|
41
42
|
return 0
|
42
43
|
end
|
43
44
|
|
@@ -72,29 +73,26 @@ class PuppetCheck
|
|
72
73
|
# establish default settings
|
73
74
|
def self.defaults(settings = {})
|
74
75
|
private_class_method :method
|
75
|
-
# initialize fail on warning, style check, and regression check bools
|
76
|
-
settings[:fail_on_warning] ||= false
|
77
|
-
settings[:style] ||= false
|
78
|
-
settings[:smoke] ||= false
|
79
|
-
settings[:regression] ||= false
|
80
76
|
|
81
|
-
#
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
77
|
+
# return settings with defaults where unspecified
|
78
|
+
{
|
79
|
+
# initialize fail on warning, style check, and regression check bools
|
80
|
+
fail_on_warning: false,
|
81
|
+
style: false,
|
82
|
+
smoke: false,
|
83
|
+
regression: false,
|
84
|
+
# initialize ssl keys for eyaml checks
|
85
|
+
public: nil,
|
86
|
+
private: nil,
|
87
|
+
# initialize output format option
|
88
|
+
output_format: 'text',
|
89
|
+
# initialize octocatalog-diff options
|
90
|
+
octoconfig: '.octocatalog-diff.cfg.rb',
|
91
|
+
octonodes: %w[localhost.localdomain],
|
92
|
+
# initialize style arg arrays
|
93
|
+
puppetlint_args: [],
|
94
|
+
rubocop_args: []
|
95
|
+
}.merge(settings)
|
98
96
|
end
|
99
97
|
|
100
98
|
# parse the paths and return the array of files
|
@@ -102,36 +100,36 @@ class PuppetCheck
|
|
102
100
|
private_class_method :method
|
103
101
|
files = []
|
104
102
|
|
105
|
-
# traverse the unique paths and return all files
|
103
|
+
# traverse the unique paths and return all files not explicitly in fixtures
|
106
104
|
paths.uniq.each do |path|
|
107
105
|
if File.directory?(path)
|
108
|
-
|
109
|
-
|
106
|
+
# glob all files in directory and concat them
|
107
|
+
files.concat(Dir.glob("#{path}/**/*").select { |subpath| File.file?(subpath) && !subpath.include?('fixtures') })
|
108
|
+
elsif File.file?(path) && !path.include?('fixtures')
|
110
109
|
files.push(path)
|
110
|
+
else
|
111
|
+
warn "puppet-check: #{path} is not a directory, file, or symlink, and will not be considered during parsing"
|
111
112
|
end
|
112
113
|
end
|
113
114
|
|
114
|
-
#
|
115
|
-
files.reject! { |file| file.include?('fixtures') }
|
115
|
+
# check that at least one file was found, and remove double slashes from returned array
|
116
116
|
raise "puppet-check: no files found in supplied paths '#{paths.join(', ')}'." if files.empty?
|
117
|
-
files.map
|
118
|
-
|
119
|
-
files.uniq
|
117
|
+
files.map { |file| file.gsub('//', '/') }.uniq
|
120
118
|
end
|
121
119
|
|
122
120
|
private
|
123
121
|
|
124
122
|
# categorize and pass the files out to the parsers to determine their status
|
125
|
-
def execute_parsers(files,
|
123
|
+
def execute_parsers(files, style, puppetlint_args, rubocop_args, public, private)
|
126
124
|
# check manifests
|
127
125
|
manifests, files = files.partition { |file| File.extname(file) == '.pp' }
|
128
|
-
PuppetParser.manifest(manifests,
|
126
|
+
PuppetParser.manifest(manifests, style, puppetlint_args) unless manifests.empty?
|
129
127
|
# check puppet templates
|
130
128
|
templates, files = files.partition { |file| File.extname(file) == '.epp' }
|
131
129
|
PuppetParser.template(templates) unless templates.empty?
|
132
130
|
# check ruby files
|
133
131
|
rubies, files = files.partition { |file| File.extname(file) == '.rb' }
|
134
|
-
RubyParser.ruby(rubies,
|
132
|
+
RubyParser.ruby(rubies, style, rubocop_args) unless rubies.empty?
|
135
133
|
# check ruby templates
|
136
134
|
templates, files = files.partition { |file| File.extname(file) == '.erb' }
|
137
135
|
RubyParser.template(templates) unless templates.empty?
|
@@ -142,11 +140,11 @@ class PuppetCheck
|
|
142
140
|
jsons, files = files.partition { |file| File.extname(file) == '.json' }
|
143
141
|
DataParser.json(jsons) unless jsons.empty?
|
144
142
|
# check eyaml data; block this for now
|
145
|
-
|
146
|
-
|
143
|
+
eyamls, files = files.partition { |file| File.extname(file) =~ /\.eya?ml$/ }
|
144
|
+
DataParser.eyaml(eyamls, public, private) unless eyamls.empty?
|
147
145
|
# check misc ruby
|
148
146
|
librarians, files = files.partition { |file| File.basename(file) =~ /(?:Puppet|Module|Rake|Gem)file$/ }
|
149
|
-
RubyParser.librarian(librarians,
|
147
|
+
RubyParser.librarian(librarians, style, rubocop_args) unless librarians.empty?
|
150
148
|
# ignore everything else
|
151
149
|
files.each { |file| self.class.files[:ignored].push(file.to_s) }
|
152
150
|
# return PuppetCheck.files to mitigate singleton write accessor side effects
|
@@ -9,11 +9,7 @@ module OctocatalogDiff
|
|
9
9
|
settings[:hiera_config] = "#{octocatalog_diff_dir}hiera.yaml"
|
10
10
|
settings[:hiera_path] = "#{octocatalog_diff_dir}hieradata"
|
11
11
|
settings[:fact_file] = "#{octocatalog_diff_dir}facts.yaml"
|
12
|
-
settings[:puppet_binary] =
|
13
|
-
"#{octocatalog_diff_dir}../../bin/puppet"
|
14
|
-
else
|
15
|
-
'/usr/local/bin/puppet'
|
16
|
-
end
|
12
|
+
settings[:puppet_binary] = '/usr/local/bin/puppet'
|
17
13
|
settings[:bootstrapped_to_dir] = octocatalog_diff_dir
|
18
14
|
|
19
15
|
settings
|
@@ -45,23 +45,24 @@ describe DataParser do
|
|
45
45
|
expect { DataParser.eyaml(['foo.eyaml'], 'public.pem', 'private.pem') }.to output("Specified Public X509 and/or Private RSA PKCS7 certs do not exist. EYAML checks will not be executed.\n").to_stderr
|
46
46
|
end
|
47
47
|
it 'puts a bad syntax eyaml file in the error files hash' do
|
48
|
-
|
49
|
-
|
48
|
+
DataParser.eyaml(["#{fixtures_dir}hieradata/syntax.eyaml"], "#{fixtures_dir}keys/public_key.pkcs7.pem", "#{fixtures_dir}keys/private_key.pkcs7.pem")
|
49
|
+
expect(PuppetCheck.files[:errors].keys).to eql(["#{fixtures_dir}hieradata/syntax.eyaml"])
|
50
|
+
expect(PuppetCheck.files[:errors]["#{fixtures_dir}hieradata/syntax.eyaml"].join("\n")).to match(%r{^block sequence entries are not allowed})
|
50
51
|
expect(PuppetCheck.files[:warnings]).to eql({})
|
51
52
|
expect(PuppetCheck.files[:clean]).to eql([])
|
52
53
|
end
|
53
54
|
it 'puts a good eyaml file with potential hiera issues in the warning files array' do
|
54
|
-
|
55
|
+
DataParser.eyaml(["#{fixtures_dir}hieradata/style.eyaml"], "#{fixtures_dir}keys/public_key.pkcs7.pem", "#{fixtures_dir}keys/private_key.pkcs7.pem")
|
55
56
|
expect(PuppetCheck.files[:errors]).to eql({})
|
56
|
-
|
57
|
-
|
57
|
+
expect(PuppetCheck.files[:warnings].keys).to eql(["#{fixtures_dir}hieradata/style.eyaml"])
|
58
|
+
expect(PuppetCheck.files[:warnings]["#{fixtures_dir}hieradata/style.eyaml"].join("\n")).to match(%r{^Value\(s\) missing in key.*\nValue\(s\) missing in key.*\nThe string --- appears more than once in this data and Hiera may fail to parse it correctly})
|
58
59
|
expect(PuppetCheck.files[:clean]).to eql([])
|
59
60
|
end
|
60
61
|
it 'puts a good eyaml file in the clean files array' do
|
61
|
-
|
62
|
+
DataParser.eyaml(["#{fixtures_dir}hieradata/good.eyaml"], "#{fixtures_dir}keys/public_key.pkcs7.pem", "#{fixtures_dir}keys/private_key.pkcs7.pem")
|
62
63
|
expect(PuppetCheck.files[:errors]).to eql({})
|
63
64
|
expect(PuppetCheck.files[:warnings]).to eql({})
|
64
|
-
|
65
|
+
expect(PuppetCheck.files[:clean]).to eql(["#{fixtures_dir}hieradata/good.eyaml"])
|
65
66
|
end
|
66
67
|
end
|
67
68
|
|
@@ -5,19 +5,19 @@ describe OutputResults do
|
|
5
5
|
context '.text' do
|
6
6
|
it 'outputs files with errors' do
|
7
7
|
files = { errors: { 'foo' => ['i had an error'] } }
|
8
|
-
expect { OutputResults.text(files) }.to output("\033[31mThe following files have errors:\033[0m\n-- foo:\ni had an error\n").to_stdout
|
8
|
+
expect { OutputResults.text(files) }.to output("\033[31mThe following files have errors:\033[0m\n-- foo:\ni had an error\n\n").to_stdout
|
9
9
|
end
|
10
10
|
it 'outputs files with warnings' do
|
11
11
|
files = { warnings: { 'foo' => ['i had a warning'] } }
|
12
|
-
expect { OutputResults.text(files) }.to output("\
|
12
|
+
expect { OutputResults.text(files) }.to output("\033[33mThe following files have warnings:\033[0m\n-- foo:\ni had a warning\n\n").to_stdout
|
13
13
|
end
|
14
14
|
it 'outputs files with no errors or warnings' do
|
15
15
|
files = { clean: ['foo'] }
|
16
|
-
expect { OutputResults.text(files) }.to output("\
|
16
|
+
expect { OutputResults.text(files) }.to output("\033[32mThe following files have no errors or warnings:\033[0m\n-- foo\n\n").to_stdout
|
17
17
|
end
|
18
18
|
it 'outputs files that were not processed' do
|
19
19
|
files = { ignored: ['foo'] }
|
20
|
-
expect { OutputResults.text(files) }.to output("\
|
20
|
+
expect { OutputResults.text(files) }.to output("\033[36mThe following files have unrecognized formats and therefore were not processed:\033[0m\n-- foo\n\n").to_stdout
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -17,7 +17,7 @@ describe RegressionCheck do
|
|
17
17
|
|
18
18
|
context '.smoke' do
|
19
19
|
# octocatalog-diff is returning a blank error for these tests
|
20
|
-
unless ENV['
|
20
|
+
unless ENV['CIRCLECI'] == 'true' || ENV['GITHUB_ACTIONS'] == 'true'
|
21
21
|
it 'returns a pass for a successful catalog compilation' do
|
22
22
|
expect { RegressionCheck.smoke(['good.example.com'], "#{octocatalog_diff_dir}octocatalog_diff.cfg.rb") }.not_to raise_exception
|
23
23
|
end
|
@@ -14,11 +14,8 @@ describe RSpecPuppetSupport do
|
|
14
14
|
before(:each) { Dir.chdir(fixtures_dir) }
|
15
15
|
|
16
16
|
it 'creates missing directories, missing site.pp, missing symlinks, and a missing spec_helper' do
|
17
|
-
# travis ci
|
18
|
-
if ENV['TRAVIS'] == 'true'
|
19
|
-
expect { rspec_puppet_setup }.to output("puppetlabs/gruntmaster has an unspecified, or specified but unsupported, download method.\n").to_stderr
|
20
17
|
# circle ci and gh actions
|
21
|
-
|
18
|
+
if ENV['CIRCLECI'] == 'true' || ENV['GITHUB_ACTIONS'] == 'true'
|
22
19
|
expect { rspec_puppet_setup }.to output("git is not installed and cannot be used to retrieve dependency modules\nsubversion is not installed and cannot be used to retrieve dependency modules\npuppetlabs/gruntmaster has an unspecified, or specified but unsupported, download method.\n").to_stderr
|
23
20
|
else
|
24
21
|
expect { rspec_puppet_setup }.to output("subversion is not installed and cannot be used to retrieve dependency modules\npuppetlabs/gruntmaster has an unspecified, or specified but unsupported, download method.\n").to_stderr
|
data/spec/puppet_check_spec.rb
CHANGED
@@ -87,7 +87,11 @@ describe PuppetCheck do
|
|
87
87
|
|
88
88
|
it 'correctly parses one directory and returns all of its files' do
|
89
89
|
dir.each { |file| expect(File.file?(file)).to be true }
|
90
|
-
|
90
|
+
if ENV['CIRCLECI'] == 'true' || ENV['GITHUB_ACTIONS'] == 'true'
|
91
|
+
expect(dir.length).to eql(37)
|
92
|
+
else
|
93
|
+
expect(dir.length).to eql(40)
|
94
|
+
end
|
91
95
|
end
|
92
96
|
|
93
97
|
it 'correctly parses multiple directories and returns all of their files' do
|
data/spec/system/system_spec.rb
CHANGED
@@ -6,10 +6,10 @@ require_relative '../../lib/puppet-check/tasks'
|
|
6
6
|
describe PuppetCheck do
|
7
7
|
context 'executed as a system from the CLI with arguments and various files to be processed' do
|
8
8
|
# see regression_check_spec
|
9
|
-
if ENV['
|
10
|
-
let(:cli) { PuppetCheck::CLI.run(%w[-s --puppet-lint no-hard_tabs-check,no-140chars-check --rubocop Layout/LineLength,Style/Encoding .]) }
|
9
|
+
if ENV['CIRCLECI'] == 'true' || ENV['GITHUB_ACTIONS'] == 'true'
|
10
|
+
let(:cli) { PuppetCheck::CLI.run(%w[-s --puppet-lint no-hard_tabs-check,no-140chars-check --rubocop Layout/LineLength,Style/Encoding --public keys/public_key.pkcs7.pem --private keys/private_key.pkcs7.pem .]) }
|
11
11
|
else
|
12
|
-
let(:cli) { PuppetCheck::CLI.run(%w[-s --puppet-lint no-hard_tabs-check,no-140chars-check --rubocop Layout/LineLength,Style/Encoding --smoke -n good.example.com --octoconfig spec/octocatalog-diff/octocatalog-diff.cfg.rb .]) }
|
12
|
+
let(:cli) { PuppetCheck::CLI.run(%w[-s --puppet-lint no-hard_tabs-check,no-140chars-check --rubocop Layout/LineLength,Style/Encoding --public keys/public_key.pkcs7.pem --private keys/private_key.pkcs7.pem --smoke -n good.example.com --octoconfig spec/octocatalog-diff/octocatalog-diff.cfg.rb .]) }
|
13
13
|
end
|
14
14
|
|
15
15
|
it 'outputs diagnostic results correctly after processing all of the files' do
|
@@ -17,10 +17,10 @@ describe PuppetCheck do
|
|
17
17
|
|
18
18
|
expect { cli }.not_to raise_exception
|
19
19
|
|
20
|
-
expect(PuppetCheck.files[:errors].length).to eql(
|
21
|
-
expect(PuppetCheck.files[:warnings].length).to eql(
|
22
|
-
expect(PuppetCheck.files[:clean].length).to eql(
|
23
|
-
expect(PuppetCheck.files[:ignored].length).to eql(
|
20
|
+
expect(PuppetCheck.files[:errors].length).to eql(11)
|
21
|
+
expect(PuppetCheck.files[:warnings].length).to eql(12)
|
22
|
+
expect(PuppetCheck.files[:clean].length).to eql(14)
|
23
|
+
expect(PuppetCheck.files[:ignored].length).to eql(3)
|
24
24
|
|
25
25
|
expect(cli).to eql(2)
|
26
26
|
end
|
@@ -40,7 +40,7 @@ describe PuppetCheck do
|
|
40
40
|
}
|
41
41
|
settings = { style: true }
|
42
42
|
# see regression_check_spec
|
43
|
-
unless ENV['
|
43
|
+
unless ENV['CIRCLECI'] == 'true' || ENV['GITHUB_ACTIONS'] == 'true'
|
44
44
|
settings[:smoke] = true
|
45
45
|
settings[:octonodes] = %w[good.example.com]
|
46
46
|
settings[:octoconfig] = 'spec/octocatalog-diff/octocatalog-diff.cfg.rb'
|
@@ -50,10 +50,10 @@ describe PuppetCheck do
|
|
50
50
|
expect { Rake::Task[:'puppetcheck:file'].invoke(settings) }.to raise_error(ArgumentError, /Attempt to redefine entity/)
|
51
51
|
|
52
52
|
# current puppet pops limitations no longer allow testing this
|
53
|
-
# expect(PuppetCheck.files[:errors].length).to eql(
|
54
|
-
# expect(PuppetCheck.files[:warnings].length).to eql(
|
55
|
-
# expect(PuppetCheck.files[:clean].length).to eql(
|
56
|
-
# expect(PuppetCheck.files[:ignored].length).to eql(
|
53
|
+
# expect(PuppetCheck.files[:errors].length).to eql(11)
|
54
|
+
# expect(PuppetCheck.files[:warnings].length).to eql(12)
|
55
|
+
# expect(PuppetCheck.files[:clean].length).to eql(14)
|
56
|
+
# expect(PuppetCheck.files[:ignored].length).to eql(3)
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puppet-check
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Schuchard
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-05-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: puppet
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
version: '5.5'
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '
|
22
|
+
version: '9'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,27 +29,21 @@ dependencies:
|
|
29
29
|
version: '5.5'
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '
|
32
|
+
version: '9'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: puppet-lint
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
|
-
- - "
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version: '2.0'
|
40
|
-
- - "<"
|
37
|
+
- - "~>"
|
41
38
|
- !ruby/object:Gem::Version
|
42
|
-
version: '4'
|
39
|
+
version: '4.0'
|
43
40
|
type: :runtime
|
44
41
|
prerelease: false
|
45
42
|
version_requirements: !ruby/object:Gem::Requirement
|
46
43
|
requirements:
|
47
|
-
- - "
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: '2.0'
|
50
|
-
- - "<"
|
44
|
+
- - "~>"
|
51
45
|
- !ruby/object:Gem::Version
|
52
|
-
version: '4'
|
46
|
+
version: '4.0'
|
53
47
|
- !ruby/object:Gem::Dependency
|
54
48
|
name: reek
|
55
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -226,7 +220,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
226
220
|
requirements:
|
227
221
|
- - ">="
|
228
222
|
- !ruby/object:Gem::Version
|
229
|
-
version: 2.
|
223
|
+
version: 2.7.0
|
230
224
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
231
225
|
requirements:
|
232
226
|
- - ">="
|