Class: Omnibus::Packager::DEB

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

Constant Summary

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

#compression_level(val = NULL) ⇒ Integer

Compression level (1-9) to use (-Z).

Examples:

compression_level 1

Parameters:

  • val (Integer) (defaults to: NULL)

    level of compression (1, .., 9)

Returns:

  • (Integer)

    level of compression for this package


231
232
233
234
235
236
237
238
239
240
241
# File 'lib/omnibus/packagers/deb.rb', line 231

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_paramsString

Return the parameters passed to dpkg-deb for setting the compression according to configuration.

Returns:

  • (String)

406
407
408
409
410
411
412
# File 'lib/omnibus/packagers/deb.rb', line 406

def compression_params
  if compression_strategy
    "-z#{compression_level} -Z#{compression_type} -S#{compression_strategy}"
  else
    "-z#{compression_level} -Z#{compression_type}"
  end
end

#compression_strategy(val = NULL) ⇒ Symbol

Compression strategy to use (-Z). For gzip: :filtered, :huffman, :rle, or :fixed; for xz: :extreme (nil means parameter will not be passsed to dpkg-deb)

Examples:

compression_strategy :extreme

Parameters:

  • val (Symbol) (defaults to: NULL)

    compression strategy

Returns:

  • (Symbol)

    compression strategy for this package


259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/omnibus/packagers/deb.rb', line 259

def compression_strategy(val = NULL)
  if null?(val)
    @compression_strategy
  else
    unless val.is_a?(Symbol) &&
        %i{filtered huffman rle fixed extreme}.member?(val)
      raise InvalidValue.new(:compression_strategy, "be a Symbol (:filtered, "\
                                                    ":huffman, :rle, :fixed, or :extreme)")
    end

    @compression_strategy = val
  end
end

#compression_type(val = NULL) ⇒ Symbol

Compression algorithm (gzip, xz, none) to use (-Z).

Examples:

compression_type :xz

Parameters:

  • val (Symbol) (defaults to: NULL)

    type of compression (:gzip, :xz, :none)

Returns:

  • (Symbol)

    type of compression for this package


206
207
208
209
210
211
212
213
214
215
216
# File 'lib/omnibus/packagers/deb.rb', line 206

def compression_type(val = NULL)
  if null?(val)
    @compression_type || :gzip
  else
    unless val.is_a?(Symbol) && %i{gzip xz none}.member?(val)
      raise InvalidValue.new(:compression_type, "be a Symbol (:gzip, :xz, or :none)")
    end

    @compression_type = val
  end
end

#create_deb_filevoid

This method returns an undefined value.

Create the .deb file, compressing at gzip level 9. 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.


391
392
393
394
395
396
397
398
# File 'lib/omnibus/packagers/deb.rb', line 391

def create_deb_file
  log.info(log_key) { "Creating .deb file" }

  # Execute the build command
  Dir.chdir(Config.package_dir) do
    shellout!("fakeroot dpkg-deb #{compression_params} -D --build #{staging_dir} #{package_name}")
  end
end

#debian_dirString

The path where Debian-specific files will live.

Examples:

/var/.../chef-server_11.12.4/DEBIAN

Returns:

  • (String)

294
295
296
# File 'lib/omnibus/packagers/deb.rb', line 294

def debian_dir
  @debian_dir ||= File.join(staging_dir, "DEBIAN")
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


131
132
133
134
135
136
137
138
139
140
141
# File 'lib/omnibus/packagers/deb.rb', line 131

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

#package_nameObject

The name of the package to create. Note, this does *not* include the extension.


282
283
284
# File 'lib/omnibus/packagers/deb.rb', line 282

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

#package_sizeFixnum

The size of this Debian package. This is dynamically calculated.

Returns:

  • (Fixnum)

475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
# File 'lib/omnibus/packagers/deb.rb', line 475

def package_size
  @package_size ||= begin
    path  = "#{project.install_dir}/**/*"
    total = FileSyncer.glob(path).inject(0) do |size, path|
      unless File.directory?(path) || File.symlink?(path)
        size += File.size(path)
      end

      size
    end

    # Per http://www.debian.org/doc/debian-policy/ch-controlfields.html, the
    # disk space is given as the integer value of the estimated installed
    # size in bytes, divided by 1024 and rounded up.
    total / 1024
  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


156
157
158
159
160
161
162
163
164
165
166
# File 'lib/omnibus/packagers/deb.rb', line 156

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

#safe_architectureString

Debian does not follow the standards when naming 64-bit packages.

Returns:

  • (String)

571
572
573
# File 'lib/omnibus/packagers/deb.rb', line 571

def safe_architecture
  @safe_architecture ||= shellout!("dpkg --print-architecture").stdout.split("\n").first || "noarch"
end

#safe_base_package_nameString

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

Returns:

  • (String)

499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
# File 'lib/omnibus/packagers/deb.rb', line 499

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 Debian package names can only include " \
      "lower case 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_iteration, but it felt lonely among all the other safe_* methods.

Returns:

  • (String)

522
523
524
# File 'lib/omnibus/packagers/deb.rb', line 522

def safe_build_iteration
  project.build_iteration
end

#safe_versionString

Return the Debian-ready version, replacing all dashes (-) with tildes (+~+) and converting any invalid characters to underscores (_).

Returns:

  • (String)

532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
# File 'lib/omnibus/packagers/deb.rb', line 532

def safe_version
  version = project.build_version.dup

  if version =~ /\-/
    converted = version.tr("-", "~")

    log.warn(log_key) do
      "Dashes hold special significance in the Debian package versions. " \
      "Versions that contain a dash and should be considered an earlier " \
      "version (e.g. pre-releases) may actually be ordered as later " \
      "(e.g. 12.0.0-rc.6 > 12.0.0). We'll work around this by replacing " \
      "dashes (-) with tildes (~). 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 Debian package names can only include " \
      "alphabetical characters (a-z, A-Z), numbers (0-9), dots (.), " \
      "plus signs (+), dashes (-), tildes (~) and colons (:). Converting " \
      "`#{project.build_version}' to `#{converted}'."
    end

    converted
  end
end

#section(val = NULL) ⇒ String

Set or return the section for this package.

Examples:

section "databases"

Parameters:

  • val (String) (defaults to: NULL)

    the section for this package

Returns:

  • (String)

    the section for this package


181
182
183
184
185
186
187
188
189
190
191
# File 'lib/omnibus/packagers/deb.rb', line 181

def section(val = NULL)
  if null?(val)
    @section || "misc"
  else
    unless val.is_a?(String)
      raise InvalidValue.new(:section, "be a String")
    end

    @section = val
  end
end

#sign_deb_filevoid

This method returns an undefined value.

Sign the .deb file with gpg. This has to be done as separate steps from creating the .deb file. See debsigs source for behavior replicated here. gitlab.com/debsigs/debsigs/blob/master/debsigs.txt#L103-124


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
463
464
465
466
467
468
# File 'lib/omnibus/packagers/deb.rb', line 420

def sign_deb_file
  unless signing_passphrase
    log.info(log_key) { "Signing not enabled for .deb file" }
    return
  end

  log.info(log_key) { "Signing enabled for .deb file" }

  # Check our dependencies and determine command for GnuPG. +Omnibus.which+ returns the path, or nil.
  gpg = nil
  if Omnibus.which("gpg2")
    gpg = "gpg2"
  elsif Omnibus.which("gpg")
    gpg = "gpg"
  end

  if gpg && Omnibus.which("ar")
    # Create a directory that will be cleaned when we leave the block
    Dir.mktmpdir do |tmp_dir|
      Dir.chdir(tmp_dir) do
        # Extract the deb file contents
        shellout!("ar x #{Config.package_dir}/#{package_name}")
        # Concatenate contents, in order per +debsigs+ documentation.
        shellout!("cat debian-binary control.tar.* data.tar.* > complete")
        # Create signature (as +root+)
        gpg_command =  "#{gpg} --armor --sign --detach-sign"
        gpg_command << " --local-user '#{project.maintainer}'"
        gpg_command << " --homedir #{ENV["HOME"]}/.gnupg" # TODO: Make this configurable
        ## pass the +signing_passphrase+ via +STDIN+
        gpg_command << " --batch --no-tty"
        ## Check `gpg` for the compatibility/need of pinentry-mode
        # - We're calling gpg with the +--pinentry-mode+ argument, and +STDIN+ of +/dev/null+
        # - This _will_ fail with exit code 2 no matter what. We want to check the +STDERR+
        #   for the error message about the parameter. If it is _not present_ in the
        #   output, then we _do_ want to add it. (If +grep -q+ is +1+, add parameter)
        if shellout("#{gpg} --pinentry-mode loopback </dev/null 2>&1 | grep -q pinentry-mode").exitstatus == 1
          gpg_command << " --pinentry-mode loopback"
        end
        gpg_command << " --passphrase-fd 0"
        gpg_command << " -o _gpgorigin complete"
        shellout!("fakeroot #{gpg_command}", input: signing_passphrase)
        # Append +_gpgorigin+ to the +.deb+ file (as +root+)
        shellout!("fakeroot ar rc #{Config.package_dir}/#{package_name} _gpgorigin")
      end
    end
  else
    log.info(log_key) { "Signing not possible. Ensure that GnuPG and GNU AR are available" }
  end
end

#signing_passphrase(val = NULL) ⇒ String

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

Examples:

signing_passphrase "foo"

Parameters:

  • val (String) (defaults to: NULL)

    the passphrase to use when signing the DEB

Returns:

  • (String)

    the DEB-signing passphrase


85
86
87
88
89
90
91
# File 'lib/omnibus/packagers/deb.rb', line 85

def signing_passphrase(val = NULL)
  if null?(val)
    @signing_passphrase
  else
    @signing_passphrase = val
  end
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


106
107
108
109
110
111
112
113
114
115
116
# File 'lib/omnibus/packagers/deb.rb', line 106

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

#write_conffiles_filevoid

This method returns an undefined value.

Render the list of config files into the conffile.


331
332
333
334
335
336
337
338
339
# File 'lib/omnibus/packagers/deb.rb', line 331

def write_conffiles_file
  return if project.config_files.empty?

  render_template(resource_path("conffiles.erb"),
    destination: File.join(debian_dir, "conffiles"),
    variables: {
      config_files: project.config_files,
    })
end

#write_control_filevoid

This method returns an undefined value.

Render a control file in #{debian_dir}/control using the supplied ERB template.


304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/omnibus/packagers/deb.rb', line 304

def write_control_file
  render_template(resource_path("control.erb"),
    destination: File.join(debian_dir, "control"),
    variables: {
      name: safe_base_package_name,
      version: safe_version,
      iteration: safe_build_iteration,
      vendor: vendor,
      license: license,
      architecture: safe_architecture,
      maintainer: project.maintainer,
      installed_size: package_size,
      homepage: project.homepage,
      description: project.description,
      priority: priority,
      section: section,
      conflicts: project.conflicts,
      replaces: project.replaces,
      dependencies: project.runtime_dependencies,
    })
end

#write_md5_sumsvoid

This method returns an undefined value.

Generate a list of the md5 sums of every file in the package and write it to #{debian_dir}/control/md5sums.


366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
# File 'lib/omnibus/packagers/deb.rb', line 366

def write_md5_sums
  path = "#{staging_dir}/**/*"
  hash = FileSyncer.glob(path).inject({}) do |hash, path|
    if File.file?(path) && !File.symlink?(path) && !(File.dirname(path) == debian_dir)
      relative_path = path.gsub("#{staging_dir}/", "")
      hash[relative_path] = digest(path, :md5)
    end

    hash
  end

  render_template(resource_path("md5sums.erb"),
    destination: File.join(debian_dir, "md5sums"),
    variables: {
      md5sums: hash,
    })
end

#write_scriptsvoid

This method returns an undefined value.

Copy all scripts in Omnibus::Project#package_scripts_path to the control directory of this repo.


347
348
349
350
351
352
353
354
355
356
357
358
# File 'lib/omnibus/packagers/deb.rb', line 347

def write_scripts
  %w{preinst postinst prerm postrm}.each do |script|
    path = File.join(project.package_scripts_path, script)

    if File.file?(path)
      log.debug(log_key) { "Adding script `#{script}' to `#{debian_dir}' from #{path}" }
      copy_file(path, debian_dir)
      log.debug(log_key) { "SCRIPT FILE:  #{debian_dir}/#{script}" }
      FileUtils.chmod(0755, File.join(debian_dir, script))
    end
  end
end