abide_dev_utils 0.13.0 → 0.14.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/.github/workflows/mend_ruby.yaml +39 -0
- data/Gemfile.lock +10 -16
- data/abide_dev_utils.gemspec +2 -1
- data/lib/abide_dev_utils/cem/benchmark.rb +8 -5
- data/lib/abide_dev_utils/cem/generate/coverage_report.rb +9 -7
- data/lib/abide_dev_utils/cem/generate/reference.rb +176 -8
- data/lib/abide_dev_utils/cem/validate/strings/base_validator.rb +130 -0
- data/lib/abide_dev_utils/cem/validate/strings/puppet_class_validator.rb +102 -0
- data/lib/abide_dev_utils/cem/validate/strings/puppet_defined_type_validator.rb +18 -0
- data/lib/abide_dev_utils/cem/validate/strings/validation_finding.rb +31 -0
- data/lib/abide_dev_utils/cem/validate/strings.rb +82 -0
- data/lib/abide_dev_utils/cem/validate.rb +2 -1
- data/lib/abide_dev_utils/cem.rb +1 -0
- data/lib/abide_dev_utils/cli/cem.rb +63 -5
- data/lib/abide_dev_utils/markdown.rb +4 -0
- data/lib/abide_dev_utils/output.rb +22 -8
- data/lib/abide_dev_utils/ppt/code_introspection.rb +14 -1
- data/lib/abide_dev_utils/ppt/facter_utils.rb +272 -71
- data/lib/abide_dev_utils/ppt/hiera.rb +5 -4
- data/lib/abide_dev_utils/ppt/strings.rb +183 -0
- data/lib/abide_dev_utils/ppt.rb +10 -10
- data/lib/abide_dev_utils/puppet_strings.rb +108 -0
- data/lib/abide_dev_utils/validate.rb +8 -0
- data/lib/abide_dev_utils/version.rb +1 -1
- data/lib/abide_dev_utils/xccdf/parser/objects.rb +1 -1
- metadata +26 -4
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_validator'
|
4
|
+
require_relative '../../../validate'
|
5
|
+
|
6
|
+
module AbideDevUtils
|
7
|
+
module CEM
|
8
|
+
module Validate
|
9
|
+
module Strings
|
10
|
+
# Validates a Puppet Class from a Puppet Strings hash
|
11
|
+
class PuppetClassValidator < BaseValidator
|
12
|
+
def validate_puppet_class
|
13
|
+
check_text_or_summary
|
14
|
+
check_params
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [Hash] Hash of basic class data to be used in findings
|
18
|
+
def finding_data(**data)
|
19
|
+
data
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Checks if the class has a description or summary
|
25
|
+
def check_text_or_summary
|
26
|
+
valid_desc = AbideDevUtils::Validate.populated_string?(docstring)
|
27
|
+
valid_summary = AbideDevUtils::Validate.populated_string?(find_tag_name('summary')&.text)
|
28
|
+
return if valid_desc || valid_summary
|
29
|
+
|
30
|
+
new_finding(
|
31
|
+
:error,
|
32
|
+
:no_description_or_summary,
|
33
|
+
finding_data(valid_description: valid_desc, valid_summary: valid_summary),
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Checks if the class has parameters and if they are documented
|
38
|
+
def check_params
|
39
|
+
return if parameters.nil? || parameters.empty? # No params
|
40
|
+
|
41
|
+
param_tags = select_tag_name('param')
|
42
|
+
if param_tags.empty?
|
43
|
+
new_finding(:error, :no_parameter_documentation, finding_data(class_parameters: parameters))
|
44
|
+
return
|
45
|
+
end
|
46
|
+
|
47
|
+
parameters.each do |param|
|
48
|
+
param_name, def_val = param
|
49
|
+
check_param(param_name, def_val, param_tags)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Checks if a parameter is documented properly and if it has a correct default value
|
54
|
+
def check_param(param_name, def_val = nil, param_tags = select_tag_name('param'))
|
55
|
+
param_tag = param_tags.find { |t| t.name == param_name }
|
56
|
+
return unless param_documented?(param_name, param_tag)
|
57
|
+
|
58
|
+
valid_param_description?(param_tag)
|
59
|
+
valid_param_types?(param_tag)
|
60
|
+
valid_param_default?(param_tag, def_val)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Checks if a parameter is documented
|
64
|
+
def param_documented?(param_name, param_tag)
|
65
|
+
return true if param_tag
|
66
|
+
|
67
|
+
new_finding(:error, :param_not_documented, finding_data(param: param_name))
|
68
|
+
false
|
69
|
+
end
|
70
|
+
|
71
|
+
# Checks if a parameter has a description
|
72
|
+
def valid_param_description?(param)
|
73
|
+
return true if AbideDevUtils::Validate.populated_string?(param.text)
|
74
|
+
|
75
|
+
new_finding(:error, :param_missing_description, finding_data(param: param.name))
|
76
|
+
false
|
77
|
+
end
|
78
|
+
|
79
|
+
# Checks if a parameter is typed
|
80
|
+
def valid_param_types?(param)
|
81
|
+
unless param.types&.any?
|
82
|
+
new_finding(:error, :param_missing_types, finding_data(param: param.name))
|
83
|
+
return false
|
84
|
+
end
|
85
|
+
true
|
86
|
+
end
|
87
|
+
|
88
|
+
# Checks if a parameter has a default value and if it is correct for the type
|
89
|
+
def valid_param_default?(param, def_val)
|
90
|
+
return true if def_val.nil?
|
91
|
+
|
92
|
+
if param.types.first.start_with?('Optional[') && def_val != 'undef'
|
93
|
+
new_finding(:error, :param_optional_without_undef_default, param: param.name, default_value: def_val, name: name, file: file)
|
94
|
+
return false
|
95
|
+
end
|
96
|
+
true
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'puppet_class_validator'
|
4
|
+
|
5
|
+
module AbideDevUtils
|
6
|
+
module CEM
|
7
|
+
module Validate
|
8
|
+
module Strings
|
9
|
+
# Validates Puppet Defined Type strings objects
|
10
|
+
class PuppetDefinedTypeValidator < PuppetClassValidator
|
11
|
+
def validate_puppet_defined_type
|
12
|
+
validate_puppet_class
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AbideDevUtils
|
4
|
+
module CEM
|
5
|
+
module Validate
|
6
|
+
module Strings
|
7
|
+
# Represents a validation finding (warning or error)
|
8
|
+
class ValidationFinding
|
9
|
+
attr_reader :type, :title, :data
|
10
|
+
|
11
|
+
def initialize(type, title, data)
|
12
|
+
raise ArgumentError, 'type must be :error or :warning' unless %i[error warning].include?(type)
|
13
|
+
|
14
|
+
@type = type.to_sym
|
15
|
+
@title = title.to_sym
|
16
|
+
@data = data
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
"#{@type}: #{@title}: #{@data}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_hash
|
24
|
+
{ type: @type, title: @title, data: @data }
|
25
|
+
end
|
26
|
+
alias to_h to_hash
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../ppt/strings'
|
4
|
+
require_relative 'strings/puppet_class_validator'
|
5
|
+
require_relative 'strings/puppet_defined_type_validator'
|
6
|
+
|
7
|
+
module AbideDevUtils
|
8
|
+
module CEM
|
9
|
+
module Validate
|
10
|
+
# Validation objects and methods for Puppet Strings
|
11
|
+
module Strings
|
12
|
+
# Convenience method to validate Puppet Strings of current module
|
13
|
+
def self.validate(**opts)
|
14
|
+
output = Validator.new(nil, **opts).validate
|
15
|
+
output.transform_values do |results|
|
16
|
+
results.select { |r| r[:errors].any? || r[:warnings].any? }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Holds various validation methods for a AbideDevUtils::Ppt::Strings object
|
21
|
+
class Validator
|
22
|
+
def initialize(puppet_strings = nil, **opts)
|
23
|
+
unless puppet_strings.nil? || puppet_strings.is_a?(AbideDevUtils::Ppt::Strings)
|
24
|
+
raise ArgumentError, 'If puppet_strings is supplied, it must be a AbideDevUtils::Ppt::Strings object'
|
25
|
+
end
|
26
|
+
|
27
|
+
puppet_strings = AbideDevUtils::Ppt::Strings.new(**opts) if puppet_strings.nil?
|
28
|
+
@puppet_strings = puppet_strings
|
29
|
+
end
|
30
|
+
|
31
|
+
# Associate validators with each Puppet Strings object and calls #validate on each
|
32
|
+
# @return [Hash] Hash of validation results
|
33
|
+
def validate
|
34
|
+
AbideDevUtils::Ppt::Strings::REGISTRY_TYPES.each_with_object({}) do |rtype, hsh|
|
35
|
+
next unless rtype.to_s.start_with?('puppet_') && @puppet_strings.respond_to?(rtype)
|
36
|
+
|
37
|
+
hsh[rtype] = @puppet_strings.send(rtype).map do |item|
|
38
|
+
item.validator = validator_for(item)
|
39
|
+
item.validate
|
40
|
+
validation_output(item)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Returns the appropriate validator for a given Puppet Strings object
|
48
|
+
def validator_for(item)
|
49
|
+
case item.type
|
50
|
+
when :puppet_class
|
51
|
+
PuppetClassValidator.new(item)
|
52
|
+
when :puppet_defined_type
|
53
|
+
PuppetDefinedTypeValidator.new(item)
|
54
|
+
else
|
55
|
+
BaseValidator.new(item)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def validation_output(item)
|
60
|
+
{
|
61
|
+
name: item.name,
|
62
|
+
file: item.file,
|
63
|
+
line: item.line,
|
64
|
+
errors: item.errors,
|
65
|
+
warnings: item.warnings,
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
# Validate Puppet Class strings hashes.
|
70
|
+
# @return [Hash] Hash of class names and errors
|
71
|
+
def validate_classes!
|
72
|
+
@puppet_strings.puppet_classes.map! do |klass|
|
73
|
+
klass.validator = PuppetClassValidator.new(klass)
|
74
|
+
klass.validate
|
75
|
+
klass
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/abide_dev_utils/cem.rb
CHANGED
@@ -17,6 +17,7 @@ module Abide
|
|
17
17
|
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: true)
|
18
18
|
add_command(CemGenerate.new)
|
19
19
|
add_command(CemUpdateConfig.new)
|
20
|
+
add_command(CemValidate.new)
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
@@ -69,11 +70,11 @@ module Abide
|
|
69
70
|
quiet = @data.fetch(:quiet, false)
|
70
71
|
console = @data.fetch(:verbose, false) && !quiet
|
71
72
|
generate_opts = {
|
72
|
-
benchmark: @data
|
73
|
-
profile: @data
|
74
|
-
level: @data
|
73
|
+
benchmark: @data[:benchmark],
|
74
|
+
profile: @data[:profile],
|
75
|
+
level: @data[:level],
|
75
76
|
ignore_benchmark_errors: @data.fetch(:ignore_all, false),
|
76
|
-
xccdf_dir: @data
|
77
|
+
xccdf_dir: @data[:xccdf_dir],
|
77
78
|
}
|
78
79
|
AbideDevUtils::Output.simple('Generating coverage report...') unless quiet
|
79
80
|
coverage = AbideDevUtils::CEM::Generate::CoverageReport.generate(format_func: :to_h, opts: generate_opts)
|
@@ -104,11 +105,14 @@ module Abide
|
|
104
105
|
@data[:format] = f
|
105
106
|
end
|
106
107
|
options.on('-v', '--verbose', 'Verbose output') do
|
107
|
-
@data[:
|
108
|
+
@data[:debug] = true
|
108
109
|
end
|
109
110
|
options.on('-q', '--quiet', 'Quiet output') do
|
110
111
|
@data[:quiet] = true
|
111
112
|
end
|
113
|
+
options.on('-s', '--strict', 'Fails if there are any errors') do
|
114
|
+
@data[:strict] = true
|
115
|
+
end
|
112
116
|
end
|
113
117
|
|
114
118
|
def execute
|
@@ -167,5 +171,59 @@ module Abide
|
|
167
171
|
AbideDevUtils::Output.simple(change_report) unless @data[:quiet]
|
168
172
|
end
|
169
173
|
end
|
174
|
+
|
175
|
+
class CemValidate < AbideCommand
|
176
|
+
CMD_NAME = 'validate'
|
177
|
+
CMD_SHORT = 'Validation commands for CEM modules'
|
178
|
+
CMD_LONG = 'Validation commands for CEM modules'
|
179
|
+
def initialize
|
180
|
+
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: true)
|
181
|
+
add_command(CemValidatePuppetStrings.new)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
class CemValidatePuppetStrings < AbideCommand
|
186
|
+
CMD_NAME = 'puppet-strings'
|
187
|
+
CMD_SHORT = 'Validates the Puppet Strings documentation'
|
188
|
+
CMD_LONG = 'Validates the Puppet Strings documentation'
|
189
|
+
def initialize
|
190
|
+
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: false)
|
191
|
+
options.on('-v', '--verbose', 'Verbose output') do
|
192
|
+
@data[:verbose] = true
|
193
|
+
end
|
194
|
+
options.on('-q', '--quiet', 'Quiet output') do
|
195
|
+
@data[:quiet] = true
|
196
|
+
end
|
197
|
+
options.on('-f [FORMAT]', '--format [FORMAT]', 'Format for output (text, json, yaml)') do |f|
|
198
|
+
@data[:format] = f
|
199
|
+
end
|
200
|
+
options.on('-o [FILE]', '--out-file [FILE]', 'Path to save the updated config file') do |o|
|
201
|
+
@data[:out_file] = o
|
202
|
+
end
|
203
|
+
options.on('-s', '--strict', 'Exits with exit code 1 if there are any warnings') do
|
204
|
+
@data[:strict] = true
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def execute
|
209
|
+
@data[:format] ||= 'text'
|
210
|
+
AbideDevUtils::Validate.puppet_module_directory
|
211
|
+
output = AbideDevUtils::CEM::Validate::Strings.validate(**@data)
|
212
|
+
has_errors = false
|
213
|
+
has_warnings = false
|
214
|
+
output.each do |_, i|
|
215
|
+
has_errors = true if i.any? { |j| j[:errors].any? }
|
216
|
+
has_warnings = true if i.any? { |j| j[:warnings].any? }
|
217
|
+
end
|
218
|
+
AbideDevUtils::Output.send(
|
219
|
+
@data[:format].to_sym,
|
220
|
+
output,
|
221
|
+
console: !@data[:quiet],
|
222
|
+
file: @data[:out_file],
|
223
|
+
stringify: true,
|
224
|
+
)
|
225
|
+
exit 1 if has_errors || (has_warnings && @data[:strict])
|
226
|
+
end
|
227
|
+
end
|
170
228
|
end
|
171
229
|
end
|
@@ -10,30 +10,44 @@ require 'abide_dev_utils/files'
|
|
10
10
|
module AbideDevUtils
|
11
11
|
module Output
|
12
12
|
FWRITER = AbideDevUtils::Files::Writer.new
|
13
|
-
def self.simple(msg, stream: $stdout)
|
14
|
-
|
13
|
+
def self.simple(msg, stream: $stdout, **_)
|
14
|
+
case msg
|
15
|
+
when Hash
|
16
|
+
stream.puts JSON.pretty_generate(msg)
|
17
|
+
else
|
18
|
+
stream.puts msg
|
19
|
+
end
|
15
20
|
end
|
16
21
|
|
17
|
-
def self.
|
22
|
+
def self.text(msg, console: false, file: nil, **_)
|
23
|
+
simple(msg) if console
|
24
|
+
FWRITER.write_text(msg, file: file) unless file.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.json(in_obj, console: false, file: nil, pretty: true, **_)
|
18
28
|
AbideDevUtils::Validate.hashable(in_obj)
|
19
29
|
json_out = pretty ? JSON.pretty_generate(in_obj) : JSON.generate(in_obj)
|
20
30
|
simple(json_out) if console
|
21
31
|
FWRITER.write_json(json_out, file: file) unless file.nil?
|
22
32
|
end
|
23
33
|
|
24
|
-
def self.yaml(in_obj, console: false, file: nil)
|
34
|
+
def self.yaml(in_obj, console: false, file: nil, stringify: false, **_)
|
25
35
|
yaml_out = if in_obj.is_a? String
|
26
36
|
in_obj
|
27
37
|
else
|
28
38
|
AbideDevUtils::Validate.hashable(in_obj)
|
29
|
-
|
30
|
-
|
39
|
+
if stringify
|
40
|
+
JSON.parse(JSON.generate(in_obj)).to_yaml
|
41
|
+
else
|
42
|
+
# Use object's #to_yaml method if it exists, convert to hash if not
|
43
|
+
in_obj.respond_to?(:to_yaml) ? in_obj.to_yaml : in_obj.to_h.to_yaml
|
44
|
+
end
|
31
45
|
end
|
32
46
|
simple(yaml_out) if console
|
33
47
|
FWRITER.write_yaml(yaml_out, file: file) unless file.nil?
|
34
48
|
end
|
35
49
|
|
36
|
-
def self.yml(in_obj, console: false, file: nil)
|
50
|
+
def self.yml(in_obj, console: false, file: nil, **_)
|
37
51
|
AbideDevUtils::Validate.hashable(in_obj)
|
38
52
|
# Use object's #to_yaml method if it exists, convert to hash if not
|
39
53
|
yml_out = in_obj.respond_to?(:to_yaml) ? in_obj.to_yaml : in_obj.to_h.to_yaml
|
@@ -41,7 +55,7 @@ module AbideDevUtils
|
|
41
55
|
FWRITER.write_yml(yml_out, file: file) unless file.nil?
|
42
56
|
end
|
43
57
|
|
44
|
-
def self.progress(title: 'Progress', start: 0, total: 100, format: nil)
|
58
|
+
def self.progress(title: 'Progress', start: 0, total: 100, format: nil, **_)
|
45
59
|
ProgressBar.create(title: title, starting_at: start, total: total, format: format)
|
46
60
|
end
|
47
61
|
end
|
@@ -10,17 +10,30 @@ module AbideDevUtils
|
|
10
10
|
attr_reader :manifest_file
|
11
11
|
|
12
12
|
def initialize(manifest_file)
|
13
|
+
@compiler = Puppet::Pal::Compiler.new(nil)
|
13
14
|
@manifest_file = File.expand_path(manifest_file)
|
14
15
|
raise ArgumentError, "File #{@manifest_file} is not a file" unless File.file?(@manifest_file)
|
15
16
|
end
|
16
17
|
|
17
18
|
def ast
|
18
|
-
@ast ||=
|
19
|
+
@ast ||= non_validating_parse_file(manifest_file)
|
19
20
|
end
|
20
21
|
|
21
22
|
def declaration
|
22
23
|
@declaration ||= Declaration.new(ast)
|
23
24
|
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# This method gets around the normal validation performed by the regular
|
29
|
+
# Puppet::Pal::Compiler#parse_file method. This is necessary because, with
|
30
|
+
# validation enabled, the parser will raise errors during parsing if the
|
31
|
+
# file contains any calls to Facter. This is due to facter being disallowed
|
32
|
+
# in Puppet when evaluating the code in a scripting context instead of catalog
|
33
|
+
# compilation, which is what we are doing here.
|
34
|
+
def non_validating_parse_file(file)
|
35
|
+
@compiler.send(:internal_evaluator).parser.parse_file(file)&.model
|
36
|
+
end
|
24
37
|
end
|
25
38
|
|
26
39
|
class Declaration
|