pdk 2.1.0 → 2.4.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 +1329 -1292
  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 -283
  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 -192
  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 -42
  70. data/lib/pdk/config/yaml_with_schema.rb +59 -59
  71. data/lib/pdk/config.rb +390 -384
  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 -254
  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 -124
  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 -84
  115. data/lib/pdk/util/puppet_strings.rb +125 -125
  116. data/lib/pdk/util/puppet_version.rb +266 -266
  117. data/lib/pdk/util/ruby_version.rb +179 -179
  118. data/lib/pdk/util/template_uri.rb +295 -295
  119. data/lib/pdk/util/vendored_file.rb +93 -93
  120. data/lib/pdk/util/version.rb +43 -43
  121. data/lib/pdk/util/windows/api_types.rb +82 -82
  122. data/lib/pdk/util/windows/file.rb +36 -36
  123. data/lib/pdk/util/windows/process.rb +79 -79
  124. data/lib/pdk/util/windows/string.rb +16 -16
  125. data/lib/pdk/util/windows.rb +15 -15
  126. data/lib/pdk/util.rb +278 -277
  127. data/lib/pdk/validate/control_repo/control_repo_validator_group.rb +23 -23
  128. data/lib/pdk/validate/control_repo/environment_conf_validator.rb +98 -98
  129. data/lib/pdk/validate/external_command_validator.rb +208 -208
  130. data/lib/pdk/validate/internal_ruby_validator.rb +100 -100
  131. data/lib/pdk/validate/invokable_validator.rb +228 -220
  132. data/lib/pdk/validate/metadata/metadata_json_lint_validator.rb +86 -86
  133. data/lib/pdk/validate/metadata/metadata_syntax_validator.rb +78 -78
  134. data/lib/pdk/validate/metadata/metadata_validator_group.rb +20 -20
  135. data/lib/pdk/validate/puppet/puppet_epp_validator.rb +133 -133
  136. data/lib/pdk/validate/puppet/puppet_lint_validator.rb +66 -66
  137. data/lib/pdk/validate/puppet/puppet_syntax_validator.rb +137 -137
  138. data/lib/pdk/validate/puppet/puppet_validator_group.rb +21 -21
  139. data/lib/pdk/validate/ruby/ruby_rubocop_validator.rb +80 -80
  140. data/lib/pdk/validate/ruby/ruby_validator_group.rb +19 -19
  141. data/lib/pdk/validate/tasks/tasks_metadata_lint_validator.rb +88 -88
  142. data/lib/pdk/validate/tasks/tasks_name_validator.rb +50 -50
  143. data/lib/pdk/validate/tasks/tasks_validator_group.rb +20 -20
  144. data/lib/pdk/validate/validator.rb +118 -118
  145. data/lib/pdk/validate/validator_group.rb +104 -104
  146. data/lib/pdk/validate/yaml/yaml_syntax_validator.rb +95 -95
  147. data/lib/pdk/validate/yaml/yaml_validator_group.rb +19 -19
  148. data/lib/pdk/validate.rb +94 -94
  149. data/lib/pdk/version.rb +4 -4
  150. data/lib/pdk.rb +76 -76
  151. data/locales/config.yaml +21 -21
  152. data/locales/pdk.pot +2094 -2094
  153. metadata +19 -13
@@ -1,322 +1,322 @@
1
- require 'pdk'
2
-
3
- module PDK
4
- module Module
5
- class Build
6
- def self.invoke(options = {})
7
- new(options).build
8
- end
9
-
10
- attr_reader :module_dir
11
- attr_reader :target_dir
12
-
13
- def initialize(options = {})
14
- @module_dir = PDK::Util::Filesystem.expand_path(options[:module_dir] || Dir.pwd)
15
- @target_dir = PDK::Util::Filesystem.expand_path(options[:'target-dir'] || File.join(module_dir, 'pkg'))
16
- end
17
-
18
- # Read and parse the values from metadata.json for the module that is
19
- # being built.
20
- #
21
- # @return [Hash{String => Object}] The hash of metadata values.
22
- def metadata
23
- require 'pdk/module/metadata'
24
-
25
- @metadata ||= PDK::Module::Metadata.from_file(File.join(module_dir, 'metadata.json')).data
26
- end
27
-
28
- # Return the path where the built package file will be written to.
29
- def package_file
30
- @package_file ||= File.join(target_dir, "#{release_name}.tar.gz")
31
- end
32
-
33
- # Build a module package from a module directory.
34
- #
35
- # @return [String] The path to the built package file.
36
- def build
37
- create_build_dir
38
-
39
- stage_module_in_build_dir
40
- build_package
41
-
42
- package_file
43
- ensure
44
- cleanup_build_dir
45
- end
46
-
47
- # Verify if there is an existing package in the target directory and prompts
48
- # the user if they want to overwrite it.
49
- def package_already_exists?
50
- PDK::Util::Filesystem.exist?(package_file)
51
- end
52
-
53
- # Check if the module is PDK Compatible. If not, then prompt the user if
54
- # they want to run PDK Convert.
55
- def module_pdk_compatible?
56
- ['pdk-version', 'template-url'].any? { |key| metadata.key?(key) }
57
- end
58
-
59
- # Return the path to the temporary build directory, which will be placed
60
- # inside the target directory and match the release name (see #release_name).
61
- def build_dir
62
- @build_dir ||= File.join(target_dir, release_name)
63
- end
64
-
65
- # Create a temporary build directory where the files to be included in
66
- # the package will be staged before building the tarball.
67
- #
68
- # If the directory already exists, remove it first.
69
- def create_build_dir
70
- cleanup_build_dir
71
-
72
- PDK::Util::Filesystem.mkdir_p(build_dir)
73
- end
74
-
75
- # Remove the temporary build directory and all its contents from disk.
76
- #
77
- # @return nil.
78
- def cleanup_build_dir
79
- PDK::Util::Filesystem.rm_rf(build_dir, secure: true)
80
- end
81
-
82
- # Combine the module name and version into a Forge-compatible dash
83
- # separated string.
84
- #
85
- # @return [String] The module name and version, joined by a dash.
86
- def release_name
87
- @release_name ||= [
88
- metadata['name'],
89
- metadata['version'],
90
- ].join('-')
91
- end
92
-
93
- # Iterate through all the files and directories in the module and stage
94
- # them into the temporary build directory (unless ignored).
95
- #
96
- # @return nil
97
- def stage_module_in_build_dir
98
- require 'find'
99
-
100
- Find.find(module_dir) do |path|
101
- next if path == module_dir
102
-
103
- ignored_path?(path) ? Find.prune : stage_path(path)
104
- end
105
- end
106
-
107
- # Stage a file or directory from the module into the build directory.
108
- #
109
- # @param path [String] The path to the file or directory.
110
- #
111
- # @return nil.
112
- def stage_path(path)
113
- require 'pathname'
114
-
115
- relative_path = Pathname.new(path).relative_path_from(Pathname.new(module_dir))
116
- dest_path = File.join(build_dir, relative_path)
117
-
118
- validate_path_encoding!(relative_path.to_path)
119
-
120
- if PDK::Util::Filesystem.directory?(path)
121
- PDK::Util::Filesystem.mkdir_p(dest_path, mode: PDK::Util::Filesystem.stat(path).mode)
122
- elsif PDK::Util::Filesystem.symlink?(path)
123
- warn_symlink(path)
124
- else
125
- validate_ustar_path!(relative_path.to_path)
126
- PDK::Util::Filesystem.cp(path, dest_path, preserve: true)
127
- end
128
- rescue ArgumentError => e
129
- raise PDK::CLI::ExitWithError, _(
130
- '%{message} Rename the file or exclude it from the package ' \
131
- 'by adding it to the .pdkignore file in your module.',
132
- ) % { message: e.message }
133
- end
134
-
135
- # Check if the given path matches one of the patterns listed in the
136
- # ignore file.
137
- #
138
- # @param path [String] The path to be checked.
139
- #
140
- # @return [Boolean] true if the path matches and should be ignored.
141
- def ignored_path?(path)
142
- path = path.to_s + '/' if PDK::Util::Filesystem.directory?(path)
143
-
144
- !ignored_files.match_paths([path], module_dir).empty?
145
- end
146
-
147
- # Warn the user about a symlink that would have been included in the
148
- # built package.
149
- #
150
- # @param path [String] The relative or absolute path to the symlink.
151
- #
152
- # @return nil.
153
- def warn_symlink(path)
154
- require 'pathname'
155
-
156
- symlink_path = Pathname.new(path)
157
- module_path = Pathname.new(module_dir)
158
-
159
- PDK.logger.warn _('Symlinks in modules are not supported and will not be included in the package. Please investigate symlink %{from} -> %{to}.') % {
160
- from: symlink_path.relative_path_from(module_path),
161
- to: symlink_path.realpath.relative_path_from(module_path),
162
- }
163
- end
164
-
165
- # Checks if the path length will fit into the POSIX.1-1998 (ustar) tar
166
- # header format.
167
- #
168
- # POSIX.1-2001 (which allows paths of infinite length) was adopted by GNU
169
- # tar in 2004 and is supported by minitar 0.7 and above. Unfortunately
170
- # much of the Puppet ecosystem still uses minitar 0.6.1.
171
- #
172
- # POSIX.1-1998 tar format does not allow for paths greater than 256 bytes,
173
- # or paths that can't be split into a prefix of 155 bytes (max) and
174
- # a suffix of 100 bytes (max).
175
- #
176
- # This logic was pretty much copied from the private method
177
- # {Archive::Tar::Minitar::Writer#split_name}.
178
- #
179
- # @param path [String] the relative path to be added to the tar file.
180
- #
181
- # @raise [ArgumentError] if the path is too long or could not be split.
182
- #
183
- # @return [nil]
184
- def validate_ustar_path!(path)
185
- if path.bytesize > 256
186
- raise ArgumentError, _("The path '%{path}' is longer than 256 bytes.") % {
187
- path: path,
188
- }
189
- end
190
-
191
- if path.bytesize <= 100
192
- prefix = ''
193
- else
194
- parts = path.split(File::SEPARATOR)
195
- newpath = parts.pop
196
- nxt = ''
197
-
198
- loop do
199
- nxt = parts.pop || ''
200
- break if newpath.bytesize + 1 + nxt.bytesize >= 100
201
- newpath = File.join(nxt, newpath)
202
- end
203
-
204
- prefix = File.join(*parts, nxt)
205
- path = newpath
206
- end
207
-
208
- return unless path.bytesize > 100 || prefix.bytesize > 155
209
-
210
- raise ArgumentError, _(
211
- "'%{path}' could not be split at a directory separator into two " \
212
- 'parts, the first having a maximum length of 155 bytes and the ' \
213
- 'second having a maximum length of 100 bytes.',
214
- ) % { path: path }
215
- end
216
-
217
- # Checks if the path contains any non-ASCII characters.
218
- #
219
- # Java will throw an error when it encounters a path containing
220
- # characters that are not supported by the hosts locale. In order to
221
- # maximise compatibility we limit the paths to contain only ASCII
222
- # characters, which should be part of any locale character set.
223
- #
224
- # @param path [String] the relative path to be added to the tar file.
225
- #
226
- # @raise [ArgumentError] if the path contains non-ASCII characters.
227
- #
228
- # @return [nil]
229
- def validate_path_encoding!(path)
230
- return unless path =~ %r{[^\x00-\x7F]}
231
-
232
- raise ArgumentError, _(
233
- "'%{path}' can only include ASCII characters in its path or " \
234
- 'filename in order to be compatible with a wide range of hosts.',
235
- ) % { path: path }
236
- end
237
-
238
- # Creates a gzip compressed tarball of the build directory.
239
- #
240
- # If the destination package already exists, it will be removed before
241
- # creating the new tarball.
242
- #
243
- # @return nil.
244
- def build_package
245
- require 'zlib'
246
- require 'minitar'
247
- require 'find'
248
-
249
- PDK::Util::Filesystem.rm_f(package_file)
250
-
251
- Dir.chdir(target_dir) do
252
- begin
253
- gz = Zlib::GzipWriter.new(File.open(package_file, 'wb')) # rubocop:disable PDK/FileOpen
254
- tar = Minitar::Output.new(gz)
255
- Find.find(release_name) do |entry|
256
- entry_meta = {
257
- name: entry,
258
- }
259
-
260
- orig_mode = PDK::Util::Filesystem.stat(entry).mode
261
- min_mode = Minitar.dir?(entry) ? 0o755 : 0o644
262
-
263
- entry_meta[:mode] = orig_mode | min_mode
264
-
265
- if entry_meta[:mode] != orig_mode
266
- PDK.logger.debug(_('Updated permissions of packaged \'%{entry}\' to %{new_mode}') % {
267
- entry: entry,
268
- new_mode: (entry_meta[:mode] & 0o7777).to_s(8),
269
- })
270
- end
271
-
272
- Minitar.pack_file(entry_meta, tar)
273
- end
274
- ensure
275
- tar.close
276
- end
277
- end
278
- end
279
-
280
- # Select the most appropriate ignore file in the module directory.
281
- #
282
- # In order of preference, we first try `.pdkignore`, then `.pmtignore`
283
- # and finally `.gitignore`.
284
- #
285
- # @return [String] The path to the file containing the patterns of file
286
- # paths to ignore.
287
- def ignore_file
288
- @ignore_file ||= [
289
- File.join(module_dir, '.pdkignore'),
290
- File.join(module_dir, '.pmtignore'),
291
- File.join(module_dir, '.gitignore'),
292
- ].find { |file| PDK::Util::Filesystem.file?(file) && PDK::Util::Filesystem.readable?(file) }
293
- end
294
-
295
- # Instantiate a new PathSpec class and populate it with the pattern(s) of
296
- # files to be ignored.
297
- #
298
- # @return [PathSpec] The populated ignore path matcher.
299
- def ignored_files
300
- require 'pdk/module'
301
- require 'pathspec'
302
-
303
- @ignored_files ||=
304
- begin
305
- ignored = if ignore_file.nil?
306
- PathSpec.new
307
- else
308
- PathSpec.new(PDK::Util::Filesystem.read_file(ignore_file, open_args: 'rb:UTF-8'))
309
- end
310
-
311
- if File.realdirpath(target_dir).start_with?(File.realdirpath(module_dir))
312
- ignored = ignored.add("\/#{File.basename(target_dir)}\/")
313
- end
314
-
315
- PDK::Module::DEFAULT_IGNORED.each { |r| ignored.add(r) }
316
-
317
- ignored
318
- end
319
- end
320
- end
321
- end
322
- end
1
+ require 'pdk'
2
+
3
+ module PDK
4
+ module Module
5
+ class Build
6
+ def self.invoke(options = {})
7
+ new(options).build
8
+ end
9
+
10
+ attr_reader :module_dir
11
+ attr_reader :target_dir
12
+
13
+ def initialize(options = {})
14
+ @module_dir = PDK::Util::Filesystem.expand_path(options[:module_dir] || Dir.pwd)
15
+ @target_dir = PDK::Util::Filesystem.expand_path(options[:'target-dir'] || File.join(module_dir, 'pkg'))
16
+ end
17
+
18
+ # Read and parse the values from metadata.json for the module that is
19
+ # being built.
20
+ #
21
+ # @return [Hash{String => Object}] The hash of metadata values.
22
+ def metadata
23
+ require 'pdk/module/metadata'
24
+
25
+ @metadata ||= PDK::Module::Metadata.from_file(File.join(module_dir, 'metadata.json')).data
26
+ end
27
+
28
+ # Return the path where the built package file will be written to.
29
+ def package_file
30
+ @package_file ||= File.join(target_dir, "#{release_name}.tar.gz")
31
+ end
32
+
33
+ # Build a module package from a module directory.
34
+ #
35
+ # @return [String] The path to the built package file.
36
+ def build
37
+ create_build_dir
38
+
39
+ stage_module_in_build_dir
40
+ build_package
41
+
42
+ package_file
43
+ ensure
44
+ cleanup_build_dir
45
+ end
46
+
47
+ # Verify if there is an existing package in the target directory and prompts
48
+ # the user if they want to overwrite it.
49
+ def package_already_exists?
50
+ PDK::Util::Filesystem.exist?(package_file)
51
+ end
52
+
53
+ # Check if the module is PDK Compatible. If not, then prompt the user if
54
+ # they want to run PDK Convert.
55
+ def module_pdk_compatible?
56
+ ['pdk-version', 'template-url'].any? { |key| metadata.key?(key) }
57
+ end
58
+
59
+ # Return the path to the temporary build directory, which will be placed
60
+ # inside the target directory and match the release name (see #release_name).
61
+ def build_dir
62
+ @build_dir ||= File.join(target_dir, release_name)
63
+ end
64
+
65
+ # Create a temporary build directory where the files to be included in
66
+ # the package will be staged before building the tarball.
67
+ #
68
+ # If the directory already exists, remove it first.
69
+ def create_build_dir
70
+ cleanup_build_dir
71
+
72
+ PDK::Util::Filesystem.mkdir_p(build_dir)
73
+ end
74
+
75
+ # Remove the temporary build directory and all its contents from disk.
76
+ #
77
+ # @return nil.
78
+ def cleanup_build_dir
79
+ PDK::Util::Filesystem.rm_rf(build_dir, secure: true)
80
+ end
81
+
82
+ # Combine the module name and version into a Forge-compatible dash
83
+ # separated string.
84
+ #
85
+ # @return [String] The module name and version, joined by a dash.
86
+ def release_name
87
+ @release_name ||= [
88
+ metadata['name'],
89
+ metadata['version'],
90
+ ].join('-')
91
+ end
92
+
93
+ # Iterate through all the files and directories in the module and stage
94
+ # them into the temporary build directory (unless ignored).
95
+ #
96
+ # @return nil
97
+ def stage_module_in_build_dir
98
+ require 'find'
99
+
100
+ Find.find(module_dir) do |path|
101
+ next if path == module_dir
102
+
103
+ ignored_path?(path) ? Find.prune : stage_path(path)
104
+ end
105
+ end
106
+
107
+ # Stage a file or directory from the module into the build directory.
108
+ #
109
+ # @param path [String] The path to the file or directory.
110
+ #
111
+ # @return nil.
112
+ def stage_path(path)
113
+ require 'pathname'
114
+
115
+ relative_path = Pathname.new(path).relative_path_from(Pathname.new(module_dir))
116
+ dest_path = File.join(build_dir, relative_path)
117
+
118
+ validate_path_encoding!(relative_path.to_path)
119
+
120
+ if PDK::Util::Filesystem.directory?(path)
121
+ PDK::Util::Filesystem.mkdir_p(dest_path, mode: PDK::Util::Filesystem.stat(path).mode)
122
+ elsif PDK::Util::Filesystem.symlink?(path)
123
+ warn_symlink(path)
124
+ else
125
+ validate_ustar_path!(relative_path.to_path)
126
+ PDK::Util::Filesystem.cp(path, dest_path, preserve: true)
127
+ end
128
+ rescue ArgumentError => e
129
+ raise PDK::CLI::ExitWithError, _(
130
+ '%{message} Rename the file or exclude it from the package ' \
131
+ 'by adding it to the .pdkignore file in your module.',
132
+ ) % { message: e.message }
133
+ end
134
+
135
+ # Check if the given path matches one of the patterns listed in the
136
+ # ignore file.
137
+ #
138
+ # @param path [String] The path to be checked.
139
+ #
140
+ # @return [Boolean] true if the path matches and should be ignored.
141
+ def ignored_path?(path)
142
+ path = path.to_s + '/' if PDK::Util::Filesystem.directory?(path)
143
+
144
+ !ignored_files.match_paths([path], module_dir).empty?
145
+ end
146
+
147
+ # Warn the user about a symlink that would have been included in the
148
+ # built package.
149
+ #
150
+ # @param path [String] The relative or absolute path to the symlink.
151
+ #
152
+ # @return nil.
153
+ def warn_symlink(path)
154
+ require 'pathname'
155
+
156
+ symlink_path = Pathname.new(path)
157
+ module_path = Pathname.new(module_dir)
158
+
159
+ PDK.logger.warn _('Symlinks in modules are not supported and will not be included in the package. Please investigate symlink %{from} -> %{to}.') % {
160
+ from: symlink_path.relative_path_from(module_path),
161
+ to: symlink_path.realpath.relative_path_from(module_path),
162
+ }
163
+ end
164
+
165
+ # Checks if the path length will fit into the POSIX.1-1998 (ustar) tar
166
+ # header format.
167
+ #
168
+ # POSIX.1-2001 (which allows paths of infinite length) was adopted by GNU
169
+ # tar in 2004 and is supported by minitar 0.7 and above. Unfortunately
170
+ # much of the Puppet ecosystem still uses minitar 0.6.1.
171
+ #
172
+ # POSIX.1-1998 tar format does not allow for paths greater than 256 bytes,
173
+ # or paths that can't be split into a prefix of 155 bytes (max) and
174
+ # a suffix of 100 bytes (max).
175
+ #
176
+ # This logic was pretty much copied from the private method
177
+ # {Archive::Tar::Minitar::Writer#split_name}.
178
+ #
179
+ # @param path [String] the relative path to be added to the tar file.
180
+ #
181
+ # @raise [ArgumentError] if the path is too long or could not be split.
182
+ #
183
+ # @return [nil]
184
+ def validate_ustar_path!(path)
185
+ if path.bytesize > 256
186
+ raise ArgumentError, _("The path '%{path}' is longer than 256 bytes.") % {
187
+ path: path,
188
+ }
189
+ end
190
+
191
+ if path.bytesize <= 100
192
+ prefix = ''
193
+ else
194
+ parts = path.split(File::SEPARATOR)
195
+ newpath = parts.pop
196
+ nxt = ''
197
+
198
+ loop do
199
+ nxt = parts.pop || ''
200
+ break if newpath.bytesize + 1 + nxt.bytesize >= 100
201
+ newpath = File.join(nxt, newpath)
202
+ end
203
+
204
+ prefix = File.join(*parts, nxt)
205
+ path = newpath
206
+ end
207
+
208
+ return unless path.bytesize > 100 || prefix.bytesize > 155
209
+
210
+ raise ArgumentError, _(
211
+ "'%{path}' could not be split at a directory separator into two " \
212
+ 'parts, the first having a maximum length of 155 bytes and the ' \
213
+ 'second having a maximum length of 100 bytes.',
214
+ ) % { path: path }
215
+ end
216
+
217
+ # Checks if the path contains any non-ASCII characters.
218
+ #
219
+ # Java will throw an error when it encounters a path containing
220
+ # characters that are not supported by the hosts locale. In order to
221
+ # maximise compatibility we limit the paths to contain only ASCII
222
+ # characters, which should be part of any locale character set.
223
+ #
224
+ # @param path [String] the relative path to be added to the tar file.
225
+ #
226
+ # @raise [ArgumentError] if the path contains non-ASCII characters.
227
+ #
228
+ # @return [nil]
229
+ def validate_path_encoding!(path)
230
+ return unless path =~ %r{[^\x00-\x7F]}
231
+
232
+ raise ArgumentError, _(
233
+ "'%{path}' can only include ASCII characters in its path or " \
234
+ 'filename in order to be compatible with a wide range of hosts.',
235
+ ) % { path: path }
236
+ end
237
+
238
+ # Creates a gzip compressed tarball of the build directory.
239
+ #
240
+ # If the destination package already exists, it will be removed before
241
+ # creating the new tarball.
242
+ #
243
+ # @return nil.
244
+ def build_package
245
+ require 'zlib'
246
+ require 'minitar'
247
+ require 'find'
248
+
249
+ PDK::Util::Filesystem.rm_f(package_file)
250
+
251
+ Dir.chdir(target_dir) do
252
+ begin
253
+ gz = Zlib::GzipWriter.new(File.open(package_file, 'wb')) # rubocop:disable PDK/FileOpen
254
+ tar = Minitar::Output.new(gz)
255
+ Find.find(release_name) do |entry|
256
+ entry_meta = {
257
+ name: entry,
258
+ }
259
+
260
+ orig_mode = PDK::Util::Filesystem.stat(entry).mode
261
+ min_mode = Minitar.dir?(entry) ? 0o755 : 0o644
262
+
263
+ entry_meta[:mode] = orig_mode | min_mode
264
+
265
+ if entry_meta[:mode] != orig_mode
266
+ PDK.logger.debug(_('Updated permissions of packaged \'%{entry}\' to %{new_mode}') % {
267
+ entry: entry,
268
+ new_mode: (entry_meta[:mode] & 0o7777).to_s(8),
269
+ })
270
+ end
271
+
272
+ Minitar.pack_file(entry_meta, tar)
273
+ end
274
+ ensure
275
+ tar.close
276
+ end
277
+ end
278
+ end
279
+
280
+ # Select the most appropriate ignore file in the module directory.
281
+ #
282
+ # In order of preference, we first try `.pdkignore`, then `.pmtignore`
283
+ # and finally `.gitignore`.
284
+ #
285
+ # @return [String] The path to the file containing the patterns of file
286
+ # paths to ignore.
287
+ def ignore_file
288
+ @ignore_file ||= [
289
+ File.join(module_dir, '.pdkignore'),
290
+ File.join(module_dir, '.pmtignore'),
291
+ File.join(module_dir, '.gitignore'),
292
+ ].find { |file| PDK::Util::Filesystem.file?(file) && PDK::Util::Filesystem.readable?(file) }
293
+ end
294
+
295
+ # Instantiate a new PathSpec class and populate it with the pattern(s) of
296
+ # files to be ignored.
297
+ #
298
+ # @return [PathSpec] The populated ignore path matcher.
299
+ def ignored_files
300
+ require 'pdk/module'
301
+ require 'pathspec'
302
+
303
+ @ignored_files ||=
304
+ begin
305
+ ignored = if ignore_file.nil?
306
+ PathSpec.new
307
+ else
308
+ PathSpec.new(PDK::Util::Filesystem.read_file(ignore_file, open_args: 'rb:UTF-8'))
309
+ end
310
+
311
+ if File.realdirpath(target_dir).start_with?(File.realdirpath(module_dir))
312
+ ignored = ignored.add("\/#{File.basename(target_dir)}\/")
313
+ end
314
+
315
+ PDK::Module::DEFAULT_IGNORED.each { |r| ignored.add(r) }
316
+
317
+ ignored
318
+ end
319
+ end
320
+ end
321
+ end
322
+ end