Class: PDK::Module::Build
- Inherits:
-
Object
- Object
- PDK::Module::Build
- Defined in:
- lib/pdk/module/build.rb
Instance Attribute Summary collapse
-
#module_dir ⇒ Object
readonly
Returns the value of attribute module_dir.
-
#target_dir ⇒ Object
readonly
Returns the value of attribute target_dir.
Class Method Summary collapse
Instance Method Summary collapse
-
#build ⇒ String
Build a module package from a module directory.
-
#build_dir ⇒ Object
Return the path to the temporary build directory, which will be placed inside the target directory and match the release name (see #release_name).
-
#build_package ⇒ Object
Creates a gzip compressed tarball of the build directory.
-
#cleanup_build_dir ⇒ Object
Remove the temporary build directory and all its contents from disk.
-
#create_build_dir ⇒ Object
Create a temporary build directory where the files to be included in the package will be staged before building the tarball.
-
#ignore_file ⇒ String
Select the most appropriate ignore file in the module directory.
-
#ignored_files ⇒ PathSpec
Instantiate a new PathSpec class and populate it with the pattern(s) of files to be ignored.
-
#ignored_path?(path) ⇒ Boolean
Check if the given path matches one of the patterns listed in the ignore file.
-
#initialize(options = {}) ⇒ Build
constructor
A new instance of Build.
-
#metadata ⇒ Hash{String => Object}
Read and parse the values from metadata.json for the module that is being built.
-
#module_pdk_compatible? ⇒ Boolean
Check if the module is PDK Compatible.
-
#package_already_exists? ⇒ Boolean
Verify if there is an existing package in the target directory and prompts the user if they want to overwrite it.
-
#package_file ⇒ Object
Return the path where the built package file will be written to.
-
#release_name ⇒ String
Combine the module name and version into a Forge-compatible dash separated string.
-
#stage_module_in_build_dir ⇒ Object
Iterate through all the files and directories in the module and stage them into the temporary build directory (unless ignored).
-
#stage_path(path) ⇒ Object
Stage a file or directory from the module into the build directory.
-
#validate_path_encoding!(path) ⇒ nil
Checks if the path contains any non-ASCII characters.
-
#validate_ustar_path!(path) ⇒ nil
Checks if the path length will fit into the POSIX.1-1998 (ustar) tar header format.
-
#warn_symlink(path) ⇒ Object
Warn the user about a symlink that would have been included in the built package.
Constructor Details
#initialize(options = {}) ⇒ Build
Returns a new instance of Build.
13 14 15 16 |
# File 'lib/pdk/module/build.rb', line 13 def initialize( = {}) @module_dir = PDK::Util::Filesystem.([:module_dir] || Dir.pwd) @target_dir = PDK::Util::Filesystem.([:'target-dir'] || File.join(module_dir, 'pkg')) end |
Instance Attribute Details
#module_dir ⇒ Object (readonly)
Returns the value of attribute module_dir.
10 11 12 |
# File 'lib/pdk/module/build.rb', line 10 def module_dir @module_dir end |
#target_dir ⇒ Object (readonly)
Returns the value of attribute target_dir.
11 12 13 |
# File 'lib/pdk/module/build.rb', line 11 def target_dir @target_dir end |
Class Method Details
.invoke(options = {}) ⇒ Object
6 7 8 |
# File 'lib/pdk/module/build.rb', line 6 def self.invoke( = {}) new().build end |
Instance Method Details
#build ⇒ String
Build a module package from a module directory.
36 37 38 39 40 41 42 43 44 45 |
# File 'lib/pdk/module/build.rb', line 36 def build create_build_dir stage_module_in_build_dir build_package package_file ensure cleanup_build_dir end |
#build_dir ⇒ Object
Return the path to the temporary build directory, which will be placed inside the target directory and match the release name (see #release_name).
61 62 63 |
# File 'lib/pdk/module/build.rb', line 61 def build_dir @build_dir ||= File.join(target_dir, release_name) end |
#build_package ⇒ Object
Creates a gzip compressed tarball of the build directory.
If the destination package already exists, it will be removed before creating the new tarball.
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/pdk/module/build.rb', line 244 def build_package require 'zlib' require 'minitar' require 'find' PDK::Util::Filesystem.rm_f(package_file) Dir.chdir(target_dir) do begin gz = Zlib::GzipWriter.new(File.open(package_file, 'wb')) # rubocop:disable PDK/FileOpen tar = Minitar::Output.new(gz) Find.find(release_name) do |entry| = { name: entry, } orig_mode = PDK::Util::Filesystem.stat(entry).mode min_mode = Minitar.dir?(entry) ? 0o755 : 0o644 [:mode] = orig_mode | min_mode if [:mode] != orig_mode PDK.logger.debug(_('Updated permissions of packaged \'%{entry}\' to %{new_mode}') % { entry: entry, new_mode: ([:mode] & 0o7777).to_s(8), }) end Minitar.pack_file(, tar) end ensure tar.close end end end |
#cleanup_build_dir ⇒ Object
Remove the temporary build directory and all its contents from disk.
78 79 80 |
# File 'lib/pdk/module/build.rb', line 78 def cleanup_build_dir PDK::Util::Filesystem.rm_rf(build_dir, secure: true) end |
#create_build_dir ⇒ Object
Create a temporary build directory where the files to be included in the package will be staged before building the tarball.
If the directory already exists, remove it first.
69 70 71 72 73 |
# File 'lib/pdk/module/build.rb', line 69 def create_build_dir cleanup_build_dir PDK::Util::Filesystem.mkdir_p(build_dir) end |
#ignore_file ⇒ String
Select the most appropriate ignore file in the module directory.
In order of preference, we first try ‘.pdkignore`, then `.pmtignore` and finally `.gitignore`.
287 288 289 290 291 292 293 |
# File 'lib/pdk/module/build.rb', line 287 def ignore_file @ignore_file ||= [ File.join(module_dir, '.pdkignore'), File.join(module_dir, '.pmtignore'), File.join(module_dir, '.gitignore'), ].find { |file| PDK::Util::Filesystem.file?(file) && PDK::Util::Filesystem.readable?(file) } end |
#ignored_files ⇒ PathSpec
Instantiate a new PathSpec class and populate it with the pattern(s) of files to be ignored.
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/pdk/module/build.rb', line 299 def ignored_files require 'pdk/module' require 'pathspec' @ignored_files ||= begin ignored = if ignore_file.nil? PathSpec.new else PathSpec.new(PDK::Util::Filesystem.read_file(ignore_file, open_args: 'rb:UTF-8')) end if File.realdirpath(target_dir).start_with?(File.realdirpath(module_dir)) ignored = ignored.add("\/#{File.basename(target_dir)}\/") end PDK::Module::DEFAULT_IGNORED.each { |r| ignored.add(r) } ignored end end |
#ignored_path?(path) ⇒ Boolean
Check if the given path matches one of the patterns listed in the ignore file.
141 142 143 144 145 |
# File 'lib/pdk/module/build.rb', line 141 def ignored_path?(path) path = path.to_s + '/' if PDK::Util::Filesystem.directory?(path) !ignored_files.match_paths([path], module_dir).empty? end |
#metadata ⇒ Hash{String => Object}
Read and parse the values from metadata.json for the module that is being built.
22 23 24 25 26 |
# File 'lib/pdk/module/build.rb', line 22 def require 'pdk/module/metadata' @metadata ||= PDK::Module::Metadata.from_file(File.join(module_dir, 'metadata.json')).data end |
#module_pdk_compatible? ⇒ Boolean
Check if the module is PDK Compatible. If not, then prompt the user if they want to run PDK Convert.
55 56 57 |
# File 'lib/pdk/module/build.rb', line 55 def module_pdk_compatible? ['pdk-version', 'template-url'].any? { |key| .key?(key) } end |
#package_already_exists? ⇒ Boolean
Verify if there is an existing package in the target directory and prompts the user if they want to overwrite it.
49 50 51 |
# File 'lib/pdk/module/build.rb', line 49 def package_already_exists? PDK::Util::Filesystem.exist?(package_file) end |
#package_file ⇒ Object
Return the path where the built package file will be written to.
29 30 31 |
# File 'lib/pdk/module/build.rb', line 29 def package_file @package_file ||= File.join(target_dir, "#{release_name}.tar.gz") end |
#release_name ⇒ String
Combine the module name and version into a Forge-compatible dash separated string.
86 87 88 89 90 91 |
# File 'lib/pdk/module/build.rb', line 86 def release_name @release_name ||= [ ['name'], ['version'], ].join('-') end |
#stage_module_in_build_dir ⇒ Object
Iterate through all the files and directories in the module and stage them into the temporary build directory (unless ignored).
97 98 99 100 101 102 103 104 105 |
# File 'lib/pdk/module/build.rb', line 97 def stage_module_in_build_dir require 'find' Find.find(module_dir) do |path| next if path == module_dir ignored_path?(path) ? Find.prune : stage_path(path) end end |
#stage_path(path) ⇒ Object
Stage a file or directory from the module into the build directory.
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/pdk/module/build.rb', line 112 def stage_path(path) require 'pathname' relative_path = Pathname.new(path).relative_path_from(Pathname.new(module_dir)) dest_path = File.join(build_dir, relative_path) validate_path_encoding!(relative_path.to_path) if PDK::Util::Filesystem.directory?(path) PDK::Util::Filesystem.mkdir_p(dest_path, mode: PDK::Util::Filesystem.stat(path).mode) elsif PDK::Util::Filesystem.symlink?(path) warn_symlink(path) else validate_ustar_path!(relative_path.to_path) PDK::Util::Filesystem.cp(path, dest_path, preserve: true) end rescue ArgumentError => e raise PDK::CLI::ExitWithError, _( '%{message} Rename the file or exclude it from the package ' \ 'by adding it to the .pdkignore file in your module.', ) % { message: e. } end |
#validate_path_encoding!(path) ⇒ nil
Checks if the path contains any non-ASCII characters.
Java will throw an error when it encounters a path containing characters that are not supported by the hosts locale. In order to maximise compatibility we limit the paths to contain only ASCII characters, which should be part of any locale character set.
229 230 231 232 233 234 235 236 |
# File 'lib/pdk/module/build.rb', line 229 def validate_path_encoding!(path) return unless path =~ %r{[^\x00-\x7F]} raise ArgumentError, _( "'%{path}' can only include ASCII characters in its path or " \ 'filename in order to be compatible with a wide range of hosts.', ) % { path: path } end |
#validate_ustar_path!(path) ⇒ nil
Checks if the path length will fit into the POSIX.1-1998 (ustar) tar header format.
POSIX.1-2001 (which allows paths of infinite length) was adopted by GNU tar in 2004 and is supported by minitar 0.7 and above. Unfortunately much of the Puppet ecosystem still uses minitar 0.6.1.
POSIX.1-1998 tar format does not allow for paths greater than 256 bytes, or paths that can’t be split into a prefix of 155 bytes (max) and a suffix of 100 bytes (max).
This logic was pretty much copied from the private method Archive::Tar::Minitar::Writer#split_name.
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/pdk/module/build.rb', line 184 def validate_ustar_path!(path) if path.bytesize > 256 raise ArgumentError, _("The path '%{path}' is longer than 256 bytes.") % { path: path, } end if path.bytesize <= 100 prefix = '' else parts = path.split(File::SEPARATOR) newpath = parts.pop nxt = '' loop do nxt = parts.pop || '' break if newpath.bytesize + 1 + nxt.bytesize >= 100 newpath = File.join(nxt, newpath) end prefix = File.join(*parts, nxt) path = newpath end return unless path.bytesize > 100 || prefix.bytesize > 155 raise ArgumentError, _( "'%{path}' could not be split at a directory separator into two " \ 'parts, the first having a maximum length of 155 bytes and the ' \ 'second having a maximum length of 100 bytes.', ) % { path: path } end |
#warn_symlink(path) ⇒ Object
Warn the user about a symlink that would have been included in the built package.
153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/pdk/module/build.rb', line 153 def warn_symlink(path) require 'pathname' symlink_path = Pathname.new(path) module_path = Pathname.new(module_dir) PDK.logger.warn _('Symlinks in modules are not supported and will not be included in the package. Please investigate symlink %{from} -> %{to}.') % { from: symlink_path.relative_path_from(module_path), to: symlink_path.realpath.relative_path_from(module_path), } end |