class Omnibus::Packager::PKG

Constants

SCRIPT_MAP

@return [Hash]

Public Instance Methods

build_component_pkg() click to toggle source

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.

@return [void]

# 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_pkg() click to toggle source

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

@return [void]

# 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_pkg() click to toggle source

The name of the (only) component package.

@return [String] the filename of the component .pkg file to create.

# File lib/omnibus/packagers/pkg.rb, line 320
def component_pkg
  "#{safe_base_package_name}-core.pkg"
end
final_pkg() click to toggle source

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

@return [String]

# 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) click to toggle source

Given a file path return any linked libraries.

@param [String] file_path

The path to a file

@return [Array<String>]

The linked libs
# 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) click to toggle source

The identifer for the PKG package.

@example

identifier 'com.getchef.chefdk'

@param [String] val

the package identifier

@return [String]

# File lib/omnibus/packagers/pkg.rb, line 91
def identifier(val = NULL)
  if null?(val)
    @identifier
  else
    @identifier = val
  end
end
is_binary?(bin) click to toggle source
# 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) click to toggle source
# 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_name() click to toggle source

@see Base#package_name

# 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_dir() click to toggle source

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.

@return [String]

# File lib/omnibus/packagers/pkg.rb, line 148
def resources_dir
  File.expand_path("#{staging_dir}/Resources")
end
safe_architecture() click to toggle source

Return the architecture

@return [String]

# File lib/omnibus/packagers/pkg.rb, line 329
def safe_architecture
  @safe_architecture ||= Ohai["kernel"]["machine"]
end
safe_base_package_name() click to toggle source

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

@return [String]

# 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_iteration() click to toggle source

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

@return [String]

# File lib/omnibus/packagers/pkg.rb, line 374
def safe_build_iteration
  project.build_iteration
end
safe_identifier() click to toggle source

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.

@return [String]

# 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_version() click to toggle source

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

@return [String]

# 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_dir() click to toggle source

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.

@return [String]

# File lib/omnibus/packagers/pkg.rb, line 160
def scripts_dir
  File.expand_path("#{staging_dir}/Scripts")
end
sign_binary(bin, hardened_runtime = false) click to toggle source
# 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) click to toggle source
# File lib/omnibus/packagers/pkg.rb, line 418
def sign_library(lib)
  sign_binary(lib)
end
sign_software_libs_and_bins() click to toggle source
# 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) click to toggle source

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

@example

signing_identity "foo"

@param [String] val

the identity to use when signing the PKG

@return [String]

the PKG-signing identity
# 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_file() click to toggle source

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.

@return [void]

# 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_scripts() click to toggle source

Copy all scripts in {Project#package_scripts_path} to the package directory.

@return [void]

# 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