class AutomateIt::ShellManager::Portable

ShellManager::Portable

Pure-Ruby, portable driver for ShellManager provides Unix-like shell commands for manipulating files and executing commands.

It does not provide commands for:

Constants

DIRECTORY_MASK
FILE_MASK

Public Instance Methods

backup(*sources) click to toggle source

See ShellManager#backup

# File lib/automateit/shell_manager/portable.rb, line 32
def backup(*sources)
  sources, opts = args_and_opts(*sources)

  targets = []
  for source in sources
    is_dir = File.directory?(source)

    tempster_opts = {
      :verbose => false,
      :noop => noop?,
      :delete => false,
      :dir => File.dirname(source),
      :prefix => "%s.%s" % [File.basename(source), Time.now.to_i],
      :suffix => ".bak",
      :kind => is_dir ? :directory : :file,
    }

    target = ::Tempster.tempster(tempster_opts)

    log.silence(opts[:quiet] ? Logger::WARN : log.level) do
      if is_dir
        cp_opts = {}
        cp_opts[:recursive] = true if is_dir
        cp_opts[:preserve] = :try

        source_children = _directory_contents(source)
        #puts "sc: %s" % source_children.inspect

        interpreter.cp_r(source_children, target, cp_opts)
      else
        interpreter.cp(source, target)
      end
    end

    targets << target
  end
  return sources.size == 1 ? targets.first : targets
end
cd(dir, opts={}, &block) click to toggle source

See ShellManager#cd

# File lib/automateit/shell_manager/portable.rb, line 124
def cd(dir, opts={}, &block)
  if block
    log.enqueue(:info, PEXEC+(block ? "pushd" : "cd")+" "+dir)
    begin
      if writing? or File.directory?(dir)
        FileUtils.cd(dir, &block)
      else
        block.call(true)
      end
    rescue Exception => e
      raise e
    ensure
      log.dequeue(:info, PEXEC+"popd # => #{pwd}")
    end
  else
    FileUtils.cd(dir) if writing?
  end
  return dir
end
chmod(mode, targets, opts={}) click to toggle source

See ShellManager#chmod

# File lib/automateit/shell_manager/portable.rb, line 453
def chmod(mode, targets, opts={})
  chperm(targets, {:mode => mode}.merge(opts))
end
chmod_R(mode, targets, opts={}) click to toggle source

See ShellManager#chmod_R

# File lib/automateit/shell_manager/portable.rb, line 458
def chmod_R(mode, targets, opts={})
  chmod(mode, targets, {:recursive => true}.merge(opts))
end
chown(user, group, targets, opts={}) click to toggle source

See ShellManager#chown

# File lib/automateit/shell_manager/portable.rb, line 463
def chown(user, group, targets, opts={})
  chperm(targets, {:user => user, :group => group}.merge(opts))
end
chown_R(user, group, targets, opts={}) click to toggle source

See ShellManager#chown_R

# File lib/automateit/shell_manager/portable.rb, line 468
def chown_R(user, group, targets, opts={})
  chown(user, group, targets, {:recursive => true}.merge(opts))
end
chperm(targets, opts={}) click to toggle source

See ShellManager#chperm

# File lib/automateit/shell_manager/portable.rb, line 369
def chperm(targets, opts={})
  _replace_owner_with_user(opts)
  user = \
    if opts[:user]
      opts[:user] = opts[:user].to_s if opts[:user].is_a?(Symbol)
      if opts[:user].is_a?(String)
        begin
          Etc.getpwnam(opts[:user]).uid
        rescue ArgumentError
          :not_present
        end
      else
        opts[:user]
      end
    end

  group = \
    if opts[:group]
      opts[:group] = opts[:group].to_s if opts[:group].is_a?(Symbol)
      if opts[:group].is_a?(String)
        begin
          Etc.getgrnam(opts[:group]).gid
        rescue ArgumentError
          :not_present
        end
      else
        opts[:group]
      end
    end

  modified_entries = []
  modified_ownership = false
  modified_permission = false
  Find.find(*targets) do |path|
    modified = false
    stat = writing? || File.exists?(path) ? File.stat(path) : nil
    if opts[:mode]
      # TODO ShellManager::Portable#chperm -- process chmod symbolic strings, e.g., [ugoa...][[+-=][rwxXstugo...]...][,...]
      mode = opts[:mode] | (stat.directory? ? DIRECTORY_MASK : FILE_MASK) if stat
      unless stat and (mode ^ stat.mode).zero?
        modified = true
        modified_permission = true
        File.chmod(mode, path) if writing?
      end
    end
    if user and (not stat or user != stat.uid)
      modified = true
      modified_ownership = true
      File.chown(user, nil, path) if writing?
    end
    if group and (not stat or group != stat.gid)
      modified = true
      modified_ownership = true
      File.chown(nil, group, path) if writing?
    end
    modified_entries << path if modified
    Find.prune if not opts[:recursive] and File.directory?(path)
  end

  return false if modified_entries.empty?

  display_entries = opts[:details] ? modified_entries : targets
  display_entries = [display_entries].flatten

  if modified_permission
    msg = "chmod"
    msg << " -R" if opts[:recursive]
    msg << " 0%o" % opts[:mode] if opts[:mode]
    msg << " " << display_entries.join(' ')
    log.info(PEXEC+msg)
  end
  if modified_ownership
    msg = "chown"
    msg << " -R" if opts[:recursive]
    msg << " %s" % opts[:user] if opts[:user]
    msg << ":" if opts[:user] and opts[:group]
    msg << "%s" % opts[:group] if opts[:group]
    msg << " " << display_entries.join(' ')
    log.info(PEXEC+msg)
  end
  return targets.is_a?(String) ? modified_entries.first : modified_entries
end
cp(sources, target, opts={}) click to toggle source

See ShellManager#cp

# File lib/automateit/shell_manager/portable.rb, line 210
def cp(sources, target, opts={})
  # TODO ShellManager::Portable#cp -- rather funky, needs a code review
  fu_opts = _fileutils_opts
  for opt in [:noop, :verbose]
    opt = opt.to_sym
    fu_opts[opt] = opts[opt] if opts[opt]
  end

  fu_opts_with_preserve = fu_opts.clone
  fu_opts_with_preserve[:preserve] = \
    if opts[:preserve] == :try
      fsim = File::Stat.instance_methods
      (fsim.include?("uid") and fsim.include?("gid") and
       fsim.include?("mode") and fsim.include?("atime"))
    else
      opts[:preserve]
    end

  changed = []
  sources_a = [sources].flatten
  sources_a.each do |parent|
    Find.find(parent) do |child|
      source_fn = File.directory?(child) ? child+"/" : child
      target_dir = File.directory?(target)
      target_fn = peer_for(source_fn, target)

      log.debug(PNOTE+"comparing %s => %s" % [source_fn, target_fn])
      source_st = File.stat(source_fn)
      is_copy = false
      begin
        begin
          target_st = File.stat(target_fn)

          unless target_dir
            # Is the file obviously different?
            if source_st.file?
              for kind in %w(size mtime)
                next if kind == "mtime" and ! opts[:preserve]
                unless source_st.send(kind) == target_st.send(kind)
                  log.debug(PNOTE+"%s not same %s" % [target_fn, kind])
                  raise EOFError.new
                end
              end

              unless FileUtils.identical?(source_fn, target_fn)
                log.debug(PNOTE+"%s not identical" % target_fn)
                raise EOFError.new
              end
            end

            # File just needs to be altered
            if opts[:preserve]
              unless source_st.mode == target_st.mode
                changed << child
                log.debug(PNOTE+"%s not same mode" % target_fn)
                chmod(source_st.mode, target_fn, fu_opts)
              end
              unless source_st.uid == target_st.uid and source_st.gid == target_st.gid
                changed << child
                log.debug(PNOTE+"%s not same uid/gid" % target_fn)
                chown(source_st.uid, source_st.gid, target_fn, fu_opts)
              end
            end
          end
        rescue EOFError
          changed << child
          is_copy = true
        end
      rescue Errno::ENOENT
        changed << child
        log.debug(PNOTE+"%s not present" % target_fn)
        is_copy = true
      end
      if is_copy
        log.info(PEXEC+"cp%s %s %s" % [opts[:recursive] ? ' -r' : '', source_fn, target_fn])
        ## puts "fo %s" % fu_opts.inspect
        ## puts "fowp %s" % fu_opts_with_preserve.inspect
        FileUtils.cp_r(source_fn, target_fn, fu_opts_with_preserve)
      end
    end
  end

  result = \
    if changed.empty?
      false
    else
      if sources_a.size == 1
        changed.first
      else
        changed.uniq
      end
    end
  return result
end
cp_R(*args) click to toggle source

See ShellManager#cp_R

# File lib/automateit/shell_manager/portable.rb, line 311
def cp_R(*args)
  cp_r(*args)
end
cp_r(sources, target, opts={}) click to toggle source

See ShellManager#cp_r

# File lib/automateit/shell_manager/portable.rb, line 306
def cp_r(sources, target, opts={})
  cp(sources, target, {:recursive => true}.merge(opts))
end
install(source, target, mode=nil) click to toggle source

See ShellManager#install

# File lib/automateit/shell_manager/portable.rb, line 194
def install(source, target, mode=nil)
  cp_rv = nil
  chmod_rv = nil
  log.silence(Logger::WARN) do
    cp_rv = cp(source, target)
    chmod_rv = chmod(mode, peer_for(source, target)) if mode
  end

  return false unless cp_rv or chmod_rv

  log.info(PEXEC+"install%s %s %s" %
           [mode ? ' -m 0%o' % mode : '', source, target])
  return source
end
mkdir(dirs, opts={}, &block) click to toggle source

See ShellManager#mkdir

# File lib/automateit/shell_manager/portable.rb, line 150
def mkdir(dirs, opts={}, &block)
  _replace_owner_with_user(opts)
  kind = opts[:parents] ? :mkdir_p : :mkdir
  missing = [dirs].flatten.select{|dir| ! File.directory?(dir)}
  result = false
  if missing.empty? and not block
    chperm(opts) if opts[:user] or opts[:group] or opts[:mode]
    return result
  end
  unless missing.empty?
    cmd = kind.to_s.gsub(/_/, ' -')
    log.info(PEXEC+"#{cmd} #{missing.join(" ")}")
    result = [FileUtils.send(kind, missing, _fileutils_opts)].flatten
    result = result.first if [dirs].flatten.size == 1
  end
  if block
    if missing.size > 1
      raise ArgumentError.new(
        "can only use a block if you mkdir a single directory")
    end
    dir = [dirs].flatten.first
    cd(dir) do
      block.call(result)
    end
  end
  chperm(opts) if opts[:user] or opts[:group] or opts[:mode]
  return missing
end
mkdir_p(dirs, opts={}, &block) click to toggle source

See ShellManager#mkdir_p

# File lib/automateit/shell_manager/portable.rb, line 180
def mkdir_p(dirs, opts={}, &block)
  mkdir(dirs, {:parents => true}.merge(opts), &block)
end
mktemp(name=nil, &block) click to toggle source

See ShellManager#mktemp

# File lib/automateit/shell_manager/portable.rb, line 88
def mktemp(name=nil, &block)
  _mktemp_helper(:mktemp, name, &block)
end
mktempdir(name=nil, &block) click to toggle source

See ShellManager#mktempdir

# File lib/automateit/shell_manager/portable.rb, line 93
def mktempdir(name=nil, &block)
  _mktemp_helper(:mktempdir, name, &block)
end
mktempdircd(name=nil, &block) click to toggle source

See ShellManager#mktempdircd

# File lib/automateit/shell_manager/portable.rb, line 98
def mktempdircd(name=nil, &block)
  _mktemp_helper(:mktempdircd, name, &block)
end
mv(sources, target) click to toggle source

See ShellManager#mv

# File lib/automateit/shell_manager/portable.rb, line 316
def mv(sources, target)
  present = [sources].flatten.select{|entry| self._present?(entry)}
  return false if present.empty?
  present = present.first if present.size == 1
  FileUtils.mv(present, target, _fileutils_opts) && present
  return present
end
provides_mode?() click to toggle source
# File lib/automateit/shell_manager/portable.rb, line 21
def provides_mode?
  ! broken?
end
provides_ownership?() click to toggle source
# File lib/automateit/shell_manager/portable.rb, line 25
def provides_ownership?
  ! broken?
end
pwd() click to toggle source

See ShellManager#pwd

# File lib/automateit/shell_manager/portable.rb, line 145
def pwd()
  return FileUtils.pwd()
end
rm(targets, opts={}) click to toggle source

See ShellManager#rm

# File lib/automateit/shell_manager/portable.rb, line 325
def rm(targets, opts={})
  kind = \
    if opts[:recursive] and opts[:force]
      :rm_rf
    elsif opts[:recursive]
      :rm_r
    else
      :rm
    end

  present = [targets].flatten.select{|entry| self._present?(entry)}
  return false if present.empty?

  msg = "rm"
  if opts[:recursive] and opts[:force]
    msg << " -rf"
  elsif opts[:recursive]
    msg << " -r"
  elsif opts[:force]
    msg << " -f"
  end
  msg << " " << present.join(' ')
  log.info(PEXEC+msg)

  present = present.first if present.size == 0

  FileUtils.send(kind, present, _fileutils_opts)
  return present
end
rm_r(targets, opts={}) click to toggle source

See ShellManager#rm_r

# File lib/automateit/shell_manager/portable.rb, line 356
def rm_r(targets, opts={})
  rm(targets, {:recursive => true}.merge(opts))
end
rm_rf(targets, opts={}) click to toggle source

See ShellManager#rm_rf

# File lib/automateit/shell_manager/portable.rb, line 361
def rm_rf(targets, opts={})
  rm(targets, {:recursive => true, :force => true}.merge(opts))
end
rmdir(dirs) click to toggle source

See ShellManager#rmdir

# File lib/automateit/shell_manager/portable.rb, line 185
def rmdir(dirs)
  present = [dirs].flatten.select{|dir| File.directory?(dir)}
  return false if present.empty?
  log.info(PEXEC+"rmdir #{String === dirs ? dirs : dirs.join(' ')}")
  FileUtils.rmdir(present, _fileutils_opts)
  return present
end
sh(*commands) click to toggle source

See ShellManager#sh

# File lib/automateit/shell_manager/portable.rb, line 72
def sh(*commands)
  args, opts = args_and_opts(*commands)
  log.info(PEXEC+"#{args.join(' ')}")
  return writing? ? system(*args) : true
end
touch(targets, opts={}) click to toggle source

See ShellManager#touch

# File lib/automateit/shell_manager/portable.rb, line 473
def touch(targets, opts={})
  like = opts.delete(:like)
  stamp = opts.delete(:stamp)
  quiet = opts.delete(:quiet) == true ? true : false
  time = \
    if stamp
      stamp
    elsif like
      begin
        File.stat(like).mtime
      rescue Errno::ENOENT => e
        if preview?
          Time.now
        else
          raise e
        end
      end
    else
      Time.now
    end

  unless quiet
    msg = "touch"
    msg << " --reference %s" % like if like
    msg << " --stamp %s" % stamp if stamp
    msg << " " << [targets].flatten.join(" ")
    log.info(PEXEC+msg)
  end

  results = []
  for target in [targets].flatten
    begin
      stat = File.stat(target)
      next if stat.mtime.to_i == time.to_i
    rescue Errno::ENOENT
      File.open(target, "a"){} unless preview?
    end
    File.utime(time, time, target) unless preview?
    results << target
  end

  return false if results.empty?
  return targets.is_a?(String) ? results.first : results
end
umask(mode=nil, &block) click to toggle source

See ShellManager#umask

# File lib/automateit/shell_manager/portable.rb, line 103
def umask(mode=nil, &block)
  if mode
    old = File::umask
    File::umask(mode)
    if block
      begin
        block.call
      rescue Exception => e
        raise e
      ensure
        File::umask(old)
      end
    end
  else
    File::umask
  end
end

Protected Instance Methods

_present?(path) click to toggle source

Is the file or symlink at this path present?

# File lib/automateit/shell_manager/portable.rb, line 519
def _present?(path)
  File.symlink?(path) ? true : File.exists?(path)
end

Private Instance Methods

_mktemp_helper(kind, name=nil, opts={}, &block) click to toggle source
# File lib/automateit/shell_manager/portable.rb, line 78
def _mktemp_helper(kind, name=nil, opts={}, &block)
  # Tempster takes care of rethrowing exceptions
  opts[:name] = name || "automateit_temp"
  opts[:message_callback] = lambda{|msg| log.info(PEXEC+msg)}
  opts[:noop] = interpreter.preview?
  ::Tempster.send(kind, opts, &block)
end
broken?() click to toggle source
# File lib/automateit/shell_manager/portable.rb, line 16
def broken?
  RUBY_PLATFORM =~ /mswin|java/
end