pdk 1.9.0 → 3.2.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 +5 -5
- data/CHANGELOG.md +744 -711
- data/README.md +23 -21
- data/lib/pdk/answer_file.rb +3 -112
- data/lib/pdk/bolt.rb +20 -0
- data/lib/pdk/cli/build.rb +51 -54
- data/lib/pdk/cli/bundle.rb +33 -29
- data/lib/pdk/cli/console.rb +148 -0
- data/lib/pdk/cli/convert.rb +46 -37
- data/lib/pdk/cli/env.rb +51 -0
- data/lib/pdk/cli/errors.rb +4 -3
- data/lib/pdk/cli/exec/command.rb +285 -0
- data/lib/pdk/cli/exec/interactive_command.rb +109 -0
- data/lib/pdk/cli/exec.rb +32 -201
- data/lib/pdk/cli/exec_group.rb +79 -43
- data/lib/pdk/cli/get/config.rb +26 -0
- data/lib/pdk/cli/get.rb +22 -0
- data/lib/pdk/cli/new/class.rb +20 -22
- data/lib/pdk/cli/new/defined_type.rb +21 -21
- data/lib/pdk/cli/new/fact.rb +27 -0
- data/lib/pdk/cli/new/function.rb +27 -0
- data/lib/pdk/cli/new/module.rb +40 -29
- data/lib/pdk/cli/new/provider.rb +18 -18
- data/lib/pdk/cli/new/task.rb +23 -22
- data/lib/pdk/cli/new/test.rb +52 -0
- data/lib/pdk/cli/new/transport.rb +27 -0
- data/lib/pdk/cli/new.rb +15 -9
- data/lib/pdk/cli/release/prep.rb +39 -0
- data/lib/pdk/cli/release/publish.rb +46 -0
- data/lib/pdk/cli/release.rb +185 -0
- data/lib/pdk/cli/remove/config.rb +83 -0
- data/lib/pdk/cli/remove.rb +22 -0
- data/lib/pdk/cli/set/config.rb +121 -0
- data/lib/pdk/cli/set.rb +22 -0
- data/lib/pdk/cli/test/unit.rb +71 -69
- data/lib/pdk/cli/test.rb +9 -8
- data/lib/pdk/cli/update.rb +38 -21
- data/lib/pdk/cli/util/command_redirector.rb +13 -3
- data/lib/pdk/cli/util/interview.rb +25 -9
- data/lib/pdk/cli/util/option_normalizer.rb +6 -6
- data/lib/pdk/cli/util/option_validator.rb +19 -9
- data/lib/pdk/cli/util/spinner.rb +13 -0
- data/lib/pdk/cli/util/update_manager_printer.rb +82 -0
- data/lib/pdk/cli/util.rb +105 -48
- data/lib/pdk/cli/validate.rb +96 -111
- data/lib/pdk/cli.rb +134 -87
- data/lib/pdk/config/errors.rb +5 -0
- data/lib/pdk/config/ini_file.rb +184 -0
- data/lib/pdk/config/ini_file_setting.rb +35 -0
- data/lib/pdk/config/json.rb +35 -0
- data/lib/pdk/config/json_schema_namespace.rb +137 -0
- data/lib/pdk/config/json_schema_setting.rb +51 -0
- data/lib/pdk/config/json_with_schema.rb +47 -0
- data/lib/pdk/config/namespace.rb +362 -0
- data/lib/pdk/config/setting.rb +134 -0
- data/lib/pdk/config/task_schema.json +116 -0
- data/lib/pdk/config/validator.rb +31 -0
- data/lib/pdk/config/yaml.rb +41 -0
- data/lib/pdk/config/yaml_with_schema.rb +51 -0
- data/lib/pdk/config.rb +304 -0
- data/lib/pdk/context/control_repo.rb +61 -0
- data/lib/pdk/context/module.rb +28 -0
- data/lib/pdk/context/none.rb +22 -0
- data/lib/pdk/context.rb +98 -0
- data/lib/pdk/control_repo.rb +89 -0
- data/lib/pdk/generate/defined_type.rb +27 -33
- data/lib/pdk/generate/fact.rb +26 -0
- data/lib/pdk/generate/function.rb +49 -0
- data/lib/pdk/generate/module.rb +160 -153
- data/lib/pdk/generate/provider.rb +16 -69
- data/lib/pdk/generate/puppet_class.rb +27 -32
- data/lib/pdk/generate/puppet_object.rb +100 -159
- data/lib/pdk/generate/task.rb +34 -51
- data/lib/pdk/generate/transport.rb +34 -0
- data/lib/pdk/generate.rb +21 -8
- data/lib/pdk/logger.rb +24 -6
- data/lib/pdk/module/build.rb +125 -37
- data/lib/pdk/module/convert.rb +146 -65
- data/lib/pdk/module/metadata.rb +72 -71
- data/lib/pdk/module/release.rb +255 -0
- data/lib/pdk/module/update.rb +48 -37
- data/lib/pdk/module/update_manager.rb +75 -39
- data/lib/pdk/module.rb +10 -2
- data/lib/pdk/monkey_patches.rb +268 -0
- data/lib/pdk/report/event.rb +36 -48
- data/lib/pdk/report.rb +35 -22
- data/lib/pdk/template/fetcher/git.rb +84 -0
- data/lib/pdk/template/fetcher/local.rb +29 -0
- data/lib/pdk/template/fetcher.rb +100 -0
- data/lib/pdk/template/renderer/v1/legacy_template_dir.rb +108 -0
- data/lib/pdk/template/renderer/v1/renderer.rb +131 -0
- data/lib/pdk/template/renderer/v1/template_file.rb +100 -0
- data/lib/pdk/template/renderer/v1.rb +25 -0
- data/lib/pdk/template/renderer.rb +97 -0
- data/lib/pdk/template/template_dir.rb +67 -0
- data/lib/pdk/template.rb +52 -0
- data/lib/pdk/tests/unit.rb +101 -51
- data/lib/pdk/util/bundler.rb +44 -42
- data/lib/pdk/util/changelog_generator.rb +138 -0
- data/lib/pdk/util/env.rb +48 -0
- data/lib/pdk/util/filesystem.rb +139 -2
- data/lib/pdk/util/git.rb +108 -8
- data/lib/pdk/util/json_finder.rb +86 -0
- data/lib/pdk/util/puppet_strings.rb +125 -0
- data/lib/pdk/util/puppet_version.rb +71 -87
- data/lib/pdk/util/ruby_version.rb +49 -25
- data/lib/pdk/util/template_uri.rb +283 -0
- data/lib/pdk/util/vendored_file.rb +34 -42
- data/lib/pdk/util/version.rb +11 -10
- data/lib/pdk/util/windows/api_types.rb +74 -44
- data/lib/pdk/util/windows/file.rb +31 -27
- data/lib/pdk/util/windows/process.rb +74 -0
- data/lib/pdk/util/windows/string.rb +19 -12
- data/lib/pdk/util/windows.rb +2 -0
- data/lib/pdk/util.rb +111 -124
- data/lib/pdk/validate/control_repo/control_repo_validator_group.rb +23 -0
- data/lib/pdk/validate/control_repo/environment_conf_validator.rb +98 -0
- data/lib/pdk/validate/external_command_validator.rb +213 -0
- data/lib/pdk/validate/internal_ruby_validator.rb +101 -0
- data/lib/pdk/validate/invokable_validator.rb +238 -0
- data/lib/pdk/validate/metadata/metadata_json_lint_validator.rb +84 -0
- data/lib/pdk/validate/metadata/metadata_syntax_validator.rb +76 -0
- data/lib/pdk/validate/metadata/metadata_validator_group.rb +20 -0
- data/lib/pdk/validate/puppet/puppet_epp_validator.rb +131 -0
- data/lib/pdk/validate/puppet/puppet_lint_validator.rb +66 -0
- data/lib/pdk/validate/puppet/puppet_plan_syntax_validator.rb +38 -0
- data/lib/pdk/validate/puppet/puppet_syntax_validator.rb +135 -0
- data/lib/pdk/validate/puppet/puppet_validator_group.rb +22 -0
- data/lib/pdk/validate/ruby/ruby_rubocop_validator.rb +79 -0
- data/lib/pdk/validate/ruby/ruby_validator_group.rb +19 -0
- data/lib/pdk/validate/tasks/tasks_metadata_lint_validator.rb +83 -0
- data/lib/pdk/validate/tasks/tasks_name_validator.rb +45 -0
- data/lib/pdk/validate/tasks/tasks_validator_group.rb +20 -0
- data/lib/pdk/validate/validator.rb +120 -0
- data/lib/pdk/validate/validator_group.rb +107 -0
- data/lib/pdk/validate/yaml/yaml_syntax_validator.rb +86 -0
- data/lib/pdk/validate/yaml/yaml_validator_group.rb +19 -0
- data/lib/pdk/validate.rb +86 -12
- data/lib/pdk/version.rb +2 -2
- data/lib/pdk.rb +60 -10
- metadata +138 -100
- data/lib/pdk/cli/module/build.rb +0 -14
- data/lib/pdk/cli/module/generate.rb +0 -45
- data/lib/pdk/cli/module.rb +0 -14
- data/lib/pdk/i18n.rb +0 -4
- data/lib/pdk/module/templatedir.rb +0 -321
- data/lib/pdk/template_file.rb +0 -95
- data/lib/pdk/validate/base_validator.rb +0 -215
- data/lib/pdk/validate/metadata/metadata_json_lint.rb +0 -86
- data/lib/pdk/validate/metadata/metadata_syntax.rb +0 -109
- data/lib/pdk/validate/metadata_validator.rb +0 -30
- data/lib/pdk/validate/puppet/puppet_lint.rb +0 -67
- data/lib/pdk/validate/puppet/puppet_syntax.rb +0 -112
- data/lib/pdk/validate/puppet_validator.rb +0 -30
- data/lib/pdk/validate/ruby/rubocop.rb +0 -77
- data/lib/pdk/validate/ruby_validator.rb +0 -29
- data/lib/pdk/validate/tasks/metadata_lint.rb +0 -126
- data/lib/pdk/validate/tasks/name.rb +0 -88
- data/lib/pdk/validate/tasks_validator.rb +0 -33
- data/lib/pdk/validate/yaml/syntax.rb +0 -109
- data/lib/pdk/validate/yaml_validator.rb +0 -31
- data/locales/config.yaml +0 -21
- data/locales/pdk.pot +0 -1291
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
require 'pdk'
|
|
2
|
+
|
|
3
|
+
module PDK
|
|
4
|
+
module Validate
|
|
5
|
+
# An abstract validator that runs external commands within a Ruby Bundled environment
|
|
6
|
+
# e.g. `puppet-lint`, or `puppet validate`
|
|
7
|
+
#
|
|
8
|
+
# At a a minimum child classes should implment the `name`, `cmd`, `pattern` and `parse_output` methods
|
|
9
|
+
#
|
|
10
|
+
# An example concrete implementation could look like:
|
|
11
|
+
#
|
|
12
|
+
# module PDK
|
|
13
|
+
# module Validate
|
|
14
|
+
# module Ruby
|
|
15
|
+
# class RubyRubocopValidator < ExternalCommandValidator
|
|
16
|
+
# def name
|
|
17
|
+
# 'rubocop'
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# def cmd
|
|
21
|
+
# 'rubocop'
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# def pattern
|
|
25
|
+
# '**/**.rb'
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# def parse_options(targets)
|
|
29
|
+
# ['--format', 'json']
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
# def parse_output(report, result, _targets)
|
|
33
|
+
# ... ruby code ...
|
|
34
|
+
# report.add_event(
|
|
35
|
+
# line: offense['location']['line'],
|
|
36
|
+
# column: offense['location']['column'],
|
|
37
|
+
# message: offense['message'],
|
|
38
|
+
# severity: offense['corrected'] ? 'corrected' : offense['severity'],
|
|
39
|
+
# test: offense['cop_name'],
|
|
40
|
+
# state: :failure,
|
|
41
|
+
# )
|
|
42
|
+
# end
|
|
43
|
+
# end
|
|
44
|
+
# end
|
|
45
|
+
# end
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# @see PDK::Validate::InvokableValidator
|
|
49
|
+
class ExternalCommandValidator < InvokableValidator
|
|
50
|
+
# @return Array[PDK::CLI::Exec::Command] This is a private implementation attribute used for unit testing
|
|
51
|
+
# @api private
|
|
52
|
+
attr_reader :commands
|
|
53
|
+
|
|
54
|
+
# @see PDK::Validate::Validator.spinner
|
|
55
|
+
def spinner
|
|
56
|
+
# The validator has sub-commands with their own spinners.
|
|
57
|
+
nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Calculates the text of the spinner based on the target list
|
|
61
|
+
# @return [String]
|
|
62
|
+
# @abstract
|
|
63
|
+
def spinner_text_for_targets(targets); end
|
|
64
|
+
|
|
65
|
+
# The name of the command to be run for validation
|
|
66
|
+
# @return [String]
|
|
67
|
+
# @abstract
|
|
68
|
+
def cmd; end
|
|
69
|
+
|
|
70
|
+
# Alternate paths which the command (cmd) may exist in. Typically other ruby gem caches,
|
|
71
|
+
# or packaged installation bin directories.
|
|
72
|
+
# @return [Array[String]]
|
|
73
|
+
# @api private
|
|
74
|
+
def alternate_bin_paths
|
|
75
|
+
[
|
|
76
|
+
PDK::Util::RubyVersion.bin_path,
|
|
77
|
+
File.join(PDK::Util::RubyVersion.gem_home, 'bin'),
|
|
78
|
+
PDK::Util::RubyVersion.gem_paths_raw.map { |gem_path_raw| File.join(gem_path_raw, 'bin') },
|
|
79
|
+
PDK::Util.package_install? ? File.join(PDK::Util.pdk_package_basedir, 'bin') : nil
|
|
80
|
+
].flatten.compact
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# The full path to the command (cmd)
|
|
84
|
+
# Can be overridden in child classes to a non-default path
|
|
85
|
+
# @return [String]
|
|
86
|
+
# @api private
|
|
87
|
+
def cmd_path
|
|
88
|
+
return @cmd_path unless @cmd_path.nil?
|
|
89
|
+
|
|
90
|
+
@cmd_path = File.join(context.root_path, 'bin', cmd)
|
|
91
|
+
# Return the path to the command if it exists on disk, or we have a gemfile (i.e. Bundled install)
|
|
92
|
+
# The Bundle may be created after the prepare_invoke so if the file doesn't exist, it may not be an error
|
|
93
|
+
return @cmd_path if PDK::Util::Filesystem.exist?(@cmd_path) || !PDK::Util::Bundler::BundleHelper.new.gemfile.nil?
|
|
94
|
+
|
|
95
|
+
# But if there is no Gemfile AND cmd doesn't exist in the default path, we need to go searching...
|
|
96
|
+
@cmd_path = alternate_bin_paths.map { |alternate_path| File.join(alternate_path, cmd) }
|
|
97
|
+
.find { |path| PDK::Util::Filesystem.exist?(path) }
|
|
98
|
+
return @cmd_path unless @cmd_path.nil?
|
|
99
|
+
|
|
100
|
+
# If we can't find it anywhere, just let the OS find it
|
|
101
|
+
@cmd_path = cmd
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# An array of command line arguments to pass to the command for validation
|
|
105
|
+
# @return Array[String]
|
|
106
|
+
# @abstract
|
|
107
|
+
def parse_options(_targets)
|
|
108
|
+
[]
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Parses the output from the command and appends formatted events to the report.
|
|
112
|
+
# This is called for each command, which is a group of targets
|
|
113
|
+
#
|
|
114
|
+
# @param report [PDK::Report] The report to add events to
|
|
115
|
+
# @param result [Hash[Symbol => Object]] The result of validation command process
|
|
116
|
+
# @param targets [Array[String]] The targets for this command result
|
|
117
|
+
# @api private
|
|
118
|
+
# @see PDK::CLI::Exec::Command.execute!
|
|
119
|
+
# @abstract
|
|
120
|
+
def parse_output(_report, _result, _targets); end
|
|
121
|
+
|
|
122
|
+
# Prepares for invokation by parsing targets and creating the needed commands.
|
|
123
|
+
# @api private
|
|
124
|
+
# @see PDK::Validate::Validator.prepare_invoke!
|
|
125
|
+
def prepare_invoke!
|
|
126
|
+
return if @prepared
|
|
127
|
+
|
|
128
|
+
super
|
|
129
|
+
|
|
130
|
+
@targets, @skipped, @invalid = parse_targets
|
|
131
|
+
@targets = [] if @targets.nil?
|
|
132
|
+
|
|
133
|
+
target_groups = if @targets.empty? && allow_empty_targets?
|
|
134
|
+
# If we have no targets and we allow empty targets, create an empty target group list
|
|
135
|
+
[[]]
|
|
136
|
+
elsif invoke_style == :per_target
|
|
137
|
+
# If invoking :per_target, split the targets array into an array of
|
|
138
|
+
# single element arrays (one per target).
|
|
139
|
+
@targets.combination(1).to_a.compact
|
|
140
|
+
else
|
|
141
|
+
# Else we're invoking :once, wrap the targets array in another array. This is so we
|
|
142
|
+
# can loop through the invokes with the same logic, regardless of which invoke style
|
|
143
|
+
# is needed.
|
|
144
|
+
@targets.each_slice(1000).to_a.compact
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Register all of the commands for all of the targets
|
|
148
|
+
@commands = []
|
|
149
|
+
target_groups.each do |invokation_targets|
|
|
150
|
+
next if invokation_targets.empty? && !allow_empty_targets?
|
|
151
|
+
|
|
152
|
+
cmd_argv = parse_options(invokation_targets).unshift(cmd_path).compact
|
|
153
|
+
cmd_argv.unshift(File.join(PDK::Util::RubyVersion.bin_path, 'ruby.exe'), '-W0') if Gem.win_platform?
|
|
154
|
+
|
|
155
|
+
command = PDK::CLI::Exec::Command.new(*cmd_argv).tap do |c|
|
|
156
|
+
c.context = :module
|
|
157
|
+
c.environment = { 'PUPPET_GEM_VERSION' => options[:puppet] } if options[:puppet]
|
|
158
|
+
|
|
159
|
+
if spinners_enabled?
|
|
160
|
+
parent_validator = options[:parent_validator]
|
|
161
|
+
if parent_validator.nil? || parent_validator.spinner.nil? || !parent_validator.spinner.is_a?(TTY::Spinner::Multi)
|
|
162
|
+
c.add_spinner(spinner_text_for_targets(invokation_targets))
|
|
163
|
+
else
|
|
164
|
+
spinner = TTY::Spinner.new("[:spinner] #{spinner_text_for_targets(invokation_targets)}", PDK::CLI::Util.spinner_opts_for_platform)
|
|
165
|
+
parent_validator.spinner.register(spinner)
|
|
166
|
+
c.register_spinner(spinner, PDK::CLI::Util.spinner_opts_for_platform)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
@commands << { command: command, invokation_targets: invokation_targets }
|
|
172
|
+
end
|
|
173
|
+
nil
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Invokes the prepared commands as an ExecGroup
|
|
177
|
+
# @see PDK::Validate::Validator.invoke
|
|
178
|
+
def invoke(report)
|
|
179
|
+
prepare_invoke!
|
|
180
|
+
|
|
181
|
+
process_skipped(report, @skipped)
|
|
182
|
+
process_invalid(report, @invalid)
|
|
183
|
+
|
|
184
|
+
# Nothing to execute so return success
|
|
185
|
+
return 0 if @commands.empty?
|
|
186
|
+
|
|
187
|
+
# If there's no Gemfile, then we can't ensure the binstubs are correct
|
|
188
|
+
PDK::Util::Bundler.ensure_binstubs!(cmd) unless PDK::Util::Bundler::BundleHelper.new.gemfile.nil?
|
|
189
|
+
|
|
190
|
+
exec_group = PDK::CLI::ExecGroup.create(name, { parallel: false }, options)
|
|
191
|
+
|
|
192
|
+
# Register all of the commands for all of the targets
|
|
193
|
+
@commands.each do |item|
|
|
194
|
+
command = item[:command]
|
|
195
|
+
invokation_targets = item[:invokation_targets]
|
|
196
|
+
|
|
197
|
+
exec_group.register do
|
|
198
|
+
result = command.execute!
|
|
199
|
+
begin
|
|
200
|
+
parse_output(report, result, invokation_targets.compact)
|
|
201
|
+
rescue PDK::Validate::ParseOutputError => e
|
|
202
|
+
$stderr.puts e.message
|
|
203
|
+
end
|
|
204
|
+
result[:exit_code]
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Now execute and get the return code
|
|
209
|
+
exec_group.exit_code
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
require 'pdk'
|
|
2
|
+
|
|
3
|
+
module PDK
|
|
4
|
+
module Validate
|
|
5
|
+
# An abstract validator that runs ruby code internal to the PDK e.g. JSON and YAML validation, on a single file.
|
|
6
|
+
# The validator code must run within the PDK Ruby environment as opposed to the bundled Ruby environment for a module.
|
|
7
|
+
#
|
|
8
|
+
# At a a minimum child classes should implment the `name`, `pattern` and `validate_target` methods
|
|
9
|
+
#
|
|
10
|
+
# An example concrete implementation could look like:
|
|
11
|
+
#
|
|
12
|
+
# module PDK
|
|
13
|
+
# module Validate
|
|
14
|
+
# module Tasks
|
|
15
|
+
# class TasksNameValidator < InternalRubyValidator
|
|
16
|
+
# def name
|
|
17
|
+
# 'task-name'
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# def pattern
|
|
21
|
+
# 'tasks/**/*'
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# def validate_target(report, target)
|
|
25
|
+
# task_name = File.basename(target, File.extname(target))
|
|
26
|
+
# ... ruby code ...
|
|
27
|
+
# success ? 0 : 1
|
|
28
|
+
# end
|
|
29
|
+
# end
|
|
30
|
+
# end
|
|
31
|
+
# end
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
#
|
|
35
|
+
# @abstract
|
|
36
|
+
# @see PDK::Validate::InvokableValidator
|
|
37
|
+
class InternalRubyValidator < InvokableValidator
|
|
38
|
+
# @see PDK::Validate::Validator.prepare_invoke!
|
|
39
|
+
def prepare_invoke!
|
|
40
|
+
return if @prepared
|
|
41
|
+
|
|
42
|
+
super
|
|
43
|
+
|
|
44
|
+
# Parse the targets
|
|
45
|
+
@targets, @skipped, @invalid = parse_targets
|
|
46
|
+
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Invokes the validator to call `validate_target` on each target
|
|
51
|
+
# @see PDK::Validate::Validator.invoke
|
|
52
|
+
def invoke(report)
|
|
53
|
+
prepare_invoke!
|
|
54
|
+
|
|
55
|
+
process_skipped(report, @skipped)
|
|
56
|
+
process_invalid(report, @invalid)
|
|
57
|
+
|
|
58
|
+
return 0 if @targets.empty?
|
|
59
|
+
|
|
60
|
+
return_val = 0
|
|
61
|
+
|
|
62
|
+
before_validation
|
|
63
|
+
|
|
64
|
+
start_spinner
|
|
65
|
+
@targets.each do |target|
|
|
66
|
+
validation_result = validate_target(report, target)
|
|
67
|
+
if validation_result.nil?
|
|
68
|
+
report.add_event(
|
|
69
|
+
file: target,
|
|
70
|
+
source: name,
|
|
71
|
+
state: :failure,
|
|
72
|
+
severity: 'error',
|
|
73
|
+
message: "Validation did not return an exit code for #{target}"
|
|
74
|
+
)
|
|
75
|
+
validation_result = 1
|
|
76
|
+
end
|
|
77
|
+
return_val = validation_result if validation_result > return_val
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
stop_spinner(return_val.zero?)
|
|
81
|
+
return_val
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Validates a single target
|
|
85
|
+
# It is the responsibility of this method to populate the report with validation messages
|
|
86
|
+
#
|
|
87
|
+
# @param report [PDK::Report] The report to add the events to
|
|
88
|
+
# @param target [String] The target to validate
|
|
89
|
+
#
|
|
90
|
+
# @return [Integer] The exitcode of the validation. Zero indicates success. A non-zero code indicates failure
|
|
91
|
+
# @api private
|
|
92
|
+
# @abstract
|
|
93
|
+
def validate_target(report, target); end
|
|
94
|
+
|
|
95
|
+
# Tasks to run before validation occurs. This is run once every time `.invoke` is called
|
|
96
|
+
# @api private
|
|
97
|
+
# @abstract
|
|
98
|
+
def before_validation; end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
require 'pdk'
|
|
2
|
+
|
|
3
|
+
module PDK
|
|
4
|
+
module Validate
|
|
5
|
+
# A base class for file based validators.
|
|
6
|
+
# This class provides base methods and helpers to help determine the file targets to validate against.
|
|
7
|
+
# Acutal validator implementation should inherit from other child abstract classes e.g. ExternalCommandValdiator
|
|
8
|
+
# @see PDK::Validate::Validator
|
|
9
|
+
# @abstract
|
|
10
|
+
class InvokableValidator < Validator
|
|
11
|
+
# Controls how many times the validator is invoked.
|
|
12
|
+
#
|
|
13
|
+
# :once - The validator will be invoked once and passed all the
|
|
14
|
+
# targets.
|
|
15
|
+
# :per_target - The validator will be invoked for each target
|
|
16
|
+
# separately.
|
|
17
|
+
# @abstract
|
|
18
|
+
def invoke_style
|
|
19
|
+
:once
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Whether this Validator can be invoked in this context. By default any InvokableValidator can work in any Context, except ::None
|
|
23
|
+
# @see PDK::Validate::Validator
|
|
24
|
+
def valid_in_context?
|
|
25
|
+
!context.is_a?(PDK::Context::None)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# An array, or a string, of glob patterns to use to find targets
|
|
29
|
+
# @return [Array[String], String]
|
|
30
|
+
# @abstract
|
|
31
|
+
def pattern; end
|
|
32
|
+
|
|
33
|
+
# An array, or a string, of glob patterns to use to ignore targets
|
|
34
|
+
# @return [Array[String], String, Nil]
|
|
35
|
+
# @abstract
|
|
36
|
+
def pattern_ignore; end
|
|
37
|
+
|
|
38
|
+
# @see PDK::Validate::Validator.prepare_invoke!
|
|
39
|
+
def prepare_invoke!
|
|
40
|
+
return if @prepared
|
|
41
|
+
|
|
42
|
+
super
|
|
43
|
+
|
|
44
|
+
# Register the spinner
|
|
45
|
+
spinner
|
|
46
|
+
nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Parses the target strings provided from the CLI
|
|
50
|
+
#
|
|
51
|
+
# @param options [Hash] A Hash containing the input options from the CLI.
|
|
52
|
+
#
|
|
53
|
+
# @return targets [Array] An Array of Strings containing target file paths
|
|
54
|
+
# for the validator to validate.
|
|
55
|
+
# @return skipped [Array] An Array of Strings containing targets
|
|
56
|
+
# that are skipped due to target not containing
|
|
57
|
+
# any files that can be validated by the validator.
|
|
58
|
+
# @return invalid [Array] An Array of Strings containing targets that do
|
|
59
|
+
# not exist, and will not be run by validator.
|
|
60
|
+
def parse_targets
|
|
61
|
+
requested_targets = options.fetch(:targets, [])
|
|
62
|
+
# If no targets are specified and empty targets are allowed return with an empty list.
|
|
63
|
+
# It will be up to the validator (and whatever validation tool it uses) to determine the
|
|
64
|
+
# targets. For example, using rubocop with no targets, will allow rubocop to determine the
|
|
65
|
+
# target list using it's .rubocop.yml file
|
|
66
|
+
return [[], [], []] if requested_targets.empty? && allow_empty_targets?
|
|
67
|
+
|
|
68
|
+
# If no targets are specified, then we will run validations from the base context directory.
|
|
69
|
+
targets = requested_targets.empty? ? [context.root_path] : requested_targets
|
|
70
|
+
targets.map! { |r| r.gsub(File::ALT_SEPARATOR, File::SEPARATOR) } if File::ALT_SEPARATOR
|
|
71
|
+
|
|
72
|
+
# If this validator is not valid in this context then skip all of the targets
|
|
73
|
+
return [[], targets, []] unless valid_in_context?
|
|
74
|
+
|
|
75
|
+
skipped = []
|
|
76
|
+
invalid = []
|
|
77
|
+
matched = targets.filter_map do |target|
|
|
78
|
+
if pattern.nil?
|
|
79
|
+
target
|
|
80
|
+
else
|
|
81
|
+
if PDK::Util::Filesystem.directory?(target) # rubocop:disable Style/IfInsideElse
|
|
82
|
+
target_root = context.root_path
|
|
83
|
+
pattern_glob = Array(pattern).map { |p| PDK::Util::Filesystem.glob(File.join(target_root, p), File::FNM_DOTMATCH) }
|
|
84
|
+
target_list = pattern_glob.flatten
|
|
85
|
+
.select { |glob| PDK::Util::Filesystem.fnmatch(File.join(PDK::Util::Filesystem.expand_path(PDK::Util.canonical_path(target)), '*'), glob, File::FNM_DOTMATCH) }
|
|
86
|
+
.map { |glob| Pathname.new(glob).relative_path_from(Pathname.new(context.root_path)).to_s }
|
|
87
|
+
|
|
88
|
+
ignore_list = ignore_pathspec
|
|
89
|
+
target_list = target_list.reject { |file| ignore_list.match(file) }
|
|
90
|
+
|
|
91
|
+
if target_list.flatten.empty?
|
|
92
|
+
PDK.logger.info(format('Validator \'%{validator}\' skipped for \'%{target}\'. No files matching \'%{pattern}\' found to validate.', validator: name, target: target, pattern: pattern))
|
|
93
|
+
|
|
94
|
+
skipped << target
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
target_list
|
|
98
|
+
elsif PDK::Util::Filesystem.file?(target)
|
|
99
|
+
if (Array(pattern).include? target) || fnmatch?(pattern, target)
|
|
100
|
+
target
|
|
101
|
+
else
|
|
102
|
+
skipped << target
|
|
103
|
+
next
|
|
104
|
+
end
|
|
105
|
+
else
|
|
106
|
+
invalid << target
|
|
107
|
+
next
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end.flatten.uniq
|
|
111
|
+
[matched, skipped, invalid]
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Matches a target against a pattern
|
|
115
|
+
# @param pattern [String, Array[String]] The pattern to match against
|
|
116
|
+
# @return [Boolean]
|
|
117
|
+
def fnmatch?(pattern, target)
|
|
118
|
+
Array(pattern).any? { |p| PDK::Util::Filesystem.fnmatch(PDK::Util::Filesystem.expand_path(p), PDK::Util::Filesystem.expand_path(target), File::FNM_DOTMATCH) }
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Whether the target parsing ignores "dotfiles" (e.g. .gitignore or .pdkignore) which are considered hidden files in POSIX
|
|
122
|
+
# @return [Boolean]
|
|
123
|
+
# @abstract
|
|
124
|
+
def ignore_dotfiles?
|
|
125
|
+
true
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# @see PDK::Validate::Validator.spinner_text
|
|
129
|
+
# @abstract
|
|
130
|
+
def spinner_text
|
|
131
|
+
format('Running %{name} validator ...', name: name)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# @see PDK::Validate::Validator.spinner
|
|
135
|
+
def spinner
|
|
136
|
+
return nil unless spinners_enabled?
|
|
137
|
+
return @spinner unless @spinner.nil?
|
|
138
|
+
|
|
139
|
+
require 'pdk/cli/util/spinner'
|
|
140
|
+
|
|
141
|
+
@spinner = TTY::Spinner.new("[:spinner] #{spinner_text}", PDK::CLI::Util.spinner_opts_for_platform)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Process any targets that were skipped by the validator and add the events to the validation report
|
|
145
|
+
# @param report [PDK::Report] The report to add the events to
|
|
146
|
+
# @param skipped [Array[String]] The list of skipped targets
|
|
147
|
+
def process_skipped(report, skipped = [])
|
|
148
|
+
skipped.each do |skipped_target|
|
|
149
|
+
PDK.logger.debug(format('%{validator}: Skipped \'%{target}\'. Target does not contain any files to validate (%{pattern}).', validator: name, target: skipped_target, pattern: pattern))
|
|
150
|
+
report.add_event(
|
|
151
|
+
file: skipped_target,
|
|
152
|
+
source: name,
|
|
153
|
+
message: format('Target does not contain any files to validate (%{pattern}).', pattern: pattern),
|
|
154
|
+
severity: :info,
|
|
155
|
+
state: :skipped
|
|
156
|
+
)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Process any targets that were invalid by the validator and add the events to the validation report
|
|
161
|
+
# @param report [PDK::Report] The report to add the events to
|
|
162
|
+
# @param invalid [Array[String]] The list of invalid targets
|
|
163
|
+
def process_invalid(report, invalid = [])
|
|
164
|
+
invalid.each do |invalid_target|
|
|
165
|
+
PDK.logger.debug(format('%{validator}: Skipped \'%{target}\'. Target file not found.', validator: name, target: invalid_target))
|
|
166
|
+
report.add_event(
|
|
167
|
+
file: invalid_target,
|
|
168
|
+
source: name,
|
|
169
|
+
message: 'File does not exist.',
|
|
170
|
+
severity: :error,
|
|
171
|
+
state: :error
|
|
172
|
+
)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Controls how the validator behaves if not passed any targets.
|
|
177
|
+
#
|
|
178
|
+
# true - PDK will not pass the globbed targets to the validator command
|
|
179
|
+
# and it will instead rely on the underlying tool to find its
|
|
180
|
+
# own default targets.
|
|
181
|
+
# false - PDK will pass the globbed targets to the validator command.
|
|
182
|
+
# @abstract
|
|
183
|
+
def allow_empty_targets?
|
|
184
|
+
false
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
protected
|
|
188
|
+
|
|
189
|
+
# Takes the pattern used in a module context and transforms it depending on the
|
|
190
|
+
# context e.g. A Control Repo will use the module pattern in each module path
|
|
191
|
+
#
|
|
192
|
+
# @param [Array, String] The pattern when used in the module root. Does not start with '/'
|
|
193
|
+
#
|
|
194
|
+
# @return [Array[String]]
|
|
195
|
+
def contextual_pattern(module_pattern)
|
|
196
|
+
module_pattern = [module_pattern] unless module_pattern.is_a?(Array)
|
|
197
|
+
return module_pattern unless context.is_a?(PDK::Context::ControlRepo)
|
|
198
|
+
|
|
199
|
+
context.actualized_module_paths.map { |mod_path| module_pattern.map { |pat_path| "#{mod_path}/*/#{pat_path}" } }.flatten
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
private
|
|
203
|
+
|
|
204
|
+
# Helper method to collate the default ignored paths
|
|
205
|
+
# @return [PathSpec] Paths to ignore
|
|
206
|
+
def ignore_pathspec
|
|
207
|
+
ignore_pathspec = case context
|
|
208
|
+
when PDK::Context::Module
|
|
209
|
+
require 'pdk/module'
|
|
210
|
+
PDK::Module.default_ignored_pathspec(ignore_dotfiles?)
|
|
211
|
+
when PDK::Context::ControlRepo
|
|
212
|
+
require 'pdk/control_repo'
|
|
213
|
+
PDK::ControlRepo.default_ignored_pathspec(ignore_dotfiles?)
|
|
214
|
+
else
|
|
215
|
+
PathSpec.new.tap do |ps|
|
|
216
|
+
ps.add('.*') if ignore_dotfiles?
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
unless pattern_ignore.nil?
|
|
221
|
+
Array(pattern_ignore).each do |pattern|
|
|
222
|
+
ignore_pathspec.add(pattern)
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# block will always be [] because it is intialized in config
|
|
227
|
+
ignore_files = PDK.config.get_within_scopes('validate.ignore')
|
|
228
|
+
unless ignore_files.nil? || ignore_files.empty?
|
|
229
|
+
Array(ignore_files).each do |pattern|
|
|
230
|
+
ignore_pathspec.add(pattern)
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
ignore_pathspec
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require 'pdk'
|
|
2
|
+
|
|
3
|
+
module PDK
|
|
4
|
+
module Validate
|
|
5
|
+
module Metadata
|
|
6
|
+
class MetadataJSONLintValidator < ExternalCommandValidator
|
|
7
|
+
# Validate each metadata file separately, as metadata-json-lint does not
|
|
8
|
+
# support multiple targets.
|
|
9
|
+
def invoke_style
|
|
10
|
+
:per_target
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def name
|
|
14
|
+
'metadata-json-lint'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def cmd
|
|
18
|
+
'metadata-json-lint'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def spinner_text_for_targets(targets)
|
|
22
|
+
format('Checking module metadata style (%{targets}).', targets: PDK::Util.targets_relative_to_pwd(targets.flatten).join(' '))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def pattern
|
|
26
|
+
contextual_pattern('metadata.json')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def parse_options(targets)
|
|
30
|
+
cmd_options = ['--format', 'json']
|
|
31
|
+
cmd_options << '--strict-dependencies'
|
|
32
|
+
|
|
33
|
+
cmd_options.concat(targets)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def parse_output(report, result, targets)
|
|
37
|
+
raise ArgumentError, 'More than 1 target provided to PDK::Validate::MetadataJSONLintValidator.' if targets.count > 1
|
|
38
|
+
|
|
39
|
+
if result[:stdout].strip.empty?
|
|
40
|
+
# metadata-json-lint will print nothing if there are no problems with
|
|
41
|
+
# the file being linted. This should be handled separately to
|
|
42
|
+
# metadata-json-lint generating output that can not be parsed as JSON
|
|
43
|
+
# (unhandled exception in metadata-json-lint).
|
|
44
|
+
json_data = {}
|
|
45
|
+
else
|
|
46
|
+
begin
|
|
47
|
+
json_data = JSON.parse(result[:stdout])
|
|
48
|
+
rescue JSON::ParserError
|
|
49
|
+
raise PDK::Validate::ParseOutputError, result[:stdout]
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
if json_data.empty?
|
|
54
|
+
report.add_event(
|
|
55
|
+
file: targets.first,
|
|
56
|
+
source: name,
|
|
57
|
+
state: :passed,
|
|
58
|
+
severity: :ok
|
|
59
|
+
)
|
|
60
|
+
else
|
|
61
|
+
json_data.delete('result')
|
|
62
|
+
json_data.each_key do |type|
|
|
63
|
+
json_data[type].each do |offense|
|
|
64
|
+
# metadata-json-lint groups the offenses by type, so the type ends
|
|
65
|
+
# up being `warnings` or `errors`. We want to convert that to the
|
|
66
|
+
# singular noun for the event.
|
|
67
|
+
event_type = type[/\A(.+?)s?\Z/, 1]
|
|
68
|
+
|
|
69
|
+
report.add_event(
|
|
70
|
+
file: targets.first,
|
|
71
|
+
source: name,
|
|
72
|
+
message: offense['msg'],
|
|
73
|
+
test: offense['check'],
|
|
74
|
+
severity: event_type,
|
|
75
|
+
state: :failure
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|