Class: Omnibus::Packager::RPM

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

Constant Summary collapse

SCRIPT_MAP =

Returns:

  • (Hash)
{
  # Default Omnibus naming
  preinst: "pre",
  postinst: "post",
  prerm: "preun",
  postrm: "postun",
  # Default RPM naming
  pre: "pre",
  post: "post",
  preun: "preun",
  postun: "postun",
  verifyscript: "verifyscript",
  pretrans: "pretrans",
  posttrans: "posttrans",
}.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_dirString

The path to the BUILD directory inside the staging directory.

Returns:

  • (String)


300
301
302
# File 'lib/omnibus/packagers/rpm.rb', line 300

def build_dir
  @build_dir ||= File.join(staging_dir, "BUILD")
end

#build_filepath(path) ⇒ String

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

Returns:

  • (String)


469
470
471
472
473
474
475
476
477
478
479
480
# 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) ⇒ String

Set or return the category for this package.

Examples:

category "databases"

Parameters:

  • val (String) (defaults to: NULL)

    the category for this package

Returns:

  • (String)

    the category for this package



192
193
194
195
196
197
198
199
200
201
202
# 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

#compressionString

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

Returns:

  • (String)


395
396
397
398
399
400
401
402
403
404
405
# 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) ⇒ Integer

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

Examples:

compression_level 6

Parameters:

  • val (Integer) (defaults to: NULL)

    the compression level

Returns:

  • (Integer)

    the compression level for this package



267
268
269
270
271
272
273
274
275
276
277
# 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) ⇒ String

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

Examples:

compression_type :xz

Parameters:

  • val (Symbol) (defaults to: NULL)

    the compression type

Returns:

  • (String)

    the compression type for this package



242
243
244
245
246
247
248
249
250
251
252
# 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_filesArray

Get a list of user-declared config files

Returns:

  • (Array)


309
310
311
# File 'lib/omnibus/packagers/rpm.rb', line 309

def config_files
  @config_files ||= project.config_files.map { |file| rpm_safe(file) }
end

#create_rpm_filevoid

This method returns an undefined value.

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.



414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
# 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) ⇒ String

Set or return the dist_tag for this package

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

Parameters:

  • val (String) (defaults to: NULL)

    the dist_tag for this package

Returns:

  • (String)

    the dist_tag for this package

See Also:



221
222
223
224
225
226
227
# 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_directoriesArray

Returns:

  • (Array)


319
320
321
# 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) ⇒ String

Set or return the license for this package.

Examples:

license "Apache 2.0"

Parameters:

  • val (String) (defaults to: NULL)

    the license for this package

Returns:

  • (String)

    the license for this package



142
143
144
145
146
147
148
149
150
151
152
# 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) ⇒ String

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

Returns:

  • (String)


329
330
331
332
333
334
335
336
337
# 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_nameString

Returns:

  • (String)


287
288
289
290
291
292
293
# 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) ⇒ String

Set or return the priority for this package.

Examples:

priority "extra"

Parameters:

  • val (String) (defaults to: NULL)

    the priority for this package

Returns:

  • (String)

    the priority for this package



167
168
169
170
171
172
173
174
175
176
177
# 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_fileString

The full path to the rpm file.

Returns:

  • (String)


496
497
498
# File 'lib/omnibus/packagers/rpm.rb', line 496

def rpm_file
  "#{staging_dir}/RPMS/#{safe_architecture}/#{package_name}"
end

#rpm_safe(string) ⇒ Object

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

Parameters:

  • string (String)

    the string to sanitize



539
540
541
542
543
544
545
546
547
# 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_architectureString

The architecture for this RPM package.

Returns:

  • (String)


642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
# 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_nameString

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

Returns:

  • (String)


555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
# 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_iterationString

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

Returns:

  • (String)


578
579
580
# File 'lib/omnibus/packagers/rpm.rb', line 578

def safe_build_iteration
  project.build_iteration
end

#safe_versionString

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

Returns:

  • (String)


588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
# 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) ⇒ String

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

Examples:

signing_passphrase "foo"

Parameters:

  • val (String) (defaults to: NULL)

    the passphrase to use when signing the RPM

Returns:

  • (String)

    the RPM-signing passphrase



96
97
98
99
100
101
102
# 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_fileString

The full path to this spec file on disk.

Returns:

  • (String)


487
488
489
# File 'lib/omnibus/packagers/rpm.rb', line 487

def spec_file
  "#{staging_dir}/SPECS/#{package_name}.spec"
end

#vendor(val = NULL) ⇒ String

Set or return the vendor who made this package.

Examples:

vendor "Seth Vargo <[email protected]>"

Parameters:

  • val (String) (defaults to: NULL)

    the vendor who make this package

Returns:

  • (String)

    the vendor who make this package



117
118
119
120
121
122
123
124
125
126
127
# File 'lib/omnibus/packagers/rpm.rb', line 117

def vendor(val = NULL)
  if null?(val)
    @vendor || "Omnibus <[email protected]>"
  else
    unless val.is_a?(String)
      raise InvalidValue.new(:vendor, "be a String")
    end

    @vendor = val
  end
end

#with_rpm_signing(&block) ⇒ String

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.

Parameters:

  • block (Proc)

    the block to call

Returns:

  • (String)


510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
# 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_specvoid

This method returns an undefined value.

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



345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
# 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