pdk 2.4.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (153) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1336 -1329
  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 -278
  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 +2111 -2094
  153. metadata +3 -3
@@ -1,28 +1,28 @@
1
- require 'pdk'
2
-
3
- module PDK
4
- module Generate
5
- class Provider < PuppetObject
6
- def friendly_name
7
- 'Resource API Provider'.freeze
8
- end
9
-
10
- def template_files
11
- files = {
12
- 'provider_spec.erb' => File.join('spec', 'unit', 'puppet', 'provider', object_name, object_name) + '_spec.rb',
13
- 'provider_type_spec.erb' => File.join('spec', 'unit', 'puppet', 'type', object_name) + '_spec.rb',
14
- }
15
- return files if spec_only?
16
- files.merge(
17
- 'provider.erb' => File.join('lib', 'puppet', 'provider', object_name, object_name) + '.rb',
18
- 'provider_type.erb' => File.join('lib', 'puppet', 'type', object_name) + '.rb',
19
- )
20
- end
21
-
22
- def template_data
23
- { name: object_name,
24
- provider_class: class_name_from_object_name(object_name) }
25
- end
26
- end
27
- end
28
- end
1
+ require 'pdk'
2
+
3
+ module PDK
4
+ module Generate
5
+ class Provider < PuppetObject
6
+ def friendly_name
7
+ 'Resource API Provider'.freeze
8
+ end
9
+
10
+ def template_files
11
+ files = {
12
+ 'provider_spec.erb' => File.join('spec', 'unit', 'puppet', 'provider', object_name, object_name) + '_spec.rb',
13
+ 'provider_type_spec.erb' => File.join('spec', 'unit', 'puppet', 'type', object_name) + '_spec.rb',
14
+ }
15
+ return files if spec_only?
16
+ files.merge(
17
+ 'provider.erb' => File.join('lib', 'puppet', 'provider', object_name, object_name) + '.rb',
18
+ 'provider_type.erb' => File.join('lib', 'puppet', 'type', object_name) + '.rb',
19
+ )
20
+ end
21
+
22
+ def template_data
23
+ { name: object_name,
24
+ provider_class: class_name_from_object_name(object_name) }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,43 +1,43 @@
1
- require 'pdk'
2
-
3
- module PDK
4
- module Generate
5
- class PuppetClass < PuppetObject
6
- PUPPET_STRINGS_TYPE = 'puppet_classes'.freeze
7
-
8
- def initialize(*_args)
9
- super
10
- object_name_parts = @object_name.split('::')
11
-
12
- @object_name = if object_name_parts.first == module_name
13
- object_name
14
- else
15
- [module_name, object_name].join('::')
16
- end
17
- end
18
-
19
- def friendly_name
20
- 'Puppet Class'.freeze
21
- end
22
-
23
- def template_files
24
- # Calculate the class tests name
25
- class_name_parts = object_name.split('::')
26
- # Drop the module name if the object name contains multiple parts
27
- class_name_parts.delete_at(0) if class_name_parts.length > 1
28
- files = { 'class_spec.erb' => File.join('spec', 'classes', *class_name_parts) + '_spec.rb' }
29
- return files if spec_only?
30
-
31
- class_name_parts = object_name.split('::')[1..-1]
32
- class_name_parts << 'init' if class_name_parts.empty?
33
- files['class.erb'] = File.join('manifests', *class_name_parts) + '.pp'
34
-
35
- files
36
- end
37
-
38
- def template_data
39
- { name: object_name }
40
- end
41
- end
42
- end
43
- end
1
+ require 'pdk'
2
+
3
+ module PDK
4
+ module Generate
5
+ class PuppetClass < PuppetObject
6
+ PUPPET_STRINGS_TYPE = 'puppet_classes'.freeze
7
+
8
+ def initialize(*_args)
9
+ super
10
+ object_name_parts = @object_name.split('::')
11
+
12
+ @object_name = if object_name_parts.first == module_name
13
+ object_name
14
+ else
15
+ [module_name, object_name].join('::')
16
+ end
17
+ end
18
+
19
+ def friendly_name
20
+ 'Puppet Class'.freeze
21
+ end
22
+
23
+ def template_files
24
+ # Calculate the class tests name
25
+ class_name_parts = object_name.split('::')
26
+ # Drop the module name if the object name contains multiple parts
27
+ class_name_parts.delete_at(0) if class_name_parts.length > 1
28
+ files = { 'class_spec.erb' => File.join('spec', 'classes', *class_name_parts) + '_spec.rb' }
29
+ return files if spec_only?
30
+
31
+ class_name_parts = object_name.split('::')[1..-1]
32
+ class_name_parts << 'init' if class_name_parts.empty?
33
+ files['class.erb'] = File.join('manifests', *class_name_parts) + '.pp'
34
+
35
+ files
36
+ end
37
+
38
+ def template_data
39
+ { name: object_name }
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,232 +1,232 @@
1
- require 'pdk'
2
-
3
- module PDK
4
- module Generate
5
- class PuppetObject
6
- attr_reader :context
7
- attr_reader :object_name
8
- attr_reader :options
9
-
10
- # Initialises the PDK::Generate::PuppetObject object.
11
- #
12
- # In general, this object should never be instantiated directly. Instead,
13
- # one of the subclasses should be used e.g. PDK::Generate::Klass.
14
- #
15
- # New subclasses generally only need to inherit this class, set the
16
- # OBJECT_TYPE constant and implement the {#template_data},
17
- # {#target_object_path} and {#target_spec_path} methods.
18
- #
19
- # @param module_dir [String] The path to the root of module that the
20
- # will contain the object.
21
- # @param object_name [String] The name of the object.
22
- # @param options [Hash{Symbol => Object}]
23
- def initialize(context, object_name, options)
24
- raise ArgumentError, _('Expected PDK::Context::AbstractContext but got \'%{klass}\' for context') % { klass: context.class } unless context.is_a?(PDK::Context::AbstractContext)
25
- @context = context
26
- @options = options
27
- @object_name = object_name
28
- end
29
-
30
- # Whether the generator should only return test (spec) files
31
- # @return [Boolean]
32
- def spec_only?
33
- @options[:spec_only]
34
- end
35
-
36
- # Subclass and implement {#friendly_name} to provide a nice name to show users in CLI
37
- # @abstract
38
- # @return String
39
- def friendly_name
40
- raise NotImplementedError
41
- end
42
-
43
- # Subclass and implement {#template_files} to provide the template files to
44
- # render. Implementations of this method should return a Hash!{String => String}.
45
- # @abstract
46
- # @return Hash{String => String} Hash key is the source template file and the Hash value is
47
- # the relative destination path
48
- def template_files
49
- raise NotImplementedError
50
- end
51
-
52
- # Subclass and implement {#template_data} to provide data to the templates during rendering.
53
- # @abstract
54
- # @return Hash{Symbol => Object}
55
- def template_data
56
- raise NotImplementedError
57
- end
58
-
59
- # Raises an error if any pre-conditions are not met
60
- #
61
- # @return [void]
62
- # @abstract
63
- def check_preconditions
64
- raise ArgumentError, _('Expected a module context but got %{context_name}') % { context_name: context.display_name } unless context.is_a?(PDK::Context::Module)
65
- end
66
-
67
- # Check the preconditions of this template group, behaving as a predicate rather than raising an exception.
68
- #
69
- # @return [Boolean] true if the generator is safe to run, otherwise false.
70
- def can_run?
71
- check_preconditions
72
- true
73
- rescue StandardError
74
- false
75
- end
76
-
77
- # Creates an instance of an update manager
78
- # @api private
79
- def update_manager_instance
80
- require 'pdk/module/update_manager'
81
- PDK::Module::UpdateManager.new
82
- end
83
-
84
- # Stages and then executes the changes for the templates to be rendereed.
85
- # This is the main entry point for the class.
86
- #
87
- # @see #stage_changes
88
- # @return [PDK::Module::UpdateManager] The update manager which implemented the changes
89
- # @api public
90
- def run(update_manager = update_manager_instance)
91
- stage_changes(update_manager).sync_changes!
92
- update_manager
93
- end
94
-
95
- # Check that the templates can be rendered. Find an appropriate template
96
- # and stages the target files from the template. This is the main entry
97
- # point for the class.
98
- #
99
- # @raise [PDK::CLI::ExitWithError] if the target files already exist.
100
- # @raise [PDK::CLI::FatalError] (see #render_file)
101
- # @return [PDK::Module::UpdateManager] The update manager with the staged changes
102
- # @api public
103
- def stage_changes(update_manager)
104
- check_preconditions
105
-
106
- with_templates do |template_dir|
107
- template_files.each do |source_file, relative_dest_path|
108
- new_content = template_dir.render_single_item(source_file, template_data)
109
- next if new_content.nil?
110
-
111
- stage_change(relative_dest_path, new_content, update_manager)
112
- end
113
- end
114
- non_template_files.each { |relative_dest_path, content| stage_change(relative_dest_path, content, update_manager) }
115
-
116
- update_manager
117
- end
118
-
119
- # Stages a single file into the Update Manager.
120
- # @return [void]
121
- # @api private
122
- def stage_change(relative_dest_path, content, update_manager)
123
- absolute_file_path = File.join(context.root_path, relative_dest_path)
124
- if PDK::Util::Filesystem.exist?(absolute_file_path)
125
- raise PDK::CLI::ExitWithError, _("Unable to generate %{object_type}; '%{file}' already exists.") % {
126
- file: absolute_file_path,
127
- object_type: spec_only? ? 'unit test' : friendly_name,
128
- }
129
- end
130
- update_manager.add_file(absolute_file_path, content)
131
- end
132
-
133
- # A subclass may wish to stage files into the Update Manager, but the content is not templated. Subclasses
134
- # can override this method to stage arbitrary files
135
- #
136
- # @api private
137
- # @return [Hash{String => String}] A Hash with the relative file path as the key and the new file content as the value.
138
- # @abstract
139
- def non_template_files
140
- {}
141
- end
142
-
143
- # Search the possible template directories in order of preference to find
144
- # a template that can be used to render a new object of the specified
145
- # type.
146
- #
147
- # @yieldparam template_paths [Hash{Symbol => String}] :object contains
148
- # the path on disk to the template file for the object, :spec contains
149
- # the path on disk to the template file for the tests for the object
150
- # (if it exists).
151
- # @yieldparam config_hash [Hash{Object => Object}] the contents of the
152
- # :global key in the config_defaults.yml file.
153
- #
154
- # @raise [PDK::CLI::FatalError] if no suitable template could be found.
155
- #
156
- # @api private
157
- def with_templates
158
- require 'pdk/logger'
159
- require 'pdk/util/template_uri'
160
-
161
- templates.each do |template|
162
- if template[:uri].nil?
163
- PDK.logger.debug(_('No %{dir_type} template found; trying next template directory.') % { dir_type: template[:type] })
164
- next
165
- end
166
-
167
- PDK::Template.with(PDK::Util::TemplateURI.new(template[:uri]), context) do |template_dir|
168
- if template_files.any? { |source_file, _| template_dir.has_single_item?(source_file) }
169
- yield template_dir
170
- # TODO: refactor to a search-and-execute form instead
171
- return # work is done # rubocop:disable Lint/NonLocalExitFromIterator
172
- elsif template[:allow_fallback]
173
- PDK.logger.debug(_('Unable to find a %{type} template in %{url}; trying next template directory.') % { type: friendly_name, url: template[:uri] })
174
- else
175
- raise PDK::CLI::FatalError, _('Unable to find the %{type} template in %{url}.') % { type: friendly_name, url: template[:uri] }
176
- end
177
- end
178
- end
179
- rescue ArgumentError => e
180
- raise PDK::CLI::ExitWithError, e
181
- end
182
-
183
- # Provides the possible template directory locations in the order in
184
- # which they should be searched for a valid template.
185
- #
186
- # If a template-url has been specified on in the options hash (e.g. from
187
- # a CLI parameter), then this template directory will be checked first
188
- # and we do not fall back to the next possible template directory.
189
- #
190
- # If we have not been provided a specific template directory to use, we
191
- # try the template specified in the module metadata (as set during
192
- # PDK::Generate::Module) and fall back to the default template if
193
- # necessary.
194
- #
195
- # @return [Array<Hash{Symbol => Object}>] an array of hashes. Each hash
196
- # contains 3 keys: :type contains a String that describes the template
197
- # directory, :url contains a String with the URL to the template
198
- # directory, and :allow_fallback contains a Boolean that specifies if
199
- # the lookup process should proceed to the next template directory if
200
- # the template file is not in this template directory.
201
- #
202
- # @api private
203
- def templates
204
- require 'pdk/util/template_uri'
205
-
206
- @templates ||= PDK::Util::TemplateURI.templates(@options)
207
- end
208
-
209
- # Retrieves the name of the module (without the forge username) from the
210
- # module metadata.
211
- #
212
- # @return [String] The name of the module.
213
- #
214
- # @api private
215
- def module_name
216
- return nil unless context.is_a?(PDK::Context::Module)
217
-
218
- require 'pdk/util'
219
- @module_name ||= PDK::Util.module_metadata(context.root_path)['name'].rpartition('-').last
220
- rescue ArgumentError => e
221
- raise PDK::CLI::FatalError, e
222
- end
223
-
224
- private
225
-
226
- # Transform an object name into a ruby class name
227
- def class_name_from_object_name(object_name)
228
- object_name.to_s.split('_').map(&:capitalize).join
229
- end
230
- end
231
- end
232
- end
1
+ require 'pdk'
2
+
3
+ module PDK
4
+ module Generate
5
+ class PuppetObject
6
+ attr_reader :context
7
+ attr_reader :object_name
8
+ attr_reader :options
9
+
10
+ # Initialises the PDK::Generate::PuppetObject object.
11
+ #
12
+ # In general, this object should never be instantiated directly. Instead,
13
+ # one of the subclasses should be used e.g. PDK::Generate::Klass.
14
+ #
15
+ # New subclasses generally only need to inherit this class, set the
16
+ # OBJECT_TYPE constant and implement the {#template_data},
17
+ # {#target_object_path} and {#target_spec_path} methods.
18
+ #
19
+ # @param module_dir [String] The path to the root of module that the
20
+ # will contain the object.
21
+ # @param object_name [String] The name of the object.
22
+ # @param options [Hash{Symbol => Object}]
23
+ def initialize(context, object_name, options)
24
+ raise ArgumentError, _('Expected PDK::Context::AbstractContext but got \'%{klass}\' for context') % { klass: context.class } unless context.is_a?(PDK::Context::AbstractContext)
25
+ @context = context
26
+ @options = options
27
+ @object_name = object_name
28
+ end
29
+
30
+ # Whether the generator should only return test (spec) files
31
+ # @return [Boolean]
32
+ def spec_only?
33
+ @options[:spec_only]
34
+ end
35
+
36
+ # Subclass and implement {#friendly_name} to provide a nice name to show users in CLI
37
+ # @abstract
38
+ # @return String
39
+ def friendly_name
40
+ raise NotImplementedError
41
+ end
42
+
43
+ # Subclass and implement {#template_files} to provide the template files to
44
+ # render. Implementations of this method should return a Hash!{String => String}.
45
+ # @abstract
46
+ # @return Hash{String => String} Hash key is the source template file and the Hash value is
47
+ # the relative destination path
48
+ def template_files
49
+ raise NotImplementedError
50
+ end
51
+
52
+ # Subclass and implement {#template_data} to provide data to the templates during rendering.
53
+ # @abstract
54
+ # @return Hash{Symbol => Object}
55
+ def template_data
56
+ raise NotImplementedError
57
+ end
58
+
59
+ # Raises an error if any pre-conditions are not met
60
+ #
61
+ # @return [void]
62
+ # @abstract
63
+ def check_preconditions
64
+ raise ArgumentError, _('Expected a module context but got %{context_name}') % { context_name: context.display_name } unless context.is_a?(PDK::Context::Module)
65
+ end
66
+
67
+ # Check the preconditions of this template group, behaving as a predicate rather than raising an exception.
68
+ #
69
+ # @return [Boolean] true if the generator is safe to run, otherwise false.
70
+ def can_run?
71
+ check_preconditions
72
+ true
73
+ rescue StandardError
74
+ false
75
+ end
76
+
77
+ # Creates an instance of an update manager
78
+ # @api private
79
+ def update_manager_instance
80
+ require 'pdk/module/update_manager'
81
+ PDK::Module::UpdateManager.new
82
+ end
83
+
84
+ # Stages and then executes the changes for the templates to be rendereed.
85
+ # This is the main entry point for the class.
86
+ #
87
+ # @see #stage_changes
88
+ # @return [PDK::Module::UpdateManager] The update manager which implemented the changes
89
+ # @api public
90
+ def run(update_manager = update_manager_instance)
91
+ stage_changes(update_manager).sync_changes!
92
+ update_manager
93
+ end
94
+
95
+ # Check that the templates can be rendered. Find an appropriate template
96
+ # and stages the target files from the template. This is the main entry
97
+ # point for the class.
98
+ #
99
+ # @raise [PDK::CLI::ExitWithError] if the target files already exist.
100
+ # @raise [PDK::CLI::FatalError] (see #render_file)
101
+ # @return [PDK::Module::UpdateManager] The update manager with the staged changes
102
+ # @api public
103
+ def stage_changes(update_manager)
104
+ check_preconditions
105
+
106
+ with_templates do |template_dir|
107
+ template_files.each do |source_file, relative_dest_path|
108
+ new_content = template_dir.render_single_item(source_file, template_data)
109
+ next if new_content.nil?
110
+
111
+ stage_change(relative_dest_path, new_content, update_manager)
112
+ end
113
+ end
114
+ non_template_files.each { |relative_dest_path, content| stage_change(relative_dest_path, content, update_manager) }
115
+
116
+ update_manager
117
+ end
118
+
119
+ # Stages a single file into the Update Manager.
120
+ # @return [void]
121
+ # @api private
122
+ def stage_change(relative_dest_path, content, update_manager)
123
+ absolute_file_path = File.join(context.root_path, relative_dest_path)
124
+ if PDK::Util::Filesystem.exist?(absolute_file_path)
125
+ raise PDK::CLI::ExitWithError, _("Unable to generate %{object_type}; '%{file}' already exists.") % {
126
+ file: absolute_file_path,
127
+ object_type: spec_only? ? 'unit test' : friendly_name,
128
+ }
129
+ end
130
+ update_manager.add_file(absolute_file_path, content)
131
+ end
132
+
133
+ # A subclass may wish to stage files into the Update Manager, but the content is not templated. Subclasses
134
+ # can override this method to stage arbitrary files
135
+ #
136
+ # @api private
137
+ # @return [Hash{String => String}] A Hash with the relative file path as the key and the new file content as the value.
138
+ # @abstract
139
+ def non_template_files
140
+ {}
141
+ end
142
+
143
+ # Search the possible template directories in order of preference to find
144
+ # a template that can be used to render a new object of the specified
145
+ # type.
146
+ #
147
+ # @yieldparam template_paths [Hash{Symbol => String}] :object contains
148
+ # the path on disk to the template file for the object, :spec contains
149
+ # the path on disk to the template file for the tests for the object
150
+ # (if it exists).
151
+ # @yieldparam config_hash [Hash{Object => Object}] the contents of the
152
+ # :global key in the config_defaults.yml file.
153
+ #
154
+ # @raise [PDK::CLI::FatalError] if no suitable template could be found.
155
+ #
156
+ # @api private
157
+ def with_templates
158
+ require 'pdk/logger'
159
+ require 'pdk/util/template_uri'
160
+
161
+ templates.each do |template|
162
+ if template[:uri].nil?
163
+ PDK.logger.debug(_('No %{dir_type} template found; trying next template directory.') % { dir_type: template[:type] })
164
+ next
165
+ end
166
+
167
+ PDK::Template.with(PDK::Util::TemplateURI.new(template[:uri]), context) do |template_dir|
168
+ if template_files.any? { |source_file, _| template_dir.has_single_item?(source_file) }
169
+ yield template_dir
170
+ # TODO: refactor to a search-and-execute form instead
171
+ return # work is done # rubocop:disable Lint/NonLocalExitFromIterator
172
+ elsif template[:allow_fallback]
173
+ PDK.logger.debug(_('Unable to find a %{type} template in %{url}; trying next template directory.') % { type: friendly_name, url: template[:uri] })
174
+ else
175
+ raise PDK::CLI::FatalError, _('Unable to find the %{type} template in %{url}.') % { type: friendly_name, url: template[:uri] }
176
+ end
177
+ end
178
+ end
179
+ rescue ArgumentError => e
180
+ raise PDK::CLI::ExitWithError, e
181
+ end
182
+
183
+ # Provides the possible template directory locations in the order in
184
+ # which they should be searched for a valid template.
185
+ #
186
+ # If a template-url has been specified on in the options hash (e.g. from
187
+ # a CLI parameter), then this template directory will be checked first
188
+ # and we do not fall back to the next possible template directory.
189
+ #
190
+ # If we have not been provided a specific template directory to use, we
191
+ # try the template specified in the module metadata (as set during
192
+ # PDK::Generate::Module) and fall back to the default template if
193
+ # necessary.
194
+ #
195
+ # @return [Array<Hash{Symbol => Object}>] an array of hashes. Each hash
196
+ # contains 3 keys: :type contains a String that describes the template
197
+ # directory, :url contains a String with the URL to the template
198
+ # directory, and :allow_fallback contains a Boolean that specifies if
199
+ # the lookup process should proceed to the next template directory if
200
+ # the template file is not in this template directory.
201
+ #
202
+ # @api private
203
+ def templates
204
+ require 'pdk/util/template_uri'
205
+
206
+ @templates ||= PDK::Util::TemplateURI.templates(@options)
207
+ end
208
+
209
+ # Retrieves the name of the module (without the forge username) from the
210
+ # module metadata.
211
+ #
212
+ # @return [String] The name of the module.
213
+ #
214
+ # @api private
215
+ def module_name
216
+ return nil unless context.is_a?(PDK::Context::Module)
217
+
218
+ require 'pdk/util'
219
+ @module_name ||= PDK::Util.module_metadata(context.root_path)['name'].rpartition('-').last
220
+ rescue ArgumentError => e
221
+ raise PDK::CLI::FatalError, e
222
+ end
223
+
224
+ private
225
+
226
+ # Transform an object name into a ruby class name
227
+ def class_name_from_object_name(object_name)
228
+ object_name.to_s.split('_').map(&:capitalize).join
229
+ end
230
+ end
231
+ end
232
+ end