pdk 1.13.0 → 1.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +25 -0
  3. data/README.md +41 -0
  4. data/lib/pdk.rb +0 -13
  5. data/lib/pdk/analytics.rb +18 -2
  6. data/lib/pdk/analytics/client/google_analytics.rb +3 -0
  7. data/lib/pdk/answer_file.rb +4 -1
  8. data/lib/pdk/cli.rb +7 -2
  9. data/lib/pdk/cli/build.rb +2 -2
  10. data/lib/pdk/cli/bundle.rb +7 -1
  11. data/lib/pdk/cli/console.rb +148 -0
  12. data/lib/pdk/cli/convert.rb +2 -2
  13. data/lib/pdk/cli/exec.rb +14 -14
  14. data/lib/pdk/cli/exec/command.rb +16 -11
  15. data/lib/pdk/cli/exec/interactive_command.rb +4 -0
  16. data/lib/pdk/cli/exec_group.rb +5 -5
  17. data/lib/pdk/cli/module/build.rb +0 -2
  18. data/lib/pdk/cli/module/generate.rb +1 -2
  19. data/lib/pdk/cli/new.rb +1 -1
  20. data/lib/pdk/cli/new/defined_type.rb +2 -0
  21. data/lib/pdk/cli/new/provider.rb +2 -0
  22. data/lib/pdk/cli/new/task.rb +2 -0
  23. data/lib/pdk/cli/new/{unit_test.rb → test.rb} +16 -12
  24. data/lib/pdk/cli/new/transport.rb +2 -0
  25. data/lib/pdk/cli/test/unit.rb +5 -3
  26. data/lib/pdk/cli/update.rb +2 -3
  27. data/lib/pdk/cli/util.rb +45 -14
  28. data/lib/pdk/cli/util/spinner.rb +2 -2
  29. data/lib/pdk/cli/validate.rb +6 -2
  30. data/lib/pdk/config.rb +20 -8
  31. data/lib/pdk/config/analytics_schema.json +26 -0
  32. data/lib/pdk/config/json.rb +14 -3
  33. data/lib/pdk/config/json_schema_namespace.rb +143 -0
  34. data/lib/pdk/config/json_schema_setting.rb +53 -0
  35. data/lib/pdk/config/json_with_schema.rb +50 -0
  36. data/lib/pdk/config/namespace.rb +84 -76
  37. data/lib/pdk/config/setting.rb +132 -0
  38. data/lib/pdk/config/yaml.rb +15 -3
  39. data/lib/pdk/config/yaml_with_schema.rb +59 -0
  40. data/lib/pdk/generate.rb +0 -2
  41. data/lib/pdk/generate/module.rb +29 -16
  42. data/lib/pdk/generate/puppet_object.rb +31 -28
  43. data/lib/pdk/module.rb +2 -2
  44. data/lib/pdk/module/build.rb +21 -8
  45. data/lib/pdk/module/convert.rb +64 -7
  46. data/lib/pdk/module/metadata.rb +5 -1
  47. data/lib/pdk/module/templatedir.rb +24 -7
  48. data/lib/pdk/module/update.rb +5 -1
  49. data/lib/pdk/module/update_manager.rb +21 -13
  50. data/lib/pdk/report.rb +4 -3
  51. data/lib/pdk/report/event.rb +5 -3
  52. data/lib/pdk/tests/unit.rb +36 -7
  53. data/lib/pdk/util.rb +20 -8
  54. data/lib/pdk/util/bundler.rb +14 -6
  55. data/lib/pdk/util/filesystem.rb +5 -0
  56. data/lib/pdk/util/git.rb +6 -0
  57. data/lib/pdk/util/puppet_strings.rb +24 -2
  58. data/lib/pdk/util/puppet_version.rb +25 -10
  59. data/lib/pdk/util/ruby_version.rb +13 -1
  60. data/lib/pdk/util/template_uri.rb +23 -2
  61. data/lib/pdk/util/vendored_file.rb +28 -24
  62. data/lib/pdk/util/version.rb +5 -5
  63. data/lib/pdk/validate/base_validator.rb +5 -4
  64. data/lib/pdk/validate/metadata/metadata_json_lint.rb +0 -4
  65. data/lib/pdk/validate/metadata/metadata_syntax.rb +5 -3
  66. data/lib/pdk/validate/metadata_validator.rb +0 -2
  67. data/lib/pdk/validate/puppet/puppet_epp.rb +4 -4
  68. data/lib/pdk/validate/puppet/puppet_lint.rb +0 -3
  69. data/lib/pdk/validate/puppet/puppet_syntax.rb +4 -4
  70. data/lib/pdk/validate/puppet_validator.rb +0 -2
  71. data/lib/pdk/validate/ruby/rubocop.rb +0 -5
  72. data/lib/pdk/validate/ruby_validator.rb +0 -2
  73. data/lib/pdk/validate/tasks/metadata_lint.rb +9 -5
  74. data/lib/pdk/validate/tasks/name.rb +4 -2
  75. data/lib/pdk/validate/tasks_validator.rb +0 -2
  76. data/lib/pdk/validate/yaml/syntax.rb +4 -4
  77. data/lib/pdk/validate/yaml_validator.rb +0 -2
  78. data/lib/pdk/version.rb +1 -1
  79. data/locales/pdk.pot +351 -311
  80. metadata +11 -7
  81. data/lib/pdk/config/validator.rb +0 -31
  82. data/lib/pdk/config/value.rb +0 -94
@@ -0,0 +1,132 @@
1
+ module PDK
2
+ class Config
3
+ # A class for describing the setting of a {PDK::Config} setting.
4
+ #
5
+ # Generally, this is never instantiated manually, but is instead
6
+ # instantiated by passing a block to {PDK::Config::Namespace#setting}.
7
+ #
8
+ # @example
9
+ #
10
+ # PDK::Config::Namespace.new('analytics') do
11
+ # setting :disabled do
12
+ # validate PDK::Config::Validator.boolean
13
+ # default_to { false }
14
+ # end
15
+ # end
16
+ class Setting
17
+ attr_reader :namespace
18
+
19
+ # It is possible to have multiple setting definitions for the same setting; for example, defining a default value with a lambda, but the
20
+ # the validation is within a JSON schema document. These are expressed as two settings objects, and uses a single linked list to join them
21
+ # together:
22
+ #
23
+ # (PDK::Config::JSONSchemaSetting) --previous_setting--> (PDK::Config::Setting)
24
+ #
25
+ # So in the example above, calling `default` the on the first object in the list will:
26
+ # 1. Look at `default` on PDK::Config::JSONSchemaSetting
27
+ # 2. If a default could not be found then it calls `default` on previous_setting
28
+ # 3. If a default could not be found then it calls `default` on previous_setting.previous_setting
29
+ # 4. and so on down the linked list (chain) of settings
30
+ attr_writer :previous_setting
31
+
32
+ # Initialises an empty setting definition.
33
+ #
34
+ # @param name [String,Symbol] the name of the setting.
35
+ # @param namespace [PDK::Config::Namespace] The namespace this setting belongs to
36
+ def initialize(name, namespace, initial_value = nil)
37
+ @name = name.to_s
38
+ @validators = []
39
+ @namespace = namespace
40
+ @value = initial_value
41
+ end
42
+
43
+ def qualified_name
44
+ [namespace.name, @name].join('.')
45
+ end
46
+
47
+ def value # rubocop:disable Style/TrivialAccessors
48
+ @value
49
+ end
50
+
51
+ def value=(obj)
52
+ validate!(obj)
53
+ @value = obj
54
+ end
55
+
56
+ def to_s
57
+ @value.to_s
58
+ end
59
+
60
+ # Assign a validator to the setting. Subclasses should not override this method.
61
+ #
62
+ # @param validator [Hash{Symbol => [Proc,String]}]
63
+ # @option validator [Proc] :proc a lambda that takes the setting to be
64
+ # validated as the argument and returns `true` if the setting is valid.
65
+ # @option validator [String] :message a description of what the validator
66
+ # is testing for, that is displayed to the user as part of the error
67
+ # message for invalid settings.
68
+ #
69
+ # @raise [ArgumentError] if not passed a Hash.
70
+ # @raise [ArgumentError] if the Hash doesn't have a `:proc` key that
71
+ # contains a Proc.
72
+ # @raise [ArgumentError] if the Hash doesn't have a `:message` key that
73
+ # contains a String.
74
+ #
75
+ # @return [nil]
76
+ def validate(validator)
77
+ raise ArgumentError, _('`validator` must be a Hash') unless validator.is_a?(Hash)
78
+ raise ArgumentError, _('the :proc key must contain a Proc') unless validator.key?(:proc) && validator[:proc].is_a?(Proc)
79
+ raise ArgumentError, _('the :message key must contain a String') unless validator.key?(:message) && validator[:message].is_a?(String)
80
+
81
+ @validators << validator
82
+ end
83
+
84
+ # Validate a setting against the assigned validators.
85
+ #
86
+ # @param setting [Object] the setting being validated.
87
+ #
88
+ # @raise [ArgumentError] if any of the assigned validators fail to
89
+ # validate the setting.
90
+ #
91
+ # @return [nil]
92
+ def validate!(value)
93
+ @validators.each do |validator|
94
+ next if validator[:proc].call(value)
95
+
96
+ raise ArgumentError, _('%{key} %{message}') % {
97
+ key: qualified_name,
98
+ message: validator[:message],
99
+ }
100
+ end
101
+ end
102
+
103
+ # Assign a default value proc for the setting. Subclasses should not override this method.
104
+ #
105
+ # @param block [Proc] a block that is lazy evaluated when necessary in
106
+ # order to determine the default setting.
107
+ #
108
+ # @return [nil]
109
+ def default_to(&block)
110
+ raise ArgumentError, _('must be passed a block') unless block_given?
111
+ @default_to = block
112
+ end
113
+
114
+ # Evaluate the default setting.
115
+ #
116
+ # @return [Object,nil] the result of evaluating the block given to
117
+ # {#default_to}, or `nil` if the setting has no default.
118
+ def default
119
+ return @default_to.call if default_block?
120
+ # If there is a previous setting in the chain, use its default
121
+ @previous_setting.nil? ? nil : @previous_setting.default
122
+ end
123
+
124
+ private
125
+
126
+ # @return [Boolean] true if the setting has a default setting block. Subclasses should not override this method.
127
+ def default_block?
128
+ !@default_to.nil?
129
+ end
130
+ end
131
+ end
132
+ end
@@ -1,14 +1,23 @@
1
1
  require 'pdk/config/namespace'
2
+ require 'pdk/config/setting'
2
3
 
3
4
  module PDK
4
5
  class Config
6
+ # Parses a YAML document.
7
+ #
8
+ # @see PDK::Config::Namespace.parse_file
5
9
  class YAML < Namespace
6
- def parse_data(data, filename)
7
- return {} if data.nil? || data.empty?
10
+ def parse_file(filename)
11
+ raise unless block_given?
12
+ data = load_data(filename)
13
+ return if data.nil? || data.empty?
8
14
 
9
15
  require 'yaml'
10
16
 
11
- ::YAML.safe_load(data, [Symbol], [], true)
17
+ data = ::YAML.safe_load(data, [Symbol], [], true)
18
+ return if data.nil?
19
+
20
+ data.each { |k, v| yield k, PDK::Config::Setting.new(k, self, v) }
12
21
  rescue Psych::SyntaxError => e
13
22
  raise PDK::Config::LoadError, _('Syntax error when loading %{file}: %{error}') % {
14
23
  file: filename,
@@ -21,6 +30,9 @@ module PDK
21
30
  }
22
31
  end
23
32
 
33
+ # Serializes object data into a YAML string.
34
+ #
35
+ # @see PDK::Config::Namespace.serialize_data
24
36
  def serialize_data(data)
25
37
  require 'yaml'
26
38
 
@@ -0,0 +1,59 @@
1
+ require 'pdk/config/json_schema_namespace'
2
+
3
+ module PDK
4
+ class Config
5
+ # Parses a YAML document with a JSON schema.
6
+ #
7
+ # @see PDK::Config::Namespace.parse_file
8
+ class YAMLWithSchema < JSONSchemaNamespace
9
+ def parse_file(filename)
10
+ raise unless block_given?
11
+ data = load_data(filename)
12
+ data = '' if data.nil?
13
+ require 'yaml'
14
+ require 'json-schema'
15
+
16
+ @raw_data = ::YAML.safe_load(data, [Symbol], [], true)
17
+ @raw_data = {} if @raw_data.nil?
18
+
19
+ begin
20
+ # Ensure the parsed document is actually valid
21
+ validate_document!(@raw_data)
22
+ rescue ::JSON::Schema::ValidationError => e
23
+ raise PDK::Config::LoadError, _('The configuration file %{filename} is not valid: %{message}') % {
24
+ filename: filename,
25
+ message: e.message,
26
+ }
27
+ end
28
+
29
+ require 'pdk/config/json_schema_setting'
30
+
31
+ schema_property_names.each do |key|
32
+ yield key, PDK::Config::JSONSchemaSetting.new(key, self, @raw_data[key])
33
+ end
34
+
35
+ # Remove all of the "known" settings from the schema and
36
+ # we're left with the settings that we don't manage.
37
+ self.unmanaged_settings = @raw_data.reject { |k, _| schema_property_names.include?(k) }
38
+ rescue Psych::SyntaxError => e
39
+ raise PDK::Config::LoadError, _('Syntax error when loading %{file}: %{error}') % {
40
+ file: filename,
41
+ error: "#{e.problem} #{e.context}",
42
+ }
43
+ rescue Psych::DisallowedClass => e
44
+ raise PDK::Config::LoadError, _('Unsupported class in %{file}: %{error}') % {
45
+ file: filename,
46
+ error: e.message,
47
+ }
48
+ end
49
+
50
+ # Serializes object data into a YAML string.
51
+ #
52
+ # @see PDK::Config::Namespace.serialize_data
53
+ def serialize_data(data)
54
+ require 'yaml'
55
+ ::YAML.dump(data)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -4,8 +4,6 @@ require 'pdk/generate/provider'
4
4
  require 'pdk/generate/puppet_class'
5
5
  require 'pdk/generate/task'
6
6
  require 'pdk/generate/transport'
7
- require 'pdk/module/metadata'
8
- require 'pdk/module/templatedir'
9
7
 
10
8
  module PDK
11
9
  module Generate
@@ -1,18 +1,4 @@
1
- require 'etc'
2
- require 'pathname'
3
- require 'fileutils'
4
- require 'tty-prompt'
5
-
6
- require 'pdk'
7
- require 'pdk/logger'
8
- require 'pdk/module/metadata'
9
- require 'pdk/module/templatedir'
10
- require 'pdk/cli/exec'
11
- require 'pdk/cli/util'
12
- require 'pdk/cli/util/interview'
13
- require 'pdk/cli/util/option_validator'
14
- require 'pdk/util'
15
- require 'pdk/util/version'
1
+ require 'pdk/util/filesystem'
16
2
 
17
3
  module PDK
18
4
  module Generate
@@ -20,6 +6,8 @@ module PDK
20
6
  extend PDK::Util::Filesystem
21
7
 
22
8
  def self.validate_options(opts)
9
+ require 'pdk/cli/util/option_validator'
10
+
23
11
  unless PDK::CLI::Util::OptionValidator.valid_module_name?(opts[:module_name])
24
12
  error_msg = _(
25
13
  "'%{module_name}' is not a valid module name.\n" \
@@ -33,6 +21,12 @@ module PDK
33
21
  end
34
22
 
35
23
  def self.invoke(opts = {})
24
+ require 'pdk/module/templatedir'
25
+ require 'pdk/util'
26
+ require 'pdk/util/template_uri'
27
+ require 'fileutils'
28
+ require 'pathname'
29
+
36
30
  validate_options(opts) unless opts[:module_name].nil?
37
31
 
38
32
  metadata = prepare_metadata(opts)
@@ -76,6 +70,7 @@ module PDK
76
70
  end
77
71
 
78
72
  # Only update the answers files after metadata has been written.
73
+ require 'pdk/answer_file'
79
74
  if template_uri.default?
80
75
  # If the user specifies our default template url via the command
81
76
  # line, remove the saved template-url answer so that the template_uri
@@ -89,7 +84,12 @@ module PDK
89
84
 
90
85
  begin
91
86
  if FileUtils.mv(temp_target_dir, target_dir)
92
- Dir.chdir(target_dir) { PDK::Util::Bundler.ensure_bundle! } unless opts[:'skip-bundle-install']
87
+ unless opts[:'skip-bundle-install']
88
+ Dir.chdir(target_dir) do
89
+ require 'pdk/util/bundler'
90
+ PDK::Util::Bundler.ensure_bundle!
91
+ end
92
+ end
93
93
 
94
94
  PDK.logger.info _('Module \'%{name}\' generated at path \'%{path}\', from template \'%{url}\'.') % { name: opts[:module_name], path: target_dir, url: template_uri.git_remote }
95
95
  PDK.logger.info(_('In your module directory, add classes with the \'pdk new class\' command.'))
@@ -104,6 +104,8 @@ module PDK
104
104
  end
105
105
 
106
106
  def self.username_from_login
107
+ require 'etc'
108
+
107
109
  login = Etc.getlogin || ''
108
110
  login_clean = login.downcase.gsub(%r{[^0-9a-z]}i, '')
109
111
  login_clean = 'username' if login_clean.empty?
@@ -118,6 +120,9 @@ module PDK
118
120
  end
119
121
 
120
122
  def self.prepare_metadata(opts = {})
123
+ require 'pdk/answer_file'
124
+ require 'pdk/module/metadata'
125
+
121
126
  opts[:username] = (opts[:username] || PDK.answers['forge_username'] || username_from_login).downcase
122
127
 
123
128
  defaults = PDK::Module::Metadata::DEFAULTS.dup
@@ -134,6 +139,8 @@ module PDK
134
139
  end
135
140
 
136
141
  def self.prepare_module_directory(target_dir)
142
+ require 'fileutils'
143
+
137
144
  [
138
145
  File.join(target_dir, 'examples'),
139
146
  File.join(target_dir, 'files'),
@@ -153,6 +160,9 @@ module PDK
153
160
  end
154
161
 
155
162
  def self.module_interview(metadata, opts = {})
163
+ require 'pdk/module/metadata'
164
+ require 'pdk/cli/util/interview'
165
+
156
166
  questions = [
157
167
  {
158
168
  name: 'module_name',
@@ -307,6 +317,8 @@ module PDK
307
317
  metadata.update!(answers)
308
318
 
309
319
  if opts[:prompt].nil? || opts[:prompt]
320
+ require 'pdk/cli/util'
321
+
310
322
  continue = PDK::CLI::Util.prompt_for_yes(
311
323
  _('Metadata will be generated based on this information, continue?'),
312
324
  prompt: prompt,
@@ -319,6 +331,7 @@ module PDK
319
331
  end
320
332
  end
321
333
 
334
+ require 'pdk/answer_file'
322
335
  PDK.answers.update!(
323
336
  {
324
337
  'forge_username' => opts[:username],
@@ -1,12 +1,3 @@
1
- require 'fileutils'
2
-
3
- require 'pdk'
4
- require 'pdk/logger'
5
- require 'pdk/module/metadata'
6
- require 'pdk/module/templatedir'
7
- require 'pdk/template_file'
8
- require 'pdk/util/filesystem'
9
-
10
1
  module PDK
11
2
  module Generate
12
3
  class PuppetObject
@@ -141,8 +132,10 @@ module PDK
141
132
  #
142
133
  # @api public
143
134
  def check_preconditions
135
+ require 'pdk/util/filesystem'
136
+
144
137
  targets.each do |target_file|
145
- next unless File.exist?(target_file)
138
+ next unless PDK::Util::Filesystem.exist?(target_file)
146
139
 
147
140
  raise PDK::CLI::ExitWithError, _("Unable to generate %{object_type}; '%{file}' already exists.") % {
148
141
  file: target_file,
@@ -151,6 +144,18 @@ module PDK
151
144
  end
152
145
  end
153
146
 
147
+ # Check the preconditions of this template group, behaving as a
148
+ # predicate rather than raising an exception.
149
+ #
150
+ # @return [Boolean] true if the generator is safe to run, otherwise
151
+ # false.
152
+ def can_run?
153
+ check_preconditions
154
+ true
155
+ rescue PDK::CLI::ExitWithError
156
+ false
157
+ end
158
+
154
159
  # Check that the templates can be rendered. Find an appropriate template
155
160
  # and create the target files from the template. This is the main entry
156
161
  # point for the class.
@@ -191,6 +196,8 @@ module PDK
191
196
  #
192
197
  # @api private
193
198
  def render_file(dest_path, template_path, data)
199
+ require 'pdk/template_file'
200
+
194
201
  write_file(dest_path) do
195
202
  PDK::TemplateFile.new(template_path, data).render
196
203
  end
@@ -211,12 +218,15 @@ module PDK
211
218
  #
212
219
  # @api private
213
220
  def write_file(dest_path)
221
+ require 'pdk/logger'
222
+ require 'pdk/util/filesystem'
223
+
214
224
  PDK.logger.info(_("Creating '%{file}' from template.") % { file: dest_path })
215
225
 
216
226
  file_content = yield
217
227
 
218
228
  begin
219
- FileUtils.mkdir_p(File.dirname(dest_path))
229
+ PDK::Util::Filesystem.mkdir_p(File.dirname(dest_path))
220
230
  rescue SystemCallError => e
221
231
  raise PDK::CLI::FatalError, _("Unable to create directory '%{path}': %{message}") % {
222
232
  path: File.dirname(dest_path),
@@ -247,6 +257,10 @@ module PDK
247
257
  #
248
258
  # @api private
249
259
  def with_templates
260
+ require 'pdk/logger'
261
+ require 'pdk/module/templatedir'
262
+ require 'pdk/util/template_uri'
263
+
250
264
  templates.each do |template|
251
265
  if template[:uri].nil?
252
266
  PDK.logger.debug(_('No %{dir_type} template found; trying next template directory.') % { dir_type: template[:type] })
@@ -293,34 +307,23 @@ module PDK
293
307
  #
294
308
  # @api private
295
309
  def templates
310
+ require 'pdk/util/template_uri'
311
+
296
312
  @templates ||= PDK::Util::TemplateURI.templates(@options)
297
313
  end
298
314
 
299
315
  # Retrieves the name of the module (without the forge username) from the
300
316
  # module metadata.
301
317
  #
302
- # @raise (see #module_metadata)
303
318
  # @return [String] The name of the module.
304
319
  #
305
320
  # @api private
306
321
  def module_name
307
- @module_name ||= module_metadata.data['name'].rpartition('-').last
308
- end
322
+ require 'pdk/util'
309
323
 
310
- # Parses the metadata.json file for the module.
311
- #
312
- # @raise [PDK::CLI::FatalError] if the metadata.json file does not exist,
313
- # can not be read, or contains invalid metadata.
314
- #
315
- # @return [PDK::Module::Metadata] the parsed module metadata.
316
- #
317
- # @api private
318
- def module_metadata
319
- @module_metadata ||= begin
320
- PDK::Module::Metadata.from_file(File.join(module_dir, 'metadata.json'))
321
- rescue ArgumentError => e
322
- raise PDK::CLI::FatalError, _("'%{dir}' does not contain valid Puppet module metadata: %{msg}") % { dir: module_dir, msg: e.message }
323
- end
324
+ @module_name ||= PDK::Util.module_metadata['name'].rpartition('-').last
325
+ rescue ArgumentError => e
326
+ raise PDK::CLI::FatalError, e
324
327
  end
325
328
 
326
329
  # transform a object name into a ruby class name