] [-p] [...]
#/
#/ Run a command with exec, optionally changing directory first
set -e
error() {
echo $@ >&2
exit 1
}
usage() {
cat $0 | grep '^#/' | cut -c4-
exit
}
read_profile=""
while getopts ":hd:p" OPT; do
case $OPT in
d) cd "$OPTARG" ;;
p) read_profile="1" ;;
h) usage ;;
\?) error "invalid option: -$OPTARG" ;;
:) error "option -$OPTARG requires an argument" ;;
esac
done
shift $((OPTIND-1))
[ -z "$1" ] && usage
if [ "$read_profile" = "1" ]; then
if [ -f .profile ]; then
. ./.profile
fi
fi
exec "$@"
foreman-0.88.1/lib/ 0000755 0000041 0000041 00000000000 14620136004 014031 5 ustar www-data www-data foreman-0.88.1/lib/foreman/ 0000755 0000041 0000041 00000000000 14620136004 015460 5 ustar www-data www-data foreman-0.88.1/lib/foreman/export.rb 0000644 0000041 0000041 00000001773 14620136004 017336 0 ustar www-data www-data require "foreman"
require "foreman/helpers"
require "pathname"
module Foreman::Export
extend Foreman::Helpers
class Exception < ::Exception; end
def self.formatter(format)
begin
require "foreman/export/#{ format.tr('-', '_') }"
classy_format = classify(format)
formatter = constantize("Foreman::Export::#{ classy_format }")
rescue NameError => ex
error "Unknown export format: #{format} (no class Foreman::Export::#{ classy_format })."
rescue LoadError => ex
error "Unknown export format: #{format} (unable to load file 'foreman/export/#{ format.tr('-', '_') }')."
end
end
def self.error(message)
raise Foreman::Export::Exception.new(message)
end
end
require "foreman/export/base"
require "foreman/export/inittab"
require "foreman/export/upstart"
require "foreman/export/daemon"
require "foreman/export/bluepill"
require "foreman/export/runit"
require "foreman/export/supervisord"
require "foreman/export/launchd"
require "foreman/export/systemd"
foreman-0.88.1/lib/foreman/procfile.rb 0000644 0000041 0000041 00000003754 14620136004 017621 0 ustar www-data www-data require "foreman"
# Reads and writes Procfiles
#
# A valid Procfile entry is captured by this regex:
#
# /^([A-Za-z0-9_-]+):\s*(.+)$/
#
# All other lines are ignored.
#
class Foreman::Procfile
EmptyFileError = Class.new(StandardError)
# Initialize a Procfile
#
# @param [String] filename (nil) An optional filename to read from
#
def initialize(filename=nil)
@entries = []
load(filename) if filename
end
# Yield each +Procfile+ entry in order
#
def entries
@entries.each do |(name, command)|
yield name, command
end
end
# Retrieve a +Procfile+ command by name
#
# @param [String] name The name of the Procfile entry to retrieve
#
def [](name)
if entry = @entries.detect { |n,c| name == n }
entry.last
end
end
# Create a +Procfile+ entry
#
# @param [String] name The name of the +Procfile+ entry to create
# @param [String] command The command of the +Procfile+ entry to create
#
def []=(name, command)
delete name
@entries << [name, command]
end
# Remove a +Procfile+ entry
#
# @param [String] name The name of the +Procfile+ entry to remove
#
def delete(name)
@entries.reject! { |n,c| name == n }
end
# Load a Procfile from a file
#
# @param [String] filename The filename of the +Procfile+ to load
#
def load(filename)
parse_data = parse(filename)
raise EmptyFileError if parse_data.empty?
@entries.replace parse_data
end
# Save a Procfile to a file
#
# @param [String] filename Save the +Procfile+ to this file
#
def save(filename)
File.open(filename, 'w') do |file|
file.puts self.to_s
end
end
# Get the +Procfile+ as a +String+
#
def to_s
@entries.map do |name, command|
[ name, command ].join(": ")
end.join("\n")
end
private
def parse(filename)
File.read(filename).gsub("\r\n","\n").split("\n").map do |line|
if line =~ /^([A-Za-z0-9_-]+):\s*(.+)$/
[$1, $2]
end
end.compact
end
end
foreman-0.88.1/lib/foreman/process.rb 0000644 0000041 0000041 00000003656 14620136004 017475 0 ustar www-data www-data require "foreman"
require "shellwords"
class Foreman::Process
attr_reader :command
attr_reader :env
# Create a Process
#
# @param [String] command The command to run
# @param [Hash] options
#
# @option options [String] :cwd (./) Change to this working directory before executing the process
# @option options [Hash] :env ({}) Environment variables to set for this process
#
def initialize(command, options={})
@command = command
@options = options.dup
@options[:env] ||= {}
end
# Get environment-expanded command for a +Process+
#
# @param [Hash] custom_env ({}) Environment variables to merge with defaults
#
# @return [String] The expanded command
#
def expanded_command(custom_env={})
env = @options[:env].merge(custom_env)
expanded_command = command.dup
env.each do |key, val|
expanded_command.gsub!("$#{key}", val)
end
expanded_command
end
# Run a +Process+
#
# @param [Hash] options
#
# @option options :env ({}) Environment variables to set for this execution
# @option options :output ($stdout) The output stream
#
# @returns [Fixnum] pid The +pid+ of the process
#
def run(options={})
env = @options[:env].merge(options[:env] || {})
output = options[:output] || $stdout
runner = "#{Foreman.runner}".shellescape
Dir.chdir(cwd) do
Process.spawn env, expanded_command(env), :out => output, :err => output
end
end
# Exec a +Process+
#
# @param [Hash] options
#
# @option options :env ({}) Environment variables to set for this execution
#
# @return Does not return
def exec(options={})
env = @options[:env].merge(options[:env] || {})
env.each { |k, v| ENV[k] = v }
Dir.chdir(cwd)
Kernel.exec expanded_command(env)
end
# Returns the working directory for this +Process+
#
# @returns [String]
#
def cwd
File.expand_path(@options[:cwd] || ".")
end
end
foreman-0.88.1/lib/foreman/engine.rb 0000644 0000041 0000041 00000027730 14620136004 017263 0 ustar www-data www-data require "foreman"
require "foreman/env"
require "foreman/process"
require "foreman/procfile"
require "tempfile"
require "fileutils"
require "thread"
class Foreman::Engine
# The signals that the engine cares about.
#
HANDLED_SIGNALS = [ :TERM, :INT, :HUP, :USR1, :USR2 ]
attr_reader :env
attr_reader :options
attr_reader :processes
# Create an +Engine+ for running processes
#
# @param [Hash] options
#
# @option options [String] :formation (all=1) The process formation to use
# @option options [Fixnum] :port (5000) The base port to assign to processes
# @option options [String] :root (Dir.pwd) The root directory from which to run processes
#
def initialize(options={})
@options = options.dup
@options[:formation] ||= "all=1"
@options[:timeout] ||= 5
@env = {}
@mutex = Mutex.new
@names = {}
@processes = []
@running = {}
@readers = {}
@shutdown = false
# Self-pipe for deferred signal-handling (ala djb: http://cr.yp.to/docs/selfpipe.html)
reader, writer = create_pipe
reader.close_on_exec = true if reader.respond_to?(:close_on_exec)
writer.close_on_exec = true if writer.respond_to?(:close_on_exec)
@selfpipe = { :reader => reader, :writer => writer }
# Set up a global signal queue
# http://blog.rubybestpractices.com/posts/ewong/016-Implementing-Signal-Handlers.html
Thread.main[:signal_queue] = []
end
# Start the processes registered to this +Engine+
#
def start
register_signal_handlers
startup
spawn_processes
watch_for_output
sleep 0.1
wait_for_shutdown_or_child_termination
shutdown
exit(@exitstatus) if @exitstatus
end
# Set up deferred signal handlers
#
def register_signal_handlers
HANDLED_SIGNALS.each do |sig|
if ::Signal.list.include? sig.to_s
trap(sig) { Thread.main[:signal_queue] << sig ; notice_signal }
end
end
end
# Unregister deferred signal handlers
#
def restore_default_signal_handlers
HANDLED_SIGNALS.each do |sig|
trap(sig, :DEFAULT) if ::Signal.list.include? sig.to_s
end
end
# Wake the main thread up via the selfpipe when there's a signal
#
def notice_signal
@selfpipe[:writer].write_nonblock( '.' )
rescue Errno::EAGAIN
# Ignore writes that would block
rescue Errno::EINTR
# Retry if another signal arrived while writing
retry
end
# Invoke the real handler for signal +sig+. This shouldn't be called directly
# by signal handlers, as it might invoke code which isn't re-entrant.
#
# @param [Symbol] sig the name of the signal to be handled
#
def handle_signal(sig)
case sig
when :TERM
handle_term_signal
when :INT
handle_interrupt
when :HUP
handle_hangup
when *HANDLED_SIGNALS
handle_signal_forward(sig)
else
system "unhandled signal #{sig}"
end
end
# Handle a TERM signal
#
def handle_term_signal
system "SIGTERM received, starting shutdown"
@shutdown = true
end
# Handle an INT signal
#
def handle_interrupt
system "SIGINT received, starting shutdown"
@shutdown = true
end
# Handle a HUP signal
#
def handle_hangup
system "SIGHUP received, starting shutdown"
@shutdown = true
end
def handle_signal_forward(signal)
system "#{signal} received, forwarding it to children"
kill_children signal
end
# Register a process to be run by this +Engine+
#
# @param [String] name A name for this process
# @param [String] command The command to run
# @param [Hash] options
#
# @option options [Hash] :env A custom environment for this process
#
def register(name, command, options={})
options[:env] ||= env
options[:cwd] ||= File.dirname(command.split(" ").first)
process = Foreman::Process.new(command, options)
@names[process] = name
@processes << process
end
# Clear the processes registered to this +Engine+
#
def clear
@names = {}
@processes = []
end
# Register processes by reading a Procfile
#
# @param [String] filename A Procfile from which to read processes to register
#
def load_procfile(filename)
options[:root] ||= File.dirname(filename)
Foreman::Procfile.new(filename).entries do |name, command|
register name, command, :cwd => options[:root]
end
self
end
# Load a .env file into the +env+ for this +Engine+
#
# @param [String] filename A .env file to load into the environment
#
def load_env(filename)
Foreman::Env.new(filename).entries do |name, value|
@env[name] = value
end
end
# Send a signal to all processes started by this +Engine+
#
# @param [String] signal The signal to send to each process
#
def kill_children(signal="SIGTERM")
if Foreman.windows?
@running.each do |pid, (process, index)|
system "sending #{signal} to #{name_for(pid)} at pid #{pid}"
begin
Process.kill(signal, pid)
rescue Errno::ESRCH, Errno::EPERM
end
end
else
begin
pids = @running.keys.compact
Process.kill signal, *pids unless pids.empty?
rescue Errno::ESRCH, Errno::EPERM
end
end
end
# Send a signal to the whole process group.
#
# @param [String] signal The signal to send
#
def killall(signal="SIGTERM")
if Foreman.windows?
kill_children(signal)
else
begin
Process.kill "-#{signal}", Process.pid
rescue Errno::ESRCH, Errno::EPERM
end
end
end
# Get the process formation
#
# @returns [Fixnum] The formation count for the specified process
#
def formation
@formation ||= parse_formation(options[:formation])
end
# List the available process names
#
# @returns [Array] A list of process names
#
def process_names
@processes.map { |p| @names[p] }
end
# Get the +Process+ for a specifid name
#
# @param [String] name The process name
#
# @returns [Foreman::Process] The +Process+ for the specified name
#
def process(name)
@names.invert[name]
end
# Yield each +Process+ in order
#
def each_process
process_names.each do |name|
yield name, process(name)
end
end
# Get the root directory for this +Engine+
#
# @returns [String] The root directory
#
def root
File.expand_path(options[:root] || Dir.pwd)
end
# Get the port for a given process and offset
#
# @param [Foreman::Process] process A +Process+ associated with this engine
# @param [Fixnum] instance The instance of the process
#
# @returns [Fixnum] port The port to use for this instance of this process
#
def port_for(process, instance, base=nil)
if base
base + (@processes.index(process.process) * 100) + (instance - 1)
else
base_port + (@processes.index(process) * 100) + (instance - 1)
end
end
# Get the base port for this foreman instance
#
# @returns [Fixnum] port The base port
#
def base_port
(options[:port] || env["PORT"] || ENV["PORT"] || 5000).to_i
end
# deprecated
def environment
env
end
private
### Engine API ######################################################
def startup
raise TypeError, "must use a subclass of Foreman::Engine"
end
def output(name, data)
raise TypeError, "must use a subclass of Foreman::Engine"
end
def shutdown
raise TypeError, "must use a subclass of Foreman::Engine"
end
## Helpers ##########################################################
def create_pipe
IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
end
def name_for(pid)
process, index = @running[pid]
name_for_index(process, index)
end
def name_for_index(process, index)
[ @names[process], index.to_s ].compact.join(".")
end
def parse_formation(formation)
pairs = formation.to_s.gsub(/\s/, "").split(",")
pairs.inject(Hash.new(0)) do |ax, pair|
process, amount = pair.split("=")
process == "all" ? ax.default = amount.to_i : ax[process] = amount.to_i
ax
end
end
def output_with_mutex(name, message)
@mutex.synchronize do
output name, message
end
end
def system(message)
output_with_mutex "system", message
end
def termination_message_for(status)
if status.exited?
"exited with code #{status.exitstatus}"
elsif status.signaled?
"terminated by SIG#{Signal.list.invert[status.termsig]}"
else
"died a mysterious death"
end
end
def flush_reader(reader)
until reader.eof?
data = reader.gets
output_with_mutex name_for(@readers.key(reader)), data
end
end
## Engine ###########################################################
def spawn_processes
@processes.each do |process|
1.upto(formation[@names[process]]) do |n|
reader, writer = create_pipe
begin
pid = process.run(:output => writer, :env => {
"PORT" => port_for(process, n).to_s,
"PS" => name_for_index(process, n)
})
writer.puts "started with pid #{pid}"
rescue Errno::ENOENT
writer.puts "unknown command: #{process.command}"
end
@running[pid] = [process, n]
@readers[pid] = reader
end
end
end
def read_self_pipe
@selfpipe[:reader].read_nonblock(11)
rescue Errno::EAGAIN, Errno::EINTR, Errno::EBADF, Errno::EWOULDBLOCK
# ignore
end
def handle_signals
while sig = Thread.main[:signal_queue].shift
self.handle_signal(sig)
end
end
def handle_io(readers)
readers.each do |reader|
next if reader == @selfpipe[:reader]
if reader.eof?
@readers.delete_if { |key, value| value == reader }
else
data = reader.gets
output_with_mutex name_for(@readers.invert[reader]), data
end
end
end
def watch_for_output
Thread.new do
begin
loop do
io = IO.select([@selfpipe[:reader]] + @readers.values, nil, nil, 30)
read_self_pipe
handle_signals
handle_io(io ? io.first : [])
end
rescue Exception => ex
puts ex.message
puts ex.backtrace
end
end
end
def wait_for_shutdown_or_child_termination
loop do
# Stop if it is time to shut down (asked via a signal)
break if @shutdown
# Stop if any of the children died
break if check_for_termination
# Sleep for a moment and do not blow up if any signals are coming our way
begin
sleep(1)
rescue Exception
# noop
end
end
# Ok, we have exited from the main loop, time to shut down gracefully
terminate_gracefully
end
def check_for_termination
# Check if any of the children have died off
pid, status = begin
Process.wait2(-1, Process::WNOHANG)
rescue Errno::ECHILD
return nil
end
# record the exit status
@exitstatus ||= status.exitstatus if status
# If no childred have died, nothing to do here
return nil unless pid
# Log the information about the process that exited
output_with_mutex name_for(pid), termination_message_for(status)
# Delete it from the list of running processes and return its pid
@running.delete(pid)
return pid
end
def terminate_gracefully
restore_default_signal_handlers
# Tell all children to stop gracefully
if Foreman.windows?
system "sending SIGKILL to all processes"
kill_children "SIGKILL"
else
system "sending SIGTERM to all processes"
kill_children "SIGTERM"
end
# Wait for all children to stop or until the time comes to kill them all
start_time = Time.now
while Time.now - start_time <= options[:timeout]
return if @running.empty?
check_for_termination
# Sleep for a moment and do not blow up if more signals are coming our way
begin
sleep(0.1)
rescue Exception
# noop
end
end
# Ok, we have no other option than to kill all of our children
system "sending SIGKILL to all processes"
kill_children "SIGKILL"
end
end
foreman-0.88.1/lib/foreman/helpers.rb 0000644 0000041 0000041 00000002440 14620136004 017447 0 ustar www-data www-data module Foreman::Helpers
# Copied whole sale from, https://github.com/defunkt/resque/
# Given a word with dashes, returns a camel cased version of it.
#
# classify('job-name') # => 'JobName'
def classify(dashed_word)
dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
end # Tries to find a constant with the name specified in the argument string:
#
# constantize("Module") # => Module
# constantize("Test::Unit") # => Test::Unit
#
# The name is assumed to be the one of a top-level constant, no matter
# whether it starts with "::" or not. No lexical context is taken into
# account:
#
# C = 'outside'
# module M
# C = 'inside'
# C # => 'inside'
# constantize("C") # => 'outside', same as ::C
# end
#
# NameError is raised when the constant is unknown.
def constantize(camel_cased_word)
camel_cased_word = camel_cased_word.to_s
names = camel_cased_word.split('::')
names.shift if names.empty? || names.first.empty?
constant = Object
names.each do |name|
args = Module.method(:const_get).arity != 1 ? [false] : []
if constant.const_defined?(name, *args)
constant = constant.const_get(name)
else
constant = constant.const_missing(name)
end
end
constant
end
end
foreman-0.88.1/lib/foreman/distribution.rb 0000644 0000041 0000041 00000000304 14620136004 020521 0 ustar www-data www-data module Foreman
module Distribution
def self.files
Dir[File.expand_path("../../../{bin,data,lib}/**/*", __FILE__)].select do |file|
File.file?(file)
end
end
end
end
foreman-0.88.1/lib/foreman/cli.rb 0000644 0000041 0000041 00000013117 14620136004 016557 0 ustar www-data www-data require "foreman"
require "foreman/helpers"
require "foreman/engine"
require "foreman/engine/cli"
require "foreman/export"
require "foreman/version"
require "shellwords"
require "yaml"
require "foreman/vendor/thor/lib/thor"
class Foreman::CLI < Foreman::Thor
include Foreman::Helpers
map ["-v", "--version"] => :version
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
class_option :root, :type => :string, :aliases => "-d", :desc => "Default: Procfile directory"
desc "start [PROCESS]", "Start the application (or a specific PROCESS)"
method_option :color, :type => :boolean, :aliases => "-c", :desc => "Force color to be enabled"
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
method_option :formation, :type => :string, :aliases => "-m", :banner => '"alpha=5,bar=3"', :desc => 'Specify what processes will run and how many. Default: "all=1"'
method_option :port, :type => :numeric, :aliases => "-p"
method_option :timeout, :type => :numeric, :aliases => "-t", :desc => "Specify the amount of time (in seconds) processes have to shutdown gracefully before receiving a SIGKILL, defaults to 5."
method_option :timestamp, :type => :boolean, :default => true, :desc => "Include timestamp in output"
class << self
# Hackery. Take the run method away from Thor so that we can redefine it.
def is_thor_reserved_word?(word, type)
return false if word == "run"
super
end
end
def start(process=nil)
check_procfile!
load_environment!
engine.load_procfile(procfile)
engine.options[:formation] = "#{process}=1" if process
engine.start
rescue Foreman::Procfile::EmptyFileError
error "no processes defined"
end
desc "export FORMAT LOCATION", "Export the application to another process management format"
method_option :app, :type => :string, :aliases => "-a"
method_option :log, :type => :string, :aliases => "-l"
method_option :run, :type => :string, :aliases => "-r", :desc => "Specify the pid file directory, defaults to /var/run/"
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
method_option :port, :type => :numeric, :aliases => "-p"
method_option :user, :type => :string, :aliases => "-u"
method_option :template, :type => :string, :aliases => "-t"
method_option :formation, :type => :string, :aliases => "-m", :banner => '"alpha=5,bar=3"', :desc => 'Specify what processes will run and how many. Default: "all=1"'
method_option :timeout, :type => :numeric, :aliases => "-t", :desc => "Specify the amount of time (in seconds) processes have to shutdown gracefully before receiving a SIGKILL, defaults to 5."
def export(format, location=nil)
check_procfile!
load_environment!
engine.load_procfile(procfile)
formatter = Foreman::Export.formatter(format)
formatter.new(location, engine, options).export
rescue Foreman::Export::Exception, Foreman::Procfile::EmptyFileError => ex
error ex.message
end
desc "check", "Validate your application's Procfile"
def check
check_procfile!
engine.load_procfile(procfile)
puts "valid procfile detected (#{engine.process_names.join(', ')})"
rescue Foreman::Procfile::EmptyFileError
error "no processes defined"
end
desc "run COMMAND [ARGS...]", "Run a command using your application's environment"
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
stop_on_unknown_option! :run
def run(*args)
load_environment!
if File.file?(procfile)
engine.load_procfile(procfile)
end
pid = fork do
begin
engine.env.each { |k,v| ENV[k] = v }
if args.size == 1 && process = engine.process(args.first)
process.exec(:env => engine.env)
else
exec args.shelljoin
end
rescue Errno::EACCES
error "not executable: #{args.first}"
rescue Errno::ENOENT
error "command not found: #{args.first}"
end
end
trap("INT") do
Process.kill(:INT, pid)
end
Process.wait(pid)
exit $?.exitstatus || 0
rescue Interrupt
rescue Foreman::Procfile::EmptyFileError
error "no processes defined"
end
desc "version", "Display Foreman gem version"
def version
puts Foreman::VERSION
end
no_tasks do
def engine
@engine ||= begin
engine_class = Foreman::Engine::CLI
engine = engine_class.new(options)
engine
end
end
end
private ######################################################################
def error(message)
puts "ERROR: #{message}"
exit 1
end
def check_procfile!
error("#{procfile} does not exist.") unless File.file?(procfile)
end
def load_environment!
if options[:env]
options[:env].split(",").each do |file|
engine.load_env file
end
else
default_env = File.join(engine.root, ".env")
engine.load_env default_env if File.file?(default_env)
end
end
def procfile
case
when options[:procfile] then options[:procfile]
when options[:root] then File.expand_path(File.join(options[:root], "Procfile"))
else "Procfile"
end
end
def options
original_options = super
return original_options unless File.file?(".foreman")
defaults = ::YAML::load_file(".foreman") || {}
Foreman::Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
end
end
foreman-0.88.1/lib/foreman/export/ 0000755 0000041 0000041 00000000000 14620136004 017001 5 ustar www-data www-data foreman-0.88.1/lib/foreman/export/launchd.rb 0000644 0000041 0000041 00000001022 14620136004 020737 0 ustar www-data www-data require "erb"
require "foreman/export"
class Foreman::Export::Launchd < Foreman::Export::Base
def export
super
engine.each_process do |name, process|
1.upto(engine.formation[name]) do |num|
port = engine.port_for(process, num)
command_args = process.command.split(/\s+/).map{|arg|
case arg
when "$PORT" then port
else arg
end
}
write_template "launchd/launchd.plist.erb", "#{app}-#{name}-#{num}.plist", binding
end
end
end
end
foreman-0.88.1/lib/foreman/export/systemd.rb 0000644 0000041 0000041 00000001602 14620136004 021015 0 ustar www-data www-data require "erb"
require "foreman/export"
class Foreman::Export::Systemd < Foreman::Export::Base
def export
super
Dir["#{location}/#{app}*.target"]
.concat(Dir["#{location}/#{app}*.service"])
.concat(Dir["#{location}/#{app}*.target.wants/#{app}*.service"])
.each do |file|
clean file
end
Dir["#{location}/#{app}*.target.wants"].each do |file|
clean_dir file
end
service_names = []
engine.each_process do |name, process|
1.upto(engine.formation[name]) do |num|
port = engine.port_for(process, num)
process_name = "#{name}.#{num}"
service_filename = "#{app}-#{process_name}.service"
write_template "systemd/process.service.erb", service_filename, binding
service_names << service_filename
end
end
write_template "systemd/master.target.erb", "#{app}.target", binding
end
end
foreman-0.88.1/lib/foreman/export/runit.rb 0000644 0000041 0000041 00000001656 14620136004 020477 0 ustar www-data www-data require "erb"
require "foreman/export"
class Foreman::Export::Runit < Foreman::Export::Base
ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
def export
super
engine.each_process do |name, process|
1.upto(engine.formation[name]) do |num|
process_directory = "#{app}-#{name}-#{num}"
create_directory process_directory
create_directory "#{process_directory}/env"
create_directory "#{process_directory}/log"
write_template "runit/run.erb", "#{process_directory}/run", binding
chmod 0755, "#{process_directory}/run"
port = engine.port_for(process, num)
engine.env.merge("PORT" => port.to_s).each do |key, value|
write_file "#{process_directory}/env/#{key}", value
end
write_template "runit/log/run.erb", "#{process_directory}/log/run", binding
chmod 0755, "#{process_directory}/log/run"
end
end
end
end
foreman-0.88.1/lib/foreman/export/supervisord.rb 0000644 0000041 0000041 00000000431 14620136004 021711 0 ustar www-data www-data require "erb"
require "foreman/export"
class Foreman::Export::Supervisord < Foreman::Export::Base
def export
super
Dir["#{location}/#{app}.conf"].each do |file|
clean file
end
write_template "supervisord/app.conf.erb", "#{app}.conf", binding
end
end
foreman-0.88.1/lib/foreman/export/base.rb 0000644 0000041 0000041 00000010456 14620136004 020246 0 ustar www-data www-data require "foreman/export"
require "ostruct"
require "pathname"
require "shellwords"
class Foreman::Export::Base
attr_reader :location
attr_reader :engine
attr_reader :options
attr_reader :formation
# deprecated
attr_reader :port
def initialize(location, engine, options={})
@location = location
@engine = engine
@options = options.dup
@formation = engine.formation
# deprecated
def port
Foreman::Export::Base.warn_deprecation!
engine.base_port
end
# deprecated
def template
Foreman::Export::Base.warn_deprecation!
options[:template]
end
# deprecated
def @engine.procfile
Foreman::Export::Base.warn_deprecation!
@processes.map do |process|
OpenStruct.new(
:name => @names[process],
:process => process
)
end
end
end
def export
error("Must specify a location") unless location
FileUtils.mkdir_p(location) rescue error("Could not create: #{location}")
chown user, log
chown user, run
end
def app
options[:app] || "app"
end
def log
options[:log] || "/var/log/#{app}"
end
def run
options[:run] || "/var/run/#{app}"
end
def user
options[:user] || app
end
private ######################################################################
def self.warn_deprecation!
@@deprecation_warned ||= false
return if @@deprecation_warned
puts "WARNING: Using deprecated exporter interface. Please update your exporter"
puts "the interface shown in the upstart exporter:"
puts
puts "https://github.com/ddollar/foreman/blob/main/lib/foreman/export/upstart.rb"
puts "https://github.com/ddollar/foreman/blob/main/data/export/upstart/process.conf.erb"
puts
@@deprecation_warned = true
end
def chown user, dir
FileUtils.chown user, nil, dir
rescue
error("Could not chown #{dir} to #{user}") unless File.writable?(dir) || ! File.exist?(dir)
end
def error(message)
raise Foreman::Export::Exception.new(message)
end
def say(message)
puts "[foreman export] %s" % message
end
def clean(filename)
return unless File.exist?(filename)
say "cleaning up: #{filename}"
FileUtils.rm(filename)
end
def clean_dir(dirname)
return unless File.exist?(dirname)
say "cleaning up directory: #{dirname}"
FileUtils.rm_r(dirname)
end
def shell_quote(value)
Shellwords.escape(value)
end
# deprecated
def old_export_template(exporter, file, template_root)
if template_root && File.exist?(file_path = File.join(template_root, file))
File.read(file_path)
elsif File.exist?(file_path = File.expand_path(File.join("~/.foreman/templates", file)))
File.read(file_path)
else
File.read(File.expand_path("../../../../data/export/#{exporter}/#{file}", __FILE__))
end
end
def export_template(name, file=nil, template_root=nil)
if file && template_root
old_export_template name, file, template_root
else
name_without_first = name.split("/")[1..-1].join("/")
matchers = []
matchers << File.join(options[:template], name_without_first) if options[:template]
matchers << File.expand_path("~/.foreman/templates/#{name}")
matchers << File.expand_path("../../../../data/export/#{name}", __FILE__)
File.read(matchers.detect { |m| File.exist?(m) })
end
end
def write_template(name, target, binding)
compiled = if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
ERB.new(export_template(name), trim_mode: '-').result(binding)
else
ERB.new(export_template(name), nil, '-').result(binding)
end
write_file target, compiled
end
def chmod(mode, file)
say "setting #{file} to mode #{mode}"
FileUtils.chmod mode, File.join(location, file)
end
def create_directory(dir)
say "creating: #{dir}"
FileUtils.mkdir_p(File.join(location, dir))
end
def create_symlink(link, target)
say "symlinking: #{link} -> #{target}"
FileUtils.symlink(target, File.join(location, link))
end
def write_file(filename, contents)
say "writing: #{filename}"
filename = File.join(location, filename) unless Pathname.new(filename).absolute?
File.open(filename, "w") do |file|
file.puts contents
end
end
end
foreman-0.88.1/lib/foreman/export/upstart.rb 0000644 0000041 0000041 00000002025 14620136004 021027 0 ustar www-data www-data require "erb"
require "foreman/export"
class Foreman::Export::Upstart < Foreman::Export::Base
def export
super
master_file = "#{app}.conf"
clean File.join(location, master_file)
write_template master_template, master_file, binding
engine.each_process do |name, process|
process_master_file = "#{app}-#{name}.conf"
process_file = "#{app}-#{name}-%s.conf"
Dir[
File.join(location, process_master_file),
File.join(location, process_file % "*")
].each { |f| clean(f) }
next if engine.formation[name] < 1
write_template process_master_template, process_master_file, binding
1.upto(engine.formation[name]) do |num|
port = engine.port_for(process, num)
write_template process_template, process_file % num, binding
end
end
end
private
def master_template
"upstart/master.conf.erb"
end
def process_master_template
"upstart/process_master.conf.erb"
end
def process_template
"upstart/process.conf.erb"
end
end
foreman-0.88.1/lib/foreman/export/inittab.rb 0000644 0000041 0000041 00000002136 14620136004 020762 0 ustar www-data www-data require "foreman/export"
class Foreman::Export::Inittab < Foreman::Export::Base
def export
error("Must specify a location") unless location
inittab = []
inittab << "# ----- foreman #{app} processes -----"
index = 1
engine.each_process do |name, process|
1.upto(engine.formation[name]) do |num|
id = app.slice(0, 2).upcase + sprintf("%02d", index)
port = engine.port_for(process, num)
commands = []
commands << "cd #{engine.root}"
commands << "export PORT=#{port}"
engine.env.each_pair do |var, env|
commands << "export #{var.upcase}=#{shell_quote(env)}"
end
commands << "#{process.command} >> #{log}/#{name}-#{num}.log 2>&1"
inittab << "#{id}:4:respawn:/bin/su - #{user} -c '#{commands.join(";")}'"
index += 1
end
end
inittab << "# ----- end foreman #{app} processes -----"
inittab = inittab.join("\n") + "\n"
if location == "-"
puts inittab
else
say "writing: #{location}"
File.open(location, "w") { |file| file.puts inittab }
end
end
end
foreman-0.88.1/lib/foreman/export/bluepill.rb 0000644 0000041 0000041 00000000355 14620136004 021141 0 ustar www-data www-data require "erb"
require "foreman/export"
class Foreman::Export::Bluepill < Foreman::Export::Base
def export
super
clean "#{location}/#{app}.pill"
write_template "bluepill/master.pill.erb", "#{app}.pill", binding
end
end
foreman-0.88.1/lib/foreman/export/daemon.rb 0000644 0000041 0000041 00000001506 14620136004 020573 0 ustar www-data www-data require "erb"
require "foreman/export"
class Foreman::Export::Daemon < Foreman::Export::Base
def export
super
(Dir["#{location}/#{app}-*.conf"] << "#{location}/#{app}.conf").each do |file|
clean file
end
write_template "daemon/master.conf.erb", "#{app}.conf", binding
engine.each_process do |name, process|
next if engine.formation[name] < 1
write_template "daemon/process_master.conf.erb", "#{app}-#{name}.conf", binding
1.upto(engine.formation[name]) do |num|
port = engine.port_for(process, num)
arguments = process.command.split(" ")
executable = arguments.slice!(0)
arguments = arguments.size > 0 ? " -- #{arguments.join(' ')}" : ""
write_template "daemon/process.conf.erb", "#{app}-#{name}-#{num}.conf", binding
end
end
end
end
foreman-0.88.1/lib/foreman/engine/ 0000755 0000041 0000041 00000000000 14620136004 016725 5 ustar www-data www-data foreman-0.88.1/lib/foreman/engine/cli.rb 0000644 0000041 0000041 00000004706 14620136004 020030 0 ustar www-data www-data require "foreman/engine"
class Foreman::Engine::CLI < Foreman::Engine
module Color
ANSI = {
:reset => 0,
:black => 30,
:red => 31,
:green => 32,
:yellow => 33,
:blue => 34,
:magenta => 35,
:cyan => 36,
:white => 37,
:bright_black => 30,
:bright_red => 31,
:bright_green => 32,
:bright_yellow => 33,
:bright_blue => 34,
:bright_magenta => 35,
:bright_cyan => 36,
:bright_white => 37,
}
def self.enable(io, force=false)
io.extend(self)
@@color_force = force
end
def color?
return true if @@color_force
return false if Foreman.windows?
return false unless self.respond_to?(:isatty)
self.isatty && ENV["TERM"]
end
def color(name)
return "" unless color?
return "" unless ansi = ANSI[name.to_sym]
"\e[#{ansi}m"
end
end
FOREMAN_COLORS = %w( cyan yellow green magenta red blue bright_cyan bright_yellow
bright_green bright_magenta bright_red bright_blue )
def startup
@colors = map_colors
proctitle "foreman: main" unless Foreman.windows?
Color.enable($stdout, options[:color])
end
def output(name, data)
data.to_s.lines.map(&:chomp).each do |message|
output = ""
output += $stdout.color(@colors[name.split(".").first].to_sym)
output += "#{Time.now.strftime("%H:%M:%S")} " if options[:timestamp]
output += "#{pad_process_name(name)} | "
output += $stdout.color(:reset)
output += message
$stdout.puts output
$stdout.flush
end
rescue Errno::EPIPE
terminate_gracefully
end
def shutdown
end
private
def name_padding
@name_padding ||= begin
index_padding = @names.values.map { |n| formation[n] }.max.to_s.length + 1
name_padding = @names.values.map { |n| n.length + index_padding }.sort.last
[ 6, name_padding.to_i ].max
end
end
def pad_process_name(name)
name.ljust(name_padding, " ")
end
def map_colors
colors = Hash.new("white")
@names.values.each_with_index do |name, index|
colors[name] = FOREMAN_COLORS[index % FOREMAN_COLORS.length]
end
colors["system"] = "bright_white"
colors
end
def proctitle(title)
$0 = title
end
def termtitle(title)
printf("\033]0;#{title}\007") unless Foreman.windows?
end
end
foreman-0.88.1/lib/foreman/version.rb 0000644 0000041 0000041 00000000052 14620136004 017467 0 ustar www-data www-data module Foreman
VERSION = "0.88.1"
end
foreman-0.88.1/lib/foreman/env.rb 0000644 0000041 0000041 00000001240 14620136004 016572 0 ustar www-data www-data require "foreman"
class Foreman::Env
attr_reader :entries
def initialize(filename)
@entries = File.read(filename).gsub("\r\n","\n").split("\n").inject({}) do |ax, line|
if line =~ /\A([A-Za-z_0-9]+)=(.*)\z/
key = $1
case val = $2
# Remove single quotes
when /\A'(.*)'\z/ then ax[key] = $1
# Remove double quotes and unescape string preserving newline characters
when /\A"(.*)"\z/ then ax[key] = $1.gsub('\n', "\n").gsub(/\\(.)/, '\1')
else ax[key] = val
end
end
ax
end
end
def entries
@entries.each do |key, value|
yield key, value
end
end
end
foreman-0.88.1/lib/foreman/vendor/ 0000755 0000041 0000041 00000000000 14620136004 016755 5 ustar www-data www-data foreman-0.88.1/lib/foreman/vendor/thor/ 0000755 0000041 0000041 00000000000 14620136004 017731 5 ustar www-data www-data foreman-0.88.1/lib/foreman/vendor/thor/lib/ 0000755 0000041 0000041 00000000000 14620136004 020477 5 ustar www-data www-data foreman-0.88.1/lib/foreman/vendor/thor/lib/thor.rb 0000644 0000041 0000041 00000036416 14620136004 022012 0 ustar www-data www-data require "set"
require "foreman/vendor/thor/lib/thor/base"
class Foreman::Thor
class << self
# Allows for custom "Command" package naming.
#
# === Parameters
# name
# options
#
def package_name(name, _ = {})
@package_name = name.nil? || name == "" ? nil : name
end
# Sets the default command when thor is executed without an explicit command to be called.
#
# ==== Parameters
# meth:: name of the default command
#
def default_command(meth = nil)
if meth
@default_command = meth == :none ? "help" : meth.to_s
else
@default_command ||= from_superclass(:default_command, "help")
end
end
alias_method :default_task, :default_command
# Registers another Foreman::Thor subclass as a command.
#
# ==== Parameters
# klass:: Foreman::Thor subclass to register
# command:: Subcommand name to use
# usage:: Short usage for the subcommand
# description:: Description for the subcommand
def register(klass, subcommand_name, usage, description, options = {})
if klass <= Foreman::Thor::Group
desc usage, description, options
define_method(subcommand_name) { |*args| invoke(klass, args) }
else
desc usage, description, options
subcommand subcommand_name, klass
end
end
# Defines the usage and the description of the next command.
#
# ==== Parameters
# usage
# description
# options
#
def desc(usage, description, options = {})
if options[:for]
command = find_and_refresh_command(options[:for])
command.usage = usage if usage
command.description = description if description
else
@usage = usage
@desc = description
@hide = options[:hide] || false
end
end
# Defines the long description of the next command.
#
# ==== Parameters
# long description
#
def long_desc(long_description, options = {})
if options[:for]
command = find_and_refresh_command(options[:for])
command.long_description = long_description if long_description
else
@long_desc = long_description
end
end
# Maps an input to a command. If you define:
#
# map "-T" => "list"
#
# Running:
#
# thor -T
#
# Will invoke the list command.
#
# ==== Parameters
# Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given command.
#
def map(mappings = nil)
@map ||= from_superclass(:map, {})
if mappings
mappings.each do |key, value|
if key.respond_to?(:each)
key.each { |subkey| @map[subkey] = value }
else
@map[key] = value
end
end
end
@map
end
# Declares the options for the next command to be declared.
#
# ==== Parameters
# Hash[Symbol => Object]:: The hash key is the name of the option and the value
# is the type of the option. Can be :string, :array, :hash, :boolean, :numeric
# or :required (string). If you give a value, the type of the value is used.
#
def method_options(options = nil)
@method_options ||= {}
build_options(options, @method_options) if options
@method_options
end
alias_method :options, :method_options
# Adds an option to the set of method options. If :for is given as option,
# it allows you to change the options from a previous defined command.
#
# def previous_command
# # magic
# end
#
# method_option :foo => :bar, :for => :previous_command
#
# def next_command
# # magic
# end
#
# ==== Parameters
# name:: The name of the argument.
# options:: Described below.
#
# ==== Options
# :desc - Description for the argument.
# :required - If the argument is required or not.
# :default - Default value for this argument. It cannot be required and have default values.
# :aliases - Aliases for this option.
# :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
# :banner - String to show on usage notes.
# :hide - If you want to hide this option from the help.
#
def method_option(name, options = {})
scope = if options[:for]
find_and_refresh_command(options[:for]).options
else
method_options
end
build_option(name, options, scope)
end
alias_method :option, :method_option
def disable_class_options
@disable_class_options = true
end
# Prints help information for the given command.
#
# ==== Parameters
# shell
# command_name
#
def command_help(shell, command_name)
meth = normalize_command_name(command_name)
command = all_commands[meth]
handle_no_command_error(meth) unless command
shell.say "Usage:"
shell.say " #{banner(command)}"
shell.say
class_options_help(shell, nil => command.options.values)
if command.long_description
shell.say "Description:"
shell.print_wrapped(command.long_description, :indent => 2)
else
shell.say command.description
end
end
alias_method :task_help, :command_help
# Prints help information for this class.
#
# ==== Parameters
# shell
#
def help(shell, subcommand = false)
list = printable_commands(true, subcommand)
Foreman::Thor::Util.thor_classes_in(self).each do |klass|
list += klass.printable_commands(false)
end
list.sort! { |a, b| a[0] <=> b[0] }
if defined?(@package_name) && @package_name
shell.say "#{@package_name} commands:"
else
shell.say "Commands:"
end
shell.print_table(list, :indent => 2, :truncate => true)
shell.say
class_options_help(shell)
end
# Returns commands ready to be printed.
def printable_commands(all = true, subcommand = false)
(all ? all_commands : commands).map do |_, command|
next if command.hidden?
item = []
item << banner(command, false, subcommand)
item << (command.description ? "# #{command.description.gsub(/\s+/m, ' ')}" : "")
item
end.compact
end
alias_method :printable_tasks, :printable_commands
def subcommands
@subcommands ||= from_superclass(:subcommands, [])
end
alias_method :subtasks, :subcommands
def subcommand_classes
@subcommand_classes ||= {}
end
def subcommand(subcommand, subcommand_class)
subcommands << subcommand.to_s
subcommand_class.subcommand_help subcommand
subcommand_classes[subcommand.to_s] = subcommand_class
define_method(subcommand) do |*args|
args, opts = Foreman::Thor::Arguments.split(args)
invoke_args = [args, opts, {:invoked_via_subcommand => true, :class_options => options}]
invoke_args.unshift "help" if opts.delete("--help") || opts.delete("-h")
invoke subcommand_class, *invoke_args
end
end
alias_method :subtask, :subcommand
# Extend check unknown options to accept a hash of conditions.
#
# === Parameters
# options: A hash containing :only and/or :except keys
def check_unknown_options!(options = {})
@check_unknown_options ||= {}
options.each do |key, value|
if value
@check_unknown_options[key] = Array(value)
else
@check_unknown_options.delete(key)
end
end
@check_unknown_options
end
# Overwrite check_unknown_options? to take subcommands and options into account.
def check_unknown_options?(config) #:nodoc:
options = check_unknown_options
return false unless options
command = config[:current_command]
return true unless command
name = command.name
if subcommands.include?(name)
false
elsif options[:except]
!options[:except].include?(name.to_sym)
elsif options[:only]
options[:only].include?(name.to_sym)
else
true
end
end
# Stop parsing of options as soon as an unknown option or a regular
# argument is encountered. All remaining arguments are passed to the command.
# This is useful if you have a command that can receive arbitrary additional
# options, and where those additional options should not be handled by
# Foreman::Thor.
#
# ==== Example
#
# To better understand how this is useful, let's consider a command that calls
# an external command. A user may want to pass arbitrary options and
# arguments to that command. The command itself also accepts some options,
# which should be handled by Foreman::Thor.
#
# class_option "verbose", :type => :boolean
# stop_on_unknown_option! :exec
# check_unknown_options! :except => :exec
#
# desc "exec", "Run a shell command"
# def exec(*args)
# puts "diagnostic output" if options[:verbose]
# Kernel.exec(*args)
# end
#
# Here +exec+ can be called with +--verbose+ to get diagnostic output,
# e.g.:
#
# $ thor exec --verbose echo foo
# diagnostic output
# foo
#
# But if +--verbose+ is given after +echo+, it is passed to +echo+ instead:
#
# $ thor exec echo --verbose foo
# --verbose foo
#
# ==== Parameters
# Symbol ...:: A list of commands that should be affected.
def stop_on_unknown_option!(*command_names)
stop_on_unknown_option.merge(command_names)
end
def stop_on_unknown_option?(command) #:nodoc:
command && stop_on_unknown_option.include?(command.name.to_sym)
end
protected
def stop_on_unknown_option #:nodoc:
@stop_on_unknown_option ||= Set.new
end
# The method responsible for dispatching given the args.
def dispatch(meth, given_args, given_opts, config) #:nodoc: # rubocop:disable MethodLength
meth ||= retrieve_command_name(given_args)
command = all_commands[normalize_command_name(meth)]
if !command && config[:invoked_via_subcommand]
# We're a subcommand and our first argument didn't match any of our
# commands. So we put it back and call our default command.
given_args.unshift(meth)
command = all_commands[normalize_command_name(default_command)]
end
if command
args, opts = Foreman::Thor::Options.split(given_args)
if stop_on_unknown_option?(command) && !args.empty?
# given_args starts with a non-option, so we treat everything as
# ordinary arguments
args.concat opts
opts.clear
end
else
args = given_args
opts = nil
command = dynamic_command_class.new(meth)
end
opts = given_opts || opts || []
config[:current_command] = command
config[:command_options] = command.options
instance = new(args, opts, config)
yield instance if block_given?
args = instance.args
trailing = args[Range.new(arguments.size, -1)]
instance.invoke_command(command, trailing || [])
end
# The banner for this class. You can customize it if you are invoking the
# thor class by another ways which is not the Foreman::Thor::Runner. It receives
# the command that is going to be invoked and a boolean which indicates if
# the namespace should be displayed as arguments.
#
def banner(command, namespace = nil, subcommand = false)
"#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}"
end
def baseclass #:nodoc:
Foreman::Thor
end
def dynamic_command_class #:nodoc:
Foreman::Thor::DynamicCommand
end
def create_command(meth) #:nodoc:
@usage ||= nil
@desc ||= nil
@long_desc ||= nil
@disable_class_options ||= nil
if @usage && @desc
base_class = @hide ? Foreman::Thor::HiddenCommand : Foreman::Thor::Command
commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options, @disable_class_options)
@usage, @desc, @long_desc, @method_options, @hide, @disable_class_options = nil
true
elsif all_commands[meth] || meth == "method_missing"
true
else
puts "[WARNING] Attempted to create command #{meth.inspect} without usage or description. " \
"Call desc if you want this method to be available as command or declare it inside a " \
"no_commands{} block. Invoked from #{caller[1].inspect}."
false
end
end
alias_method :create_task, :create_command
def initialize_added #:nodoc:
class_options.merge!(method_options)
@method_options = nil
end
# Retrieve the command name from given args.
def retrieve_command_name(args) #:nodoc:
meth = args.first.to_s unless args.empty?
args.shift if meth && (map[meth] || meth !~ /^\-/)
end
alias_method :retrieve_task_name, :retrieve_command_name
# receives a (possibly nil) command name and returns a name that is in
# the commands hash. In addition to normalizing aliases, this logic
# will determine if a shortened command is an unambiguous substring of
# a command or alias.
#
# +normalize_command_name+ also converts names like +animal-prison+
# into +animal_prison+.
def normalize_command_name(meth) #:nodoc:
return default_command.to_s.tr("-", "_") unless meth
possibilities = find_command_possibilities(meth)
raise AmbiguousTaskError, "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]" if possibilities.size > 1
if possibilities.empty?
meth ||= default_command
elsif map[meth]
meth = map[meth]
else
meth = possibilities.first
end
meth.to_s.tr("-", "_") # treat foo-bar as foo_bar
end
alias_method :normalize_task_name, :normalize_command_name
# this is the logic that takes the command name passed in by the user
# and determines whether it is an unambiguous substrings of a command or
# alias name.
def find_command_possibilities(meth)
len = meth.to_s.length
possibilities = all_commands.merge(map).keys.select { |n| meth == n[0, len] }.sort
unique_possibilities = possibilities.map { |k| map[k] || k }.uniq
if possibilities.include?(meth)
[meth]
elsif unique_possibilities.size == 1
unique_possibilities
else
possibilities
end
end
alias_method :find_task_possibilities, :find_command_possibilities
def subcommand_help(cmd)
desc "help [COMMAND]", "Describe subcommands or one specific subcommand"
class_eval "
def help(command = nil, subcommand = true); super; end
"
end
alias_method :subtask_help, :subcommand_help
end
include Foreman::Thor::Base
map HELP_MAPPINGS => :help
desc "help [COMMAND]", "Describe available commands or one specific command"
disable_class_options
def help(command = nil, subcommand = false)
if command
if self.class.subcommands.include? command
self.class.subcommand_classes[command].help(shell, true)
else
self.class.command_help(shell, command)
end
else
self.class.help(shell, subcommand)
end
end
end
foreman-0.88.1/lib/foreman/vendor/thor/lib/thor/ 0000755 0000041 0000041 00000000000 14620136004 021453 5 ustar www-data www-data foreman-0.88.1/lib/foreman/vendor/thor/lib/thor/group.rb 0000644 0000041 0000041 00000021561 14620136004 023141 0 ustar www-data www-data require "foreman/vendor/thor/lib/thor/base"
# Foreman::Thor has a special class called Foreman::Thor::Group. The main difference to Foreman::Thor class
# is that it invokes all commands at once. It also include some methods that allows
# invocations to be done at the class method, which are not available to Foreman::Thor
# commands.
class Foreman::Thor::Group
class << self
# The description for this Foreman::Thor::Group. If none is provided, but a source root
# exists, tries to find the USAGE one folder above it, otherwise searches
# in the superclass.
#
# ==== Parameters
# description:: The description for this Foreman::Thor::Group.
#
def desc(description = nil)
if description
@desc = description
else
@desc ||= from_superclass(:desc, nil)
end
end
# Prints help information.
#
# ==== Options
# short:: When true, shows only usage.
#
def help(shell)
shell.say "Usage:"
shell.say " #{banner}\n"
shell.say
class_options_help(shell)
shell.say desc if desc
end
# Stores invocations for this class merging with superclass values.
#
def invocations #:nodoc:
@invocations ||= from_superclass(:invocations, {})
end
# Stores invocation blocks used on invoke_from_option.
#
def invocation_blocks #:nodoc:
@invocation_blocks ||= from_superclass(:invocation_blocks, {})
end
# Invoke the given namespace or class given. It adds an instance
# method that will invoke the klass and command. You can give a block to
# configure how it will be invoked.
#
# The namespace/class given will have its options showed on the help
# usage. Check invoke_from_option for more information.
#
def invoke(*names, &block)
options = names.last.is_a?(Hash) ? names.pop : {}
verbose = options.fetch(:verbose, true)
names.each do |name|
invocations[name] = false
invocation_blocks[name] = block if block_given?
class_eval <<-METHOD, __FILE__, __LINE__
def _invoke_#{name.to_s.gsub(/\W/, '_')}
klass, command = self.class.prepare_for_invocation(nil, #{name.inspect})
if klass
say_status :invoke, #{name.inspect}, #{verbose.inspect}
block = self.class.invocation_blocks[#{name.inspect}]
_invoke_for_class_method klass, command, &block
else
say_status :error, %(#{name.inspect} [not found]), :red
end
end
METHOD
end
end
# Invoke a thor class based on the value supplied by the user to the
# given option named "name". A class option must be created before this
# method is invoked for each name given.
#
# ==== Examples
#
# class GemGenerator < Foreman::Thor::Group
# class_option :test_framework, :type => :string
# invoke_from_option :test_framework
# end
#
# ==== Boolean options
#
# In some cases, you want to invoke a thor class if some option is true or
# false. This is automatically handled by invoke_from_option. Then the
# option name is used to invoke the generator.
#
# ==== Preparing for invocation
#
# In some cases you want to customize how a specified hook is going to be
# invoked. You can do that by overwriting the class method
# prepare_for_invocation. The class method must necessarily return a klass
# and an optional command.
#
# ==== Custom invocations
#
# You can also supply a block to customize how the option is going to be
# invoked. The block receives two parameters, an instance of the current
# class and the klass to be invoked.
#
def invoke_from_option(*names, &block)
options = names.last.is_a?(Hash) ? names.pop : {}
verbose = options.fetch(:verbose, :white)
names.each do |name|
unless class_options.key?(name)
raise ArgumentError, "You have to define the option #{name.inspect} " \
"before setting invoke_from_option."
end
invocations[name] = true
invocation_blocks[name] = block if block_given?
class_eval <<-METHOD, __FILE__, __LINE__
def _invoke_from_option_#{name.to_s.gsub(/\W/, '_')}
return unless options[#{name.inspect}]
value = options[#{name.inspect}]
value = #{name.inspect} if TrueClass === value
klass, command = self.class.prepare_for_invocation(#{name.inspect}, value)
if klass
say_status :invoke, value, #{verbose.inspect}
block = self.class.invocation_blocks[#{name.inspect}]
_invoke_for_class_method klass, command, &block
else
say_status :error, %(\#{value} [not found]), :red
end
end
METHOD
end
end
# Remove a previously added invocation.
#
# ==== Examples
#
# remove_invocation :test_framework
#
def remove_invocation(*names)
names.each do |name|
remove_command(name)
remove_class_option(name)
invocations.delete(name)
invocation_blocks.delete(name)
end
end
# Overwrite class options help to allow invoked generators options to be
# shown recursively when invoking a generator.
#
def class_options_help(shell, groups = {}) #:nodoc:
get_options_from_invocations(groups, class_options) do |klass|
klass.send(:get_options_from_invocations, groups, class_options)
end
super(shell, groups)
end
# Get invocations array and merge options from invocations. Those
# options are added to group_options hash. Options that already exists
# in base_options are not added twice.
#
def get_options_from_invocations(group_options, base_options) #:nodoc: # rubocop:disable MethodLength
invocations.each do |name, from_option|
value = if from_option
option = class_options[name]
option.type == :boolean ? name : option.default
else
name
end
next unless value
klass, _ = prepare_for_invocation(name, value)
next unless klass && klass.respond_to?(:class_options)
value = value.to_s
human_name = value.respond_to?(:classify) ? value.classify : value
group_options[human_name] ||= []
group_options[human_name] += klass.class_options.values.select do |class_option|
base_options[class_option.name.to_sym].nil? && class_option.group.nil? &&
!group_options.values.flatten.any? { |i| i.name == class_option.name }
end
yield klass if block_given?
end
end
# Returns commands ready to be printed.
def printable_commands(*)
item = []
item << banner
item << (desc ? "# #{desc.gsub(/\s+/m, ' ')}" : "")
[item]
end
alias_method :printable_tasks, :printable_commands
def handle_argument_error(command, error, _args, arity) #:nodoc:
msg = "#{basename} #{command.name} takes #{arity} argument"
msg << "s" if arity > 1
msg << ", but it should not."
raise error, msg
end
protected
# The method responsible for dispatching given the args.
def dispatch(command, given_args, given_opts, config) #:nodoc:
if Foreman::Thor::HELP_MAPPINGS.include?(given_args.first)
help(config[:shell])
return
end
args, opts = Foreman::Thor::Options.split(given_args)
opts = given_opts || opts
instance = new(args, opts, config)
yield instance if block_given?
if command
instance.invoke_command(all_commands[command])
else
instance.invoke_all
end
end
# The banner for this class. You can customize it if you are invoking the
# thor class by another ways which is not the Foreman::Thor::Runner.
def banner
"#{basename} #{self_command.formatted_usage(self, false)}"
end
# Represents the whole class as a command.
def self_command #:nodoc:
Foreman::Thor::DynamicCommand.new(namespace, class_options)
end
alias_method :self_task, :self_command
def baseclass #:nodoc:
Foreman::Thor::Group
end
def create_command(meth) #:nodoc:
commands[meth.to_s] = Foreman::Thor::Command.new(meth, nil, nil, nil, nil)
true
end
alias_method :create_task, :create_command
end
include Foreman::Thor::Base
protected
# Shortcut to invoke with padding and block handling. Use internally by
# invoke and invoke_from_option class methods.
def _invoke_for_class_method(klass, command = nil, *args, &block) #:nodoc:
with_padding do
if block
case block.arity
when 3
yield(self, klass, command)
when 2
yield(self, klass)
when 1
instance_exec(klass, &block)
end
else
invoke klass, command, *args
end
end
end
end
foreman-0.88.1/lib/foreman/vendor/thor/lib/thor/runner.rb 0000644 0000041 0000041 00000023675 14620136004 023326 0 ustar www-data www-data require "foreman/vendor/thor/lib/thor"
require "foreman/vendor/thor/lib/thor/group"
require "foreman/vendor/thor/lib/thor/core_ext/io_binary_read"
require "fileutils"
require "open-uri"
require "yaml"
require "digest/md5"
require "pathname"
class Foreman::Thor::Runner < Foreman::Thor #:nodoc: # rubocop:disable ClassLength
map "-T" => :list, "-i" => :install, "-u" => :update, "-v" => :version
def self.banner(command, all = false, subcommand = false)
"thor " + command.formatted_usage(self, all, subcommand)
end
def self.exit_on_failure?
true
end
# Override Foreman::Thor#help so it can give information about any class and any method.
#
def help(meth = nil)
if meth && !respond_to?(meth)
initialize_thorfiles(meth)
klass, command = Foreman::Thor::Util.find_class_and_command_by_namespace(meth)
self.class.handle_no_command_error(command, false) if klass.nil?
klass.start(["-h", command].compact, :shell => shell)
else
super
end
end
# If a command is not found on Foreman::Thor::Runner, method missing is invoked and
# Foreman::Thor::Runner is then responsible for finding the command in all classes.
#
def method_missing(meth, *args)
meth = meth.to_s
initialize_thorfiles(meth)
klass, command = Foreman::Thor::Util.find_class_and_command_by_namespace(meth)
self.class.handle_no_command_error(command, false) if klass.nil?
args.unshift(command) if command
klass.start(args, :shell => shell)
end
desc "install NAME", "Install an optionally named Foreman::Thor file into your system commands"
method_options :as => :string, :relative => :boolean, :force => :boolean
def install(name) # rubocop:disable MethodLength
initialize_thorfiles
# If a directory name is provided as the argument, look for a 'main.thor'
# command in said directory.
begin
if File.directory?(File.expand_path(name))
base = File.join(name, "main.thor")
package = :directory
contents = open(base, &:read)
else
base = name
package = :file
contents = open(name, &:read)
end
rescue OpenURI::HTTPError
raise Error, "Error opening URI '#{name}'"
rescue Errno::ENOENT
raise Error, "Error opening file '#{name}'"
end
say "Your Foreman::Thorfile contains:"
say contents
unless options["force"]
return false if no?("Do you wish to continue [y/N]?")
end
as = options["as"] || begin
first_line = contents.split("\n")[0]
(match = first_line.match(/\s*#\s*module:\s*([^\n]*)/)) ? match[1].strip : nil
end
unless as
basename = File.basename(name)
as = ask("Please specify a name for #{name} in the system repository [#{basename}]:")
as = basename if as.empty?
end
location = if options[:relative] || name =~ %r{^https?://}
name
else
File.expand_path(name)
end
thor_yaml[as] = {
:filename => Digest::MD5.hexdigest(name + as),
:location => location,
:namespaces => Foreman::Thor::Util.namespaces_in_content(contents, base)
}
save_yaml(thor_yaml)
say "Storing thor file in your system repository"
destination = File.join(thor_root, thor_yaml[as][:filename])
if package == :file
File.open(destination, "w") { |f| f.puts contents }
else
FileUtils.cp_r(name, destination)
end
thor_yaml[as][:filename] # Indicate success
end
desc "version", "Show Foreman::Thor version"
def version
require "foreman/vendor/thor/lib/thor/version"
say "Foreman::Thor #{Foreman::Thor::VERSION}"
end
desc "uninstall NAME", "Uninstall a named Foreman::Thor module"
def uninstall(name)
raise Error, "Can't find module '#{name}'" unless thor_yaml[name]
say "Uninstalling #{name}."
FileUtils.rm_rf(File.join(thor_root, (thor_yaml[name][:filename]).to_s))
thor_yaml.delete(name)
save_yaml(thor_yaml)
puts "Done."
end
desc "update NAME", "Update a Foreman::Thor file from its original location"
def update(name)
raise Error, "Can't find module '#{name}'" if !thor_yaml[name] || !thor_yaml[name][:location]
say "Updating '#{name}' from #{thor_yaml[name][:location]}"
old_filename = thor_yaml[name][:filename]
self.options = options.merge("as" => name)
if File.directory? File.expand_path(name)
FileUtils.rm_rf(File.join(thor_root, old_filename))
thor_yaml.delete(old_filename)
save_yaml(thor_yaml)
filename = install(name)
else
filename = install(thor_yaml[name][:location])
end
File.delete(File.join(thor_root, old_filename)) unless filename == old_filename
end
desc "installed", "List the installed Foreman::Thor modules and commands"
method_options :internal => :boolean
def installed
initialize_thorfiles(nil, true)
display_klasses(true, options["internal"])
end
desc "list [SEARCH]", "List the available thor commands (--substring means .*SEARCH)"
method_options :substring => :boolean, :group => :string, :all => :boolean, :debug => :boolean
def list(search = "")
initialize_thorfiles
search = ".*#{search}" if options["substring"]
search = /^#{search}.*/i
group = options[:group] || "standard"
klasses = Foreman::Thor::Base.subclasses.select do |k|
(options[:all] || k.group == group) && k.namespace =~ search
end
display_klasses(false, false, klasses)
end
private
def thor_root
Foreman::Thor::Util.thor_root
end
def thor_yaml
@thor_yaml ||= begin
yaml_file = File.join(thor_root, "thor.yml")
yaml = YAML.load_file(yaml_file) if File.exist?(yaml_file)
yaml || {}
end
end
# Save the yaml file. If none exists in thor root, creates one.
#
def save_yaml(yaml)
yaml_file = File.join(thor_root, "thor.yml")
unless File.exist?(yaml_file)
FileUtils.mkdir_p(thor_root)
yaml_file = File.join(thor_root, "thor.yml")
FileUtils.touch(yaml_file)
end
File.open(yaml_file, "w") { |f| f.puts yaml.to_yaml }
end
# Load the Foreman::Thorfiles. If relevant_to is supplied, looks for specific files
# in the thor_root instead of loading them all.
#
# By default, it also traverses the current path until find Foreman::Thor files, as
# described in thorfiles. This look up can be skipped by supplying
# skip_lookup true.
#
def initialize_thorfiles(relevant_to = nil, skip_lookup = false)
thorfiles(relevant_to, skip_lookup).each do |f|
Foreman::Thor::Util.load_thorfile(f, nil, options[:debug]) unless Foreman::Thor::Base.subclass_files.keys.include?(File.expand_path(f))
end
end
# Finds Foreman::Thorfiles by traversing from your current directory down to the root
# directory of your system. If at any time we find a Foreman::Thor file, we stop.
#
# We also ensure that system-wide Foreman::Thorfiles are loaded first, so local
# Foreman::Thorfiles can override them.
#
# ==== Example
#
# If we start at /Users/wycats/dev/thor ...
#
# 1. /Users/wycats/dev/thor
# 2. /Users/wycats/dev
# 3. /Users/wycats <-- we find a Foreman::Thorfile here, so we stop
#
# Suppose we start at c:\Documents and Settings\james\dev\thor ...
#
# 1. c:\Documents and Settings\james\dev\thor
# 2. c:\Documents and Settings\james\dev
# 3. c:\Documents and Settings\james
# 4. c:\Documents and Settings
# 5. c:\ <-- no Foreman::Thorfiles found!
#
def thorfiles(relevant_to = nil, skip_lookup = false)
thorfiles = []
unless skip_lookup
Pathname.pwd.ascend do |path|
thorfiles = Foreman::Thor::Util.globs_for(path).map { |g| Dir[g] }.flatten
break unless thorfiles.empty?
end
end
files = (relevant_to ? thorfiles_relevant_to(relevant_to) : Foreman::Thor::Util.thor_root_glob)
files += thorfiles
files -= ["#{thor_root}/thor.yml"]
files.map! do |file|
File.directory?(file) ? File.join(file, "main.thor") : file
end
end
# Load Foreman::Thorfiles relevant to the given method. If you provide "foo:bar" it
# will load all thor files in the thor.yaml that has "foo" e "foo:bar"
# namespaces registered.
#
def thorfiles_relevant_to(meth)
lookup = [meth, meth.split(":")[0...-1].join(":")]
files = thor_yaml.select do |_, v|
v[:namespaces] && !(v[:namespaces] & lookup).empty?
end
files.map { |_, v| File.join(thor_root, (v[:filename]).to_s) }
end
# Display information about the given klasses. If with_module is given,
# it shows a table with information extracted from the yaml file.
#
def display_klasses(with_modules = false, show_internal = false, klasses = Foreman::Thor::Base.subclasses)
klasses -= [Foreman::Thor, Foreman::Thor::Runner, Foreman::Thor::Group] unless show_internal
raise Error, "No Foreman::Thor commands available" if klasses.empty?
show_modules if with_modules && !thor_yaml.empty?
list = Hash.new { |h, k| h[k] = [] }
groups = klasses.select { |k| k.ancestors.include?(Foreman::Thor::Group) }
# Get classes which inherit from Foreman::Thor
(klasses - groups).each { |k| list[k.namespace.split(":").first] += k.printable_commands(false) }
# Get classes which inherit from Foreman::Thor::Base
groups.map! { |k| k.printable_commands(false).first }
list["root"] = groups
# Order namespaces with default coming first
list = list.sort { |a, b| a[0].sub(/^default/, "") <=> b[0].sub(/^default/, "") }
list.each { |n, commands| display_commands(n, commands) unless commands.empty? }
end
def display_commands(namespace, list) #:nodoc:
list.sort! { |a, b| a[0] <=> b[0] }
say shell.set_color(namespace, :blue, true)
say "-" * namespace.size
print_table(list, :truncate => true)
say
end
alias_method :display_tasks, :display_commands
def show_modules #:nodoc:
info = []
labels = %w(Modules Namespaces)
info << labels
info << ["-" * labels[0].size, "-" * labels[1].size]
thor_yaml.each do |name, hash|
info << [name, hash[:namespaces].join(", ")]
end
print_table info
say ""
end
end
foreman-0.88.1/lib/foreman/vendor/thor/lib/thor/line_editor.rb 0000644 0000041 0000041 00000000657 14620136004 024305 0 ustar www-data www-data require "foreman/vendor/thor/lib/thor/line_editor/basic"
require "foreman/vendor/thor/lib/thor/line_editor/readline"
class Foreman::Thor
module LineEditor
def self.readline(prompt, options = {})
best_available.new(prompt, options).readline
end
def self.best_available
[
Foreman::Thor::LineEditor::Readline,
Foreman::Thor::LineEditor::Basic
].detect(&:available?)
end
end
end
foreman-0.88.1/lib/foreman/vendor/thor/lib/thor/parser/ 0000755 0000041 0000041 00000000000 14620136004 022747 5 ustar www-data www-data foreman-0.88.1/lib/foreman/vendor/thor/lib/thor/parser/arguments.rb 0000644 0000041 0000041 00000010627 14620136004 025307 0 ustar www-data www-data class Foreman::Thor
class Arguments #:nodoc: # rubocop:disable ClassLength
NUMERIC = /[-+]?(\d*\.\d+|\d+)/
# Receives an array of args and returns two arrays, one with arguments
# and one with switches.
#
def self.split(args)
arguments = []
args.each do |item|
break if item =~ /^-/
arguments << item
end
[arguments, args[Range.new(arguments.size, -1)]]
end
def self.parse(*args)
to_parse = args.pop
new(*args).parse(to_parse)
end
# Takes an array of Foreman::Thor::Argument objects.
#
def initialize(arguments = [])
@assigns = {}
@non_assigned_required = []
@switches = arguments
arguments.each do |argument|
if !argument.default.nil?
@assigns[argument.human_name] = argument.default
elsif argument.required?
@non_assigned_required << argument
end
end
end
def parse(args)
@pile = args.dup
@switches.each do |argument|
break unless peek
@non_assigned_required.delete(argument)
@assigns[argument.human_name] = send(:"parse_#{argument.type}", argument.human_name)
end
check_requirement!
@assigns
end
def remaining
@pile
end
private
def no_or_skip?(arg)
arg =~ /^--(no|skip)-([-\w]+)$/
$2
end
def last?
@pile.empty?
end
def peek
@pile.first
end
def shift
@pile.shift
end
def unshift(arg)
if arg.is_a?(Array)
@pile = arg + @pile
else
@pile.unshift(arg)
end
end
def current_is_value?
peek && peek.to_s !~ /^-/
end
# Runs through the argument array getting strings that contains ":" and
# mark it as a hash:
#
# [ "name:string", "age:integer" ]
#
# Becomes:
#
# { "name" => "string", "age" => "integer" }
#
def parse_hash(name)
return shift if peek.is_a?(Hash)
hash = {}
while current_is_value? && peek.include?(":")
key, value = shift.split(":", 2)
raise MalformattedArgumentError, "You can't specify '#{key}' more than once in option '#{name}'; got #{key}:#{hash[key]} and #{key}:#{value}" if hash.include? key
hash[key] = value
end
hash
end
# Runs through the argument array getting all strings until no string is
# found or a switch is found.
#
# ["a", "b", "c"]
#
# And returns it as an array:
#
# ["a", "b", "c"]
#
def parse_array(name)
return shift if peek.is_a?(Array)
array = []
array << shift while current_is_value?
array
end
# Check if the peek is numeric format and return a Float or Integer.
# Check if the peek is included in enum if enum is provided.
# Otherwise raises an error.
#
def parse_numeric(name)
return shift if peek.is_a?(Numeric)
unless peek =~ NUMERIC && $& == peek
raise MalformattedArgumentError, "Expected numeric value for '#{name}'; got #{peek.inspect}"
end
value = $&.index(".") ? shift.to_f : shift.to_i
if @switches.is_a?(Hash) && switch = @switches[name]
if switch.enum && !switch.enum.include?(value)
raise MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}"
end
end
value
end
# Parse string:
# for --string-arg, just return the current value in the pile
# for --no-string-arg, nil
# Check if the peek is included in enum if enum is provided. Otherwise raises an error.
#
def parse_string(name)
if no_or_skip?(name)
nil
else
value = shift
if @switches.is_a?(Hash) && switch = @switches[name]
if switch.enum && !switch.enum.include?(value)
raise MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}"
end
end
value
end
end
# Raises an error if @non_assigned_required array is not empty.
#
def check_requirement!
return if @non_assigned_required.empty?
names = @non_assigned_required.map do |o|
o.respond_to?(:switch_name) ? o.switch_name : o.human_name
end.join("', '")
class_name = self.class.name.split("::").last.downcase
raise RequiredArgumentMissingError, "No value provided for required #{class_name} '#{names}'"
end
end
end
foreman-0.88.1/lib/foreman/vendor/thor/lib/thor/parser/option.rb 0000644 0000041 0000041 00000007612 14620136004 024612 0 ustar www-data www-data class Foreman::Thor
class Option < Argument #:nodoc:
attr_reader :aliases, :group, :lazy_default, :hide
VALID_TYPES = [:boolean, :numeric, :hash, :array, :string]
def initialize(name, options = {})
options[:required] = false unless options.key?(:required)
super
@lazy_default = options[:lazy_default]
@group = options[:group].to_s.capitalize if options[:group]
@aliases = Array(options[:aliases])
@hide = options[:hide]
end
# This parse quick options given as method_options. It makes several
# assumptions, but you can be more specific using the option method.
#
# parse :foo => "bar"
# #=> Option foo with default value bar
#
# parse [:foo, :baz] => "bar"
# #=> Option foo with default value bar and alias :baz
#
# parse :foo => :required
# #=> Required option foo without default value
#
# parse :foo => 2
# #=> Option foo with default value 2 and type numeric
#
# parse :foo => :numeric
# #=> Option foo without default value and type numeric
#
# parse :foo => true
# #=> Option foo with default value true and type boolean
#
# The valid types are :boolean, :numeric, :hash, :array and :string. If none
# is given a default type is assumed. This default type accepts arguments as
# string (--foo=value) or booleans (just --foo).
#
# By default all options are optional, unless :required is given.
#
def self.parse(key, value)
if key.is_a?(Array)
name, *aliases = key
else
name = key
aliases = []
end
name = name.to_s
default = value
type = case value
when Symbol
default = nil
if VALID_TYPES.include?(value)
value
elsif required = (value == :required) # rubocop:disable AssignmentInCondition
:string
end
when TrueClass, FalseClass
:boolean
when Numeric
:numeric
when Hash, Array, String
value.class.name.downcase.to_sym
end
new(name.to_s, :required => required, :type => type, :default => default, :aliases => aliases)
end
def switch_name
@switch_name ||= dasherized? ? name : dasherize(name)
end
def human_name
@human_name ||= dasherized? ? undasherize(name) : name
end
def usage(padding = 0)
sample = if banner && !banner.to_s.empty?
"#{switch_name}=#{banner}"
else
switch_name
end
sample = "[#{sample}]" unless required?
if boolean?
sample << ", [#{dasherize('no-' + human_name)}]" unless (name == "force") || name.start_with?("no-")
end
if aliases.empty?
(" " * padding) << sample
else
"#{aliases.join(', ')}, #{sample}"
end
end
VALID_TYPES.each do |type|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{type}?
self.type == #{type.inspect}
end
RUBY
end
protected
def validate!
raise ArgumentError, "An option cannot be boolean and required." if boolean? && required?
validate_default_type!
end
def validate_default_type!
default_type = case @default
when nil
return
when TrueClass, FalseClass
required? ? :string : :boolean
when Numeric
:numeric
when Symbol
:string
when Hash, Array, String
@default.class.name.downcase.to_sym
end
# TODO: This should raise an ArgumentError in a future version of Foreman::Thor
warn "Expected #{@type} default value for '#{switch_name}'; got #{@default.inspect} (#{default_type})" unless default_type == @type
end
def dasherized?
name.index("-") == 0
end
def undasherize(str)
str.sub(/^-{1,2}/, "")
end
def dasherize(str)
(str.length > 1 ? "--" : "-") + str.tr("_", "-")
end
end
end
foreman-0.88.1/lib/foreman/vendor/thor/lib/thor/parser/argument.rb 0000644 0000041 0000041 00000003404 14620136004 025117 0 ustar www-data www-data class Foreman::Thor
class Argument #:nodoc:
VALID_TYPES = [:numeric, :hash, :array, :string]
attr_reader :name, :description, :enum, :required, :type, :default, :banner
alias_method :human_name, :name
def initialize(name, options = {})
class_name = self.class.name.split("::").last
type = options[:type]
raise ArgumentError, "#{class_name} name can't be nil." if name.nil?
raise ArgumentError, "Type :#{type} is not valid for #{class_name.downcase}s." if type && !valid_type?(type)
@name = name.to_s
@description = options[:desc]
@required = options.key?(:required) ? options[:required] : true
@type = (type || :string).to_sym
@default = options[:default]
@banner = options[:banner] || default_banner
@enum = options[:enum]
validate! # Trigger specific validations
end
def usage
required? ? banner : "[#{banner}]"
end
def required?
required
end
def show_default?
case default
when Array, String, Hash
!default.empty?
else
default
end
end
protected
def validate!
raise ArgumentError, "An argument cannot be required and have default value." if required? && !default.nil?
raise ArgumentError, "An argument cannot have an enum other than an array." if @enum && !@enum.is_a?(Array)
end
def valid_type?(type)
self.class::VALID_TYPES.include?(type.to_sym)
end
def default_banner
case type
when :boolean
nil
when :string, :default
human_name.upcase
when :numeric
"N"
when :hash
"key:value"
when :array
"one two three"
end
end
end
end
foreman-0.88.1/lib/foreman/vendor/thor/lib/thor/parser/options.rb 0000644 0000041 0000041 00000013465 14620136004 025000 0 ustar www-data www-data class Foreman::Thor
class Options < Arguments #:nodoc: # rubocop:disable ClassLength
LONG_RE = /^(--\w+(?:-\w+)*)$/
SHORT_RE = /^(-[a-z])$/i
EQ_RE = /^(--\w+(?:-\w+)*|-[a-z])=(.*)$/i
SHORT_SQ_RE = /^-([a-z]{2,})$/i # Allow either -x -v or -xv style for single char args
SHORT_NUM = /^(-[a-z])#{NUMERIC}$/i
OPTS_END = "--".freeze
# Receives a hash and makes it switches.
def self.to_switches(options)
options.map do |key, value|
case value
when true
"--#{key}"
when Array
"--#{key} #{value.map(&:inspect).join(' ')}"
when Hash
"--#{key} #{value.map { |k, v| "#{k}:#{v}" }.join(' ')}"
when nil, false
""
else
"--#{key} #{value.inspect}"
end
end.join(" ")
end
# Takes a hash of Foreman::Thor::Option and a hash with defaults.
#
# If +stop_on_unknown+ is true, #parse will stop as soon as it encounters
# an unknown option or a regular argument.
def initialize(hash_options = {}, defaults = {}, stop_on_unknown = false)
@stop_on_unknown = stop_on_unknown
options = hash_options.values
super(options)
# Add defaults
defaults.each do |key, value|
@assigns[key.to_s] = value
@non_assigned_required.delete(hash_options[key])
end
@shorts = {}
@switches = {}
@extra = []
options.each do |option|
@switches[option.switch_name] = option
option.aliases.each do |short|
name = short.to_s.sub(/^(?!\-)/, "-")
@shorts[name] ||= option.switch_name
end
end
end
def remaining
@extra
end
def peek
return super unless @parsing_options
result = super
if result == OPTS_END
shift
@parsing_options = false
super
else
result
end
end
def parse(args) # rubocop:disable MethodLength
@pile = args.dup
@parsing_options = true
while peek
if parsing_options?
match, is_switch = current_is_switch?
shifted = shift
if is_switch
case shifted
when SHORT_SQ_RE
unshift($1.split("").map { |f| "-#{f}" })
next
when EQ_RE, SHORT_NUM
unshift($2)
switch = $1
when LONG_RE, SHORT_RE
switch = $1
end
switch = normalize_switch(switch)
option = switch_option(switch)
@assigns[option.human_name] = parse_peek(switch, option)
elsif @stop_on_unknown
@parsing_options = false
@extra << shifted
@extra << shift while peek
break
elsif match
@extra << shifted
@extra << shift while peek && peek !~ /^-/
else
@extra << shifted
end
else
@extra << shift
end
end
check_requirement!
assigns = Foreman::Thor::CoreExt::HashWithIndifferentAccess.new(@assigns)
assigns.freeze
assigns
end
def check_unknown!
# an unknown option starts with - or -- and has no more --'s afterward.
unknown = @extra.select { |str| str =~ /^--?(?:(?!--).)*$/ }
raise UnknownArgumentError, "Unknown switches '#{unknown.join(', ')}'" unless unknown.empty?
end
protected
# Check if the current value in peek is a registered switch.
#
# Two booleans are returned. The first is true if the current value
# starts with a hyphen; the second is true if it is a registered switch.
def current_is_switch?
case peek
when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM
[true, switch?($1)]
when SHORT_SQ_RE
[true, $1.split("").any? { |f| switch?("-#{f}") }]
else
[false, false]
end
end
def current_is_switch_formatted?
case peek
when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM, SHORT_SQ_RE
true
else
false
end
end
def current_is_value?
peek && (!parsing_options? || super)
end
def switch?(arg)
switch_option(normalize_switch(arg))
end
def switch_option(arg)
if match = no_or_skip?(arg) # rubocop:disable AssignmentInCondition
@switches[arg] || @switches["--#{match}"]
else
@switches[arg]
end
end
# Check if the given argument is actually a shortcut.
#
def normalize_switch(arg)
(@shorts[arg] || arg).tr("_", "-")
end
def parsing_options?
peek
@parsing_options
end
# Parse boolean values which can be given as --foo=true, --foo or --no-foo.
#
def parse_boolean(switch)
if current_is_value?
if ["true", "TRUE", "t", "T", true].include?(peek)
shift
true
elsif ["false", "FALSE", "f", "F", false].include?(peek)
shift
false
else
true
end
else
@switches.key?(switch) || !no_or_skip?(switch)
end
end
# Parse the value at the peek analyzing if it requires an input or not.
#
def parse_peek(switch, option)
if parsing_options? && (current_is_switch_formatted? || last?)
if option.boolean?
# No problem for boolean types
elsif no_or_skip?(switch)
return nil # User set value to nil
elsif option.string? && !option.required?
# Return the default if there is one, else the human name
return option.lazy_default || option.default || option.human_name
elsif option.lazy_default
return option.lazy_default
else
raise MalformattedArgumentError, "No value provided for option '#{switch}'"
end
end
@non_assigned_required.delete(option)
send(:"parse_#{option.type}", switch)
end
end
end
foreman-0.88.1/lib/foreman/vendor/thor/lib/thor/shell/ 0000755 0000041 0000041 00000000000 14620136004 022562 5 ustar www-data www-data foreman-0.88.1/lib/foreman/vendor/thor/lib/thor/shell/color.rb 0000644 0000041 0000041 00000011257 14620136004 024233 0 ustar www-data www-data require "foreman/vendor/thor/lib/thor/shell/basic"
class Foreman::Thor
module Shell
# Inherit from Foreman::Thor::Shell::Basic and add set_color behavior. Check
# Foreman::Thor::Shell::Basic to see all available methods.
#
class Color < Basic
# Embed in a String to clear all previous ANSI sequences.
CLEAR = "\e[0m"
# The start of an ANSI bold sequence.
BOLD = "\e[1m"
# Set the terminal's foreground ANSI color to black.
BLACK = "\e[30m"
# Set the terminal's foreground ANSI color to red.
RED = "\e[31m"
# Set the terminal's foreground ANSI color to green.
GREEN = "\e[32m"
# Set the terminal's foreground ANSI color to yellow.
YELLOW = "\e[33m"
# Set the terminal's foreground ANSI color to blue.
BLUE = "\e[34m"
# Set the terminal's foreground ANSI color to magenta.
MAGENTA = "\e[35m"
# Set the terminal's foreground ANSI color to cyan.
CYAN = "\e[36m"
# Set the terminal's foreground ANSI color to white.
WHITE = "\e[37m"
# Set the terminal's background ANSI color to black.
ON_BLACK = "\e[40m"
# Set the terminal's background ANSI color to red.
ON_RED = "\e[41m"
# Set the terminal's background ANSI color to green.
ON_GREEN = "\e[42m"
# Set the terminal's background ANSI color to yellow.
ON_YELLOW = "\e[43m"
# Set the terminal's background ANSI color to blue.
ON_BLUE = "\e[44m"
# Set the terminal's background ANSI color to magenta.
ON_MAGENTA = "\e[45m"
# Set the terminal's background ANSI color to cyan.
ON_CYAN = "\e[46m"
# Set the terminal's background ANSI color to white.
ON_WHITE = "\e[47m"
# Set color by using a string or one of the defined constants. If a third
# option is set to true, it also adds bold to the string. This is based
# on Highline implementation and it automatically appends CLEAR to the end
# of the returned String.
#
# Pass foreground, background and bold options to this method as
# symbols.
#
# Example:
#
# set_color "Hi!", :red, :on_white, :bold
#
# The available colors are:
#
# :bold
# :black
# :red
# :green
# :yellow
# :blue
# :magenta
# :cyan
# :white
# :on_black
# :on_red
# :on_green
# :on_yellow
# :on_blue
# :on_magenta
# :on_cyan
# :on_white
def set_color(string, *colors)
if colors.compact.empty? || !can_display_colors?
string
elsif colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) }
ansi_colors = colors.map { |color| lookup_color(color) }
"#{ansi_colors.join}#{string}#{CLEAR}"
else
# The old API was `set_color(color, bold=boolean)`. We
# continue to support the old API because you should never
# break old APIs unnecessarily :P
foreground, bold = colors
foreground = self.class.const_get(foreground.to_s.upcase) if foreground.is_a?(Symbol)
bold = bold ? BOLD : ""
"#{bold}#{foreground}#{string}#{CLEAR}"
end
end
protected
def can_display_colors?
stdout.tty?
end
# Overwrite show_diff to show diff with colors if Diff::LCS is
# available.
#
def show_diff(destination, content) #:nodoc:
if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil?
actual = File.binread(destination).to_s.split("\n")
content = content.to_s.split("\n")
Diff::LCS.sdiff(actual, content).each do |diff|
output_diff_line(diff)
end
else
super
end
end
def output_diff_line(diff) #:nodoc:
case diff.action
when "-"
say "- #{diff.old_element.chomp}", :red, true
when "+"
say "+ #{diff.new_element.chomp}", :green, true
when "!"
say "- #{diff.old_element.chomp}", :red, true
say "+ #{diff.new_element.chomp}", :green, true
else
say " #{diff.old_element.chomp}", nil, true
end
end
# Check if Diff::LCS is loaded. If it is, use it to create pretty output
# for diff.
#
def diff_lcs_loaded? #:nodoc:
return true if defined?(Diff::LCS)
return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
@diff_lcs_loaded = begin
require "diff/lcs"
true
rescue LoadError
false
end
end
end
end
end
foreman-0.88.1/lib/foreman/vendor/thor/lib/thor/shell/basic.rb 0000644 0000041 0000041 00000030755 14620136004 024202 0 ustar www-data www-data require "tempfile"
require "io/console" if RUBY_VERSION > "1.9.2"
class Foreman::Thor
module Shell
class Basic
attr_accessor :base
attr_reader :padding
# Initialize base, mute and padding to nil.
#
def initialize #:nodoc:
@base = nil
@mute = false
@padding = 0
@always_force = false
end
# Mute everything that's inside given block
#
def mute
@mute = true
yield
ensure
@mute = false
end
# Check if base is muted
#
def mute?
@mute
end
# Sets the output padding, not allowing less than zero values.
#
def padding=(value)
@padding = [0, value].max
end
# Sets the output padding while executing a block and resets it.
#
def indent(count = 1)
orig_padding = padding
self.padding = padding + count
yield
self.padding = orig_padding
end
# Asks something to the user and receives a response.
#
# If asked to limit the correct responses, you can pass in an
# array of acceptable answers. If one of those is not supplied,
# they will be shown a message stating that one of those answers
# must be given and re-asked the question.
#
# If asking for sensitive information, the :echo option can be set
# to false to mask user input from $stdin.
#
# If the required input is a path, then set the path option to
# true. This will enable tab completion for file paths relative
# to the current working directory on systems that support
# Readline.
#
# ==== Example
# ask("What is your name?")
#
# ask("What is your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"])
#
# ask("What is your password?", :echo => false)
#
# ask("Where should the file be saved?", :path => true)
#
def ask(statement, *args)
options = args.last.is_a?(Hash) ? args.pop : {}
color = args.first
if options[:limited_to]
ask_filtered(statement, color, options)
else
ask_simply(statement, color, options)
end
end
# Say (print) something to the user. If the sentence ends with a whitespace
# or tab character, a new line is not appended (print + flush). Otherwise
# are passed straight to puts (behavior got from Highline).
#
# ==== Example
# say("I know you knew that.")
#
def say(message = "", color = nil, force_new_line = (message.to_s !~ /( |\t)\Z/))
buffer = prepare_message(message, *color)
buffer << "\n" if force_new_line && !message.to_s.end_with?("\n")
stdout.print(buffer)
stdout.flush
end
# Say a status with the given color and appends the message. Since this
# method is used frequently by actions, it allows nil or false to be given
# in log_status, avoiding the message from being shown. If a Symbol is
# given in log_status, it's used as the color.
#
def say_status(status, message, log_status = true)
return if quiet? || log_status == false
spaces = " " * (padding + 1)
color = log_status.is_a?(Symbol) ? log_status : :green
status = status.to_s.rjust(12)
status = set_color status, color, true if color
buffer = "#{status}#{spaces}#{message}"
buffer << "\n" unless buffer.end_with?("\n")
stdout.print(buffer)
stdout.flush
end
# Make a question the to user and returns true if the user replies "y" or
# "yes".
#
def yes?(statement, color = nil)
!!(ask(statement, color, :add_to_history => false) =~ is?(:yes))
end
# Make a question the to user and returns true if the user replies "n" or
# "no".
#
def no?(statement, color = nil)
!!(ask(statement, color, :add_to_history => false) =~ is?(:no))
end
# Prints values in columns
#
# ==== Parameters
# Array[String, String, ...]
#
def print_in_columns(array)
return if array.empty?
colwidth = (array.map { |el| el.to_s.size }.max || 0) + 2
array.each_with_index do |value, index|
# Don't output trailing spaces when printing the last column
if ((((index + 1) % (terminal_width / colwidth))).zero? && !index.zero?) || index + 1 == array.length
stdout.puts value
else
stdout.printf("%-#{colwidth}s", value)
end
end
end
# Prints a table.
#
# ==== Parameters
# Array[Array[String, String, ...]]
#
# ==== Options
# indent:: Indent the first column by indent value.
# colwidth:: Force the first column to colwidth spaces wide.
#
def print_table(array, options = {}) # rubocop:disable MethodLength
return if array.empty?
formats = []
indent = options[:indent].to_i
colwidth = options[:colwidth]
options[:truncate] = terminal_width if options[:truncate] == true
formats << "%-#{colwidth + 2}s" if colwidth
start = colwidth ? 1 : 0
colcount = array.max { |a, b| a.size <=> b.size }.size
maximas = []
start.upto(colcount - 1) do |index|
maxima = array.map { |row| row[index] ? row[index].to_s.size : 0 }.max
maximas << maxima
formats << if index == colcount - 1
# Don't output 2 trailing spaces when printing the last column
"%-s"
else
"%-#{maxima + 2}s"
end
end
formats[0] = formats[0].insert(0, " " * indent)
formats << "%s"
array.each do |row|
sentence = ""
row.each_with_index do |column, index|
maxima = maximas[index]
f = if column.is_a?(Numeric)
if index == row.size - 1
# Don't output 2 trailing spaces when printing the last column
"%#{maxima}s"
else
"%#{maxima}s "
end
else
formats[index]
end
sentence << f % column.to_s
end
sentence = truncate(sentence, options[:truncate]) if options[:truncate]
stdout.puts sentence
end
end
# Prints a long string, word-wrapping the text to the current width of the
# terminal display. Ideal for printing heredocs.
#
# ==== Parameters
# String
#
# ==== Options
# indent:: Indent each line of the printed paragraph by indent value.
#
def print_wrapped(message, options = {})
indent = options[:indent] || 0
width = terminal_width - indent
paras = message.split("\n\n")
paras.map! do |unwrapped|
unwrapped.strip.tr("\n", " ").squeeze(" ").gsub(/.{1,#{width}}(?:\s|\Z)/) { ($& + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n") }
end
paras.each do |para|
para.split("\n").each do |line|
stdout.puts line.insert(0, " " * indent)
end
stdout.puts unless para == paras.last
end
end
# Deals with file collision and returns true if the file should be
# overwritten and false otherwise. If a block is given, it uses the block
# response as the content for the diff.
#
# ==== Parameters
# destination:: the destination file to solve conflicts
# block:: an optional block that returns the value to be used in diff
#
def file_collision(destination)
return true if @always_force
options = block_given? ? "[Ynaqdh]" : "[Ynaqh]"
loop do
answer = ask(
%[Overwrite #{destination}? (enter "h" for help) #{options}],
:add_to_history => false
)
case answer
when is?(:yes), is?(:force), ""
return true
when is?(:no), is?(:skip)
return false
when is?(:always)
return @always_force = true
when is?(:quit)
say "Aborting..."
raise SystemExit
when is?(:diff)
show_diff(destination, yield) if block_given?
say "Retrying..."
else
say file_collision_help
end
end
end
# This code was copied from Rake, available under MIT-LICENSE
# Copyright (c) 2003, 2004 Jim Weirich
def terminal_width
result = if ENV["THOR_COLUMNS"]
ENV["THOR_COLUMNS"].to_i
else
unix? ? dynamic_width : 80
end
result < 10 ? 80 : result
rescue
80
end
# Called if something goes wrong during the execution. This is used by Foreman::Thor
# internally and should not be used inside your scripts. If something went
# wrong, you can always raise an exception. If you raise a Foreman::Thor::Error, it
# will be rescued and wrapped in the method below.
#
def error(statement)
stderr.puts statement
end
# Apply color to the given string with optional bold. Disabled in the
# Foreman::Thor::Shell::Basic class.
#
def set_color(string, *) #:nodoc:
string
end
protected
def prepare_message(message, *color)
spaces = " " * padding
spaces + set_color(message.to_s, *color)
end
def can_display_colors?
false
end
def lookup_color(color)
return color unless color.is_a?(Symbol)
self.class.const_get(color.to_s.upcase)
end
def stdout
$stdout
end
def stderr
$stderr
end
def is?(value) #:nodoc:
value = value.to_s
if value.size == 1
/\A#{value}\z/i
else
/\A(#{value}|#{value[0, 1]})\z/i
end
end
def file_collision_help #:nodoc:
<<-HELP
Y - yes, overwrite
n - no, do not overwrite
a - all, overwrite this and all others
q - quit, abort
d - diff, show the differences between the old and the new
h - help, show this help
HELP
end
def show_diff(destination, content) #:nodoc:
diff_cmd = ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u"
Tempfile.open(File.basename(destination), File.dirname(destination)) do |temp|
temp.write content
temp.rewind
system %(#{diff_cmd} "#{destination}" "#{temp.path}")
end
end
def quiet? #:nodoc:
mute? || (base && base.options[:quiet])
end
# Calculate the dynamic width of the terminal
def dynamic_width
@dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
end
def dynamic_width_stty
`stty size 2>/dev/null`.split[1].to_i
end
def dynamic_width_tput
`tput cols 2>/dev/null`.to_i
end
def unix?
RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
end
def truncate(string, width)
as_unicode do
chars = string.chars.to_a
if chars.length <= width
chars.join
else
chars[0, width - 3].join + "..."
end
end
end
if "".respond_to?(:encode)
def as_unicode
yield
end
else
def as_unicode
old = $KCODE
$KCODE = "U"
yield
ensure
$KCODE = old
end
end
def ask_simply(statement, color, options)
default = options[:default]
message = [statement, ("(#{default})" if default), nil].uniq.join(" ")
message = prepare_message(message, *color)
result = Foreman::Thor::LineEditor.readline(message, options)
return unless result
result.strip!
if default && result == ""
default
else
result
end
end
def ask_filtered(statement, color, options)
answer_set = options[:limited_to]
correct_answer = nil
until correct_answer
answers = answer_set.join(", ")
answer = ask_simply("#{statement} [#{answers}]", color, options)
correct_answer = answer_set.include?(answer) ? answer : nil
say("Your response must be one of: [#{answers}]. Please try again.") unless correct_answer
end
correct_answer
end
end
end
end
foreman-0.88.1/lib/foreman/vendor/thor/lib/thor/shell/html.rb 0000644 0000041 0000041 00000010557 14620136004 024063 0 ustar www-data www-data require "foreman/vendor/thor/lib/thor/shell/basic"
class Foreman::Thor
module Shell
# Inherit from Foreman::Thor::Shell::Basic and add set_color behavior. Check
# Foreman::Thor::Shell::Basic to see all available methods.
#
class HTML < Basic
# The start of an HTML bold sequence.
BOLD = "font-weight: bold"
# Set the terminal's foreground HTML color to black.
BLACK = "color: black"
# Set the terminal's foreground HTML color to red.
RED = "color: red"
# Set the terminal's foreground HTML color to green.
GREEN = "color: green"
# Set the terminal's foreground HTML color to yellow.
YELLOW = "color: yellow"
# Set the terminal's foreground HTML color to blue.
BLUE = "color: blue"
# Set the terminal's foreground HTML color to magenta.
MAGENTA = "color: magenta"
# Set the terminal's foreground HTML color to cyan.
CYAN = "color: cyan"
# Set the terminal's foreground HTML color to white.
WHITE = "color: white"
# Set the terminal's background HTML color to black.
ON_BLACK = "background-color: black"
# Set the terminal's background HTML color to red.
ON_RED = "background-color: red"
# Set the terminal's background HTML color to green.
ON_GREEN = "background-color: green"
# Set the terminal's background HTML color to yellow.
ON_YELLOW = "background-color: yellow"
# Set the terminal's background HTML color to blue.
ON_BLUE = "background-color: blue"
# Set the terminal's background HTML color to magenta.
ON_MAGENTA = "background-color: magenta"
# Set the terminal's background HTML color to cyan.
ON_CYAN = "background-color: cyan"
# Set the terminal's background HTML color to white.
ON_WHITE = "background-color: white"
# Set color by using a string or one of the defined constants. If a third
# option is set to true, it also adds bold to the string. This is based
# on Highline implementation and it automatically appends CLEAR to the end
# of the returned String.
#
def set_color(string, *colors)
if colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) }
html_colors = colors.map { |color| lookup_color(color) }
"#{string}"
else
color, bold = colors
html_color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol)
styles = [html_color]
styles << BOLD if bold
"#{string}"
end
end
# Ask something to the user and receives a response.
#
# ==== Example
# ask("What is your name?")
#
# TODO: Implement #ask for Foreman::Thor::Shell::HTML
def ask(statement, color = nil)
raise NotImplementedError, "Implement #ask for Foreman::Thor::Shell::HTML"
end
protected
def can_display_colors?
true
end
# Overwrite show_diff to show diff with colors if Diff::LCS is
# available.
#
def show_diff(destination, content) #:nodoc:
if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil?
actual = File.binread(destination).to_s.split("\n")
content = content.to_s.split("\n")
Diff::LCS.sdiff(actual, content).each do |diff|
output_diff_line(diff)
end
else
super
end
end
def output_diff_line(diff) #:nodoc:
case diff.action
when "-"
say "- #{diff.old_element.chomp}", :red, true
when "+"
say "+ #{diff.new_element.chomp}", :green, true
when "!"
say "- #{diff.old_element.chomp}", :red, true
say "+ #{diff.new_element.chomp}", :green, true
else
say " #{diff.old_element.chomp}", nil, true
end
end
# Check if Diff::LCS is loaded. If it is, use it to create pretty output
# for diff.
#
def diff_lcs_loaded? #:nodoc:
return true if defined?(Diff::LCS)
return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
@diff_lcs_loaded = begin
require "diff/lcs"
true
rescue LoadError
false
end
end
end
end
end
foreman-0.88.1/lib/foreman/vendor/thor/lib/thor/shell.rb 0000644 0000041 0000041 00000004447 14620136004 023120 0 ustar www-data www-data require "rbconfig"
class Foreman::Thor
module Base
class << self
attr_writer :shell
# Returns the shell used in all Foreman::Thor classes. If you are in a Unix platform
# it will use a colored log, otherwise it will use a basic one without color.
#
def shell
@shell ||= if ENV["THOR_SHELL"] && !ENV["THOR_SHELL"].empty?
Foreman::Thor::Shell.const_get(ENV["THOR_SHELL"])
elsif RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ && !ENV["ANSICON"]
Foreman::Thor::Shell::Basic
else
Foreman::Thor::Shell::Color
end
end
end
end
module Shell
SHELL_DELEGATED_METHODS = [:ask, :error, :set_color, :yes?, :no?, :say, :say_status, :print_in_columns, :print_table, :print_wrapped, :file_collision, :terminal_width]
attr_writer :shell
autoload :Basic, "foreman/vendor/thor/lib/thor/shell/basic"
autoload :Color, "foreman/vendor/thor/lib/thor/shell/color"
autoload :HTML, "foreman/vendor/thor/lib/thor/shell/html"
# Add shell to initialize config values.
#
# ==== Configuration
# shell