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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a8202ec2f014dfda495943251a944a05a2f02dd411d4432d4ae2fa670090147f
4
- data.tar.gz: 79926aa5d6f7a5b4abdf5141a110d08f2dd2d678439b89a3a4193ba957b3eb36
3
+ metadata.gz: caea7da11b0b884512bc9e078c71caece3bfb2045f5798c55b41201b948e5d3f
4
+ data.tar.gz: 6e4eae239a2cbbeda1709741c523161720dd8b28399d9e1e5cae6add54cef59b
5
5
  SHA512:
6
- metadata.gz: 5881b391a02f1f7afd9d902e16c578fa02128511275e9c670767ec2c1ebcce263a1137f36244c1e8bb118da057719f955727df577a6df421d66966cb39d3d054
7
- data.tar.gz: d74c947acf7b79cbc0f82ba7280617495eb2bfd7574e6e22791b59bffa8a9fa4228aec7194fd3c8c9b77e99993a10dc9886074e43c3166da69b2d0ccccd1a052
6
+ metadata.gz: 18d8d802b337a1b47de63aa25a4f0c2b7e6f6f6678047b2f941a28766ee7d1a18f07244b287faf37747170d170230dfb58073f67a63dce95513421acd125ca5a
7
+ data.tar.gz: '09031de8c432747e7967241ff0abd4290d34d38892eb4df0443e3f7e88acd5d30c177fbabf811a6958874533fe951ea795965acea0617d21e0f2f28832987e17'
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ### 2.3.0
2
+ - Minimum Ruby version increased to 2.7.
3
+ - Support Puppet 8.
4
+ - Upgrade EYAML validation functionality from experimental to beta.
5
+ - Code execution optimization.
6
+
1
7
  ### 2.2.2
2
8
  - Workaround Ruby passbyref issue mutating an immutable.
3
9
 
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. The code is blocked in the current release (the files will continue to be treated as unrecognized) and will be unblocked when the feature is finished in a future version.
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)
@@ -29,7 +29,9 @@ class PuppetCheck::CLI
29
29
 
30
30
  # base options
31
31
  opts.on('--version', 'Display the current version.') do
32
- puts 'puppet-check 2.2.2'
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
- # grab all encoded portions of the eyaml
46
+ # check encoded yaml syntax
47
+ parsed = YAML.load_file(file)
47
48
 
48
- # decrypt the encoded portions
49
- decrypted = OpenSSL::PKCS7.new(File.read(file)).decrypt(rsa, x509)
49
+ # extract encoded values
50
+ # ENC[PKCS7]
50
51
 
51
- # insert decrypted portions back into eyaml (pass into loader below)
52
+ # decrypt the encoded yaml
53
+ # decrypted = OpenSSL::PKCS7.new(File.read(file)).decrypt(rsa, x509)
52
54
 
53
- # check yaml syntax
54
- begin
55
- parsed = YAML.load_file(decrypted)
56
- rescue StandardError => err
57
- PuppetCheck.files[:errors][file] = err.to_s.gsub("(#{file}): ", '').split("\n")
58
- else
59
- warnings = []
60
-
61
- # perform some rudimentary hiera checks if data exists and is hieradata
62
- warnings = hiera(parsed, file) if parsed
63
-
64
- next PuppetCheck.files[:warnings][file] = warnings unless warnings.empty?
65
- PuppetCheck.files[:clean].push(file.to_s)
66
- end
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
- begin
83
- parsed = JSON.parse(File.read(file))
84
- rescue JSON::ParserError => err
85
- PuppetCheck.files[:errors][file] = err.to_s.lines.first.strip.split("\n")
86
- else
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
- # check requirements and dependencies keys
103
- REQ_DEP_KEYS.each do |key|
104
- # skip if key is missing or value is an empty string, array, or hash
105
- next if !parsed.key?(key) || parsed[key].empty?
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
- # check that dependencies and requirements are an array of hashes
108
- next errors.push("Field '#{key}' is not an array of hashes.") unless (parsed[key].is_a? Array) && (parsed[key][0].is_a? Hash)
96
+ # check for errors
97
+ errors = []
109
98
 
110
- # check dependencies and requirements values
111
- names = []
112
- parsed[key].each do |req_dep|
113
- # check for duplicate dependencies and requirements
114
- name = req_dep['name']
115
- next errors.push("Duplicate #{key} on #{name}.") if names.include?(name)
116
- names << name
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
- # warn and skip if key is missing
119
- next warnings.push("'#{req_dep['name']}' is missing a 'version_requirement' key.") if req_dep['version_requirement'].nil?
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
- # warn and skip if no upper bound
122
- next warnings.push("'#{req_dep['name']}' is missing an upper bound.") unless req_dep['version_requirement'].include?('<')
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
- # check for semantic versioning
125
- if key == 'dependencies' && req_dep['version_requirement'] !~ /\d+\.\d+\.\d+.*\d+\.\d+\.\d+/
126
- warnings.push("'#{req_dep['name']}' has non-semantic versioning in its 'version_requirement' key.")
127
- end
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
- # check for deprecated fields
132
- DEPRECATED_KEYS.each do |key|
133
- errors.push("Deprecated field '#{key}' found.") if parsed.key?(key)
134
- end
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
- # check for summary under 144 character
137
- errors.push('Summary exceeds 144 characters.') if parsed.key?('summary') && parsed['summary'].size > 144
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
- next PuppetCheck.files[:errors][file] = errors unless errors.empty?
141
+ next PuppetCheck.files[:errors][file] = errors unless errors.empty?
140
142
 
141
- # check for warnings
142
- # check for operatingsystem_support hash array
143
- if parsed.key?('operatingsystem_support')
144
- # check if operatingsystem_support array is actually empty
145
- if !(parsed['operatingsystem_support'].is_a? Array) || parsed['operatingsystem_support'].empty? || (!parsed['operatingsystem_support'].empty? && !(parsed['operatingsystem_support'][0].is_a? Hash))
146
- warnings.push('Recommended field \'operatingsystem\' not found.')
147
- warnings.push('Recommended field \'operatingsystemrelease\' not found.')
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
- # check for operatingsystem string
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
- # check for spdx license
168
- if parsed.key?('license') && !Gem::Licenses.match?(parsed['license']) && parsed['license'] !~ /[pP]roprietary/
169
- warnings.push("License identifier '#{parsed['license']}' is not in the SPDX list: http://spdx.org/licenses/")
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('input_method value is not a String')
162
+ warnings.push('Recommended field \'operatingsystemrelease\' not found.')
181
163
  end
182
164
  end
183
- # check that parameters is a hash
184
- if parsed.key?('parameters') && !parsed['parameters'].is_a?(Hash)
185
- warnings.push('parameters value is not a Hash')
186
- end
187
- # check that puppet_task_version is an integer
188
- if parsed.key?('puppet_task_version') && !parsed['puppet_task_version'].is_a?(Integer)
189
- warnings.push('puppet_task_version value is not an Integer')
190
- end
191
- # check that supports_noop is a boolean
192
- if parsed.key?('supports_noop') && !(parsed['supports_noop'].is_a?(TrueClass) || parsed['supports_noop'].is_a?(FalseClass))
193
- warnings.push('supports_noop value is not a Boolean')
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
- next PuppetCheck.files[:warnings][file] = warnings unless warnings.empty?
201
- PuppetCheck.files[:clean].push(file.to_s)
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(/---/).count >= 2
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
- print "\n\033[36mThe following files have unrecognized formats and therefore were not processed:\033[0m\n-- "
49
- puts files[:ignored].join("\n-- ")
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
- # prepare the PuppetLint object for style checks
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
- # 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]) }
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
- # initialize ssl keys for eyaml checks
82
- settings[:public] ||= nil
83
- settings[:private] ||= nil
84
-
85
- # initialize output format option
86
- settings[:output_format] ||= 'text'
87
-
88
- # initialize octocatalog-diff options
89
- settings[:octoconfig] ||= '.octocatalog-diff.cfg.rb'
90
- settings[:octonodes] ||= %w[localhost.localdomain]
91
-
92
- # initialize style arg arrays
93
- settings[:puppetlint_args] ||= []
94
- settings[:rubocop_args] ||= []
95
-
96
- # return update settings
97
- settings
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
- files.concat(Dir.glob("#{path}/**/*").select { |subpath| File.file?(subpath) })
109
- elsif File.file?(path)
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
- # do not process fixtures, check that at least one file was found, and remove double slashes
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! { |file| file.gsub('//', '/') }
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, settings)
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, settings[:style], settings[:puppetlint_args]) unless manifests.empty?
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, settings[:style], settings[:rubocop_args]) unless rubies.empty?
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
- # eyamls, files = files.partition { |file| File.extname(file) =~ /\.eya?ml$/ }
146
- # DataParser.eyaml(eyamls, public, private) unless eyamls.empty?
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, settings[:style], settings[:rubocop_args]) unless librarians.empty?
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] = if ENV['TRAVIS'] == 'true'
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
- # 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][0]).to match(%r{^#{fixtures_dir}hieradata/syntax.eyaml:\nblock sequence entries are not allowed})
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
- # DataParser.eyaml(["#{fixtures_dir}hieradata/style.eyaml'], fixtures_dir + 'keys/public_key.pkcs7.pem', fixtures_dir + 'keys/private_key.pkcs7.pem")
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
- # expect(PuppetCheck.files[:warnings].keys).to eql(["#{fixtures_dir}hieradata/style.eyaml"])
57
- # expect(PuppetCheck.files[:warnings]["#{fixtures_dir}hieradata/style.eyaml"]).to match(%r{^Value\(s\) missing in key.*\nValue\(s\) missing in key.*\nThe string --- appears more than once in this data and Hiera will fail to parse it correctly})
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
- # DataParser.eyaml(["#{fixtures_dir}hieradata/good.eyaml'], fixtures_dir + 'keys/public_key.pkcs7.pem', fixtures_dir + 'keys/private_key.pkcs7.pem")
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
- # expect(PuppetCheck.files[:clean]).to eql(["#{fixtures_dir}hieradata/good.eyaml"])
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("\n\033[33mThe following files have warnings:\033[0m\n-- foo:\ni had a warning\n").to_stdout
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("\n\033[32mThe following files have no errors or warnings:\033[0m\n-- foo\n").to_stdout
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("\n\033[36mThe following files have unrecognized formats and therefore were not processed:\033[0m\n-- foo\n").to_stdout
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['TRAVIS'] == 'true' || ENV['CIRCLECI'] == 'true' || ENV['GITHUB_ACTIONS'] == 'true'
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
- elsif ENV['CIRCLECI'] == 'true' || ENV['GITHUB_ACTIONS'] == 'true'
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
@@ -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
- expect(dir.length).to eql(37)
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
@@ -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['TRAVIS'] == 'true' || 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 .]) }
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(10)
21
- expect(PuppetCheck.files[:warnings].length).to eql(11)
22
- expect(PuppetCheck.files[:clean].length).to eql(13)
23
- expect(PuppetCheck.files[:ignored].length).to eql(6)
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['TRAVIS'] == 'true' || ENV['CIRCLECI'] == 'true' || ENV['GITHUB_ACTIONS'] == 'true'
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(10)
54
- # expect(PuppetCheck.files[:warnings].length).to eql(11)
55
- # expect(PuppetCheck.files[:clean].length).to eql(13)
56
- # expect(PuppetCheck.files[:ignored].length).to eql(6)
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.2.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: 2023-03-27 00:00:00.000000000 Z
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: '8'
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: '8'
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.6.0
223
+ version: 2.7.0
230
224
  required_rubygems_version: !ruby/object:Gem::Requirement
231
225
  requirements:
232
226
  - - ">="