Class: Omnibus::Packager::RPM

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

Constant Summary collapse

SCRIPT_MAP =
{
  # 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 NullArgumentable

included, #null?

Methods included from Logging

included

Methods included from Digestable

#digest, #digest_directory

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.


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

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

#category(val = NULL) ⇒ String

Set or return the category for this package.

Examples:

category "databases"

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

#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.


292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/omnibus/packagers/rpm.rb', line 292

def create_rpm_file
  command =  %|fakeroot rpmbuild|
  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

#license(val = NULL) ⇒ String

Set or return the license for this package.

Examples:

license "Apache 2.0"

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

#package_nameString


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

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

#priority(val = NULL) ⇒ String

Set or return the priority for this package.

Examples:

priority "extra"

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


386
387
388
389
390
391
392
393
394
# File 'lib/omnibus/packagers/rpm.rb', line 386

def rpm_safe(string)
  string = "\"#{string}\"" if string[/\s/]

  string.dup
    .gsub("[", "[\\[]")
    .gsub("*", "[*]")
    .gsub("?", "[?]")
    .gsub("%", "[%]")
end

#safe_architectureString

The architecture for this RPM package.


479
480
481
482
483
484
485
486
487
488
489
490
# File 'lib/omnibus/packagers/rpm.rb', line 479

def safe_architecture
  case Ohai['kernel']['machine']
  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 (-).


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

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.


425
426
427
# File 'lib/omnibus/packagers/rpm.rb', line 425

def safe_build_iteration
  project.build_iteration
end

#safe_versionString

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


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
463
464
465
466
467
468
469
470
471
472
# File 'lib/omnibus/packagers/rpm.rb', line 435

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 =~ /\-/
    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

    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"

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.


342
343
344
# File 'lib/omnibus/packagers/rpm.rb', line 342

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]>"

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.


356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
# File 'lib/omnibus/packagers/rpm.rb', line 356

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.


231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/omnibus/packagers/rpm.rb', line 231

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

  # Exclude directories from the spec that are owned by the filesystem package:
  # http://fedoraproject.org/wiki/Packaging:Guidelines#File_and_Directory_Ownership
  filesystem_directories = IO.readlines(resource_path('filesystem_list'))
  filesystem_directories.map! { |dirname| dirname.chomp }

  # Get a list of user-declared config files
  config_files = project.config_files.map { |file| rpm_safe(file) }

  # Get a list of all files
  files = FileSyncer.glob("#{build_dir}/**/*")
            .map    { |path| path.gsub("#{build_dir}/", '') }
            .map    { |path| "/#{path}" }
            .map    { |path| rpm_safe(path) }
            .reject { |path| config_files.include?(path) }
            .reject { |path| filesystem_directories.include?(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,
      architecture:   safe_architecture,
      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,
    }
  )
end