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',
  pretans:      'pretans',
  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, #staging_dir

Methods included from Util

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

Methods included from Templating

included, #render_template

Methods included from Sugarable

extended, included, #node

Methods included from NullArgumentable

included, #null?

Methods included from Logging

included

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)

221
222
223
# File 'lib/omnibus/packagers/rpm.rb', line 221

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)

368
369
370
371
372
373
374
375
376
377
# File 'lib/omnibus/packagers/rpm.rb', line 368

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.gsub!('"', '')
  # 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

#config_filesArray

Get a list of user-declared config files

Returns:

  • (Array)

230
231
232
# File 'lib/omnibus/packagers/rpm.rb', line 230

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. The use of the fakeroot command is required so that the package is owned by root:root, but the build user does not need to have sudo permissions.


317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/omnibus/packagers/rpm.rb', line 317

def create_rpm_file
  command =  %|fakeroot rpmbuild|
  command << %| --target #{safe_architecture}|
  command << %| -bb|
  command << %| --buildroot #{staging_dir}/BUILD|
  command << %| --define '_topdir #{staging_dir}'|

  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: project.maintainer,
          gpg_path: "#{ENV['HOME']}/.gnupg", # TODO: Make this configurable
        }
      )
    end

    command << " --sign"
    command << " #{spec_file}"

    with_rpm_signing do |signing_script|
      log.info(log_key) { "Creating .rpm file" }
      shellout!("#{signing_script} \"#{command}\"", environment: { 'HOME' => home })
    end
  else
    log.info(log_key) { "Creating .rpm file" }
    command << " #{spec_file}"
    shellout!("#{command}")
  end

  FileSyncer.glob("#{staging_dir}/RPMS/**/*.rpm").each do |rpm|
    copy_file(rpm, Config.package_dir)
  end
end

#dist_tagString

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


445
446
447
# File 'lib/omnibus/packagers/rpm.rb', line 445

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

#filesystem_directoriesArray

Returns:

  • (Array)

240
241
242
# File 'lib/omnibus/packagers/rpm.rb', line 240

def filesystem_directories
  @filesystem_directories ||= IO.readlines(resource_path('filesystem_list')).map { |f| f.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 || 'unknown'
  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)

250
251
252
253
254
255
256
257
258
# File 'lib/omnibus/packagers/rpm.rb', line 250

def mark_filesystem_directories(fsdir)
  if fsdir.eql?('/') || fsdir.eql?('/usr/lib') || fsdir.eql?('/usr/share/empty')
    return "%dir %attr(0555,root,root) #{fsdir}"
  elsif filesystem_directories.include?(fsdir)
    return "%dir %attr(0755,root,root) #{fsdir}"
  else
    return "%dir #{fsdir}"
  end
end

#package_nameString

Returns:

  • (String)

212
213
214
# File 'lib/omnibus/packagers/rpm.rb', line 212

def package_name
  "#{safe_base_package_name}-#{safe_version}-#{safe_build_iteration}#{dist_tag}.#{safe_architecture}.rpm"
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_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


428
429
430
431
432
433
434
435
436
# File 'lib/omnibus/packagers/rpm.rb', line 428

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)

543
544
545
546
547
548
549
550
551
552
553
554
555
556
# File 'lib/omnibus/packagers/rpm.rb', line 543

def safe_architecture
  case Ohai['kernel']['machine']
  when 'i686'
    'i386'
  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)

455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
# File 'lib/omnibus/packagers/rpm.rb', line 455

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)

478
479
480
# File 'lib/omnibus/packagers/rpm.rb', line 478

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)

488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
# File 'lib/omnibus/packagers/rpm.rb', line 488

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.gsub('-', '_') #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.gsub('-', '~')
      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)

384
385
386
# File 'lib/omnibus/packagers/rpm.rb', line 384

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)

398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
# File 'lib/omnibus/packagers/rpm.rb', line 398

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
  block.call(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.


266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/omnibus/packagers/rpm.rb', line 266

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']
    }
  )
end