Class: Omnibus::Packager::PKG

Inherits:
Base
  • Object
show all
Defined in:
lib/omnibus/packagers/pkg.rb

Constant Summary collapse

SCRIPT_MAP =

Returns:

  • (Hash)
{
  # Default Omnibus naming
  preinst: "preinstall",
  postinst: "postinstall",
  # Default PKG naming
  preinstall: "preinstall",
  postinstall: "postinstall",
}.freeze

Constants included from Util

Util::SHELLOUT_OPTIONS

Constants included from NullArgumentable

NullArgumentable::NULL

Instance Attribute Summary

Attributes inherited from Base

#project

DSL methods collapse

Instance Method Summary collapse

Methods inherited from Base

build, #exclusions, id, #id, #initialize, #install_dir, #package_path, #resource_path, #resources_path, #run!, setup, #skip_packager, #staging_dir, #staging_dir_path

Methods included from Util

#compiler_safe_path, #copy_file, #create_directory, #create_file, #create_link, included, #path_key, #remove_directory, #remove_file, #retry_block, #shellout, #shellout!, #windows_safe_path

Methods included from Templating

included, #render_template, #render_template_content

Methods included from Sugarable

extended, included, #node

Methods included from NullArgumentable

included, #null?

Methods included from Logging

included

Methods included from Instrumentation

#measure

Methods included from Digestable

#digest, #digest_directory, included

Constructor Details

This class inherits a constructor from Omnibus::Packager::Base

Instance Method Details

#build_component_pkgvoid

This method returns an undefined value.

Construct the intermediate build product. It can be installed with the Installer.app, but doesn’t contain the data needed to customize the installer UI.



250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/omnibus/packagers/pkg.rb', line 250

def build_component_pkg
  command = <<~EOH
    pkgbuild \\
      --identifier "#{safe_identifier}" \\
      --version "#{safe_version}" \\
      --scripts "#{scripts_dir}" \\
      --root "#{project.install_dir}" \\
      --install-location "#{project.install_dir}" \\
      --preserve-xattr \\
  EOH

  command << %Q{  --sign "#{signing_identity}" \\\n} if signing_identity
  command << %Q{  "#{component_pkg}"}
  command << %Q{\n}

  Dir.chdir(staging_dir) do
    shellout!(command)
  end
end

#build_product_pkgvoid

This method returns an undefined value.

Construct the product package. The generated package is the final build product that is shipped to end users.



299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/omnibus/packagers/pkg.rb', line 299

def build_product_pkg
  command = <<~EOH
    productbuild \\
      --distribution "#{staging_dir}/Distribution" \\
      --resources "#{resources_dir}" \\
  EOH

  command << %Q{  --sign "#{signing_identity}" \\\n} if signing_identity
  command << %Q{  "#{final_pkg}"}
  command << %Q{\n}

  Dir.chdir(staging_dir) do
    shellout!(command)
  end
end

#component_pkgString

The name of the (only) component package.

Returns:

  • (String)

    the filename of the component .pkg file to create.



320
321
322
# File 'lib/omnibus/packagers/pkg.rb', line 320

def component_pkg
  "#{safe_base_package_name}-core.pkg"
end

#final_pkgString

The full path where the product package was/will be written.

Returns:

  • (String)


136
137
138
# File 'lib/omnibus/packagers/pkg.rb', line 136

def final_pkg
  File.expand_path("#{Config.package_dir}/#{package_name}")
end

#find_linked_libs(file_path) ⇒ Array<String>

Given a file path return any linked libraries.

Parameters:

  • file_path (String)

    The path to a file

Returns:

  • (Array<String>)

    The linked libs



409
410
411
412
413
414
415
416
# File 'lib/omnibus/packagers/pkg.rb', line 409

def find_linked_libs(file_path)
  # Find all libaries for each bin
  command = "otool -L #{file_path}"

  stdout = shellout!(command).stdout
  stdout.slice!(file_path)
  stdout.scan(/#{install_dir}\S*/)
end

#identifier(val = NULL) ⇒ String

The identifer for the PKG package.

Examples:

identifier 'com.getchef.chefdk'

Parameters:

  • val (String) (defaults to: NULL)

    the package identifier

Returns:

  • (String)


91
92
93
94
95
96
97
# File 'lib/omnibus/packagers/pkg.rb', line 91

def identifier(val = NULL)
  if null?(val)
    @identifier
  else
    @identifier = val
  end
end

#is_binary?(bin) ⇒ Boolean

Returns:

  • (Boolean)


433
434
435
436
437
438
# File 'lib/omnibus/packagers/pkg.rb', line 433

def is_binary?(bin)
  return false unless File.file?(bin) && File.executable?(bin) && !File.symlink?(bin)

  log.debug(log_key) { "    skipping non-binary file from signing: #{bin}" }
  true
end

#is_macho?(lib) ⇒ Boolean

Returns:

  • (Boolean)


440
441
442
443
444
445
446
447
448
449
# File 'lib/omnibus/packagers/pkg.rb', line 440

def is_macho?(lib)
  return false unless File.file?(lib) && File.executable?(lib) && !File.symlink?(lib)

  if shellout!("file #{lib}").stdout.match?(/Mach-O.*(library|bundle)/) # https://rubular.com/r/nRgaQlAbkM9wHL
    log.debug(log_key) { "    skipping non-Mach-O library file from signing: #{lib}" }
    return true
  end

  false
end

#package_nameObject

See Also:



127
128
129
# File 'lib/omnibus/packagers/pkg.rb', line 127

def package_name
  "#{safe_base_package_name}-#{safe_version}-#{safe_build_iteration}.#{safe_architecture}.pkg"
end

#resources_dirString

The path where the product package resources will live. We cannot store resources in the top-level staging dir, because productbuild‘s --resources flag expects a directory that does not contain the parent package.

Returns:

  • (String)


148
149
150
# File 'lib/omnibus/packagers/pkg.rb', line 148

def resources_dir
  File.expand_path("#{staging_dir}/Resources")
end

#safe_architectureString

Return the architecture

Returns:

  • (String)


329
330
331
# File 'lib/omnibus/packagers/pkg.rb', line 329

def safe_architecture
  @safe_architecture ||= Ohai["kernel"]["machine"]
end

#safe_base_package_nameString

Return the PKG-ready base package name, removing any invalid characters.

Returns:

  • (String)


338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/omnibus/packagers/pkg.rb', line 338

def safe_base_package_name
  if project.package_name =~ /\A[[:alnum:]-]+\z/
    project.package_name.dup
  else
    converted = project.package_name.downcase.gsub(/[^[:alnum:]+]/, "")

    log.warn(log_key) do
      "The `name' component of Mac package names can only include " \
      "alphabetical characters (a-z, A-Z), numbers (0-9), and -. Converting " \
      "`#{project.package_name}' to `#{converted}'."
    end

    converted
  end
end

#safe_build_iterationString

This is actually just the regular build_iternation, but it felt lonely among all the other safe_* methods.

Returns:

  • (String)


374
375
376
# File 'lib/omnibus/packagers/pkg.rb', line 374

def safe_build_iteration
  project.build_iteration
end

#safe_identifierString

The identifier for this mac package (the com.whatever.thing.whatever). This is a configurable project value, but a default value is calculated if one is not given.

Returns:

  • (String)


361
362
363
364
365
366
# File 'lib/omnibus/packagers/pkg.rb', line 361

def safe_identifier
  return identifier if identifier

  maintainer = project.maintainer.gsub(/[^[:alnum:]+]/, "").downcase
  "test.#{maintainer}.pkg.#{safe_base_package_name}"
end

#safe_versionString

Return the PKG-ready version, converting any invalid characters to dashes (-).

Returns:

  • (String)


384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
# File 'lib/omnibus/packagers/pkg.rb', line 384

def safe_version
  if project.build_version =~ /\A[a-zA-Z0-9\.\+\-]+\z/
    project.build_version.dup
  else
    converted = project.build_version.gsub(/[^a-zA-Z0-9\.\+\-]+/, "-")

    log.warn(log_key) do
      "The `version' component of Mac package names can only include " \
      "alphabetical characters (a-z, A-Z), numbers (0-9), dots (.), " \
      "plus signs (+), and dashes (-). Converting " \
      "`#{project.build_version}' to `#{converted}'."
    end

    converted
  end
end

#scripts_dirString

The path where the package scripts will live. We cannot store scripts in the top-level staging dir, because pkgbuild‘s --scripts flag expects a directory that does not contain the parent package.

Returns:

  • (String)


160
161
162
# File 'lib/omnibus/packagers/pkg.rb', line 160

def scripts_dir
  File.expand_path("#{staging_dir}/Scripts")
end

#sign_binary(bin, hardened_runtime = false) ⇒ Object



422
423
424
425
426
427
428
429
430
431
# File 'lib/omnibus/packagers/pkg.rb', line 422

def sign_binary(bin, hardened_runtime = false)
  command = "codesign -s '#{signing_identity}' '#{bin}'"
  command << %q{ --options=runtime} if hardened_runtime
  command << %Q{ --entitlements #{resource_path("entitlements.plist")}} if File.exist?(resource_path("entitlements.plist")) && hardened_runtime
  ## Force re-signing to deal with binaries that have the same sha.
  command << %q{ --force}
  command << %Q{\n}

  shellout!(command)
end

#sign_library(lib) ⇒ Object



418
419
420
# File 'lib/omnibus/packagers/pkg.rb', line 418

def sign_library(lib)
  sign_binary(lib)
end

#sign_software_libs_and_binsObject



182
183
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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/omnibus/packagers/pkg.rb', line 182

def sign_software_libs_and_bins
  if signing_identity
    log.info(log_key) { "Finding libraries and binaries that require signing." }

    bin_dirs = Set[]
    lib_dirs = Set[]
    binaries = Set[]
    libraries = Set[]

    # Capture lib_dirs and bin_dirs from each software
    project.softwares.each do |software|
      lib_dirs.merge(software.lib_dirs)
      bin_dirs.merge(software.bin_dirs)
    end

    # Find all binaries in each bind_dir
    bin_dirs.each do |dir|
      binaries.merge Dir["#{dir}/*"]
    end
    # Filter out symlinks, non-files, and non-executables
    log.debug(log_key) { "  Filtering non-binary files:" }
    binaries.select! { |bin| is_binary?(bin) }

    # Use otool to find all libries that are used by our binaries
    binaries.each do |bin|
      libraries.merge find_linked_libs bin
    end

    # Find all libraries in each lib_dir and add any we missed with otool
    lib_dirs.each do |dir|
      libraries.merge Dir["#{dir}/*"]
    end

    # Filter Mach-O libraries and bundles
    log.debug(log_key) { "  Filtering non-library files:" }
    libraries.select! { |lib| is_macho?(lib) }

    # Use otool to find all libries that are used by our libraries
    otool_libs = Set[]
    libraries.each do |lib|
      otool_libs.merge find_linked_libs lib
    end

    # Filter Mach-O libraries and bundles
    otool_libs.select! { |lib| is_macho?(lib) }
    libraries.merge otool_libs

    log.info(log_key) { "  Signing libraries:" } unless libraries.empty?
    libraries.each do |library|
      log.debug(log_key) { "    Signing: #{library}" }
      sign_library(library)
    end

    log.info(log_key) { "  Signing binaries:" } unless binaries.empty?
    binaries.each do |binary|
      log.debug(log_key) { "    Signing: #{binary}" }
      sign_binary(binary, true)
    end
  end
end

#signing_identity(val = NULL) ⇒ String

Set or return the signing identity. If this value is provided, Omnibus will attempt to sign the PKG.

Examples:

signing_identity "foo"

Parameters:

  • val (String) (defaults to: NULL)

    the identity to use when signing the PKG

Returns:

  • (String)

    the PKG-signing identity



113
114
115
116
117
118
119
# File 'lib/omnibus/packagers/pkg.rb', line 113

def signing_identity(val = NULL)
  if null?(val)
    @signing_identity
  else
    @signing_identity = val
  end
end

#write_distribution_filevoid

This method returns an undefined value.

Write the Distribution file to the staging area. This method generates the content of the Distribution file, which is used by productbuild to select the component packages to include in the product package.

It also includes information used to customize the UI of the Mac OS X installer.



280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/omnibus/packagers/pkg.rb', line 280

def write_distribution_file
  render_template(resource_path("distribution.xml.erb"),
    destination: "#{staging_dir}/Distribution",
    mode: 0600,
    variables: {
      friendly_name: project.friendly_name,
      identifier: safe_identifier,
      version: safe_version,
      component_pkg: component_pkg,
      host_architecture: safe_architecture,
    })
end

#write_scriptsvoid

This method returns an undefined value.

Copy all scripts in Omnibus::Project#package_scripts_path to the package directory.



170
171
172
173
174
175
176
177
178
179
180
# File 'lib/omnibus/packagers/pkg.rb', line 170

def write_scripts
  SCRIPT_MAP.each do |source, destination|
    source_path = File.join(project.package_scripts_path, source.to_s)

    if File.file?(source_path)
      destination_path = File.join(scripts_dir, destination)
      log.debug(log_key) { "Adding script `#{source}' to `#{destination_path}'" }
      copy_file(source_path, destination_path)
    end
  end
end