pdk 1.9.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (163) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +744 -711
  3. data/README.md +23 -21
  4. data/lib/pdk/answer_file.rb +3 -112
  5. data/lib/pdk/bolt.rb +20 -0
  6. data/lib/pdk/cli/build.rb +51 -54
  7. data/lib/pdk/cli/bundle.rb +33 -29
  8. data/lib/pdk/cli/console.rb +148 -0
  9. data/lib/pdk/cli/convert.rb +46 -37
  10. data/lib/pdk/cli/env.rb +51 -0
  11. data/lib/pdk/cli/errors.rb +4 -3
  12. data/lib/pdk/cli/exec/command.rb +285 -0
  13. data/lib/pdk/cli/exec/interactive_command.rb +109 -0
  14. data/lib/pdk/cli/exec.rb +32 -201
  15. data/lib/pdk/cli/exec_group.rb +79 -43
  16. data/lib/pdk/cli/get/config.rb +26 -0
  17. data/lib/pdk/cli/get.rb +22 -0
  18. data/lib/pdk/cli/new/class.rb +20 -22
  19. data/lib/pdk/cli/new/defined_type.rb +21 -21
  20. data/lib/pdk/cli/new/fact.rb +27 -0
  21. data/lib/pdk/cli/new/function.rb +27 -0
  22. data/lib/pdk/cli/new/module.rb +40 -29
  23. data/lib/pdk/cli/new/provider.rb +18 -18
  24. data/lib/pdk/cli/new/task.rb +23 -22
  25. data/lib/pdk/cli/new/test.rb +52 -0
  26. data/lib/pdk/cli/new/transport.rb +27 -0
  27. data/lib/pdk/cli/new.rb +15 -9
  28. data/lib/pdk/cli/release/prep.rb +39 -0
  29. data/lib/pdk/cli/release/publish.rb +46 -0
  30. data/lib/pdk/cli/release.rb +185 -0
  31. data/lib/pdk/cli/remove/config.rb +83 -0
  32. data/lib/pdk/cli/remove.rb +22 -0
  33. data/lib/pdk/cli/set/config.rb +121 -0
  34. data/lib/pdk/cli/set.rb +22 -0
  35. data/lib/pdk/cli/test/unit.rb +71 -69
  36. data/lib/pdk/cli/test.rb +9 -8
  37. data/lib/pdk/cli/update.rb +38 -21
  38. data/lib/pdk/cli/util/command_redirector.rb +13 -3
  39. data/lib/pdk/cli/util/interview.rb +25 -9
  40. data/lib/pdk/cli/util/option_normalizer.rb +6 -6
  41. data/lib/pdk/cli/util/option_validator.rb +19 -9
  42. data/lib/pdk/cli/util/spinner.rb +13 -0
  43. data/lib/pdk/cli/util/update_manager_printer.rb +82 -0
  44. data/lib/pdk/cli/util.rb +105 -48
  45. data/lib/pdk/cli/validate.rb +96 -111
  46. data/lib/pdk/cli.rb +134 -87
  47. data/lib/pdk/config/errors.rb +5 -0
  48. data/lib/pdk/config/ini_file.rb +184 -0
  49. data/lib/pdk/config/ini_file_setting.rb +35 -0
  50. data/lib/pdk/config/json.rb +35 -0
  51. data/lib/pdk/config/json_schema_namespace.rb +137 -0
  52. data/lib/pdk/config/json_schema_setting.rb +51 -0
  53. data/lib/pdk/config/json_with_schema.rb +47 -0
  54. data/lib/pdk/config/namespace.rb +362 -0
  55. data/lib/pdk/config/setting.rb +134 -0
  56. data/lib/pdk/config/task_schema.json +116 -0
  57. data/lib/pdk/config/validator.rb +31 -0
  58. data/lib/pdk/config/yaml.rb +41 -0
  59. data/lib/pdk/config/yaml_with_schema.rb +51 -0
  60. data/lib/pdk/config.rb +304 -0
  61. data/lib/pdk/context/control_repo.rb +61 -0
  62. data/lib/pdk/context/module.rb +28 -0
  63. data/lib/pdk/context/none.rb +22 -0
  64. data/lib/pdk/context.rb +98 -0
  65. data/lib/pdk/control_repo.rb +89 -0
  66. data/lib/pdk/generate/defined_type.rb +27 -33
  67. data/lib/pdk/generate/fact.rb +26 -0
  68. data/lib/pdk/generate/function.rb +49 -0
  69. data/lib/pdk/generate/module.rb +160 -153
  70. data/lib/pdk/generate/provider.rb +16 -69
  71. data/lib/pdk/generate/puppet_class.rb +27 -32
  72. data/lib/pdk/generate/puppet_object.rb +100 -159
  73. data/lib/pdk/generate/task.rb +34 -51
  74. data/lib/pdk/generate/transport.rb +34 -0
  75. data/lib/pdk/generate.rb +21 -8
  76. data/lib/pdk/logger.rb +24 -6
  77. data/lib/pdk/module/build.rb +125 -37
  78. data/lib/pdk/module/convert.rb +146 -65
  79. data/lib/pdk/module/metadata.rb +72 -71
  80. data/lib/pdk/module/release.rb +255 -0
  81. data/lib/pdk/module/update.rb +48 -37
  82. data/lib/pdk/module/update_manager.rb +75 -39
  83. data/lib/pdk/module.rb +10 -2
  84. data/lib/pdk/monkey_patches.rb +268 -0
  85. data/lib/pdk/report/event.rb +36 -48
  86. data/lib/pdk/report.rb +35 -22
  87. data/lib/pdk/template/fetcher/git.rb +84 -0
  88. data/lib/pdk/template/fetcher/local.rb +29 -0
  89. data/lib/pdk/template/fetcher.rb +100 -0
  90. data/lib/pdk/template/renderer/v1/legacy_template_dir.rb +108 -0
  91. data/lib/pdk/template/renderer/v1/renderer.rb +131 -0
  92. data/lib/pdk/template/renderer/v1/template_file.rb +100 -0
  93. data/lib/pdk/template/renderer/v1.rb +25 -0
  94. data/lib/pdk/template/renderer.rb +97 -0
  95. data/lib/pdk/template/template_dir.rb +67 -0
  96. data/lib/pdk/template.rb +52 -0
  97. data/lib/pdk/tests/unit.rb +101 -51
  98. data/lib/pdk/util/bundler.rb +44 -42
  99. data/lib/pdk/util/changelog_generator.rb +138 -0
  100. data/lib/pdk/util/env.rb +48 -0
  101. data/lib/pdk/util/filesystem.rb +139 -2
  102. data/lib/pdk/util/git.rb +108 -8
  103. data/lib/pdk/util/json_finder.rb +86 -0
  104. data/lib/pdk/util/puppet_strings.rb +125 -0
  105. data/lib/pdk/util/puppet_version.rb +71 -87
  106. data/lib/pdk/util/ruby_version.rb +49 -25
  107. data/lib/pdk/util/template_uri.rb +283 -0
  108. data/lib/pdk/util/vendored_file.rb +34 -42
  109. data/lib/pdk/util/version.rb +11 -10
  110. data/lib/pdk/util/windows/api_types.rb +74 -44
  111. data/lib/pdk/util/windows/file.rb +31 -27
  112. data/lib/pdk/util/windows/process.rb +74 -0
  113. data/lib/pdk/util/windows/string.rb +19 -12
  114. data/lib/pdk/util/windows.rb +2 -0
  115. data/lib/pdk/util.rb +111 -124
  116. data/lib/pdk/validate/control_repo/control_repo_validator_group.rb +23 -0
  117. data/lib/pdk/validate/control_repo/environment_conf_validator.rb +98 -0
  118. data/lib/pdk/validate/external_command_validator.rb +213 -0
  119. data/lib/pdk/validate/internal_ruby_validator.rb +101 -0
  120. data/lib/pdk/validate/invokable_validator.rb +238 -0
  121. data/lib/pdk/validate/metadata/metadata_json_lint_validator.rb +84 -0
  122. data/lib/pdk/validate/metadata/metadata_syntax_validator.rb +76 -0
  123. data/lib/pdk/validate/metadata/metadata_validator_group.rb +20 -0
  124. data/lib/pdk/validate/puppet/puppet_epp_validator.rb +131 -0
  125. data/lib/pdk/validate/puppet/puppet_lint_validator.rb +66 -0
  126. data/lib/pdk/validate/puppet/puppet_plan_syntax_validator.rb +38 -0
  127. data/lib/pdk/validate/puppet/puppet_syntax_validator.rb +135 -0
  128. data/lib/pdk/validate/puppet/puppet_validator_group.rb +22 -0
  129. data/lib/pdk/validate/ruby/ruby_rubocop_validator.rb +79 -0
  130. data/lib/pdk/validate/ruby/ruby_validator_group.rb +19 -0
  131. data/lib/pdk/validate/tasks/tasks_metadata_lint_validator.rb +83 -0
  132. data/lib/pdk/validate/tasks/tasks_name_validator.rb +45 -0
  133. data/lib/pdk/validate/tasks/tasks_validator_group.rb +20 -0
  134. data/lib/pdk/validate/validator.rb +120 -0
  135. data/lib/pdk/validate/validator_group.rb +107 -0
  136. data/lib/pdk/validate/yaml/yaml_syntax_validator.rb +86 -0
  137. data/lib/pdk/validate/yaml/yaml_validator_group.rb +19 -0
  138. data/lib/pdk/validate.rb +86 -12
  139. data/lib/pdk/version.rb +2 -2
  140. data/lib/pdk.rb +60 -10
  141. metadata +138 -100
  142. data/lib/pdk/cli/module/build.rb +0 -14
  143. data/lib/pdk/cli/module/generate.rb +0 -45
  144. data/lib/pdk/cli/module.rb +0 -14
  145. data/lib/pdk/i18n.rb +0 -4
  146. data/lib/pdk/module/templatedir.rb +0 -321
  147. data/lib/pdk/template_file.rb +0 -95
  148. data/lib/pdk/validate/base_validator.rb +0 -215
  149. data/lib/pdk/validate/metadata/metadata_json_lint.rb +0 -86
  150. data/lib/pdk/validate/metadata/metadata_syntax.rb +0 -109
  151. data/lib/pdk/validate/metadata_validator.rb +0 -30
  152. data/lib/pdk/validate/puppet/puppet_lint.rb +0 -67
  153. data/lib/pdk/validate/puppet/puppet_syntax.rb +0 -112
  154. data/lib/pdk/validate/puppet_validator.rb +0 -30
  155. data/lib/pdk/validate/ruby/rubocop.rb +0 -77
  156. data/lib/pdk/validate/ruby_validator.rb +0 -29
  157. data/lib/pdk/validate/tasks/metadata_lint.rb +0 -126
  158. data/lib/pdk/validate/tasks/name.rb +0 -88
  159. data/lib/pdk/validate/tasks_validator.rb +0 -33
  160. data/lib/pdk/validate/yaml/syntax.rb +0 -109
  161. data/lib/pdk/validate/yaml_validator.rb +0 -31
  162. data/locales/config.yaml +0 -21
  163. data/locales/pdk.pot +0 -1291
@@ -1,10 +1,4 @@
1
- require 'fileutils'
2
- require 'minitar'
3
- require 'zlib'
4
- require 'pathspec'
5
- require 'find'
6
- require 'pdk/module'
7
- require 'pdk/tests/unit'
1
+ require 'pdk'
8
2
 
9
3
  module PDK
10
4
  module Module
@@ -13,12 +7,11 @@ module PDK
13
7
  new(options).build
14
8
  end
15
9
 
16
- attr_reader :module_dir
17
- attr_reader :target_dir
10
+ attr_reader :module_dir, :target_dir
18
11
 
19
12
  def initialize(options = {})
20
- @module_dir = File.expand_path(options[:module_dir] || Dir.pwd)
21
- @target_dir = File.expand_path(options[:'target-dir'] || File.join(module_dir, 'pkg'))
13
+ @module_dir = PDK::Util::Filesystem.expand_path(options[:module_dir] || Dir.pwd)
14
+ @target_dir = PDK::Util::Filesystem.expand_path(options[:'target-dir'] || File.join(module_dir, 'pkg'))
22
15
  end
23
16
 
24
17
  # Read and parse the values from metadata.json for the module that is
@@ -26,6 +19,8 @@ module PDK
26
19
  #
27
20
  # @return [Hash{String => Object}] The hash of metadata values.
28
21
  def metadata
22
+ require 'pdk/module/metadata'
23
+
29
24
  @metadata ||= PDK::Module::Metadata.from_file(File.join(module_dir, 'metadata.json')).data
30
25
  end
31
26
 
@@ -51,7 +46,7 @@ module PDK
51
46
  # Verify if there is an existing package in the target directory and prompts
52
47
  # the user if they want to overwrite it.
53
48
  def package_already_exists?
54
- File.exist? package_file
49
+ PDK::Util::Filesystem.exist?(package_file)
55
50
  end
56
51
 
57
52
  # Check if the module is PDK Compatible. If not, then prompt the user if
@@ -73,14 +68,14 @@ module PDK
73
68
  def create_build_dir
74
69
  cleanup_build_dir
75
70
 
76
- FileUtils.mkdir_p(build_dir)
71
+ PDK::Util::Filesystem.mkdir_p(build_dir)
77
72
  end
78
73
 
79
74
  # Remove the temporary build directory and all its contents from disk.
80
75
  #
81
76
  # @return nil.
82
77
  def cleanup_build_dir
83
- FileUtils.rm_rf(build_dir, secure: true)
78
+ PDK::Util::Filesystem.rm_rf(build_dir, secure: true)
84
79
  end
85
80
 
86
81
  # Combine the module name and version into a Forge-compatible dash
@@ -90,7 +85,7 @@ module PDK
90
85
  def release_name
91
86
  @release_name ||= [
92
87
  metadata['name'],
93
- metadata['version'],
88
+ metadata['version']
94
89
  ].join('-')
95
90
  end
96
91
 
@@ -99,6 +94,8 @@ module PDK
99
94
  #
100
95
  # @return nil
101
96
  def stage_module_in_build_dir
97
+ require 'find'
98
+
102
99
  Find.find(module_dir) do |path|
103
100
  next if path == module_dir
104
101
 
@@ -112,16 +109,24 @@ module PDK
112
109
  #
113
110
  # @return nil.
114
111
  def stage_path(path)
112
+ require 'pathname'
113
+
115
114
  relative_path = Pathname.new(path).relative_path_from(Pathname.new(module_dir))
116
115
  dest_path = File.join(build_dir, relative_path)
117
116
 
118
- if File.directory?(path)
119
- FileUtils.mkdir_p(dest_path, mode: File.stat(path).mode)
120
- elsif File.symlink?(path)
117
+ validate_path_encoding!(relative_path.to_path)
118
+
119
+ if PDK::Util::Filesystem.directory?(path)
120
+ PDK::Util::Filesystem.mkdir_p(dest_path, mode: PDK::Util::Filesystem.stat(path).mode)
121
+ elsif PDK::Util::Filesystem.symlink?(path)
121
122
  warn_symlink(path)
122
123
  else
123
- FileUtils.cp(path, dest_path, preserve: true)
124
+ validate_ustar_path!(relative_path.to_path)
125
+ PDK::Util::Filesystem.cp(path, dest_path, preserve: true)
124
126
  end
127
+ rescue ArgumentError => e
128
+ raise PDK::CLI::ExitWithError, format('%{message} Rename the file or exclude it from the package ' \
129
+ 'by adding it to the .pdkignore file in your module.', message: e.message)
125
130
  end
126
131
 
127
132
  # Check if the given path matches one of the patterns listed in the
@@ -131,7 +136,7 @@ module PDK
131
136
  #
132
137
  # @return [Boolean] true if the path matches and should be ignored.
133
138
  def ignored_path?(path)
134
- path = path.to_s + '/' if File.directory?(path)
139
+ path = "#{path}/" if PDK::Util::Filesystem.directory?(path)
135
140
 
136
141
  !ignored_files.match_paths([path], module_dir).empty?
137
142
  end
@@ -143,13 +148,80 @@ module PDK
143
148
  #
144
149
  # @return nil.
145
150
  def warn_symlink(path)
151
+ require 'pathname'
152
+
146
153
  symlink_path = Pathname.new(path)
147
154
  module_path = Pathname.new(module_dir)
148
155
 
149
- PDK.logger.warn _('Symlinks in modules are not supported and will not be included in the package. Please investigate symlink %{from} -> %{to}.') % {
150
- from: symlink_path.relative_path_from(module_path),
151
- to: symlink_path.realpath.relative_path_from(module_path),
152
- }
156
+ PDK.logger.warn format('Symlinks in modules are not supported and will not be included in the package. Please investigate symlink %{from} -> %{to}.',
157
+ from: symlink_path.relative_path_from(module_path), to: symlink_path.realpath.relative_path_from(module_path))
158
+ end
159
+
160
+ # Checks if the path length will fit into the POSIX.1-1998 (ustar) tar
161
+ # header format.
162
+ #
163
+ # POSIX.1-2001 (which allows paths of infinite length) was adopted by GNU
164
+ # tar in 2004 and is supported by minitar 0.7 and above. Unfortunately
165
+ # much of the Puppet ecosystem still uses minitar 0.6.1.
166
+ #
167
+ # POSIX.1-1998 tar format does not allow for paths greater than 256 bytes,
168
+ # or paths that can't be split into a prefix of 155 bytes (max) and
169
+ # a suffix of 100 bytes (max).
170
+ #
171
+ # This logic was pretty much copied from the private method
172
+ # {Archive::Tar::Minitar::Writer#split_name}.
173
+ #
174
+ # @param path [String] the relative path to be added to the tar file.
175
+ #
176
+ # @raise [ArgumentError] if the path is too long or could not be split.
177
+ #
178
+ # @return [nil]
179
+ def validate_ustar_path!(path)
180
+ raise ArgumentError, format("The path '%{path}' is longer than 256 bytes.", path: path) if path.bytesize > 256
181
+
182
+ if path.bytesize <= 100
183
+ prefix = ''
184
+ else
185
+ parts = path.split(File::SEPARATOR)
186
+ newpath = parts.pop
187
+ nxt = ''
188
+
189
+ loop do
190
+ nxt = parts.pop || ''
191
+ break if newpath.bytesize + 1 + nxt.bytesize >= 100
192
+
193
+ newpath = File.join(nxt, newpath)
194
+ end
195
+
196
+ prefix = File.join(*parts, nxt)
197
+ path = newpath
198
+ end
199
+
200
+ return unless path.bytesize > 100 || prefix.bytesize > 155
201
+
202
+ raise ArgumentError,
203
+ format("'%{path}' could not be split at a directory separator into two " \
204
+ 'parts, the first having a maximum length of 155 bytes and the ' \
205
+ 'second having a maximum length of 100 bytes.', path: path)
206
+ end
207
+
208
+ # Checks if the path contains any non-ASCII characters.
209
+ #
210
+ # Java will throw an error when it encounters a path containing
211
+ # characters that are not supported by the hosts locale. In order to
212
+ # maximise compatibility we limit the paths to contain only ASCII
213
+ # characters, which should be part of any locale character set.
214
+ #
215
+ # @param path [String] the relative path to be added to the tar file.
216
+ #
217
+ # @raise [ArgumentError] if the path contains non-ASCII characters.
218
+ #
219
+ # @return [nil]
220
+ def validate_path_encoding!(path)
221
+ return unless /[^\x00-\x7F]/.match?(path)
222
+
223
+ raise ArgumentError, format("'%{path}' can only include ASCII characters in its path or " \
224
+ 'filename in order to be compatible with a wide range of hosts.', path: path)
153
225
  end
154
226
 
155
227
  # Creates a gzip compressed tarball of the build directory.
@@ -159,12 +231,31 @@ module PDK
159
231
  #
160
232
  # @return nil.
161
233
  def build_package
162
- FileUtils.rm_f(package_file)
234
+ require 'zlib'
235
+ require 'minitar'
236
+ require 'find'
237
+
238
+ PDK::Util::Filesystem.rm_f(package_file)
163
239
 
164
240
  Dir.chdir(target_dir) do
165
- Zlib::GzipWriter.open(package_file) do |package_fd|
166
- Minitar.pack(release_name, package_fd)
241
+ gz = Zlib::GzipWriter.new(File.open(package_file, 'wb')) # rubocop:disable PDK/FileOpen
242
+ tar = Minitar::Output.new(gz)
243
+ Find.find(release_name) do |entry|
244
+ entry_meta = {
245
+ name: entry
246
+ }
247
+
248
+ orig_mode = PDK::Util::Filesystem.stat(entry).mode
249
+ min_mode = Minitar.dir?(entry) ? 0o755 : 0o644
250
+
251
+ entry_meta[:mode] = orig_mode | min_mode
252
+
253
+ PDK.logger.debug(format('Updated permissions of packaged \'%{entry}\' to %{new_mode}', entry: entry, new_mode: (entry_meta[:mode] & 0o7777).to_s(8))) if entry_meta[:mode] != orig_mode
254
+
255
+ Minitar.pack_file(entry_meta, tar)
167
256
  end
257
+ ensure
258
+ tar.close
168
259
  end
169
260
  end
170
261
 
@@ -179,8 +270,8 @@ module PDK
179
270
  @ignore_file ||= [
180
271
  File.join(module_dir, '.pdkignore'),
181
272
  File.join(module_dir, '.pmtignore'),
182
- File.join(module_dir, '.gitignore'),
183
- ].find { |file| File.file?(file) && File.readable?(file) }
273
+ File.join(module_dir, '.gitignore')
274
+ ].find { |file| PDK::Util::Filesystem.file?(file) && PDK::Util::Filesystem.readable?(file) }
184
275
  end
185
276
 
186
277
  # Instantiate a new PathSpec class and populate it with the pattern(s) of
@@ -188,21 +279,18 @@ module PDK
188
279
  #
189
280
  # @return [PathSpec] The populated ignore path matcher.
190
281
  def ignored_files
282
+ require 'pdk/module'
283
+ require 'pathspec'
284
+
191
285
  @ignored_files ||=
192
286
  begin
193
287
  ignored = if ignore_file.nil?
194
288
  PathSpec.new
195
289
  else
196
- fd = File.open(ignore_file, 'rb:UTF-8')
197
- data = fd.read
198
- fd.close
199
-
200
- PathSpec.new(data)
290
+ PathSpec.new(PDK::Util::Filesystem.read_file(ignore_file, open_args: 'rb:UTF-8'))
201
291
  end
202
292
 
203
- if File.realdirpath(target_dir).start_with?(File.realdirpath(module_dir))
204
- ignored = ignored.add("\/#{File.basename(target_dir)}\/")
205
- end
293
+ ignored = ignored.add("/#{File.basename(target_dir)}/") if File.realdirpath(target_dir).start_with?(File.realdirpath(module_dir))
206
294
 
207
295
  PDK::Module::DEFAULT_IGNORED.each { |r| ignored.add(r) }
208
296
 
@@ -1,26 +1,36 @@
1
- require 'pdk/generate/module'
2
- require 'pdk/module/update_manager'
3
- require 'pdk/util'
4
- require 'pdk/report'
1
+ require 'pdk'
5
2
 
6
3
  module PDK
7
4
  module Module
8
5
  class Convert
9
- def self.invoke(options)
10
- new(options).run
6
+ def self.invoke(module_dir, options)
7
+ new(module_dir, options).run
11
8
  end
12
9
 
13
- attr_reader :options
10
+ attr_reader :module_dir, :options
14
11
 
15
- def initialize(options = {})
12
+ def initialize(module_dir, options = {})
13
+ @module_dir = module_dir
16
14
  @options = options
17
15
  end
18
16
 
17
+ def convert?
18
+ instance_of?(PDK::Module::Convert)
19
+ end
20
+
19
21
  def run
20
22
  stage_changes!
21
23
 
22
24
  unless update_manager.changes?
23
- PDK::Report.default_target.puts(_('No changes required.'))
25
+ if adding_tests?
26
+ add_tests!
27
+ print_result 'Convert completed'
28
+ else
29
+ require 'pdk/report'
30
+
31
+ PDK::Report.default_target.puts('No changes required.')
32
+ end
33
+
24
34
  return
25
35
  end
26
36
 
@@ -31,24 +41,26 @@ module PDK
31
41
  return if noop?
32
42
 
33
43
  unless force?
34
- PDK.logger.info _(
35
- 'Module conversion is a potentially destructive action. ' \
36
- 'Ensure that you have committed your module to a version control ' \
37
- 'system or have a backup, and review the changes above before continuing.',
38
- )
39
- continue = PDK::CLI::Util.prompt_for_yes(_('Do you want to continue and make these changes to your module?'))
44
+ require 'pdk/cli/util'
45
+
46
+ PDK.logger.info 'Module conversion is a potentially destructive action. ' \
47
+ 'Ensure that you have committed your module to a version control ' \
48
+ 'system or have a backup, and review the changes above before continuing.'
49
+ continue = PDK::CLI::Util.prompt_for_yes('Do you want to continue and make these changes to your module?')
40
50
  return unless continue
41
51
  end
42
52
 
43
- # Remove these files straight away as these changes are not something that the user needs to review.
44
- if needs_bundle_update?
45
- update_manager.unlink_file('Gemfile.lock')
46
- update_manager.unlink_file(File.join('.bundle', 'config'))
47
- end
53
+ # Remove these files straight away as these changes are not something
54
+ # that the user needs to review.
55
+ update_manager.unlink_file('Gemfile.lock')
56
+ update_manager.unlink_file(File.join('.bundle', 'config'))
48
57
 
49
58
  update_manager.sync_changes!
50
59
 
51
- PDK::Util::Bundler.ensure_bundle! if needs_bundle_update?
60
+ require 'pdk/util/bundler'
61
+ PDK::Util::Bundler.ensure_bundle!
62
+
63
+ add_tests! if adding_tests?
52
64
 
53
65
  print_result 'Convert completed'
54
66
  end
@@ -61,35 +73,100 @@ module PDK
61
73
  options[:force]
62
74
  end
63
75
 
64
- def needs_bundle_update?
65
- update_manager.changed?('Gemfile')
76
+ def add_tests?
77
+ options[:'add-tests']
78
+ end
79
+
80
+ def adding_tests?
81
+ add_tests? && missing_tests?
66
82
  end
67
83
 
68
- def stage_changes!
69
- metadata_path = 'metadata.json'
84
+ def missing_tests?
85
+ !available_test_generators.empty?
86
+ end
87
+
88
+ def available_test_generators
89
+ # Only select generators which can run and have no pre-existing files
90
+ test_generators.select do |gen|
91
+ if gen.can_run?
92
+ gen.template_files.none? { |_, dst_path| PDK::Util::Filesystem.exist?(File.join(gen.context.root_path, dst_path)) }
93
+ else
94
+ false
95
+ end
96
+ end
97
+ end
70
98
 
71
- PDK::Module::TemplateDir.new(template_url, nil, false) do |templates|
72
- new_metadata = update_metadata(metadata_path, templates.metadata)
73
- templates.module_metadata = new_metadata.data unless new_metadata.nil?
99
+ def test_generators(context = PDK.context)
100
+ return @test_generators unless @test_generators.nil?
74
101
 
102
+ require 'pdk/util/puppet_strings'
103
+
104
+ test_gens = PDK::Util::PuppetStrings.all_objects.map do |generator, objects|
105
+ (objects || []).map do |obj|
106
+ generator.new(context, obj['name'], spec_only: true)
107
+ end
108
+ end
109
+
110
+ @test_generators = test_gens.flatten
111
+ end
112
+
113
+ def stage_tests!(manager)
114
+ available_test_generators.each do |gen|
115
+ gen.stage_changes(manager)
116
+ end
117
+ manager
118
+ end
119
+
120
+ def add_tests!
121
+ update_manager.clear!
122
+ stage_tests!(update_manager)
123
+
124
+ if update_manager.changes?
125
+ update_manager.sync_changes!
126
+ print_summary
127
+ else
128
+ PDK::Report.default_target.puts('No test changes required.')
129
+ end
130
+ end
131
+
132
+ def stage_changes!(context = PDK.context)
133
+ require 'pdk/util/filesystem'
134
+
135
+ metadata_path = File.join(module_dir, 'metadata.json')
136
+
137
+ PDK::Template.with(template_uri, context) do |template_dir|
138
+ new_metadata = update_metadata(metadata_path, template_dir.metadata)
75
139
  if options[:noop] && new_metadata.nil?
76
140
  update_manager.add_file(metadata_path, '')
77
- elsif File.file?(metadata_path)
141
+ elsif PDK::Util::Filesystem.file?(metadata_path)
78
142
  update_manager.modify_file(metadata_path, new_metadata.to_json)
79
143
  else
80
144
  update_manager.add_file(metadata_path, new_metadata.to_json)
81
145
  end
82
146
 
83
- templates.render do |file_path, file_content, file_status|
84
- if file_status == :unmanage
85
- PDK.logger.debug(_("skipping '%{path}'") % { path: file_path })
86
- elsif file_status == :delete
87
- update_manager.remove_file(file_path)
88
- elsif file_status == :manage
89
- if File.exist? file_path
90
- update_manager.modify_file(file_path, file_content)
147
+ # new_metadata == nil when creating a new module but with --noop@
148
+ module_name = new_metadata.nil? ? 'new-module' : new_metadata.data['name']
149
+ metadata_for_render = new_metadata.nil? ? {} : new_metadata.data
150
+
151
+ template_dir.render_new_module(module_name, metadata_for_render) do |relative_file_path, file_content, file_status, file_executable|
152
+ absolute_file_path = File.join(module_dir, relative_file_path)
153
+ case file_status
154
+ when :unmanage
155
+ PDK.logger.debug(format("skipping '%{path}'", path: absolute_file_path))
156
+ when :delete
157
+ update_manager.remove_file(absolute_file_path)
158
+ when :init
159
+ if convert? && !PDK::Util::Filesystem.exist?(absolute_file_path)
160
+ update_manager.add_file(absolute_file_path, file_content)
161
+ update_manager.make_file_executable(absolute_file_path) if file_executable
162
+ end
163
+ when :manage
164
+ if PDK::Util::Filesystem.exist?(absolute_file_path)
165
+ update_manager.modify_file(absolute_file_path, file_content)
166
+ update_manager.make_file_executable(absolute_file_path) if file_executable && !PDK::Util::Filesystem.executable?(absolute_file_path)
91
167
  else
92
- update_manager.add_file(file_path, file_content)
168
+ update_manager.add_file(absolute_file_path, file_content)
169
+ update_manager.make_file_executable(absolute_file_path) if file_executable
93
170
  end
94
171
  end
95
172
  end
@@ -99,20 +176,24 @@ module PDK
99
176
  end
100
177
 
101
178
  def update_manager
179
+ require 'pdk/module/update_manager'
180
+
102
181
  @update_manager ||= PDK::Module::UpdateManager.new
103
182
  end
104
183
 
105
- def template_url
106
- @template_url ||= options.fetch(:'template-url', PDK::Util.default_template_url)
184
+ def template_uri
185
+ require 'pdk/util/template_uri'
186
+
187
+ @template_uri ||= PDK::Util::TemplateURI.new(options)
107
188
  end
108
189
 
109
190
  def update_metadata(metadata_path, template_metadata)
110
- if File.file?(metadata_path)
111
- unless File.readable?(metadata_path)
112
- raise PDK::CLI::ExitWithError, _('Unable to update module metadata; %{path} exists but it is not readable.') % {
113
- path: metadata_path,
114
- }
115
- end
191
+ require 'pdk/generate/module'
192
+ require 'pdk/util/filesystem'
193
+ require 'pdk/module/metadata'
194
+
195
+ if PDK::Util::Filesystem.file?(metadata_path)
196
+ raise PDK::CLI::ExitWithError, format('Unable to update module metadata; %{path} exists but it is not readable.', path: metadata_path) unless PDK::Util::Filesystem.readable?(metadata_path)
116
197
 
117
198
  begin
118
199
  metadata = PDK::Module::Metadata.from_file(metadata_path)
@@ -124,12 +205,10 @@ module PDK
124
205
  rescue ArgumentError
125
206
  metadata = PDK::Generate::Module.prepare_metadata(options) unless options[:noop]
126
207
  end
127
- elsif File.exist?(metadata_path)
128
- raise PDK::CLI::ExitWithError, _('Unable to update module metadata; %{path} exists but it is not a file.') % {
129
- path: metadata_path,
130
- }
208
+ elsif PDK::Util::Filesystem.exist?(metadata_path)
209
+ raise PDK::CLI::ExitWithError, format('Unable to update module metadata; %{path} exists but it is not a file.', path: metadata_path)
131
210
  else
132
- return nil if options[:noop]
211
+ return if options[:noop]
133
212
 
134
213
  project_dir = File.basename(Dir.pwd)
135
214
  options[:module_name] = project_dir.split('-', 2).compact[-1]
@@ -163,34 +242,36 @@ module PDK
163
242
  end
164
243
 
165
244
  def print_summary
245
+ require 'pdk/report'
246
+
166
247
  footer = false
167
248
 
168
- summary.keys.each do |category|
249
+ summary.each_key do |category|
169
250
  next if summary[category].empty?
170
251
 
171
- PDK::Report.default_target.puts(_("\n%{banner}") % { banner: generate_banner("Files to be #{category}", 40) })
252
+ PDK::Report.default_target.puts(format("\n%{banner}", banner: generate_banner("Files to be #{category}", 40)))
172
253
  PDK::Report.default_target.puts(summary[category])
173
254
  footer = true
174
255
  end
175
256
 
176
- PDK::Report.default_target.puts(_("\n%{banner}") % { banner: generate_banner('', 40) }) if footer
257
+ PDK::Report.default_target.puts(format("\n%{banner}", banner: generate_banner('', 40))) if footer
177
258
  end
178
259
 
179
260
  def print_result(banner_text)
180
- PDK::Report.default_target.puts(_("\n%{banner}") % { banner: generate_banner(banner_text, 40) })
181
- summary_to_print = summary.map { |k, v| "#{v.length} files #{k}" unless v.empty? }.compact
182
- PDK::Report.default_target.puts(_("\n%{summary}\n\n") % { summary: "#{summary_to_print.join(', ')}." })
261
+ require 'pdk/report'
262
+
263
+ PDK::Report.default_target.puts(format("\n%{banner}", banner: generate_banner(banner_text, 40)))
264
+ summary_to_print = summary.filter_map { |k, v| "#{v.length} files #{k}" unless v.empty? }
265
+ PDK::Report.default_target.puts(format("\n%{summary}\n\n", summary: "#{summary_to_print.join(', ')}."))
183
266
  end
184
267
 
185
268
  def full_report(path)
186
- File.open(path, 'w') do |f|
187
- f.write("/* Report generated by PDK at #{Time.now} */")
188
- update_manager.changes[:modified].each do |_, diff|
189
- f.write("\n\n\n" + diff)
190
- end
191
- f.write("\n")
192
- end
193
- PDK::Report.default_target.puts(_("\nYou can find a report of differences in %{path}.\n\n") % { path: path })
269
+ require 'pdk/report'
270
+
271
+ report = ["/* Report generated by PDK at #{Time.now} */"]
272
+ report.concat(update_manager.changes[:modified].map { |_, diff| "\n\n\n#{diff}" })
273
+ PDK::Util::Filesystem.write_file(path, report.join)
274
+ PDK::Report.default_target.puts(format("\nYou can find a report of differences in %{path}.\n\n", path: path))
194
275
  end
195
276
 
196
277
  def generate_banner(text, width = 80)