pdk 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1329 -1321
- data/LICENSE +201 -201
- data/README.md +163 -163
- data/exe/pdk +10 -10
- data/lib/pdk/analytics/client/google_analytics.rb +143 -143
- data/lib/pdk/analytics/client/noop.rb +25 -25
- data/lib/pdk/analytics/util.rb +19 -19
- data/lib/pdk/analytics.rb +30 -30
- data/lib/pdk/answer_file.rb +12 -12
- data/lib/pdk/bolt.rb +19 -19
- data/lib/pdk/cli/build.rb +82 -82
- data/lib/pdk/cli/bundle.rb +48 -48
- data/lib/pdk/cli/config/get.rb +26 -26
- data/lib/pdk/cli/config.rb +22 -22
- data/lib/pdk/cli/console.rb +148 -148
- data/lib/pdk/cli/convert.rb +52 -52
- data/lib/pdk/cli/env.rb +52 -52
- data/lib/pdk/cli/errors.rb +25 -25
- data/lib/pdk/cli/exec/command.rb +293 -293
- data/lib/pdk/cli/exec/interactive_command.rb +114 -114
- data/lib/pdk/cli/exec.rb +84 -84
- data/lib/pdk/cli/exec_group.rb +104 -104
- data/lib/pdk/cli/get/config.rb +24 -24
- data/lib/pdk/cli/get.rb +20 -20
- data/lib/pdk/cli/module/build.rb +12 -12
- data/lib/pdk/cli/module/generate.rb +47 -47
- data/lib/pdk/cli/module.rb +14 -14
- data/lib/pdk/cli/new/class.rb +32 -32
- data/lib/pdk/cli/new/defined_type.rb +32 -32
- data/lib/pdk/cli/new/fact.rb +29 -29
- data/lib/pdk/cli/new/function.rb +29 -29
- data/lib/pdk/cli/new/module.rb +53 -53
- data/lib/pdk/cli/new/provider.rb +29 -29
- data/lib/pdk/cli/new/task.rb +34 -34
- data/lib/pdk/cli/new/test.rb +52 -52
- data/lib/pdk/cli/new/transport.rb +27 -27
- data/lib/pdk/cli/new.rb +21 -21
- data/lib/pdk/cli/release/prep.rb +39 -39
- data/lib/pdk/cli/release/publish.rb +50 -50
- data/lib/pdk/cli/release.rb +194 -194
- data/lib/pdk/cli/remove/config.rb +80 -80
- data/lib/pdk/cli/remove.rb +20 -20
- data/lib/pdk/cli/set/config.rb +119 -119
- data/lib/pdk/cli/set.rb +20 -20
- data/lib/pdk/cli/test/unit.rb +90 -90
- data/lib/pdk/cli/test.rb +11 -11
- data/lib/pdk/cli/update.rb +64 -64
- data/lib/pdk/cli/util/command_redirector.rb +27 -27
- data/lib/pdk/cli/util/interview.rb +72 -72
- data/lib/pdk/cli/util/option_normalizer.rb +55 -55
- data/lib/pdk/cli/util/option_validator.rb +68 -68
- data/lib/pdk/cli/util/spinner.rb +13 -13
- data/lib/pdk/cli/util/update_manager_printer.rb +82 -82
- data/lib/pdk/cli/util.rb +305 -305
- data/lib/pdk/cli/validate.rb +116 -116
- data/lib/pdk/cli.rb +175 -175
- data/lib/pdk/config/analytics_schema.json +26 -26
- data/lib/pdk/config/errors.rb +5 -5
- data/lib/pdk/config/ini_file.rb +183 -183
- data/lib/pdk/config/ini_file_setting.rb +39 -39
- data/lib/pdk/config/json.rb +34 -34
- data/lib/pdk/config/json_schema_namespace.rb +142 -142
- data/lib/pdk/config/json_schema_setting.rb +53 -53
- data/lib/pdk/config/json_with_schema.rb +49 -49
- data/lib/pdk/config/namespace.rb +354 -354
- data/lib/pdk/config/setting.rb +135 -135
- data/lib/pdk/config/validator.rb +31 -31
- data/lib/pdk/config/yaml.rb +46 -46
- data/lib/pdk/config/yaml_with_schema.rb +59 -59
- data/lib/pdk/config.rb +390 -390
- data/lib/pdk/context/control_repo.rb +60 -60
- data/lib/pdk/context/module.rb +28 -28
- data/lib/pdk/context/none.rb +22 -22
- data/lib/pdk/context.rb +99 -99
- data/lib/pdk/control_repo.rb +90 -90
- data/lib/pdk/generate/defined_type.rb +43 -43
- data/lib/pdk/generate/fact.rb +25 -25
- data/lib/pdk/generate/function.rb +48 -48
- data/lib/pdk/generate/module.rb +352 -352
- data/lib/pdk/generate/provider.rb +28 -28
- data/lib/pdk/generate/puppet_class.rb +43 -43
- data/lib/pdk/generate/puppet_object.rb +232 -232
- data/lib/pdk/generate/task.rb +68 -68
- data/lib/pdk/generate/transport.rb +33 -33
- data/lib/pdk/generate.rb +24 -24
- data/lib/pdk/i18n.rb +4 -4
- data/lib/pdk/logger.rb +45 -45
- data/lib/pdk/module/build.rb +322 -322
- data/lib/pdk/module/convert.rb +296 -296
- data/lib/pdk/module/metadata.rb +202 -202
- data/lib/pdk/module/release.rb +260 -260
- data/lib/pdk/module/update.rb +131 -131
- data/lib/pdk/module/update_manager.rb +227 -227
- data/lib/pdk/module.rb +30 -30
- data/lib/pdk/report/event.rb +370 -370
- data/lib/pdk/report.rb +121 -121
- data/lib/pdk/template/fetcher/git.rb +85 -85
- data/lib/pdk/template/fetcher/local.rb +28 -28
- data/lib/pdk/template/fetcher.rb +98 -98
- data/lib/pdk/template/renderer/v1/legacy_template_dir.rb +116 -116
- data/lib/pdk/template/renderer/v1/renderer.rb +132 -132
- data/lib/pdk/template/renderer/v1/template_file.rb +102 -102
- data/lib/pdk/template/renderer/v1.rb +25 -25
- data/lib/pdk/template/renderer.rb +96 -96
- data/lib/pdk/template/template_dir.rb +67 -67
- data/lib/pdk/template.rb +59 -59
- data/lib/pdk/tests/unit.rb +252 -252
- data/lib/pdk/util/bundler.rb +259 -259
- data/lib/pdk/util/changelog_generator.rb +137 -137
- data/lib/pdk/util/env.rb +47 -47
- data/lib/pdk/util/filesystem.rb +138 -138
- data/lib/pdk/util/git.rb +179 -179
- data/lib/pdk/util/json_finder.rb +85 -85
- data/lib/pdk/util/puppet_strings.rb +125 -125
- data/lib/pdk/util/puppet_version.rb +266 -266
- data/lib/pdk/util/ruby_version.rb +179 -179
- data/lib/pdk/util/template_uri.rb +295 -295
- data/lib/pdk/util/vendored_file.rb +93 -93
- data/lib/pdk/util/version.rb +43 -43
- data/lib/pdk/util/windows/api_types.rb +82 -82
- data/lib/pdk/util/windows/file.rb +36 -36
- data/lib/pdk/util/windows/process.rb +79 -79
- data/lib/pdk/util/windows/string.rb +16 -16
- data/lib/pdk/util/windows.rb +15 -15
- data/lib/pdk/util.rb +278 -277
- data/lib/pdk/validate/control_repo/control_repo_validator_group.rb +23 -23
- data/lib/pdk/validate/control_repo/environment_conf_validator.rb +98 -98
- data/lib/pdk/validate/external_command_validator.rb +208 -208
- data/lib/pdk/validate/internal_ruby_validator.rb +100 -100
- data/lib/pdk/validate/invokable_validator.rb +228 -228
- data/lib/pdk/validate/metadata/metadata_json_lint_validator.rb +86 -86
- data/lib/pdk/validate/metadata/metadata_syntax_validator.rb +78 -78
- data/lib/pdk/validate/metadata/metadata_validator_group.rb +20 -20
- data/lib/pdk/validate/puppet/puppet_epp_validator.rb +133 -133
- data/lib/pdk/validate/puppet/puppet_lint_validator.rb +66 -66
- data/lib/pdk/validate/puppet/puppet_syntax_validator.rb +137 -137
- data/lib/pdk/validate/puppet/puppet_validator_group.rb +21 -21
- data/lib/pdk/validate/ruby/ruby_rubocop_validator.rb +80 -80
- data/lib/pdk/validate/ruby/ruby_validator_group.rb +19 -19
- data/lib/pdk/validate/tasks/tasks_metadata_lint_validator.rb +88 -88
- data/lib/pdk/validate/tasks/tasks_name_validator.rb +50 -50
- data/lib/pdk/validate/tasks/tasks_validator_group.rb +20 -20
- data/lib/pdk/validate/validator.rb +118 -118
- data/lib/pdk/validate/validator_group.rb +104 -104
- data/lib/pdk/validate/yaml/yaml_syntax_validator.rb +95 -95
- data/lib/pdk/validate/yaml/yaml_validator_group.rb +19 -19
- data/lib/pdk/validate.rb +94 -94
- data/lib/pdk/version.rb +4 -4
- data/lib/pdk.rb +76 -76
- data/locales/config.yaml +21 -21
- data/locales/pdk.pot +2094 -2094
- metadata +5 -6
data/lib/pdk/module/build.rb
CHANGED
|
@@ -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
|