Class: FPM::Package

Inherits:
Object
  • Object
show all
Includes:
Cabin::Inspectable, Util
Defined in:
lib/fpm/package.rb,
lib/fpm/namespace.rb

Overview

This class is the parent of all packages. If you want to implement an FPM package type, you'll inherit from this.

Defined Under Namespace

Classes: APK, CPAN, Deb, Dir, Empty, FileAlreadyExists, FreeBSD, Gem, InvalidArgument, NPM, OSXpkg, P5P, PEAR, Pacman, ParentDirectoryMissing, Pkgin, PleaseRun, Puppet, Python, RPM, Sh, Snap, Solaris, Tar, Virtualenv, Zip

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Util

#ar_cmd, #ar_cmd_deterministic?, #copied_entries, #copy_entry, #copy_metadata, #default_shell, #execmd, #expand_pessimistic_constraints, #logger, #mknod_w, #program_exists?, #program_in_path?, #safesystem, #safesystemout, #tar_cmd, #tar_cmd_supports_sort_names_and_set_mtime?

Constructor Details

#initializePackage

Returns a new instance of Package.


120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/fpm/package.rb', line 120

def initialize
  # Attributes for this specific package
  @attributes = {
    # Default work location
    :workdir => ::Dir.tmpdir
  }

  # Reference
  # http://www.debian.org/doc/manuals/maint-guide/first.en.html
  # http://wiki.debian.org/DeveloperConfiguration
  # https://github.com/jordansissel/fpm/issues/37
  if ENV.include?("DEBEMAIL") and ENV.include?("DEBFULLNAME")
    # Use DEBEMAIL and DEBFULLNAME as the default maintainer if available.
    @maintainer = "#{ENV["DEBFULLNAME"]} <#{ENV["DEBEMAIL"]}>"
  else
    # TODO(sissel): Maybe support using 'git config' for a default as well?
    # git config --get user.name, etc can be useful.
    #
    # Otherwise default to [email protected]
    @maintainer = "<#{ENV["USER"]}@#{Socket.gethostname}>"
  end

  # Set attribute defaults based on flags
  # This allows you to define command line options with default values
  # that also are obeyed if fpm is used programmatically.
  self.class.default_attributes do |attribute, value|
    attributes[attribute] = value
  end

  @name = nil
  @architecture = "native"
  @description = "no description given"
  @version = nil
  @epoch = nil
  @iteration = nil
  @url = nil
  @category = "default"
  @license = "unknown"
  @vendor = "none"

  # Iterate over all the options and set defaults
  if self.class.respond_to?(:declared_options)
    self.class.declared_options.each do |option|
      option.attribute_name.tap do |attr|
        # clamp makes option attributes available as accessor methods
        # do --foo-bar is available as 'foo_bar'
        # make these available as package attributes.
        attr = "#{attr}?" if !respond_to?(attr)
        input.attributes[attr.to_sym] = send(attr) if respond_to?(attr)
      end
    end
  end

  @provides = []
  @conflicts = []
  @replaces = []
  @dependencies = []
  @scripts = {}
  @config_files = []
  @directories = []
  @attrs = {}

  build_path
  # Dont' initialize staging_path just yet, do it lazily so subclass can get a word in.
end

Instance Attribute Details

#architectureObject

What architecture is this package for?


81
82
83
# File 'lib/fpm/package.rb', line 81

def architecture
  @architecture
end

#attributesObject

Any other attributes specific to this package. This is where you'd put rpm, deb, or other specific attributes.


114
115
116
# File 'lib/fpm/package.rb', line 114

def attributes
  @attributes
end

#attrsObject

Returns the value of attribute attrs


116
117
118
# File 'lib/fpm/package.rb', line 116

def attrs
  @attrs
end

#categoryObject

The category of this package. RedHat calls this 'Group' Debian calls this 'Section' FreeBSD would put this in /usr/ports/<category>/…


75
76
77
# File 'lib/fpm/package.rb', line 75

def category
  @category
end

#config_filesObject

Array of configuration files


108
109
110
# File 'lib/fpm/package.rb', line 108

def config_files
  @config_files
end

#conflictsObject

Array of things this package conflicts with. (Not all packages support this)


92
93
94
# File 'lib/fpm/package.rb', line 92

def conflicts
  @conflicts
end

#dependenciesObject

Array of dependencies.


84
85
86
# File 'lib/fpm/package.rb', line 84

def dependencies
  @dependencies
end

#descriptionObject

a summary or description of the package


99
100
101
# File 'lib/fpm/package.rb', line 99

def description
  @description
end

#directoriesObject

Returns the value of attribute directories


110
111
112
# File 'lib/fpm/package.rb', line 110

def directories
  @directories
end

#epochObject

The epoch version of this package This is used most when an upstream package changes it's versioning style so standard comparisions wouldn't work.


48
49
50
# File 'lib/fpm/package.rb', line 48

def epoch
  @epoch
end

#iterationObject

The iteration of this package.

Debian calls this 'release' and is the last '-NUMBER' in the version
RedHat has this as 'Release' in the .spec file
FreeBSD calls this 'PORTREVISION'

Iteration can be nil. If nil, the fpm package implementation is expected to handle any default value that should be instead.


57
58
59
# File 'lib/fpm/package.rb', line 57

def iteration
  @iteration
end

#licenseObject

A identifier representing the license. Any string is fine.


78
79
80
# File 'lib/fpm/package.rb', line 78

def license
  @license
end

#maintainerObject

Who maintains this package? This could be the upstream author or the package maintainer. You pick.


61
62
63
# File 'lib/fpm/package.rb', line 61

def maintainer
  @maintainer
end

#nameObject

The name of this package


40
41
42
# File 'lib/fpm/package.rb', line 40

def name
  @name
end

#providesObject

Array of things this package provides. (Not all packages support this)


88
89
90
# File 'lib/fpm/package.rb', line 88

def provides
  @provides
end

#replacesObject

Array of things this package replaces. (Not all packages support this)


96
97
98
# File 'lib/fpm/package.rb', line 96

def replaces
  @replaces
end

#scriptsObject

hash of scripts for maintainer/package scripts (postinstall, etc)

The keys are :before_install, etc The values are the text to use in the script.


105
106
107
# File 'lib/fpm/package.rb', line 105

def scripts
  @scripts
end

#urlObject

URL for this package. Could be the homepage. Could be the download url. You pick.


69
70
71
# File 'lib/fpm/package.rb', line 69

def url
  @url
end

#vendorObject

A identifier representing the vendor. Any string is fine. This is usually who produced the software.


65
66
67
# File 'lib/fpm/package.rb', line 65

def vendor
  @vendor
end

#versionObject

Get the version of this package


43
44
45
# File 'lib/fpm/package.rb', line 43

def version
  @version
end

Class Method Details

.apply_options(clampcommand) ⇒ Object

Apply the options for this package on the clamp command

Package flags become attributes 'type-flag'

So if you have:

class Foo < FPM::Package
  option "--bar-baz" ...
end

The attribute value for –foo-bar-baz will be :foo_bar_baz“


453
454
455
456
457
458
459
# File 'lib/fpm/package.rb', line 453

def apply_options(clampcommand)
  @options ||= []
  @options.each do |args|
    flag, param, help, options, block = args
    clampcommand.option(flag, param, help, options, &block)
  end
end

.default_attributes(&block) ⇒ Object

def apply_options


461
462
463
464
465
466
467
468
# File 'lib/fpm/package.rb', line 461

def default_attributes(&block)
  return if @options.nil?
  @options.each do |flag, param, help, options, _block|
    attr = flag.first.gsub(/^-+/, "").gsub(/-/, "_").gsub("[no_]", "")
    attr += "?" if param == :flag
    yield attr.to_sym, options[:default]
  end
end

.inherited(klass) ⇒ Object

This method is invoked when subclass occurs.

Lets us track all known FPM::Package subclasses


414
415
416
417
# File 'lib/fpm/package.rb', line 414

def inherited(klass)
  @subclasses ||= {}
  @subclasses[klass.name.gsub(/.*:/, "").downcase] = klass
end

.option(flag, param, help, options = {}, &block) ⇒ Object

This allows packages to define flags for the fpm command line


425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
# File 'lib/fpm/package.rb', line 425

def option(flag, param, help, options={}, &block)
  @options ||= []
  if !flag.is_a?(Array)
    flag = [flag]
  end

  if param == :flag
    # Automatically make 'flag' (boolean) options tunable with '--[no-]...'
    flag = flag.collect { |f| "--[no-]#{type}-#{f.gsub(/^--/, "")}" }
  else
    flag = flag.collect { |f| "--#{type}-#{f.gsub(/^--/, "")}" }
  end

  help = "(#{type} only) #{help}"
  @options << [flag, param, help, options, block]
end

.typeObject

Get the type of this package class.

For “Foo::Bar::BAZ” this will return “baz”


473
474
475
# File 'lib/fpm/package.rb', line 473

def type
  self.name.split(':').last.downcase
end

.typesObject

Get a list of all known package subclasses


420
421
422
# File 'lib/fpm/package.rb', line 420

def types
  return @subclasses
end

Instance Method Details

#build_path(path = nil) ⇒ Object

def staging_path


266
267
268
269
270
271
272
273
274
# File 'lib/fpm/package.rb', line 266

def build_path(path=nil)
  @build_path ||= Stud::Temporary.directory("package-#{type}-build")

  if path.nil?
    return @build_path
  else
    return File.join(@build_path, path)
  end
end

#cleanupObject

Clean up any temporary storage used by this class.


277
278
279
280
# File 'lib/fpm/package.rb', line 277

def cleanup
  cleanup_staging
  cleanup_build
end

#cleanup_buildObject

def cleanup_staging


289
290
291
292
293
294
# File 'lib/fpm/package.rb', line 289

def cleanup_build
  if File.directory?(build_path)
    logger.debug("Cleaning up build path", :path => build_path)
    FileUtils.rm_r(build_path)
  end
end

#cleanup_stagingObject

def cleanup


282
283
284
285
286
287
# File 'lib/fpm/package.rb', line 282

def cleanup_staging
  if File.directory?(staging_path)
    logger.debug("Cleaning up staging path", :path => staging_path)
    FileUtils.rm_r(staging_path)
  end
end

#convert(klass) ⇒ Object

Convert this package to a new package type


194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/fpm/package.rb', line 194

def convert(klass)
  logger.info("Converting #{self.type} to #{klass.type}")

  exclude

  pkg = klass.new
  pkg.cleanup_staging # purge any directories that may have been created by klass.new

  # copy other bits
  ivars = [
    :@architecture, :@category, :@config_files, :@conflicts,
    :@dependencies, :@description, :@epoch, :@iteration, :@license, :@maintainer,
    :@name, :@provides, :@replaces, :@scripts, :@url, :@vendor, :@version,
    :@directories, :@staging_path, :@attrs
  ]
  ivars.each do |ivar|
    #logger.debug("Copying ivar", :ivar => ivar, :value => instance_variable_get(ivar),
                  #:from => self.type, :to => pkg.type)
    pkg.instance_variable_set(ivar, instance_variable_get(ivar))
  end

  # Attributes are special! We do not want to remove the default values of
  # the destination package type unless their value is specified on the
  # source package object.
  pkg.attributes.merge!(self.attributes)

  pkg.converted_from(self.class)
  return pkg
end

#converted_from(origin) ⇒ Object

This method is invoked on a package when it has been converted to a new package format. The purpose of this method is to do any extra conversion steps, like translating dependency conditions, etc.


227
228
229
230
# File 'lib/fpm/package.rb', line 227

def converted_from(origin)
  # nothing to do by default. Subclasses may implement this.
  # See the RPM package class for an example.
end

#edit_file(path) ⇒ Object

def to_s


366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
# File 'lib/fpm/package.rb', line 366

def edit_file(path)
  editor = ENV['FPM_EDITOR'] || ENV['EDITOR'] || 'vi'
  logger.info("Launching editor", :file => path)
  command = "#{editor} #{Shellwords.escape(path)}"
  system("#{editor} #{Shellwords.escape(path)}")
  if !$?.success?
    raise ProcessFailed.new("'#{editor}' failed (exit code " \
                            "#{$?.exitstatus}) Full command was: " \
                            "#{command}");
  end

  if File.size(path) == 0
    raise "Empty file after editing: #{path.inspect}"
  end
end

#filesObject

List all files in the staging_path

The paths will all be relative to staging_path and will not include that path.

This method will emit 'leaf' paths. Files, symlinks, and other file-like things are emitted. Intermediate directories are ignored, but empty directories are emitted.


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

def files
  is_leaf = lambda do |path|
    # True if this is a file/symlink/etc, but not a plain directory
    return true if !(File.directory?(path) and !File.symlink?(path))
    # Empty directories are leafs as well.
    return true if ::Dir.entries(path).sort == [".", ".."]
    # False otherwise (non-empty directory, etc)
    return false
  end # is_leaf

  # Find all leaf-like paths (files, symlink, empty directories, etc)
  # Also trim the leading path such that '#{staging_path}/' is removed from
  # the path before returning.
  #
  # Wrapping Find.find in an Enumerator is required for sane operation in ruby 1.8.7,
  # but requires the 'backports/latest' gem (which is used in other places in fpm)
  return Enumerator.new { |y| Find.find(staging_path) { |path| y << path } } \
    .select { |path| path != staging_path } \
    .select { |path| is_leaf.call(path) } \
    .collect { |path| path[staging_path.length + 1.. -1] }
end

#input(thing_to_input) ⇒ Object

Add a new source to this package. The exact behavior depends on the kind of package being managed.

For instance:

  • for FPM::Package::Dir, << expects a path to a directory or files.

  • for FPM::Package::RPM, << expects a path to an rpm.

The idea is that you can keep pumping in new things to a package for later conversion or output.

Implementations are expected to put files relevant to the 'input' in the staging_path

Raises:

  • (NotImplementedError)

245
246
247
248
# File 'lib/fpm/package.rb', line 245

def input(thing_to_input)
  raise NotImplementedError.new("#{self.class.name} does not yet support " \
                                "reading #{self.type} packages")
end

#output(path) ⇒ Object

Output this package to the given path.

Raises:

  • (NotImplementedError)

251
252
253
254
# File 'lib/fpm/package.rb', line 251

def output(path)
  raise NotImplementedError.new("#{self.class.name} does not yet support " \
                                "creating #{self.type} packages")
end

#script(script_name) ⇒ Object

Get the contents of the script by a given name.

If template_scripts? is set in attributes (often by the –template-scripts flag), then apply it as an ERB template.


519
520
521
522
523
524
525
526
527
528
# File 'lib/fpm/package.rb', line 519

def script(script_name)
  if attributes[:template_scripts?]
    erb = ERB.new(scripts[script_name], nil, "-")
    # TODO(sissel): find the original file name for the file.
    erb.filename = "script(#{script_name})"
    return erb.result(binding)
  else
    return scripts[script_name]
  end
end

#staging_path(path = nil) ⇒ Object

def output


256
257
258
259
260
261
262
263
264
# File 'lib/fpm/package.rb', line 256

def staging_path(path=nil)
  @staging_path ||= Stud::Temporary.directory("package-#{type}-staging")

  if path.nil?
    return @staging_path
  else
    return File.join(@staging_path, path)
  end
end

#to_s(fmt = nil) ⇒ Object


354
355
356
357
358
359
360
361
362
363
364
# File 'lib/fpm/package.rb', line 354

def to_s(fmt=nil)
  fmt = "NAME.EXTENSION" if fmt.nil?
  return fmt.gsub("ARCH", to_s_arch) \
    .gsub("NAME",         to_s_name) \
    .gsub("FULLVERSION",  to_s_fullversion) \
    .gsub("VERSION",      to_s_version) \
    .gsub("ITERATION",    to_s_iteration) \
    .gsub("EPOCH",        to_s_epoch) \
    .gsub("TYPE",         to_s_type) \
    .gsub("EXTENSION",    to_s_extension)
end

#typeObject

Get the 'type' for this instance.

For FPM::Package::ABC, this returns 'abc'


189
190
191
# File 'lib/fpm/package.rb', line 189

def type
  self.class.type
end