foreman 0.47.0 → 0.48.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/taskman +8 -0
- data/data/example/Procfile +4 -3
- data/data/example/spawnee +14 -0
- data/data/example/spawner +7 -0
- data/data/export/bluepill/master.pill.erb +10 -10
- data/data/export/launchd/launchd.plist.erb +3 -3
- data/data/export/runit/log/run.erb +7 -0
- data/data/export/runit/run.erb +2 -2
- data/data/export/supervisord/app.conf.erb +12 -12
- data/data/export/upstart/master.conf.erb +2 -2
- data/data/export/upstart/process.conf.erb +3 -3
- data/lib/foreman/cli.rb +49 -21
- data/lib/foreman/engine.rb +208 -148
- data/lib/foreman/engine/cli.rb +98 -0
- data/lib/foreman/env.rb +27 -0
- data/lib/foreman/export.rb +0 -1
- data/lib/foreman/export/base.rb +58 -35
- data/lib/foreman/export/bluepill.rb +3 -17
- data/lib/foreman/export/inittab.rb +8 -11
- data/lib/foreman/export/launchd.rb +4 -16
- data/lib/foreman/export/runit.rb +14 -39
- data/lib/foreman/export/supervisord.rb +3 -13
- data/lib/foreman/export/upstart.rb +9 -27
- data/lib/foreman/process.rb +56 -67
- data/lib/foreman/procfile.rb +59 -25
- data/lib/foreman/version.rb +1 -1
- data/man/foreman.1 +4 -0
- data/spec/foreman/cli_spec.rb +38 -152
- data/spec/foreman/engine_spec.rb +46 -80
- data/spec/foreman/export/base_spec.rb +4 -7
- data/spec/foreman/export/bluepill_spec.rb +7 -6
- data/spec/foreman/export/inittab_spec.rb +7 -7
- data/spec/foreman/export/launchd_spec.rb +4 -7
- data/spec/foreman/export/runit_spec.rb +12 -17
- data/spec/foreman/export/supervisord_spec.rb +7 -56
- data/spec/foreman/export/upstart_spec.rb +18 -23
- data/spec/foreman/process_spec.rb +27 -124
- data/spec/foreman/procfile_spec.rb +26 -16
- 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/export/bluepill/app-concurrency.pill +4 -4
- data/spec/resources/export/bluepill/app.pill +4 -4
- 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
- data/spec/resources/export/supervisord/app-alpha-1.conf +24 -0
- data/spec/resources/export/supervisord/app-alpha-2.conf +4 -4
- data/spec/spec_helper.rb +58 -6
- metadata +24 -22
- data/data/export/runit/log_run.erb +0 -7
- data/lib/foreman/color.rb +0 -40
- data/lib/foreman/procfile_entry.rb +0 -26
- data/lib/foreman/utils.rb +0 -18
- data/spec/foreman/color_spec.rb +0 -31
- data/spec/foreman/procfile_entry_spec.rb +0 -13
- data/spec/resources/export/supervisord/app-env-with-comma.conf +0 -24
- data/spec/resources/export/supervisord/app-env.conf +0 -21
- data/spec/resources/export/supervisord/app.conf +0 -24
@@ -4,40 +4,22 @@ require "foreman/export"
|
|
4
4
|
class Foreman::Export::Upstart < Foreman::Export::Base
|
5
5
|
|
6
6
|
def export
|
7
|
-
|
8
|
-
|
9
|
-
FileUtils.mkdir_p location
|
10
|
-
|
11
|
-
app = self.app || File.basename(engine.directory)
|
12
|
-
user = self.user || app
|
13
|
-
log_root = self.log || "/var/log/#{app}"
|
14
|
-
template_root = self.template
|
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
|
-
master_config = ERB.new(master_template).result(binding)
|
23
|
-
write_file "#{location}/#{app}.conf", master_config
|
13
|
+
write_template "upstart/master.conf.erb", "#{app}.conf", binding
|
24
14
|
|
25
|
-
|
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
|
26
18
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
process_master_config = ERB.new(process_master_template).result(binding)
|
31
|
-
write_file "#{location}/#{app}-#{process.name}.conf", process_master_config
|
32
|
-
|
33
|
-
1.upto(self.concurrency[process.name]) do |num|
|
34
|
-
port = engine.port_for(process, num, self.port)
|
35
|
-
process_config = ERB.new(process_template).result(binding)
|
36
|
-
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
|
37
22
|
end
|
38
23
|
end
|
39
|
-
|
40
|
-
FileUtils.mkdir_p(log_root) rescue error "could not create #{log_root}"
|
41
|
-
FileUtils.chown(user, nil, log_root) rescue error "could not chown #{log_root} to #{user}"
|
42
24
|
end
|
43
25
|
end
|
data/lib/foreman/process.rb
CHANGED
@@ -3,94 +3,83 @@ require "rubygems"
|
|
3
3
|
|
4
4
|
class Foreman::Process
|
5
5
|
|
6
|
-
attr_reader :
|
7
|
-
attr_reader :
|
8
|
-
attr_reader :pid
|
9
|
-
attr_reader :port
|
6
|
+
attr_reader :command
|
7
|
+
attr_reader :env
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
16
20
|
|
17
|
-
|
18
|
-
with_environment(environment.merge("PORT" => port.to_s)) do
|
19
|
-
run_process basedir, entry.command, pipe
|
20
|
-
end
|
21
|
+
@options[:env] ||= {}
|
21
22
|
end
|
22
23
|
|
23
|
-
|
24
|
-
|
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
|
36
|
+
|
37
|
+
if Foreman.windows?
|
38
|
+
Dir.chdir(cwd) do
|
39
|
+
Process.spawn env, command, :out => output, :err => output, :new_pgroup => true
|
40
|
+
end
|
41
|
+
elsif Foreman.jruby?
|
42
|
+
Dir.chdir(cwd) do
|
43
|
+
require "posix/spawn"
|
44
|
+
POSIX::Spawn.spawn env, command, :out => output, :err => output, :pgroup => 0
|
45
|
+
end
|
46
|
+
else
|
47
|
+
Dir.chdir(cwd) do
|
48
|
+
Process.spawn env, command, :out => output, :err => output, :pgroup => 0
|
49
|
+
end
|
50
|
+
end
|
25
51
|
end
|
26
52
|
|
53
|
+
# Send a signal to this +Process+
|
54
|
+
#
|
55
|
+
# @param [String] signal The signal to send
|
56
|
+
#
|
27
57
|
def kill(signal)
|
28
|
-
pid && Process.kill(signal, pid)
|
58
|
+
pid && Process.kill(signal, -1 * pid)
|
29
59
|
rescue Errno::ESRCH
|
30
60
|
false
|
31
61
|
end
|
32
62
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
63
|
+
# Test whether or not this +Process+ is still running
|
64
|
+
#
|
65
|
+
# @returns [Boolean]
|
66
|
+
#
|
37
67
|
def alive?
|
38
68
|
kill(0)
|
39
69
|
end
|
40
70
|
|
71
|
+
# Test whether or not this +Process+ has terminated
|
72
|
+
#
|
73
|
+
# @returns [Boolean]
|
74
|
+
#
|
41
75
|
def dead?
|
42
76
|
!alive?
|
43
77
|
end
|
44
78
|
|
45
79
|
private
|
46
80
|
|
47
|
-
def
|
48
|
-
|
49
|
-
command = replace_command_env(command)
|
50
|
-
pid = if Foreman.windows?
|
51
|
-
Dir.chdir(basedir) do
|
52
|
-
Process.spawn command, :out => writer, :err => writer
|
53
|
-
end
|
54
|
-
elsif Foreman.jruby?
|
55
|
-
require "posix/spawn"
|
56
|
-
POSIX::Spawn.spawn(Foreman.runner, "-d", basedir, command, {
|
57
|
-
:out => writer, :err => writer
|
58
|
-
})
|
59
|
-
else
|
60
|
-
fork do
|
61
|
-
writer.sync = true
|
62
|
-
$stdout.reopen writer
|
63
|
-
$stderr.reopen writer
|
64
|
-
reader.close
|
65
|
-
exec Foreman.runner, "-d", basedir, *command.shellsplit
|
66
|
-
end
|
67
|
-
end
|
68
|
-
[ reader, pid ]
|
69
|
-
end
|
70
|
-
|
71
|
-
def run_process(basedir, command, pipe)
|
72
|
-
io, @pid = fork_with_io(command, basedir)
|
73
|
-
output pipe, "started with pid %d" % @pid
|
74
|
-
Thread.new do
|
75
|
-
until io.eof?
|
76
|
-
output pipe, io.gets
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def output(pipe, message)
|
82
|
-
pipe.puts "%s,%s" % [ name, message ]
|
81
|
+
def cwd
|
82
|
+
@options[:cwd] || "."
|
83
83
|
end
|
84
84
|
|
85
|
-
def replace_command_env(command)
|
86
|
-
command.gsub(/\$(\w+)/) { |e| ENV[e[1..-1]] }
|
87
|
-
end
|
88
|
-
|
89
|
-
def with_environment(environment)
|
90
|
-
original = ENV.to_hash
|
91
|
-
ENV.update environment
|
92
|
-
yield
|
93
|
-
ensure
|
94
|
-
ENV.replace original
|
95
|
-
end
|
96
85
|
end
|
data/lib/foreman/procfile.rb
CHANGED
@@ -1,56 +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
|
-
|
15
|
-
|
13
|
+
# Initialize a Procfile
|
14
|
+
#
|
15
|
+
# @param [String] filename (nil) An optional filename to read from
|
16
|
+
#
|
16
17
|
def initialize(filename=nil)
|
17
18
|
@entries = []
|
18
19
|
load(filename) if filename
|
19
20
|
end
|
20
21
|
|
22
|
+
# Yield each +Procfile+ entry in order
|
23
|
+
#
|
24
|
+
def entries(&blk)
|
25
|
+
@entries.each do |(name, command)|
|
26
|
+
yield name, command
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Retrieve a +Procfile+ command by name
|
31
|
+
#
|
32
|
+
# @param [String] name The name of the Procfile entry to retrieve
|
33
|
+
#
|
21
34
|
def [](name)
|
22
|
-
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]
|
23
46
|
end
|
24
47
|
|
25
|
-
|
26
|
-
|
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 }
|
27
54
|
end
|
28
55
|
|
56
|
+
# Load a Procfile from a file
|
57
|
+
#
|
58
|
+
# @param [String] filename The filename of the +Procfile+ to load
|
59
|
+
#
|
29
60
|
def load(filename)
|
30
|
-
entries.
|
31
|
-
parse_procfile(filename)
|
61
|
+
@entries.replace parse(filename)
|
32
62
|
end
|
33
63
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
39
71
|
end
|
40
72
|
end
|
41
73
|
|
42
|
-
|
43
|
-
|
44
|
-
|
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")
|
45
80
|
end
|
46
81
|
|
82
|
+
private
|
47
83
|
|
48
|
-
|
49
|
-
|
50
|
-
def parse_procfile(filename)
|
84
|
+
def parse(filename)
|
51
85
|
File.read(filename).split("\n").map do |line|
|
52
86
|
if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
|
53
|
-
|
87
|
+
[$1, $2]
|
54
88
|
end
|
55
89
|
end.compact
|
56
90
|
end
|
data/lib/foreman/version.rb
CHANGED
data/man/foreman.1
CHANGED
@@ -46,6 +46,10 @@ Specify an alternate Procfile to load, implies \fB\-d\fR at the Procfile root\.
|
|
46
46
|
\fB\-p\fR, \fB\-\-port\fR
|
47
47
|
Specify which port to use as the base for this application\. Should be a multiple of 1000\.
|
48
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
|
+
.
|
49
53
|
.P
|
50
54
|
\fBforeman run\fR is used to run one\-off commands using the same environment as your defined processes\.
|
51
55
|
.
|
data/spec/foreman/cli_spec.rb
CHANGED
@@ -3,188 +3,74 @@ require "foreman/cli"
|
|
3
3
|
|
4
4
|
describe "Foreman::CLI", :fakefs do
|
5
5
|
subject { Foreman::CLI.new }
|
6
|
-
let(:engine) { subject.send(:engine) }
|
7
|
-
let(:entries) { engine.procfile.entries.inject({}) { |h,e| h.update(e.name => e) } }
|
8
6
|
|
9
|
-
describe "
|
10
|
-
|
11
|
-
it "prints an error" do
|
12
|
-
mock_error(subject, "Procfile does not exist.") do
|
13
|
-
dont_allow.instance_of(Foreman::Engine).start
|
14
|
-
subject.start
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
7
|
+
describe ".foreman" do
|
8
|
+
before { File.open(".foreman", "w") { |f| f.puts "formation: alpha=2" } }
|
18
9
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
it "runs successfully" do
|
23
|
-
dont_allow(subject).error
|
24
|
-
mock.instance_of(Foreman::Engine).start
|
25
|
-
subject.start
|
26
|
-
end
|
27
|
-
|
28
|
-
it "can run a single process" do
|
29
|
-
dont_allow(subject).error
|
30
|
-
stub(engine).watch_for_output
|
31
|
-
stub(engine).watch_for_termination
|
32
|
-
mock(entries["alpha"]).spawn(1, is_a(IO), engine.directory, {}, 5000) { [] }
|
33
|
-
mock(entries["bravo"]).spawn(0, is_a(IO), engine.directory, {}, 5100) { [] }
|
34
|
-
subject.start("alpha")
|
35
|
-
end
|
10
|
+
it "provides default options" do
|
11
|
+
subject.send(:options)["formation"].should == "alpha=2"
|
36
12
|
end
|
37
13
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
mock(Foreman::Procfile).new("/some/app/Procfile")
|
42
|
-
mock.instance_of(Foreman::Engine).start
|
43
|
-
foreman %{ start -d /some/app }
|
44
|
-
end
|
14
|
+
it "is overridden by options at the cli" do
|
15
|
+
subject = Foreman::CLI.new([], :formation => "alpha=3")
|
16
|
+
subject.send(:options)["formation"].should == "alpha=3"
|
45
17
|
end
|
46
18
|
end
|
47
19
|
|
48
|
-
describe "
|
49
|
-
describe "
|
50
|
-
it "
|
51
|
-
write_procfile
|
52
|
-
File.open(".foreman", "w") { |f| f.puts "concurrency: alpha=2" }
|
53
|
-
mock_export = mock(Foreman::Export::Upstart)
|
54
|
-
mock(Foreman::Export::Upstart).new("/upstart", is_a(Foreman::Engine), { "concurrency" => "alpha=2" }) { mock_export }
|
55
|
-
mock_export.export
|
56
|
-
foreman %{ export upstart /upstart }
|
57
|
-
end
|
58
|
-
|
59
|
-
it "respects --env" do
|
60
|
-
write_procfile
|
61
|
-
write_env("envfile")
|
62
|
-
mock_export = mock(Foreman::Export::Upstart)
|
63
|
-
mock(Foreman::Export::Upstart).new("/upstart", is_a(Foreman::Engine), { "env" => "envfile" }) { mock_export }
|
64
|
-
mock_export.export
|
65
|
-
foreman %{ export upstart /upstart --env envfile }
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
describe "with a non-existent Procfile" do
|
70
|
-
it "prints an error" do
|
20
|
+
describe "start" do
|
21
|
+
describe "when a Procfile doesnt exist", :fakefs do
|
22
|
+
it "displays an error" do
|
71
23
|
mock_error(subject, "Procfile does not exist.") do
|
72
|
-
dont_allow.instance_of(Foreman::Engine).
|
73
|
-
subject.
|
24
|
+
dont_allow.instance_of(Foreman::Engine).start
|
25
|
+
subject.start
|
74
26
|
end
|
75
27
|
end
|
76
28
|
end
|
77
29
|
|
78
|
-
describe "with a Procfile" do
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
def export
|
85
|
-
raise Foreman::Export::Exception.new("foo")
|
86
|
-
end
|
87
|
-
end }
|
88
|
-
end
|
89
|
-
|
90
|
-
it "prints an error" do
|
91
|
-
mock_error(subject, "foo") do
|
92
|
-
subject.export("errorful")
|
93
|
-
end
|
30
|
+
describe "with a valid Procfile" do
|
31
|
+
it "can run a single command" do
|
32
|
+
without_fakefs do
|
33
|
+
output = foreman("start env -f #{resource_path("Procfile")}")
|
34
|
+
output.should =~ /env.1/
|
35
|
+
output.should_not =~ /test.1/
|
94
36
|
end
|
95
37
|
end
|
96
38
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
mock(Foreman::Export::Upstart).new("/tmp/foo", is_a(Foreman::Engine), {}) { mock_export }
|
104
|
-
mock_export.export
|
105
|
-
subject.export("upstart", "/tmp/foo")
|
39
|
+
it "can run all commands" do
|
40
|
+
without_fakefs do
|
41
|
+
output = foreman("start -f #{resource_path("Procfile")} -e #{resource_path(".env")}")
|
42
|
+
output.should =~ /echo.1 \| echoing/
|
43
|
+
output.should =~ /env.1 \| bar/
|
44
|
+
output.should =~ /test.1 \| testing/
|
106
45
|
end
|
107
46
|
end
|
108
47
|
end
|
109
48
|
end
|
110
49
|
|
111
50
|
describe "check" do
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
it "displays the jobs" do
|
116
|
-
mock(subject).puts("valid procfile detected (alpha, bravo)")
|
117
|
-
subject.check
|
118
|
-
end
|
51
|
+
it "with a valid Procfile displays the jobs" do
|
52
|
+
write_procfile
|
53
|
+
foreman("check").should == "valid procfile detected (alpha, bravo)\n"
|
119
54
|
end
|
120
55
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
125
|
-
|
126
|
-
it "displays an error" do
|
127
|
-
mock_error(subject, "no processes defined") do
|
128
|
-
subject.check
|
129
|
-
end
|
130
|
-
end
|
56
|
+
it "with a blank Procfile displays an error" do
|
57
|
+
FileUtils.touch "Procfile"
|
58
|
+
foreman("check").should == "ERROR: no processes defined\n"
|
131
59
|
end
|
132
60
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
subject.check
|
137
|
-
end
|
138
|
-
end
|
61
|
+
it "without a Procfile displays an error" do
|
62
|
+
FileUtils.rm_f "Procfile"
|
63
|
+
foreman("check").should == "ERROR: Procfile does not exist.\n"
|
139
64
|
end
|
140
65
|
end
|
141
66
|
|
142
67
|
describe "run" do
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
describe "and a command" do
|
147
|
-
let(:command) { ["ls", "-l", "foo bar"] }
|
148
|
-
|
149
|
-
before(:each) do
|
150
|
-
stub(subject).exec
|
151
|
-
end
|
152
|
-
|
153
|
-
it "should load the environment file" do
|
154
|
-
write_env
|
155
|
-
preserving_env do
|
156
|
-
subject.run *command
|
157
|
-
ENV["FOO"].should == "bar"
|
158
|
-
end
|
159
|
-
|
160
|
-
ENV["FOO"].should be_nil
|
161
|
-
end
|
162
|
-
|
163
|
-
it "should exec the argument list as a shell command" do
|
164
|
-
mock(subject).exec(command.shelljoin)
|
165
|
-
subject.run *command
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
describe "and a non-existent command" do
|
170
|
-
let(:command) { "iuhtngrglhulhdfg" }
|
171
|
-
|
172
|
-
it "should print an error" do
|
173
|
-
mock_error(subject, "command not found: #{command}") do
|
174
|
-
subject.run command
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
describe "and a non-executable command" do
|
180
|
-
let(:command) { __FILE__ }
|
68
|
+
it "can run a command" do
|
69
|
+
forked_foreman("run echo 1").should == "1\n"
|
70
|
+
end
|
181
71
|
|
182
|
-
|
183
|
-
|
184
|
-
subject.run command
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
72
|
+
it "includes the environment" do
|
73
|
+
forked_foreman("run #{resource_path("bin/env FOO")} -e #{resource_path(".env")}").should == "bar\n"
|
188
74
|
end
|
189
75
|
end
|
190
76
|
|