class FPM::Command

The main fpm command entry point.

Public Class Methods

new(*args) click to toggle source

A new FPM::Command

Calls superclass method
# File lib/fpm/command.rb, line 261
def initialize(*args)
  super(*args)
  @conflicts = []
  @replaces = []
  @provides = []
  @dependencies = []
  @config_files = []
  @directories = []
end

Public Instance Methods

execute() click to toggle source

Execute this command. See Clamp::Command#execute and Clamp's documentation

# File lib/fpm/command.rb, line 272
def execute
  logger.level = :warn
  logger.level = :info if verbose? # --verbose
  logger.level = :debug if debug? # --debug
  if log_level
    logger.level = log_level.to_sym
  end

  if (stray_flags = args.grep(/^-/); stray_flags.any?)
    logger.warn("All flags should be before the first argument " \
                 "(stray flags found: #{stray_flags}")
  end

  # Some older behavior, if you specify:
  #   'fpm -s dir -t ... -C somepath'
  # fpm would assume you meant to add '.' to the end of the commandline.
  # Let's hack that. https://github.com/jordansissel/fpm/issues/187
  if input_type == "dir" and args.empty? and !chdir.nil?
    logger.info("No args, but -s dir and -C are given, assuming '.' as input")
    args << "."
  end

  logger.info("Setting workdir", :workdir => workdir)
  ENV["TMP"] = workdir

  validator = Validator.new(self)
  if !validator.ok?
    validator.messages.each do |message|
      logger.warn(message)
    end

    logger.fatal("Fix the above problems, and you'll be rolling packages in no time!")
    return 1
  end
  input_class = FPM::Package.types[input_type]
  output_class = FPM::Package.types[output_type]

  input = input_class.new

  # Merge in package settings.
  # The 'settings' stuff comes in from #apply_options, which goes through
  # all the options defined in known packages and puts them into our command.
  # Flags in packages defined as "--foo-bar" become named "--<packagetype>-foo-bar"
  # They are stored in 'settings' as :gem_foo_bar.
  input.attributes ||= {}

  # Iterate over all the options and set their values in the package's
  # attribute hash.
  #
  # Things like '--foo-bar' will be available as pkg.attributes[:foo_bar]
  self.class.declared_options.each do |option|
    option.attribute_name.tap do |attr|
      next if attr == "help"
      # clamp makes option attributes available as accessor methods
      # --foo-bar is available as 'foo_bar'. Put these in the package
      # attributes hash. (See FPM::Package#attributes)
      #
      # In the case of 'flag' options, the accessor is actually 'foo_bar?'
      # instead of just 'foo_bar'

      # If the instance variable @{attr} is defined, then
      # it means the flag was given on the command line.
      flag_given = instance_variable_defined?("@#{attr}")
      input.attributes["#{attr}_given?".to_sym] = flag_given
      attr = "#{attr}?" if !respond_to?(attr) # handle boolean :flag cases
      input.attributes[attr.to_sym] = send(attr) if respond_to?(attr)
      logger.debug("Setting attribute", attr.to_sym => send(attr))
    end
  end

  if input_type == "pleaserun"
    # Special case for pleaserun that all parameters are considered the 'command'
    # to run through pleaserun.
    input.input(args)
  else
    # Each remaining command line parameter is used as an 'input' argument.
    # For directories, this means paths. For things like gem and python, this
    # means package name or paths to the packages (rails, foo-1.0.gem, django,
    # bar/setup.py, etc)
    args.each do |arg|
      input.input(arg)
    end
  end

  # If --inputs was specified, read it as a file.
  if !inputs.nil?
    if !File.exists?(inputs)
      logger.fatal("File given for --inputs does not exist (#{inputs})")
      return 1
    end

    # Read each line as a path
    File.new(inputs, "r").each_line do |line|
      # Handle each line as if it were an argument
      input.input(line.strip)
    end
  end

  # If --exclude-file was specified, read it as a file and append to
  # the exclude pattern list.
  if !exclude_file.nil?
    if !File.exists?(exclude_file)
      logger.fatal("File given for --exclude-file does not exist (#{exclude_file})")
      return 1
    end

    # Ensure hash is initialized
    input.attributes[:excludes] ||= []

    # Read each line as a path
    File.new(exclude_file, "r").each_line do |line|
      # Handle each line as if it were an argument
      input.attributes[:excludes] << line.strip
    end
  end

  # Override package settings if they are not the default flag values
  # the below proc essentially does:
  #
  # if someflag != default_someflag
  #   input.someflag = someflag
  # end
  set = proc do |object, attribute|
    # if the package's attribute is currently nil *or* the flag setting for this
    # attribute is non-default, use the value.
    if object.send(attribute).nil? || send(attribute) != send("default_#{attribute}")
      logger.info("Setting from flags: #{attribute}=#{send(attribute)}")
      object.send("#{attribute}=", send(attribute))
    end
  end
  set.call(input, :architecture)
  set.call(input, :category)
  set.call(input, :description)
  set.call(input, :epoch)
  set.call(input, :iteration)
  set.call(input, :license)
  set.call(input, :maintainer)
  set.call(input, :name)
  set.call(input, :url)
  set.call(input, :vendor)
  set.call(input, :version)

  input.conflicts += conflicts
  input.dependencies += dependencies
  input.provides += provides
  input.replaces += replaces
  input.config_files += config_files
  input.directories += directories

  h = {}
  attrs.each do | e |

    s = e.split(':', 2)
    h[s.last] = s.first
  end

  input.attrs = h

  script_errors = []
  setscript = proc do |scriptname|
    # 'self.send(scriptname) == self.before_install == --before-install
    # Gets the path to the script
    path = self.send(scriptname)
    # Skip scripts not set
    next if path.nil?

    if !File.exists?(path)
      logger.error("No such file (for #{scriptname.to_s}): #{path.inspect}")
      script_errors << path
    end

    # Load the script into memory.
    input.scripts[scriptname] = File.read(path)
  end

  setscript.call(:before_install)
  setscript.call(:after_install)
  setscript.call(:before_remove)
  setscript.call(:after_remove)
  setscript.call(:before_upgrade)
  setscript.call(:after_upgrade)

  # Bail if any setscript calls had errors. We don't need to log
  # anything because we've already logged the error(s) above.
  return 1 if script_errors.any?

  # Validate the package
  if input.name.nil? or input.name.empty?
    logger.fatal("No name given for this package (set name with '-n', " \
                  "for example, '-n packagename')")
    return 1
  end

  # Convert to the output type
  output = input.convert(output_class)

  # Provide any template values as methods on the package.
  if template_scripts?
    template_value_list.each do |key, value|
      (class << output; self; end).send(:define_method, key) { value }
    end
  end

  # Write the output somewhere, package can be nil if no --package is specified,
  # and that's OK.

  # If the package output (-p flag) is a directory, write to the default file name
  # but inside that directory.
  if ! package.nil? && File.directory?(package)
    package_file = File.join(package, output.to_s)
  else
    package_file = output.to_s(package)
  end

  begin
    output.output(package_file)
  rescue FPM::Package::FileAlreadyExists => e
    logger.fatal(e.message)
    return 1
  rescue FPM::Package::ParentDirectoryMissing => e
    logger.fatal(e.message)
    return 1
  end

  logger.log("Created package", :path => package_file)
  return 0
rescue FPM::Util::ExecutableNotFound => e
  logger.error("Need executable '#{e}' to convert #{input_type} to #{output_type}")
  return 1
rescue FPM::InvalidPackageConfiguration => e
  logger.error("Invalid package configuration: #{e}")
  return 1
rescue FPM::Util::ProcessFailed => e
  logger.error("Process failed: #{e}")
  return 1
ensure
  if debug_workspace?
    # only emit them if they have files
    [input, output].each do |plugin|
      next if plugin.nil?
      [:staging_path, :build_path].each do |pathtype|
        path = plugin.send(pathtype)
        next unless Dir.open(path).to_a.size > 2
        logger.log("plugin directory", :plugin => plugin.type, :pathtype => pathtype, :path => path)
      end
    end
  else
    input.cleanup unless input.nil?
    output.cleanup unless output.nil?
  end
end
help(*args) click to toggle source
Calls superclass method
# File lib/fpm/command.rb, line 26
def help(*args)
  lines = [
    "Intro:",
    "",
    "  This is fpm version #{FPM::VERSION}",
    "",
    "  If you think something is wrong, it's probably a bug! :)",
    "  Please file these here: https://github.com/jordansissel/fpm/issues",
    "",
    "  You can find support on irc (#fpm on freenode irc) or via email with",
    "  fpm-users@googlegroups.com",
    "",
    "Loaded package types:",
  ]
  FPM::Package.types.each do |name, _|
    lines.push("  - #{name}")
  end
  lines.push("")
  lines.push(super)
  return lines.join("\n")
end
run(run_args) click to toggle source
Calls superclass method
# File lib/fpm/command.rb, line 524
def run(run_args)
  logger.subscribe(STDOUT)

  # Short circuit for a `fpm --version` or `fpm -v` short invocation that
  # is the user asking us for the version of fpm.
  if run_args == [ "-v" ] || run_args == [ "--version" ]
    puts FPM::VERSION
    return 0
  end

  # fpm initialization files, note the order of the following array is
  # important, try .fpm in users home directory first and then the current
  # directory
  rc_files = [ ".fpm" ]
  rc_files << File.join(ENV["HOME"], ".fpm") if ENV["HOME"]

  rc_args = []

  if ENV["FPMOPTS"]
    logger.warn("Loading flags from FPMOPTS environment variable")
    rc_args.push(*Shellwords.shellsplit(ENV["FPMOPTS"]))
  end

  rc_files.each do |rc_file|
    if File.readable? rc_file
      logger.warn("Loading flags from rc file #{rc_file}")
      rc_args.push(*Shellwords.shellsplit(File.read(rc_file)))
    end
  end

  flags = []
  args = []
  while rc_args.size > 0 do
    arg = rc_args.shift
    opt = self.class.find_option(arg)
    if opt and not opt.flag?
      flags.push(arg)
      flags.push(rc_args.shift)
    elsif opt or arg[0] == "-"
      flags.push(arg)
    else
      args.push(arg)
    end
  end

  logger.warn("Additional options: #{flags.join " "}") if flags.size > 0
  logger.warn("Additional arguments: #{args.join " "}") if args.size > 0

  ARGV.unshift(*flags)
  ARGV.push(*args)
  super(run_args)
rescue FPM::Package::InvalidArgument => e
  logger.error("Invalid package argument: #{e}")
  return 1
end