puppet-check 2.2.2 → 2.3.0
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 +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
|
-
[](https://travis-ci.com/mschuchard/puppet-check)
|
3
|
-
[](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
|

|
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
|
- - ">="
|