foreman 0.46.0 → 0.47.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 CHANGED
@@ -27,6 +27,12 @@ Manage Procfile-based applications
27
27
  * [wiki](http://github.com/ddollar/foreman/wiki)
28
28
  * [changelog](https://github.com/ddollar/foreman/blob/master/Changelog.md)
29
29
 
30
+ ## Ports
31
+
32
+ * [shoreman](https://github.com/hecticjeff/shoreman) - shell
33
+ * [honcho](https://github.com/nickstenning/honcho) - python
34
+ * [norman](https://github.com/josh/norman) - node.js
35
+
30
36
  ## Authors
31
37
 
32
38
  #### Created and maintained by
data/bin/foreman-runner CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/bin/sh
2
2
  #
3
- #/ Usage: foreman-runner [-d <dir>] <command>
3
+ #/ Usage: foreman-runner [-d <dir>] <command> [<args>...]
4
4
  #/
5
5
  #/ Run a command with exec, optionally changing directory first
6
6
 
@@ -27,10 +27,6 @@ done
27
27
 
28
28
  shift $((OPTIND-1))
29
29
 
30
- command=$1
30
+ [ -z "$1" ] && usage
31
31
 
32
- if [ -z "$1" ]; then
33
- usage
34
- fi
35
-
36
- exec $1
32
+ exec "$@"
@@ -11,8 +11,9 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
11
11
 
12
12
  process.working_dir = "<%= engine.directory %>"
13
13
  process.daemonize = true
14
- process.environment = {"PORT" => "<%= port %>"}
14
+ process.environment = {"PORT" => "<%= port %>"<% engine.environment.each_pair do |var,env| %> , "<%= var.upcase %>" => "<%= env %>" <% end %>}
15
15
  process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
16
+ process.stop_grace_time = 45.seconds
16
17
 
17
18
  process.stdout = process.stderr = "<%= log_root %>/<%= app %>-<%= process.name %>-<%=num%>.log"
18
19
 
@@ -0,0 +1,22 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key>
6
+ <string><%= "#{app}-#{process.name}-#{num}" %></string>
7
+ <key>ProgramArguments</key>
8
+ <array>
9
+ <string><%= process.command %></string>
10
+ </array>
11
+ <key>KeepAlive</key>
12
+ <true/>
13
+ <key>RunAtLoad</key>
14
+ <true/>
15
+ <key>StandardErrorPath</key>
16
+ <string><%= log_root %>/<%= app %>-<%= process.name %>-<%=num%>.log</string>
17
+ <key>UserName</key>
18
+ <string><%= user %></string>
19
+ <key>WorkingDirectory</key>
20
+ <string><%= engine.directory %></string>
21
+ </dict>
22
+ </plist>
@@ -2,4 +2,4 @@ start on starting <%= app %>-<%= process.name %>
2
2
  stop on stopping <%= app %>-<%= process.name %>
3
3
  respawn
4
4
 
5
- exec su - <%= user %> -c 'cd <%= engine.directory %>; export PORT=<%= port %>;<% engine.environment.each_pair do |var,env| %> export <%= var.upcase %>=<%= env %>; <% end %> <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
5
+ exec su - <%= user %> -c 'cd <%= engine.directory %>; export PORT=<%= port %>;<% engine.environment.each_pair do |var,env| %> export <%= var.upcase %>=<%= shell_quote(env) %>; <% end %> <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
data/lib/foreman/cli.rb CHANGED
@@ -2,6 +2,7 @@ require "foreman"
2
2
  require "foreman/helpers"
3
3
  require "foreman/engine"
4
4
  require "foreman/export"
5
+ require "shellwords"
5
6
  require "thor"
6
7
  require "yaml"
7
8
 
@@ -59,12 +60,12 @@ class Foreman::CLI < Thor
59
60
  puts "valid procfile detected (#{engine.procfile.process_names.join(', ')})"
60
61
  end
61
62
 
62
- desc "run COMMAND", "Run a command using your application's environment"
63
+ desc "run COMMAND [ARGS...]", "Run a command using your application's environment"
63
64
 
64
65
  def run(*args)
65
66
  engine.apply_environment!
66
67
  begin
67
- exec args.join(" ")
68
+ exec args.shelljoin
68
69
  rescue Errno::EACCES
69
70
  error "not executable: #{args.first}"
70
71
  rescue Errno::ENOENT
@@ -36,6 +36,7 @@ class Foreman::Engine
36
36
 
37
37
  trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
38
38
  trap("INT") { puts "SIGINT received"; terminate_gracefully }
39
+ trap("HUP") { puts "SIGHUP received"; terminate_gracefully }
39
40
 
40
41
  assign_colors
41
42
  spawn_processes
@@ -84,7 +85,7 @@ private ######################################################################
84
85
  end
85
86
 
86
87
  def base_port
87
- options[:port] || 5000
88
+ options[:port] || environment["PORT"] || ENV["PORT"] || 5000
88
89
  end
89
90
 
90
91
  def kill_all(signal="SIGTERM")
@@ -31,4 +31,5 @@ require "foreman/export/upstart"
31
31
  require "foreman/export/bluepill"
32
32
  require "foreman/export/runit"
33
33
  require "foreman/export/supervisord"
34
+ require "foreman/export/launchd"
34
35
 
@@ -48,4 +48,19 @@ private ######################################################################
48
48
  end
49
49
  end
50
50
 
51
+ # Quote a string to be used on the command line. Backslashes are escapde to \\ and quotes
52
+ # escaped to \"
53
+ #
54
+ # str - string to be quoted
55
+ #
56
+ # Examples
57
+ #
58
+ # shell_quote("FB|123\"\\1")
59
+ # # => "\"FB|123\"\\"\\\\1\""
60
+ #
61
+ # Returns the the escaped string surrounded by quotes
62
+ def shell_quote(str)
63
+ "\"#{str.gsub(/\\/){ '\\\\' }.gsub(/["]/){ "\\\"" }}\""
64
+ end
65
+
51
66
  end
@@ -0,0 +1,27 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Launchd < Foreman::Export::Base
5
+
6
+ def export
7
+ error("Must specify a location") unless location
8
+
9
+ app = self.app || File.basename(engine.directory)
10
+ user = self.user || app
11
+ log_root = self.log || "/var/log/#{app}"
12
+ template_root = self.template
13
+
14
+ FileUtils.mkdir_p(location)
15
+
16
+ engine.procfile.entries.each do |process|
17
+ 1.upto(self.concurrency[process.name]) do |num|
18
+
19
+ master_template = export_template("launchd", "launchd.plist.erb", template_root)
20
+ master_config = ERB.new(master_template).result(binding)
21
+ write_file "#{location}/#{app}-#{process.name}-#{num}.plist", master_config
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -62,7 +62,7 @@ private
62
62
  $stdout.reopen writer
63
63
  $stderr.reopen writer
64
64
  reader.close
65
- exec Foreman.runner, "-d", basedir, command
65
+ exec Foreman.runner, "-d", basedir, *command.shellsplit
66
66
  end
67
67
  end
68
68
  [ reader, pid ]
@@ -1,5 +1,5 @@
1
1
  module Foreman
2
2
 
3
- VERSION = "0.46.0"
3
+ VERSION = "0.47.0"
4
4
 
5
5
  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" "April 2012" "Foreman 0.45.0" "Foreman Manual"
4
+ .TH "FOREMAN" "1" "April 2012" "Foreman 0.46.0" "Foreman Manual"
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBforeman\fR \- manage Procfile\-based applications
@@ -144,7 +144,7 @@ describe "Foreman::CLI", :fakefs do
144
144
  before { write_procfile }
145
145
 
146
146
  describe "and a command" do
147
- let(:command) { ["ls", "-l"] }
147
+ let(:command) { ["ls", "-l", "foo bar"] }
148
148
 
149
149
  before(:each) do
150
150
  stub(subject).exec
@@ -160,8 +160,8 @@ describe "Foreman::CLI", :fakefs do
160
160
  ENV["FOO"].should be_nil
161
161
  end
162
162
 
163
- it "should runute the command as a string" do
164
- mock(subject).exec(command.join(" "))
163
+ it "should exec the argument list as a shell command" do
164
+ mock(subject).exec(command.shelljoin)
165
165
  subject.run *command
166
166
  end
167
167
  end
@@ -100,6 +100,13 @@ describe "Foreman::Engine", :fakefs do
100
100
  engine.start
101
101
  end
102
102
 
103
+ it "should set port from .env if specified" do
104
+ File.open(".env", "w") { |f| f.puts("PORT=8017") }
105
+ engine = Foreman::Engine.new("Procfile")
106
+ engine.send(:base_port).should == "8017"
107
+ engine.start
108
+ end
109
+
103
110
  it "should be loaded relative to the Procfile" do
104
111
  FileUtils.mkdir_p "/some/app"
105
112
  File.open("/some/app/.env", "w") { |f| f.puts("FOO=qoo") }
@@ -0,0 +1,24 @@
1
+ require "spec_helper"
2
+ require "foreman/engine"
3
+ require "foreman/export/launchd"
4
+ require "tmpdir"
5
+
6
+ describe Foreman::Export::Launchd, :fakefs do
7
+ let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
8
+ let(:engine) { Foreman::Engine.new(procfile) }
9
+ let(:options) { Hash.new }
10
+ let(:launchd) { Foreman::Export::Launchd.new("/tmp/init", engine, options) }
11
+
12
+ before(:each) { load_export_templates_into_fakefs("launchd") }
13
+ before(:each) { stub(launchd).say }
14
+
15
+ it "exports to the filesystem" do
16
+ launchd.export
17
+
18
+ normalize_space(File.read("/tmp/init/app-alpha-1.plist")).should == normalize_space(example_export_file("launchd/launchd-a.default"))
19
+
20
+ normalize_space(File.read("/tmp/init/app-bravo-1.plist")).should == normalize_space(example_export_file("launchd/launchd-b.default"))
21
+
22
+ end
23
+
24
+ end
@@ -33,6 +33,12 @@ describe Foreman::Export::Upstart, :fakefs do
33
33
  upstart.export
34
34
  end
35
35
 
36
+ it "quotes and escapes environment variables" do
37
+ engine.environment['KEY'] = 'd"\|d'
38
+ upstart.export
39
+ File.read("/tmp/init/app-alpha-1.conf").should include('KEY="d\"\\\\|d"')
40
+ end
41
+
36
42
  context "with concurrency" do
37
43
  let(:options) { Hash[:concurrency => "alpha=2"] }
38
44
 
@@ -121,11 +121,25 @@ describe Foreman::Process do
121
121
  output.should include('777')
122
122
  end
123
123
 
124
- it 'should handle arguments' do
125
- pending
124
+ it 'should handle multi-word arguments (old test)' do
125
+ # TODO: This test used to be marked pending; it now passes,
126
+ # but is very slow. The next test is a fast replacement.
126
127
  run %{ sh -c "trap '' TERM; sleep 10" }
127
128
  subject.should be_alive
128
129
  end
130
+
131
+ it 'should handle multi-word arguments' do
132
+ # We have to be a little clever here since Foreman will always
133
+ # print a status message that includes the command.
134
+ run %{ sh -c 'echo abcdef | tr a-c x | tr d-f y' }
135
+ output.should include('xxxyyy')
136
+ end
137
+
138
+ it 'should not clobber "$x"-subexpressions' do
139
+ pending 'this conflicts with the variable interpolation hack'
140
+ run %{ sh -c 'echo \$abcdef | tr \$ %' }
141
+ output.should include('%abcdef')
142
+ end
129
143
  end
130
144
  end
131
145
  end
@@ -13,6 +13,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
13
13
  process.daemonize = true
14
14
  process.environment = {"PORT" => "5000"}
15
15
  process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
16
+ process.stop_grace_time = 45.seconds
16
17
 
17
18
  process.stdout = process.stderr = "/var/log/app/app-alpha-1.log"
18
19
 
@@ -31,6 +32,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
31
32
  process.daemonize = true
32
33
  process.environment = {"PORT" => "5001"}
33
34
  process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
35
+ process.stop_grace_time = 45.seconds
34
36
 
35
37
  process.stdout = process.stderr = "/var/log/app/app-alpha-2.log"
36
38
 
@@ -13,6 +13,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
13
13
  process.daemonize = true
14
14
  process.environment = {"PORT" => "5000"}
15
15
  process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
16
+ process.stop_grace_time = 45.seconds
16
17
 
17
18
  process.stdout = process.stderr = "/var/log/app/app-alpha-1.log"
18
19
 
@@ -30,6 +31,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
30
31
  process.daemonize = true
31
32
  process.environment = {"PORT" => "5100"}
32
33
  process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
34
+ process.stop_grace_time = 45.seconds
33
35
 
34
36
  process.stdout = process.stderr = "/var/log/app/app-bravo-1.log"
35
37
 
@@ -0,0 +1,22 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key>
6
+ <string>app-alpha-1</string>
7
+ <key>ProgramArguments</key>
8
+ <array>
9
+ <string>./alpha</string>
10
+ </array>
11
+ <key>KeepAlive</key>
12
+ <true/>
13
+ <key>RunAtLoad</key>
14
+ <true/>
15
+ <key>StandardErrorPath</key>
16
+ <string>/var/log/app/app-alpha-1.log</string>
17
+ <key>UserName</key>
18
+ <string>app</string>
19
+ <key>WorkingDirectory</key>
20
+ <string>/tmp/app</string>
21
+ </dict>
22
+ </plist>
@@ -0,0 +1,22 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key>
6
+ <string>app-bravo-1</string>
7
+ <key>ProgramArguments</key>
8
+ <array>
9
+ <string>./bravo</string>
10
+ </array>
11
+ <key>KeepAlive</key>
12
+ <true/>
13
+ <key>RunAtLoad</key>
14
+ <true/>
15
+ <key>StandardErrorPath</key>
16
+ <string>/var/log/app/app-bravo-1.log</string>
17
+ <key>UserName</key>
18
+ <string>app</string>
19
+ <key>WorkingDirectory</key>
20
+ <string>/tmp/app</string>
21
+ </dict>
22
+ </plist>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.46.0
4
+ version: 0.47.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-02 00:00:00.000000000 Z
12
+ date: 2012-06-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: thor
16
- requirement: &70104163807140 !ruby/object:Gem::Requirement
16
+ requirement: &70170917701380 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: 0.13.6
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70104163807140
24
+ version_requirements: *70170917701380
25
25
  description: Process manager for applications with multiple components
26
26
  email: ddollar@gmail.com
27
27
  executables:
@@ -38,6 +38,7 @@ files:
38
38
  - data/example/ticker
39
39
  - data/example/utf8
40
40
  - data/export/bluepill/master.pill.erb
41
+ - data/export/launchd/launchd.plist.erb
41
42
  - data/export/runit/log_run.erb
42
43
  - data/export/runit/run.erb
43
44
  - data/export/supervisord/app.conf.erb
@@ -51,6 +52,7 @@ files:
51
52
  - lib/foreman/export/base.rb
52
53
  - lib/foreman/export/bluepill.rb
53
54
  - lib/foreman/export/inittab.rb
55
+ - lib/foreman/export/launchd.rb
54
56
  - lib/foreman/export/runit.rb
55
57
  - lib/foreman/export/supervisord.rb
56
58
  - lib/foreman/export/upstart.rb
@@ -69,6 +71,7 @@ files:
69
71
  - spec/foreman/export/base_spec.rb
70
72
  - spec/foreman/export/bluepill_spec.rb
71
73
  - spec/foreman/export/inittab_spec.rb
74
+ - spec/foreman/export/launchd_spec.rb
72
75
  - spec/foreman/export/runit_spec.rb
73
76
  - spec/foreman/export/supervisord_spec.rb
74
77
  - spec/foreman/export/upstart_spec.rb
@@ -84,6 +87,8 @@ files:
84
87
  - spec/resources/export/bluepill/app.pill
85
88
  - spec/resources/export/inittab/inittab.concurrency
86
89
  - spec/resources/export/inittab/inittab.default
90
+ - spec/resources/export/launchd/launchd-a.default
91
+ - spec/resources/export/launchd/launchd-b.default
87
92
  - spec/resources/export/runit/app-alpha-1-log-run
88
93
  - spec/resources/export/runit/app-alpha-1-run
89
94
  - spec/resources/export/runit/app-alpha-2-log-run
@@ -114,18 +119,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
114
119
  - - ! '>='
115
120
  - !ruby/object:Gem::Version
116
121
  version: '0'
117
- segments:
118
- - 0
119
- hash: -4034722301368458418
120
122
  required_rubygems_version: !ruby/object:Gem::Requirement
121
123
  none: false
122
124
  requirements:
123
125
  - - ! '>='
124
126
  - !ruby/object:Gem::Version
125
127
  version: '0'
126
- segments:
127
- - 0
128
- hash: -4034722301368458418
129
128
  requirements: []
130
129
  rubyforge_project:
131
130
  rubygems_version: 1.8.11