foreman 0.27.0 → 0.37.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.
Files changed (38) hide show
  1. data/README.md +39 -0
  2. data/bin/runner +36 -0
  3. data/data/export/bluepill/master.pill.erb +3 -3
  4. data/lib/foreman/cli.rb +34 -32
  5. data/lib/foreman/engine.rb +97 -78
  6. data/lib/foreman/export/base.rb +12 -5
  7. data/lib/foreman/export/bluepill.rb +5 -7
  8. data/lib/foreman/export/inittab.rb +11 -13
  9. data/lib/foreman/export/runit.rb +24 -25
  10. data/lib/foreman/export/upstart.rb +9 -11
  11. data/lib/foreman/export.rb +21 -0
  12. data/lib/foreman/helpers.rb +45 -0
  13. data/lib/foreman/process.rb +88 -6
  14. data/lib/foreman/procfile.rb +8 -7
  15. data/lib/foreman/procfile_entry.rb +22 -0
  16. data/lib/foreman/utils.rb +4 -1
  17. data/lib/foreman/version.rb +1 -1
  18. data/lib/foreman.rb +13 -1
  19. data/man/foreman.1 +5 -1
  20. data/spec/foreman/cli_spec.rb +77 -7
  21. data/spec/foreman/engine_spec.rb +13 -26
  22. data/spec/foreman/export/base_spec.rb +22 -0
  23. data/spec/foreman/export/bluepill_spec.rb +23 -7
  24. data/spec/foreman/export/inittab_spec.rb +40 -0
  25. data/spec/foreman/export/runit_spec.rb +18 -12
  26. data/spec/foreman/export/upstart_spec.rb +40 -8
  27. data/spec/foreman/export_spec.rb +22 -0
  28. data/spec/foreman/helpers_spec.rb +26 -0
  29. data/spec/foreman/process_spec.rb +131 -2
  30. data/spec/foreman_spec.rb +8 -7
  31. data/spec/helper_spec.rb +18 -0
  32. data/spec/resources/export/bluepill/app-concurrency.pill +47 -0
  33. data/spec/resources/export/bluepill/app.pill +1 -22
  34. data/spec/resources/export/inittab/inittab.concurrency +4 -0
  35. data/spec/resources/export/inittab/inittab.default +4 -0
  36. data/spec/spec_helper.rb +31 -3
  37. metadata +19 -15
  38. data/README.markdown +0 -49
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Foreman
2
+
3
+ Manage Procfile-based applications
4
+
5
+ <table>
6
+ <tr>
7
+ <th>If you have...</th>
8
+ <th>Install with...</th>
9
+ </tr>
10
+ <tr>
11
+ <td>Ruby (MRI, JRuby, Windows)</td>
12
+ <td><pre>$ gem install foreman</pre></td>
13
+ </tr>
14
+ <tr>
15
+ <td>Mac OS X</td>
16
+ <td><a href="http://assets.foreman.io/foreman/foreman.pkg">foreman.pkg</a></td>
17
+ </tr>
18
+ </table>
19
+
20
+ ## Getting Started
21
+
22
+ * http://blog.daviddollar.org/2011/05/06/introducing-foreman.html
23
+
24
+ ## Documentation
25
+
26
+ * [man page](http://ddollar.github.com/foreman)
27
+ * [wiki](http://github.com/ddollar/foreman/wiki)
28
+
29
+ ## Authors
30
+
31
+ #### Created and maintained by
32
+ David Dollar
33
+
34
+ #### Patches contributed by
35
+ Adam Wiggins, Chris Continanza, Chris Lowder, Craig R Webster, Dan Farina, Dan Peterson, David Dollar, Fletcher Nichol, Gabriel Burt, Gamaliel Toro, Greg Reinacker, Hugues Le Gendre, Hunter Nield, Iain Hecker, Jay Zeschin, Keith Rarick, Khaja Minhajuddin, Lincoln Stoll, Marcos Muino Garcia, Mark McGranaghan, Matt Griffin, Matt Haynes, Matthijs Langenberg, Michael Dwan, Michael van Rooijen, Mike Javorski, Nathan Broadbent, Nathan L Smith, Nick Zadrozny, Phil Hagelberg, Ricardo Chimal, Jr, Thom May, Tom Ward, brainopia, clifff, jc00ke
36
+
37
+ ## License
38
+
39
+ MIT
data/bin/runner ADDED
@@ -0,0 +1,36 @@
1
+ #!/bin/sh
2
+ #
3
+ #/ Usage: runner [-d <dir>] <command>
4
+ #/
5
+ #/ Run a command with exec, optionally changing directory first
6
+
7
+ set -e
8
+
9
+ error() {
10
+ echo $@ >&2
11
+ exit 1
12
+ }
13
+
14
+ usage() {
15
+ cat $0 | grep '^#/' | cut -c4-
16
+ exit
17
+ }
18
+
19
+ while getopts ":hd:" OPT; do
20
+ case $OPT in
21
+ d) cd $OPTARG ;;
22
+ h) usage ;;
23
+ \?) error "invalid option: -$OPTARG" ;;
24
+ :) error "option -$OPTARG requires an argument" ;;
25
+ esac
26
+ done
27
+
28
+ shift $((OPTIND-1))
29
+
30
+ command=$1
31
+
32
+ if [ -z "$1" ]; then
33
+ usage
34
+ fi
35
+
36
+ exec $1
@@ -3,9 +3,9 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
3
3
  app.uid = "<%= user %>"
4
4
  app.gid = "<%= user %>"
5
5
 
6
- <% engine.processes.each do |process| %>
6
+ <% engine.procfile.entries.each do |process| %>
7
7
  <% 1.upto(concurrency[process.name]) do |num| %>
8
- <% port = engine.port_for(process, num, options[:port]) %>
8
+ <% port = engine.port_for(process, num, self.port) %>
9
9
  app.process("<%= process.name %>-<%=num%>") do |process|
10
10
  process.start_command = "<%= process.command.gsub("$PORT", port.to_s) %>"
11
11
 
@@ -19,7 +19,7 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
19
19
  process.monitor_children do |children|
20
20
  children.stop_command "kill -QUIT {{PID}}"
21
21
  end
22
-
22
+
23
23
  process.group = "<%= app %>-<%= process.name %>"
24
24
  end
25
25
  <% end %>
data/lib/foreman/cli.rb CHANGED
@@ -1,29 +1,37 @@
1
1
  require "foreman"
2
+ require "foreman/helpers"
2
3
  require "foreman/engine"
3
4
  require "foreman/export"
4
5
  require "thor"
5
6
  require "yaml"
6
7
 
7
8
  class Foreman::CLI < Thor
9
+ include Foreman::Helpers
8
10
 
9
11
  class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
10
12
 
11
- desc "start [PROCESS]", "Start the application, or a specific process"
13
+ desc "start", "Start the application"
14
+
15
+ class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
16
+ class_option :app_root, :type => :string, :aliases => "-d", :desc => "Default: Procfile directory"
12
17
 
13
18
  method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
14
19
  method_option :port, :type => :numeric, :aliases => "-p"
15
20
  method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
16
21
 
17
- def start(process=nil)
18
- check_procfile!
19
-
20
- if process
21
- engine.execute(process)
22
- else
23
- engine.start
22
+ class << self
23
+ # Hackery. Take the run method away from Thor so that we can redefine it.
24
+ def is_thor_reserved_word?(word, type)
25
+ return false if word == 'run'
26
+ super
24
27
  end
25
28
  end
26
29
 
30
+ def start
31
+ check_procfile!
32
+ engine.start
33
+ end
34
+
27
35
  desc "export FORMAT LOCATION", "Export the application to another process management format"
28
36
 
29
37
  method_option :app, :type => :string, :aliases => "-a"
@@ -32,22 +40,12 @@ class Foreman::CLI < Thor
32
40
  method_option :port, :type => :numeric, :aliases => "-p"
33
41
  method_option :user, :type => :string, :aliases => "-u"
34
42
  method_option :template, :type => :string, :aliases => "-t"
35
- method_option :concurrency, :type => :string, :aliases => "-c",
36
- :banner => '"alpha=5,bar=3"'
43
+ method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
37
44
 
38
45
  def export(format, location=nil)
39
46
  check_procfile!
40
-
41
- formatter = case format
42
- when "inittab" then Foreman::Export::Inittab
43
- when "upstart" then Foreman::Export::Upstart
44
- when "bluepill" then Foreman::Export::Bluepill
45
- when "runit" then Foreman::Export::Runit
46
- else error "Unknown export format: #{format}."
47
- end
48
-
49
- formatter.new(engine).export(location, options)
50
-
47
+ formatter = Foreman::Export.formatter(format)
48
+ formatter.new(location, engine, options).export
51
49
  rescue Foreman::Export::Exception => ex
52
50
  error ex.message
53
51
  end
@@ -55,8 +53,21 @@ class Foreman::CLI < Thor
55
53
  desc "check", "Validate your application's Procfile"
56
54
 
57
55
  def check
58
- error "no processes defined" unless engine.processes.length > 0
59
- display "valid procfile detected (#{engine.processes.map(&:name).join(', ')})"
56
+ error "no processes defined" unless engine.procfile.entries.length > 0
57
+ puts "valid procfile detected (#{engine.procfile.process_names.join(', ')})"
58
+ end
59
+
60
+ desc "run COMMAND", "Run a command using your application's environment"
61
+
62
+ def run(*args)
63
+ engine.apply_environment!
64
+ begin
65
+ exec args.join(" ")
66
+ rescue Errno::EACCES
67
+ error "not executable: #{args.first}"
68
+ rescue Errno::ENOENT
69
+ error "command not found: #{args.first}"
70
+ end
60
71
  end
61
72
 
62
73
  private ######################################################################
@@ -73,24 +84,15 @@ private ######################################################################
73
84
  options[:procfile] || "Procfile"
74
85
  end
75
86
 
76
- def display(message)
77
- puts message
78
- end
79
-
80
87
  def error(message)
81
88
  puts "ERROR: #{message}"
82
89
  exit 1
83
90
  end
84
91
 
85
- def procfile_exists?(procfile)
86
- File.exist?(procfile)
87
- end
88
-
89
92
  def options
90
93
  original_options = super
91
94
  return original_options unless File.exists?(".foreman")
92
95
  defaults = YAML::load_file(".foreman") || {}
93
96
  Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
94
97
  end
95
-
96
98
  end
@@ -2,11 +2,11 @@ require "foreman"
2
2
  require "foreman/process"
3
3
  require "foreman/procfile"
4
4
  require "foreman/utils"
5
- require "pty"
6
5
  require "tempfile"
7
6
  require "timeout"
8
7
  require "term/ansicolor"
9
8
  require "fileutils"
9
+ require "thread"
10
10
 
11
11
  class Foreman::Engine
12
12
 
@@ -16,13 +16,16 @@ class Foreman::Engine
16
16
 
17
17
  extend Term::ANSIColor
18
18
 
19
- COLORS = [ cyan, yellow, green, magenta, red ]
19
+ COLORS = [ cyan, yellow, green, magenta, red, blue,
20
+ intense_cyan, intense_yellow, intense_green, intense_magenta,
21
+ intense_red, intense_blue ]
20
22
 
21
23
  def initialize(procfile, options={})
22
24
  @procfile = Foreman::Procfile.new(procfile)
23
- @directory = File.expand_path(File.dirname(procfile))
25
+ @directory = options[:app_root] || File.expand_path(File.dirname(procfile))
24
26
  @options = options
25
27
  @environment = read_environment_files(options[:env])
28
+ @output_mutex = Mutex.new
26
29
  end
27
30
 
28
31
  def self.load_env!(env_file)
@@ -32,34 +35,17 @@ class Foreman::Engine
32
35
 
33
36
  def start
34
37
  proctitle "ruby: foreman master"
35
- termtitle "#{File.basename(@directory)} - foreman (#{processes.size} processes)"
36
-
37
- processes.each do |process|
38
- process.color = next_color
39
- fork process
40
- end
38
+ termtitle "#{File.basename(@directory)} - foreman"
41
39
 
42
40
  trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
43
41
  trap("INT") { puts "SIGINT received"; terminate_gracefully }
44
42
 
43
+ assign_colors
44
+ spawn_processes
45
+ watch_for_output
45
46
  watch_for_termination
46
47
  end
47
48
 
48
- def execute(name)
49
- error "no such process: #{name}" unless process = procfile[name]
50
- process.color = next_color
51
- fork process
52
-
53
- trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
54
- trap("INT") { puts "SIGINT received"; terminate_gracefully }
55
-
56
- watch_for_termination
57
- end
58
-
59
- def processes
60
- procfile.processes
61
- end
62
-
63
49
  def port_for(process, num, base_port=nil)
64
50
  base_port ||= 5000
65
51
  offset = procfile.process_names.index(process.name) * 100
@@ -68,84 +54,94 @@ class Foreman::Engine
68
54
 
69
55
  private ######################################################################
70
56
 
71
- def fork(process)
57
+ def spawn_processes
72
58
  concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency])
73
59
 
74
- 1.upto(concurrency[process.name]) do |num|
75
- fork_individual(process, num, port_for(process, num, @options[:port]))
76
- end
77
- end
78
-
79
- def fork_individual(process, num, port)
80
- apply_environment!
81
-
82
- ENV["PORT"] = port.to_s
83
- ENV["PS"] = "#{process.name}.#{num}"
84
-
85
- pid = Process.fork do
86
- run(process)
60
+ procfile.entries.each do |entry|
61
+ reader, writer = IO.pipe
62
+ entry.spawn(concurrency[entry.name], writer, @directory, @environment, port_for(entry, 1, base_port)).each do |process|
63
+ running_processes[process.pid] = process
64
+ readers[process] = reader
65
+ end
87
66
  end
88
-
89
- info "started with pid #{pid}", process
90
- running_processes[pid] = process
91
67
  end
92
68
 
93
- def run(process)
94
- proctitle "ruby: foreman #{process.name}"
95
- trap("SIGINT", "IGNORE")
96
-
97
- begin
98
- Dir.chdir directory do
99
- PTY.spawn(process.command) do |stdin, stdout, pid|
100
- trap("SIGTERM") { Process.kill("SIGTERM", pid) }
101
- until stdin.eof?
102
- info stdin.gets, process
103
- end
104
- end
105
- end
106
- rescue PTY::ChildExited, Interrupt, Errno::EIO, Errno::ENOENT
107
- begin
108
- info "process exiting", process
109
- rescue Interrupt
110
- end
111
- end
69
+ def base_port
70
+ options[:port] || 5000
112
71
  end
113
72
 
114
73
  def kill_all(signal="SIGTERM")
115
74
  running_processes.each do |pid, process|
116
- Process.kill(signal, pid) rescue Errno::ESRCH
75
+ info "sending #{signal} to pid #{pid}"
76
+ process.kill signal
117
77
  end
118
78
  end
119
79
 
120
80
  def terminate_gracefully
81
+ return if @terminating
82
+ @terminating = true
121
83
  info "sending SIGTERM to all processes"
122
84
  kill_all "SIGTERM"
123
- Timeout.timeout(3) { Process.waitall }
85
+ Timeout.timeout(5) do
86
+ while running_processes.length > 0
87
+ pid, status = Process.wait2
88
+ process = running_processes.delete(pid)
89
+ info "process terminated", process.name
90
+ end
91
+ end
124
92
  rescue Timeout::Error
125
93
  info "sending SIGKILL to all processes"
126
94
  kill_all "SIGKILL"
127
95
  end
128
96
 
97
+ def watch_for_output
98
+ Thread.new do
99
+ require "win32console" if Foreman.windows?
100
+ begin
101
+ loop do
102
+ rs, ws = IO.select(readers.values, [], [], 1)
103
+ (rs || []).each do |r|
104
+ data = r.gets
105
+ next unless data
106
+ ps, message = data.split(",", 2)
107
+ color = colors[ps.split(".").first]
108
+ info message, ps, color
109
+ end
110
+ end
111
+ rescue Exception => ex
112
+ puts ex.message
113
+ puts ex.backtrace
114
+ end
115
+ end
116
+ end
117
+
129
118
  def watch_for_termination
130
119
  pid, status = Process.wait2
131
120
  process = running_processes.delete(pid)
132
- info "process terminated", process
121
+ info "process terminated", process.name
133
122
  terminate_gracefully
134
- kill_all
135
123
  rescue Errno::ECHILD
136
124
  end
137
125
 
138
- def info(message, process=nil)
139
- print process.color if process
140
- print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(process)} | "
141
- print Term::ANSIColor.reset
142
- print message.chomp
143
- puts
126
+ def info(message, name="system", color=Term::ANSIColor.white)
127
+ output = ""
128
+ output += color
129
+ output += "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
130
+ output += Term::ANSIColor.reset
131
+ output += message.chomp
132
+ puts output
144
133
  end
145
134
 
146
- def error(message)
147
- puts "ERROR: #{message}"
148
- exit 1
135
+ def print(message=nil)
136
+ @output_mutex.synchronize do
137
+ $stdout.print message
138
+ end
139
+ end
140
+
141
+ def puts(message=nil)
142
+ @output_mutex.synchronize do
143
+ $stdout.puts message
144
+ end
149
145
  end
150
146
 
151
147
  def longest_process_name
@@ -156,9 +152,8 @@ private ######################################################################
156
152
  end
157
153
  end
158
154
 
159
- def pad_process_name(process)
160
- name = process ? "#{ENV["PS"]}" : "system"
161
- name.ljust(longest_process_name + 3) # add 3 for process number padding
155
+ def pad_process_name(name="system")
156
+ name.to_s.ljust(longest_process_name + 3) # add 3 for process number padding
162
157
  end
163
158
 
164
159
  def proctitle(title)
@@ -166,17 +161,36 @@ private ######################################################################
166
161
  end
167
162
 
168
163
  def termtitle(title)
169
- printf("\033]0;#{title}\007")
164
+ printf("\033]0;#{title}\007") unless Foreman.windows?
170
165
  end
171
166
 
172
167
  def running_processes
173
168
  @running_processes ||= {}
174
169
  end
175
170
 
171
+ def readers
172
+ @readers ||= {}
173
+ end
174
+
175
+ def colors
176
+ @colors ||= {}
177
+ end
178
+
179
+ def assign_colors
180
+ procfile.entries.each do |entry|
181
+ colors[entry.name] = next_color
182
+ end
183
+ end
184
+
185
+ def process_by_reader(reader)
186
+ readers.invert[reader]
187
+ end
188
+
176
189
  def next_color
177
190
  @current_color ||= -1
178
191
  @current_color += 1
179
- @current_color >= COLORS.length ? "" : COLORS[@current_color]
192
+ @current_color = 0 if COLORS.length < @current_color
193
+ COLORS[@current_color]
180
194
  end
181
195
 
182
196
  module Env
@@ -208,6 +222,11 @@ private ######################################################################
208
222
  def apply_environment!
209
223
  @environment.each { |k,v| ENV[k] = v }
210
224
  end
225
+
226
+ def error(message)
227
+ puts "ERROR: #{message}"
228
+ exit 1
229
+ end
211
230
  end
212
231
 
213
232
  include Env
@@ -3,10 +3,17 @@ require "foreman/utils"
3
3
 
4
4
  class Foreman::Export::Base
5
5
 
6
- attr_reader :engine
7
-
8
- def initialize(engine)
9
- @engine = engine
6
+ attr_reader :location, :engine, :app, :log, :port, :user, :template, :concurrency
7
+
8
+ def initialize(location, engine, options={})
9
+ @location = location
10
+ @engine = engine
11
+ @app = options[:app]
12
+ @log = options[:log]
13
+ @port = options[:port]
14
+ @user = options[:user]
15
+ @template = options[:template]
16
+ @concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
10
17
  end
11
18
 
12
19
  def export
@@ -26,7 +33,7 @@ private ######################################################################
26
33
  def export_template(exporter, file, template_root)
27
34
  if template_root && File.exist?(file_path = File.join(template_root, file))
28
35
  File.read(file_path)
29
- elsif File.exist?(file_path = File.join("~/.foreman/templates", file))
36
+ elsif File.exist?(file_path = File.expand_path(File.join("~/.foreman/templates", file)))
30
37
  File.read(file_path)
31
38
  else
32
39
  File.read(File.expand_path("../../../../data/export/#{exporter}/#{file}", __FILE__))
@@ -3,23 +3,21 @@ require "foreman/export"
3
3
 
4
4
  class Foreman::Export::Bluepill < Foreman::Export::Base
5
5
 
6
- def export(location, options={})
6
+ def export
7
7
  error("Must specify a location") unless location
8
8
 
9
9
  FileUtils.mkdir_p location
10
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]
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
15
15
 
16
16
  Dir["#{location}/#{app}.pill"].each do |file|
17
17
  say "cleaning up: #{file}"
18
18
  FileUtils.rm(file)
19
19
  end
20
20
 
21
- concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
22
-
23
21
  master_template = export_template("bluepill", "master.pill.erb", template_root)
24
22
  master_config = ERB.new(master_template).result(binding)
25
23
  write_file "#{location}/#{app}.pill", master_config
@@ -2,20 +2,18 @@ require "foreman/export"
2
2
 
3
3
  class Foreman::Export::Inittab < Foreman::Export::Base
4
4
 
5
- def export(fname=nil, options={})
6
- app = options[:app] || File.basename(engine.directory)
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
+ app = self.app || File.basename(engine.directory)
7
+ user = self.user || app
8
+ log_root = self.log || "/var/log/#{app}"
11
9
 
12
10
  inittab = []
13
11
  inittab << "# ----- foreman #{app} processes -----"
14
12
 
15
- engine.processes.inject(1) do |index, process|
16
- 1.upto(concurrency[process.name]) do |num|
13
+ engine.procfile.entries.inject(1) do |index, process|
14
+ 1.upto(self.concurrency[process.name]) do |num|
17
15
  id = app.slice(0, 2).upcase + sprintf("%02d", index)
18
- port = engine.port_for(process, num, options[:port])
16
+ port = engine.port_for(process, num, self.port)
19
17
  inittab << "#{id}:4:respawn:/bin/su - #{user} -c 'PORT=#{port} #{process.command} >> #{log_root}/#{process.name}-#{num}.log 2>&1'"
20
18
  index += 1
21
19
  end
@@ -26,12 +24,12 @@ class Foreman::Export::Inittab < Foreman::Export::Base
26
24
 
27
25
  inittab = inittab.join("\n") + "\n"
28
26
 
29
- if fname
27
+ if location == "-"
28
+ puts inittab
29
+ else
30
30
  FileUtils.mkdir_p(log_root) rescue error "could not create #{log_root}"
31
31
  FileUtils.chown(user, nil, log_root) rescue error "could not chown #{log_root} to #{user}"
32
- write_file(fname, inittab)
33
- else
34
- puts inittab
32
+ write_file(location, inittab)
35
33
  end
36
34
  end
37
35
 
@@ -3,58 +3,57 @@ require "foreman/export"
3
3
 
4
4
  class Foreman::Export::Runit < Foreman::Export::Base
5
5
  ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
6
-
7
- def export(location, options={})
6
+
7
+ def export
8
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
-
15
- concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
16
-
9
+
10
+ app = self.app || File.basename(engine.directory)
11
+ user = self.user || app
12
+ log_root = self.log || "/var/log/#{app}"
13
+ template_root = self.template
14
+
17
15
  run_template = export_template('runit', 'run.erb', template_root)
18
16
  log_run_template = export_template('runit', 'log_run.erb', template_root)
19
17
 
20
- engine.processes.each do |process|
21
- 1.upto(concurrency[process.name]) do |num|
18
+ engine.procfile.entries.each do |process|
19
+ 1.upto(self.concurrency[process.name]) do |num|
22
20
  process_directory = "#{location}/#{app}-#{process.name}-#{num}"
23
21
  process_env_directory = "#{process_directory}/env"
24
22
  process_log_directory = "#{process_directory}/log"
25
-
23
+
26
24
  create_directory process_directory
27
25
  create_directory process_env_directory
28
26
  create_directory process_log_directory
29
-
27
+
30
28
  run = ERB.new(run_template).result(binding)
31
29
  write_file "#{process_directory}/run", run
32
-
33
- port = engine.port_for(process, num, options[:port])
30
+ FileUtils.chmod 0755, "#{process_directory}/run"
31
+
32
+ port = engine.port_for(process, num, self.port)
34
33
  environment_variables = {'PORT' => port}.
35
34
  merge(engine.environment).
36
35
  merge(inline_variables(process.command))
37
-
36
+
38
37
  environment_variables.each_pair do |var, env|
39
38
  write_file "#{process_env_directory}/#{var.upcase}", env
40
39
  end
41
-
40
+
42
41
  log_run = ERB.new(log_run_template).result(binding)
43
42
  write_file "#{process_log_directory}/run", log_run
44
-
43
+ FileUtils.chmod 0755, "#{process_log_directory}/run"
45
44
  end
46
45
  end
47
-
46
+
48
47
  end
49
-
48
+
50
49
  private
51
50
  def create_directory(location)
52
51
  say "creating: #{location}"
53
- FileUtils.mkdir(location)
52
+ FileUtils.mkdir_p(location)
54
53
  end
55
-
54
+
56
55
  def inline_variables(command)
57
- variable_name_regex =
56
+ variable_name_regex =
58
57
  Hash[*command.scan(ENV_VARIABLE_REGEX).flatten]
59
58
  end
60
- end
59
+ end