class Omnibus::Packager::RPM

Constants

SCRIPT_MAP

@return [Hash]

Public Instance Methods

build_dir() click to toggle source

The path to the BUILD directory inside the staging directory.

@return [String]

# File lib/omnibus/packagers/rpm.rb, line 300
def build_dir
  @build_dir ||= File.join(staging_dir, "BUILD")
end
build_filepath(path) click to toggle source

Convert the path of a file in the staging directory to an entry for use in the spec file.

@return [String]

# File lib/omnibus/packagers/rpm.rb, line 469
def build_filepath(path)
  filepath = rpm_safe("/" + path.gsub("#{build_dir}/", ""))
  return if config_files.include?(filepath)

  full_path = build_dir + filepath.gsub("[%]", "%")
  # FileSyncer.glob quotes pathnames that contain spaces, which is a problem on el7
  full_path.delete!('"')
  # Mark directories with the %dir directive to prevent rpmbuild from counting their contents twice.
  return mark_filesystem_directories(filepath) if !File.symlink?(full_path) && File.directory?(full_path)

  filepath
end
category(val = NULL) click to toggle source

Set or return the category for this package.

@example

category "databases"

@param [String] val

the category for this package

@return [String]

the category for this package
# File lib/omnibus/packagers/rpm.rb, line 192
def category(val = NULL)
  if null?(val)
    @category || "default"
  else
    unless val.is_a?(String)
      raise InvalidValue.new(:category, "be a String")
    end

    @category = val
  end
end
compression() click to toggle source

Returns the RPM spec “_binary_payload” line corresponding to the compression configuration.

@return [String]

# File lib/omnibus/packagers/rpm.rb, line 395
def compression
  compression_name = case compression_type
                     when :bzip2
                       "bzdio"
                     when :xz
                       "xzdio"
                     else # default to gzip
                       "gzdio"
                     end
  "w#{compression_level}.#{compression_name}"
end
compression_level(val = NULL) click to toggle source

Set or return the compression level (1-9) for this package

@example

compression_level 6

@param [Integer] val

the compression level

@return [Integer]

the compression level for this package
# File lib/omnibus/packagers/rpm.rb, line 267
def compression_level(val = NULL)
  if null?(val)
    @compression_level || 9
  else
    unless val.is_a?(Integer) && 1 <= val && 9 >= val
      raise InvalidValue.new(:compression_level, "be an Integer (between 1 and 9)")
    end

    @compression_level = val
  end
end
compression_type(val = NULL) click to toggle source

Set or return the compression type (:gzip, :bzip2, :xz) for this package

@example

compression_type :xz

@param [Symbol] val

the compression type

@return [String]

the compression type for this package
# File lib/omnibus/packagers/rpm.rb, line 242
def compression_type(val = NULL)
  if null?(val)
    @compression_type || :gzip
  else
    unless val.is_a?(Symbol) && %i{gzip bzip2 xz}.member?(val)
      raise InvalidValue.new(:compression_type, "be a Symbol (:gzip, :bzip2, or :xz)")
    end

    @compression_type = val
  end
end
config_files() click to toggle source

Get a list of user-declared config files

@return [Array]

# File lib/omnibus/packagers/rpm.rb, line 309
def config_files
  @config_files ||= project.config_files.map { |file| rpm_safe(file) }
end
create_rpm_file() click to toggle source

Generate the RPM file using rpmbuild. Unlike debian,the fakeroot command is not required for the package to be owned by root:root. The rpmuser specified in the spec file dictates this.

@return [void]

# File lib/omnibus/packagers/rpm.rb, line 414
def create_rpm_file
  command =  %{rpmbuild}
  command << %{ --target #{safe_architecture}}
  command << %{ -bb}
  command << %{ --buildroot #{staging_dir}/BUILD}
  command << %{ --define '_topdir #{staging_dir}'}
  command << " #{spec_file}"

  log.info(log_key) { "Creating .rpm file" }
  shellout!("#{command}")

  if signing_passphrase
    log.info(log_key) { "Signing enabled for .rpm file" }

    if File.exist?("#{ENV["HOME"]}/.rpmmacros")
      log.info(log_key) { "Detected .rpmmacros file at `#{ENV["HOME"]}'" }
      home = ENV["HOME"]
    else
      log.info(log_key) { "Using default .rpmmacros file from Omnibus" }

      # Generate a temporary home directory
      home = Dir.mktmpdir

      render_template(resource_path("rpmmacros.erb"),
        destination: "#{home}/.rpmmacros",
        variables: {
          gpg_name: "Opscode Packages",
          gpg_path: "#{ENV["HOME"]}/.gnupg", # TODO: Make this configurable
        })
    end

    sign_cmd = "rpmsign --addsign #{rpm_file}"
    with_rpm_signing do |signing_script|
      log.info(log_key) { "Signing the built rpm file" }

      # RHEL 8 has gpg-agent running so we can skip the expect script since the agent
      # takes care of the passphrase entering on the signing
      if dist_tag != ".el8" && dist_tag != ".el9"
        sign_cmd.prepend("#{signing_script} \"").concat("\"")
      end

      shellout!("#{sign_cmd}", environment: { "HOME" => home })
    end
  end

  FileSyncer.glob("#{staging_dir}/RPMS/**/*.rpm").each do |rpm|
    copy_file(rpm, Config.package_dir)
  end
end
dist_tag(val = NULL) click to toggle source

Set or return the dist_tag for this package

The Dist Tag for this RPM package as per the Fedora packaging guidlines.

@see fedoraproject.org/wiki/Packaging:DistTag

@example

dist_tag ".#{Omnibus::Metadata.platform_shortname}#{Omnibus::Metadata.platform_version}"

@param [String] val

the dist_tag for this package

@return [String]

the dist_tag for this package
# File lib/omnibus/packagers/rpm.rb, line 221
def dist_tag(val = NULL)
  if null?(val)
    @dist_tag || ".#{Omnibus::Metadata.platform_shortname}#{Omnibus::Metadata.platform_version}"
  else
    @dist_tag = val
  end
end
filesystem_directories() click to toggle source

Directories owned by the filesystem package: fedoraproject.org/wiki/Packaging:Guidelines#File_and_Directory_Ownership

@return [Array]

# File lib/omnibus/packagers/rpm.rb, line 319
def filesystem_directories
  @filesystem_directories ||= IO.readlines(resource_path("filesystem_list")).map(&:chomp)
end
license(val = NULL) click to toggle source

Set or return the license for this package.

@example

license "Apache 2.0"

@param [String] val

the license for this package

@return [String]

the license for this package
# File lib/omnibus/packagers/rpm.rb, line 142
def license(val = NULL)
  if null?(val)
    @license || project.license
  else
    unless val.is_a?(String)
      raise InvalidValue.new(:license, "be a String")
    end

    @license = val
  end
end
mark_filesystem_directories(fsdir) click to toggle source

Mark filesystem directories with ownership and permissions specified in the filesystem package git.fedorahosted.org/cgit/filesystem.git/plain/filesystem.spec

@return [String]

# File lib/omnibus/packagers/rpm.rb, line 329
def mark_filesystem_directories(fsdir)
  if fsdir.eql?("/") || fsdir.eql?("/usr/lib") || fsdir.eql?("/usr/share/empty")
    "%dir %attr(0555,root,root) #{fsdir}"
  elsif filesystem_directories.include?(fsdir)
    "%dir %attr(0755,root,root) #{fsdir}"
  else
    "%dir #{fsdir}"
  end
end
package_name() click to toggle source

@return [String]

# File lib/omnibus/packagers/rpm.rb, line 287
def package_name
  if dist_tag
    "#{safe_base_package_name}-#{safe_version}-#{safe_build_iteration}#{dist_tag}.#{safe_architecture}.rpm"
  else
    "#{safe_base_package_name}-#{safe_version}-#{safe_build_iteration}.#{safe_architecture}.rpm"
  end
end
priority(val = NULL) click to toggle source

Set or return the priority for this package.

@example

priority "extra"

@param [String] val

the priority for this package

@return [String]

the priority for this package
# File lib/omnibus/packagers/rpm.rb, line 167
def priority(val = NULL)
  if null?(val)
    @priority || "extra"
  else
    unless val.is_a?(String)
      raise InvalidValue.new(:priority, "be a String")
    end

    @priority = val
  end
end
rpm_file() click to toggle source

The full path to the rpm file.

@return [String]

# File lib/omnibus/packagers/rpm.rb, line 496
def rpm_file
  "#{staging_dir}/RPMS/#{safe_architecture}/#{package_name}"
end
rpm_safe(string) click to toggle source

Generate an RPM-safe name from the given string, doing the following:

  • Replace [ with [[] to make rpm not use globs

  • Replace * with [*] to make rpm not use globs

  • Replace ? with [?] to make rpm not use globs

  • Replace % with [%] to make rpm not expand macros

@param [String] string

the string to sanitize
# File lib/omnibus/packagers/rpm.rb, line 539
def rpm_safe(string)
  string = "\"#{string}\"" if string[/\s/]

  string.dup
    .gsub("[", "[\\[]")
    .gsub("*", "[*]")
    .gsub("?", "[?]")
    .gsub("%", "[%]")
end
safe_architecture() click to toggle source

The architecture for this RPM package.

@return [String]

# File lib/omnibus/packagers/rpm.rb, line 642
def safe_architecture
  case Ohai["kernel"]["machine"]
  when "i686"
    "i386"
  when "armv7l" # raspberry pi 3 CentOS
    "armv7hl"
  when "armv6l"
    if Ohai["platform"] == "pidora"
      "armv6hl"
    else
      "armv6l"
    end
  else
    Ohai["kernel"]["machine"]
  end
end
safe_base_package_name() click to toggle source

Return the RPM-ready base package name, converting any invalid characters to dashes (-).

@return [String]

# File lib/omnibus/packagers/rpm.rb, line 555
def safe_base_package_name
  if project.package_name =~ /\A[a-z0-9\.\+\-]+\z/
    project.package_name.dup
  else
    converted = project.package_name.downcase.gsub(/[^a-z0-9\.\+\-]+/, "-")

    log.warn(log_key) do
      "The `name' component of RPM package names can only include " \
      "lowercase alphabetical characters (a-z), numbers (0-9), dots (.), " \
      "plus signs (+), and dashes (-). 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/rpm.rb, line 578
def safe_build_iteration
  project.build_iteration
end
safe_version() click to toggle source

RPM package versions cannot contain dashes, so we will convert them to underscores.

@return [String]

# File lib/omnibus/packagers/rpm.rb, line 588
def safe_version
  version = project.build_version.dup

  # RPM 4.10+ added support for using the tilde (~) as a way to mark
  # versions as lower priority in comparisons. More details on this
  # feature can be found here:
  #
  #   http://rpm.org/ticket/56
  #
  if version =~ /\-/
    if Ohai["platform_family"] == "wrlinux"
      converted = version.tr("-", "_") # WRL has an elderly RPM version
      log.warn(log_key) do
        "Omnibus replaces dashes (-) with tildes (~) so pre-release " \
        "versions get sorted earlier than final versions.  However, the " \
        "version of rpmbuild on Wind River Linux does not support this. " \
        "All dashes will be replaced with underscores (_). Converting " \
        "`#{project.build_version}' to `#{converted}'."
      end
    else
      converted = version.tr("-", "~")
      log.warn(log_key) do
        "Tildes hold special significance in the RPM package versions. " \
        "They mark a version as lower priority in RPM's version compare " \
        "logic. We'll replace all dashes (-) with tildes (~) so pre-release" \
        "versions get sorted earlier then final versions. Converting" \
        "`#{project.build_version}' to `#{converted}'."
      end
    end

    version = converted
  end

  if version =~ /\A[a-zA-Z0-9\.\+\~]+\z/
    version
  else
    converted = version.gsub(/[^a-zA-Z0-9\.\+\~]+/, "_")

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

    converted
  end
end
signing_passphrase(val = NULL) click to toggle source

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

@example

signing_passphrase "foo"

@param [String] val

the passphrase to use when signing the RPM

@return [String]

the RPM-signing passphrase
# File lib/omnibus/packagers/rpm.rb, line 96
def signing_passphrase(val = NULL)
  if null?(val)
    @signing_passphrase
  else
    @signing_passphrase = val
  end
end
spec_file() click to toggle source

The full path to this spec file on disk.

@return [String]

# File lib/omnibus/packagers/rpm.rb, line 487
def spec_file
  "#{staging_dir}/SPECS/#{package_name}.spec"
end
vendor(val = NULL) click to toggle source

Set or return the vendor who made this package.

@example

vendor "Seth Vargo <sethvargo@gmail.com>"

@param [String] val

the vendor who make this package

@return [String]

the vendor who make this package
# File lib/omnibus/packagers/rpm.rb, line 117
def vendor(val = NULL)
  if null?(val)
    @vendor || "Omnibus <omnibus@getchef.com>"
  else
    unless val.is_a?(String)
      raise InvalidValue.new(:vendor, "be a String")
    end

    @vendor = val
  end
end
with_rpm_signing() { |destination| ... } click to toggle source

Render the rpm signing script with secure permissions, call the given block with the path to the script, and ensure deletion of the script from disk since it contains sensitive information.

@param [Proc] block

the block to call

@return [String]

# File lib/omnibus/packagers/rpm.rb, line 510
def with_rpm_signing(&block)
  directory   = Dir.mktmpdir
  destination = "#{directory}/sign-rpm"

  render_template(resource_path("signing.erb"),
    destination: destination,
    mode: 0700,
    variables: {
      passphrase: signing_passphrase,
    })

  # Yield the destination to the block
  yield(destination)
ensure
  remove_file(destination)
  remove_directory(directory)
end
write_rpm_spec() click to toggle source

Render an rpm spec file in +SPECS/#{name}.spec+ using the supplied ERB template.

@return [void]

# File lib/omnibus/packagers/rpm.rb, line 345
def write_rpm_spec
  # Create a map of scripts that exist and their contents
  scripts = SCRIPT_MAP.inject({}) do |hash, (source, destination)|
    path =  File.join(project.package_scripts_path, source.to_s)

    if File.file?(path)
      hash[destination] = File.read(path)
    end

    hash
  end

  # Get a list of all files
  files = FileSyncer.glob("#{build_dir}/**/*")
    .map { |path| build_filepath(path) }

  render_template(resource_path("spec.erb"),
    destination: spec_file,
    variables: {
      name: safe_base_package_name,
      version: safe_version,
      iteration: safe_build_iteration,
      vendor: vendor,
      license: license,
      dist_tag: dist_tag,
      maintainer: project.maintainer,
      homepage: project.homepage,
      description: project.description,
      priority: priority,
      category: category,
      conflicts: project.conflicts,
      replaces: project.replaces,
      dependencies: project.runtime_dependencies,
      user: project.package_user,
      group: project.package_group,
      scripts: scripts,
      config_files: config_files,
      files: files,
      build_dir: build_dir,
      platform_family: Ohai["platform_family"],
      compression: compression,
    })
end