pdk 1.17.0 → 2.1.1

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 +4 -4
  2. data/CHANGELOG.md +148 -11
  3. data/README.md +1 -1
  4. data/lib/pdk.rb +1 -1
  5. data/lib/pdk/cli.rb +7 -1
  6. data/lib/pdk/cli/convert.rb +7 -9
  7. data/lib/pdk/cli/env.rb +52 -0
  8. data/lib/pdk/cli/exec/command.rb +11 -1
  9. data/lib/pdk/cli/new.rb +2 -0
  10. data/lib/pdk/cli/new/class.rb +2 -1
  11. data/lib/pdk/cli/new/defined_type.rb +2 -1
  12. data/lib/pdk/cli/new/fact.rb +29 -0
  13. data/lib/pdk/cli/new/function.rb +29 -0
  14. data/lib/pdk/cli/new/provider.rb +2 -1
  15. data/lib/pdk/cli/new/task.rb +2 -1
  16. data/lib/pdk/cli/new/test.rb +2 -1
  17. data/lib/pdk/cli/new/transport.rb +2 -1
  18. data/lib/pdk/cli/release.rb +1 -1
  19. data/lib/pdk/cli/release/publish.rb +11 -1
  20. data/lib/pdk/cli/remove.rb +20 -0
  21. data/lib/pdk/cli/remove/config.rb +80 -0
  22. data/lib/pdk/cli/set.rb +20 -0
  23. data/lib/pdk/cli/set/config.rb +119 -0
  24. data/lib/pdk/cli/update.rb +6 -8
  25. data/lib/pdk/cli/util.rb +1 -0
  26. data/lib/pdk/cli/util/option_validator.rb +6 -0
  27. data/lib/pdk/cli/util/update_manager_printer.rb +82 -0
  28. data/lib/pdk/config.rb +96 -13
  29. data/lib/pdk/context.rb +8 -5
  30. data/lib/pdk/generate/defined_type.rb +25 -32
  31. data/lib/pdk/generate/fact.rb +25 -0
  32. data/lib/pdk/generate/function.rb +48 -0
  33. data/lib/pdk/generate/module.rb +11 -10
  34. data/lib/pdk/generate/provider.rb +15 -64
  35. data/lib/pdk/generate/puppet_class.rb +25 -31
  36. data/lib/pdk/generate/puppet_object.rb +83 -187
  37. data/lib/pdk/generate/task.rb +28 -46
  38. data/lib/pdk/generate/transport.rb +20 -74
  39. data/lib/pdk/module.rb +1 -1
  40. data/lib/pdk/module/convert.rb +43 -23
  41. data/lib/pdk/module/metadata.rb +6 -2
  42. data/lib/pdk/module/release.rb +8 -2
  43. data/lib/pdk/module/update.rb +7 -11
  44. data/lib/pdk/module/update_manager.rb +7 -0
  45. data/lib/pdk/report.rb +3 -3
  46. data/lib/pdk/report/event.rb +8 -2
  47. data/lib/pdk/template.rb +59 -0
  48. data/lib/pdk/template/fetcher.rb +98 -0
  49. data/lib/pdk/template/fetcher/git.rb +85 -0
  50. data/lib/pdk/template/fetcher/local.rb +28 -0
  51. data/lib/pdk/template/renderer.rb +96 -0
  52. data/lib/pdk/template/renderer/v1.rb +25 -0
  53. data/lib/pdk/template/renderer/v1/legacy_template_dir.rb +116 -0
  54. data/lib/pdk/template/renderer/v1/renderer.rb +132 -0
  55. data/lib/pdk/template/renderer/v1/template_file.rb +102 -0
  56. data/lib/pdk/template/template_dir.rb +67 -0
  57. data/lib/pdk/tests/unit.rb +8 -1
  58. data/lib/pdk/util.rb +4 -35
  59. data/lib/pdk/util/bundler.rb +1 -1
  60. data/lib/pdk/util/changelog_generator.rb +20 -3
  61. data/lib/pdk/util/json_finder.rb +85 -0
  62. data/lib/pdk/util/puppet_strings.rb +3 -3
  63. data/lib/pdk/util/puppet_version.rb +2 -2
  64. data/lib/pdk/util/ruby_version.rb +5 -1
  65. data/lib/pdk/util/template_uri.rb +9 -11
  66. data/lib/pdk/util/vendored_file.rb +1 -2
  67. data/lib/pdk/validate.rb +17 -10
  68. data/lib/pdk/validate/control_repo/control_repo_validator_group.rb +23 -0
  69. data/lib/pdk/validate/control_repo/environment_conf_validator.rb +98 -0
  70. data/lib/pdk/validate/invokable_validator.rb +8 -4
  71. data/lib/pdk/validate/tasks/tasks_metadata_lint_validator.rb +1 -1
  72. data/lib/pdk/validate/validator.rb +7 -0
  73. data/lib/pdk/validate/validator_group.rb +1 -0
  74. data/lib/pdk/validate/yaml/yaml_syntax_validator.rb +2 -2
  75. data/lib/pdk/version.rb +1 -1
  76. data/locales/pdk.pot +356 -228
  77. metadata +65 -28
  78. data/lib/pdk/module/template_dir.rb +0 -115
  79. data/lib/pdk/module/template_dir/base.rb +0 -268
  80. data/lib/pdk/module/template_dir/git.rb +0 -91
  81. data/lib/pdk/module/template_dir/local.rb +0 -21
  82. data/lib/pdk/template_file.rb +0 -96
@@ -0,0 +1,119 @@
1
+ module PDK::CLI
2
+ module Set
3
+ module Config
4
+ ALLOWED_TYPE_NAMES = %w[array boolean number string].freeze
5
+
6
+ # :nocov:
7
+ def self.pretty_allowed_names
8
+ ALLOWED_TYPE_NAMES.map { |name| "'#{name}'" }.join(', ')
9
+ end
10
+ # :nocov:
11
+
12
+ def self.transform_value(type_name, value)
13
+ normalized_name = type_name.downcase.strip
14
+ unless ALLOWED_TYPE_NAMES.include?(normalized_name)
15
+ raise PDK::CLI::ExitWithError, _('Unknown type %{type_name}. Expected one of %{allowed}') % { type_name: type_name, allowed: pretty_allowed_names }
16
+ end
17
+
18
+ # Short circuit string conversions as it's trivial
19
+ if normalized_name == 'string'
20
+ raise PDK::CLI::ExitWithError, _('An error occured converting \'%{value}\' into a %{type_name}') % { value: value.nil? ? 'nil' : value, type_name: type_name } unless value.is_a?(String)
21
+ return value
22
+ end
23
+
24
+ begin
25
+ case normalized_name
26
+ when 'array'
27
+ convert_to_array(value)
28
+ when 'boolean'
29
+ convert_to_boolean(value)
30
+ when 'number'
31
+ convert_to_number(value)
32
+ else
33
+ value
34
+ end
35
+ rescue ArgumentError, TypeError
36
+ raise PDK::CLI::ExitWithError, _('An error occured converting \'%{value}\' into a %{type_name}') % { value: value.nil? ? 'nil' : value, type_name: type_name }
37
+ end
38
+ end
39
+
40
+ def self.convert_to_array(value)
41
+ return [] if value.nil?
42
+ value.is_a?(Array) ? value : [value]
43
+ end
44
+ private_class_method :convert_to_array
45
+
46
+ def self.convert_to_boolean(value)
47
+ string_val = value.to_s.strip.downcase
48
+
49
+ return true if %w[yes true -1 1].include?(string_val)
50
+ return false if %w[no false 0].include?(string_val)
51
+
52
+ raise ArgumentError
53
+ end
54
+ private_class_method :convert_to_boolean
55
+
56
+ def self.convert_to_number(value)
57
+ float_val = Float(value)
58
+ # Return an Integer if this is actually and Integer, otherwise return the float
59
+ (float_val.truncate == float_val) ? float_val.truncate : float_val
60
+ end
61
+ private_class_method :convert_to_number
62
+
63
+ def self.run(opts, args)
64
+ item_name = (args.count > 0) ? args[0] : nil
65
+ item_value = (args.count > 1) ? args[1] : nil
66
+
67
+ opts[:type] = opts[:as] if opts[:type].nil? && !opts[:as].nil?
68
+ force = opts[:force] || false
69
+
70
+ # Transform the value if we need to
71
+ item_value = PDK::CLI::Set::Config.transform_value(opts[:type], item_value) unless opts[:type].nil?
72
+
73
+ raise PDK::CLI::ExitWithError, _('Configuration name is required') if item_name.nil?
74
+ raise PDK::CLI::ExitWithError, _('Configuration value is required. If you wish to remove a value use \'pdk remove config\'') if item_value.nil?
75
+
76
+ current_value = PDK.config.get(item_name)
77
+ raise PDK::CLI::ExitWithError, _("The configuration item '%{name}' can not have a value set.") % { name: item_name } if current_value.is_a?(PDK::Config::Namespace)
78
+
79
+ # If we're forcing the value, don't do any munging
80
+ unless force
81
+ # Check if the setting already exists
82
+ if current_value.is_a?(Array) && current_value.include?(item_value)
83
+ PDK.logger.info(_("No changes made to '%{name}' as it already contains value '%{to}'") % { name: item_name, to: item_value })
84
+ return 0
85
+ end
86
+ end
87
+
88
+ new_value = PDK.config.set(item_name, item_value, force: opts[:force])
89
+ if current_value.nil? || force
90
+ PDK.logger.info(_("Set initial value of '%{name}' to '%{to}'") % { name: item_name, to: new_value })
91
+ elsif current_value.is_a?(Array)
92
+ # Arrays have a special output format
93
+ PDK.logger.info(_("Added new value '%{to}' to '%{name}'") % { name: item_name, to: item_value })
94
+ else
95
+ PDK.logger.info(_("Changed existing value of '%{name}' from '%{from}' to '%{to}'") % { name: item_name, from: current_value, to: new_value })
96
+ end
97
+
98
+ # Same output as `get config`
99
+ $stdout.puts _('%{name}=%{value}') % { name: item_name, value: PDK.config.get(item_name) }
100
+ 0
101
+ end
102
+ end
103
+ end
104
+
105
+ @set_config_cmd = @set_cmd.define_command do
106
+ name 'config'
107
+ usage _('config [name] [value]')
108
+ summary _('Set or update the configuration for <name>')
109
+
110
+ option :f, :force, _('Force the configuration setting to be overwitten.'), argument: :forbidden
111
+
112
+ option :t, :type, _('The type of value to set. Acceptable values: %{values}') % { values: PDK::CLI::Set::Config.pretty_allowed_names }, argument: :required
113
+ option nil, :as, _('Alias of --type'), argument: :required
114
+
115
+ run do |opts, args, _cmd|
116
+ exit PDK::CLI::Set::Config.run(opts, args)
117
+ end
118
+ end
119
+ end
@@ -10,14 +10,12 @@ module PDK::CLI
10
10
  PDK::CLI.template_ref_option(self)
11
11
 
12
12
  run do |opts, _args, _cmd|
13
- require 'pdk/cli/util'
14
- require 'pdk/util'
15
- require 'pdk/module/update'
13
+ # Write the context information to the debug log
14
+ PDK.context.to_debug_log
16
15
 
17
- PDK::CLI::Util.ensure_in_module!(
18
- message: _('`pdk update` can only be run from inside a valid module directory.'),
19
- log_level: :info,
20
- )
16
+ unless PDK.context.is_a?(PDK::Context::Module)
17
+ raise PDK::CLI::ExitWithError, _('`pdk update` can only be run from inside a valid module directory.')
18
+ end
21
19
 
22
20
  raise PDK::CLI::ExitWithError, _('This module does not appear to be PDK compatible. To make the module compatible with PDK, run `pdk convert`.') unless PDK::Util.module_pdk_compatible?
23
21
 
@@ -46,7 +44,7 @@ module PDK::CLI
46
44
 
47
45
  PDK::CLI::Util.analytics_screen_view('update', opts)
48
46
 
49
- updater = PDK::Module::Update.new(PDK::Util.module_root, opts)
47
+ updater = PDK::Module::Update.new(PDK.context.root_path, opts)
50
48
 
51
49
  if updater.pinned_to_puppetlabs_template_tag?
52
50
  PDK.logger.info _(
data/lib/pdk/cli/util.rb CHANGED
@@ -8,6 +8,7 @@ module PDK
8
8
  autoload :OptionValidator, 'pdk/cli/util/option_validator'
9
9
  autoload :Interview, 'pdk/cli/util/interview'
10
10
  autoload :Spinner, 'pdk/cli/util/spinner'
11
+ autoload :UpdateManagerPrinter, 'pdk/cli/util/update_manager_printer'
11
12
 
12
13
  # Ensures the calling code is being run from inside a module directory.
13
14
  #
@@ -8,6 +8,11 @@ module PDK
8
8
  (list =~ %r{^[\w\-]+(?:,[\w\-]+)+$}) ? true : false
9
9
  end
10
10
 
11
+ # @return [Boolean] true if the fact name is valid
12
+ def self.valid_fact_name?(name)
13
+ name.length > 1
14
+ end
15
+
11
16
  def self.enum(val, valid_entries, _options = {})
12
17
  vals = val.is_a?(Array) ? val : [val]
13
18
  invalid_entries = vals.reject { |e| valid_entries.include?(e) }
@@ -41,6 +46,7 @@ module PDK
41
46
 
42
47
  !(string =~ %r{\A([a-z][a-z0-9_]*)(::[a-z][a-z0-9_]*)*\Z}).nil?
43
48
  end
49
+ singleton_class.send(:alias_method, :valid_function_name?, :valid_namespace?)
44
50
 
45
51
  singleton_class.send(:alias_method, :valid_class_name?, :valid_namespace?)
46
52
  singleton_class.send(:alias_method, :valid_defined_type_name?, :valid_namespace?)
@@ -0,0 +1,82 @@
1
+ require 'pdk'
2
+
3
+ module PDK
4
+ module CLI
5
+ module Util
6
+ module UpdateManagerPrinter
7
+ # Prints the summary for a PDK::Module::UpdateManager Object
8
+ # @param update_manager [PDK::Module::UpdateManager] The object to print a summary of
9
+ # @param options [Hash{Object => Object}] A list of options when printing
10
+ # @option options [Boolean] :tense Whether to use future (:future) or past (:past) tense when printing the summary ("Files to be added" versus "Files added"). Default is :future
11
+ #
12
+ # @return [void]
13
+ def self.print_summary(update_manager, options = {})
14
+ require 'pdk/report'
15
+
16
+ options = {
17
+ tense: :future,
18
+ }.merge(options)
19
+
20
+ footer = false
21
+
22
+ summary(update_manager).each do |category, files|
23
+ next if files.empty?
24
+
25
+ PDK::Report.default_target.puts('')
26
+ PDK::Report.default_target.puts(generate_banner("Files #{(options[:tense] == :future) ? 'to be ' : ''}#{category}", 40))
27
+ PDK::Report.default_target.puts(files.map(&:to_s).join("\n"))
28
+ footer = true
29
+ end
30
+
31
+ if footer # rubocop:disable Style/GuardClause No.
32
+ PDK::Report.default_target.puts('')
33
+ PDK::Report.default_target.puts(generate_banner('', 40))
34
+ end
35
+ end
36
+
37
+ #:nocov: Tested as part of the public methods
38
+ # Returns a hash, summarizing the contents of the Update Manager object
39
+ # @param update_manager [PDK::Module::UpdateManager] The object to create a summary of
40
+ #
41
+ # @return [Hash{Symbol => Array[String]}] A hash of each category and the file paths in each category
42
+ def self.summary(update_manager)
43
+ summary = {}
44
+ update_manager.changes.each do |category, update_category|
45
+ if update_category.respond_to?(:keys)
46
+ updated_files = update_category.keys
47
+ else
48
+ begin
49
+ updated_files = update_category.map { |file| file[:path] }
50
+ rescue TypeError
51
+ updated_files = update_category.to_a
52
+ end
53
+ end
54
+
55
+ summary[category] = updated_files
56
+ end
57
+
58
+ summary
59
+ end
60
+ private_class_method :summary
61
+
62
+ # Creates a line of text, with `text` centered in the middle
63
+ # @param text [String] The text to put in the middle of the banner
64
+ # @param width [Integer] The width of the banner in characters. Default is 80
65
+ # @return [String] The generated banner
66
+ def self.generate_banner(text, width = 80)
67
+ padding = width - text.length
68
+ banner = ''
69
+ padding_char = '-'
70
+
71
+ (padding / 2.0).ceil.times { banner << padding_char }
72
+ banner << text
73
+ (padding / 2.0).floor.times { banner << padding_char }
74
+
75
+ banner
76
+ end
77
+ private_class_method :generate_banner
78
+ #:nocov:
79
+ end
80
+ end
81
+ end
82
+ end
data/lib/pdk/config.rb CHANGED
@@ -33,20 +33,12 @@ module PDK
33
33
  }.merge(options)
34
34
  end
35
35
 
36
- # The user configuration settings.
37
- # @deprecated This method is only provided as a courtesy until the `pdk set config` CLI and associated changes in this class, are completed.
38
- # Any read-only operations should be using `.get` or `.get_within_scopes`
39
- # @return [PDK::Config::Namespace]
40
- def user
41
- user_config
42
- end
43
-
44
36
  # The system level configuration settings.
45
37
  # @return [PDK::Config::Namespace]
46
38
  # @api private
47
39
  def system_config
48
40
  local_options = @config_options
49
- @system ||= PDK::Config::JSON.new('system', file: local_options['system.path']) do
41
+ @system_config ||= PDK::Config::JSON.new('system', file: local_options['system.path']) do
50
42
  mount :module_defaults, PDK::Config::JSON.new(file: local_options['system.module_defaults.path'])
51
43
  end
52
44
  end
@@ -56,7 +48,7 @@ module PDK
56
48
  # @api private
57
49
  def user_config
58
50
  local_options = @config_options
59
- @user ||= PDK::Config::JSON.new('user', file: local_options['user.path']) do
51
+ @user_config ||= PDK::Config::JSON.new('user', file: local_options['user.path']) do
60
52
  mount :module_defaults, PDK::Config::JSON.new(file: local_options['user.module_defaults.path'])
61
53
 
62
54
  # Due to the json-schema gem having issues with Windows based paths, and only supporting Draft 05 (or less) do
@@ -119,6 +111,8 @@ module PDK
119
111
  # - PDK.config.get('user.a.b.c')
120
112
  # - PDK.config.get(['user', 'a', 'b', 'c'])
121
113
  # - PDK.config.get('user', 'a', 'b', 'c')
114
+ # @param root [Array[String], String] The root setting name or the entire setting name as a single string
115
+ # @param keys [String] The child names of the setting
122
116
  # @return [PDK::Config::Namespace, Object, nil] The value of the configuration setting. Returns nil if it does no exist
123
117
  def get(root, *keys)
124
118
  return nil if root.nil? || root.empty?
@@ -169,6 +163,26 @@ module PDK
169
163
  yield value unless value.nil?
170
164
  end
171
165
 
166
+ # Sets a configuration setting by name. This name can either be a String or an Array
167
+ # - PDK.config.set('user.a.b.c', ...)
168
+ # - PDK.config.set(['user', 'a', 'b', 'c'], ...)
169
+ # @param key [String, Array[String]] The name of the configuration key to change
170
+ # @param value [Object] The value to set the configuration setting to
171
+ # @param options [Hash] Changes the behaviour of the setting process
172
+ # @option options [Boolean] :force Disables any munging or array processing, and sets the value as it is. Default is false
173
+ # @return [Object] The new value of the configuration setting
174
+ def set(key, value, options = {})
175
+ options = {
176
+ force: false,
177
+ }.merge(options)
178
+
179
+ names = key.is_a?(String) ? split_key_string(key) : key
180
+ raise ArgumentError, _('Invalid configuration names') if names.nil? || !names.is_a?(Array) || names.empty?
181
+ scope_name = names[0]
182
+ raise ArgumentError, _("Unknown configuration root '%{name}'") % { name: scope_name } if all_scopes[scope_name].nil?
183
+ deep_set_object(value, options[:force], send(all_scopes[scope_name]), *names[1..-1])
184
+ end
185
+
172
186
  def self.bolt_analytics_config
173
187
  file = PDK::Util::Filesystem.expand_path('~/.puppetlabs/bolt/analytics.yaml')
174
188
  PDK::Config::YAML.new(file: file)
@@ -247,9 +261,9 @@ module PDK
247
261
 
248
262
  if answers.nil?
249
263
  PDK.logger.info _('No answer given, opting out of analytics collection.')
250
- PDK.config.user['analytics']['disabled'] = true
264
+ PDK.config.set(%w[user analytics disabled], true)
251
265
  else
252
- PDK.config.user['analytics']['disabled'] = !answers['enabled']
266
+ PDK.config.set(%w[user analytics disabled], !answers['enabled'])
253
267
  end
254
268
 
255
269
  PDK.logger.info(text: post_message, wrap: true)
@@ -260,7 +274,12 @@ module PDK
260
274
  #:nocov: This is a private method and is tested elsewhere
261
275
  def traverse_object(object, *names)
262
276
  return nil if object.nil? || !object.respond_to?(:[])
263
- return nil if names.nil? || names.empty?
277
+ return nil if names.nil?
278
+ # It's possible to pass in empty names at the root traversal layer
279
+ # but this should _only_ happen at the root namespace level
280
+ if names.empty?
281
+ return (object.is_a?(PDK::Config::Namespace) ? object : nil)
282
+ end
264
283
 
265
284
  name = names.shift
266
285
  value = object[name]
@@ -296,6 +315,70 @@ module PDK
296
315
  'system' => :system_config,
297
316
  }.freeze
298
317
  end
318
+
319
+ #:nocov: This is a private method and is tested elsewhere
320
+ # Deeply traverses an object tree via `[]` and sets the last
321
+ # element to the value specified.
322
+ #
323
+ # Creating any missing parent hashes during the traversal
324
+ def deep_set_object(value, force, namespace, *names)
325
+ raise ArgumentError, _('Missing or invalid namespace') unless namespace.is_a?(PDK::Config::Namespace)
326
+ raise ArgumentError, _('Missing a name to set') if names.nil? || names.empty?
327
+
328
+ name = names.shift
329
+ current_value = namespace[name]
330
+
331
+ # If the next thing in the traversal chain is another namespace, set the value using that child namespace.
332
+ if current_value.is_a?(PDK::Config::Namespace)
333
+ return deep_set_object(value, force, current_value, *names)
334
+ end
335
+
336
+ # We're at the end of the name traversal
337
+ if names.empty?
338
+ if force || !current_value.is_a?(Array)
339
+ namespace[name] = value
340
+ return value
341
+ end
342
+
343
+ # Arrays are a special case if we're not forcing the value
344
+ namespace[name] = current_value << value unless current_value.include?(value)
345
+ return value
346
+ end
347
+
348
+ # Need to generate a deep hash using the current remaining names
349
+ # So given an origin *names of ['a', 'b', 'c', 'd'] and a value 'foo',
350
+ # we eventually want a hash of `{"b"=>{"c"=>{"d"=>"foo"}}}`
351
+ #
352
+ # The code above has already shifted the first element so we currently have
353
+ # name : 'a'
354
+ # names: ['b', 'c', 'd']
355
+ #
356
+ #
357
+ # First we need to pop off the last element ('d') in this case as we need to set that in the `reduce` call below
358
+ # So now we have:
359
+ # name : 'a'
360
+ # names: ['b', 'c']
361
+ # last_name : 'd'
362
+ last_name = names.pop
363
+ # Using reduce and an accumulator, we create the nested hash from the deepest value first. In this case the deepest value
364
+ # is the last_name, so the starting condition is {"d"=>"foo"}
365
+ # After the first iteration ('c'), the accumulator has {"c"=>{"d"=>"foo"}}}
366
+ # After the last iteration ('b'), the accumulator has {"b"=>{"c"=>{"d"=>"foo"}}}
367
+ hash_value = names.reverse.reduce(last_name => value) { |accumulator, item| { item => accumulator } }
368
+
369
+ # If the current value is nil, then it can't be a namespace or an existing value
370
+ # or
371
+ # If the current value is not a Hash and are forcing the change.
372
+ if current_value.nil? || (force && !current_value.is_a?(Hash))
373
+ namespace[name] = hash_value
374
+ return value
375
+ end
376
+
377
+ raise ArgumentError, _("Unable to set '%{key}' to '%{value}' as it is not a Hash") % { key: namespace.name + '.' + name, value: hash_value } unless current_value.is_a?(Hash)
378
+
379
+ namespace[name] = current_value.merge(hash_value)
380
+ value
381
+ end
299
382
  #:nocov:
300
383
  end
301
384
  end
data/lib/pdk/context.rb CHANGED
@@ -33,13 +33,15 @@ module PDK
33
33
  # Abstract class which all PDK Contexts will subclass from.
34
34
  # @abstract
35
35
  class AbstractContext
36
- # The root of this context, for example the module root when inside a module. This is different from context_path
36
+ # The root of this context, for example the module root when inside a module. This can be different from context_path
37
37
  # For example a Module context_path could be /path/to/module/manifests/ but the root_path will be /path/to/module as
38
- # that is the root of the Module context
39
- # @return [String, Nil]
40
- attr_reader :root_path
38
+ # that is the root of the Module context. Defaults to the context_path if not set.
39
+ # @return [String]
40
+ def root_path
41
+ @root_path || @context_path
42
+ end
41
43
 
42
- # The path used to create this context, for example the current working directory. This is different from root_path
44
+ # The path used to create this context, for example the current working directory. This can be different from root_path
43
45
  # For example a Module context_path could be /path/to/module/manifests/ but the root_path will be /path/to/module as
44
46
  # that is the root of the Module context
45
47
  # @return [String]
@@ -48,6 +50,7 @@ module PDK
48
50
  # @param context_path [String] The path where this context was created from e.g. Dir.pwd
49
51
  def initialize(context_path)
50
52
  @context_path = context_path
53
+ @root_path = nil
51
54
  end
52
55
 
53
56
  # Whether the current context is compatible with the PDK e.g. in a Module context, whether it has the correct metadata.json content