pdk 2.3.0 → 2.4.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.
Files changed (153) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1329 -1321
  3. data/LICENSE +201 -201
  4. data/README.md +163 -163
  5. data/exe/pdk +10 -10
  6. data/lib/pdk/analytics/client/google_analytics.rb +143 -143
  7. data/lib/pdk/analytics/client/noop.rb +25 -25
  8. data/lib/pdk/analytics/util.rb +19 -19
  9. data/lib/pdk/analytics.rb +30 -30
  10. data/lib/pdk/answer_file.rb +12 -12
  11. data/lib/pdk/bolt.rb +19 -19
  12. data/lib/pdk/cli/build.rb +82 -82
  13. data/lib/pdk/cli/bundle.rb +48 -48
  14. data/lib/pdk/cli/config/get.rb +26 -26
  15. data/lib/pdk/cli/config.rb +22 -22
  16. data/lib/pdk/cli/console.rb +148 -148
  17. data/lib/pdk/cli/convert.rb +52 -52
  18. data/lib/pdk/cli/env.rb +52 -52
  19. data/lib/pdk/cli/errors.rb +25 -25
  20. data/lib/pdk/cli/exec/command.rb +293 -293
  21. data/lib/pdk/cli/exec/interactive_command.rb +114 -114
  22. data/lib/pdk/cli/exec.rb +84 -84
  23. data/lib/pdk/cli/exec_group.rb +104 -104
  24. data/lib/pdk/cli/get/config.rb +24 -24
  25. data/lib/pdk/cli/get.rb +20 -20
  26. data/lib/pdk/cli/module/build.rb +12 -12
  27. data/lib/pdk/cli/module/generate.rb +47 -47
  28. data/lib/pdk/cli/module.rb +14 -14
  29. data/lib/pdk/cli/new/class.rb +32 -32
  30. data/lib/pdk/cli/new/defined_type.rb +32 -32
  31. data/lib/pdk/cli/new/fact.rb +29 -29
  32. data/lib/pdk/cli/new/function.rb +29 -29
  33. data/lib/pdk/cli/new/module.rb +53 -53
  34. data/lib/pdk/cli/new/provider.rb +29 -29
  35. data/lib/pdk/cli/new/task.rb +34 -34
  36. data/lib/pdk/cli/new/test.rb +52 -52
  37. data/lib/pdk/cli/new/transport.rb +27 -27
  38. data/lib/pdk/cli/new.rb +21 -21
  39. data/lib/pdk/cli/release/prep.rb +39 -39
  40. data/lib/pdk/cli/release/publish.rb +50 -50
  41. data/lib/pdk/cli/release.rb +194 -194
  42. data/lib/pdk/cli/remove/config.rb +80 -80
  43. data/lib/pdk/cli/remove.rb +20 -20
  44. data/lib/pdk/cli/set/config.rb +119 -119
  45. data/lib/pdk/cli/set.rb +20 -20
  46. data/lib/pdk/cli/test/unit.rb +90 -90
  47. data/lib/pdk/cli/test.rb +11 -11
  48. data/lib/pdk/cli/update.rb +64 -64
  49. data/lib/pdk/cli/util/command_redirector.rb +27 -27
  50. data/lib/pdk/cli/util/interview.rb +72 -72
  51. data/lib/pdk/cli/util/option_normalizer.rb +55 -55
  52. data/lib/pdk/cli/util/option_validator.rb +68 -68
  53. data/lib/pdk/cli/util/spinner.rb +13 -13
  54. data/lib/pdk/cli/util/update_manager_printer.rb +82 -82
  55. data/lib/pdk/cli/util.rb +305 -305
  56. data/lib/pdk/cli/validate.rb +116 -116
  57. data/lib/pdk/cli.rb +175 -175
  58. data/lib/pdk/config/analytics_schema.json +26 -26
  59. data/lib/pdk/config/errors.rb +5 -5
  60. data/lib/pdk/config/ini_file.rb +183 -183
  61. data/lib/pdk/config/ini_file_setting.rb +39 -39
  62. data/lib/pdk/config/json.rb +34 -34
  63. data/lib/pdk/config/json_schema_namespace.rb +142 -142
  64. data/lib/pdk/config/json_schema_setting.rb +53 -53
  65. data/lib/pdk/config/json_with_schema.rb +49 -49
  66. data/lib/pdk/config/namespace.rb +354 -354
  67. data/lib/pdk/config/setting.rb +135 -135
  68. data/lib/pdk/config/validator.rb +31 -31
  69. data/lib/pdk/config/yaml.rb +46 -46
  70. data/lib/pdk/config/yaml_with_schema.rb +59 -59
  71. data/lib/pdk/config.rb +390 -390
  72. data/lib/pdk/context/control_repo.rb +60 -60
  73. data/lib/pdk/context/module.rb +28 -28
  74. data/lib/pdk/context/none.rb +22 -22
  75. data/lib/pdk/context.rb +99 -99
  76. data/lib/pdk/control_repo.rb +90 -90
  77. data/lib/pdk/generate/defined_type.rb +43 -43
  78. data/lib/pdk/generate/fact.rb +25 -25
  79. data/lib/pdk/generate/function.rb +48 -48
  80. data/lib/pdk/generate/module.rb +352 -352
  81. data/lib/pdk/generate/provider.rb +28 -28
  82. data/lib/pdk/generate/puppet_class.rb +43 -43
  83. data/lib/pdk/generate/puppet_object.rb +232 -232
  84. data/lib/pdk/generate/task.rb +68 -68
  85. data/lib/pdk/generate/transport.rb +33 -33
  86. data/lib/pdk/generate.rb +24 -24
  87. data/lib/pdk/i18n.rb +4 -4
  88. data/lib/pdk/logger.rb +45 -45
  89. data/lib/pdk/module/build.rb +322 -322
  90. data/lib/pdk/module/convert.rb +296 -296
  91. data/lib/pdk/module/metadata.rb +202 -202
  92. data/lib/pdk/module/release.rb +260 -260
  93. data/lib/pdk/module/update.rb +131 -131
  94. data/lib/pdk/module/update_manager.rb +227 -227
  95. data/lib/pdk/module.rb +30 -30
  96. data/lib/pdk/report/event.rb +370 -370
  97. data/lib/pdk/report.rb +121 -121
  98. data/lib/pdk/template/fetcher/git.rb +85 -85
  99. data/lib/pdk/template/fetcher/local.rb +28 -28
  100. data/lib/pdk/template/fetcher.rb +98 -98
  101. data/lib/pdk/template/renderer/v1/legacy_template_dir.rb +116 -116
  102. data/lib/pdk/template/renderer/v1/renderer.rb +132 -132
  103. data/lib/pdk/template/renderer/v1/template_file.rb +102 -102
  104. data/lib/pdk/template/renderer/v1.rb +25 -25
  105. data/lib/pdk/template/renderer.rb +96 -96
  106. data/lib/pdk/template/template_dir.rb +67 -67
  107. data/lib/pdk/template.rb +59 -59
  108. data/lib/pdk/tests/unit.rb +252 -252
  109. data/lib/pdk/util/bundler.rb +259 -259
  110. data/lib/pdk/util/changelog_generator.rb +137 -137
  111. data/lib/pdk/util/env.rb +47 -47
  112. data/lib/pdk/util/filesystem.rb +138 -138
  113. data/lib/pdk/util/git.rb +179 -179
  114. data/lib/pdk/util/json_finder.rb +85 -85
  115. data/lib/pdk/util/puppet_strings.rb +125 -125
  116. data/lib/pdk/util/puppet_version.rb +266 -266
  117. data/lib/pdk/util/ruby_version.rb +179 -179
  118. data/lib/pdk/util/template_uri.rb +295 -295
  119. data/lib/pdk/util/vendored_file.rb +93 -93
  120. data/lib/pdk/util/version.rb +43 -43
  121. data/lib/pdk/util/windows/api_types.rb +82 -82
  122. data/lib/pdk/util/windows/file.rb +36 -36
  123. data/lib/pdk/util/windows/process.rb +79 -79
  124. data/lib/pdk/util/windows/string.rb +16 -16
  125. data/lib/pdk/util/windows.rb +15 -15
  126. data/lib/pdk/util.rb +278 -277
  127. data/lib/pdk/validate/control_repo/control_repo_validator_group.rb +23 -23
  128. data/lib/pdk/validate/control_repo/environment_conf_validator.rb +98 -98
  129. data/lib/pdk/validate/external_command_validator.rb +208 -208
  130. data/lib/pdk/validate/internal_ruby_validator.rb +100 -100
  131. data/lib/pdk/validate/invokable_validator.rb +228 -228
  132. data/lib/pdk/validate/metadata/metadata_json_lint_validator.rb +86 -86
  133. data/lib/pdk/validate/metadata/metadata_syntax_validator.rb +78 -78
  134. data/lib/pdk/validate/metadata/metadata_validator_group.rb +20 -20
  135. data/lib/pdk/validate/puppet/puppet_epp_validator.rb +133 -133
  136. data/lib/pdk/validate/puppet/puppet_lint_validator.rb +66 -66
  137. data/lib/pdk/validate/puppet/puppet_syntax_validator.rb +137 -137
  138. data/lib/pdk/validate/puppet/puppet_validator_group.rb +21 -21
  139. data/lib/pdk/validate/ruby/ruby_rubocop_validator.rb +80 -80
  140. data/lib/pdk/validate/ruby/ruby_validator_group.rb +19 -19
  141. data/lib/pdk/validate/tasks/tasks_metadata_lint_validator.rb +88 -88
  142. data/lib/pdk/validate/tasks/tasks_name_validator.rb +50 -50
  143. data/lib/pdk/validate/tasks/tasks_validator_group.rb +20 -20
  144. data/lib/pdk/validate/validator.rb +118 -118
  145. data/lib/pdk/validate/validator_group.rb +104 -104
  146. data/lib/pdk/validate/yaml/yaml_syntax_validator.rb +95 -95
  147. data/lib/pdk/validate/yaml/yaml_validator_group.rb +19 -19
  148. data/lib/pdk/validate.rb +94 -94
  149. data/lib/pdk/version.rb +4 -4
  150. data/lib/pdk.rb +76 -76
  151. data/locales/config.yaml +21 -21
  152. data/locales/pdk.pot +2094 -2094
  153. metadata +5 -6
@@ -1,23 +1,23 @@
1
- require 'pdk'
2
-
3
- module PDK
4
- module Validate
5
- module ControlRepo
6
- class ControlRepoValidatorGroup < ValidatorGroup
7
- def name
8
- 'control-repo'
9
- end
10
-
11
- def valid_in_context?
12
- context.is_a?(PDK::Context::ControlRepo)
13
- end
14
-
15
- def validators
16
- [
17
- EnvironmentConfValidator,
18
- ].freeze
19
- end
20
- end
21
- end
22
- end
23
- end
1
+ require 'pdk'
2
+
3
+ module PDK
4
+ module Validate
5
+ module ControlRepo
6
+ class ControlRepoValidatorGroup < ValidatorGroup
7
+ def name
8
+ 'control-repo'
9
+ end
10
+
11
+ def valid_in_context?
12
+ context.is_a?(PDK::Context::ControlRepo)
13
+ end
14
+
15
+ def validators
16
+ [
17
+ EnvironmentConfValidator,
18
+ ].freeze
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,98 +1,98 @@
1
- require 'pdk'
2
-
3
- module PDK
4
- module Validate
5
- module ControlRepo
6
- class EnvironmentConfValidator < InternalRubyValidator
7
- ALLOWED_SETTINGS = %w[modulepath manifest config_version environment_timeout].freeze
8
-
9
- def name
10
- 'environment-conf'
11
- end
12
-
13
- def valid_in_context?
14
- context.is_a?(PDK::Context::ControlRepo)
15
- end
16
-
17
- def pattern
18
- ['environment.conf']
19
- end
20
-
21
- def spinner_text
22
- _('Checking Puppet Environment settings (%{patterns}).') % {
23
- patterns: pattern.join(' '),
24
- }
25
- end
26
-
27
- def validate_target(report, target)
28
- unless PDK::Util::Filesystem.readable?(target)
29
- report.add_event(
30
- file: target,
31
- source: name,
32
- state: :failure,
33
- severity: 'error',
34
- message: _('Could not be read.'),
35
- )
36
- return 1
37
- end
38
-
39
- is_valid = true
40
- begin
41
- env_conf = PDK::ControlRepo.environment_conf_as_config(target)
42
-
43
- env_conf.resolve.each do |setting_name, setting_value|
44
- # Remove the 'environment.' setting_name prefix
45
- setting_name = setting_name.slice(12..-1)
46
- next if ALLOWED_SETTINGS.include?(setting_name)
47
- # A hash indicates that the ini file has a section in it.
48
- message = if setting_value.is_a?(Hash)
49
- _("Invalid section '%{name}'") % { name: setting_name }
50
- else
51
- _("Invalid setting '%{name}'") % { name: setting_name }
52
- end
53
-
54
- report.add_event(
55
- file: target,
56
- source: name,
57
- state: :failure,
58
- severity: 'error',
59
- message: message,
60
- )
61
- is_valid = false
62
- end
63
-
64
- timeout = env_conf.fetch('environment_timeout', nil)
65
- unless timeout.nil? || timeout == '0' || timeout == 'unlimited'
66
- report.add_event(
67
- file: target,
68
- source: name,
69
- state: :failure,
70
- severity: 'error',
71
- message: _("environment_timeout is set to '%{timeout}' but should be 0, 'unlimited' or not set.") % { timeout: timeout },
72
- )
73
- is_valid = false
74
- end
75
-
76
- return 1 unless is_valid
77
- report.add_event(
78
- file: target,
79
- source: name,
80
- state: :passed,
81
- severity: 'ok',
82
- )
83
- return 0
84
- rescue StandardError => e
85
- report.add_event(
86
- file: target,
87
- source: name,
88
- state: :failure,
89
- severity: 'error',
90
- message: e.message,
91
- )
92
- return 1
93
- end
94
- end
95
- end
96
- end
97
- end
98
- end
1
+ require 'pdk'
2
+
3
+ module PDK
4
+ module Validate
5
+ module ControlRepo
6
+ class EnvironmentConfValidator < InternalRubyValidator
7
+ ALLOWED_SETTINGS = %w[modulepath manifest config_version environment_timeout].freeze
8
+
9
+ def name
10
+ 'environment-conf'
11
+ end
12
+
13
+ def valid_in_context?
14
+ context.is_a?(PDK::Context::ControlRepo)
15
+ end
16
+
17
+ def pattern
18
+ ['environment.conf']
19
+ end
20
+
21
+ def spinner_text
22
+ _('Checking Puppet Environment settings (%{patterns}).') % {
23
+ patterns: pattern.join(' '),
24
+ }
25
+ end
26
+
27
+ def validate_target(report, target)
28
+ unless PDK::Util::Filesystem.readable?(target)
29
+ report.add_event(
30
+ file: target,
31
+ source: name,
32
+ state: :failure,
33
+ severity: 'error',
34
+ message: _('Could not be read.'),
35
+ )
36
+ return 1
37
+ end
38
+
39
+ is_valid = true
40
+ begin
41
+ env_conf = PDK::ControlRepo.environment_conf_as_config(target)
42
+
43
+ env_conf.resolve.each do |setting_name, setting_value|
44
+ # Remove the 'environment.' setting_name prefix
45
+ setting_name = setting_name.slice(12..-1)
46
+ next if ALLOWED_SETTINGS.include?(setting_name)
47
+ # A hash indicates that the ini file has a section in it.
48
+ message = if setting_value.is_a?(Hash)
49
+ _("Invalid section '%{name}'") % { name: setting_name }
50
+ else
51
+ _("Invalid setting '%{name}'") % { name: setting_name }
52
+ end
53
+
54
+ report.add_event(
55
+ file: target,
56
+ source: name,
57
+ state: :failure,
58
+ severity: 'error',
59
+ message: message,
60
+ )
61
+ is_valid = false
62
+ end
63
+
64
+ timeout = env_conf.fetch('environment_timeout', nil)
65
+ unless timeout.nil? || timeout == '0' || timeout == 'unlimited'
66
+ report.add_event(
67
+ file: target,
68
+ source: name,
69
+ state: :failure,
70
+ severity: 'error',
71
+ message: _("environment_timeout is set to '%{timeout}' but should be 0, 'unlimited' or not set.") % { timeout: timeout },
72
+ )
73
+ is_valid = false
74
+ end
75
+
76
+ return 1 unless is_valid
77
+ report.add_event(
78
+ file: target,
79
+ source: name,
80
+ state: :passed,
81
+ severity: 'ok',
82
+ )
83
+ return 0
84
+ rescue StandardError => e
85
+ report.add_event(
86
+ file: target,
87
+ source: name,
88
+ state: :failure,
89
+ severity: 'error',
90
+ message: e.message,
91
+ )
92
+ return 1
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -1,208 +1,208 @@
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
- @cmd_path = File.join(context.root_path, 'bin', cmd)
90
- # Return the path to the command if it exists on disk, or we have a gemfile (i.e. Bundled install)
91
- # The Bundle may be created after the prepare_invoke so if the file doesn't exist, it may not be an error
92
- return @cmd_path if PDK::Util::Filesystem.exist?(@cmd_path) || !PDK::Util::Bundler::BundleHelper.new.gemfile.nil?
93
- # But if there is no Gemfile AND cmd doesn't exist in the default path, we need to go searching...
94
- @cmd_path = alternate_bin_paths.map { |alternate_path| File.join(alternate_path, cmd) }
95
- .find { |path| PDK::Util::Filesystem.exist?(path) }
96
- return @cmd_path unless @cmd_path.nil?
97
- # If we can't find it anywhere, just let the OS find it
98
- @cmd_path = cmd
99
- end
100
-
101
- # An array of command line arguments to pass to the command for validation
102
- # @return Array[String]
103
- # @abstract
104
- def parse_options(_targets)
105
- []
106
- end
107
-
108
- # Parses the output from the command and appends formatted events to the report.
109
- # This is called for each command, which is a group of targets
110
- #
111
- # @param report [PDK::Report] The report to add events to
112
- # @param result [Hash[Symbol => Object]] The result of validation command process
113
- # @param targets [Array[String]] The targets for this command result
114
- # @api private
115
- # @see PDK::CLI::Exec::Command.execute!
116
- # @abstract
117
- def parse_output(_report, _result, _targets); end
118
-
119
- # Prepares for invokation by parsing targets and creating the needed commands.
120
- # @api private
121
- # @see PDK::Validate::Validator.prepare_invoke!
122
- def prepare_invoke!
123
- return if @prepared
124
- super
125
-
126
- @targets, @skipped, @invalid = parse_targets
127
- @targets = [] if @targets.nil?
128
-
129
- target_groups = if @targets.empty? && allow_empty_targets?
130
- # If we have no targets and we allow empty targets, create an empty target group list
131
- [[]]
132
- elsif invoke_style == :per_target
133
- # If invoking :per_target, split the targets array into an array of
134
- # single element arrays (one per target).
135
- @targets.combination(1).to_a.compact
136
- else
137
- # Else we're invoking :once, wrap the targets array in another array. This is so we
138
- # can loop through the invokes with the same logic, regardless of which invoke style
139
- # is needed.
140
- @targets.each_slice(1000).to_a.compact
141
- end
142
-
143
- # Register all of the commands for all of the targets
144
- @commands = []
145
- target_groups.each do |invokation_targets|
146
- next if invokation_targets.empty? && !allow_empty_targets?
147
- cmd_argv = parse_options(invokation_targets).unshift(cmd_path).compact
148
- cmd_argv.unshift(File.join(PDK::Util::RubyVersion.bin_path, 'ruby.exe'), '-W0') if Gem.win_platform?
149
-
150
- command = PDK::CLI::Exec::Command.new(*cmd_argv).tap do |c|
151
- c.context = :module
152
- c.environment = { 'PUPPET_GEM_VERSION' => options[:puppet] } if options[:puppet]
153
-
154
- if spinners_enabled?
155
- parent_validator = options[:parent_validator]
156
- if parent_validator.nil? || parent_validator.spinner.nil? || !parent_validator.spinner.is_a?(TTY::Spinner::Multi)
157
- c.add_spinner(spinner_text_for_targets(invokation_targets))
158
- else
159
- spinner = TTY::Spinner.new("[:spinner] #{spinner_text_for_targets(invokation_targets)}", PDK::CLI::Util.spinner_opts_for_platform)
160
- parent_validator.spinner.register(spinner)
161
- c.register_spinner(spinner, PDK::CLI::Util.spinner_opts_for_platform)
162
- end
163
- end
164
- end
165
-
166
- @commands << { command: command, invokation_targets: invokation_targets }
167
- end
168
- nil
169
- end
170
-
171
- # Invokes the prepared commands as an ExecGroup
172
- # @see PDK::Validate::Validator.invoke
173
- def invoke(report)
174
- prepare_invoke!
175
-
176
- process_skipped(report, @skipped)
177
- process_invalid(report, @invalid)
178
-
179
- # Nothing to execute so return success
180
- return 0 if @commands.empty?
181
-
182
- # If there's no Gemfile, then we can't ensure the binstubs are correct
183
- PDK::Util::Bundler.ensure_binstubs!(cmd) unless PDK::Util::Bundler::BundleHelper.new.gemfile.nil?
184
-
185
- exec_group = PDK::CLI::ExecGroup.create(name, { parallel: false }, options)
186
-
187
- # Register all of the commands for all of the targets
188
- @commands.each do |item|
189
- command = item[:command]
190
- invokation_targets = item[:invokation_targets]
191
-
192
- exec_group.register do
193
- result = command.execute!
194
- begin
195
- parse_output(report, result, invokation_targets.compact)
196
- rescue PDK::Validate::ParseOutputError => e
197
- $stderr.puts e.message
198
- end
199
- result[:exit_code]
200
- end
201
- end
202
-
203
- # Now execute and get the return code
204
- exec_group.exit_code
205
- end
206
- end
207
- end
208
- end
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
+ @cmd_path = File.join(context.root_path, 'bin', cmd)
90
+ # Return the path to the command if it exists on disk, or we have a gemfile (i.e. Bundled install)
91
+ # The Bundle may be created after the prepare_invoke so if the file doesn't exist, it may not be an error
92
+ return @cmd_path if PDK::Util::Filesystem.exist?(@cmd_path) || !PDK::Util::Bundler::BundleHelper.new.gemfile.nil?
93
+ # But if there is no Gemfile AND cmd doesn't exist in the default path, we need to go searching...
94
+ @cmd_path = alternate_bin_paths.map { |alternate_path| File.join(alternate_path, cmd) }
95
+ .find { |path| PDK::Util::Filesystem.exist?(path) }
96
+ return @cmd_path unless @cmd_path.nil?
97
+ # If we can't find it anywhere, just let the OS find it
98
+ @cmd_path = cmd
99
+ end
100
+
101
+ # An array of command line arguments to pass to the command for validation
102
+ # @return Array[String]
103
+ # @abstract
104
+ def parse_options(_targets)
105
+ []
106
+ end
107
+
108
+ # Parses the output from the command and appends formatted events to the report.
109
+ # This is called for each command, which is a group of targets
110
+ #
111
+ # @param report [PDK::Report] The report to add events to
112
+ # @param result [Hash[Symbol => Object]] The result of validation command process
113
+ # @param targets [Array[String]] The targets for this command result
114
+ # @api private
115
+ # @see PDK::CLI::Exec::Command.execute!
116
+ # @abstract
117
+ def parse_output(_report, _result, _targets); end
118
+
119
+ # Prepares for invokation by parsing targets and creating the needed commands.
120
+ # @api private
121
+ # @see PDK::Validate::Validator.prepare_invoke!
122
+ def prepare_invoke!
123
+ return if @prepared
124
+ super
125
+
126
+ @targets, @skipped, @invalid = parse_targets
127
+ @targets = [] if @targets.nil?
128
+
129
+ target_groups = if @targets.empty? && allow_empty_targets?
130
+ # If we have no targets and we allow empty targets, create an empty target group list
131
+ [[]]
132
+ elsif invoke_style == :per_target
133
+ # If invoking :per_target, split the targets array into an array of
134
+ # single element arrays (one per target).
135
+ @targets.combination(1).to_a.compact
136
+ else
137
+ # Else we're invoking :once, wrap the targets array in another array. This is so we
138
+ # can loop through the invokes with the same logic, regardless of which invoke style
139
+ # is needed.
140
+ @targets.each_slice(1000).to_a.compact
141
+ end
142
+
143
+ # Register all of the commands for all of the targets
144
+ @commands = []
145
+ target_groups.each do |invokation_targets|
146
+ next if invokation_targets.empty? && !allow_empty_targets?
147
+ cmd_argv = parse_options(invokation_targets).unshift(cmd_path).compact
148
+ cmd_argv.unshift(File.join(PDK::Util::RubyVersion.bin_path, 'ruby.exe'), '-W0') if Gem.win_platform?
149
+
150
+ command = PDK::CLI::Exec::Command.new(*cmd_argv).tap do |c|
151
+ c.context = :module
152
+ c.environment = { 'PUPPET_GEM_VERSION' => options[:puppet] } if options[:puppet]
153
+
154
+ if spinners_enabled?
155
+ parent_validator = options[:parent_validator]
156
+ if parent_validator.nil? || parent_validator.spinner.nil? || !parent_validator.spinner.is_a?(TTY::Spinner::Multi)
157
+ c.add_spinner(spinner_text_for_targets(invokation_targets))
158
+ else
159
+ spinner = TTY::Spinner.new("[:spinner] #{spinner_text_for_targets(invokation_targets)}", PDK::CLI::Util.spinner_opts_for_platform)
160
+ parent_validator.spinner.register(spinner)
161
+ c.register_spinner(spinner, PDK::CLI::Util.spinner_opts_for_platform)
162
+ end
163
+ end
164
+ end
165
+
166
+ @commands << { command: command, invokation_targets: invokation_targets }
167
+ end
168
+ nil
169
+ end
170
+
171
+ # Invokes the prepared commands as an ExecGroup
172
+ # @see PDK::Validate::Validator.invoke
173
+ def invoke(report)
174
+ prepare_invoke!
175
+
176
+ process_skipped(report, @skipped)
177
+ process_invalid(report, @invalid)
178
+
179
+ # Nothing to execute so return success
180
+ return 0 if @commands.empty?
181
+
182
+ # If there's no Gemfile, then we can't ensure the binstubs are correct
183
+ PDK::Util::Bundler.ensure_binstubs!(cmd) unless PDK::Util::Bundler::BundleHelper.new.gemfile.nil?
184
+
185
+ exec_group = PDK::CLI::ExecGroup.create(name, { parallel: false }, options)
186
+
187
+ # Register all of the commands for all of the targets
188
+ @commands.each do |item|
189
+ command = item[:command]
190
+ invokation_targets = item[:invokation_targets]
191
+
192
+ exec_group.register do
193
+ result = command.execute!
194
+ begin
195
+ parse_output(report, result, invokation_targets.compact)
196
+ rescue PDK::Validate::ParseOutputError => e
197
+ $stderr.puts e.message
198
+ end
199
+ result[:exit_code]
200
+ end
201
+ end
202
+
203
+ # Now execute and get the return code
204
+ exec_group.exit_code
205
+ end
206
+ end
207
+ end
208
+ end