foreman 0.31.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +46 -0
- data/bin/foreman-runner +32 -0
- data/bin/taskman +8 -0
- data/data/example/Procfile +4 -2
- data/data/example/spawnee +14 -0
- data/data/example/spawner +7 -0
- data/data/example/utf8 +11 -0
- data/data/export/bluepill/master.pill.erb +11 -10
- data/data/export/launchd/launchd.plist.erb +22 -0
- data/data/export/runit/log/run.erb +7 -0
- data/data/export/runit/run.erb +2 -2
- data/data/export/supervisord/app.conf.erb +27 -0
- data/data/export/upstart/master.conf.erb +2 -2
- data/data/export/upstart/process.conf.erb +3 -3
- data/lib/foreman/cli.rb +83 -34
- data/lib/foreman/engine/cli.rb +105 -0
- data/lib/foreman/engine.rb +232 -140
- data/lib/foreman/env.rb +27 -0
- data/lib/foreman/export/base.rb +108 -6
- data/lib/foreman/export/bluepill.rb +4 -20
- data/lib/foreman/export/inittab.rb +11 -16
- data/lib/foreman/export/launchd.rb +15 -0
- data/lib/foreman/export/runit.rb +15 -41
- data/lib/foreman/export/supervisord.rb +16 -0
- data/lib/foreman/export/upstart.rb +10 -28
- data/lib/foreman/export.rb +23 -0
- data/lib/foreman/helpers.rb +45 -0
- data/lib/foreman/process.rb +80 -46
- data/lib/foreman/procfile.rb +68 -14
- data/lib/foreman/version.rb +1 -1
- data/lib/foreman.rb +12 -7
- data/man/foreman.1 +29 -3
- data/spec/foreman/cli_spec.rb +54 -60
- data/spec/foreman/engine_spec.rb +64 -46
- data/spec/foreman/export/base_spec.rb +19 -0
- data/spec/foreman/export/bluepill_spec.rb +24 -6
- data/spec/foreman/export/inittab_spec.rb +40 -0
- data/spec/foreman/export/launchd_spec.rb +21 -0
- data/spec/foreman/export/runit_spec.rb +23 -22
- data/spec/foreman/export/supervisord_spec.rb +36 -0
- data/spec/foreman/export/upstart_spec.rb +49 -16
- data/spec/foreman/export_spec.rb +22 -0
- data/spec/foreman/helpers_spec.rb +26 -0
- data/spec/foreman/process_spec.rb +48 -2
- data/spec/foreman/procfile_spec.rb +41 -0
- data/spec/foreman_spec.rb +3 -20
- data/spec/helper_spec.rb +18 -0
- data/spec/resources/Procfile +4 -0
- data/spec/resources/bin/echo +2 -0
- data/spec/resources/bin/env +2 -0
- data/spec/resources/bin/test +2 -0
- data/spec/resources/bin/utf8 +2 -0
- data/spec/resources/export/bluepill/app-concurrency.pill +49 -0
- data/spec/resources/export/bluepill/app.pill +6 -25
- data/spec/resources/export/inittab/inittab.concurrency +4 -0
- data/spec/resources/export/inittab/inittab.default +4 -0
- data/spec/resources/export/launchd/launchd-a.default +22 -0
- data/spec/resources/export/launchd/launchd-b.default +22 -0
- data/spec/resources/export/supervisord/app-alpha-1.conf +24 -0
- data/spec/resources/export/supervisord/app-alpha-2.conf +24 -0
- data/spec/spec_helper.rb +93 -10
- metadata +72 -76
- data/README.markdown +0 -49
- data/bin/runner +0 -2
- data/data/export/runit/log_run.erb +0 -7
- data/lib/foreman/procfile_entry.rb +0 -22
- data/lib/foreman/utils.rb +0 -15
- /data/spec/resources/export/runit/{app-alpha-1-log-run → app-alpha-1/log/run} +0 -0
- /data/spec/resources/export/runit/{app-alpha-1-run → app-alpha-1/run} +0 -0
- /data/spec/resources/export/runit/{app-alpha-2-log-run → app-alpha-2/log/run} +0 -0
- /data/spec/resources/export/runit/{app-alpha-2-run → app-alpha-2/run} +0 -0
- /data/spec/resources/export/runit/{app-bravo-1-log-run → app-bravo-1/log/run} +0 -0
- /data/spec/resources/export/runit/{app-bravo-1-run → app-bravo-1/run} +0 -0
|
@@ -2,36 +2,31 @@ require "foreman/export"
|
|
|
2
2
|
|
|
3
3
|
class Foreman::Export::Inittab < Foreman::Export::Base
|
|
4
4
|
|
|
5
|
-
def export
|
|
6
|
-
|
|
7
|
-
user = options[:user] || app
|
|
8
|
-
log_root = options[:log] || "/var/log/#{app}"
|
|
9
|
-
|
|
10
|
-
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
|
5
|
+
def export
|
|
6
|
+
error("Must specify a location") unless location
|
|
11
7
|
|
|
12
8
|
inittab = []
|
|
13
9
|
inittab << "# ----- foreman #{app} processes -----"
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
index = 1
|
|
12
|
+
engine.each_process do |name, process|
|
|
13
|
+
1.upto(engine.formation[name]) do |num|
|
|
17
14
|
id = app.slice(0, 2).upcase + sprintf("%02d", index)
|
|
18
|
-
port = engine.port_for(process, num
|
|
19
|
-
inittab << "#{id}:4:respawn:/bin/su - #{user} -c 'PORT=#{port} #{process.command} >> #{
|
|
15
|
+
port = engine.port_for(process, num)
|
|
16
|
+
inittab << "#{id}:4:respawn:/bin/su - #{user} -c 'PORT=#{port} #{process.command} >> #{log}/#{name}-#{num}.log 2>&1'"
|
|
20
17
|
index += 1
|
|
21
18
|
end
|
|
22
|
-
index
|
|
23
19
|
end
|
|
24
20
|
|
|
25
21
|
inittab << "# ----- end foreman #{app} processes -----"
|
|
26
22
|
|
|
27
23
|
inittab = inittab.join("\n") + "\n"
|
|
28
24
|
|
|
29
|
-
if
|
|
30
|
-
FileUtils.mkdir_p(log_root) rescue error "could not create #{log_root}"
|
|
31
|
-
FileUtils.chown(user, nil, log_root) rescue error "could not chown #{log_root} to #{user}"
|
|
32
|
-
write_file(fname, inittab)
|
|
33
|
-
else
|
|
25
|
+
if location == "-"
|
|
34
26
|
puts inittab
|
|
27
|
+
else
|
|
28
|
+
say "writing: #{location}"
|
|
29
|
+
File.open(location, "w") { |file| file.puts inittab }
|
|
35
30
|
end
|
|
36
31
|
end
|
|
37
32
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require "erb"
|
|
2
|
+
require "foreman/export"
|
|
3
|
+
|
|
4
|
+
class Foreman::Export::Launchd < Foreman::Export::Base
|
|
5
|
+
|
|
6
|
+
def export
|
|
7
|
+
super
|
|
8
|
+
engine.each_process do |name, process|
|
|
9
|
+
1.upto(engine.formation[name]) do |num|
|
|
10
|
+
write_template "launchd/launchd.plist.erb", "#{app}-#{name}-#{num}.plist", binding
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
data/lib/foreman/export/runit.rb
CHANGED
|
@@ -2,59 +2,33 @@ require "erb"
|
|
|
2
2
|
require "foreman/export"
|
|
3
3
|
|
|
4
4
|
class Foreman::Export::Runit < Foreman::Export::Base
|
|
5
|
-
ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
|
|
6
|
-
|
|
7
|
-
def export(location, options={})
|
|
8
|
-
error("Must specify a location") unless location
|
|
9
|
-
|
|
10
|
-
app = options[:app] || File.basename(engine.directory)
|
|
11
|
-
user = options[:user] || app
|
|
12
|
-
log_root = options[:log] || "/var/log/#{app}"
|
|
13
|
-
template_root = options[:template]
|
|
14
5
|
|
|
15
|
-
|
|
6
|
+
ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
|
|
16
7
|
|
|
17
|
-
|
|
18
|
-
|
|
8
|
+
def export
|
|
9
|
+
super
|
|
19
10
|
|
|
20
|
-
engine.
|
|
21
|
-
1.upto(
|
|
22
|
-
process_directory
|
|
23
|
-
process_env_directory = "#{process_directory}/env"
|
|
24
|
-
process_log_directory = "#{process_directory}/log"
|
|
11
|
+
engine.each_process do |name, process|
|
|
12
|
+
1.upto(engine.formation[name]) do |num|
|
|
13
|
+
process_directory = "#{app}-#{name}-#{num}"
|
|
25
14
|
|
|
26
15
|
create_directory process_directory
|
|
27
|
-
create_directory
|
|
28
|
-
create_directory
|
|
29
|
-
|
|
30
|
-
run = ERB.new(run_template).result(binding)
|
|
31
|
-
write_file "#{process_directory}/run", run
|
|
16
|
+
create_directory "#{process_directory}/env"
|
|
17
|
+
create_directory "#{process_directory}/log"
|
|
32
18
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
merge(engine.environment).
|
|
36
|
-
merge(inline_variables(process.command))
|
|
19
|
+
write_template "runit/run.erb", "#{process_directory}/run", binding
|
|
20
|
+
chmod 0755, "#{process_directory}/run"
|
|
37
21
|
|
|
38
|
-
|
|
39
|
-
|
|
22
|
+
port = engine.port_for(process, num)
|
|
23
|
+
engine.env.merge("PORT" => port.to_s).each do |key, value|
|
|
24
|
+
write_file "#{process_directory}/env/#{key}", value
|
|
40
25
|
end
|
|
41
26
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
27
|
+
write_template "runit/log/run.erb", "#{process_directory}/log/run", binding
|
|
28
|
+
chmod 0755, "#{process_directory}/log/run"
|
|
45
29
|
end
|
|
46
30
|
end
|
|
47
31
|
|
|
48
32
|
end
|
|
49
33
|
|
|
50
|
-
private
|
|
51
|
-
def create_directory(location)
|
|
52
|
-
say "creating: #{location}"
|
|
53
|
-
FileUtils.mkdir(location)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def inline_variables(command)
|
|
57
|
-
variable_name_regex =
|
|
58
|
-
Hash[*command.scan(ENV_VARIABLE_REGEX).flatten]
|
|
59
|
-
end
|
|
60
34
|
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require "erb"
|
|
2
|
+
require "foreman/export"
|
|
3
|
+
|
|
4
|
+
class Foreman::Export::Supervisord < Foreman::Export::Base
|
|
5
|
+
|
|
6
|
+
def export
|
|
7
|
+
super
|
|
8
|
+
|
|
9
|
+
Dir["#{location}/#{app}*.conf"].each do |file|
|
|
10
|
+
clean file
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
write_template "supervisord/app.conf.erb", "#{app}.conf", binding
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
@@ -3,41 +3,23 @@ require "foreman/export"
|
|
|
3
3
|
|
|
4
4
|
class Foreman::Export::Upstart < Foreman::Export::Base
|
|
5
5
|
|
|
6
|
-
def export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
FileUtils.mkdir_p location
|
|
10
|
-
|
|
11
|
-
app = options[:app] || File.basename(engine.directory)
|
|
12
|
-
user = options[:user] || app
|
|
13
|
-
log_root = options[:log] || "/var/log/#{app}"
|
|
14
|
-
template_root = options[:template]
|
|
6
|
+
def export
|
|
7
|
+
super
|
|
15
8
|
|
|
16
9
|
Dir["#{location}/#{app}*.conf"].each do |file|
|
|
17
|
-
|
|
18
|
-
FileUtils.rm(file)
|
|
10
|
+
clean file
|
|
19
11
|
end
|
|
20
12
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
master_template = export_template("upstart", "master.conf.erb", template_root)
|
|
24
|
-
master_config = ERB.new(master_template).result(binding)
|
|
25
|
-
write_file "#{location}/#{app}.conf", master_config
|
|
13
|
+
write_template "upstart/master.conf.erb", "#{app}.conf", binding
|
|
26
14
|
|
|
27
|
-
|
|
15
|
+
engine.each_process do |name, process|
|
|
16
|
+
next if engine.formation[name] < 1
|
|
17
|
+
write_template "upstart/process_master.conf.erb", "#{app}-#{name}.conf", binding
|
|
28
18
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
process_master_config = ERB.new(process_master_template).result(binding)
|
|
33
|
-
write_file "#{location}/#{app}-#{process.name}.conf", process_master_config
|
|
34
|
-
|
|
35
|
-
1.upto(concurrency[process.name]) do |num|
|
|
36
|
-
port = engine.port_for(process, num, options[:port])
|
|
37
|
-
process_config = ERB.new(process_template).result(binding)
|
|
38
|
-
write_file "#{location}/#{app}-#{process.name}-#{num}.conf", process_config
|
|
19
|
+
1.upto(engine.formation[name]) do |num|
|
|
20
|
+
port = engine.port_for(process, num)
|
|
21
|
+
write_template "upstart/process.conf.erb", "#{app}-#{name}-#{num}.conf", binding
|
|
39
22
|
end
|
|
40
23
|
end
|
|
41
24
|
end
|
|
42
|
-
|
|
43
25
|
end
|
data/lib/foreman/export.rb
CHANGED
|
@@ -1,7 +1,28 @@
|
|
|
1
1
|
require "foreman"
|
|
2
|
+
require "foreman/helpers"
|
|
3
|
+
require "pathname"
|
|
2
4
|
|
|
3
5
|
module Foreman::Export
|
|
6
|
+
extend Foreman::Helpers
|
|
7
|
+
|
|
4
8
|
class Exception < ::Exception; end
|
|
9
|
+
|
|
10
|
+
def self.formatter(format)
|
|
11
|
+
begin
|
|
12
|
+
require "foreman/export/#{ format.tr('-', '_') }"
|
|
13
|
+
classy_format = classify(format)
|
|
14
|
+
formatter = constantize("Foreman::Export::#{ classy_format }")
|
|
15
|
+
rescue NameError => ex
|
|
16
|
+
error "Unknown export format: #{format} (no class Foreman::Export::#{ classy_format })."
|
|
17
|
+
rescue LoadError => ex
|
|
18
|
+
error "Unknown export format: #{format} (unable to load file 'foreman/export/#{ format.tr('-', '_') }')."
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.error(message)
|
|
23
|
+
raise Foreman::Export::Exception.new(message)
|
|
24
|
+
end
|
|
25
|
+
|
|
5
26
|
end
|
|
6
27
|
|
|
7
28
|
require "foreman/export/base"
|
|
@@ -9,3 +30,5 @@ require "foreman/export/inittab"
|
|
|
9
30
|
require "foreman/export/upstart"
|
|
10
31
|
require "foreman/export/bluepill"
|
|
11
32
|
require "foreman/export/runit"
|
|
33
|
+
require "foreman/export/supervisord"
|
|
34
|
+
require "foreman/export/launchd"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Foreman::Helpers
|
|
2
|
+
# Copied whole sale from, https://github.com/defunkt/resque/
|
|
3
|
+
|
|
4
|
+
# Given a word with dashes, returns a camel cased version of it.
|
|
5
|
+
#
|
|
6
|
+
# classify('job-name') # => 'JobName'
|
|
7
|
+
def classify(dashed_word)
|
|
8
|
+
dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
|
|
9
|
+
end # Tries to find a constant with the name specified in the argument string:
|
|
10
|
+
|
|
11
|
+
#
|
|
12
|
+
# constantize("Module") # => Module
|
|
13
|
+
# constantize("Test::Unit") # => Test::Unit
|
|
14
|
+
#
|
|
15
|
+
# The name is assumed to be the one of a top-level constant, no matter
|
|
16
|
+
# whether it starts with "::" or not. No lexical context is taken into
|
|
17
|
+
# account:
|
|
18
|
+
#
|
|
19
|
+
# C = 'outside'
|
|
20
|
+
# module M
|
|
21
|
+
# C = 'inside'
|
|
22
|
+
# C # => 'inside'
|
|
23
|
+
# constantize("C") # => 'outside', same as ::C
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# NameError is raised when the constant is unknown.
|
|
27
|
+
def constantize(camel_cased_word)
|
|
28
|
+
camel_cased_word = camel_cased_word.to_s
|
|
29
|
+
|
|
30
|
+
names = camel_cased_word.split('::')
|
|
31
|
+
names.shift if names.empty? || names.first.empty?
|
|
32
|
+
|
|
33
|
+
constant = Object
|
|
34
|
+
names.each do |name|
|
|
35
|
+
args = Module.method(:const_get).arity != 1 ? [false] : []
|
|
36
|
+
|
|
37
|
+
if constant.const_defined?(name, *args)
|
|
38
|
+
constant = constant.const_get(name)
|
|
39
|
+
else
|
|
40
|
+
constant = constant.const_missing(name)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
constant
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/foreman/process.rb
CHANGED
|
@@ -1,68 +1,102 @@
|
|
|
1
1
|
require "foreman"
|
|
2
|
+
require "rubygems"
|
|
2
3
|
|
|
3
4
|
class Foreman::Process
|
|
4
5
|
|
|
5
|
-
attr_reader :
|
|
6
|
-
attr_reader :
|
|
7
|
-
attr_reader :pid
|
|
8
|
-
attr_reader :port
|
|
6
|
+
attr_reader :command
|
|
7
|
+
attr_reader :env
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
# Create a Process
|
|
10
|
+
#
|
|
11
|
+
# @param [String] command The command to run
|
|
12
|
+
# @param [Hash] options
|
|
13
|
+
#
|
|
14
|
+
# @option options [String] :cwd (./) Change to this working directory before executing the process
|
|
15
|
+
# @option options [Hash] :env ({}) Environment variables to set for this process
|
|
16
|
+
#
|
|
17
|
+
def initialize(command, options={})
|
|
18
|
+
@command = command
|
|
19
|
+
@options = options.dup
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
Dir.chdir(basedir) do
|
|
18
|
-
with_environment(environment.merge("PORT" => port.to_s)) do
|
|
19
|
-
run_process entry.command, pipe
|
|
20
|
-
end
|
|
21
|
-
end
|
|
21
|
+
@options[:env] ||= {}
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
# Run a +Process+
|
|
25
|
+
#
|
|
26
|
+
# @param [Hash] options
|
|
27
|
+
#
|
|
28
|
+
# @option options :env ({}) Environment variables to set for this execution
|
|
29
|
+
# @option options :output ($stdout) The output stream
|
|
30
|
+
#
|
|
31
|
+
# @returns [Fixnum] pid The +pid+ of the process
|
|
32
|
+
#
|
|
33
|
+
def run(options={})
|
|
34
|
+
env = options[:env] ? @options[:env].merge(options[:env]) : @options[:env]
|
|
35
|
+
output = options[:output] || $stdout
|
|
27
36
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
if Foreman.windows?
|
|
38
|
+
Dir.chdir(cwd) do
|
|
39
|
+
expanded_command = command.dup
|
|
40
|
+
env.each do |key, val|
|
|
41
|
+
expanded_command.gsub!("$#{key}", val)
|
|
42
|
+
end
|
|
43
|
+
Process.spawn env, expanded_command, :out => output, :err => output
|
|
44
|
+
end
|
|
45
|
+
elsif Foreman.jruby?
|
|
46
|
+
Dir.chdir(cwd) do
|
|
47
|
+
require "posix/spawn"
|
|
48
|
+
POSIX::Spawn.spawn env, command, :out => output, :err => output
|
|
49
|
+
end
|
|
50
|
+
elsif Foreman.ruby_18?
|
|
51
|
+
Dir.chdir(cwd) do
|
|
52
|
+
fork do
|
|
53
|
+
$stdout.reopen output
|
|
54
|
+
$stderr.reopen output
|
|
55
|
+
env.each { |k,v| ENV[k] = v }
|
|
56
|
+
exec command
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
else
|
|
60
|
+
Dir.chdir(cwd) do
|
|
61
|
+
Process.spawn env, command, :out => output, :err => output
|
|
62
|
+
end
|
|
37
63
|
end
|
|
38
|
-
[ reader, pid ]
|
|
39
64
|
end
|
|
40
65
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
66
|
+
# Send a signal to this +Process+
|
|
67
|
+
#
|
|
68
|
+
# @param [String] signal The signal to send
|
|
69
|
+
#
|
|
70
|
+
def kill(signal)
|
|
71
|
+
if Foreman.windows?
|
|
72
|
+
pid && Process.kill(signal, pid)
|
|
73
|
+
else
|
|
74
|
+
pid && Process.kill("-#{signal}", pid)
|
|
48
75
|
end
|
|
76
|
+
rescue Errno::ESRCH
|
|
77
|
+
false
|
|
49
78
|
end
|
|
50
79
|
|
|
51
|
-
|
|
52
|
-
|
|
80
|
+
# Test whether or not this +Process+ is still running
|
|
81
|
+
#
|
|
82
|
+
# @returns [Boolean]
|
|
83
|
+
#
|
|
84
|
+
def alive?
|
|
85
|
+
kill(0)
|
|
53
86
|
end
|
|
54
87
|
|
|
55
|
-
|
|
56
|
-
|
|
88
|
+
# Test whether or not this +Process+ has terminated
|
|
89
|
+
#
|
|
90
|
+
# @returns [Boolean]
|
|
91
|
+
#
|
|
92
|
+
def dead?
|
|
93
|
+
!alive?
|
|
57
94
|
end
|
|
58
95
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
ENV.clear
|
|
64
|
-
old_env.each { |k,v| ENV[k] = v}
|
|
65
|
-
ret
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def cwd
|
|
99
|
+
@options[:cwd] || "."
|
|
66
100
|
end
|
|
67
101
|
|
|
68
102
|
end
|
data/lib/foreman/procfile.rb
CHANGED
|
@@ -1,36 +1,90 @@
|
|
|
1
1
|
require "foreman"
|
|
2
|
-
require "foreman/procfile_entry"
|
|
3
2
|
|
|
4
|
-
#
|
|
5
|
-
#
|
|
3
|
+
# Reads and writes Procfiles
|
|
4
|
+
#
|
|
5
|
+
# A valid Procfile entry is captured by this regex:
|
|
6
6
|
#
|
|
7
|
-
#
|
|
7
|
+
# /^([A-Za-z0-9_]+):\s*(.+)$/
|
|
8
8
|
#
|
|
9
|
-
#
|
|
10
|
-
# $2 = command
|
|
9
|
+
# All other lines are ignored.
|
|
11
10
|
#
|
|
12
11
|
class Foreman::Procfile
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
# Initialize a Procfile
|
|
14
|
+
#
|
|
15
|
+
# @param [String] filename (nil) An optional filename to read from
|
|
16
|
+
#
|
|
17
|
+
def initialize(filename=nil)
|
|
18
|
+
@entries = []
|
|
19
|
+
load(filename) if filename
|
|
20
|
+
end
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
|
|
22
|
+
# Yield each +Procfile+ entry in order
|
|
23
|
+
#
|
|
24
|
+
def entries(&blk)
|
|
25
|
+
@entries.each do |(name, command)|
|
|
26
|
+
yield name, command
|
|
27
|
+
end
|
|
18
28
|
end
|
|
19
29
|
|
|
30
|
+
# Retrieve a +Procfile+ command by name
|
|
31
|
+
#
|
|
32
|
+
# @param [String] name The name of the Procfile entry to retrieve
|
|
33
|
+
#
|
|
20
34
|
def [](name)
|
|
21
|
-
entries.detect { |
|
|
35
|
+
@entries.detect { |n,c| name == n }.last
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Create a +Procfile+ entry
|
|
39
|
+
#
|
|
40
|
+
# @param [String] name The name of the +Procfile+ entry to create
|
|
41
|
+
# @param [String] command The command of the +Procfile+ entry to create
|
|
42
|
+
#
|
|
43
|
+
def []=(name, command)
|
|
44
|
+
delete name
|
|
45
|
+
@entries << [name, command]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Remove a +Procfile+ entry
|
|
49
|
+
#
|
|
50
|
+
# @param [String] name The name of the +Procfile+ entry to remove
|
|
51
|
+
#
|
|
52
|
+
def delete(name)
|
|
53
|
+
@entries.reject! { |n,c| name == n }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Load a Procfile from a file
|
|
57
|
+
#
|
|
58
|
+
# @param [String] filename The filename of the +Procfile+ to load
|
|
59
|
+
#
|
|
60
|
+
def load(filename)
|
|
61
|
+
@entries.replace parse(filename)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Save a Procfile to a file
|
|
65
|
+
#
|
|
66
|
+
# @param [String] filename Save the +Procfile+ to this file
|
|
67
|
+
#
|
|
68
|
+
def save(filename)
|
|
69
|
+
File.open(filename, 'w') do |file|
|
|
70
|
+
file.puts self.to_s
|
|
71
|
+
end
|
|
22
72
|
end
|
|
23
73
|
|
|
24
|
-
|
|
25
|
-
|
|
74
|
+
# Get the +Procfile+ as a +String+
|
|
75
|
+
#
|
|
76
|
+
def to_s
|
|
77
|
+
@entries.map do |name, command|
|
|
78
|
+
[ name, command ].join(": ")
|
|
79
|
+
end.join("\n")
|
|
26
80
|
end
|
|
27
81
|
|
|
28
82
|
private
|
|
29
83
|
|
|
30
|
-
def
|
|
84
|
+
def parse(filename)
|
|
31
85
|
File.read(filename).split("\n").map do |line|
|
|
32
86
|
if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
|
|
33
|
-
|
|
87
|
+
[$1, $2]
|
|
34
88
|
end
|
|
35
89
|
end.compact
|
|
36
90
|
end
|
data/lib/foreman/version.rb
CHANGED
data/lib/foreman.rb
CHANGED
|
@@ -4,15 +4,20 @@ module Foreman
|
|
|
4
4
|
|
|
5
5
|
class AppDoesNotExist < Exception; end
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
require 'foreman/engine'
|
|
10
|
-
Foreman::Engine.load_env!(env_file)
|
|
7
|
+
def self.runner
|
|
8
|
+
File.expand_path("../../bin/foreman-runner", __FILE__)
|
|
11
9
|
end
|
|
12
10
|
|
|
13
|
-
def self.
|
|
14
|
-
|
|
11
|
+
def self.jruby?
|
|
12
|
+
defined?(RUBY_PLATFORM) and RUBY_PLATFORM == "java"
|
|
15
13
|
end
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
def self.ruby_18?
|
|
16
|
+
defined?(RUBY_VERSION) and RUBY_VERSION =~ /^1\.8\.\d+/
|
|
17
|
+
end
|
|
18
18
|
|
|
19
|
+
def self.windows?
|
|
20
|
+
defined?(RUBY_PLATFORM) and RUBY_PLATFORM =~ /(win|w)32$/
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
data/man/foreman.1
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
.\" generated with Ronn/v0.7.3
|
|
2
2
|
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
|
3
3
|
.
|
|
4
|
-
.TH "FOREMAN" "1" "
|
|
4
|
+
.TH "FOREMAN" "1" "July 2012" "Foreman 0.50.0" "Foreman Manual"
|
|
5
5
|
.
|
|
6
6
|
.SH "NAME"
|
|
7
7
|
\fBforeman\fR \- manage Procfile\-based applications
|
|
@@ -10,10 +10,13 @@
|
|
|
10
10
|
\fBforeman start [process]\fR
|
|
11
11
|
.
|
|
12
12
|
.br
|
|
13
|
+
\fBforeman run <command>\fR
|
|
14
|
+
.
|
|
15
|
+
.br
|
|
13
16
|
\fBforeman export <format> [location]\fR
|
|
14
17
|
.
|
|
15
18
|
.SH "DESCRIPTION"
|
|
16
|
-
|
|
19
|
+
Foreman is a manager for Procfile\-based applications\. Its aim is to abstract away the details of the Procfile format, and allow you to either run your application directly or export it to some other process management format\.
|
|
17
20
|
.
|
|
18
21
|
.SH "RUNNING"
|
|
19
22
|
\fBforeman start\fR is used to run your application directly from the command line\.
|
|
@@ -32,9 +35,24 @@ The following options control how the application is run:
|
|
|
32
35
|
Specify the number of each process type to run\. The value passed in should be in the format \fBprocess=num,process=num\fR
|
|
33
36
|
.
|
|
34
37
|
.TP
|
|
38
|
+
\fB\-e\fR, \fB\-\-env\fR
|
|
39
|
+
Specify one or more \.env files to load
|
|
40
|
+
.
|
|
41
|
+
.TP
|
|
42
|
+
\fB\-f\fR, \fB\-\-procfile\fR
|
|
43
|
+
Specify an alternate Procfile to load, implies \fB\-d\fR at the Procfile root\.
|
|
44
|
+
.
|
|
45
|
+
.TP
|
|
35
46
|
\fB\-p\fR, \fB\-\-port\fR
|
|
36
47
|
Specify which port to use as the base for this application\. Should be a multiple of 1000\.
|
|
37
48
|
.
|
|
49
|
+
.TP
|
|
50
|
+
\fB\-t\fR, \fB\-\-tmux\fR
|
|
51
|
+
Runs the processes in a tmux session\. Creates one window for each process and an extra window containing the output of each window (requires gawk)\.
|
|
52
|
+
.
|
|
53
|
+
.P
|
|
54
|
+
\fBforeman run\fR is used to run one\-off commands using the same environment as your defined processes\.
|
|
55
|
+
.
|
|
38
56
|
.SH "EXPORTING"
|
|
39
57
|
\fBforeman export\fR is used to export your application to another process management format\.
|
|
40
58
|
.
|
|
@@ -61,13 +79,21 @@ Specify the directory to place process logs in\.
|
|
|
61
79
|
Specify which port to use as the base for this application\. Should be a multiple of 1000\.
|
|
62
80
|
.
|
|
63
81
|
.TP
|
|
82
|
+
\fB\-t\fR, \fB\-\-template\fR
|
|
83
|
+
Specify an alternate template to use for creating export files\. See \fIhttps://github\.com/ddollar/foreman/tree/master/data/export\fR for examples\.
|
|
84
|
+
.
|
|
85
|
+
.TP
|
|
64
86
|
\fB\-u\fR, \fB\-\-user\fR
|
|
65
87
|
Specify the user the application should be run as\. Defaults to the app name
|
|
66
88
|
.
|
|
67
|
-
.SH "OPTIONS"
|
|
89
|
+
.SH "GLOBAL OPTIONS"
|
|
68
90
|
These options control all modes of foreman\'s operation\.
|
|
69
91
|
.
|
|
70
92
|
.TP
|
|
93
|
+
\fB\-d\fR, \fB\-\-directory\fR
|
|
94
|
+
Specify an alternate application root\. This defaults to the directory containing the Procfile\.
|
|
95
|
+
.
|
|
96
|
+
.TP
|
|
71
97
|
\fB\-e\fR, \fB\-\-env\fR
|
|
72
98
|
Specify an alternate environment file\. You can specify more than one file by using: \fB\-\-env file1,file2\fR\.
|
|
73
99
|
.
|