class FPM::Package
This class is the parent of all packages. If you want to implement an FPM
package type, you’ll inherit from this.
Attributes
What architecture is this package for?
Any other attributes specific to this package. This is where you’d put rpm, deb, or other specific attributes.
The category of this package. RedHat calls this ‘Group’ Debian calls this ‘Section’ FreeBSD
would put this in /usr/ports/<category>/…
Array of configuration files
Array of things this package conflicts with. (Not all packages support this)
Array of dependencies.
a summary or description of the package
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.
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.
A identifier representing the license. Any string is fine.
Who maintains this package? This could be the upstream author or the package maintainer. You pick.
The name of this package
Array of things this package provides. (Not all packages support this)
Array of things this package replaces. (Not all packages support this)
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.
URL for this package. Could be the homepage. Could be the download url. You pick.
A identifier representing the vendor. Any string is fine. This is usually who produced the software.
The version of this package (the upstream version)
Public Class Methods
Source
# File lib/fpm/package.rb, line 454 def apply_options(clampcommand) @options ||= [] @options.each do |args| flag, param, help, options, block = args clampcommand.option(flag, param, help, options, &block) end end
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“
Source
# File lib/fpm/package.rb, line 462 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
Source
# File lib/fpm/package.rb, line 415 def inherited(klass) @subclasses ||= {} @subclasses[klass.name.gsub(/.*:/, "").downcase] = klass end
This method is invoked when subclass occurs.
Lets us track all known FPM::Package
subclasses
Source
# 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 user@currenthost @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
Source
# File lib/fpm/package.rb, line 426 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
This allows packages to define flags for the fpm command line
Source
# File lib/fpm/package.rb, line 474 def type self.name.split(':').last.downcase end
Get the type of this package class.
For “Foo::Bar::BAZ” this will return “baz”
Source
# File lib/fpm/package.rb, line 421 def types return @subclasses end
Get a list of all known package subclasses
Public Instance Methods
Source
# 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
Source
# File lib/fpm/package.rb, line 277 def cleanup cleanup_staging cleanup_build end
Clean up any temporary storage used by this class.
Source
# 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
Source
# 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
Source
# 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
Convert this package to a new package type
Source
# 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
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.
Source
# File lib/fpm/package.rb, line 367 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
Source
# 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
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.
Source
# 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
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
Source
# 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
Output this package to the given path.
Source
# File lib/fpm/package.rb, line 545 def provides=(value) if !value.is_a?(Array) @provides = [value] else @provides = value end end
Source
# File lib/fpm/package.rb, line 520 def script(script_name) if attributes[:template_scripts?] erb = erbnew(scripts[script_name]) # 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
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.
Source
# 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
Source
# File lib/fpm/package.rb, line 355 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
Source
# File lib/fpm/package.rb, line 189 def type self.class.type end
Get the ‘type’ for this instance.
For FPM::Package::ABC, this returns ‘abc’
Private Instance Methods
Source
# File lib/fpm/package.rb, line 385 def exclude return if attributes[:excludes].nil? if @attributes.include?(:prefix) installdir = staging_path(@attributes[:prefix]) else installdir = staging_path end Find.find(installdir) do |path| match_path = path.sub("#{installdir.chomp('/')}/", '') attributes[:excludes].each do |wildcard| logger.debug("Checking path against wildcard", :path => match_path, :wildcard => wildcard) if File.fnmatch(wildcard, match_path) logger.info("Removing excluded path", :path => match_path, :matches => wildcard) FileUtils.rm_r(path) Find.prune break end end end end
This method removes excluded files from the staging_path. Subclasses can remove the files during the input phase rather than deleting them here
Source
# File lib/fpm/package.rb, line 531 def output_check(output_path) if !File.directory?(File.dirname(output_path)) raise ParentDirectoryMissing.new(output_path) end if File.file?(output_path) if attributes[:force?] logger.warn("Force flag given. Overwriting package at #{output_path}") File.delete(output_path) else raise FileAlreadyExists.new(output_path) end end end
Source
# File lib/fpm/package.rb, line 496 def script?(name) return scripts.include?(name) end
Does this package have the given script?
Source
# File lib/fpm/package.rb, line 330 def template(path) template_path = File.join(template_dir, path) template_code = File.read(template_path) logger.info("Reading template", :path => template_path) erb = erbnew(template_code) erb.filename = template_path return erb end
Source
# File lib/fpm/package.rb, line 326 def template_dir File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "templates")) end
Source
# File lib/fpm/package.rb, line 345 def to_s_arch; architecture.to_s; end
The following methods are provided to easily override particular substitut- ions performed by to_s
for subclasses
Source
# File lib/fpm/package.rb, line 347 def to_s_fullversion; iteration ? "#{version}-#{iteration}" : "#{version}"; end
Source
# File lib/fpm/package.rb, line 349 def to_s_iteration; iteration.to_s; end
Source
# File lib/fpm/package.rb, line 501 def write_scripts scripts_path = File.join(staging_path, ".scripts") target_scripts = [:before_install, :after_install, :before_remove, :after_remove] if target_scripts.any? {|name| script?(name)} ::Dir.mkdir(scripts_path) target_scripts.each do |name| next unless script?(name) out = File.join(scripts_path, name.to_s) logger.debug('Writing script', :source => name, :target => out) File.write(out, script(name)) File.chmod(0755, out) end end end
write all scripts to .scripts (tar and dir)